annotate widgets/Pane.cpp @ 101:0f36cdf407a6 sv1-v0.9rc1

* Make vertical scale alignment modes work in note layer as well as time-value layer, and several significant fixes to it * Make it possible to draw notes properly on the note layer * Show units (and frequencies etc in note layer's case) in the time-value and note layer description boxes * Minor fix to item edit dialog layout * Some minor menu rearrangement * Comment out a lot of debug output * Add SV website and reference URLs to Help menu, and add code to (attempt to) open them in the user's preferred browser
author Chris Cannam
date Fri, 12 May 2006 14:40:43 +0000
parents 453f7da3534e
children 8dfa20f1c70a
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "widgets/Pane.h"
Chris@0 17 #include "base/Layer.h"
Chris@0 18 #include "base/Model.h"
Chris@0 19 #include "base/ZoomConstraint.h"
Chris@0 20 #include "base/RealTime.h"
Chris@0 21 #include "base/Profiler.h"
Chris@13 22 #include "base/ViewManager.h"
Chris@99 23 #include "base/CommandHistory.h"
Chris@41 24 #include "layer/WaveformLayer.h"
Chris@0 25
Chris@0 26 #include <QPaintEvent>
Chris@0 27 #include <QPainter>
Chris@0 28 #include <iostream>
Chris@0 29 #include <cmath>
Chris@0 30
Chris@0 31 using std::cerr;
Chris@0 32 using std::endl;
Chris@0 33
Chris@0 34 Pane::Pane(QWidget *w) :
Chris@0 35 View(w, true),
Chris@0 36 m_identifyFeatures(false),
Chris@0 37 m_clickedInRange(false),
Chris@0 38 m_shiftPressed(false),
Chris@13 39 m_ctrlPressed(false),
Chris@17 40 m_navigating(false),
Chris@17 41 m_resizing(false),
Chris@0 42 m_centreLineVisible(true)
Chris@0 43 {
Chris@0 44 setObjectName("Pane");
Chris@0 45 setMouseTracking(true);
Chris@0 46 }
Chris@0 47
Chris@0 48 bool
Chris@44 49 Pane::shouldIlluminateLocalFeatures(const Layer *layer, QPoint &pos) const
Chris@0 50 {
Chris@42 51 QPoint discard;
Chris@42 52 bool b0, b1;
Chris@42 53
Chris@42 54 if (layer == getSelectedLayer() &&
Chris@42 55 !shouldIlluminateLocalSelection(discard, b0, b1)) {
Chris@42 56
Chris@27 57 pos = m_identifyPoint;
Chris@27 58 return m_identifyFeatures;
Chris@27 59 }
Chris@0 60
Chris@0 61 return false;
Chris@0 62 }
Chris@0 63
Chris@42 64 bool
Chris@42 65 Pane::shouldIlluminateLocalSelection(QPoint &pos,
Chris@42 66 bool &closeToLeft,
Chris@44 67 bool &closeToRight) const
Chris@42 68 {
Chris@42 69 if (m_identifyFeatures &&
Chris@42 70 m_manager &&
Chris@42 71 m_manager->getToolMode() == ViewManager::EditMode &&
Chris@42 72 !m_manager->getSelections().empty() &&
Chris@42 73 !selectionIsBeingEdited()) {
Chris@42 74
Chris@42 75 Selection s(getSelectionAt(m_identifyPoint.x(),
Chris@42 76 closeToLeft, closeToRight));
Chris@42 77
Chris@42 78 if (!s.isEmpty()) {
Chris@42 79 if (getSelectedLayer() && getSelectedLayer()->isLayerEditable()) {
Chris@42 80
Chris@42 81 pos = m_identifyPoint;
Chris@42 82 return true;
Chris@42 83 }
Chris@42 84 }
Chris@42 85 }
Chris@42 86
Chris@42 87 return false;
Chris@42 88 }
Chris@42 89
Chris@42 90 bool
Chris@42 91 Pane::selectionIsBeingEdited() const
Chris@42 92 {
Chris@42 93 if (!m_editingSelection.isEmpty()) {
Chris@42 94 if (m_mousePos != m_clickPos &&
Chris@42 95 getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) {
Chris@42 96 return true;
Chris@42 97 }
Chris@42 98 }
Chris@42 99 return false;
Chris@42 100 }
Chris@42 101
Chris@0 102 void
Chris@0 103 Pane::setCentreLineVisible(bool visible)
Chris@0 104 {
Chris@0 105 m_centreLineVisible = visible;
Chris@0 106 update();
Chris@0 107 }
Chris@0 108
Chris@0 109 void
Chris@0 110 Pane::paintEvent(QPaintEvent *e)
Chris@0 111 {
Chris@0 112 QPainter paint;
Chris@0 113
Chris@0 114 QRect r(rect());
Chris@0 115
Chris@0 116 if (e) {
Chris@0 117 r = e->rect();
Chris@0 118 }
Chris@0 119 /*
Chris@0 120 paint.begin(this);
Chris@0 121 paint.setClipRect(r);
Chris@0 122
Chris@0 123 if (hasLightBackground()) {
Chris@0 124 paint.setPen(Qt::white);
Chris@0 125 paint.setBrush(Qt::white);
Chris@0 126 } else {
Chris@0 127 paint.setPen(Qt::black);
Chris@0 128 paint.setBrush(Qt::black);
Chris@0 129 }
Chris@0 130 paint.drawRect(r);
Chris@0 131
Chris@0 132 paint.end();
Chris@0 133 */
Chris@0 134 View::paintEvent(e);
Chris@0 135
Chris@0 136 paint.begin(this);
Chris@0 137
Chris@0 138 if (e) {
Chris@0 139 paint.setClipRect(r);
Chris@0 140 }
Chris@41 141
Chris@41 142 const Model *waveformModel = 0; // just for reporting purposes
Chris@51 143 int verticalScaleWidth = 0;
Chris@41 144
Chris@55 145 int fontHeight = paint.fontMetrics().height();
Chris@55 146 int fontAscent = paint.fontMetrics().ascent();
Chris@55 147
Chris@99 148 if (m_manager &&
Chris@99 149 !m_manager->isPlaying() &&
Chris@99 150 m_manager->getToolMode() == ViewManager::SelectMode) {
Chris@0 151
Chris@99 152 for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
Chris@99 153 --vi;
Chris@41 154
Chris@77 155 std::vector<QRect> crosshairExtents;
Chris@77 156
Chris@77 157 if ((*vi)->getCrosshairExtents(this, paint, m_identifyPoint,
Chris@77 158 crosshairExtents)) {
Chris@77 159 (*vi)->paintCrosshairs(this, paint, m_identifyPoint);
Chris@99 160 break;
Chris@99 161 } else if ((*vi)->isLayerOpaque()) {
Chris@99 162 break;
Chris@70 163 }
Chris@77 164 }
Chris@99 165 }
Chris@99 166
Chris@99 167 for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
Chris@99 168 --vi;
Chris@99 169
Chris@99 170 if (dynamic_cast<WaveformLayer *>(*vi)) {
Chris@99 171 waveformModel = (*vi)->getModel();
Chris@99 172 }
Chris@0 173
Chris@77 174 if (!m_manager ||
Chris@77 175 m_manager->getOverlayMode() == ViewManager::NoOverlays) {
Chris@77 176 break;
Chris@77 177 }
Chris@70 178
Chris@77 179 verticalScaleWidth = (*vi)->getVerticalScaleWidth(this, paint);
Chris@77 180
Chris@77 181 if (verticalScaleWidth > 0 && r.left() < verticalScaleWidth) {
Chris@0 182
Chris@0 183 // Profiler profiler("Pane::paintEvent - painting vertical scale", true);
Chris@0 184
Chris@0 185 // std::cerr << "Pane::paintEvent: calling paint.save() in vertical scale block" << std::endl;
Chris@77 186 paint.save();
Chris@77 187
Chris@77 188 paint.setPen(Qt::black);
Chris@77 189 paint.setBrush(Qt::white);
Chris@77 190 paint.drawRect(0, -1, verticalScaleWidth, height()+1);
Chris@77 191
Chris@77 192 paint.setBrush(Qt::NoBrush);
Chris@77 193 (*vi)->paintVerticalScale
Chris@77 194 (this, paint, QRect(0, 0, verticalScaleWidth, height()));
Chris@77 195
Chris@77 196 paint.restore();
Chris@77 197 }
Chris@77 198
Chris@77 199 if (m_identifyFeatures) {
Chris@77 200
Chris@77 201 QPoint pos = m_identifyPoint;
Chris@77 202 QString desc = (*vi)->getFeatureDescription(this, pos);
Chris@77 203
Chris@77 204 if (desc != "") {
Chris@77 205
Chris@70 206 paint.save();
Chris@77 207
Chris@77 208 int tabStop =
Chris@77 209 paint.fontMetrics().width(tr("Some lengthy prefix:"));
Chris@77 210
Chris@77 211 QRect boundingRect =
Chris@77 212 paint.fontMetrics().boundingRect
Chris@77 213 (rect(),
Chris@77 214 Qt::AlignRight | Qt::AlignTop | Qt::TextExpandTabs,
Chris@77 215 desc, tabStop);
Chris@0 216
Chris@77 217 if (hasLightBackground()) {
Chris@77 218 paint.setPen(Qt::NoPen);
Chris@77 219 paint.setBrush(QColor(250, 250, 250, 200));
Chris@77 220 } else {
Chris@77 221 paint.setPen(Qt::NoPen);
Chris@77 222 paint.setBrush(QColor(50, 50, 50, 200));
Chris@77 223 }
Chris@0 224
Chris@77 225 int extra = paint.fontMetrics().descent();
Chris@77 226 paint.drawRect(width() - boundingRect.width() - 10 - extra,
Chris@77 227 10 - extra,
Chris@77 228 boundingRect.width() + 2 * extra,
Chris@77 229 boundingRect.height() + extra);
Chris@77 230
Chris@77 231 if (hasLightBackground()) {
Chris@77 232 paint.setPen(QColor(150, 20, 0));
Chris@77 233 } else {
Chris@77 234 paint.setPen(QColor(255, 150, 100));
Chris@77 235 }
Chris@77 236
Chris@77 237 QTextOption option;
Chris@77 238 option.setWrapMode(QTextOption::NoWrap);
Chris@77 239 option.setAlignment(Qt::AlignRight | Qt::AlignTop);
Chris@77 240 option.setTabStop(tabStop);
Chris@77 241 paint.drawText(QRectF(width() - boundingRect.width() - 10, 10,
Chris@77 242 boundingRect.width(),
Chris@77 243 boundingRect.height()),
Chris@77 244 desc,
Chris@77 245 option);
Chris@0 246
Chris@70 247 paint.restore();
Chris@70 248 }
Chris@77 249 }
Chris@25 250
Chris@77 251 break;
Chris@0 252 }
Chris@0 253
Chris@55 254 int sampleRate = getModelsSampleRate();
Chris@55 255 paint.setBrush(Qt::NoBrush);
Chris@55 256
Chris@0 257 if (m_centreLineVisible) {
Chris@0 258
Chris@0 259 if (hasLightBackground()) {
Chris@0 260 paint.setPen(QColor(50, 50, 50));
Chris@0 261 } else {
Chris@0 262 paint.setPen(QColor(200, 200, 200));
Chris@0 263 }
Chris@0 264 paint.drawLine(width() / 2, 0, width() / 2, height() - 1);
Chris@0 265
Chris@55 266 paint.setPen(QColor(50, 50, 50));
Chris@55 267
Chris@55 268 int y = height() - fontHeight
Chris@55 269 + fontAscent - 6;
Chris@0 270
Chris@0 271 LayerList::iterator vi = m_layers.end();
Chris@0 272
Chris@0 273 if (vi != m_layers.begin()) {
Chris@0 274
Chris@0 275 switch ((*--vi)->getPreferredFrameCountPosition()) {
Chris@0 276
Chris@0 277 case Layer::PositionTop:
Chris@55 278 y = fontAscent + 6;
Chris@0 279 break;
Chris@0 280
Chris@0 281 case Layer::PositionMiddle:
Chris@55 282 y = (height() - fontHeight) / 2
Chris@55 283 + fontAscent;
Chris@0 284 break;
Chris@0 285
Chris@0 286 case Layer::PositionBottom:
Chris@0 287 // y already set correctly
Chris@0 288 break;
Chris@0 289 }
Chris@0 290 }
Chris@0 291
Chris@70 292 if (m_manager &&
Chris@70 293 m_manager->getOverlayMode() != ViewManager::NoOverlays) {
Chris@0 294
Chris@70 295 if (sampleRate) {
Chris@0 296
Chris@70 297 QString text(QString::fromStdString
Chris@70 298 (RealTime::frame2RealTime
Chris@70 299 (m_centreFrame, sampleRate).toText(true)));
Chris@70 300
Chris@70 301 int tw = paint.fontMetrics().width(text);
Chris@70 302 int x = width()/2 - 4 - tw;
Chris@70 303
Chris@70 304 drawVisibleText(paint, x, y, text, OutlinedText);
Chris@70 305 }
Chris@70 306
Chris@70 307 QString text = QString("%1").arg(m_centreFrame);
Chris@70 308
Chris@70 309 int tw = paint.fontMetrics().width(text);
Chris@70 310 int x = width()/2 + 4;
Chris@70 311
Chris@70 312 drawVisibleText(paint, x, y, text, OutlinedText);
Chris@70 313 }
Chris@55 314
Chris@55 315 } else {
Chris@55 316
Chris@55 317 paint.setPen(QColor(50, 50, 50));
Chris@55 318 }
Chris@55 319
Chris@55 320 if (waveformModel &&
Chris@70 321 m_manager &&
Chris@70 322 m_manager->getOverlayMode() != ViewManager::NoOverlays &&
Chris@55 323 r.y() + r.height() >= height() - fontHeight - 6) {
Chris@55 324
Chris@55 325 size_t mainModelRate = m_manager->getMainModelSampleRate();
Chris@55 326 size_t playbackRate = m_manager->getPlaybackSampleRate();
Chris@55 327
Chris@55 328 QString srNote = "";
Chris@55 329
Chris@55 330 // Show (R) for waveform models that will be resampled on
Chris@55 331 // playback, and (X) for waveform models that will be played
Chris@55 332 // at the wrong rate because their rate differs from that of
Chris@55 333 // the main model.
Chris@55 334
Chris@55 335 if (sampleRate == mainModelRate) {
Chris@55 336 if (sampleRate != playbackRate) srNote = " " + tr("(R)");
Chris@0 337 } else {
Chris@55 338 std::cerr << "Sample rate = " << sampleRate << ", main model rate = " << mainModelRate << std::endl;
Chris@55 339 srNote = " " + tr("(X)");
Chris@0 340 }
Chris@41 341
Chris@55 342 QString desc = tr("%1 / %2Hz%3")
Chris@55 343 .arg(RealTime::frame2RealTime(waveformModel->getEndFrame(),
Chris@55 344 sampleRate)
Chris@55 345 .toText(false).c_str())
Chris@55 346 .arg(sampleRate)
Chris@55 347 .arg(srNote);
Chris@49 348
Chris@55 349 if (r.x() < verticalScaleWidth + 5 + paint.fontMetrics().width(desc)) {
Chris@55 350 drawVisibleText(paint, verticalScaleWidth + 5,
Chris@55 351 height() - fontHeight + fontAscent - 6,
Chris@55 352 desc, OutlinedText);
Chris@55 353 }
Chris@55 354 }
Chris@50 355
Chris@70 356 if (m_manager &&
Chris@70 357 m_manager->getOverlayMode() == ViewManager::AllOverlays &&
Chris@70 358 r.y() + r.height() >= height() - m_layers.size() * fontHeight - 6) {
Chris@51 359
Chris@51 360 std::vector<QString> texts;
Chris@51 361 int maxTextWidth = 0;
Chris@51 362
Chris@51 363 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@51 364
Chris@56 365 QString text = (*i)->getLayerPresentationName();
Chris@51 366 int tw = paint.fontMetrics().width(text);
Chris@61 367 bool reduced = false;
Chris@61 368 while (tw > width() / 3 && text.length() > 4) {
Chris@61 369 if (!reduced && text.length() > 8) {
Chris@61 370 text = text.left(text.length() - 4);
Chris@61 371 } else {
Chris@61 372 text = text.left(text.length() - 2);
Chris@61 373 }
Chris@61 374 reduced = true;
Chris@61 375 tw = paint.fontMetrics().width(text + "...");
Chris@61 376 }
Chris@61 377 if (reduced) {
Chris@61 378 texts.push_back(text + "...");
Chris@61 379 } else {
Chris@61 380 texts.push_back(text);
Chris@61 381 }
Chris@51 382 if (tw > maxTextWidth) maxTextWidth = tw;
Chris@51 383 }
Chris@55 384
Chris@51 385 int lly = height() - 6;
Chris@51 386
Chris@55 387 if (r.x() + r.width() >= width() - maxTextWidth - 5) {
Chris@55 388
Chris@55 389 for (int i = 0; i < texts.size(); ++i) {
Chris@51 390
Chris@55 391 if (i == texts.size() - 1) {
Chris@51 392 paint.setPen(Qt::black);
Chris@51 393 }
Chris@55 394
Chris@55 395 drawVisibleText(paint, width() - maxTextWidth - 5,
Chris@55 396 lly - fontHeight + fontAscent,
Chris@55 397 texts[i], OutlinedText);
Chris@55 398
Chris@55 399 lly -= fontHeight;
Chris@51 400 }
Chris@51 401 }
Chris@0 402 }
Chris@0 403
Chris@0 404 if (m_clickedInRange && m_shiftPressed) {
Chris@19 405 if (m_manager && (m_manager->getToolMode() == ViewManager::NavigateMode)) {
Chris@19 406 //!!! be nice if this looked a bit more in keeping with the
Chris@19 407 //selection block
Chris@19 408 paint.setPen(Qt::blue);
Chris@19 409 paint.drawRect(m_clickPos.x(), m_clickPos.y(),
Chris@19 410 m_mousePos.x() - m_clickPos.x(),
Chris@19 411 m_mousePos.y() - m_clickPos.y());
Chris@19 412 }
Chris@0 413 }
Chris@0 414
Chris@42 415 if (selectionIsBeingEdited()) {
Chris@42 416
Chris@42 417 int offset = m_mousePos.x() - m_clickPos.x();
Chris@42 418 int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset;
Chris@42 419 int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
Chris@42 420
Chris@42 421 if (m_editingSelectionEdge < 0) {
Chris@42 422 p1 = getXForFrame(m_editingSelection.getEndFrame());
Chris@42 423 } else if (m_editingSelectionEdge > 0) {
Chris@42 424 p0 = getXForFrame(m_editingSelection.getStartFrame());
Chris@42 425 }
Chris@42 426
Chris@42 427 paint.save();
Chris@42 428 if (hasLightBackground()) {
Chris@42 429 paint.setPen(QPen(Qt::black, 2));
Chris@42 430 } else {
Chris@42 431 paint.setPen(QPen(Qt::white, 2));
Chris@42 432 }
Chris@42 433
Chris@42 434 //!!! duplicating display policy with View::drawSelections
Chris@42 435
Chris@42 436 if (m_editingSelectionEdge < 0) {
Chris@42 437 paint.drawLine(p0, 1, p1, 1);
Chris@42 438 paint.drawLine(p0, 0, p0, height());
Chris@42 439 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@42 440 } else if (m_editingSelectionEdge > 0) {
Chris@42 441 paint.drawLine(p0, 1, p1, 1);
Chris@42 442 paint.drawLine(p1, 0, p1, height());
Chris@42 443 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@42 444 } else {
Chris@42 445 paint.setBrush(Qt::NoBrush);
Chris@42 446 paint.drawRect(p0, 1, p1 - p0, height() - 2);
Chris@42 447 }
Chris@42 448 paint.restore();
Chris@42 449 }
Chris@42 450
Chris@0 451 paint.end();
Chris@0 452 }
Chris@0 453
Chris@17 454 Selection
Chris@44 455 Pane::getSelectionAt(int x, bool &closeToLeftEdge, bool &closeToRightEdge) const
Chris@17 456 {
Chris@17 457 closeToLeftEdge = closeToRightEdge = false;
Chris@17 458
Chris@17 459 if (!m_manager) return Selection();
Chris@17 460
Chris@20 461 long testFrame = getFrameForX(x - 5);
Chris@17 462 if (testFrame < 0) {
Chris@20 463 testFrame = getFrameForX(x);
Chris@17 464 if (testFrame < 0) return Selection();
Chris@17 465 }
Chris@17 466
Chris@17 467 Selection selection = m_manager->getContainingSelection(testFrame, true);
Chris@17 468 if (selection.isEmpty()) return selection;
Chris@17 469
Chris@20 470 int lx = getXForFrame(selection.getStartFrame());
Chris@20 471 int rx = getXForFrame(selection.getEndFrame());
Chris@17 472
Chris@17 473 int fuzz = 2;
Chris@17 474 if (x < lx - fuzz || x > rx + fuzz) return Selection();
Chris@17 475
Chris@17 476 int width = rx - lx;
Chris@17 477 fuzz = 3;
Chris@17 478 if (width < 12) fuzz = width / 4;
Chris@17 479 if (fuzz < 1) fuzz = 1;
Chris@17 480
Chris@17 481 if (x < lx + fuzz) closeToLeftEdge = true;
Chris@17 482 if (x > rx - fuzz) closeToRightEdge = true;
Chris@17 483
Chris@17 484 return selection;
Chris@17 485 }
Chris@17 486
Chris@0 487 void
Chris@0 488 Pane::mousePressEvent(QMouseEvent *e)
Chris@0 489 {
Chris@89 490 if (e->buttons() & Qt::RightButton) {
Chris@89 491 emit rightButtonMenuRequested(mapToGlobal(e->pos()));
Chris@89 492 return;
Chris@89 493 }
Chris@89 494
Chris@0 495 m_clickPos = e->pos();
Chris@0 496 m_clickedInRange = true;
Chris@42 497 m_editingSelection = Selection();
Chris@42 498 m_editingSelectionEdge = 0;
Chris@0 499 m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
Chris@13 500 m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
Chris@13 501
Chris@13 502 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@13 503 if (m_manager) mode = m_manager->getToolMode();
Chris@13 504
Chris@17 505 m_navigating = false;
Chris@13 506
Chris@17 507 if (mode == ViewManager::NavigateMode || (e->buttons() & Qt::MidButton)) {
Chris@17 508
Chris@17 509 if (mode != ViewManager::NavigateMode) {
Chris@17 510 setCursor(Qt::PointingHandCursor);
Chris@17 511 }
Chris@17 512
Chris@17 513 m_navigating = true;
Chris@13 514 m_dragCentreFrame = m_centreFrame;
Chris@13 515
Chris@13 516 } else if (mode == ViewManager::SelectMode) {
Chris@13 517
Chris@17 518 bool closeToLeft = false, closeToRight = false;
Chris@17 519 Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight);
Chris@17 520
Chris@17 521 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
Chris@17 522
Chris@17 523 m_manager->removeSelection(selection);
Chris@17 524
Chris@17 525 if (closeToLeft) {
Chris@17 526 m_selectionStartFrame = selection.getEndFrame();
Chris@17 527 } else {
Chris@17 528 m_selectionStartFrame = selection.getStartFrame();
Chris@17 529 }
Chris@17 530
Chris@17 531 m_manager->setInProgressSelection(selection, false);
Chris@17 532 m_resizing = true;
Chris@13 533
Chris@17 534 } else {
Chris@17 535
Chris@20 536 int mouseFrame = getFrameForX(e->x());
Chris@17 537 size_t resolution = 1;
Chris@17 538 int snapFrame = mouseFrame;
Chris@17 539
Chris@17 540 Layer *layer = getSelectedLayer();
Chris@82 541 if (layer && !m_shiftPressed) {
Chris@44 542 layer->snapToFeatureFrame(this, snapFrame,
Chris@44 543 resolution, Layer::SnapLeft);
Chris@17 544 }
Chris@17 545
Chris@17 546 if (snapFrame < 0) snapFrame = 0;
Chris@17 547 m_selectionStartFrame = snapFrame;
Chris@17 548 if (m_manager) {
Chris@17 549 m_manager->setInProgressSelection(Selection(snapFrame,
Chris@17 550 snapFrame + resolution),
Chris@17 551 !m_ctrlPressed);
Chris@17 552 }
Chris@17 553
Chris@17 554 m_resizing = false;
Chris@17 555 }
Chris@17 556
Chris@17 557 update();
Chris@17 558
Chris@17 559 } else if (mode == ViewManager::DrawMode) {
Chris@17 560
Chris@13 561 Layer *layer = getSelectedLayer();
Chris@23 562 if (layer && layer->isLayerEditable()) {
Chris@44 563 layer->drawStart(this, e);
Chris@13 564 }
Chris@18 565
Chris@18 566 } else if (mode == ViewManager::EditMode) {
Chris@18 567
Chris@42 568 if (!editSelectionStart(e)) {
Chris@42 569 Layer *layer = getSelectedLayer();
Chris@42 570 if (layer && layer->isLayerEditable()) {
Chris@44 571 layer->editStart(this, e);
Chris@42 572 }
Chris@18 573 }
Chris@13 574 }
Chris@0 575
Chris@0 576 emit paneInteractedWith();
Chris@0 577 }
Chris@0 578
Chris@0 579 void
Chris@0 580 Pane::mouseReleaseEvent(QMouseEvent *e)
Chris@0 581 {
Chris@89 582 if (e->buttons() & Qt::RightButton) {
Chris@89 583 return;
Chris@89 584 }
Chris@89 585
Chris@13 586 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@13 587 if (m_manager) mode = m_manager->getToolMode();
Chris@13 588
Chris@0 589 if (m_clickedInRange) {
Chris@0 590 mouseMoveEvent(e);
Chris@0 591 }
Chris@0 592
Chris@17 593 if (m_navigating || mode == ViewManager::NavigateMode) {
Chris@17 594
Chris@17 595 m_navigating = false;
Chris@17 596
Chris@17 597 if (mode != ViewManager::NavigateMode) {
Chris@17 598 // restore cursor
Chris@17 599 toolModeChanged();
Chris@17 600 }
Chris@0 601
Chris@13 602 if (m_shiftPressed) {
Chris@0 603
Chris@13 604 int x0 = std::min(m_clickPos.x(), m_mousePos.x());
Chris@13 605 int x1 = std::max(m_clickPos.x(), m_mousePos.x());
Chris@13 606 int w = x1 - x0;
Chris@13 607
Chris@20 608 long newStartFrame = getFrameForX(x0);
Chris@13 609
Chris@20 610 long visibleFrames = getEndFrame() - getStartFrame();
Chris@20 611 if (newStartFrame <= -visibleFrames) {
Chris@20 612 newStartFrame = -visibleFrames + 1;
Chris@13 613 }
Chris@13 614
Chris@13 615 if (newStartFrame >= long(getModelsEndFrame())) {
Chris@13 616 newStartFrame = getModelsEndFrame() - 1;
Chris@13 617 }
Chris@13 618
Chris@13 619 float ratio = float(w) / float(width());
Chris@13 620 // std::cerr << "ratio: " << ratio << std::endl;
Chris@13 621 size_t newZoomLevel = (size_t)nearbyint(m_zoomLevel * ratio);
Chris@13 622 if (newZoomLevel < 1) newZoomLevel = 1;
Chris@13 623
Chris@13 624 // std::cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << std::endl;
Chris@13 625 setZoomLevel(getZoomConstraintBlockSize(newZoomLevel));
Chris@13 626 setStartFrame(newStartFrame);
Chris@13 627
Chris@13 628 //cerr << "mouseReleaseEvent: start frame now " << m_startFrame << endl;
Chris@13 629 // update();
Chris@0 630 }
Chris@0 631
Chris@13 632 } else if (mode == ViewManager::SelectMode) {
Chris@13 633
Chris@13 634 if (m_manager && m_manager->haveInProgressSelection()) {
Chris@13 635
Chris@13 636 bool exclusive;
Chris@13 637 Selection selection = m_manager->getInProgressSelection(exclusive);
Chris@13 638
Chris@13 639 if (selection.getEndFrame() < selection.getStartFrame() + 2) {
Chris@13 640 selection = Selection();
Chris@13 641 }
Chris@13 642
Chris@13 643 m_manager->clearInProgressSelection();
Chris@13 644
Chris@13 645 if (exclusive) {
Chris@13 646 m_manager->setSelection(selection);
Chris@13 647 } else {
Chris@13 648 m_manager->addSelection(selection);
Chris@13 649 }
Chris@0 650 }
Chris@13 651
Chris@13 652 update();
Chris@17 653
Chris@17 654 } else if (mode == ViewManager::DrawMode) {
Chris@17 655
Chris@17 656 Layer *layer = getSelectedLayer();
Chris@23 657 if (layer && layer->isLayerEditable()) {
Chris@44 658 layer->drawEnd(this, e);
Chris@17 659 update();
Chris@17 660 }
Chris@18 661
Chris@18 662 } else if (mode == ViewManager::EditMode) {
Chris@18 663
Chris@42 664 if (!editSelectionEnd(e)) {
Chris@42 665 Layer *layer = getSelectedLayer();
Chris@42 666 if (layer && layer->isLayerEditable()) {
Chris@44 667 layer->editEnd(this, e);
Chris@42 668 update();
Chris@42 669 }
Chris@18 670 }
Chris@17 671 }
Chris@0 672
Chris@0 673 m_clickedInRange = false;
Chris@0 674
Chris@0 675 emit paneInteractedWith();
Chris@0 676 }
Chris@0 677
Chris@0 678 void
Chris@0 679 Pane::mouseMoveEvent(QMouseEvent *e)
Chris@0 680 {
Chris@89 681 if (e->buttons() & Qt::RightButton) {
Chris@89 682 return;
Chris@89 683 }
Chris@89 684
Chris@13 685 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@13 686 if (m_manager) mode = m_manager->getToolMode();
Chris@13 687
Chris@28 688 QPoint prevPoint = m_identifyPoint;
Chris@28 689 m_identifyPoint = e->pos();
Chris@28 690
Chris@0 691 if (!m_clickedInRange) {
Chris@0 692
Chris@17 693 if (mode == ViewManager::SelectMode) {
Chris@17 694 bool closeToLeft = false, closeToRight = false;
Chris@17 695 getSelectionAt(e->x(), closeToLeft, closeToRight);
Chris@17 696 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
Chris@17 697 setCursor(Qt::SizeHorCursor);
Chris@17 698 } else {
Chris@17 699 setCursor(Qt::ArrowCursor);
Chris@17 700 }
Chris@17 701 }
Chris@0 702
Chris@35 703 //!!! if (mode != ViewManager::DrawMode) {
Chris@0 704
Chris@67 705 if (!m_manager->isPlaying()) {
Chris@67 706
Chris@55 707 if (getSelectedLayer()) {
Chris@55 708
Chris@17 709 bool previouslyIdentifying = m_identifyFeatures;
Chris@17 710 m_identifyFeatures = true;
Chris@17 711
Chris@17 712 if (m_identifyFeatures != previouslyIdentifying ||
Chris@17 713 m_identifyPoint != prevPoint) {
Chris@17 714 update();
Chris@17 715 }
Chris@55 716 }
Chris@55 717
Chris@67 718 }
Chris@67 719
Chris@35 720 // }
Chris@0 721
Chris@13 722 return;
Chris@13 723 }
Chris@0 724
Chris@17 725 if (m_navigating || mode == ViewManager::NavigateMode) {
Chris@0 726
Chris@13 727 if (m_shiftPressed) {
Chris@0 728
Chris@13 729 m_mousePos = e->pos();
Chris@13 730 update();
Chris@0 731
Chris@0 732 } else {
Chris@13 733
Chris@20 734 long frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x());
Chris@20 735
Chris@13 736 size_t newCentreFrame = m_dragCentreFrame;
Chris@13 737
Chris@13 738 if (frameOff < 0) {
Chris@13 739 newCentreFrame -= frameOff;
Chris@13 740 } else if (newCentreFrame >= size_t(frameOff)) {
Chris@13 741 newCentreFrame -= frameOff;
Chris@13 742 } else {
Chris@13 743 newCentreFrame = 0;
Chris@13 744 }
Chris@13 745
Chris@13 746 if (newCentreFrame >= getModelsEndFrame()) {
Chris@13 747 newCentreFrame = getModelsEndFrame();
Chris@13 748 if (newCentreFrame > 0) --newCentreFrame;
Chris@13 749 }
Chris@20 750
Chris@20 751 if (getXForFrame(m_centreFrame) != getXForFrame(newCentreFrame)) {
Chris@13 752 setCentreFrame(newCentreFrame);
Chris@13 753 }
Chris@0 754 }
Chris@0 755
Chris@13 756 } else if (mode == ViewManager::SelectMode) {
Chris@13 757
Chris@20 758 int mouseFrame = getFrameForX(e->x());
Chris@13 759 size_t resolution = 1;
Chris@13 760 int snapFrameLeft = mouseFrame;
Chris@13 761 int snapFrameRight = mouseFrame;
Chris@13 762
Chris@13 763 Layer *layer = getSelectedLayer();
Chris@82 764 if (layer && !m_shiftPressed) {
Chris@44 765 layer->snapToFeatureFrame(this, snapFrameLeft,
Chris@44 766 resolution, Layer::SnapLeft);
Chris@44 767 layer->snapToFeatureFrame(this, snapFrameRight,
Chris@44 768 resolution, Layer::SnapRight);
Chris@13 769 }
Chris@13 770
Chris@37 771 // std::cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << std::endl;
Chris@28 772
Chris@13 773 if (snapFrameLeft < 0) snapFrameLeft = 0;
Chris@13 774 if (snapFrameRight < 0) snapFrameRight = 0;
Chris@13 775
Chris@13 776 size_t min, max;
Chris@13 777
Chris@13 778 if (m_selectionStartFrame > snapFrameLeft) {
Chris@13 779 min = snapFrameLeft;
Chris@13 780 max = m_selectionStartFrame;
Chris@13 781 } else if (snapFrameRight > m_selectionStartFrame) {
Chris@13 782 min = m_selectionStartFrame;
Chris@13 783 max = snapFrameRight;
Chris@13 784 } else {
Chris@13 785 min = snapFrameLeft;
Chris@13 786 max = snapFrameRight;
Chris@0 787 }
Chris@0 788
Chris@13 789 if (m_manager) {
Chris@13 790 m_manager->setInProgressSelection(Selection(min, max),
Chris@17 791 !m_resizing && !m_ctrlPressed);
Chris@0 792 }
Chris@15 793
Chris@15 794 bool doScroll = false;
Chris@15 795 if (!m_manager) doScroll = true;
Chris@15 796 if (!m_manager->isPlaying()) doScroll = true;
Chris@15 797 if (m_followPlay != PlaybackScrollContinuous) doScroll = true;
Chris@15 798
Chris@15 799 if (doScroll) {
Chris@13 800 int offset = mouseFrame - getStartFrame();
Chris@13 801 int available = getEndFrame() - getStartFrame();
Chris@15 802 if (offset >= available * 0.95) {
Chris@15 803 int move = int(offset - available * 0.95) + 1;
Chris@14 804 setCentreFrame(m_centreFrame + move);
Chris@15 805 } else if (offset <= available * 0.10) {
Chris@15 806 int move = int(available * 0.10 - offset) + 1;
Chris@14 807 if (m_centreFrame > move) {
Chris@14 808 setCentreFrame(m_centreFrame - move);
Chris@14 809 } else {
Chris@14 810 setCentreFrame(0);
Chris@14 811 }
Chris@13 812 }
Chris@13 813 }
Chris@13 814
Chris@13 815 update();
Chris@17 816
Chris@17 817 } else if (mode == ViewManager::DrawMode) {
Chris@17 818
Chris@17 819 Layer *layer = getSelectedLayer();
Chris@23 820 if (layer && layer->isLayerEditable()) {
Chris@44 821 layer->drawDrag(this, e);
Chris@17 822 }
Chris@18 823
Chris@18 824 } else if (mode == ViewManager::EditMode) {
Chris@18 825
Chris@42 826 if (!editSelectionDrag(e)) {
Chris@42 827 Layer *layer = getSelectedLayer();
Chris@42 828 if (layer && layer->isLayerEditable()) {
Chris@44 829 layer->editDrag(this, e);
Chris@42 830 }
Chris@18 831 }
Chris@0 832 }
Chris@0 833 }
Chris@0 834
Chris@0 835 void
Chris@0 836 Pane::mouseDoubleClickEvent(QMouseEvent *e)
Chris@0 837 {
Chris@89 838 if (e->buttons() & Qt::RightButton) {
Chris@89 839 return;
Chris@89 840 }
Chris@89 841
Chris@101 842 // std::cerr << "mouseDoubleClickEvent" << std::endl;
Chris@36 843
Chris@36 844 m_clickPos = e->pos();
Chris@36 845 m_clickedInRange = true;
Chris@36 846 m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
Chris@36 847 m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
Chris@36 848
Chris@36 849 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@36 850 if (m_manager) mode = m_manager->getToolMode();
Chris@36 851
Chris@101 852 if (mode == ViewManager::NavigateMode ||
Chris@101 853 mode == ViewManager::EditMode) {
Chris@36 854
Chris@36 855 Layer *layer = getSelectedLayer();
Chris@36 856 if (layer && layer->isLayerEditable()) {
Chris@44 857 layer->editOpen(this, e);
Chris@36 858 }
Chris@36 859 }
Chris@0 860 }
Chris@0 861
Chris@0 862 void
Chris@0 863 Pane::leaveEvent(QEvent *)
Chris@0 864 {
Chris@0 865 bool previouslyIdentifying = m_identifyFeatures;
Chris@0 866 m_identifyFeatures = false;
Chris@0 867 if (previouslyIdentifying) update();
Chris@0 868 }
Chris@0 869
Chris@0 870 void
Chris@0 871 Pane::wheelEvent(QWheelEvent *e)
Chris@0 872 {
Chris@0 873 //std::cerr << "wheelEvent, delta " << e->delta() << std::endl;
Chris@0 874
Chris@0 875 int count = e->delta();
Chris@0 876
Chris@0 877 if (count > 0) {
Chris@0 878 if (count >= 120) count /= 120;
Chris@0 879 else count = 1;
Chris@0 880 }
Chris@0 881
Chris@0 882 if (count < 0) {
Chris@0 883 if (count <= -120) count /= 120;
Chris@0 884 else count = -1;
Chris@0 885 }
Chris@17 886
Chris@17 887 if (e->modifiers() & Qt::ControlModifier) {
Chris@17 888
Chris@20 889 // Scroll left or right, rapidly
Chris@20 890
Chris@17 891 if (getStartFrame() < 0 &&
Chris@17 892 getEndFrame() >= getModelsEndFrame()) return;
Chris@17 893
Chris@17 894 long delta = ((width() / 2) * count * m_zoomLevel);
Chris@17 895
Chris@17 896 if (int(m_centreFrame) < delta) {
Chris@17 897 setCentreFrame(0);
Chris@17 898 } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) {
Chris@17 899 setCentreFrame(getModelsEndFrame());
Chris@17 900 } else {
Chris@17 901 setCentreFrame(m_centreFrame - delta);
Chris@17 902 }
Chris@17 903
Chris@17 904 } else {
Chris@17 905
Chris@20 906 // Zoom in or out
Chris@20 907
Chris@17 908 int newZoomLevel = m_zoomLevel;
Chris@0 909
Chris@17 910 while (count > 0) {
Chris@17 911 if (newZoomLevel <= 2) {
Chris@17 912 newZoomLevel = 1;
Chris@17 913 break;
Chris@17 914 }
Chris@17 915 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1,
Chris@17 916 ZoomConstraint::RoundDown);
Chris@17 917 --count;
Chris@0 918 }
Chris@17 919
Chris@17 920 while (count < 0) {
Chris@17 921 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
Chris@17 922 ZoomConstraint::RoundUp);
Chris@17 923 ++count;
Chris@17 924 }
Chris@17 925
Chris@17 926 if (newZoomLevel != m_zoomLevel) {
Chris@17 927 setZoomLevel(newZoomLevel);
Chris@17 928 }
Chris@0 929 }
Chris@0 930
Chris@0 931 emit paneInteractedWith();
Chris@0 932 }
Chris@8 933
Chris@42 934 bool
Chris@42 935 Pane::editSelectionStart(QMouseEvent *e)
Chris@42 936 {
Chris@43 937 if (!m_identifyFeatures ||
Chris@43 938 !m_manager ||
Chris@43 939 m_manager->getToolMode() != ViewManager::EditMode) {
Chris@43 940 return false;
Chris@43 941 }
Chris@43 942
Chris@42 943 bool closeToLeft, closeToRight;
Chris@42 944 Selection s(getSelectionAt(e->x(), closeToLeft, closeToRight));
Chris@42 945 if (s.isEmpty()) return false;
Chris@42 946 m_editingSelection = s;
Chris@42 947 m_editingSelectionEdge = (closeToLeft ? -1 : closeToRight ? 1 : 0);
Chris@42 948 m_mousePos = e->pos();
Chris@42 949 return true;
Chris@42 950 }
Chris@42 951
Chris@42 952 bool
Chris@42 953 Pane::editSelectionDrag(QMouseEvent *e)
Chris@42 954 {
Chris@42 955 if (m_editingSelection.isEmpty()) return false;
Chris@42 956 m_mousePos = e->pos();
Chris@42 957 update();
Chris@42 958 return true;
Chris@42 959 }
Chris@42 960
Chris@42 961 bool
Chris@42 962 Pane::editSelectionEnd(QMouseEvent *e)
Chris@42 963 {
Chris@42 964 if (m_editingSelection.isEmpty()) return false;
Chris@43 965
Chris@43 966 int offset = m_mousePos.x() - m_clickPos.x();
Chris@43 967 Layer *layer = getSelectedLayer();
Chris@43 968
Chris@43 969 if (offset == 0 || !layer) {
Chris@43 970 m_editingSelection = Selection();
Chris@43 971 return true;
Chris@43 972 }
Chris@43 973
Chris@43 974 int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset;
Chris@43 975 int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
Chris@43 976
Chris@43 977 long f0 = getFrameForX(p0);
Chris@43 978 long f1 = getFrameForX(p1);
Chris@43 979
Chris@43 980 Selection newSelection(f0, f1);
Chris@43 981
Chris@43 982 if (m_editingSelectionEdge == 0) {
Chris@43 983
Chris@99 984 CommandHistory::getInstance()->startCompoundOperation
Chris@99 985 (tr("Drag Selection"), true);
Chris@99 986
Chris@43 987 layer->moveSelection(m_editingSelection, f0);
Chris@43 988
Chris@43 989 } else {
Chris@43 990
Chris@99 991 CommandHistory::getInstance()->startCompoundOperation
Chris@99 992 (tr("Resize Selection"), true);
Chris@99 993
Chris@43 994 if (m_editingSelectionEdge < 0) {
Chris@43 995 f1 = m_editingSelection.getEndFrame();
Chris@43 996 } else {
Chris@43 997 f0 = m_editingSelection.getStartFrame();
Chris@43 998 }
Chris@43 999
Chris@43 1000 newSelection = Selection(f0, f1);
Chris@43 1001 layer->resizeSelection(m_editingSelection, newSelection);
Chris@43 1002 }
Chris@43 1003
Chris@43 1004 m_manager->removeSelection(m_editingSelection);
Chris@43 1005 m_manager->addSelection(newSelection);
Chris@43 1006
Chris@99 1007 CommandHistory::getInstance()->endCompoundOperation();
Chris@99 1008
Chris@42 1009 m_editingSelection = Selection();
Chris@42 1010 return true;
Chris@42 1011 }
Chris@42 1012
Chris@13 1013 void
Chris@13 1014 Pane::toolModeChanged()
Chris@13 1015 {
Chris@13 1016 ViewManager::ToolMode mode = m_manager->getToolMode();
Chris@101 1017 // std::cerr << "Pane::toolModeChanged(" << mode << ")" << std::endl;
Chris@13 1018
Chris@13 1019 switch (mode) {
Chris@13 1020
Chris@13 1021 case ViewManager::NavigateMode:
Chris@13 1022 setCursor(Qt::PointingHandCursor);
Chris@13 1023 break;
Chris@13 1024
Chris@13 1025 case ViewManager::SelectMode:
Chris@13 1026 setCursor(Qt::ArrowCursor);
Chris@13 1027 break;
Chris@13 1028
Chris@13 1029 case ViewManager::EditMode:
Chris@19 1030 setCursor(Qt::UpArrowCursor);
Chris@13 1031 break;
Chris@13 1032
Chris@13 1033 case ViewManager::DrawMode:
Chris@13 1034 setCursor(Qt::CrossCursor);
Chris@13 1035 break;
Chris@36 1036 /*
Chris@13 1037 case ViewManager::TextMode:
Chris@13 1038 setCursor(Qt::IBeamCursor);
Chris@13 1039 break;
Chris@36 1040 */
Chris@13 1041 }
Chris@13 1042 }
Chris@13 1043
Chris@8 1044 QString
Chris@8 1045 Pane::toXmlString(QString indent, QString extraAttributes) const
Chris@8 1046 {
Chris@8 1047 return View::toXmlString
Chris@8 1048 (indent,
Chris@55 1049 QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3")
Chris@55 1050 .arg(m_centreLineVisible).arg(height()).arg(extraAttributes));
Chris@8 1051 }
Chris@8 1052
Chris@0 1053
Chris@0 1054 #ifdef INCLUDE_MOCFILES
Chris@0 1055 #include "Pane.moc.cpp"
Chris@0 1056 #endif
Chris@0 1057