annotate widgets/Pane.cpp @ 77:fd348f36c0d3

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