annotate view/Pane.cpp @ 378:22b72f0f6a4e

* More work to abstract out interactive components used in the data library, so that it does not need to depend on QtGui.
author Chris Cannam
date Fri, 14 Mar 2008 17:14:21 +0000
parents e1a9e478b7f2
children 06b5f110c5d2
rev   line source
Chris@127 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@127 2
Chris@127 3 /*
Chris@127 4 Sonic Visualiser
Chris@127 5 An audio file viewer and annotation editor.
Chris@127 6 Centre for Digital Music, Queen Mary, University of London.
Chris@264 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@127 8
Chris@127 9 This program is free software; you can redistribute it and/or
Chris@127 10 modify it under the terms of the GNU General Public License as
Chris@127 11 published by the Free Software Foundation; either version 2 of the
Chris@127 12 License, or (at your option) any later version. See the file
Chris@127 13 COPYING included with this distribution for more information.
Chris@127 14 */
Chris@127 15
Chris@128 16 #include "Pane.h"
Chris@128 17 #include "layer/Layer.h"
Chris@128 18 #include "data/model/Model.h"
Chris@127 19 #include "base/ZoomConstraint.h"
Chris@127 20 #include "base/RealTime.h"
Chris@127 21 #include "base/Profiler.h"
Chris@128 22 #include "ViewManager.h"
Chris@376 23 #include "widgets/CommandHistory.h"
Chris@376 24 #include "widgets/TextAbbrev.h"
Chris@338 25 #include "base/Preferences.h"
Chris@127 26 #include "layer/WaveformLayer.h"
Chris@127 27
Chris@326 28 //!!! ugh
Chris@326 29 #include "data/model/WaveFileModel.h"
Chris@326 30
Chris@127 31 #include <QPaintEvent>
Chris@127 32 #include <QPainter>
Chris@257 33 #include <QBitmap>
Chris@312 34 #include <QDragEnterEvent>
Chris@312 35 #include <QDropEvent>
Chris@257 36 #include <QCursor>
Chris@316 37 #include <QTextStream>
Chris@316 38
Chris@127 39 #include <iostream>
Chris@127 40 #include <cmath>
Chris@127 41
Chris@133 42 //!!! for HUD -- pull out into a separate class
Chris@133 43 #include <QFrame>
Chris@133 44 #include <QGridLayout>
Chris@133 45 #include <QPushButton>
Chris@133 46 #include "widgets/Thumbwheel.h"
Chris@172 47 #include "widgets/Panner.h"
Chris@188 48 #include "widgets/RangeInputDialog.h"
Chris@189 49 #include "widgets/NotifyingPushButton.h"
Chris@133 50
Chris@282 51 #include "widgets/KeyReference.h" //!!! should probably split KeyReference into a data class in base and another that shows the widget
Chris@282 52
Chris@363 53 //#define DEBUG_PANE
Chris@363 54
Chris@127 55 using std::cerr;
Chris@127 56 using std::endl;
Chris@127 57
Chris@267 58 QCursor *Pane::m_measureCursor1 = 0;
Chris@267 59 QCursor *Pane::m_measureCursor2 = 0;
Chris@262 60
Chris@127 61 Pane::Pane(QWidget *w) :
Chris@127 62 View(w, true),
Chris@127 63 m_identifyFeatures(false),
Chris@127 64 m_clickedInRange(false),
Chris@127 65 m_shiftPressed(false),
Chris@127 66 m_ctrlPressed(false),
Chris@127 67 m_navigating(false),
Chris@127 68 m_resizing(false),
Chris@343 69 m_editing(false),
Chris@343 70 m_releasing(false),
Chris@133 71 m_centreLineVisible(true),
Chris@222 72 m_scaleWidth(0),
Chris@237 73 m_headsUpDisplay(0),
Chris@237 74 m_vpan(0),
Chris@237 75 m_hthumb(0),
Chris@237 76 m_vthumb(0),
Chris@290 77 m_reset(0),
Chris@290 78 m_mouseInWidget(false)
Chris@127 79 {
Chris@127 80 setObjectName("Pane");
Chris@127 81 setMouseTracking(true);
Chris@312 82 setAcceptDrops(true);
Chris@133 83
Chris@133 84 updateHeadsUpDisplay();
Chris@133 85 }
Chris@133 86
Chris@133 87 void
Chris@133 88 Pane::updateHeadsUpDisplay()
Chris@133 89 {
Chris@187 90 Profiler profiler("Pane::updateHeadsUpDisplay", true);
Chris@187 91
Chris@192 92 if (!isVisible()) return;
Chris@192 93
Chris@132 94 /*
Chris@132 95 int count = 0;
Chris@132 96 int currentLevel = 1;
Chris@132 97 int level = 1;
Chris@132 98 while (true) {
Chris@132 99 if (getZoomLevel() == level) currentLevel = count;
Chris@132 100 int newLevel = getZoomConstraintBlockSize(level + 1,
Chris@132 101 ZoomConstraint::RoundUp);
Chris@132 102 if (newLevel == level) break;
Chris@132 103 if (newLevel == 131072) break; //!!! just because
Chris@132 104 level = newLevel;
Chris@132 105 ++count;
Chris@132 106 }
Chris@132 107
Chris@132 108 std::cerr << "Have " << count+1 << " zoom levels" << std::endl;
Chris@132 109 */
Chris@133 110
Chris@188 111 Layer *layer = 0;
Chris@188 112 if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
Chris@188 113
Chris@133 114 if (!m_headsUpDisplay) {
Chris@133 115
Chris@133 116 m_headsUpDisplay = new QFrame(this);
Chris@133 117
Chris@133 118 QGridLayout *layout = new QGridLayout;
Chris@133 119 layout->setMargin(0);
Chris@133 120 layout->setSpacing(0);
Chris@133 121 m_headsUpDisplay->setLayout(layout);
Chris@133 122
Chris@133 123 m_hthumb = new Thumbwheel(Qt::Horizontal);
Chris@187 124 m_hthumb->setObjectName(tr("Horizontal Zoom"));
Chris@260 125 m_hthumb->setCursor(Qt::ArrowCursor);
Chris@173 126 layout->addWidget(m_hthumb, 1, 0, 1, 2);
Chris@133 127 m_hthumb->setFixedWidth(70);
Chris@133 128 m_hthumb->setFixedHeight(16);
Chris@133 129 m_hthumb->setDefaultValue(0);
Chris@165 130 m_hthumb->setSpeed(0.6);
Chris@133 131 connect(m_hthumb, SIGNAL(valueChanged(int)), this,
Chris@133 132 SLOT(horizontalThumbwheelMoved(int)));
Chris@189 133 connect(m_hthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 134 connect(m_hthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@172 135
Chris@172 136 m_vpan = new Panner;
Chris@260 137 m_vpan->setCursor(Qt::ArrowCursor);
Chris@172 138 layout->addWidget(m_vpan, 0, 1);
Chris@173 139 m_vpan->setFixedWidth(12);
Chris@172 140 m_vpan->setFixedHeight(70);
Chris@174 141 m_vpan->setAlpha(80, 130);
Chris@174 142 connect(m_vpan, SIGNAL(rectExtentsChanged(float, float, float, float)),
Chris@174 143 this, SLOT(verticalPannerMoved(float, float, float, float)));
Chris@188 144 connect(m_vpan, SIGNAL(doubleClicked()),
Chris@188 145 this, SLOT(editVerticalPannerExtents()));
Chris@189 146 connect(m_vpan, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 147 connect(m_vpan, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@172 148
Chris@133 149 m_vthumb = new Thumbwheel(Qt::Vertical);
Chris@187 150 m_vthumb->setObjectName(tr("Vertical Zoom"));
Chris@260 151 m_vthumb->setCursor(Qt::ArrowCursor);
Chris@172 152 layout->addWidget(m_vthumb, 0, 2);
Chris@133 153 m_vthumb->setFixedWidth(16);
Chris@133 154 m_vthumb->setFixedHeight(70);
Chris@133 155 connect(m_vthumb, SIGNAL(valueChanged(int)), this,
Chris@133 156 SLOT(verticalThumbwheelMoved(int)));
Chris@189 157 connect(m_vthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 158 connect(m_vthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@133 159
Chris@188 160 if (layer) {
Chris@188 161 RangeMapper *rm = layer->getNewVerticalZoomRangeMapper();
Chris@188 162 if (rm) m_vthumb->setRangeMapper(rm);
Chris@188 163 }
Chris@188 164
Chris@189 165 m_reset = new NotifyingPushButton;
Chris@260 166 m_reset->setCursor(Qt::ArrowCursor);
Chris@189 167 m_reset->setFixedHeight(16);
Chris@189 168 m_reset->setFixedWidth(16);
Chris@189 169 layout->addWidget(m_reset, 1, 2);
Chris@189 170 connect(m_reset, SIGNAL(clicked()), m_hthumb, SLOT(resetToDefault()));
Chris@189 171 connect(m_reset, SIGNAL(clicked()), m_vthumb, SLOT(resetToDefault()));
Chris@189 172 connect(m_reset, SIGNAL(clicked()), m_vpan, SLOT(resetToDefault()));
Chris@189 173 connect(m_reset, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@189 174 connect(m_reset, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@133 175 }
Chris@133 176
Chris@133 177 int count = 0;
Chris@133 178 int current = 0;
Chris@133 179 int level = 1;
Chris@133 180
Chris@137 181 //!!! pull out into function (presumably in View)
Chris@137 182 bool haveConstraint = false;
Chris@137 183 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end();
Chris@137 184 ++i) {
Chris@137 185 if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) {
Chris@137 186 haveConstraint = true;
Chris@137 187 break;
Chris@137 188 }
Chris@137 189 }
Chris@137 190
Chris@137 191 if (haveConstraint) {
Chris@137 192 while (true) {
Chris@137 193 if (getZoomLevel() == level) current = count;
Chris@137 194 int newLevel = getZoomConstraintBlockSize(level + 1,
Chris@137 195 ZoomConstraint::RoundUp);
Chris@137 196 if (newLevel == level) break;
Chris@137 197 level = newLevel;
Chris@137 198 if (++count == 50) break;
Chris@137 199 }
Chris@137 200 } else {
Chris@137 201 // if we have no particular constraints, we can really spread out
Chris@137 202 while (true) {
Chris@137 203 if (getZoomLevel() >= level) current = count;
Chris@137 204 int step = level / 10;
Chris@137 205 int pwr = 0;
Chris@137 206 while (step > 0) {
Chris@137 207 ++pwr;
Chris@137 208 step /= 2;
Chris@137 209 }
Chris@137 210 step = 1;
Chris@137 211 while (pwr > 0) {
Chris@137 212 step *= 2;
Chris@137 213 --pwr;
Chris@137 214 }
Chris@154 215 // std::cerr << level << std::endl;
Chris@137 216 level += step;
Chris@137 217 if (++count == 100 || level > 262144) break;
Chris@137 218 }
Chris@133 219 }
Chris@133 220
Chris@133 221 // std::cerr << "Have " << count << " zoom levels" << std::endl;
Chris@133 222
Chris@133 223 m_hthumb->setMinimumValue(0);
Chris@133 224 m_hthumb->setMaximumValue(count);
Chris@133 225 m_hthumb->setValue(count - current);
Chris@133 226
Chris@133 227 // std::cerr << "set value to " << count-current << std::endl;
Chris@133 228
Chris@133 229 // std::cerr << "default value is " << m_hthumb->getDefaultValue() << std::endl;
Chris@133 230
Chris@133 231 if (count != 50 && m_hthumb->getDefaultValue() == 0) {
Chris@133 232 m_hthumb->setDefaultValue(count - current);
Chris@133 233 // std::cerr << "set default value to " << m_hthumb->getDefaultValue() << std::endl;
Chris@133 234 }
Chris@133 235
Chris@204 236 bool haveVThumb = false;
Chris@204 237
Chris@133 238 if (layer) {
Chris@133 239 int defaultStep = 0;
Chris@133 240 int max = layer->getVerticalZoomSteps(defaultStep);
Chris@133 241 if (max == 0) {
Chris@133 242 m_vthumb->hide();
Chris@133 243 } else {
Chris@204 244 haveVThumb = true;
Chris@133 245 m_vthumb->show();
Chris@187 246 m_vthumb->blockSignals(true);
Chris@133 247 m_vthumb->setMinimumValue(0);
Chris@133 248 m_vthumb->setMaximumValue(max);
Chris@133 249 m_vthumb->setDefaultValue(defaultStep);
Chris@133 250 m_vthumb->setValue(layer->getCurrentVerticalZoomStep());
Chris@187 251 m_vthumb->blockSignals(false);
Chris@135 252
Chris@205 253 // std::cerr << "Vertical thumbwheel: min 0, max " << max
Chris@205 254 // << ", default " << defaultStep << ", value "
Chris@205 255 // << m_vthumb->getValue() << std::endl;
Chris@135 256
Chris@133 257 }
Chris@133 258 }
Chris@133 259
Chris@174 260 updateVerticalPanner();
Chris@174 261
Chris@133 262 if (m_manager && m_manager->getZoomWheelsEnabled() &&
Chris@133 263 width() > 120 && height() > 100) {
Chris@165 264 if (!m_headsUpDisplay->isVisible()) {
Chris@165 265 m_headsUpDisplay->show();
Chris@165 266 }
Chris@204 267 if (haveVThumb) {
Chris@204 268 m_headsUpDisplay->setFixedHeight(m_vthumb->height() + m_hthumb->height());
Chris@133 269 m_headsUpDisplay->move(width() - 86, height() - 86);
Chris@133 270 } else {
Chris@204 271 m_headsUpDisplay->setFixedHeight(m_hthumb->height());
Chris@204 272 m_headsUpDisplay->move(width() - 86, height() - 16);
Chris@133 273 }
Chris@133 274 } else {
Chris@133 275 m_headsUpDisplay->hide();
Chris@133 276 }
Chris@127 277 }
Chris@127 278
Chris@174 279 void
Chris@174 280 Pane::updateVerticalPanner()
Chris@174 281 {
Chris@174 282 if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return;
Chris@174 283
Chris@208 284 // In principle we should show or hide the panner on the basis of
Chris@208 285 // whether the top layer has adjustable display extents, and we do
Chris@208 286 // that below. However, we have no basis for layout of the panner
Chris@208 287 // if the vertical scroll wheel is not also present. So if we
Chris@208 288 // have no vertical scroll wheel, we should remove the panner as
Chris@208 289 // well. Ideally any layer that implements display extents should
Chris@208 290 // implement vertical zoom steps as well, but they don't all at
Chris@208 291 // the moment.
Chris@208 292
Chris@208 293 Layer *layer = 0;
Chris@208 294 if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
Chris@208 295 int discard;
Chris@208 296 if (layer && layer->getVerticalZoomSteps(discard) == 0) {
Chris@208 297 m_vpan->hide();
Chris@208 298 return;
Chris@208 299 }
Chris@208 300
Chris@174 301 float vmin, vmax, dmin, dmax;
Chris@174 302 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax) && vmax != vmin) {
Chris@174 303 float y0 = (dmin - vmin) / (vmax - vmin);
Chris@174 304 float y1 = (dmax - vmin) / (vmax - vmin);
Chris@174 305 m_vpan->blockSignals(true);
Chris@174 306 m_vpan->setRectExtents(0, 1.0 - y1, 1, y1 - y0);
Chris@174 307 m_vpan->blockSignals(false);
Chris@174 308 m_vpan->show();
Chris@174 309 } else {
Chris@174 310 m_vpan->hide();
Chris@174 311 }
Chris@174 312 }
Chris@174 313
Chris@127 314 bool
Chris@127 315 Pane::shouldIlluminateLocalFeatures(const Layer *layer, QPoint &pos) const
Chris@127 316 {
Chris@127 317 QPoint discard;
Chris@127 318 bool b0, b1;
Chris@127 319
Chris@262 320 if (m_manager && m_manager->getToolMode() == ViewManager::MeasureMode) {
Chris@262 321 return false;
Chris@262 322 }
Chris@262 323
Chris@326 324 if (m_manager && !m_manager->shouldIlluminateLocalFeatures()) {
Chris@326 325 return false;
Chris@326 326 }
Chris@326 327
Chris@127 328 if (layer == getSelectedLayer() &&
Chris@127 329 !shouldIlluminateLocalSelection(discard, b0, b1)) {
Chris@127 330
Chris@127 331 pos = m_identifyPoint;
Chris@127 332 return m_identifyFeatures;
Chris@127 333 }
Chris@127 334
Chris@127 335 return false;
Chris@127 336 }
Chris@127 337
Chris@127 338 bool
Chris@127 339 Pane::shouldIlluminateLocalSelection(QPoint &pos,
Chris@127 340 bool &closeToLeft,
Chris@127 341 bool &closeToRight) const
Chris@127 342 {
Chris@127 343 if (m_identifyFeatures &&
Chris@127 344 m_manager &&
Chris@127 345 m_manager->getToolMode() == ViewManager::EditMode &&
Chris@127 346 !m_manager->getSelections().empty() &&
Chris@127 347 !selectionIsBeingEdited()) {
Chris@127 348
Chris@127 349 Selection s(getSelectionAt(m_identifyPoint.x(),
Chris@127 350 closeToLeft, closeToRight));
Chris@127 351
Chris@127 352 if (!s.isEmpty()) {
Chris@127 353 if (getSelectedLayer() && getSelectedLayer()->isLayerEditable()) {
Chris@127 354
Chris@127 355 pos = m_identifyPoint;
Chris@127 356 return true;
Chris@127 357 }
Chris@127 358 }
Chris@127 359 }
Chris@127 360
Chris@127 361 return false;
Chris@127 362 }
Chris@127 363
Chris@127 364 bool
Chris@127 365 Pane::selectionIsBeingEdited() const
Chris@127 366 {
Chris@127 367 if (!m_editingSelection.isEmpty()) {
Chris@127 368 if (m_mousePos != m_clickPos &&
Chris@127 369 getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) {
Chris@127 370 return true;
Chris@127 371 }
Chris@127 372 }
Chris@127 373 return false;
Chris@127 374 }
Chris@127 375
Chris@127 376 void
Chris@127 377 Pane::setCentreLineVisible(bool visible)
Chris@127 378 {
Chris@127 379 m_centreLineVisible = visible;
Chris@127 380 update();
Chris@127 381 }
Chris@127 382
Chris@127 383 void
Chris@127 384 Pane::paintEvent(QPaintEvent *e)
Chris@127 385 {
Chris@127 386 // Profiler profiler("Pane::paintEvent", true);
Chris@127 387
Chris@127 388 QPainter paint;
Chris@127 389
Chris@127 390 QRect r(rect());
Chris@261 391 if (e) r = e->rect();
Chris@127 392
Chris@127 393 View::paintEvent(e);
Chris@127 394
Chris@127 395 paint.begin(this);
Chris@339 396 setPaintFont(paint);
Chris@338 397
Chris@261 398 if (e) paint.setClipRect(r);
Chris@127 399
Chris@259 400 ViewManager::ToolMode toolMode = m_manager->getToolMode();
Chris@259 401
Chris@127 402 if (m_manager &&
Chris@290 403 // !m_manager->isPlaying() &&
Chris@290 404 m_mouseInWidget &&
Chris@259 405 toolMode == ViewManager::MeasureMode) {
Chris@127 406
Chris@127 407 for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
Chris@127 408 --vi;
Chris@127 409
Chris@127 410 std::vector<QRect> crosshairExtents;
Chris@127 411
Chris@127 412 if ((*vi)->getCrosshairExtents(this, paint, m_identifyPoint,
Chris@127 413 crosshairExtents)) {
Chris@127 414 (*vi)->paintCrosshairs(this, paint, m_identifyPoint);
Chris@127 415 break;
Chris@127 416 } else if ((*vi)->isLayerOpaque()) {
Chris@127 417 break;
Chris@127 418 }
Chris@127 419 }
Chris@127 420 }
Chris@127 421
Chris@268 422 Layer *topLayer = getTopLayer();
Chris@277 423 bool haveSomeTimeXAxis = false;
Chris@268 424
Chris@258 425 const Model *waveformModel = 0; // just for reporting purposes
Chris@326 426 const Model *workModel = 0;
Chris@326 427
Chris@127 428 for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
Chris@127 429 --vi;
Chris@277 430 if (!haveSomeTimeXAxis && (*vi)->hasTimeXAxis()) {
Chris@277 431 haveSomeTimeXAxis = true;
Chris@277 432 }
Chris@127 433 if (dynamic_cast<WaveformLayer *>(*vi)) {
Chris@127 434 waveformModel = (*vi)->getModel();
Chris@326 435 workModel = waveformModel;
Chris@326 436 } else {
Chris@326 437 Model *m = (*vi)->getModel();
Chris@326 438 if (dynamic_cast<WaveFileModel *>(m)) {
Chris@326 439 workModel = m;
Chris@326 440 } else if (m && dynamic_cast<WaveFileModel *>(m->getSourceModel())) {
Chris@326 441 workModel = m->getSourceModel();
Chris@326 442 }
Chris@127 443 }
Chris@326 444
Chris@326 445 if (waveformModel && workModel && haveSomeTimeXAxis) break;
Chris@258 446 }
Chris@127 447
Chris@261 448 m_scaleWidth = 0;
Chris@261 449
Chris@261 450 if (m_manager && m_manager->shouldShowVerticalScale() && topLayer) {
Chris@261 451 drawVerticalScale(r, topLayer, paint);
Chris@261 452 }
Chris@261 453
Chris@326 454 if (m_identifyFeatures &&
Chris@326 455 m_manager && m_manager->shouldIlluminateLocalFeatures() &&
Chris@326 456 topLayer) {
Chris@261 457 drawFeatureDescription(topLayer, paint);
Chris@261 458 }
Chris@261 459
Chris@261 460 int sampleRate = getModelsSampleRate();
Chris@261 461 paint.setBrush(Qt::NoBrush);
Chris@261 462
Chris@261 463 if (m_centreLineVisible &&
Chris@261 464 m_manager &&
Chris@261 465 m_manager->shouldShowCentreLine()) {
Chris@277 466 drawCentreLine(sampleRate, paint, !haveSomeTimeXAxis);
Chris@261 467 }
Chris@261 468
Chris@261 469 paint.setPen(QColor(50, 50, 50));
Chris@261 470
Chris@261 471 if (waveformModel &&
Chris@261 472 m_manager &&
Chris@261 473 m_manager->shouldShowDuration()) {
Chris@261 474 drawDurationAndRate(r, waveformModel, sampleRate, paint);
Chris@261 475 }
Chris@261 476
Chris@326 477 bool haveWorkTitle = false;
Chris@326 478
Chris@326 479 if (workModel &&
Chris@326 480 m_manager &&
Chris@326 481 m_manager->shouldShowWorkTitle()) {
Chris@326 482 drawWorkTitle(r, paint, workModel);
Chris@326 483 haveWorkTitle = true;
Chris@326 484 }
Chris@326 485
Chris@326 486 if (workModel &&
Chris@320 487 m_manager &&
Chris@320 488 m_manager->getAlignMode()) {
Chris@326 489 drawAlignmentStatus(r, paint, workModel, haveWorkTitle);
Chris@320 490 }
Chris@320 491
Chris@261 492 if (m_manager &&
Chris@261 493 m_manager->shouldShowLayerNames()) {
Chris@261 494 drawLayerNames(r, paint);
Chris@261 495 }
Chris@261 496
Chris@262 497 if (m_shiftPressed && m_clickedInRange &&
Chris@283 498 (toolMode == ViewManager::NavigateMode || m_navigating)) {
Chris@261 499
Chris@261 500 //!!! be nice if this looked a bit more in keeping with the
Chris@261 501 //selection block
Chris@262 502
Chris@262 503 paint.setPen(Qt::blue);
Chris@262 504 //!!! shouldn't use clickPos -- needs to use a clicked frame
Chris@262 505 paint.drawRect(m_clickPos.x(), m_clickPos.y(),
Chris@262 506 m_mousePos.x() - m_clickPos.x(),
Chris@262 507 m_mousePos.y() - m_clickPos.y());
Chris@261 508
Chris@262 509 }
Chris@261 510
Chris@266 511 if (toolMode == ViewManager::MeasureMode && topLayer) {
Chris@272 512 bool showFocus = false;
Chris@272 513 if (!m_manager || !m_manager->isPlaying()) showFocus = true;
Chris@272 514 topLayer->paintMeasurementRects(this, paint, showFocus, m_identifyPoint);
Chris@261 515 }
Chris@261 516
Chris@261 517 if (selectionIsBeingEdited()) {
Chris@261 518 drawEditingSelection(paint);
Chris@261 519 }
Chris@261 520
Chris@261 521 paint.end();
Chris@261 522 }
Chris@261 523
Chris@276 524 size_t
Chris@276 525 Pane::getVerticalScaleWidth() const
Chris@276 526 {
Chris@276 527 if (m_scaleWidth > 0) return m_scaleWidth;
Chris@276 528 else return 0;
Chris@276 529 }
Chris@276 530
Chris@261 531 void
Chris@261 532 Pane::drawVerticalScale(QRect r, Layer *topLayer, QPainter &paint)
Chris@261 533 {
Chris@258 534 Layer *scaleLayer = 0;
Chris@258 535
Chris@261 536 float min, max;
Chris@261 537 bool log;
Chris@261 538 QString unit;
Chris@258 539
Chris@261 540 // If the top layer has no scale and reports no display
Chris@261 541 // extents, but does report a unit, then the scale should be
Chris@261 542 // drawn from any underlying layer with a scale and that unit.
Chris@261 543 // If the top layer has no scale and no value extents at all,
Chris@261 544 // then the scale should be drawn from any underlying layer
Chris@261 545 // with a scale regardless of unit.
Chris@258 546
Chris@261 547 int sw = topLayer->getVerticalScaleWidth(this, paint);
Chris@258 548
Chris@261 549 if (sw > 0) {
Chris@261 550 scaleLayer = topLayer;
Chris@261 551 m_scaleWidth = sw;
Chris@258 552
Chris@261 553 } else {
Chris@258 554
Chris@261 555 bool hasDisplayExtents = topLayer->getDisplayExtents(min, max);
Chris@261 556 bool hasValueExtents = topLayer->getValueExtents(min, max, log, unit);
Chris@261 557
Chris@261 558 if (!hasDisplayExtents) {
Chris@258 559
Chris@261 560 if (!hasValueExtents) {
Chris@258 561
Chris@261 562 for (LayerList::iterator vi = m_layers.end();
Chris@261 563 vi != m_layers.begin(); ) {
Chris@261 564
Chris@261 565 --vi;
Chris@261 566
Chris@261 567 if ((*vi) == topLayer) continue;
Chris@261 568
Chris@261 569 sw = (*vi)->getVerticalScaleWidth(this, paint);
Chris@261 570
Chris@261 571 if (sw > 0) {
Chris@261 572 scaleLayer = *vi;
Chris@261 573 m_scaleWidth = sw;
Chris@261 574 break;
Chris@261 575 }
Chris@261 576 }
Chris@261 577 } else if (unit != "") { // && hasValueExtents && !hasDisplayExtents
Chris@258 578
Chris@261 579 QString requireUnit = unit;
Chris@261 580
Chris@261 581 for (LayerList::iterator vi = m_layers.end();
Chris@261 582 vi != m_layers.begin(); ) {
Chris@258 583
Chris@261 584 --vi;
Chris@258 585
Chris@261 586 if ((*vi) == topLayer) continue;
Chris@258 587
Chris@261 588 if ((*vi)->getDisplayExtents(min, max)) {
Chris@261 589
Chris@261 590 // search no further than this: if the
Chris@261 591 // scale from this layer isn't suitable,
Chris@261 592 // we'll have to draw no scale (else we'd
Chris@261 593 // risk ending up with the wrong scale)
Chris@261 594
Chris@261 595 if ((*vi)->getValueExtents(min, max, log, unit) &&
Chris@261 596 unit == requireUnit) {
Chris@261 597
Chris@261 598 sw = (*vi)->getVerticalScaleWidth(this, paint);
Chris@261 599 if (sw > 0) {
Chris@261 600 scaleLayer = *vi;
Chris@261 601 m_scaleWidth = sw;
Chris@261 602 }
Chris@258 603 }
Chris@261 604 break;
Chris@258 605 }
Chris@258 606 }
Chris@258 607 }
Chris@127 608 }
Chris@258 609 }
Chris@127 610
Chris@258 611 if (!scaleLayer) m_scaleWidth = 0;
Chris@258 612
Chris@258 613 if (m_scaleWidth > 0 && r.left() < m_scaleWidth) {
Chris@127 614
Chris@127 615 // Profiler profiler("Pane::paintEvent - painting vertical scale", true);
Chris@127 616
Chris@127 617 // std::cerr << "Pane::paintEvent: calling paint.save() in vertical scale block" << std::endl;
Chris@258 618 paint.save();
Chris@258 619
Chris@287 620 paint.setPen(getForeground());
Chris@287 621 paint.setBrush(getBackground());
Chris@258 622 paint.drawRect(0, -1, m_scaleWidth, height()+1);
Chris@258 623
Chris@258 624 paint.setBrush(Qt::NoBrush);
Chris@258 625 scaleLayer->paintVerticalScale
Chris@258 626 (this, paint, QRect(0, 0, m_scaleWidth, height()));
Chris@258 627
Chris@258 628 paint.restore();
Chris@258 629 }
Chris@261 630 }
Chris@261 631
Chris@261 632 void
Chris@261 633 Pane::drawFeatureDescription(Layer *topLayer, QPainter &paint)
Chris@261 634 {
Chris@261 635 QPoint pos = m_identifyPoint;
Chris@261 636 QString desc = topLayer->getFeatureDescription(this, pos);
Chris@261 637
Chris@261 638 if (desc != "") {
Chris@261 639
Chris@261 640 paint.save();
Chris@261 641
Chris@261 642 int tabStop =
Chris@261 643 paint.fontMetrics().width(tr("Some lengthy prefix:"));
Chris@261 644
Chris@261 645 QRect boundingRect =
Chris@261 646 paint.fontMetrics().boundingRect
Chris@261 647 (rect(),
Chris@261 648 Qt::AlignRight | Qt::AlignTop | Qt::TextExpandTabs,
Chris@261 649 desc, tabStop);
Chris@261 650
Chris@261 651 if (hasLightBackground()) {
Chris@261 652 paint.setPen(Qt::NoPen);
Chris@261 653 paint.setBrush(QColor(250, 250, 250, 200));
Chris@261 654 } else {
Chris@261 655 paint.setPen(Qt::NoPen);
Chris@261 656 paint.setBrush(QColor(50, 50, 50, 200));
Chris@261 657 }
Chris@261 658
Chris@261 659 int extra = paint.fontMetrics().descent();
Chris@261 660 paint.drawRect(width() - boundingRect.width() - 10 - extra,
Chris@261 661 10 - extra,
Chris@261 662 boundingRect.width() + 2 * extra,
Chris@261 663 boundingRect.height() + extra);
Chris@261 664
Chris@261 665 if (hasLightBackground()) {
Chris@261 666 paint.setPen(QColor(150, 20, 0));
Chris@261 667 } else {
Chris@261 668 paint.setPen(QColor(255, 150, 100));
Chris@261 669 }
Chris@261 670
Chris@261 671 QTextOption option;
Chris@261 672 option.setWrapMode(QTextOption::NoWrap);
Chris@261 673 option.setAlignment(Qt::AlignRight | Qt::AlignTop);
Chris@261 674 option.setTabStop(tabStop);
Chris@261 675 paint.drawText(QRectF(width() - boundingRect.width() - 10, 10,
Chris@261 676 boundingRect.width(),
Chris@261 677 boundingRect.height()),
Chris@261 678 desc,
Chris@261 679 option);
Chris@261 680
Chris@261 681 paint.restore();
Chris@261 682 }
Chris@261 683 }
Chris@258 684
Chris@261 685 void
Chris@277 686 Pane::drawCentreLine(int sampleRate, QPainter &paint, bool omitLine)
Chris@261 687 {
Chris@261 688 int fontHeight = paint.fontMetrics().height();
Chris@261 689 int fontAscent = paint.fontMetrics().ascent();
Chris@261 690
Chris@261 691 QColor c = QColor(0, 0, 0);
Chris@261 692 if (!hasLightBackground()) {
Chris@261 693 c = QColor(240, 240, 240);
Chris@261 694 }
Chris@277 695
Chris@261 696 paint.setPen(c);
Chris@274 697 int x = width() / 2;
Chris@277 698
Chris@277 699 if (!omitLine) {
Chris@277 700 paint.drawLine(x, 0, x, height() - 1);
Chris@277 701 paint.drawLine(x-1, 1, x+1, 1);
Chris@277 702 paint.drawLine(x-2, 0, x+2, 0);
Chris@277 703 paint.drawLine(x-1, height() - 2, x+1, height() - 2);
Chris@277 704 paint.drawLine(x-2, height() - 1, x+2, height() - 1);
Chris@277 705 }
Chris@261 706
Chris@261 707 paint.setPen(QColor(50, 50, 50));
Chris@261 708
Chris@261 709 int y = height() - fontHeight + fontAscent - 6;
Chris@261 710
Chris@261 711 LayerList::iterator vi = m_layers.end();
Chris@261 712
Chris@261 713 if (vi != m_layers.begin()) {
Chris@261 714
Chris@261 715 switch ((*--vi)->getPreferredFrameCountPosition()) {
Chris@258 716
Chris@261 717 case Layer::PositionTop:
Chris@261 718 y = fontAscent + 6;
Chris@261 719 break;
Chris@258 720
Chris@261 721 case Layer::PositionMiddle:
Chris@261 722 y = (height() - fontHeight) / 2
Chris@261 723 + fontAscent;
Chris@261 724 break;
Chris@127 725
Chris@261 726 case Layer::PositionBottom:
Chris@261 727 // y already set correctly
Chris@261 728 break;
Chris@127 729 }
Chris@127 730 }
Chris@127 731
Chris@261 732 if (m_manager && m_manager->shouldShowFrameCount()) {
Chris@261 733
Chris@261 734 if (sampleRate) {
Chris@127 735
Chris@261 736 QString text(QString::fromStdString
Chris@261 737 (RealTime::frame2RealTime
Chris@261 738 (m_centreFrame, sampleRate).toText(true)));
Chris@127 739
Chris@261 740 int tw = paint.fontMetrics().width(text);
Chris@261 741 int x = width()/2 - 4 - tw;
Chris@127 742
Chris@127 743 drawVisibleText(paint, x, y, text, OutlinedText);
Chris@127 744 }
Chris@261 745
Chris@261 746 QString text = QString("%1").arg(m_centreFrame);
Chris@261 747
Chris@261 748 int x = width()/2 + 4;
Chris@261 749
Chris@261 750 drawVisibleText(paint, x, y, text, OutlinedText);
Chris@261 751 }
Chris@261 752 }
Chris@127 753
Chris@261 754 void
Chris@326 755 Pane::drawAlignmentStatus(QRect r, QPainter &paint, const Model *model,
Chris@326 756 bool down)
Chris@320 757 {
Chris@320 758 const Model *reference = model->getAlignmentReference();
Chris@320 759 /*
Chris@320 760 if (!reference) {
Chris@320 761 std::cerr << "Pane[" << this << "]::drawAlignmentStatus: No reference" << std::endl;
Chris@320 762 } else if (reference == model) {
Chris@320 763 std::cerr << "Pane[" << this << "]::drawAlignmentStatus: This is the reference model" << std::endl;
Chris@320 764 } else {
Chris@320 765 std::cerr << "Pane[" << this << "]::drawAlignmentStatus: This is not the reference" << std::endl;
Chris@320 766 }
Chris@320 767 */
Chris@320 768 QString text;
Chris@320 769 int completion = 100;
Chris@320 770
Chris@320 771 if (reference == model) {
Chris@320 772 text = tr("Reference");
Chris@320 773 } else if (!reference) {
Chris@320 774 text = tr("Unaligned");
Chris@320 775 } else {
Chris@320 776 completion = model->getAlignmentCompletion();
Chris@320 777 if (completion == 0) {
Chris@320 778 text = tr("Unaligned");
Chris@320 779 } else if (completion < 100) {
Chris@320 780 text = tr("Aligning: %1%").arg(completion);
Chris@320 781 } else {
Chris@320 782 text = tr("Aligned");
Chris@320 783 }
Chris@320 784 }
Chris@320 785
Chris@320 786 paint.save();
Chris@320 787 QFont font(paint.font());
Chris@320 788 font.setBold(true);
Chris@320 789 paint.setFont(font);
Chris@326 790 if (completion < 100) paint.setBrush(Qt::red);
Chris@326 791
Chris@326 792 int y = 5;
Chris@326 793 if (down) y += paint.fontMetrics().height();
Chris@326 794 int w = paint.fontMetrics().width(text);
Chris@326 795 int h = paint.fontMetrics().height();
Chris@326 796 if (r.top() > h + y || r.left() > w + m_scaleWidth + 5) {
Chris@326 797 paint.restore();
Chris@326 798 return;
Chris@326 799 }
Chris@320 800
Chris@320 801 drawVisibleText(paint, m_scaleWidth + 5,
Chris@326 802 paint.fontMetrics().ascent() + y, text, OutlinedText);
Chris@320 803
Chris@320 804 paint.restore();
Chris@320 805 }
Chris@320 806
Chris@320 807 void
Chris@320 808 Pane::modelAlignmentCompletionChanged()
Chris@320 809 {
Chris@320 810 View::modelAlignmentCompletionChanged();
Chris@320 811 update(QRect(0, 0, 300, 100));
Chris@320 812 }
Chris@320 813
Chris@320 814 void
Chris@326 815 Pane::drawWorkTitle(QRect r, QPainter &paint, const Model *model)
Chris@326 816 {
Chris@326 817 QString title = model->getTitle();
Chris@326 818 QString maker = model->getMaker();
Chris@327 819 //std::cerr << "Pane::drawWorkTitle: title=\"" << title.toStdString()
Chris@327 820 //<< "\", maker=\"" << maker.toStdString() << "\"" << std::endl;
Chris@326 821 if (title == "") return;
Chris@326 822
Chris@326 823 QString text = title;
Chris@326 824 if (maker != "") {
Chris@326 825 text = tr("%1 - %2").arg(title).arg(maker);
Chris@326 826 }
Chris@326 827
Chris@326 828 paint.save();
Chris@326 829 QFont font(paint.font());
Chris@326 830 font.setItalic(true);
Chris@326 831 paint.setFont(font);
Chris@326 832
Chris@326 833 int y = 5;
Chris@326 834 int w = paint.fontMetrics().width(text);
Chris@326 835 int h = paint.fontMetrics().height();
Chris@326 836 if (r.top() > h + y || r.left() > w + m_scaleWidth + 5) {
Chris@326 837 paint.restore();
Chris@326 838 return;
Chris@326 839 }
Chris@326 840
Chris@326 841 drawVisibleText(paint, m_scaleWidth + 5,
Chris@326 842 paint.fontMetrics().ascent() + y, text, OutlinedText);
Chris@326 843
Chris@326 844 paint.restore();
Chris@326 845 }
Chris@326 846
Chris@326 847 void
Chris@261 848 Pane::drawLayerNames(QRect r, QPainter &paint)
Chris@261 849 {
Chris@261 850 int fontHeight = paint.fontMetrics().height();
Chris@261 851 int fontAscent = paint.fontMetrics().ascent();
Chris@127 852
Chris@300 853 int lly = height() - 6;
Chris@300 854 if (m_manager->getZoomWheelsEnabled()) {
Chris@300 855 lly -= 20;
Chris@300 856 }
Chris@300 857
Chris@300 858 if (r.y() + r.height() < lly - int(m_layers.size()) * fontHeight) {
Chris@261 859 return;
Chris@127 860 }
Chris@127 861
Chris@294 862 QStringList texts;
Chris@299 863 std::vector<QPixmap> pixmaps;
Chris@294 864 for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
Chris@294 865 texts.push_back((*i)->getLayerPresentationName());
Chris@299 866 // std::cerr << "Pane " << this << ": Layer presentation name for " << *i << ": "
Chris@299 867 // << texts[texts.size()-1].toStdString() << std::endl;
Chris@299 868 pixmaps.push_back((*i)->getLayerPresentationPixmap
Chris@299 869 (QSize(fontAscent, fontAscent)));
Chris@294 870 }
Chris@127 871
Chris@294 872 int maxTextWidth = width() / 3;
Chris@294 873 texts = TextAbbrev::abbreviate(texts, paint.fontMetrics(), maxTextWidth);
Chris@294 874
Chris@261 875 int llx = width() - maxTextWidth - 5;
Chris@261 876 if (m_manager->getZoomWheelsEnabled()) {
Chris@261 877 llx -= 36;
Chris@261 878 }
Chris@261 879
Chris@300 880 if (r.x() + r.width() >= llx - fontAscent - 3) {
Chris@261 881
Chris@261 882 for (size_t i = 0; i < texts.size(); ++i) {
Chris@299 883
Chris@299 884 // std::cerr << "Pane "<< this << ": text " << i << ": " << texts[i].toStdString() << std::endl;
Chris@261 885
Chris@261 886 if (i + 1 == texts.size()) {
Chris@287 887 paint.setPen(getForeground());
Chris@261 888 }
Chris@261 889
Chris@261 890 drawVisibleText(paint, llx,
Chris@261 891 lly - fontHeight + fontAscent,
Chris@261 892 texts[i], OutlinedText);
Chris@299 893
Chris@299 894 if (!pixmaps[i].isNull()) {
Chris@299 895 paint.drawPixmap(llx - fontAscent - 3,
Chris@299 896 lly - fontHeight + (fontHeight-fontAscent)/2,
Chris@299 897 pixmaps[i]);
Chris@299 898 }
Chris@261 899
Chris@261 900 lly -= fontHeight;
Chris@261 901 }
Chris@261 902 }
Chris@261 903 }
Chris@127 904
Chris@261 905 void
Chris@261 906 Pane::drawEditingSelection(QPainter &paint)
Chris@261 907 {
Chris@261 908 int offset = m_mousePos.x() - m_clickPos.x();
Chris@261 909 int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset;
Chris@261 910 int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
Chris@261 911
Chris@261 912 if (m_editingSelectionEdge < 0) {
Chris@261 913 p1 = getXForFrame(m_editingSelection.getEndFrame());
Chris@261 914 } else if (m_editingSelectionEdge > 0) {
Chris@261 915 p0 = getXForFrame(m_editingSelection.getStartFrame());
Chris@127 916 }
Chris@127 917
Chris@261 918 paint.save();
Chris@287 919 paint.setPen(QPen(getForeground(), 2));
Chris@261 920
Chris@261 921 //!!! duplicating display policy with View::drawSelections
Chris@261 922
Chris@261 923 if (m_editingSelectionEdge < 0) {
Chris@261 924 paint.drawLine(p0, 1, p1, 1);
Chris@261 925 paint.drawLine(p0, 0, p0, height());
Chris@261 926 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@261 927 } else if (m_editingSelectionEdge > 0) {
Chris@261 928 paint.drawLine(p0, 1, p1, 1);
Chris@261 929 paint.drawLine(p1, 0, p1, height());
Chris@261 930 paint.drawLine(p0, height() - 1, p1, height() - 1);
Chris@261 931 } else {
Chris@261 932 paint.setBrush(Qt::NoBrush);
Chris@261 933 paint.drawRect(p0, 1, p1 - p0, height() - 2);
Chris@261 934 }
Chris@261 935 paint.restore();
Chris@261 936 }
Chris@127 937
Chris@261 938 void
Chris@261 939 Pane::drawDurationAndRate(QRect r, const Model *waveformModel,
Chris@261 940 int sampleRate, QPainter &paint)
Chris@261 941 {
Chris@261 942 int fontHeight = paint.fontMetrics().height();
Chris@261 943 int fontAscent = paint.fontMetrics().ascent();
Chris@127 944
Chris@261 945 if (r.y() + r.height() < height() - fontHeight - 6) return;
Chris@127 946
Chris@261 947 size_t modelRate = waveformModel->getSampleRate();
Chris@301 948 size_t nativeRate = waveformModel->getNativeRate();
Chris@261 949 size_t playbackRate = m_manager->getPlaybackSampleRate();
Chris@261 950 size_t outputRate = m_manager->getOutputSampleRate();
Chris@261 951
Chris@261 952 QString srNote = "";
Chris@127 953
Chris@301 954 // Show (R) for waveform models that have been resampled or will
Chris@301 955 // be resampled on playback, and (X) for waveform models that will
Chris@301 956 // be played at the wrong rate because their rate differs from the
Chris@261 957 // current playback rate (which is not necessarily that of the
Chris@261 958 // main model).
Chris@127 959
Chris@261 960 if (playbackRate != 0) {
Chris@261 961 if (modelRate == playbackRate) {
Chris@301 962 if (modelRate != outputRate || modelRate != nativeRate) {
Chris@301 963 srNote = " " + tr("(R)");
Chris@301 964 }
Chris@261 965 } else {
Chris@261 966 srNote = " " + tr("(X)");
Chris@261 967 }
Chris@127 968 }
Chris@127 969
Chris@261 970 QString desc = tr("%1 / %2Hz%3")
Chris@261 971 .arg(RealTime::frame2RealTime(waveformModel->getEndFrame(),
Chris@261 972 sampleRate)
Chris@261 973 .toText(false).c_str())
Chris@301 974 .arg(nativeRate)
Chris@261 975 .arg(srNote);
Chris@261 976
Chris@261 977 if (r.x() < m_scaleWidth + 5 + paint.fontMetrics().width(desc)) {
Chris@261 978 drawVisibleText(paint, m_scaleWidth + 5,
Chris@261 979 height() - fontHeight + fontAscent - 6,
Chris@261 980 desc, OutlinedText);
Chris@261 981 }
Chris@127 982 }
Chris@127 983
Chris@227 984 bool
Chris@229 985 Pane::render(QPainter &paint, int xorigin, size_t f0, size_t f1)
Chris@227 986 {
Chris@229 987 if (!View::render(paint, xorigin + m_scaleWidth, f0, f1)) {
Chris@227 988 return false;
Chris@227 989 }
Chris@227 990
Chris@227 991 if (m_scaleWidth > 0) {
Chris@227 992
Chris@227 993 for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
Chris@227 994 --vi;
Chris@227 995
Chris@227 996 paint.save();
Chris@227 997
Chris@287 998 paint.setPen(getForeground());
Chris@287 999 paint.setBrush(getBackground());
Chris@229 1000 paint.drawRect(xorigin, -1, m_scaleWidth, height()+1);
Chris@227 1001
Chris@227 1002 paint.setBrush(Qt::NoBrush);
Chris@227 1003 (*vi)->paintVerticalScale
Chris@229 1004 (this, paint, QRect(xorigin, 0, m_scaleWidth, height()));
Chris@227 1005
Chris@227 1006 paint.restore();
Chris@227 1007 break;
Chris@227 1008 }
Chris@227 1009 }
Chris@227 1010
Chris@227 1011 return true;
Chris@227 1012 }
Chris@227 1013
Chris@227 1014 QImage *
Chris@229 1015 Pane::toNewImage(size_t f0, size_t f1)
Chris@227 1016 {
Chris@227 1017 size_t x0 = f0 / getZoomLevel();
Chris@227 1018 size_t x1 = f1 / getZoomLevel();
Chris@227 1019
Chris@227 1020 QImage *image = new QImage(x1 - x0 + m_scaleWidth,
Chris@227 1021 height(), QImage::Format_RGB32);
Chris@227 1022
Chris@227 1023 int formerScaleWidth = m_scaleWidth;
Chris@227 1024
Chris@227 1025 if (m_manager && m_manager->shouldShowVerticalScale()) {
Chris@227 1026 for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
Chris@227 1027 --vi;
Chris@227 1028 QPainter paint(image);
Chris@227 1029 m_scaleWidth = (*vi)->getVerticalScaleWidth(this, paint);
Chris@227 1030 break;
Chris@227 1031 }
Chris@227 1032 } else {
Chris@227 1033 m_scaleWidth = 0;
Chris@227 1034 }
Chris@227 1035
Chris@227 1036 if (m_scaleWidth != formerScaleWidth) {
Chris@227 1037 delete image;
Chris@227 1038 image = new QImage(x1 - x0 + m_scaleWidth,
Chris@227 1039 height(), QImage::Format_RGB32);
Chris@227 1040 }
Chris@227 1041
Chris@227 1042 QPainter *paint = new QPainter(image);
Chris@229 1043 if (!render(*paint, 0, f0, f1)) {
Chris@227 1044 delete paint;
Chris@227 1045 delete image;
Chris@227 1046 return 0;
Chris@227 1047 } else {
Chris@227 1048 delete paint;
Chris@227 1049 return image;
Chris@227 1050 }
Chris@227 1051 }
Chris@227 1052
Chris@229 1053 QSize
Chris@229 1054 Pane::getImageSize(size_t f0, size_t f1)
Chris@229 1055 {
Chris@229 1056 QSize s = View::getImageSize(f0, f1);
Chris@229 1057 QImage *image = new QImage(100, 100, QImage::Format_RGB32);
Chris@229 1058 QPainter paint(image);
Chris@229 1059
Chris@229 1060 int sw = 0;
Chris@229 1061 if (m_manager && m_manager->shouldShowVerticalScale()) {
Chris@229 1062 for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) {
Chris@229 1063 --vi;
Chris@229 1064 QPainter paint(image);
Chris@229 1065 sw = (*vi)->getVerticalScaleWidth(this, paint);
Chris@229 1066 break;
Chris@229 1067 }
Chris@229 1068 }
Chris@229 1069
Chris@229 1070 return QSize(sw + s.width(), s.height());
Chris@229 1071 }
Chris@229 1072
Chris@222 1073 size_t
Chris@222 1074 Pane::getFirstVisibleFrame() const
Chris@222 1075 {
Chris@222 1076 long f0 = getFrameForX(m_scaleWidth);
Chris@222 1077 size_t f = View::getFirstVisibleFrame();
Chris@222 1078 if (f0 < 0 || f0 < long(f)) return f;
Chris@222 1079 return f0;
Chris@222 1080 }
Chris@222 1081
Chris@127 1082 Selection
Chris@127 1083 Pane::getSelectionAt(int x, bool &closeToLeftEdge, bool &closeToRightEdge) const
Chris@127 1084 {
Chris@127 1085 closeToLeftEdge = closeToRightEdge = false;
Chris@127 1086
Chris@127 1087 if (!m_manager) return Selection();
Chris@127 1088
Chris@127 1089 long testFrame = getFrameForX(x - 5);
Chris@127 1090 if (testFrame < 0) {
Chris@127 1091 testFrame = getFrameForX(x);
Chris@127 1092 if (testFrame < 0) return Selection();
Chris@127 1093 }
Chris@127 1094
Chris@127 1095 Selection selection = m_manager->getContainingSelection(testFrame, true);
Chris@127 1096 if (selection.isEmpty()) return selection;
Chris@127 1097
Chris@127 1098 int lx = getXForFrame(selection.getStartFrame());
Chris@127 1099 int rx = getXForFrame(selection.getEndFrame());
Chris@127 1100
Chris@127 1101 int fuzz = 2;
Chris@127 1102 if (x < lx - fuzz || x > rx + fuzz) return Selection();
Chris@127 1103
Chris@127 1104 int width = rx - lx;
Chris@127 1105 fuzz = 3;
Chris@127 1106 if (width < 12) fuzz = width / 4;
Chris@127 1107 if (fuzz < 1) fuzz = 1;
Chris@127 1108
Chris@127 1109 if (x < lx + fuzz) closeToLeftEdge = true;
Chris@127 1110 if (x > rx - fuzz) closeToRightEdge = true;
Chris@127 1111
Chris@127 1112 return selection;
Chris@127 1113 }
Chris@127 1114
Chris@174 1115 bool
Chris@174 1116 Pane::canTopLayerMoveVertical()
Chris@174 1117 {
Chris@174 1118 float vmin, vmax, dmin, dmax;
Chris@174 1119 if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return false;
Chris@174 1120 if (dmin <= vmin && dmax >= vmax) return false;
Chris@174 1121 return true;
Chris@174 1122 }
Chris@174 1123
Chris@174 1124 bool
Chris@174 1125 Pane::getTopLayerDisplayExtents(float &vmin, float &vmax,
Chris@188 1126 float &dmin, float &dmax,
Chris@188 1127 QString *unit)
Chris@174 1128 {
Chris@268 1129 Layer *layer = getTopLayer();
Chris@174 1130 if (!layer) return false;
Chris@174 1131 bool vlog;
Chris@174 1132 QString vunit;
Chris@188 1133 bool rv = (layer->getValueExtents(vmin, vmax, vlog, vunit) &&
Chris@188 1134 layer->getDisplayExtents(dmin, dmax));
Chris@188 1135 if (unit) *unit = vunit;
Chris@188 1136 return rv;
Chris@174 1137 }
Chris@174 1138
Chris@174 1139 bool
Chris@174 1140 Pane::setTopLayerDisplayExtents(float dmin, float dmax)
Chris@174 1141 {
Chris@268 1142 Layer *layer = getTopLayer();
Chris@174 1143 if (!layer) return false;
Chris@174 1144 return layer->setDisplayExtents(dmin, dmax);
Chris@174 1145 }
Chris@174 1146
Chris@127 1147 void
Chris@282 1148 Pane::registerShortcuts(KeyReference &kr)
Chris@282 1149 {
Chris@282 1150 kr.setCategory(tr("Zoom"));
Chris@282 1151 kr.registerAlternativeShortcut(tr("Zoom In"), tr("Wheel Up"));
Chris@282 1152 kr.registerAlternativeShortcut(tr("Zoom Out"), tr("Wheel Down"));
Chris@282 1153
Chris@282 1154 kr.setCategory(tr("General Pane Mouse Actions"));
Chris@282 1155
Chris@282 1156 kr.registerShortcut(tr("Zoom"), tr("Wheel"),
Chris@282 1157 tr("Zoom in or out in time axis"));
Chris@282 1158 kr.registerShortcut(tr("Ctrl+Wheel"), tr("Scroll"),
Chris@282 1159 tr("Scroll rapidly left or right in time axis"));
Chris@282 1160 kr.registerShortcut(tr("Zoom Vertically"), tr("Shift+Wheel"),
Chris@282 1161 tr("Zoom in or out in the vertical axis"));
Chris@282 1162 kr.registerShortcut(tr("Scroll Vertically"), tr("Alt+Wheel"),
Chris@282 1163 tr("Scroll up or down in the vertical axis"));
Chris@282 1164 kr.registerShortcut(tr("Navigate"), tr("Middle"),
Chris@282 1165 tr("Click middle button and drag to navigate with any tool"));
Chris@282 1166 kr.registerShortcut(tr("Relocate"), tr("Double-Click Middle"),
Chris@282 1167 tr("Double-click middle button to relocate with any tool"));
Chris@282 1168 kr.registerShortcut(tr("Menu"), tr("Right"),
Chris@282 1169 tr("Show pane context menu"));
Chris@282 1170
Chris@282 1171 kr.setCategory(tr("Navigate Tool Mouse Actions"));
Chris@282 1172
Chris@282 1173 kr.registerShortcut(tr("Navigate"), tr("Left"),
Chris@282 1174 tr("Click left button and drag to move around"));
Chris@282 1175 kr.registerShortcut(tr("Zoom to Area"), tr("Shift+Left"),
Chris@282 1176 tr("Shift-click left button and drag to zoom to a rectangular area"));
Chris@282 1177 kr.registerShortcut(tr("Relocate"), tr("Double-Click Left"),
Chris@282 1178 tr("Double-click left button to jump to clicked location"));
Chris@282 1179 kr.registerShortcut(tr("Edit"), tr("Double-Click Left"),
Chris@282 1180 tr("Double-click left button on an item to edit it"));
Chris@282 1181
Chris@282 1182 kr.setCategory(tr("Select Tool Mouse Actions"));
Chris@282 1183 kr.registerShortcut(tr("Select"), tr("Left"),
Chris@282 1184 tr("Click left button and drag to select region; drag region edge to resize"));
Chris@282 1185 kr.registerShortcut(tr("Multi Select"), tr("Ctrl+Left"),
Chris@282 1186 tr("Ctrl-click left button and drag to select an additional region"));
Chris@283 1187 kr.registerShortcut(tr("Fine Select"), tr("Shift+Left"),
Chris@283 1188 tr("Shift-click left button and drag to select without snapping to items or grid"));
Chris@282 1189
Chris@282 1190 kr.setCategory(tr("Edit Tool Mouse Actions"));
Chris@282 1191 kr.registerShortcut(tr("Move"), tr("Left"),
Chris@282 1192 tr("Click left button on an item or selected region and drag to move"));
Chris@282 1193 kr.registerShortcut(tr("Edit"), tr("Double-Click Left"),
Chris@282 1194 tr("Double-click left button on an item to edit it"));
Chris@282 1195
Chris@282 1196 kr.setCategory(tr("Draw Tool Mouse Actions"));
Chris@282 1197 kr.registerShortcut(tr("Draw"), tr("Left"),
Chris@282 1198 tr("Click left button and drag to create new item"));
Chris@282 1199
Chris@282 1200 kr.setCategory(tr("Measure Tool Mouse Actions"));
Chris@282 1201 kr.registerShortcut(tr("Measure Area"), tr("Left"),
Chris@282 1202 tr("Click left button and drag to measure a rectangular area"));
Chris@282 1203 kr.registerShortcut(tr("Measure Item"), tr("Double-Click Left"),
Chris@282 1204 tr("Click left button and drag to measure extents of an item or shape"));
Chris@283 1205 kr.registerShortcut(tr("Zoom to Area"), tr("Shift+Left"),
Chris@283 1206 tr("Shift-click left button and drag to zoom to a rectangular area"));
Chris@282 1207 }
Chris@282 1208
Chris@282 1209 void
Chris@127 1210 Pane::mousePressEvent(QMouseEvent *e)
Chris@127 1211 {
Chris@127 1212 if (e->buttons() & Qt::RightButton) {
Chris@189 1213 emit contextHelpChanged("");
Chris@127 1214 emit rightButtonMenuRequested(mapToGlobal(e->pos()));
Chris@127 1215 return;
Chris@127 1216 }
Chris@127 1217
Chris@343 1218 // std::cerr << "mousePressEvent" << std::endl;
Chris@341 1219
Chris@127 1220 m_clickPos = e->pos();
Chris@262 1221 m_mousePos = m_clickPos;
Chris@127 1222 m_clickedInRange = true;
Chris@127 1223 m_editingSelection = Selection();
Chris@127 1224 m_editingSelectionEdge = 0;
Chris@127 1225 m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
Chris@127 1226 m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
Chris@150 1227 m_dragMode = UnresolvedDrag;
Chris@127 1228
Chris@127 1229 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@127 1230 if (m_manager) mode = m_manager->getToolMode();
Chris@127 1231
Chris@127 1232 m_navigating = false;
Chris@343 1233 m_resizing = false;
Chris@343 1234 m_editing = false;
Chris@343 1235 m_releasing = false;
Chris@127 1236
Chris@283 1237 if (mode == ViewManager::NavigateMode ||
Chris@283 1238 (e->buttons() & Qt::MidButton) ||
Chris@283 1239 (mode == ViewManager::MeasureMode &&
Chris@283 1240 (e->buttons() & Qt::LeftButton) && m_shiftPressed)) {
Chris@127 1241
Chris@127 1242 if (mode != ViewManager::NavigateMode) {
Chris@127 1243 setCursor(Qt::PointingHandCursor);
Chris@127 1244 }
Chris@127 1245
Chris@127 1246 m_navigating = true;
Chris@127 1247 m_dragCentreFrame = m_centreFrame;
Chris@136 1248 m_dragStartMinValue = 0;
Chris@174 1249
Chris@174 1250 float vmin, vmax, dmin, dmax;
Chris@174 1251 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
Chris@174 1252 m_dragStartMinValue = dmin;
Chris@136 1253 }
Chris@136 1254
Chris@127 1255 } else if (mode == ViewManager::SelectMode) {
Chris@127 1256
Chris@217 1257 if (!hasTopLayerTimeXAxis()) return;
Chris@217 1258
Chris@127 1259 bool closeToLeft = false, closeToRight = false;
Chris@127 1260 Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight);
Chris@127 1261
Chris@127 1262 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
Chris@127 1263
Chris@127 1264 m_manager->removeSelection(selection);
Chris@127 1265
Chris@127 1266 if (closeToLeft) {
Chris@127 1267 m_selectionStartFrame = selection.getEndFrame();
Chris@127 1268 } else {
Chris@127 1269 m_selectionStartFrame = selection.getStartFrame();
Chris@127 1270 }
Chris@127 1271
Chris@127 1272 m_manager->setInProgressSelection(selection, false);
Chris@127 1273 m_resizing = true;
Chris@127 1274
Chris@127 1275 } else {
Chris@127 1276
Chris@127 1277 int mouseFrame = getFrameForX(e->x());
Chris@127 1278 size_t resolution = 1;
Chris@127 1279 int snapFrame = mouseFrame;
Chris@127 1280
Chris@127 1281 Layer *layer = getSelectedLayer();
Chris@127 1282 if (layer && !m_shiftPressed) {
Chris@127 1283 layer->snapToFeatureFrame(this, snapFrame,
Chris@127 1284 resolution, Layer::SnapLeft);
Chris@127 1285 }
Chris@127 1286
Chris@127 1287 if (snapFrame < 0) snapFrame = 0;
Chris@127 1288 m_selectionStartFrame = snapFrame;
Chris@127 1289 if (m_manager) {
Chris@333 1290 m_manager->setInProgressSelection
Chris@333 1291 (Selection(alignToReference(snapFrame),
Chris@333 1292 alignToReference(snapFrame + resolution)),
Chris@333 1293 !m_ctrlPressed);
Chris@127 1294 }
Chris@127 1295
Chris@127 1296 m_resizing = false;
Chris@127 1297 }
Chris@127 1298
Chris@127 1299 update();
Chris@127 1300
Chris@127 1301 } else if (mode == ViewManager::DrawMode) {
Chris@127 1302
Chris@127 1303 Layer *layer = getSelectedLayer();
Chris@127 1304 if (layer && layer->isLayerEditable()) {
Chris@127 1305 layer->drawStart(this, e);
Chris@127 1306 }
Chris@127 1307
Chris@335 1308 } else if (mode == ViewManager::EraseMode) {
Chris@335 1309
Chris@335 1310 Layer *layer = getSelectedLayer();
Chris@335 1311 if (layer && layer->isLayerEditable()) {
Chris@335 1312 layer->eraseStart(this, e);
Chris@335 1313 }
Chris@335 1314
Chris@127 1315 } else if (mode == ViewManager::EditMode) {
Chris@127 1316
Chris@343 1317 // Do nothing here -- we'll do it in mouseMoveEvent when the
Chris@343 1318 // drag threshold has been passed
Chris@262 1319
Chris@262 1320 } else if (mode == ViewManager::MeasureMode) {
Chris@262 1321
Chris@268 1322 Layer *layer = getTopLayer();
Chris@267 1323 if (layer) layer->measureStart(this, e);
Chris@262 1324 update();
Chris@127 1325 }
Chris@127 1326
Chris@127 1327 emit paneInteractedWith();
Chris@127 1328 }
Chris@127 1329
Chris@127 1330 void
Chris@127 1331 Pane::mouseReleaseEvent(QMouseEvent *e)
Chris@127 1332 {
Chris@127 1333 if (e->buttons() & Qt::RightButton) {
Chris@127 1334 return;
Chris@127 1335 }
Chris@127 1336
Chris@343 1337 // std::cerr << "mouseReleaseEvent" << std::endl;
Chris@341 1338
Chris@127 1339 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@127 1340 if (m_manager) mode = m_manager->getToolMode();
Chris@127 1341
Chris@343 1342 m_releasing = true;
Chris@343 1343
Chris@127 1344 if (m_clickedInRange) {
Chris@127 1345 mouseMoveEvent(e);
Chris@127 1346 }
Chris@127 1347
Chris@127 1348 if (m_navigating || mode == ViewManager::NavigateMode) {
Chris@127 1349
Chris@127 1350 m_navigating = false;
Chris@127 1351
Chris@127 1352 if (mode != ViewManager::NavigateMode) {
Chris@127 1353 // restore cursor
Chris@127 1354 toolModeChanged();
Chris@127 1355 }
Chris@127 1356
Chris@127 1357 if (m_shiftPressed) {
Chris@127 1358
Chris@127 1359 int x0 = std::min(m_clickPos.x(), m_mousePos.x());
Chris@127 1360 int x1 = std::max(m_clickPos.x(), m_mousePos.x());
Chris@127 1361
Chris@127 1362 int y0 = std::min(m_clickPos.y(), m_mousePos.y());
Chris@127 1363 int y1 = std::max(m_clickPos.y(), m_mousePos.y());
Chris@127 1364
Chris@174 1365 zoomToRegion(x0, y0, x1, y1);
Chris@127 1366 }
Chris@127 1367
Chris@127 1368 } else if (mode == ViewManager::SelectMode) {
Chris@127 1369
Chris@343 1370 if (!hasTopLayerTimeXAxis()) {
Chris@343 1371 m_releasing = false;
Chris@343 1372 return;
Chris@343 1373 }
Chris@217 1374
Chris@127 1375 if (m_manager && m_manager->haveInProgressSelection()) {
Chris@127 1376
Chris@127 1377 bool exclusive;
Chris@127 1378 Selection selection = m_manager->getInProgressSelection(exclusive);
Chris@127 1379
Chris@127 1380 if (selection.getEndFrame() < selection.getStartFrame() + 2) {
Chris@127 1381 selection = Selection();
Chris@127 1382 }
Chris@127 1383
Chris@127 1384 m_manager->clearInProgressSelection();
Chris@127 1385
Chris@127 1386 if (exclusive) {
Chris@127 1387 m_manager->setSelection(selection);
Chris@127 1388 } else {
Chris@127 1389 m_manager->addSelection(selection);
Chris@127 1390 }
Chris@127 1391 }
Chris@127 1392
Chris@127 1393 update();
Chris@127 1394
Chris@127 1395 } else if (mode == ViewManager::DrawMode) {
Chris@127 1396
Chris@127 1397 Layer *layer = getSelectedLayer();
Chris@127 1398 if (layer && layer->isLayerEditable()) {
Chris@127 1399 layer->drawEnd(this, e);
Chris@127 1400 update();
Chris@127 1401 }
Chris@127 1402
Chris@335 1403 } else if (mode == ViewManager::EraseMode) {
Chris@335 1404
Chris@335 1405 Layer *layer = getSelectedLayer();
Chris@335 1406 if (layer && layer->isLayerEditable()) {
Chris@335 1407 layer->eraseEnd(this, e);
Chris@335 1408 update();
Chris@335 1409 }
Chris@335 1410
Chris@127 1411 } else if (mode == ViewManager::EditMode) {
Chris@127 1412
Chris@343 1413 if (m_editing) {
Chris@343 1414 if (!editSelectionEnd(e)) {
Chris@343 1415 Layer *layer = getSelectedLayer();
Chris@343 1416 if (layer && layer->isLayerEditable()) {
Chris@343 1417 layer->editEnd(this, e);
Chris@343 1418 update();
Chris@343 1419 }
Chris@343 1420 }
Chris@343 1421 }
Chris@262 1422
Chris@262 1423 } else if (mode == ViewManager::MeasureMode) {
Chris@262 1424
Chris@268 1425 Layer *layer = getTopLayer();
Chris@267 1426 if (layer) layer->measureEnd(this, e);
Chris@267 1427 if (m_measureCursor1) setCursor(*m_measureCursor1);
Chris@267 1428 update();
Chris@127 1429 }
Chris@127 1430
Chris@127 1431 m_clickedInRange = false;
Chris@343 1432 m_releasing = false;
Chris@127 1433
Chris@127 1434 emit paneInteractedWith();
Chris@127 1435 }
Chris@127 1436
Chris@127 1437 void
Chris@127 1438 Pane::mouseMoveEvent(QMouseEvent *e)
Chris@127 1439 {
Chris@127 1440 if (e->buttons() & Qt::RightButton) {
Chris@127 1441 return;
Chris@127 1442 }
Chris@127 1443
Chris@343 1444 // std::cerr << "mouseMoveEvent" << std::endl;
Chris@341 1445
Chris@189 1446 updateContextHelp(&e->pos());
Chris@189 1447
Chris@343 1448 if (m_navigating && m_clickedInRange && !m_releasing) {
Chris@343 1449
Chris@343 1450 // if no buttons pressed, and not called from
Chris@343 1451 // mouseReleaseEvent, we want to reset clicked-ness (to avoid
Chris@343 1452 // annoying continual drags when we moved the mouse outside
Chris@343 1453 // the window after pressing button first time).
Chris@343 1454
Chris@343 1455 if (!(e->buttons() & Qt::LeftButton) &&
Chris@343 1456 !(e->buttons() & Qt::MidButton)) {
Chris@343 1457 m_clickedInRange = false;
Chris@343 1458 return;
Chris@343 1459 }
Chris@343 1460 }
Chris@343 1461
Chris@127 1462 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@127 1463 if (m_manager) mode = m_manager->getToolMode();
Chris@127 1464
Chris@127 1465 QPoint prevPoint = m_identifyPoint;
Chris@127 1466 m_identifyPoint = e->pos();
Chris@127 1467
Chris@127 1468 if (!m_clickedInRange) {
Chris@127 1469
Chris@217 1470 if (mode == ViewManager::SelectMode && hasTopLayerTimeXAxis()) {
Chris@127 1471 bool closeToLeft = false, closeToRight = false;
Chris@127 1472 getSelectionAt(e->x(), closeToLeft, closeToRight);
Chris@127 1473 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
Chris@127 1474 setCursor(Qt::SizeHorCursor);
Chris@127 1475 } else {
Chris@127 1476 setCursor(Qt::ArrowCursor);
Chris@127 1477 }
Chris@127 1478 }
Chris@127 1479
Chris@127 1480 if (!m_manager->isPlaying()) {
Chris@127 1481
Chris@272 1482 bool updating = false;
Chris@272 1483
Chris@326 1484 if (getSelectedLayer() &&
Chris@326 1485 m_manager->shouldIlluminateLocalFeatures()) {
Chris@127 1486
Chris@174 1487 bool previouslyIdentifying = m_identifyFeatures;
Chris@174 1488 m_identifyFeatures = true;
Chris@174 1489
Chris@174 1490 if (m_identifyFeatures != previouslyIdentifying ||
Chris@174 1491 m_identifyPoint != prevPoint) {
Chris@174 1492 update();
Chris@272 1493 updating = true;
Chris@272 1494 }
Chris@272 1495 }
Chris@272 1496
Chris@272 1497 if (!updating && mode == ViewManager::MeasureMode &&
Chris@272 1498 m_manager && !m_manager->isPlaying()) {
Chris@272 1499
Chris@272 1500 Layer *layer = getTopLayer();
Chris@272 1501 if (layer && layer->nearestMeasurementRectChanged
Chris@272 1502 (this, prevPoint, m_identifyPoint)) {
Chris@272 1503 update();
Chris@174 1504 }
Chris@174 1505 }
Chris@127 1506 }
Chris@127 1507
Chris@127 1508 return;
Chris@127 1509 }
Chris@127 1510
Chris@127 1511 if (m_navigating || mode == ViewManager::NavigateMode) {
Chris@127 1512
Chris@127 1513 if (m_shiftPressed) {
Chris@127 1514
Chris@127 1515 m_mousePos = e->pos();
Chris@127 1516 update();
Chris@127 1517
Chris@127 1518 } else {
Chris@127 1519
Chris@174 1520 dragTopLayer(e);
Chris@150 1521 }
Chris@127 1522
Chris@127 1523 } else if (mode == ViewManager::SelectMode) {
Chris@127 1524
Chris@217 1525 if (!hasTopLayerTimeXAxis()) return;
Chris@217 1526
Chris@174 1527 dragExtendSelection(e);
Chris@127 1528
Chris@127 1529 } else if (mode == ViewManager::DrawMode) {
Chris@127 1530
Chris@127 1531 Layer *layer = getSelectedLayer();
Chris@127 1532 if (layer && layer->isLayerEditable()) {
Chris@127 1533 layer->drawDrag(this, e);
Chris@127 1534 }
Chris@127 1535
Chris@335 1536 } else if (mode == ViewManager::EraseMode) {
Chris@335 1537
Chris@335 1538 Layer *layer = getSelectedLayer();
Chris@335 1539 if (layer && layer->isLayerEditable()) {
Chris@335 1540 layer->eraseDrag(this, e);
Chris@335 1541 }
Chris@335 1542
Chris@127 1543 } else if (mode == ViewManager::EditMode) {
Chris@127 1544
Chris@343 1545 if (m_editing) {
Chris@343 1546 if (!editSelectionDrag(e)) {
Chris@343 1547 Layer *layer = getSelectedLayer();
Chris@343 1548 if (layer && layer->isLayerEditable()) {
Chris@343 1549 layer->editDrag(this, e);
Chris@343 1550 }
Chris@343 1551 }
Chris@343 1552 }
Chris@343 1553
Chris@343 1554 if (!m_editing) {
Chris@343 1555
Chris@343 1556 DragMode newDragMode = updateDragMode
Chris@343 1557 (m_dragMode,
Chris@343 1558 m_clickPos,
Chris@343 1559 e->pos(),
Chris@343 1560 true, // can move horiz
Chris@343 1561 true, // can move vert
Chris@343 1562 true, // resist horiz
Chris@343 1563 true); // resist vert
Chris@343 1564
Chris@343 1565 if (newDragMode != UnresolvedDrag) {
Chris@343 1566
Chris@343 1567 m_editing = true;
Chris@343 1568
Chris@343 1569 QMouseEvent clickEvent(QEvent::MouseButtonPress,
Chris@343 1570 m_clickPos,
Chris@343 1571 Qt::NoButton,
Chris@343 1572 e->buttons(),
Chris@343 1573 e->modifiers());
Chris@343 1574
Chris@343 1575 if (!editSelectionStart(&clickEvent)) {
Chris@343 1576 Layer *layer = getSelectedLayer();
Chris@343 1577 if (layer && layer->isLayerEditable()) {
Chris@343 1578 layer->editStart(this, &clickEvent);
Chris@343 1579 }
Chris@343 1580 }
Chris@343 1581 }
Chris@343 1582 }
Chris@259 1583
Chris@259 1584 } else if (mode == ViewManager::MeasureMode) {
Chris@259 1585
Chris@267 1586 if (m_measureCursor2) setCursor(*m_measureCursor2);
Chris@266 1587
Chris@268 1588 Layer *layer = getTopLayer();
Chris@290 1589 if (layer) {
Chris@290 1590 layer->measureDrag(this, e);
Chris@290 1591 if (layer->hasTimeXAxis()) edgeScrollMaybe(e->x());
Chris@290 1592 }
Chris@267 1593
Chris@267 1594 update();
Chris@127 1595 }
Chris@127 1596 }
Chris@127 1597
Chris@127 1598 void
Chris@174 1599 Pane::zoomToRegion(int x0, int y0, int x1, int y1)
Chris@174 1600 {
Chris@174 1601 int w = x1 - x0;
Chris@174 1602
Chris@174 1603 long newStartFrame = getFrameForX(x0);
Chris@174 1604
Chris@174 1605 long visibleFrames = getEndFrame() - getStartFrame();
Chris@174 1606 if (newStartFrame <= -visibleFrames) {
Chris@174 1607 newStartFrame = -visibleFrames + 1;
Chris@174 1608 }
Chris@174 1609
Chris@174 1610 if (newStartFrame >= long(getModelsEndFrame())) {
Chris@174 1611 newStartFrame = getModelsEndFrame() - 1;
Chris@174 1612 }
Chris@174 1613
Chris@174 1614 float ratio = float(w) / float(width());
Chris@174 1615 // std::cerr << "ratio: " << ratio << std::endl;
Chris@174 1616 size_t newZoomLevel = (size_t)nearbyint(m_zoomLevel * ratio);
Chris@174 1617 if (newZoomLevel < 1) newZoomLevel = 1;
Chris@174 1618
Chris@174 1619 // std::cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << std::endl;
Chris@174 1620 setZoomLevel(getZoomConstraintBlockSize(newZoomLevel));
Chris@174 1621 setStartFrame(newStartFrame);
Chris@174 1622
Chris@174 1623 QString unit;
Chris@174 1624 float min, max;
Chris@174 1625 bool log;
Chris@174 1626 Layer *layer = 0;
Chris@174 1627 for (LayerList::const_iterator i = m_layers.begin();
Chris@174 1628 i != m_layers.end(); ++i) {
Chris@174 1629 if ((*i)->getValueExtents(min, max, log, unit) &&
Chris@174 1630 (*i)->getDisplayExtents(min, max)) {
Chris@174 1631 layer = *i;
Chris@174 1632 break;
Chris@174 1633 }
Chris@174 1634 }
Chris@174 1635
Chris@174 1636 if (layer) {
Chris@174 1637 if (log) {
Chris@174 1638 min = (min < 0.0) ? -log10f(-min) : (min == 0.0) ? 0.0 : log10f(min);
Chris@174 1639 max = (max < 0.0) ? -log10f(-max) : (max == 0.0) ? 0.0 : log10f(max);
Chris@174 1640 }
Chris@174 1641 float rmin = min + ((max - min) * (height() - y1)) / height();
Chris@174 1642 float rmax = min + ((max - min) * (height() - y0)) / height();
Chris@174 1643 std::cerr << "min: " << min << ", max: " << max << ", y0: " << y0 << ", y1: " << y1 << ", h: " << height() << ", rmin: " << rmin << ", rmax: " << rmax << std::endl;
Chris@174 1644 if (log) {
Chris@174 1645 rmin = powf(10, rmin);
Chris@174 1646 rmax = powf(10, rmax);
Chris@174 1647 }
Chris@174 1648 std::cerr << "finally: rmin: " << rmin << ", rmax: " << rmax << " " << unit.toStdString() << std::endl;
Chris@174 1649
Chris@174 1650 layer->setDisplayExtents(rmin, rmax);
Chris@174 1651 updateVerticalPanner();
Chris@174 1652 }
Chris@174 1653 }
Chris@174 1654
Chris@174 1655 void
Chris@174 1656 Pane::dragTopLayer(QMouseEvent *e)
Chris@174 1657 {
Chris@174 1658 // We need to avoid making it too easy to drag both
Chris@174 1659 // horizontally and vertically, in the case where the
Chris@174 1660 // mouse is moved "mostly" in horizontal or vertical axis
Chris@174 1661 // with only a small variation in the other axis. This is
Chris@174 1662 // particularly important during playback (when we want to
Chris@174 1663 // avoid small horizontal motions) or in slow refresh
Chris@174 1664 // layers like spectrogram (when we want to avoid small
Chris@174 1665 // vertical motions).
Chris@174 1666 //
Chris@174 1667 // To this end we have horizontal and vertical thresholds
Chris@174 1668 // and a series of states: unresolved, horizontally or
Chris@174 1669 // vertically constrained, free.
Chris@174 1670 //
Chris@174 1671 // When the mouse first moves, we're unresolved: we
Chris@174 1672 // restrict ourselves to whichever direction seems safest,
Chris@174 1673 // until the mouse has passed a small threshold distance
Chris@174 1674 // from the click point. Then we lock in to one of the
Chris@174 1675 // constrained modes, based on which axis that distance
Chris@174 1676 // was measured in first. Finally, if it turns out we've
Chris@174 1677 // also moved more than a certain larger distance in the
Chris@174 1678 // other direction as well, we may switch into free mode.
Chris@174 1679 //
Chris@174 1680 // If the top layer is incapable of being dragged
Chris@174 1681 // vertically, the logic is short circuited.
Chris@174 1682
Chris@343 1683 m_dragMode = updateDragMode
Chris@343 1684 (m_dragMode,
Chris@343 1685 m_clickPos,
Chris@343 1686 e->pos(),
Chris@343 1687 true, // can move horiz
Chris@343 1688 canTopLayerMoveVertical(), // can move vert
Chris@343 1689 canTopLayerMoveVertical() || (m_manager && m_manager->isPlaying()), // resist horiz
Chris@343 1690 !(m_manager && m_manager->isPlaying())); // resist vert
Chris@174 1691
Chris@343 1692 if (m_dragMode == HorizontalDrag ||
Chris@343 1693 m_dragMode == FreeDrag) {
Chris@174 1694
Chris@174 1695 long frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x());
Chris@174 1696
Chris@174 1697 size_t newCentreFrame = m_dragCentreFrame;
Chris@174 1698
Chris@174 1699 if (frameOff < 0) {
Chris@174 1700 newCentreFrame -= frameOff;
Chris@174 1701 } else if (newCentreFrame >= size_t(frameOff)) {
Chris@174 1702 newCentreFrame -= frameOff;
Chris@174 1703 } else {
Chris@174 1704 newCentreFrame = 0;
Chris@174 1705 }
Chris@363 1706
Chris@363 1707 #ifdef DEBUG_PANE
Chris@339 1708 std::cerr << "Pane::dragTopLayer: newCentreFrame = " << newCentreFrame <<
Chris@339 1709 ", models end frame = " << getModelsEndFrame() << std::endl;
Chris@363 1710 #endif
Chris@339 1711
Chris@174 1712 if (newCentreFrame >= getModelsEndFrame()) {
Chris@174 1713 newCentreFrame = getModelsEndFrame();
Chris@174 1714 if (newCentreFrame > 0) --newCentreFrame;
Chris@174 1715 }
Chris@174 1716
Chris@174 1717 if (getXForFrame(m_centreFrame) != getXForFrame(newCentreFrame)) {
Chris@174 1718 setCentreFrame(newCentreFrame);
Chris@174 1719 }
Chris@174 1720 }
Chris@174 1721
Chris@343 1722 if (m_dragMode == VerticalDrag ||
Chris@343 1723 m_dragMode == FreeDrag) {
Chris@174 1724
Chris@174 1725 float vmin = 0.f, vmax = 0.f;
Chris@174 1726 float dmin = 0.f, dmax = 0.f;
Chris@174 1727
Chris@174 1728 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
Chris@174 1729
Chris@248 1730 // std::cerr << "ydiff = " << ydiff << std::endl;
Chris@174 1731
Chris@343 1732 int ydiff = e->y() - m_clickPos.y();
Chris@174 1733 float perpix = (dmax - dmin) / height();
Chris@174 1734 float valdiff = ydiff * perpix;
Chris@248 1735 // std::cerr << "valdiff = " << valdiff << std::endl;
Chris@174 1736
Chris@343 1737 if (m_dragMode == UnresolvedDrag && ydiff != 0) {
Chris@343 1738 m_dragMode = VerticalDrag;
Chris@343 1739 }
Chris@343 1740
Chris@174 1741 float newmin = m_dragStartMinValue + valdiff;
Chris@174 1742 float newmax = m_dragStartMinValue + (dmax - dmin) + valdiff;
Chris@174 1743 if (newmin < vmin) {
Chris@174 1744 newmax += vmin - newmin;
Chris@174 1745 newmin += vmin - newmin;
Chris@174 1746 }
Chris@174 1747 if (newmax > vmax) {
Chris@174 1748 newmin -= newmax - vmax;
Chris@174 1749 newmax -= newmax - vmax;
Chris@174 1750 }
Chris@248 1751 // std::cerr << "(" << dmin << ", " << dmax << ") -> ("
Chris@248 1752 // << newmin << ", " << newmax << ") (drag start " << m_dragStartMinValue << ")" << std::endl;
Chris@174 1753
Chris@174 1754 setTopLayerDisplayExtents(newmin, newmax);
Chris@174 1755 updateVerticalPanner();
Chris@174 1756 }
Chris@174 1757 }
Chris@174 1758 }
Chris@174 1759
Chris@343 1760 Pane::DragMode
Chris@343 1761 Pane::updateDragMode(DragMode dragMode,
Chris@343 1762 QPoint origin,
Chris@343 1763 QPoint point,
Chris@343 1764 bool canMoveHorizontal,
Chris@343 1765 bool canMoveVertical,
Chris@343 1766 bool resistHorizontal,
Chris@343 1767 bool resistVertical)
Chris@343 1768 {
Chris@343 1769 int xdiff = point.x() - origin.x();
Chris@343 1770 int ydiff = point.y() - origin.y();
Chris@343 1771
Chris@343 1772 int smallThreshold = 10, bigThreshold = 80;
Chris@343 1773
Chris@343 1774 // std::cerr << "Pane::updateDragMode: xdiff = " << xdiff << ", ydiff = "
Chris@343 1775 // << ydiff << ", canMoveVertical = " << canMoveVertical << ", drag mode = " << m_dragMode << std::endl;
Chris@343 1776
Chris@343 1777 if (dragMode == UnresolvedDrag) {
Chris@343 1778
Chris@343 1779 if (abs(ydiff) > smallThreshold &&
Chris@343 1780 abs(ydiff) > abs(xdiff) * 2 &&
Chris@343 1781 canMoveVertical) {
Chris@343 1782 // std::cerr << "Pane::updateDragMode: passed vertical threshold" << std::endl;
Chris@343 1783 dragMode = VerticalDrag;
Chris@343 1784 } else if (abs(xdiff) > smallThreshold &&
Chris@343 1785 abs(xdiff) > abs(ydiff) * 2 &&
Chris@343 1786 canMoveHorizontal) {
Chris@343 1787 // std::cerr << "Pane::updateDragMode: passed horizontal threshold" << std::endl;
Chris@343 1788 dragMode = HorizontalDrag;
Chris@343 1789 } else if (abs(xdiff) > smallThreshold &&
Chris@343 1790 abs(ydiff) > smallThreshold &&
Chris@343 1791 canMoveVertical &&
Chris@343 1792 canMoveHorizontal) {
Chris@343 1793 // std::cerr << "Pane::updateDragMode: passed both thresholds" << std::endl;
Chris@343 1794 dragMode = FreeDrag;
Chris@343 1795 }
Chris@343 1796 }
Chris@343 1797
Chris@343 1798 if (dragMode == VerticalDrag && canMoveHorizontal) {
Chris@343 1799 if (abs(xdiff) > bigThreshold) dragMode = FreeDrag;
Chris@343 1800 }
Chris@343 1801
Chris@343 1802 if (dragMode == HorizontalDrag && canMoveVertical) {
Chris@343 1803 if (abs(ydiff) > bigThreshold) dragMode = FreeDrag;
Chris@343 1804 }
Chris@343 1805
Chris@343 1806 if (dragMode == UnresolvedDrag) {
Chris@343 1807 if (!resistHorizontal && xdiff != 0) {
Chris@343 1808 dragMode = HorizontalDrag;
Chris@343 1809 }
Chris@343 1810 if (!resistVertical && ydiff != 0) {
Chris@343 1811 if (dragMode == HorizontalDrag) dragMode = FreeDrag;
Chris@343 1812 else dragMode = VerticalDrag;
Chris@343 1813 }
Chris@343 1814 }
Chris@343 1815
Chris@343 1816 return dragMode;
Chris@343 1817 }
Chris@343 1818
Chris@174 1819 void
Chris@174 1820 Pane::dragExtendSelection(QMouseEvent *e)
Chris@174 1821 {
Chris@174 1822 int mouseFrame = getFrameForX(e->x());
Chris@174 1823 size_t resolution = 1;
Chris@174 1824 int snapFrameLeft = mouseFrame;
Chris@174 1825 int snapFrameRight = mouseFrame;
Chris@174 1826
Chris@174 1827 Layer *layer = getSelectedLayer();
Chris@174 1828 if (layer && !m_shiftPressed) {
Chris@174 1829 layer->snapToFeatureFrame(this, snapFrameLeft,
Chris@174 1830 resolution, Layer::SnapLeft);
Chris@174 1831 layer->snapToFeatureFrame(this, snapFrameRight,
Chris@174 1832 resolution, Layer::SnapRight);
Chris@174 1833 }
Chris@174 1834
Chris@174 1835 // std::cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << std::endl;
Chris@174 1836
Chris@174 1837 if (snapFrameLeft < 0) snapFrameLeft = 0;
Chris@174 1838 if (snapFrameRight < 0) snapFrameRight = 0;
Chris@174 1839
Chris@174 1840 size_t min, max;
Chris@174 1841
Chris@248 1842 if (m_selectionStartFrame > size_t(snapFrameLeft)) {
Chris@174 1843 min = snapFrameLeft;
Chris@174 1844 max = m_selectionStartFrame;
Chris@248 1845 } else if (size_t(snapFrameRight) > m_selectionStartFrame) {
Chris@174 1846 min = m_selectionStartFrame;
Chris@174 1847 max = snapFrameRight;
Chris@174 1848 } else {
Chris@174 1849 min = snapFrameLeft;
Chris@174 1850 max = snapFrameRight;
Chris@174 1851 }
Chris@174 1852
Chris@174 1853 if (m_manager) {
Chris@333 1854 m_manager->setInProgressSelection(Selection(alignToReference(min),
Chris@333 1855 alignToReference(max)),
Chris@174 1856 !m_resizing && !m_ctrlPressed);
Chris@174 1857 }
Chris@174 1858
Chris@259 1859 edgeScrollMaybe(e->x());
Chris@259 1860
Chris@259 1861 update();
Chris@259 1862 }
Chris@259 1863
Chris@259 1864 void
Chris@259 1865 Pane::edgeScrollMaybe(int x)
Chris@259 1866 {
Chris@259 1867 int mouseFrame = getFrameForX(x);
Chris@259 1868
Chris@174 1869 bool doScroll = false;
Chris@174 1870 if (!m_manager) doScroll = true;
Chris@174 1871 if (!m_manager->isPlaying()) doScroll = true;
Chris@174 1872 if (m_followPlay != PlaybackScrollContinuous) doScroll = true;
Chris@174 1873
Chris@174 1874 if (doScroll) {
Chris@174 1875 int offset = mouseFrame - getStartFrame();
Chris@174 1876 int available = getEndFrame() - getStartFrame();
Chris@259 1877 int move = 0;
Chris@174 1878 if (offset >= available * 0.95) {
Chris@259 1879 move = int(offset - available * 0.95) + 1;
Chris@259 1880 } else if (offset <= available * 0.10) {
Chris@259 1881 move = int(available * 0.10 - offset) + 1;
Chris@259 1882 move = -move;
Chris@259 1883 }
Chris@259 1884 if (move != 0) {
Chris@174 1885 setCentreFrame(m_centreFrame + move);
Chris@259 1886 update();
Chris@174 1887 }
Chris@174 1888 }
Chris@174 1889 }
Chris@174 1890
Chris@174 1891 void
Chris@127 1892 Pane::mouseDoubleClickEvent(QMouseEvent *e)
Chris@127 1893 {
Chris@127 1894 if (e->buttons() & Qt::RightButton) {
Chris@127 1895 return;
Chris@127 1896 }
Chris@127 1897
Chris@343 1898 // std::cerr << "mouseDoubleClickEvent" << std::endl;
Chris@127 1899
Chris@127 1900 m_clickPos = e->pos();
Chris@127 1901 m_clickedInRange = true;
Chris@127 1902 m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
Chris@127 1903 m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
Chris@127 1904
Chris@127 1905 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@127 1906 if (m_manager) mode = m_manager->getToolMode();
Chris@127 1907
Chris@255 1908 bool relocate = (mode == ViewManager::NavigateMode ||
Chris@255 1909 (e->buttons() & Qt::MidButton));
Chris@255 1910
Chris@127 1911 if (mode == ViewManager::NavigateMode ||
Chris@127 1912 mode == ViewManager::EditMode) {
Chris@127 1913
Chris@127 1914 Layer *layer = getSelectedLayer();
Chris@127 1915 if (layer && layer->isLayerEditable()) {
Chris@255 1916 if (layer->editOpen(this, e)) relocate = false;
Chris@127 1917 }
Chris@280 1918
Chris@280 1919 } else if (mode == ViewManager::MeasureMode) {
Chris@280 1920
Chris@280 1921 Layer *layer = getTopLayer();
Chris@280 1922 if (layer) layer->measureDoubleClick(this, e);
Chris@280 1923 update();
Chris@127 1924 }
Chris@255 1925
Chris@255 1926 if (relocate) {
Chris@255 1927
Chris@255 1928 long f = getFrameForX(e->x());
Chris@255 1929
Chris@255 1930 setCentreFrame(f);
Chris@255 1931
Chris@255 1932 m_dragCentreFrame = f;
Chris@255 1933 m_dragStartMinValue = 0;
Chris@255 1934 m_dragMode = UnresolvedDrag;
Chris@255 1935
Chris@255 1936 float vmin, vmax, dmin, dmax;
Chris@255 1937 if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
Chris@255 1938 m_dragStartMinValue = dmin;
Chris@255 1939 }
Chris@255 1940 }
Chris@127 1941 }
Chris@127 1942
Chris@127 1943 void
Chris@290 1944 Pane::enterEvent(QEvent *)
Chris@290 1945 {
Chris@290 1946 m_mouseInWidget = true;
Chris@290 1947 }
Chris@290 1948
Chris@290 1949 void
Chris@127 1950 Pane::leaveEvent(QEvent *)
Chris@127 1951 {
Chris@290 1952 m_mouseInWidget = false;
Chris@127 1953 bool previouslyIdentifying = m_identifyFeatures;
Chris@127 1954 m_identifyFeatures = false;
Chris@127 1955 if (previouslyIdentifying) update();
Chris@189 1956 emit contextHelpChanged("");
Chris@127 1957 }
Chris@127 1958
Chris@127 1959 void
Chris@133 1960 Pane::resizeEvent(QResizeEvent *)
Chris@133 1961 {
Chris@133 1962 updateHeadsUpDisplay();
Chris@133 1963 }
Chris@133 1964
Chris@133 1965 void
Chris@127 1966 Pane::wheelEvent(QWheelEvent *e)
Chris@127 1967 {
Chris@127 1968 //std::cerr << "wheelEvent, delta " << e->delta() << std::endl;
Chris@127 1969
Chris@127 1970 int count = e->delta();
Chris@127 1971
Chris@127 1972 if (count > 0) {
Chris@127 1973 if (count >= 120) count /= 120;
Chris@127 1974 else count = 1;
Chris@127 1975 }
Chris@127 1976
Chris@127 1977 if (count < 0) {
Chris@127 1978 if (count <= -120) count /= 120;
Chris@127 1979 else count = -1;
Chris@127 1980 }
Chris@127 1981
Chris@127 1982 if (e->modifiers() & Qt::ControlModifier) {
Chris@127 1983
Chris@127 1984 // Scroll left or right, rapidly
Chris@127 1985
Chris@127 1986 if (getStartFrame() < 0 &&
Chris@127 1987 getEndFrame() >= getModelsEndFrame()) return;
Chris@127 1988
Chris@127 1989 long delta = ((width() / 2) * count * m_zoomLevel);
Chris@127 1990
Chris@127 1991 if (int(m_centreFrame) < delta) {
Chris@127 1992 setCentreFrame(0);
Chris@127 1993 } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) {
Chris@127 1994 setCentreFrame(getModelsEndFrame());
Chris@127 1995 } else {
Chris@127 1996 setCentreFrame(m_centreFrame - delta);
Chris@127 1997 }
Chris@127 1998
Chris@256 1999 } else if (e->modifiers() & Qt::ShiftModifier) {
Chris@256 2000
Chris@256 2001 // Zoom vertically
Chris@256 2002
Chris@256 2003 if (m_vpan) {
Chris@256 2004 m_vpan->scroll(e->delta() > 0);
Chris@256 2005 }
Chris@256 2006
Chris@256 2007 } else if (e->modifiers() & Qt::AltModifier) {
Chris@256 2008
Chris@256 2009 // Zoom vertically
Chris@256 2010
Chris@256 2011 if (m_vthumb) {
Chris@256 2012 m_vthumb->scroll(e->delta() > 0);
Chris@256 2013 }
Chris@256 2014
Chris@127 2015 } else {
Chris@127 2016
Chris@127 2017 // Zoom in or out
Chris@127 2018
Chris@127 2019 int newZoomLevel = m_zoomLevel;
Chris@127 2020
Chris@127 2021 while (count > 0) {
Chris@127 2022 if (newZoomLevel <= 2) {
Chris@127 2023 newZoomLevel = 1;
Chris@127 2024 break;
Chris@127 2025 }
Chris@127 2026 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1,
Chris@127 2027 ZoomConstraint::RoundDown);
Chris@127 2028 --count;
Chris@127 2029 }
Chris@127 2030
Chris@127 2031 while (count < 0) {
Chris@127 2032 newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
Chris@127 2033 ZoomConstraint::RoundUp);
Chris@127 2034 ++count;
Chris@127 2035 }
Chris@127 2036
Chris@127 2037 if (newZoomLevel != m_zoomLevel) {
Chris@127 2038 setZoomLevel(newZoomLevel);
Chris@127 2039 }
Chris@127 2040 }
Chris@127 2041
Chris@127 2042 emit paneInteractedWith();
Chris@127 2043 }
Chris@127 2044
Chris@132 2045 void
Chris@132 2046 Pane::horizontalThumbwheelMoved(int value)
Chris@132 2047 {
Chris@137 2048 //!!! dupe with updateHeadsUpDisplay
Chris@137 2049
Chris@132 2050 int count = 0;
Chris@132 2051 int level = 1;
Chris@137 2052
Chris@137 2053
Chris@137 2054 //!!! pull out into function (presumably in View)
Chris@137 2055 bool haveConstraint = false;
Chris@137 2056 for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end();
Chris@137 2057 ++i) {
Chris@137 2058 if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) {
Chris@137 2059 haveConstraint = true;
Chris@137 2060 break;
Chris@137 2061 }
Chris@132 2062 }
Chris@132 2063
Chris@137 2064 if (haveConstraint) {
Chris@137 2065 while (true) {
Chris@137 2066 if (m_hthumb->getMaximumValue() - value == count) break;
Chris@137 2067 int newLevel = getZoomConstraintBlockSize(level + 1,
Chris@137 2068 ZoomConstraint::RoundUp);
Chris@137 2069 if (newLevel == level) break;
Chris@137 2070 level = newLevel;
Chris@137 2071 if (++count == 50) break;
Chris@137 2072 }
Chris@137 2073 } else {
Chris@137 2074 while (true) {
Chris@137 2075 if (m_hthumb->getMaximumValue() - value == count) break;
Chris@137 2076 int step = level / 10;
Chris@137 2077 int pwr = 0;
Chris@137 2078 while (step > 0) {
Chris@137 2079 ++pwr;
Chris@137 2080 step /= 2;
Chris@137 2081 }
Chris@137 2082 step = 1;
Chris@137 2083 while (pwr > 0) {
Chris@137 2084 step *= 2;
Chris@137 2085 --pwr;
Chris@137 2086 }
Chris@137 2087 // std::cerr << level << std::endl;
Chris@137 2088 level += step;
Chris@137 2089 if (++count == 100 || level > 262144) break;
Chris@137 2090 }
Chris@137 2091 }
Chris@137 2092
Chris@244 2093 // std::cerr << "new level is " << level << std::endl;
Chris@132 2094 setZoomLevel(level);
Chris@132 2095 }
Chris@132 2096
Chris@132 2097 void
Chris@132 2098 Pane::verticalThumbwheelMoved(int value)
Chris@132 2099 {
Chris@133 2100 Layer *layer = 0;
Chris@133 2101 if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
Chris@133 2102 if (layer) {
Chris@133 2103 int defaultStep = 0;
Chris@133 2104 int max = layer->getVerticalZoomSteps(defaultStep);
Chris@133 2105 if (max == 0) {
Chris@133 2106 updateHeadsUpDisplay();
Chris@133 2107 return;
Chris@133 2108 }
Chris@133 2109 if (value > max) {
Chris@133 2110 value = max;
Chris@133 2111 }
Chris@133 2112 layer->setVerticalZoomStep(value);
Chris@174 2113 updateVerticalPanner();
Chris@133 2114 }
Chris@132 2115 }
Chris@132 2116
Chris@174 2117 void
Chris@174 2118 Pane::verticalPannerMoved(float x0, float y0, float w, float h)
Chris@174 2119 {
Chris@174 2120 float vmin, vmax, dmin, dmax;
Chris@174 2121 if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return;
Chris@174 2122 float y1 = y0 + h;
Chris@174 2123 float newmax = vmin + ((1.0 - y0) * (vmax - vmin));
Chris@174 2124 float newmin = vmin + ((1.0 - y1) * (vmax - vmin));
Chris@174 2125 std::cerr << "verticalPannerMoved: (" << x0 << "," << y0 << "," << w
Chris@174 2126 << "," << h << ") -> (" << newmin << "," << newmax << ")" << std::endl;
Chris@174 2127 setTopLayerDisplayExtents(newmin, newmax);
Chris@174 2128 }
Chris@174 2129
Chris@188 2130 void
Chris@188 2131 Pane::editVerticalPannerExtents()
Chris@188 2132 {
Chris@188 2133 if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return;
Chris@188 2134
Chris@188 2135 float vmin, vmax, dmin, dmax;
Chris@188 2136 QString unit;
Chris@188 2137 if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax, &unit)
Chris@188 2138 || vmax == vmin) {
Chris@188 2139 return;
Chris@188 2140 }
Chris@188 2141
Chris@188 2142 RangeInputDialog dialog(tr("Enter new range"),
Chris@188 2143 tr("New vertical display range, from %1 to %2 %4:")
Chris@188 2144 .arg(vmin).arg(vmax).arg(unit),
Chris@188 2145 unit, vmin, vmax, this);
Chris@188 2146 dialog.setRange(dmin, dmax);
Chris@188 2147
Chris@188 2148 if (dialog.exec() == QDialog::Accepted) {
Chris@188 2149 dialog.getRange(dmin, dmax);
Chris@188 2150 setTopLayerDisplayExtents(dmin, dmax);
Chris@188 2151 updateVerticalPanner();
Chris@188 2152 }
Chris@188 2153 }
Chris@188 2154
Chris@312 2155 void
Chris@312 2156 Pane::dragEnterEvent(QDragEnterEvent *e)
Chris@312 2157 {
Chris@312 2158 QStringList formats(e->mimeData()->formats());
Chris@312 2159 std::cerr << "dragEnterEvent: format: "
Chris@312 2160 << formats.join(",").toStdString()
Chris@312 2161 << ", possibleActions: " << e->possibleActions()
Chris@312 2162 << ", proposedAction: " << e->proposedAction() << std::endl;
Chris@312 2163
Chris@312 2164 if (e->provides("text/uri-list") || e->provides("text/plain")) {
Chris@312 2165
Chris@312 2166 if (e->proposedAction() & Qt::CopyAction) {
Chris@312 2167 e->acceptProposedAction();
Chris@312 2168 } else {
Chris@312 2169 e->setDropAction(Qt::CopyAction);
Chris@312 2170 e->accept();
Chris@312 2171 }
Chris@312 2172 }
Chris@312 2173 }
Chris@312 2174
Chris@312 2175 void
Chris@312 2176 Pane::dropEvent(QDropEvent *e)
Chris@312 2177 {
Chris@312 2178 std::cerr << "dropEvent: text: \"" << e->mimeData()->text().toStdString()
Chris@312 2179 << "\"" << std::endl;
Chris@312 2180
Chris@312 2181 if (e->provides("text/uri-list") || e->provides("text/plain")) {
Chris@312 2182
Chris@312 2183 if (e->proposedAction() & Qt::CopyAction) {
Chris@312 2184 e->acceptProposedAction();
Chris@312 2185 } else {
Chris@312 2186 e->setDropAction(Qt::CopyAction);
Chris@312 2187 e->accept();
Chris@312 2188 }
Chris@312 2189
Chris@312 2190 if (e->provides("text/uri-list")) {
Chris@312 2191
Chris@312 2192 std::cerr << "accepting... data is \"" << e->encodedData("text/uri-list").data() << "\"" << std::endl;
Chris@312 2193 emit dropAccepted(QString::fromLocal8Bit
Chris@312 2194 (e->encodedData("text/uri-list").data())
Chris@312 2195 .split(QRegExp("[\\r\\n]+"),
Chris@312 2196 QString::SkipEmptyParts));
Chris@312 2197 } else {
Chris@312 2198 emit dropAccepted(QString::fromLocal8Bit
Chris@312 2199 (e->encodedData("text/plain").data()));
Chris@312 2200 }
Chris@312 2201 }
Chris@312 2202 }
Chris@312 2203
Chris@127 2204 bool
Chris@127 2205 Pane::editSelectionStart(QMouseEvent *e)
Chris@127 2206 {
Chris@127 2207 if (!m_identifyFeatures ||
Chris@127 2208 !m_manager ||
Chris@127 2209 m_manager->getToolMode() != ViewManager::EditMode) {
Chris@127 2210 return false;
Chris@127 2211 }
Chris@127 2212
Chris@127 2213 bool closeToLeft, closeToRight;
Chris@127 2214 Selection s(getSelectionAt(e->x(), closeToLeft, closeToRight));
Chris@127 2215 if (s.isEmpty()) return false;
Chris@127 2216 m_editingSelection = s;
Chris@127 2217 m_editingSelectionEdge = (closeToLeft ? -1 : closeToRight ? 1 : 0);
Chris@127 2218 m_mousePos = e->pos();
Chris@127 2219 return true;
Chris@127 2220 }
Chris@127 2221
Chris@127 2222 bool
Chris@127 2223 Pane::editSelectionDrag(QMouseEvent *e)
Chris@127 2224 {
Chris@127 2225 if (m_editingSelection.isEmpty()) return false;
Chris@127 2226 m_mousePos = e->pos();
Chris@127 2227 update();
Chris@127 2228 return true;
Chris@127 2229 }
Chris@127 2230
Chris@127 2231 bool
Chris@248 2232 Pane::editSelectionEnd(QMouseEvent *)
Chris@127 2233 {
Chris@127 2234 if (m_editingSelection.isEmpty()) return false;
Chris@127 2235
Chris@127 2236 int offset = m_mousePos.x() - m_clickPos.x();
Chris@127 2237 Layer *layer = getSelectedLayer();
Chris@127 2238
Chris@127 2239 if (offset == 0 || !layer) {
Chris@127 2240 m_editingSelection = Selection();
Chris@127 2241 return true;
Chris@127 2242 }
Chris@127 2243
Chris@127 2244 int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset;
Chris@127 2245 int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
Chris@127 2246
Chris@127 2247 long f0 = getFrameForX(p0);
Chris@127 2248 long f1 = getFrameForX(p1);
Chris@127 2249
Chris@127 2250 Selection newSelection(f0, f1);
Chris@127 2251
Chris@127 2252 if (m_editingSelectionEdge == 0) {
Chris@127 2253
Chris@127 2254 CommandHistory::getInstance()->startCompoundOperation
Chris@127 2255 (tr("Drag Selection"), true);
Chris@127 2256
Chris@127 2257 layer->moveSelection(m_editingSelection, f0);
Chris@127 2258
Chris@127 2259 } else {
Chris@127 2260
Chris@127 2261 CommandHistory::getInstance()->startCompoundOperation
Chris@127 2262 (tr("Resize Selection"), true);
Chris@127 2263
Chris@127 2264 if (m_editingSelectionEdge < 0) {
Chris@127 2265 f1 = m_editingSelection.getEndFrame();
Chris@127 2266 } else {
Chris@127 2267 f0 = m_editingSelection.getStartFrame();
Chris@127 2268 }
Chris@127 2269
Chris@127 2270 newSelection = Selection(f0, f1);
Chris@127 2271 layer->resizeSelection(m_editingSelection, newSelection);
Chris@127 2272 }
Chris@127 2273
Chris@127 2274 m_manager->removeSelection(m_editingSelection);
Chris@127 2275 m_manager->addSelection(newSelection);
Chris@127 2276
Chris@127 2277 CommandHistory::getInstance()->endCompoundOperation();
Chris@127 2278
Chris@127 2279 m_editingSelection = Selection();
Chris@127 2280 return true;
Chris@127 2281 }
Chris@127 2282
Chris@127 2283 void
Chris@127 2284 Pane::toolModeChanged()
Chris@127 2285 {
Chris@127 2286 ViewManager::ToolMode mode = m_manager->getToolMode();
Chris@127 2287 // std::cerr << "Pane::toolModeChanged(" << mode << ")" << std::endl;
Chris@127 2288
Chris@267 2289 if (mode == ViewManager::MeasureMode && !m_measureCursor1) {
Chris@267 2290 m_measureCursor1 = new QCursor(QBitmap(":/icons/measure1cursor.xbm"),
Chris@267 2291 QBitmap(":/icons/measure1mask.xbm"),
Chris@267 2292 15, 14);
Chris@267 2293 m_measureCursor2 = new QCursor(QBitmap(":/icons/measure2cursor.xbm"),
Chris@267 2294 QBitmap(":/icons/measure2mask.xbm"),
Chris@267 2295 16, 17);
Chris@257 2296 }
Chris@257 2297
Chris@127 2298 switch (mode) {
Chris@127 2299
Chris@127 2300 case ViewManager::NavigateMode:
Chris@127 2301 setCursor(Qt::PointingHandCursor);
Chris@127 2302 break;
Chris@127 2303
Chris@127 2304 case ViewManager::SelectMode:
Chris@127 2305 setCursor(Qt::ArrowCursor);
Chris@127 2306 break;
Chris@127 2307
Chris@127 2308 case ViewManager::EditMode:
Chris@127 2309 setCursor(Qt::UpArrowCursor);
Chris@127 2310 break;
Chris@127 2311
Chris@127 2312 case ViewManager::DrawMode:
Chris@127 2313 setCursor(Qt::CrossCursor);
Chris@127 2314 break;
Chris@335 2315
Chris@335 2316 case ViewManager::EraseMode:
Chris@335 2317 setCursor(Qt::CrossCursor);
Chris@335 2318 break;
Chris@257 2319
Chris@257 2320 case ViewManager::MeasureMode:
Chris@267 2321 if (m_measureCursor1) setCursor(*m_measureCursor1);
Chris@257 2322 break;
Chris@257 2323
Chris@127 2324 /*
Chris@127 2325 case ViewManager::TextMode:
Chris@127 2326 setCursor(Qt::IBeamCursor);
Chris@127 2327 break;
Chris@127 2328 */
Chris@127 2329 }
Chris@127 2330 }
Chris@127 2331
Chris@133 2332 void
Chris@133 2333 Pane::zoomWheelsEnabledChanged()
Chris@133 2334 {
Chris@133 2335 updateHeadsUpDisplay();
Chris@133 2336 update();
Chris@133 2337 }
Chris@133 2338
Chris@133 2339 void
Chris@224 2340 Pane::viewZoomLevelChanged(View *v, unsigned long z, bool locked)
Chris@133 2341 {
Chris@212 2342 // std::cerr << "Pane[" << this << "]::zoomLevelChanged (global now "
Chris@212 2343 // << (m_manager ? m_manager->getGlobalZoom() : 0) << ")" << std::endl;
Chris@192 2344
Chris@224 2345 View::viewZoomLevelChanged(v, z, locked);
Chris@224 2346
Chris@232 2347 if (m_hthumb && !m_hthumb->isVisible()) return;
Chris@224 2348
Chris@222 2349 if (v != this) {
Chris@222 2350 if (!locked || !m_followZoom) return;
Chris@222 2351 }
Chris@222 2352
Chris@133 2353 if (m_manager && m_manager->getZoomWheelsEnabled()) {
Chris@133 2354 updateHeadsUpDisplay();
Chris@133 2355 }
Chris@133 2356 }
Chris@133 2357
Chris@133 2358 void
Chris@133 2359 Pane::propertyContainerSelected(View *v, PropertyContainer *pc)
Chris@133 2360 {
Chris@133 2361 Layer *layer = 0;
Chris@133 2362
Chris@133 2363 if (getLayerCount() > 0) {
Chris@133 2364 layer = getLayer(getLayerCount() - 1);
Chris@133 2365 disconnect(layer, SIGNAL(verticalZoomChanged()),
Chris@133 2366 this, SLOT(verticalZoomChanged()));
Chris@133 2367 }
Chris@133 2368
Chris@133 2369 View::propertyContainerSelected(v, pc);
Chris@133 2370 updateHeadsUpDisplay();
Chris@133 2371
Chris@187 2372 if (m_vthumb) {
Chris@187 2373 RangeMapper *rm = 0;
Chris@187 2374 if (layer) rm = layer->getNewVerticalZoomRangeMapper();
Chris@187 2375 if (rm) m_vthumb->setRangeMapper(rm);
Chris@187 2376 }
Chris@187 2377
Chris@133 2378 if (getLayerCount() > 0) {
Chris@133 2379 layer = getLayer(getLayerCount() - 1);
Chris@133 2380 connect(layer, SIGNAL(verticalZoomChanged()),
Chris@133 2381 this, SLOT(verticalZoomChanged()));
Chris@133 2382 }
Chris@133 2383 }
Chris@133 2384
Chris@133 2385 void
Chris@133 2386 Pane::verticalZoomChanged()
Chris@133 2387 {
Chris@133 2388 Layer *layer = 0;
Chris@133 2389
Chris@133 2390 if (getLayerCount() > 0) {
Chris@133 2391
Chris@133 2392 layer = getLayer(getLayerCount() - 1);
Chris@133 2393
Chris@133 2394 if (m_vthumb && m_vthumb->isVisible()) {
Chris@133 2395 m_vthumb->setValue(layer->getCurrentVerticalZoomStep());
Chris@133 2396 }
Chris@133 2397 }
Chris@133 2398 }
Chris@133 2399
Chris@189 2400 void
Chris@189 2401 Pane::updateContextHelp(const QPoint *pos)
Chris@189 2402 {
Chris@189 2403 QString help = "";
Chris@189 2404
Chris@189 2405 if (m_clickedInRange) {
Chris@189 2406 emit contextHelpChanged("");
Chris@189 2407 return;
Chris@189 2408 }
Chris@189 2409
Chris@189 2410 ViewManager::ToolMode mode = ViewManager::NavigateMode;
Chris@189 2411 if (m_manager) mode = m_manager->getToolMode();
Chris@189 2412
Chris@189 2413 bool editable = false;
Chris@189 2414 Layer *layer = getSelectedLayer();
Chris@189 2415 if (layer && layer->isLayerEditable()) {
Chris@189 2416 editable = true;
Chris@189 2417 }
Chris@189 2418
Chris@189 2419 if (mode == ViewManager::NavigateMode) {
Chris@189 2420
Chris@189 2421 help = tr("Click and drag to navigate");
Chris@189 2422
Chris@189 2423 } else if (mode == ViewManager::SelectMode) {
Chris@189 2424
Chris@217 2425 if (!hasTopLayerTimeXAxis()) return;
Chris@217 2426
Chris@189 2427 bool haveSelection = (m_manager && !m_manager->getSelections().empty());
Chris@189 2428
Chris@189 2429 if (haveSelection) {
Chris@189 2430 if (editable) {
Chris@189 2431 help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; hold Ctrl for multi-select; middle-click and drag to navigate");
Chris@189 2432 } else {
Chris@189 2433 help = tr("Click and drag to select a range; hold Ctrl for multi-select; middle-click and drag to navigate");
Chris@189 2434 }
Chris@189 2435
Chris@189 2436 if (pos) {
Chris@189 2437 bool closeToLeft = false, closeToRight = false;
Chris@189 2438 Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
Chris@189 2439 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
Chris@189 2440
Chris@189 2441 help = tr("Click and drag to move the selection boundary");
Chris@189 2442 }
Chris@189 2443 }
Chris@189 2444 } else {
Chris@189 2445 if (editable) {
Chris@189 2446 help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; middle-click to navigate");
Chris@189 2447 } else {
Chris@189 2448 help = tr("Click and drag to select a range; middle-click and drag to navigate");
Chris@189 2449 }
Chris@189 2450 }
Chris@189 2451
Chris@189 2452 } else if (mode == ViewManager::DrawMode) {
Chris@189 2453
Chris@189 2454 //!!! could call through to a layer function to find out exact meaning
Chris@189 2455 if (editable) {
Chris@189 2456 help = tr("Click to add a new item in the active layer");
Chris@189 2457 }
Chris@335 2458
Chris@335 2459 } else if (mode == ViewManager::EraseMode) {
Chris@335 2460
Chris@335 2461 //!!! could call through to a layer function to find out exact meaning
Chris@335 2462 if (editable) {
Chris@335 2463 help = tr("Click to erase an item from the active layer");
Chris@335 2464 }
Chris@189 2465
Chris@189 2466 } else if (mode == ViewManager::EditMode) {
Chris@189 2467
Chris@189 2468 //!!! could call through to layer
Chris@189 2469 if (editable) {
Chris@189 2470 help = tr("Click and drag an item in the active layer to move it");
Chris@189 2471 if (pos) {
Chris@189 2472 bool closeToLeft = false, closeToRight = false;
Chris@189 2473 Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
Chris@189 2474 if (!selection.isEmpty()) {
Chris@189 2475 help = tr("Click and drag to move all items in the selected range");
Chris@189 2476 }
Chris@189 2477 }
Chris@189 2478 }
Chris@189 2479 }
Chris@189 2480
Chris@189 2481 emit contextHelpChanged(help);
Chris@189 2482 }
Chris@189 2483
Chris@189 2484 void
Chris@189 2485 Pane::mouseEnteredWidget()
Chris@189 2486 {
Chris@189 2487 QWidget *w = dynamic_cast<QWidget *>(sender());
Chris@189 2488 if (!w) return;
Chris@189 2489
Chris@189 2490 if (w == m_vpan) {
Chris@189 2491 emit contextHelpChanged(tr("Click and drag to adjust the visible range of the vertical scale"));
Chris@189 2492 } else if (w == m_vthumb) {
Chris@189 2493 emit contextHelpChanged(tr("Click and drag to adjust the vertical zoom level"));
Chris@189 2494 } else if (w == m_hthumb) {
Chris@189 2495 emit contextHelpChanged(tr("Click and drag to adjust the horizontal zoom level"));
Chris@189 2496 } else if (w == m_reset) {
Chris@189 2497 emit contextHelpChanged(tr("Reset horizontal and vertical zoom levels to their defaults"));
Chris@189 2498 }
Chris@189 2499 }
Chris@189 2500
Chris@189 2501 void
Chris@189 2502 Pane::mouseLeftWidget()
Chris@189 2503 {
Chris@189 2504 emit contextHelpChanged("");
Chris@189 2505 }
Chris@189 2506
Chris@316 2507 void
Chris@316 2508 Pane::toXml(QTextStream &stream,
Chris@316 2509 QString indent, QString extraAttributes) const
Chris@127 2510 {
Chris@316 2511 View::toXml
Chris@316 2512 (stream, indent,
Chris@127 2513 QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3")
Chris@127 2514 .arg(m_centreLineVisible).arg(height()).arg(extraAttributes));
Chris@127 2515 }
Chris@127 2516
Chris@127 2517