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