svgui  1.9
Pane.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006-2007 Chris Cannam and QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "Pane.h"
00017 #include "layer/Layer.h"
00018 #include "data/model/Model.h"
00019 #include "base/ZoomConstraint.h"
00020 #include "base/RealTime.h"
00021 #include "base/Profiler.h"
00022 #include "ViewManager.h"
00023 #include "widgets/CommandHistory.h"
00024 #include "widgets/TextAbbrev.h"
00025 #include "base/Preferences.h"
00026 #include "layer/WaveformLayer.h"
00027 
00028 // GF: added so we can propagate the mouse move event to the note layer for context handling.
00029 #include "layer/LayerFactory.h"
00030 #include "layer/FlexiNoteLayer.h"
00031 
00032 
00034 #include "data/model/WaveFileModel.h"
00035 
00036 #include <QPaintEvent>
00037 #include <QPainter>
00038 #include <QBitmap>
00039 #include <QDragEnterEvent>
00040 #include <QDropEvent>
00041 #include <QCursor>
00042 #include <QTextStream>
00043 #include <QMimeData>
00044 #include <QApplication>
00045 
00046 #include <iostream>
00047 #include <cmath>
00048 
00050 #include <QFrame>
00051 #include <QGridLayout>
00052 #include <QPushButton>
00053 #include "widgets/Thumbwheel.h"
00054 #include "widgets/Panner.h"
00055 #include "widgets/RangeInputDialog.h"
00056 #include "widgets/NotifyingPushButton.h"
00057 
00058 #include "widgets/KeyReference.h" 
00059 
00060 //#define DEBUG_PANE
00061 
00062 
00063 
00064 
00065 QCursor *Pane::m_measureCursor1 = 0;
00066 QCursor *Pane::m_measureCursor2 = 0;
00067 
00068 Pane::Pane(QWidget *w) :
00069     View(w, true),
00070     m_identifyFeatures(false),
00071     m_clickedInRange(false),
00072     m_shiftPressed(false),
00073     m_ctrlPressed(false),
00074     m_altPressed(false),
00075     m_navigating(false),
00076     m_resizing(false),
00077     m_editing(false),
00078     m_releasing(false),
00079     m_centreLineVisible(true),
00080     m_scaleWidth(0),
00081     m_pendingWheelAngle(0),
00082     m_headsUpDisplay(0),
00083     m_vpan(0),
00084     m_hthumb(0),
00085     m_vthumb(0),
00086     m_reset(0),
00087     m_mouseInWidget(false),
00088     m_playbackFrameMoveScheduled(false),
00089     m_playbackFrameMoveTo(0)
00090 {
00091     setObjectName("Pane");
00092     setMouseTracking(true);
00093     setAcceptDrops(true);
00094     
00095     updateHeadsUpDisplay();
00096 
00097     connect(this, SIGNAL(regionOutlined(QRect)), 
00098             this, SLOT(zoomToRegion(QRect)));
00099 
00100     cerr << "Pane::Pane(" << this << ") returning" << endl;
00101 }
00102 
00103 void
00104 Pane::updateHeadsUpDisplay()
00105 {
00106     Profiler profiler("Pane::updateHeadsUpDisplay");
00107 
00108     if (!isVisible()) return;
00109 
00110 /*
00111     int count = 0;
00112     int currentLevel = 1;
00113     int level = 1;
00114     while (true) {
00115         if (getZoomLevel() == level) currentLevel = count;
00116         int newLevel = getZoomConstraintBlockSize(level + 1,
00117                                                   ZoomConstraint::RoundUp);
00118         if (newLevel == level) break;
00119         if (newLevel == 131072) break; //!!! just because
00120         level = newLevel;
00121         ++count;
00122     }
00123 
00124     cerr << "Have " << count+1 << " zoom levels" << endl;
00125 */
00126 
00127     Layer *layer = 0;
00128     if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
00129 
00130     if (!m_headsUpDisplay) {
00131 
00132         m_headsUpDisplay = new QFrame(this);
00133 
00134         QGridLayout *layout = new QGridLayout;
00135         layout->setMargin(0);
00136         layout->setSpacing(0);
00137         m_headsUpDisplay->setLayout(layout);
00138         
00139         m_hthumb = new Thumbwheel(Qt::Horizontal);
00140         m_hthumb->setObjectName(tr("Horizontal Zoom"));
00141         m_hthumb->setCursor(Qt::ArrowCursor);
00142         layout->addWidget(m_hthumb, 1, 0, 1, 2);
00143         m_hthumb->setFixedWidth(70);
00144         m_hthumb->setFixedHeight(16);
00145         m_hthumb->setDefaultValue(0);
00146         m_hthumb->setSpeed(0.6);
00147         connect(m_hthumb, SIGNAL(valueChanged(int)), this, 
00148                 SLOT(horizontalThumbwheelMoved(int)));
00149         connect(m_hthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
00150         connect(m_hthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
00151 
00152         m_vpan = new Panner;
00153         m_vpan->setCursor(Qt::ArrowCursor);
00154         layout->addWidget(m_vpan, 0, 1);
00155         m_vpan->setFixedWidth(12);
00156         m_vpan->setFixedHeight(70);
00157         m_vpan->setAlpha(80, 130);
00158         connect(m_vpan, SIGNAL(rectExtentsChanged(float, float, float, float)),
00159                 this, SLOT(verticalPannerMoved(float, float, float, float)));
00160         connect(m_vpan, SIGNAL(doubleClicked()),
00161                 this, SLOT(editVerticalPannerExtents()));
00162         connect(m_vpan, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
00163         connect(m_vpan, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
00164 
00165         m_vthumb = new Thumbwheel(Qt::Vertical);
00166         m_vthumb->setObjectName(tr("Vertical Zoom"));
00167         m_vthumb->setCursor(Qt::ArrowCursor);
00168         layout->addWidget(m_vthumb, 0, 2);
00169         m_vthumb->setFixedWidth(16);
00170         m_vthumb->setFixedHeight(70);
00171         connect(m_vthumb, SIGNAL(valueChanged(int)), this, 
00172                 SLOT(verticalThumbwheelMoved(int)));
00173         connect(m_vthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
00174         connect(m_vthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
00175 
00176         if (layer) {
00177             RangeMapper *rm = layer->getNewVerticalZoomRangeMapper();
00178             if (rm) m_vthumb->setRangeMapper(rm);
00179         }
00180 
00181         m_reset = new NotifyingPushButton;
00182         m_reset->setFlat(true);
00183         m_reset->setCursor(Qt::ArrowCursor);
00184         m_reset->setFixedHeight(16);
00185         m_reset->setFixedWidth(16);
00186         m_reset->setIcon(QPixmap(":/icons/zoom-reset.png"));
00187         m_reset->setToolTip(tr("Reset zoom to default"));
00188         layout->addWidget(m_reset, 1, 2);
00189         
00190         layout->setColumnStretch(0, 20);
00191 
00192         connect(m_reset, SIGNAL(clicked()), m_hthumb, SLOT(resetToDefault()));
00193         connect(m_reset, SIGNAL(clicked()), m_vthumb, SLOT(resetToDefault()));
00194         connect(m_reset, SIGNAL(clicked()), m_vpan, SLOT(resetToDefault()));
00195         connect(m_reset, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
00196         connect(m_reset, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
00197     }
00198 
00199     int count = 0;
00200     int current = 0;
00201     int level = 1;
00202 
00204     bool haveConstraint = false;
00205     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end();
00206          ++i) {
00207         if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) {
00208             haveConstraint = true;
00209             break;
00210         }
00211     }
00212 
00213     if (haveConstraint) {
00214         while (true) {
00215             if (getZoomLevel() == level) current = count;
00216             int newLevel = getZoomConstraintBlockSize(level + 1,
00217                                                       ZoomConstraint::RoundUp);
00218             if (newLevel == level) break;
00219             level = newLevel;
00220             if (++count == 50) break;
00221         }
00222     } else {
00223         // if we have no particular constraints, we can really spread out
00224         while (true) {
00225             if (getZoomLevel() >= level) current = count;
00226             int step = level / 10;
00227             int pwr = 0;
00228             while (step > 0) {
00229                 ++pwr;
00230                 step /= 2;
00231             }
00232             step = 1;
00233             while (pwr > 0) {
00234                 step *= 2;
00235                 --pwr;
00236             }
00237 //            cerr << level << endl;
00238             level += step;
00239             if (++count == 100 || level > 262144) break;
00240         }
00241     }
00242 
00243 //    cerr << "Have " << count << " zoom levels" << endl;
00244 
00245     m_hthumb->setMinimumValue(0);
00246     m_hthumb->setMaximumValue(count);
00247     m_hthumb->setValue(count - current);
00248 
00249 //    cerr << "set value to " << count-current << endl;
00250 
00251 //    cerr << "default value is " << m_hthumb->getDefaultValue() << endl;
00252 
00253     if (count != 50 && m_hthumb->getDefaultValue() == 0) {
00254         m_hthumb->setDefaultValue(count - current);
00255 //        cerr << "set default value to " << m_hthumb->getDefaultValue() << endl;
00256     }
00257 
00258     bool haveVThumb = false;
00259 
00260     if (layer) {
00261         int defaultStep = 0;
00262         int max = layer->getVerticalZoomSteps(defaultStep);
00263         if (max == 0) {
00264             m_vthumb->hide();
00265         } else {
00266             haveVThumb = true;
00267             m_vthumb->show();
00268             m_vthumb->blockSignals(true);
00269             m_vthumb->setMinimumValue(0);
00270             m_vthumb->setMaximumValue(max);
00271             m_vthumb->setDefaultValue(defaultStep);
00272             m_vthumb->setValue(layer->getCurrentVerticalZoomStep());
00273             m_vthumb->blockSignals(false);
00274 
00275 //            cerr << "Vertical thumbwheel: min 0, max " << max
00276 //                      << ", default " << defaultStep << ", value "
00277 //                      << m_vthumb->getValue() << endl;
00278 
00279         }
00280     }
00281 
00282     updateVerticalPanner();
00283 
00284     if (m_manager && m_manager->getZoomWheelsEnabled() &&
00285         width() > 120 && height() > 100) {
00286         if (!m_headsUpDisplay->isVisible()) {
00287             m_headsUpDisplay->show();
00288         }
00289         if (haveVThumb) {
00290             m_headsUpDisplay->setFixedHeight(m_vthumb->height() + m_hthumb->height());
00291             m_headsUpDisplay->move(width() - 86, height() - 86);
00292         } else {
00293             m_headsUpDisplay->setFixedHeight(m_hthumb->height());
00294             m_headsUpDisplay->move(width() - 86, height() - 16);
00295         }
00296     } else {
00297         m_headsUpDisplay->hide();
00298     }
00299 }
00300 
00301 void
00302 Pane::updateVerticalPanner()
00303 {
00304     if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return;
00305 
00306     // In principle we should show or hide the panner on the basis of
00307     // whether the top layer has adjustable display extents, and we do
00308     // that below.  However, we have no basis for layout of the panner
00309     // if the vertical scroll wheel is not also present.  So if we
00310     // have no vertical scroll wheel, we should remove the panner as
00311     // well.  Ideally any layer that implements display extents should
00312     // implement vertical zoom steps as well, but they don't all at
00313     // the moment.
00314 
00315     Layer *layer = 0;
00316     if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
00317     int discard;
00318     if (layer && layer->getVerticalZoomSteps(discard) == 0) {
00319         m_vpan->hide();
00320         return;
00321     }
00322 
00323     float vmin, vmax, dmin, dmax;
00324     if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax) && vmax != vmin) {
00325         float y0 = (dmin - vmin) / (vmax - vmin);
00326         float y1 = (dmax - vmin) / (vmax - vmin);
00327         m_vpan->blockSignals(true);
00328         m_vpan->setRectExtents(0, 1.0 - y1, 1, y1 - y0);
00329         m_vpan->blockSignals(false);
00330         m_vpan->show();
00331     } else {
00332         m_vpan->hide();
00333     }
00334 }
00335 
00336 bool
00337 Pane::shouldIlluminateLocalFeatures(const Layer *layer, QPoint &pos) const
00338 {
00339     QPoint discard;
00340     bool b0, b1;
00341 
00342     if (m_manager && m_manager->getToolModeFor(this) == ViewManager::MeasureMode) {
00343         return false;
00344     }
00345     
00346     if (m_manager && !m_manager->shouldIlluminateLocalFeatures()) {
00347         return false;
00348     }
00349 
00350     if (layer == getInteractionLayer() &&
00351         !shouldIlluminateLocalSelection(discard, b0, b1)) {
00352 
00353         pos = m_identifyPoint;
00354         return m_identifyFeatures;
00355     }
00356 
00357     return false;
00358 }
00359 
00360 bool
00361 Pane::shouldIlluminateLocalSelection(QPoint &pos,
00362                      bool &closeToLeft,
00363                      bool &closeToRight) const
00364 {
00365     if (m_identifyFeatures &&
00366         m_manager &&
00367         m_manager->getToolModeFor(this) == ViewManager::EditMode &&
00368         !m_manager->getSelections().empty() &&
00369         !selectionIsBeingEdited()) {
00370 
00371         Selection s(getSelectionAt(m_identifyPoint.x(),
00372                                    closeToLeft, closeToRight));
00373 
00374         if (!s.isEmpty()) {
00375             if (getInteractionLayer() && getInteractionLayer()->isLayerEditable()) {
00376             
00377                 pos = m_identifyPoint;
00378                 return true;
00379             }
00380         }
00381     }
00382 
00383     return false;
00384 }
00385 
00386 bool
00387 Pane::selectionIsBeingEdited() const
00388 {
00389     if (!m_editingSelection.isEmpty()) {
00390     if (m_mousePos != m_clickPos &&
00391         getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) {
00392         return true;
00393     }
00394     }
00395     return false;
00396 }
00397 
00398 void
00399 Pane::setCentreLineVisible(bool visible)
00400 {
00401     m_centreLineVisible = visible;
00402     update();
00403 }
00404 
00405 void
00406 Pane::paintEvent(QPaintEvent *e)
00407 {
00408 //    Profiler profiler("Pane::paintEvent", true);
00409 
00410     QPainter paint;
00411 
00412     QRect r(rect());
00413     if (e) r = e->rect();
00414 
00415     View::paintEvent(e);
00416 
00417     paint.begin(this);
00418     setPaintFont(paint);
00419 
00420     if (e) paint.setClipRect(r);
00421 
00422     ViewManager::ToolMode toolMode = ViewManager::NavigateMode;
00423     if (m_manager) toolMode = m_manager->getToolModeFor(this);
00424 
00425     if (m_manager &&
00426         m_mouseInWidget &&
00427         toolMode == ViewManager::MeasureMode) {
00428 
00429         for (LayerList::iterator vi = m_layerStack.end(); vi != m_layerStack.begin(); ) {
00430             --vi;
00431 
00432             std::vector<QRect> crosshairExtents;
00433 
00434             if ((*vi)->getCrosshairExtents(this, paint, m_identifyPoint,
00435                                            crosshairExtents)) {
00436                 (*vi)->paintCrosshairs(this, paint, m_identifyPoint);
00437                 break;
00438             } else if ((*vi)->isLayerOpaque()) {
00439                 break;
00440             }
00441         }
00442     }
00443 
00444     Layer *topLayer = getTopLayer();
00445     bool haveSomeTimeXAxis = false;
00446 
00447     const Model *waveformModel = 0; // just for reporting purposes
00448     const Model *workModel = 0;
00449 
00450     for (LayerList::iterator vi = m_layerStack.end(); vi != m_layerStack.begin(); ) {
00451         --vi;
00452         if (!haveSomeTimeXAxis && (*vi)->hasTimeXAxis()) {
00453             haveSomeTimeXAxis = true;
00454         }
00455         if (dynamic_cast<WaveformLayer *>(*vi)) {
00456             waveformModel = (*vi)->getModel();
00457             workModel = waveformModel;
00458         } else {
00459             Model *m = (*vi)->getModel();
00460             if (dynamic_cast<WaveFileModel *>(m)) {
00461                 workModel = m;
00462             } else if (m && dynamic_cast<WaveFileModel *>(m->getSourceModel())) {
00463                 workModel = m->getSourceModel();
00464             }
00465         }
00466                 
00467         if (waveformModel && workModel && haveSomeTimeXAxis) break;
00468     }
00469 
00470     m_scaleWidth = 0;
00471 
00472     if (workModel && hasTopLayerTimeXAxis()) {
00473         drawModelTimeExtents(r, paint, workModel);
00474     }
00475 
00476     if (m_manager && m_manager->shouldShowVerticalScale() && topLayer) {
00477         drawVerticalScale(r, topLayer, paint);
00478     }
00479 
00480     if (m_identifyFeatures &&
00481         m_manager && m_manager->shouldIlluminateLocalFeatures() &&
00482         topLayer) {
00483         drawFeatureDescription(topLayer, paint);
00484     }
00485     
00486     int sampleRate = getModelsSampleRate();
00487     paint.setBrush(Qt::NoBrush);
00488 
00489     if (m_centreLineVisible &&
00490         m_manager &&
00491         m_manager->shouldShowCentreLine()) {
00492         drawCentreLine(sampleRate, paint, !haveSomeTimeXAxis);
00493     }
00494     
00495     paint.setPen(QColor(50, 50, 50));
00496 
00497     if (waveformModel &&
00498         sampleRate &&
00499         m_manager &&
00500         m_manager->shouldShowDuration()) {
00501         drawDurationAndRate(r, waveformModel, sampleRate, paint);
00502     }
00503 
00504     bool haveWorkTitle = false;
00505 
00506     if (workModel &&
00507         m_manager &&
00508         m_manager->shouldShowWorkTitle()) {
00509         drawWorkTitle(r, paint, workModel);
00510         haveWorkTitle = true;
00511     }
00512 
00513     if (workModel &&
00514         m_manager &&
00515         m_manager->getAlignMode()) {
00516         drawAlignmentStatus(r, paint, workModel, haveWorkTitle);
00517     }
00518 
00519     if (m_manager &&
00520         m_manager->shouldShowLayerNames()) {
00521         drawLayerNames(r, paint);
00522     }
00523 
00524     if (m_shiftPressed && m_clickedInRange &&
00525         (toolMode == ViewManager::NavigateMode || m_navigating)) {
00526 
00528         //selection block
00529         
00530         paint.setPen(Qt::blue);
00532         paint.drawRect(m_clickPos.x(), m_clickPos.y(),
00533                        m_mousePos.x() - m_clickPos.x(),
00534                        m_mousePos.y() - m_clickPos.y());
00535 
00536     }
00537 
00538     if (toolMode == ViewManager::MeasureMode && topLayer) {
00539         bool showFocus = false;
00540         if (!m_manager || !m_manager->isPlaying()) showFocus = true;
00541         topLayer->paintMeasurementRects(this, paint, showFocus, m_identifyPoint);
00542     }
00543     
00544     if (selectionIsBeingEdited()) {
00545         drawEditingSelection(paint);
00546     }
00547 
00548     paint.end();
00549 }
00550 
00551 int
00552 Pane::getVerticalScaleWidth() const
00553 {
00554     if (m_scaleWidth > 0) return m_scaleWidth;
00555     else return 0;
00556 }
00557 
00558 void
00559 Pane::drawVerticalScale(QRect r, Layer *topLayer, QPainter &paint)
00560 {
00561     Layer *scaleLayer = 0;
00562 
00563     float min, max;
00564     bool log;
00565     QString unit;
00566 
00567     // If the top layer has no scale and reports no display
00568     // extents, but does report a unit, then the scale should be
00569     // drawn from any underlying layer with a scale and that unit.
00570     // If the top layer has no scale and no value extents at all,
00571     // then the scale should be drawn from any underlying layer
00572     // with a scale regardless of unit.
00573 
00574     int sw = topLayer->getVerticalScaleWidth
00575         (this, m_manager->shouldShowVerticalColourScale(), paint);
00576 
00577     if (sw > 0) {
00578         scaleLayer = topLayer;
00579         m_scaleWidth = sw;
00580 
00581     } else {
00582 
00583         bool hasDisplayExtents = topLayer->getDisplayExtents(min, max);
00584         bool hasValueExtents = topLayer->getValueExtents(min, max, log, unit);
00585             
00586         if (!hasDisplayExtents) {
00587 
00588             if (!hasValueExtents) {
00589 
00590                 for (LayerList::iterator vi = m_layerStack.end();
00591                      vi != m_layerStack.begin(); ) {
00592                         
00593                     --vi;
00594                         
00595                     if ((*vi) == topLayer) continue;
00596                         
00597                     sw = (*vi)->getVerticalScaleWidth
00598                         (this, m_manager->shouldShowVerticalColourScale(), paint);
00599                         
00600                     if (sw > 0) {
00601                         scaleLayer = *vi;
00602                         m_scaleWidth = sw;
00603                         break;
00604                     }
00605                 }
00606             } else if (unit != "") { // && hasValueExtents && !hasDisplayExtents
00607 
00608                 QString requireUnit = unit;
00609 
00610                 for (LayerList::iterator vi = m_layerStack.end();
00611                      vi != m_layerStack.begin(); ) {
00612                         
00613                     --vi;
00614                         
00615                     if ((*vi) == topLayer) continue;
00616                         
00617                     if ((*vi)->getDisplayExtents(min, max)) {
00618                             
00619                         // search no further than this: if the
00620                         // scale from this layer isn't suitable,
00621                         // we'll have to draw no scale (else we'd
00622                         // risk ending up with the wrong scale)
00623                             
00624                         if ((*vi)->getValueExtents(min, max, log, unit) &&
00625                             unit == requireUnit) {
00626 
00627                             sw = (*vi)->getVerticalScaleWidth
00628                                 (this, m_manager->shouldShowVerticalColourScale(), paint);
00629                             if (sw > 0) {
00630                                 scaleLayer = *vi;
00631                                 m_scaleWidth = sw;
00632                             }
00633                         }
00634                         break;
00635                     }
00636                 }
00637             }
00638         }
00639     }
00640 
00641     if (!scaleLayer) m_scaleWidth = 0;
00642         
00643     if (m_scaleWidth > 0 && r.left() < m_scaleWidth) {
00644 
00645 //      Profiler profiler("Pane::paintEvent - painting vertical scale", true);
00646 
00647 //      SVDEBUG << "Pane::paintEvent: calling paint.save() in vertical scale block" << endl;
00648         paint.save();
00649             
00650         paint.setPen(getForeground());
00651         paint.setBrush(getBackground());
00652         paint.drawRect(0, -1, m_scaleWidth, height()+1);
00653         
00654         paint.setBrush(Qt::NoBrush);
00655         scaleLayer->paintVerticalScale
00656             (this, m_manager->shouldShowVerticalColourScale(),
00657              paint, QRect(0, 0, m_scaleWidth, height()));
00658         
00659         paint.restore();
00660     }
00661 }
00662             
00663 void
00664 Pane::drawFeatureDescription(Layer *topLayer, QPainter &paint)
00665 {
00666     QPoint pos = m_identifyPoint;
00667     QString desc = topLayer->getFeatureDescription(this, pos);
00668         
00669     if (desc != "") {
00670         
00671         paint.save();
00672         
00673         int tabStop =
00674             paint.fontMetrics().width(tr("Some lengthy prefix:"));
00675         
00676         QRect boundingRect = 
00677             paint.fontMetrics().boundingRect
00678             (rect(),
00679              Qt::AlignRight | Qt::AlignTop | Qt::TextExpandTabs,
00680              desc, tabStop);
00681         
00682         if (hasLightBackground()) {
00683             paint.setPen(Qt::NoPen);
00684             paint.setBrush(QColor(250, 250, 250, 200));
00685         } else {
00686             paint.setPen(Qt::NoPen);
00687             paint.setBrush(QColor(50, 50, 50, 200));
00688         }
00689         
00690         int extra = paint.fontMetrics().descent();
00691         paint.drawRect(width() - boundingRect.width() - 10 - extra,
00692                        10 - extra,
00693                        boundingRect.width() + 2 * extra,
00694                        boundingRect.height() + extra);
00695         
00696         if (hasLightBackground()) {
00697             paint.setPen(QColor(150, 20, 0));
00698         } else {
00699             paint.setPen(QColor(255, 150, 100));
00700         }
00701         
00702         QTextOption option;
00703         option.setWrapMode(QTextOption::NoWrap);
00704         option.setAlignment(Qt::AlignRight | Qt::AlignTop);
00705         option.setTabStop(tabStop);
00706         paint.drawText(QRectF(width() - boundingRect.width() - 10, 10,
00707                               boundingRect.width(),
00708                               boundingRect.height()),
00709                        desc,
00710                        option);
00711         
00712         paint.restore();
00713     }
00714 }
00715 
00716 void
00717 Pane::drawCentreLine(int sampleRate, QPainter &paint, bool omitLine)
00718 {
00719     int fontHeight = paint.fontMetrics().height();
00720     int fontAscent = paint.fontMetrics().ascent();
00721 
00722     QColor c = QColor(0, 0, 0);
00723     if (!hasLightBackground()) {
00724         c = QColor(240, 240, 240);
00725     }
00726 
00727     paint.setPen(c);
00728     int x = width() / 2;
00729 
00730     if (!omitLine) {
00731         paint.drawLine(x, 0, x, height() - 1);
00732         paint.drawLine(x-1, 1, x+1, 1);
00733         paint.drawLine(x-2, 0, x+2, 0);
00734         paint.drawLine(x-1, height() - 2, x+1, height() - 2);
00735         paint.drawLine(x-2, height() - 1, x+2, height() - 1);
00736     }
00737     
00738     paint.setPen(QColor(50, 50, 50));
00739     
00740     int y = height() - fontHeight + fontAscent - 6;
00741     
00742     LayerList::iterator vi = m_layerStack.end();
00743     
00744     if (vi != m_layerStack.begin()) {
00745         
00746         switch ((*--vi)->getPreferredFrameCountPosition()) {
00747             
00748         case Layer::PositionTop:
00749             y = fontAscent + 6;
00750             break;
00751             
00752         case Layer::PositionMiddle:
00753             y = (height() - fontHeight) / 2
00754                 + fontAscent;
00755             break;
00756             
00757         case Layer::PositionBottom:
00758             // y already set correctly
00759             break;
00760         }
00761     }
00762     
00763     if (m_manager && m_manager->shouldShowFrameCount()) {
00764         
00765         if (sampleRate) {
00766 
00767             QString text(QString::fromStdString
00768                          (RealTime::frame2RealTime
00769                           (m_centreFrame, sampleRate)
00770                           .toText(true)));
00771             
00772             int tw = paint.fontMetrics().width(text);
00773             int x = width()/2 - 4 - tw;
00774             
00775             drawVisibleText(paint, x, y, text, OutlinedText);
00776         }
00777         
00778         QString text = QString("%1").arg(m_centreFrame);
00779         
00780         int x = width()/2 + 4;
00781         
00782         drawVisibleText(paint, x, y, text, OutlinedText);
00783     }
00784 }
00785 
00786 void
00787 Pane::drawModelTimeExtents(QRect r, QPainter &paint, const Model *model)
00788 {
00789     int x0 = getXForFrame(model->getStartFrame());
00790     int x1 = getXForFrame(model->getEndFrame());
00791 
00792     paint.save();
00793 
00794     QBrush brush;
00795 
00796     if (hasLightBackground()) {
00797         brush = QBrush(QColor("#f8f8f8"));
00798         paint.setPen(Qt::black);
00799     } else {
00800         brush = QBrush(QColor("#101010"));
00801         paint.setPen(Qt::white);
00802     }
00803 
00804     if (x0 > r.x()) {
00805         paint.fillRect(0, 0, x0, height(), brush);
00806         paint.drawLine(x0, 0, x0, height());
00807     }
00808 
00809     if (x1 < r.x() + r.width()) {
00810         paint.fillRect(x1, 0, width() - x1, height(), brush);
00811         paint.drawLine(x1, 0, x1, height());
00812     }
00813 
00814     paint.restore();
00815 }
00816 
00817 void
00818 Pane::drawAlignmentStatus(QRect r, QPainter &paint, const Model *model,
00819                           bool down)
00820 {
00821     const Model *reference = model->getAlignmentReference();
00822 /*
00823     if (!reference) {
00824         cerr << "Pane[" << this << "]::drawAlignmentStatus: No reference" << endl;
00825     } else if (reference == model) {
00826         cerr << "Pane[" << this << "]::drawAlignmentStatus: This is the reference model" << endl;
00827     } else {
00828         cerr << "Pane[" << this << "]::drawAlignmentStatus: This is not the reference" << endl;
00829     }
00830 */
00831     QString text;
00832     int completion = 100;
00833 
00834     if (reference == model) {
00835         text = tr("Reference");
00836     } else if (!reference) {
00837         text = tr("Unaligned");
00838     } else {
00839         completion = model->getAlignmentCompletion();
00840         if (completion == 0) {
00841             text = tr("Unaligned");
00842         } else if (completion < 100) {
00843             text = tr("Aligning: %1%").arg(completion);
00844         } else {
00845             text = tr("Aligned");
00846         }
00847     }
00848 
00849     paint.save();
00850     QFont font(paint.font());
00851     font.setBold(true);
00852     paint.setFont(font);
00853     if (completion < 100) paint.setBrush(Qt::red);
00854 
00855     int y = 5;
00856     if (down) y += paint.fontMetrics().height();
00857     int w = paint.fontMetrics().width(text);
00858     int h = paint.fontMetrics().height();
00859     if (r.top() > h + y || r.left() > w + m_scaleWidth + 5) {
00860         paint.restore();
00861         return;
00862     }
00863     
00864     drawVisibleText(paint, m_scaleWidth + 5,
00865                     paint.fontMetrics().ascent() + y, text, OutlinedText);
00866 
00867     paint.restore();
00868 }
00869 
00870 void
00871 Pane::modelAlignmentCompletionChanged()
00872 {
00873     View::modelAlignmentCompletionChanged();
00874     update(QRect(0, 0, 300, 100));
00875 }
00876 
00877 void
00878 Pane::drawWorkTitle(QRect r, QPainter &paint, const Model *model)
00879 {
00880     QString title = model->getTitle();
00881     QString maker = model->getMaker();
00882 //SVDEBUG << "Pane::drawWorkTitle: title=\"" << title//<< "\", maker=\"" << maker << "\"" << endl;
00883     if (title == "") return;
00884 
00885     QString text = title;
00886     if (maker != "") {
00887         text = tr("%1 - %2").arg(title).arg(maker);
00888     }
00889     
00890     paint.save();
00891     QFont font(paint.font());
00892     font.setItalic(true);
00893     paint.setFont(font);
00894 
00895     int y = 5;
00896     int w = paint.fontMetrics().width(text);
00897     int h = paint.fontMetrics().height();
00898     if (r.top() > h + y || r.left() > w + m_scaleWidth + 5) {
00899         paint.restore();
00900         return;
00901     }
00902     
00903     drawVisibleText(paint, m_scaleWidth + 5,
00904                     paint.fontMetrics().ascent() + y, text, OutlinedText);
00905 
00906     paint.restore();
00907 }
00908 
00909 void
00910 Pane::drawLayerNames(QRect r, QPainter &paint)
00911 {
00912     int fontHeight = paint.fontMetrics().height();
00913     int fontAscent = paint.fontMetrics().ascent();
00914 
00915     int lly = height() - 6;
00916     if (m_manager->getZoomWheelsEnabled()) {
00917         lly -= 20;
00918     }
00919 
00920     if (r.y() + r.height() < lly - int(m_layerStack.size()) * fontHeight) {
00921         return;
00922     }
00923 
00924     QStringList texts;
00925     std::vector<QPixmap> pixmaps;
00926     for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
00927         texts.push_back((*i)->getLayerPresentationName());
00928 //        cerr << "Pane " << this << ": Layer presentation name for " << *i << ": "
00929 //                  << texts[texts.size()-1] << endl;
00930         pixmaps.push_back((*i)->getLayerPresentationPixmap
00931                           (QSize(fontAscent, fontAscent)));
00932     }
00933 
00934     int maxTextWidth = width() / 3;
00935     texts = TextAbbrev::abbreviate(texts, paint.fontMetrics(), maxTextWidth);
00936 
00937     int llx = width() - maxTextWidth - 5;
00938     if (m_manager->getZoomWheelsEnabled()) {
00939         llx -= 36;
00940     }
00941     
00942     if (r.x() + r.width() >= llx - fontAscent - 3) {
00943     
00944         for (int i = 0; i < texts.size(); ++i) {
00945 
00946 //            cerr << "Pane "<< this << ": text " << i << ": " << texts[i] << endl;
00947             
00948             if (i + 1 == texts.size()) {
00949                 paint.setPen(getForeground());
00950             }
00951             
00952             drawVisibleText(paint, llx,
00953                             lly - fontHeight + fontAscent,
00954                             texts[i], OutlinedText);
00955 
00956             if (!pixmaps[i].isNull()) {
00957                 paint.drawPixmap(llx - fontAscent - 3,
00958                                  lly - fontHeight + (fontHeight-fontAscent)/2,
00959                                  pixmaps[i]);
00960             }
00961             
00962             lly -= fontHeight;
00963         }
00964     }
00965 }
00966 
00967 void
00968 Pane::drawEditingSelection(QPainter &paint)
00969 {
00970     int offset = m_mousePos.x() - m_clickPos.x();
00971 
00972     int origStart = m_editingSelection.getStartFrame();
00973 
00974     int p0 = getXForFrame(origStart) + offset;
00975     int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
00976 
00977     if (m_editingSelectionEdge < 0) {
00978         p1 = getXForFrame(m_editingSelection.getEndFrame());
00979     } else if (m_editingSelectionEdge > 0) {
00980         p0 = getXForFrame(m_editingSelection.getStartFrame());
00981     }
00982     
00983     int newStart = getFrameForX(p0);
00984     int newEnd = getFrameForX(p1);
00985     
00986     paint.save();
00987     paint.setPen(QPen(getForeground(), 2));
00988 
00989     int fontHeight = paint.fontMetrics().height();
00990     int fontAscent = paint.fontMetrics().ascent();
00991     int sampleRate = getModelsSampleRate();
00992     QString startText, endText, offsetText;
00993     startText = QString("%1").arg(newStart);
00994     endText = QString("%1").arg(newEnd);
00995     offsetText = QString("%1").arg(newStart - origStart);
00996     if (newStart >= origStart) {
00997         offsetText = tr("+%1").arg(offsetText);
00998     }
00999     if (sampleRate) {
01000         startText = QString("%1 / %2")
01001             .arg(QString::fromStdString
01002                  (RealTime::frame2RealTime(newStart, sampleRate).toText()))
01003             .arg(startText);
01004         endText = QString("%1 / %2")
01005             .arg(QString::fromStdString
01006                  (RealTime::frame2RealTime(newEnd, sampleRate).toText()))
01007             .arg(endText);
01008         offsetText = QString("%1 / %2")
01009             .arg(QString::fromStdString
01010                  (RealTime::frame2RealTime(newStart - origStart, sampleRate).toText()))
01011             .arg(offsetText);
01012         if (newStart >= origStart) {
01013             offsetText = tr("+%1").arg(offsetText);
01014         }
01015     }
01016     drawVisibleText(paint, p0 + 2, fontAscent + fontHeight + 4, startText, OutlinedText);
01017     drawVisibleText(paint, p1 + 2, fontAscent + fontHeight + 4, endText, OutlinedText);
01018     drawVisibleText(paint, p0 + 2, fontAscent + fontHeight*2 + 4, offsetText, OutlinedText);
01019     drawVisibleText(paint, p1 + 2, fontAscent + fontHeight*2 + 4, offsetText, OutlinedText);
01020     
01022     
01023     if (m_editingSelectionEdge < 0) {
01024         paint.drawLine(p0, 1, p1, 1);
01025         paint.drawLine(p0, 0, p0, height());
01026         paint.drawLine(p0, height() - 1, p1, height() - 1);
01027     } else if (m_editingSelectionEdge > 0) {
01028         paint.drawLine(p0, 1, p1, 1);
01029         paint.drawLine(p1, 0, p1, height());
01030         paint.drawLine(p0, height() - 1, p1, height() - 1);
01031     } else {
01032         paint.setBrush(Qt::NoBrush);
01033         paint.drawRect(p0, 1, p1 - p0, height() - 2);
01034     }
01035     paint.restore();
01036 }
01037 
01038 void
01039 Pane::drawDurationAndRate(QRect r, const Model *waveformModel,
01040                           int sampleRate, QPainter &paint)
01041 {
01042     int fontHeight = paint.fontMetrics().height();
01043     int fontAscent = paint.fontMetrics().ascent();
01044 
01045     if (r.y() + r.height() < height() - fontHeight - 6) return;
01046 
01047     int modelRate = waveformModel->getSampleRate();
01048     int nativeRate = waveformModel->getNativeRate();
01049     int playbackRate = m_manager->getPlaybackSampleRate();
01050     int outputRate = m_manager->getOutputSampleRate();
01051         
01052     QString srNote = "";
01053 
01054     // Show (R) for waveform models that have been resampled or will
01055     // be resampled on playback, and (X) for waveform models that will
01056     // be played at the wrong rate because their rate differs from the
01057     // current playback rate (which is not necessarily that of the
01058     // main model).
01059 
01060     if (playbackRate != 0) {
01061         if (modelRate == playbackRate) {
01062             if (modelRate != outputRate || modelRate != nativeRate) {
01063                 srNote = " " + tr("(R)");
01064             }
01065         } else {
01066             srNote = " " + tr("(X)");
01067         }
01068     }
01069 
01070     QString desc = tr("%1 / %2Hz%3")
01071         .arg(RealTime::frame2RealTime(waveformModel->getEndFrame(),
01072                                       sampleRate)
01073              .toText(false).c_str())
01074         .arg(nativeRate)
01075         .arg(srNote);
01076 
01077     int x = m_scaleWidth + 5;
01078     int pbw = getProgressBarWidth();
01079     if (x < pbw + 5) x = pbw + 5;
01080 
01081     if (r.x() < x + paint.fontMetrics().width(desc)) {
01082         drawVisibleText(paint, x,
01083                         height() - fontHeight + fontAscent - 6,
01084                         desc, OutlinedText);
01085     }
01086 }
01087 
01088 bool
01089 Pane::render(QPainter &paint, int xorigin, int f0, int f1)
01090 {
01091     if (!View::render(paint, xorigin + m_scaleWidth, f0, f1)) {
01092         return false;
01093     }
01094 
01095     if (m_scaleWidth > 0) {
01096 
01097         Layer *layer = getTopLayer();
01098 
01099         if (layer) {
01100             
01101             paint.save();
01102             
01103             paint.setPen(getForeground());
01104             paint.setBrush(getBackground());
01105             paint.drawRect(xorigin, -1, m_scaleWidth, height()+1);
01106             
01107             paint.setBrush(Qt::NoBrush);
01108             layer->paintVerticalScale
01109                 (this, m_manager->shouldShowVerticalColourScale(),
01110                  paint, QRect(xorigin, 0, m_scaleWidth, height()));
01111             
01112             paint.restore();
01113         }
01114     }
01115 
01116     return true;
01117 }
01118 
01119 QImage *
01120 Pane::toNewImage(int f0, int f1)
01121 {
01122     int x0 = f0 / getZoomLevel();
01123     int x1 = f1 / getZoomLevel();
01124 
01125     QImage *image = new QImage(x1 - x0 + m_scaleWidth,
01126                                height(), QImage::Format_RGB32);
01127 
01128     int formerScaleWidth = m_scaleWidth;
01129             
01130     if (m_manager && m_manager->shouldShowVerticalScale()) {
01131         Layer *layer = getTopLayer();
01132         if (layer) {
01133             QPainter paint(image);
01134             m_scaleWidth = layer->getVerticalScaleWidth
01135                 (this, m_manager->shouldShowVerticalColourScale(), paint);
01136         }
01137     } else {
01138         m_scaleWidth = 0;
01139     }
01140 
01141     if (m_scaleWidth != formerScaleWidth) {
01142         delete image;
01143         image = new QImage(x1 - x0 + m_scaleWidth,
01144                            height(), QImage::Format_RGB32);
01145     }        
01146 
01147     QPainter *paint = new QPainter(image);
01148     if (!render(*paint, 0, f0, f1)) {
01149         delete paint;
01150         delete image;
01151         return 0;
01152     } else {
01153         delete paint;
01154         return image;
01155     }
01156 }
01157 
01158 QSize
01159 Pane::getImageSize(int f0, int f1)
01160 {
01161     QSize s = View::getImageSize(f0, f1);
01162     QImage *image = new QImage(100, 100, QImage::Format_RGB32);
01163     QPainter paint(image);
01164 
01165     int sw = 0;
01166     if (m_manager && m_manager->shouldShowVerticalScale()) {
01167         Layer *layer = getTopLayer();
01168         if (layer) {
01169             sw = layer->getVerticalScaleWidth
01170                 (this, m_manager->shouldShowVerticalColourScale(), paint);
01171         }
01172     }
01173     
01174     return QSize(sw + s.width(), s.height());
01175 }
01176 
01177 int
01178 Pane::getFirstVisibleFrame() const
01179 {
01180     int f0 = getFrameForX(m_scaleWidth);
01181     int f = View::getFirstVisibleFrame();
01182     if (f0 < 0 || f0 < long(f)) return f;
01183     return f0;
01184 }
01185 
01186 Selection
01187 Pane::getSelectionAt(int x, bool &closeToLeftEdge, bool &closeToRightEdge) const
01188 {
01189     closeToLeftEdge = closeToRightEdge = false;
01190 
01191     if (!m_manager) return Selection();
01192 
01193     int testFrame = getFrameForX(x - 5);
01194     if (testFrame < 0) {
01195     testFrame = getFrameForX(x);
01196     if (testFrame < 0) return Selection();
01197     }
01198 
01199     Selection selection = m_manager->getContainingSelection(testFrame, true);
01200     if (selection.isEmpty()) return selection;
01201 
01202     int lx = getXForFrame(selection.getStartFrame());
01203     int rx = getXForFrame(selection.getEndFrame());
01204     
01205     int fuzz = 2;
01206     if (x < lx - fuzz || x > rx + fuzz) return Selection();
01207 
01208     int width = rx - lx;
01209     fuzz = 3;
01210     if (width < 12) fuzz = width / 4;
01211     if (fuzz < 1) fuzz = 1;
01212 
01213     if (x < lx + fuzz) closeToLeftEdge = true;
01214     if (x > rx - fuzz) closeToRightEdge = true;
01215 
01216     return selection;
01217 }
01218 
01219 bool
01220 Pane::canTopLayerMoveVertical()
01221 {
01222     float vmin, vmax, dmin, dmax;
01223     if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return false;
01224     if (dmin <= vmin && dmax >= vmax) return false;
01225     return true;
01226 }
01227 
01228 bool
01229 Pane::getTopLayerDisplayExtents(float &vmin, float &vmax,
01230                                 float &dmin, float &dmax,
01231                                 QString *unit) 
01232 {
01233     Layer *layer = getTopLayer();
01234     if (!layer) return false;
01235     bool vlog;
01236     QString vunit;
01237     bool rv = (layer->getValueExtents(vmin, vmax, vlog, vunit) &&
01238                layer->getDisplayExtents(dmin, dmax));
01239     if (unit) *unit = vunit;
01240     return rv;
01241 }
01242 
01243 bool
01244 Pane::setTopLayerDisplayExtents(float dmin, float dmax)
01245 {
01246     Layer *layer = getTopLayer();
01247     if (!layer) return false;
01248     return layer->setDisplayExtents(dmin, dmax);
01249 }
01250 
01251 void
01252 Pane::registerShortcuts(KeyReference &kr)
01253 {
01254     kr.setCategory(tr("Zoom"));
01255     kr.registerAlternativeShortcut(tr("Zoom In"), tr("Wheel Up"));
01256     kr.registerAlternativeShortcut(tr("Zoom Out"), tr("Wheel Down"));
01257 
01258     kr.setCategory(tr("General Pane Mouse Actions"));
01259     
01260     kr.registerShortcut(tr("Zoom"), tr("Wheel"),
01261                         tr("Zoom in or out in time axis"));
01262     kr.registerShortcut(tr("Scroll"), tr("Ctrl+Wheel"),
01263                         tr("Scroll rapidly left or right in time axis"));
01264     kr.registerShortcut(tr("Zoom Vertically"), tr("Shift+Wheel"), 
01265                         tr("Zoom in or out in the vertical axis"));
01266     kr.registerShortcut(tr("Scroll Vertically"), tr("Alt+Wheel"), 
01267                         tr("Scroll up or down in the vertical axis"));
01268     kr.registerShortcut(tr("Navigate"), tr("Middle"), 
01269                         tr("Click middle button and drag to navigate with any tool"));
01270     kr.registerShortcut(tr("Relocate"), tr("Double-Click Middle"), 
01271                         tr("Double-click middle button to relocate with any tool"));
01272     kr.registerShortcut(tr("Menu"), tr("Right"),
01273                         tr("Show pane context menu"));
01274 }
01275 
01276 Layer *
01277 Pane::getTopFlexiNoteLayer()
01278 {
01279     for (int i = int(m_layerStack.size()) - 1; i >= 0; --i) {
01280         if (LayerFactory::getInstance()->getLayerType(m_layerStack[i]) ==
01281             LayerFactory::FlexiNotes) {
01282             return m_layerStack[i];
01283         }
01284     }
01285     return 0;
01286 }
01287 
01288 void
01289 Pane::mousePressEvent(QMouseEvent *e)
01290 {
01291     if (e->buttons() & Qt::RightButton) {
01292         emit contextHelpChanged("");
01293         emit rightButtonMenuRequested(mapToGlobal(e->pos()));
01294         return;
01295     }
01296 
01297 //    cerr << "mousePressEvent" << endl;
01298 
01299     m_clickPos = e->pos();
01300     m_mousePos = m_clickPos;
01301     m_clickedInRange = true;
01302     m_editingSelection = Selection();
01303     m_editingSelectionEdge = 0;
01304     m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
01305     m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
01306     m_altPressed = (e->modifiers() & Qt::AltModifier);
01307     m_dragMode = UnresolvedDrag;
01308 
01309     ViewManager::ToolMode mode = ViewManager::NavigateMode;
01310     if (m_manager) mode = m_manager->getToolModeFor(this);
01311 
01312     m_navigating = false;
01313     m_resizing = false;
01314     m_editing = false;
01315     m_releasing = false;
01316 
01317     if (mode == ViewManager::NavigateMode ||
01318         (e->buttons() & Qt::MidButton) ||
01319         (mode == ViewManager::MeasureMode &&
01320          (e->buttons() & Qt::LeftButton) && m_shiftPressed)) {
01321 
01322         if (mode != ViewManager::NavigateMode) {
01323             setCursor(Qt::PointingHandCursor);
01324         }
01325 
01326         m_navigating = true;
01327         m_dragCentreFrame = m_centreFrame;
01328         m_dragStartMinValue = 0;
01329         
01330         float vmin, vmax, dmin, dmax;
01331         if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
01332             m_dragStartMinValue = dmin;
01333         }
01334 
01335         if (m_followPlay == PlaybackScrollPage) {
01336             // Schedule a play-head move to the mouse frame
01337             // location. This will happen only if nothing else of
01338             // interest happens (double-click, drag) before the
01339             // timeout.
01340             schedulePlaybackFrameMove(getFrameForX(e->x()));
01341         }
01342 
01343     } else if (mode == ViewManager::SelectMode) {
01344 
01345         if (!hasTopLayerTimeXAxis()) return;
01346 
01347         bool closeToLeft = false, closeToRight = false;
01348         Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight);
01349 
01350         if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
01351 
01352             m_manager->removeSelection(selection);
01353 
01354             if (closeToLeft) {
01355                 m_selectionStartFrame = selection.getEndFrame();
01356             } else {
01357                 m_selectionStartFrame = selection.getStartFrame();
01358             }
01359             
01360             m_manager->setInProgressSelection(selection, false);
01361             m_resizing = true;
01362             
01363         } else {
01364             
01365             int mouseFrame = getFrameForX(e->x());
01366             int resolution = 1;
01367             int snapFrame = mouseFrame;
01368     
01369             Layer *layer = getInteractionLayer();
01370             if (layer && !m_shiftPressed) {
01371                 layer->snapToFeatureFrame(this, snapFrame,
01372                                           resolution, Layer::SnapLeft);
01373             }
01374         
01375             if (snapFrame < 0) snapFrame = 0;
01376             m_selectionStartFrame = snapFrame;
01377             if (m_manager) {
01378                 m_manager->setInProgressSelection
01379                     (Selection(alignToReference(snapFrame),
01380                                alignToReference(snapFrame + resolution)),
01381                      !m_ctrlPressed);
01382             }
01383 
01384             m_resizing = false;
01385 
01386             if (m_followPlay == PlaybackScrollPage) {
01387                 // Schedule a play-head move to the mouse frame
01388                 // location. This will happen only if nothing else of
01389                 // interest happens (double-click, drag) before the
01390                 // timeout.
01391                 schedulePlaybackFrameMove(mouseFrame);
01392             }
01393         }
01394 
01395         update();
01396 
01397     } else if (mode == ViewManager::DrawMode) {
01398 
01399         Layer *layer = getInteractionLayer();
01400         if (layer && layer->isLayerEditable()) {
01401             layer->drawStart(this, e);
01402         }
01403 
01404     } else if (mode == ViewManager::EraseMode) {
01405 
01406         Layer *layer = getInteractionLayer();
01407         if (layer && layer->isLayerEditable()) {
01408             layer->eraseStart(this, e);
01409         }
01410 
01411         // GF: handle mouse press for NoteEditMode 
01412     } else if (mode == ViewManager::NoteEditMode) {
01413 
01414         std::cerr << "mouse pressed in note edit mode" << std::endl;
01415         Layer *layer = getTopFlexiNoteLayer();
01416         if (layer) {
01417             layer->splitStart(this, e); 
01418         }
01419 
01420     } else if (mode == ViewManager::EditMode) {
01421 
01422         // Do nothing here -- we'll do it in mouseMoveEvent when the
01423         // drag threshold has been passed
01424 
01425     } else if (mode == ViewManager::MeasureMode) {
01426 
01427         Layer *layer = getTopLayer();
01428         if (layer) layer->measureStart(this, e);
01429         update();
01430     }
01431 
01432     emit paneInteractedWith();
01433 }
01434 
01435 void
01436 Pane::schedulePlaybackFrameMove(int frame)
01437 {
01438     m_playbackFrameMoveTo = frame;
01439     m_playbackFrameMoveScheduled = true;
01440     QTimer::singleShot(QApplication::doubleClickInterval() + 10, this,
01441                        SLOT(playbackScheduleTimerElapsed()));
01442 }
01443 
01444 void
01445 Pane::playbackScheduleTimerElapsed()
01446 {
01447     if (m_playbackFrameMoveScheduled) {
01448         m_manager->setPlaybackFrame(m_playbackFrameMoveTo);
01449         m_playbackFrameMoveScheduled = false;
01450     }
01451 }
01452 
01453 void
01454 Pane::mouseReleaseEvent(QMouseEvent *e)
01455 {
01456     if (e && (e->buttons() & Qt::RightButton)) {
01457         return;
01458     }
01459 
01460 //    cerr << "mouseReleaseEvent" << endl;
01461 
01462     ViewManager::ToolMode mode = ViewManager::NavigateMode;
01463     if (m_manager) mode = m_manager->getToolModeFor(this);
01464 
01465     m_releasing = true;
01466 
01467     if (m_clickedInRange) {
01468         mouseMoveEvent(e);
01469     }
01470 
01471     int mouseFrame = e ? getFrameForX(e->x()) : 0;
01472     if (mouseFrame < 0) mouseFrame = 0;
01473 
01474     if (m_navigating || mode == ViewManager::NavigateMode) {
01475 
01476         m_navigating = false;
01477 
01478         if (mode != ViewManager::NavigateMode) {
01479             // restore cursor
01480             toolModeChanged();
01481         }
01482 
01483         if (m_shiftPressed) {
01484 
01485             int x0 = std::min(m_clickPos.x(), m_mousePos.x());
01486             int x1 = std::max(m_clickPos.x(), m_mousePos.x());
01487 
01488             int y0 = std::min(m_clickPos.y(), m_mousePos.y());
01489             int y1 = std::max(m_clickPos.y(), m_mousePos.y());
01490 
01491             emit regionOutlined(QRect(x0, y0, x1 - x0, y1 - y0));
01492         }
01493 
01494     } else if (mode == ViewManager::SelectMode) {
01495 
01496         if (!hasTopLayerTimeXAxis()) {
01497             m_releasing = false;
01498             return;
01499         }
01500 
01501         if (m_manager && m_manager->haveInProgressSelection()) {
01502 
01503             //cerr << "JTEST: release with selection" << endl;
01504             bool exclusive;
01505             Selection selection = m_manager->getInProgressSelection(exclusive);
01506         
01507             if (selection.getEndFrame() < selection.getStartFrame() + 2) {
01508                 selection = Selection();
01509             }
01510         
01511             m_manager->clearInProgressSelection();
01512         
01513             if (exclusive) {
01514                 m_manager->setSelection(selection);
01515             } else {
01516                 m_manager->addSelection(selection);
01517             }
01518         }
01519     
01520         update();
01521 
01522     } else if (mode == ViewManager::DrawMode) {
01523 
01524         Layer *layer = getInteractionLayer();
01525         if (layer && layer->isLayerEditable()) {
01526             layer->drawEnd(this, e);
01527             update();
01528         }
01529 
01530     } else if (mode == ViewManager::EraseMode) {
01531 
01532         Layer *layer = getInteractionLayer();
01533         if (layer && layer->isLayerEditable()) {
01534             layer->eraseEnd(this, e);
01535             update();
01536         }
01537 
01538     } else if (mode == ViewManager::NoteEditMode) {
01539     
01540         //GF: handle mouse release for NoteEditMode (note: works but will need to re-think this a bit later)
01541         Layer *layer = getTopFlexiNoteLayer();
01542 
01543         if (layer) {
01544             layer->splitEnd(this, e);
01545             update();
01546 
01547             if (m_editing) {
01548                 if (!editSelectionEnd(e)) {
01549                     layer->editEnd(this, e);
01550                     update();
01551                 }
01552             }
01553         } 
01554 
01555     } else if (mode == ViewManager::EditMode) {
01556         
01557         if (m_editing) {
01558             if (!editSelectionEnd(e)) {
01559                 Layer *layer = getInteractionLayer();
01560                 if (layer && layer->isLayerEditable()) {
01561                     layer->editEnd(this, e);
01562                     update();
01563                 }
01564             }
01565         } 
01566 
01567     } else if (mode == ViewManager::MeasureMode) {
01568 
01569         Layer *layer = getTopLayer();
01570         if (layer) layer->measureEnd(this, e);
01571         if (m_measureCursor1) setCursor(*m_measureCursor1);
01572         update();
01573     }
01574 
01575     m_clickedInRange = false;
01576     m_releasing = false;
01577 
01578     emit paneInteractedWith();
01579 }
01580 
01581 void
01582 Pane::mouseMoveEvent(QMouseEvent *e)
01583 {
01584     if (!e || (e->buttons() & Qt::RightButton)) {
01585         return;
01586     }
01587 
01588 //    cerr << "mouseMoveEvent" << endl;
01589 
01590     QPoint pos = e->pos();
01591     updateContextHelp(&pos);
01592 
01593     if (m_navigating && m_clickedInRange && !m_releasing) {
01594 
01595         // if no buttons pressed, and not called from
01596         // mouseReleaseEvent, we want to reset clicked-ness (to avoid
01597         // annoying continual drags when we moved the mouse outside
01598         // the window after pressing button first time).
01599 
01600         if (!(e->buttons() & Qt::LeftButton) &&
01601             !(e->buttons() & Qt::MidButton)) {
01602             m_clickedInRange = false;
01603             return;
01604         }
01605     }
01606 
01607     ViewManager::ToolMode mode = ViewManager::NavigateMode;
01608     if (m_manager) mode = m_manager->getToolModeFor(this);
01609 
01610     QPoint prevPoint = m_identifyPoint;
01611     m_identifyPoint = e->pos();
01612 
01613     if (!m_clickedInRange) {
01614     
01615         // GF: handle mouse move for context sensitive cursor switching in NoteEditMode.
01616         // 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.
01617         if (mode == ViewManager::NoteEditMode) {
01618             FlexiNoteLayer *layer = qobject_cast<FlexiNoteLayer *>(getTopFlexiNoteLayer());
01619             if (layer) {
01620                 layer->mouseMoveEvent(this, e); 
01621                 update();
01622                 // return;
01623             }
01624         }   
01625     
01626         if (mode == ViewManager::SelectMode && hasTopLayerTimeXAxis()) {
01627             bool closeToLeft = false, closeToRight = false;
01628             getSelectionAt(e->x(), closeToLeft, closeToRight);
01629             if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
01630                 setCursor(Qt::SizeHorCursor);
01631             } else {
01632                 setCursor(Qt::ArrowCursor);
01633             }
01634         }
01635 
01636         if (m_manager && !m_manager->isPlaying()) {
01637 
01638             bool updating = false;
01639 
01640             if (getInteractionLayer() &&
01641                 m_manager->shouldIlluminateLocalFeatures()) {
01642 
01643                 bool previouslyIdentifying = m_identifyFeatures;
01644                 m_identifyFeatures = true;
01645                 
01646                 if (m_identifyFeatures != previouslyIdentifying ||
01647                     m_identifyPoint != prevPoint) {
01648                     update();
01649                     updating = true;
01650                 }
01651             }
01652 
01653             if (!updating && mode == ViewManager::MeasureMode) {
01654 
01655                 Layer *layer = getTopLayer();
01656                 if (layer && layer->nearestMeasurementRectChanged
01657                     (this, prevPoint, m_identifyPoint)) {
01658                     update();
01659                 }
01660             }
01661         }
01662 
01663         return;
01664     }
01665 
01666     if (m_navigating || mode == ViewManager::NavigateMode) {
01667 
01668         if (m_shiftPressed) {
01669 
01670             m_mousePos = e->pos();
01671             update();
01672 
01673         } else {
01674 
01675             dragTopLayer(e);
01676         }
01677 
01678     } else if (mode == ViewManager::SelectMode) {
01679 
01680         if (!hasTopLayerTimeXAxis()) return;
01681 
01682         dragExtendSelection(e);
01683 
01684     } else if (mode == ViewManager::DrawMode) {
01685 
01686         Layer *layer = getInteractionLayer();
01687         if (layer && layer->isLayerEditable()) {
01688             layer->drawDrag(this, e);
01689         }
01690 
01691     } else if (mode == ViewManager::EraseMode) {
01692 
01693         Layer *layer = getInteractionLayer();
01694         if (layer && layer->isLayerEditable()) {
01695             layer->eraseDrag(this, e);
01696         }
01697 
01698         // GF: handling NoteEditMode dragging and boundary actions for mouseMoveEvent
01699     } else if (mode == ViewManager::NoteEditMode) {
01700 
01701         bool resist = true;
01702 
01703         if ((e->modifiers() & Qt::ShiftModifier)) {
01704             m_shiftPressed = true;
01705         }
01706 
01707         if (m_shiftPressed) resist = false;
01708 
01709         m_dragMode = updateDragMode
01710             (m_dragMode,
01711              m_clickPos,
01712              e->pos(),
01713              true,    // can move horiz
01714              true,    // can move vert
01715              resist,  // resist horiz
01716              resist); // resist vert
01717 
01718         if (!m_editing) {
01719 
01720             if (m_dragMode != UnresolvedDrag) {
01721 
01722                 m_editing = true;
01723 
01724                 QMouseEvent clickEvent(QEvent::MouseButtonPress,
01725                                        m_clickPos,
01726                                        Qt::NoButton,
01727                                        e->buttons(),
01728                                        e->modifiers());
01729 
01730                 if (!editSelectionStart(&clickEvent)) {
01731                     Layer *layer = getTopFlexiNoteLayer();
01732                     if (layer) {
01733                         std::cerr << "calling edit start" << std::endl;
01734                         layer->editStart(this, &clickEvent);
01735                     }
01736                 }
01737             }
01738 
01739         } else {
01740 
01741             if (!editSelectionDrag(e)) {
01742 
01743                 Layer *layer = getInteractionLayer();
01744 
01745                 if (layer && layer->isLayerEditable()) {
01746 
01747                     int x = e->x();
01748                     int y = e->y();
01749                     if (m_dragMode == VerticalDrag) x = m_clickPos.x();
01750                     else if (m_dragMode == HorizontalDrag) y = m_clickPos.y();
01751 
01752                     QMouseEvent moveEvent(QEvent::MouseMove,
01753                                           QPoint(x, y),
01754                                           Qt::NoButton,
01755                                           e->buttons(),
01756                                           e->modifiers());
01757                     std::cerr << "calling editDrag" << std::endl;
01758                     layer->editDrag(this, &moveEvent);
01759                 }
01760             }
01761         }
01762 
01763     } else if (mode == ViewManager::EditMode) {
01764 
01765         bool resist = true;
01766 
01767         if ((e->modifiers() & Qt::ShiftModifier)) {
01768             m_shiftPressed = true;
01769             // ... but don't set it false if shift has been
01770             // released -- we want the state when we started
01771             // dragging to be used most of the time
01772         }
01773 
01774         if (m_shiftPressed) resist = false;
01775 
01776         m_dragMode = updateDragMode
01777             (m_dragMode,
01778              m_clickPos,
01779              e->pos(),
01780              true,    // can move horiz
01781              true,    // can move vert
01782              resist,  // resist horiz
01783              resist); // resist vert
01784 
01785         if (!m_editing) {
01786 
01787             if (m_dragMode != UnresolvedDrag) {
01788 
01789                 m_editing = true;
01790 
01791                 QMouseEvent clickEvent(QEvent::MouseButtonPress,
01792                                        m_clickPos,
01793                                        Qt::NoButton,
01794                                        e->buttons(),
01795                                        e->modifiers());
01796 
01797                 if (!editSelectionStart(&clickEvent)) {
01798                     Layer *layer = getInteractionLayer();
01799                     if (layer && layer->isLayerEditable()) {
01800                         layer->editStart(this, &clickEvent);
01801                     }
01802                 }
01803             }
01804 
01805         } else {
01806 
01807             if (!editSelectionDrag(e)) {
01808 
01809                 Layer *layer = getInteractionLayer();
01810 
01811                 if (layer && layer->isLayerEditable()) {
01812 
01813                     int x = e->x();
01814                     int y = e->y();
01815                     if (m_dragMode == VerticalDrag) x = m_clickPos.x();
01816                     else if (m_dragMode == HorizontalDrag) y = m_clickPos.y();
01817 
01818                     QMouseEvent moveEvent(QEvent::MouseMove,
01819                                           QPoint(x, y),
01820                                           Qt::NoButton,
01821                                           e->buttons(),
01822                                           e->modifiers());
01823                                               
01824                     layer->editDrag(this, &moveEvent);
01825                 }
01826             }
01827         }
01828 
01829     } else if (mode == ViewManager::MeasureMode) {
01830 
01831         if (m_measureCursor2) setCursor(*m_measureCursor2);
01832 
01833         Layer *layer = getTopLayer();
01834         if (layer) {
01835             layer->measureDrag(this, e);
01836             if (layer->hasTimeXAxis()) edgeScrollMaybe(e->x());
01837         }
01838 
01839         update();
01840     }
01841     
01842     if (m_dragMode != UnresolvedDrag) {
01843         m_playbackFrameMoveScheduled = false;
01844     }
01845 }
01846 
01847 void
01848 Pane::zoomToRegion(QRect r)
01849 {
01850     int x0 = r.x();
01851     int y0 = r.y();
01852     int x1 = r.x() + r.width();
01853     int y1 = r.y() + r.height();
01854 
01855     int w = x1 - x0;
01856         
01857     int newStartFrame = getFrameForX(x0);
01858         
01859     int visibleFrames = getEndFrame() - getStartFrame();
01860     if (newStartFrame <= -visibleFrames) {
01861         newStartFrame  = -visibleFrames + 1;
01862     }
01863         
01864     if (newStartFrame >= long(getModelsEndFrame())) {
01865         newStartFrame  = getModelsEndFrame() - 1;
01866     }
01867         
01868     float ratio = float(w) / float(width());
01869 //      cerr << "ratio: " << ratio << endl;
01870     int newZoomLevel = (int)nearbyint(m_zoomLevel * ratio);
01871     if (newZoomLevel < 1) newZoomLevel = 1;
01872 
01873 //      cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << endl;
01874     setZoomLevel(getZoomConstraintBlockSize(newZoomLevel));
01875     setStartFrame(newStartFrame);
01876 
01877     QString unit;
01878     float min, max;
01879     bool log;
01880     Layer *layer = 0;
01881     for (LayerList::const_iterator i = m_layerStack.begin();
01882          i != m_layerStack.end(); ++i) { 
01883         if ((*i)->getValueExtents(min, max, log, unit) &&
01884             (*i)->getDisplayExtents(min, max)) {
01885             layer = *i;
01886             break;
01887         }
01888     }
01889             
01890     if (layer) {
01891         if (log) {
01892             min = (min < 0.0) ? -log10f(-min) : (min == 0.0) ? 0.0 : log10f(min);
01893             max = (max < 0.0) ? -log10f(-max) : (max == 0.0) ? 0.0 : log10f(max);
01894         }
01895         float rmin = min + ((max - min) * (height() - y1)) / height();
01896         float rmax = min + ((max - min) * (height() - y0)) / height();
01897         cerr << "min: " << min << ", max: " << max << ", y0: " << y0 << ", y1: " << y1 << ", h: " << height() << ", rmin: " << rmin << ", rmax: " << rmax << endl;
01898         if (log) {
01899             rmin = powf(10, rmin);
01900             rmax = powf(10, rmax);
01901         }
01902         cerr << "finally: rmin: " << rmin << ", rmax: " << rmax << " " << unit << endl;
01903 
01904         layer->setDisplayExtents(rmin, rmax);
01905         updateVerticalPanner();
01906     }
01907 }
01908 
01909 void
01910 Pane::dragTopLayer(QMouseEvent *e)
01911 {
01912     // We need to avoid making it too easy to drag both
01913     // horizontally and vertically, in the case where the
01914     // mouse is moved "mostly" in horizontal or vertical axis
01915     // with only a small variation in the other axis.  This is
01916     // particularly important during playback (when we want to
01917     // avoid small horizontal motions) or in slow refresh
01918     // layers like spectrogram (when we want to avoid small
01919     // vertical motions).
01920     // 
01921     // To this end we have horizontal and vertical thresholds
01922     // and a series of states: unresolved, horizontally or
01923     // vertically constrained, free.
01924     //
01925     // When the mouse first moves, we're unresolved: we
01926     // restrict ourselves to whichever direction seems safest,
01927     // until the mouse has passed a small threshold distance
01928     // from the click point.  Then we lock in to one of the
01929     // constrained modes, based on which axis that distance
01930     // was measured in first.  Finally, if it turns out we've
01931     // also moved more than a certain larger distance in the
01932     // other direction as well, we may switch into free mode.
01933     // 
01934     // If the top layer is incapable of being dragged
01935     // vertically, the logic is short circuited.
01936 
01937     m_dragMode = updateDragMode
01938         (m_dragMode,
01939          m_clickPos,
01940          e->pos(),
01941          true, // can move horiz
01942          canTopLayerMoveVertical(), // can move vert
01943          canTopLayerMoveVertical() || (m_manager && m_manager->isPlaying()), // resist horiz
01944          !(m_manager && m_manager->isPlaying())); // resist vert
01945 
01946     if (m_dragMode == HorizontalDrag ||
01947         m_dragMode == FreeDrag) {
01948 
01949         int frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x());
01950         int newCentreFrame = m_dragCentreFrame;
01951         
01952         if (frameOff < 0) {
01953             newCentreFrame -= frameOff;
01954         } else if (newCentreFrame >= frameOff) {
01955             newCentreFrame -= frameOff;
01956         } else {
01957             newCentreFrame = 0;
01958         }
01959 
01960 #ifdef DEBUG_PANE       
01961         SVDEBUG << "Pane::dragTopLayer: newCentreFrame = " << newCentreFrame <<
01962             ", models end frame = " << getModelsEndFrame() << endl;
01963 #endif
01964 
01965         if (newCentreFrame >= getModelsEndFrame()) {
01966             newCentreFrame = getModelsEndFrame();
01967             if (newCentreFrame > 0) --newCentreFrame;
01968         }
01969                 
01970         if (getXForFrame(m_centreFrame) != getXForFrame(newCentreFrame)) {
01971             setCentreFrame(newCentreFrame, !m_altPressed);
01972         }
01973     }
01974 
01975     if (m_dragMode == VerticalDrag ||
01976         m_dragMode == FreeDrag) {
01977 
01978         float vmin = 0.f, vmax = 0.f;
01979         float dmin = 0.f, dmax = 0.f;
01980 
01981         if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
01982 
01983 //            cerr << "ydiff = " << ydiff << endl;
01984 
01985             int ydiff = e->y() - m_clickPos.y();
01986             float perpix = (dmax - dmin) / height();
01987             float valdiff = ydiff * perpix;
01988 //            cerr << "valdiff = " << valdiff << endl;
01989 
01990             if (m_dragMode == UnresolvedDrag && ydiff != 0) {
01991                 m_dragMode = VerticalDrag;
01992             }
01993 
01994             float newmin = m_dragStartMinValue + valdiff;
01995             float newmax = m_dragStartMinValue + (dmax - dmin) + valdiff;
01996             if (newmin < vmin) {
01997                 newmax += vmin - newmin;
01998                 newmin += vmin - newmin;
01999             }
02000             if (newmax > vmax) {
02001                 newmin -= newmax - vmax;
02002                 newmax -= newmax - vmax;
02003             }
02004 //            cerr << "(" << dmin << ", " << dmax << ") -> ("
02005 //                      << newmin << ", " << newmax << ") (drag start " << m_dragStartMinValue << ")" << endl;
02006 
02007             setTopLayerDisplayExtents(newmin, newmax);
02008             updateVerticalPanner();
02009         }
02010     }
02011 }
02012 
02013 Pane::DragMode
02014 Pane::updateDragMode(DragMode dragMode,
02015                      QPoint origin,
02016                      QPoint point,
02017                      bool canMoveHorizontal,
02018                      bool canMoveVertical,
02019                      bool resistHorizontal,
02020                      bool resistVertical)
02021 {
02022     int xdiff = point.x() - origin.x();
02023     int ydiff = point.y() - origin.y();
02024 
02025     int smallThreshold = 10, bigThreshold = 80;
02026 
02027 //    SVDEBUG << "Pane::updateDragMode: xdiff = " << xdiff << ", ydiff = "
02028 //              << ydiff << ", canMoveVertical = " << canMoveVertical << ", drag mode = " << m_dragMode << endl;
02029 
02030     if (dragMode == UnresolvedDrag) {
02031 
02032         if (abs(ydiff) > smallThreshold &&
02033             abs(ydiff) > abs(xdiff) * 2 &&
02034             canMoveVertical) {
02035 //            SVDEBUG << "Pane::updateDragMode: passed vertical threshold" << endl;
02036             dragMode = VerticalDrag;
02037         } else if (abs(xdiff) > smallThreshold &&
02038                    abs(xdiff) > abs(ydiff) * 2 &&
02039                    canMoveHorizontal) {
02040 //            SVDEBUG << "Pane::updateDragMode: passed horizontal threshold" << endl;
02041             dragMode = HorizontalDrag;
02042         } else if (abs(xdiff) > smallThreshold &&
02043                    abs(ydiff) > smallThreshold &&
02044                    canMoveVertical &&
02045                    canMoveHorizontal) {
02046 //            SVDEBUG << "Pane::updateDragMode: passed both thresholds" << endl;
02047             dragMode = FreeDrag;
02048         }
02049     }
02050 
02051     if (dragMode == VerticalDrag && canMoveHorizontal) {
02052         if (abs(xdiff) > bigThreshold) dragMode = FreeDrag;
02053     }
02054 
02055     if (dragMode == HorizontalDrag && canMoveVertical) {
02056         if (abs(ydiff) > bigThreshold) dragMode = FreeDrag;
02057     }
02058 
02059     if (dragMode == UnresolvedDrag) {
02060         if (!resistHorizontal && xdiff != 0) {
02061             dragMode = HorizontalDrag;
02062         }
02063         if (!resistVertical && ydiff != 0) {
02064             if (dragMode == HorizontalDrag) dragMode = FreeDrag;
02065             else dragMode = VerticalDrag;
02066         }
02067     }
02068     
02069     return dragMode;
02070 }
02071 
02072 void
02073 Pane::dragExtendSelection(QMouseEvent *e)
02074 {
02075     int mouseFrame = getFrameForX(e->x());
02076     int resolution = 1;
02077     int snapFrameLeft = mouseFrame;
02078     int snapFrameRight = mouseFrame;
02079     
02080     Layer *layer = getInteractionLayer();
02081     if (layer && !m_shiftPressed) {
02082         layer->snapToFeatureFrame(this, snapFrameLeft,
02083                                   resolution, Layer::SnapLeft);
02084         layer->snapToFeatureFrame(this, snapFrameRight,
02085                                   resolution, Layer::SnapRight);
02086     }
02087         
02088 //      cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << endl;
02089 
02090     if (snapFrameLeft < 0) snapFrameLeft = 0;
02091     if (snapFrameRight < 0) snapFrameRight = 0;
02092     
02093     int min, max;
02094     
02095     if (m_selectionStartFrame > snapFrameLeft) {
02096         min = snapFrameLeft;
02097         max = m_selectionStartFrame;
02098     } else if (snapFrameRight > m_selectionStartFrame) {
02099         min = m_selectionStartFrame;
02100         max = snapFrameRight;
02101     } else {
02102         min = snapFrameLeft;
02103         max = snapFrameRight;
02104     }
02105 
02106     if (m_manager) {
02107         m_manager->setInProgressSelection(Selection(alignToReference(min),
02108                                                     alignToReference(max)),
02109                                           !m_resizing && !m_ctrlPressed);
02110     }
02111 
02112     edgeScrollMaybe(e->x());
02113 
02114     update();
02115 
02116     if (min != max) {
02117         m_playbackFrameMoveScheduled = false;
02118     }
02119 }
02120 
02121 void
02122 Pane::edgeScrollMaybe(int x)
02123 {
02124     int mouseFrame = getFrameForX(x);
02125 
02126     bool doScroll = false;
02127     if (!m_manager) doScroll = true;
02128     else if (!m_manager->isPlaying()) doScroll = true;
02129 
02130     if (m_followPlay != PlaybackScrollContinuous) doScroll = true;
02131 
02132     if (doScroll) {
02133         int offset = mouseFrame - getStartFrame();
02134         int available = getEndFrame() - getStartFrame();
02135         int move = 0;
02136         if (offset >= available * 0.95) {
02137             move = int(offset - available * 0.95) + 1;
02138         } else if (offset <= available * 0.10) {
02139              move = int(available * 0.10 - offset) + 1;
02140              move = -move;
02141         }
02142         if (move != 0) {
02143             setCentreFrame(m_centreFrame + move);
02144             update();
02145         }
02146     }
02147 }
02148 
02149 void
02150 Pane::mouseDoubleClickEvent(QMouseEvent *e)
02151 {
02152     if (e->buttons() & Qt::RightButton) {
02153         return;
02154     }
02155 
02156     cerr << "mouseDoubleClickEvent" << endl;
02157 
02158     m_clickPos = e->pos();
02159     m_clickedInRange = true;
02160     m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
02161     m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
02162     m_altPressed = (e->modifiers() & Qt::AltModifier);
02163 
02164     // cancel any pending move that came from a single click
02165     m_playbackFrameMoveScheduled = false;
02166 
02167     ViewManager::ToolMode mode = ViewManager::NavigateMode;
02168     if (m_manager) mode = m_manager->getToolModeFor(this);
02169 
02170     bool relocate = (mode == ViewManager::NavigateMode ||
02171                      (e->buttons() & Qt::MidButton));
02172 
02173     if (mode == ViewManager::SelectMode) {
02174         m_clickedInRange = false;
02175         if (m_manager) m_manager->clearInProgressSelection();
02176         emit doubleClickSelectInvoked(getFrameForX(e->x()));
02177         return;
02178     }
02179 
02180     if (mode == ViewManager::NavigateMode ||
02181         mode == ViewManager::EditMode) {
02182 
02183         Layer *layer = getInteractionLayer();
02184         if (layer && layer->isLayerEditable()) {
02185             if (layer->editOpen(this, e)) relocate = false;
02186         }
02187 
02188     } else if (mode == ViewManager::MeasureMode) {
02189 
02190         Layer *layer = getTopLayer();
02191         if (layer) layer->measureDoubleClick(this, e);
02192         update();
02193     }
02194 
02195     if (relocate) {
02196 
02197         int f = getFrameForX(e->x());
02198 
02199         setCentreFrame(f);
02200 
02201         m_dragCentreFrame = f;
02202         m_dragStartMinValue = 0;
02203         m_dragMode = UnresolvedDrag;
02204 
02205         float vmin, vmax, dmin, dmax;
02206         if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) {
02207             m_dragStartMinValue = dmin;
02208         }
02209     }
02210     
02211     if (mode == ViewManager::NoteEditMode) {
02212         std::cerr << "double click in note edit mode" << std::endl;
02213         Layer *layer = getInteractionLayer();
02214         if (layer && layer->isLayerEditable()) {
02215             layer->addNote(this, e); 
02216         }
02217     }
02218 
02219     m_clickedInRange = false; // in case mouseReleaseEvent is not properly called
02220 }
02221 
02222 void
02223 Pane::enterEvent(QEvent *)
02224 {
02225     m_mouseInWidget = true;
02226 }
02227 
02228 void
02229 Pane::leaveEvent(QEvent *)
02230 {
02231     m_mouseInWidget = false;
02232     bool previouslyIdentifying = m_identifyFeatures;
02233     m_identifyFeatures = false;
02234     if (previouslyIdentifying) update();
02235     emit contextHelpChanged("");
02236 }
02237 
02238 void
02239 Pane::resizeEvent(QResizeEvent *)
02240 {
02241     updateHeadsUpDisplay();
02242 }
02243 
02244 void
02245 Pane::wheelEvent(QWheelEvent *e)
02246 {
02247     cerr << "wheelEvent, delta " << e->delta() << ", angleDelta " << e->angleDelta().x() << "," << e->angleDelta().y() << ", pixelDelta " << e->pixelDelta().x() << "," << e->pixelDelta().y() << ", modifiers " << e->modifiers() << endl;
02248 
02249     int dx = e->angleDelta().x();
02250     int dy = e->angleDelta().y();
02251 
02252     if (dx == 0 && dy == 0) {
02253         return;
02254     }
02255 
02256     int d = dy;
02257     bool horizontal = false;
02258 
02259     if (abs(dx) > abs(dy)) {
02260         d = dx;
02261         horizontal = true;
02262     } else if (e->modifiers() & Qt::ControlModifier) {
02263         // treat a vertical wheel as horizontal
02264         horizontal = true;
02265     }
02266 
02267     if (e->phase() == Qt::ScrollBegin ||
02268         fabs(d) >= 120 ||
02269         (d > 0 && m_pendingWheelAngle < 0) ||
02270         (d < 0 && m_pendingWheelAngle > 0)) {
02271         m_pendingWheelAngle = d;
02272     } else {
02273         m_pendingWheelAngle += d;
02274     }
02275 
02276     if (horizontal && e->pixelDelta().x() != 0) {
02277 
02278         // Have fine pixel information: use it
02279 
02280         wheelHorizontalFine(e->pixelDelta().x(), e->modifiers());
02281     
02282         m_pendingWheelAngle = 0;
02283 
02284     } else {
02285 
02286         // Coarse wheel information (or vertical zoom, which is
02287         // necessarily coarse itself)
02288 
02289         while (abs(m_pendingWheelAngle) >= 120) {
02290 
02291             int sign = (m_pendingWheelAngle < 0 ? -1 : 1);
02292 
02293             if (horizontal) {
02294                 wheelHorizontal(sign, e->modifiers());
02295             } else {
02296                 wheelVertical(sign, e->modifiers());
02297             }
02298 
02299             m_pendingWheelAngle -= sign * 120;
02300         }
02301     }
02302 }
02303 
02304 void
02305 Pane::wheelVertical(int sign, Qt::KeyboardModifiers mods)
02306 {
02307     cerr << "wheelVertical: sign = " << sign << endl;
02308 
02309     if (mods & Qt::ShiftModifier) {
02310 
02311         // Pan vertically
02312 
02313         if (m_vpan) {
02314             m_vpan->scroll(sign > 0);
02315         }
02316 
02317     } else if (mods & Qt::AltModifier) {
02318 
02319         // Zoom vertically
02320 
02321         if (m_vthumb) {
02322             m_vthumb->scroll(sign > 0);
02323         }
02324 
02325     } else {
02326 
02327         // Zoom in or out
02328 
02329         int newZoomLevel = m_zoomLevel;
02330   
02331         if (sign > 0) {
02332             if (newZoomLevel <= 2) {
02333                 newZoomLevel = 1;
02334             } else {
02335                 newZoomLevel = getZoomConstraintBlockSize
02336                     (newZoomLevel - 1, ZoomConstraint::RoundDown);
02337             }
02338         } else { // sign < 0
02339             newZoomLevel = getZoomConstraintBlockSize
02340                 (newZoomLevel + 1, ZoomConstraint::RoundUp);
02341         }
02342     
02343         if (newZoomLevel != m_zoomLevel) {
02344             setZoomLevel(newZoomLevel);
02345         }
02346     }
02347 
02348     emit paneInteractedWith();
02349 }
02350 
02351 void
02352 Pane::wheelHorizontal(int sign, Qt::KeyboardModifiers mods)
02353 {
02354     cerr << "wheelHorizontal: sign = " << sign << endl;
02355 
02356     // Scroll left or right, rapidly
02357 
02358     wheelHorizontalFine((width() / 4) * sign, mods);
02359 }
02360 
02361 void
02362 Pane::wheelHorizontalFine(int pixels, Qt::KeyboardModifiers)
02363 {
02364     cerr << "wheelHorizontalFine: pixels = " << pixels << endl;
02365 
02366     // Scroll left or right by a fixed number of pixels
02367 
02368     if (getStartFrame() < 0 && 
02369         getEndFrame() >= getModelsEndFrame()) return;
02370 
02371     int delta = (pixels * m_zoomLevel);
02372 
02373     if (m_centreFrame < delta) {
02374         setCentreFrame(0);
02375     } else if (m_centreFrame - delta >= getModelsEndFrame()) {
02376         setCentreFrame(getModelsEndFrame());
02377     } else {
02378         setCentreFrame(m_centreFrame - delta);
02379     }
02380 
02381     emit paneInteractedWith();
02382 }
02383 
02384 void
02385 Pane::horizontalThumbwheelMoved(int value)
02386 {
02388 
02389     int count = 0;
02390     int level = 1;
02391 
02392 
02394     bool haveConstraint = false;
02395     for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end();
02396          ++i) {
02397         if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) {
02398             haveConstraint = true;
02399             break;
02400         }
02401     }
02402 
02403     if (haveConstraint) {
02404         while (true) {
02405             if (m_hthumb->getMaximumValue() - value == count) break;
02406             int newLevel = getZoomConstraintBlockSize(level + 1,
02407                                                       ZoomConstraint::RoundUp);
02408             if (newLevel == level) break;
02409             level = newLevel;
02410             if (++count == 50) break;
02411         }
02412     } else {
02413         while (true) {
02414             if (m_hthumb->getMaximumValue() - value == count) break;
02415             int step = level / 10;
02416             int pwr = 0;
02417             while (step > 0) {
02418                 ++pwr;
02419                 step /= 2;
02420             }
02421             step = 1;
02422             while (pwr > 0) {
02423                 step *= 2;
02424                 --pwr;
02425             }
02426 //            cerr << level << endl;
02427             level += step;
02428             if (++count == 100 || level > 262144) break;
02429         }
02430     }
02431         
02432 //    cerr << "new level is " << level << endl;
02433     setZoomLevel(level);
02434 }    
02435 
02436 void
02437 Pane::verticalThumbwheelMoved(int value)
02438 {
02439     Layer *layer = 0;
02440     if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1);
02441     if (layer) {
02442         int defaultStep = 0;
02443         int max = layer->getVerticalZoomSteps(defaultStep);
02444         if (max == 0) {
02445             updateHeadsUpDisplay();
02446             return;
02447         }
02448         if (value > max) {
02449             value = max;
02450         }
02451         layer->setVerticalZoomStep(value);
02452         updateVerticalPanner();
02453     }
02454 }    
02455 
02456 void
02457 Pane::verticalPannerMoved(float , float y0, float , float h)
02458 {
02459     float vmin, vmax, dmin, dmax;
02460     if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return;
02461     float y1 = y0 + h;
02462     float newmax = vmin + ((1.0 - y0) * (vmax - vmin));
02463     float newmin = vmin + ((1.0 - y1) * (vmax - vmin));
02464 //    cerr << "verticalPannerMoved: (" << x0 << "," << y0 << "," << w
02465 //              << "," << h << ") -> (" << newmin << "," << newmax << ")" << endl;
02466     setTopLayerDisplayExtents(newmin, newmax);
02467 }
02468 
02469 void
02470 Pane::editVerticalPannerExtents()
02471 {
02472     if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return;
02473 
02474     float vmin, vmax, dmin, dmax;
02475     QString unit;
02476     if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax, &unit)
02477         || vmax == vmin) {
02478         return;
02479     }
02480 
02481     RangeInputDialog dialog(tr("Enter new range"),
02482                             tr("New vertical display range, from %1 to %2 %4:")
02483                             .arg(vmin).arg(vmax).arg(unit),
02484                             unit, vmin, vmax, this);
02485     dialog.setRange(dmin, dmax);
02486 
02487     if (dialog.exec() == QDialog::Accepted) {
02488         dialog.getRange(dmin, dmax);
02489         setTopLayerDisplayExtents(dmin, dmax);
02490         updateVerticalPanner();
02491     }
02492 }
02493 
02494 void
02495 Pane::layerParametersChanged()
02496 {
02497     View::layerParametersChanged();
02498     updateHeadsUpDisplay();
02499 }
02500 
02501 void
02502 Pane::dragEnterEvent(QDragEnterEvent *e)
02503 {
02504     QStringList formats(e->mimeData()->formats());
02505     cerr << "dragEnterEvent: format: "
02506               << formats.join(",")
02507               << ", possibleActions: " << e->possibleActions()
02508               << ", proposedAction: " << e->proposedAction() << endl;
02509     
02510     if (e->mimeData()->hasFormat("text/uri-list") ||
02511         e->mimeData()->hasFormat("text/plain")) {
02512 
02513         if (e->proposedAction() & Qt::CopyAction) {
02514             e->acceptProposedAction();
02515         } else {
02516             e->setDropAction(Qt::CopyAction);
02517             e->accept();
02518         }
02519     }
02520 }
02521 
02522 void
02523 Pane::dropEvent(QDropEvent *e)
02524 {
02525     cerr << "dropEvent: text: \"" << e->mimeData()->text()
02526               << "\"" << endl;
02527 
02528     if (e->mimeData()->hasFormat("text/uri-list") || 
02529         e->mimeData()->hasFormat("text/plain")) {
02530 
02531         if (e->proposedAction() & Qt::CopyAction) {
02532             e->acceptProposedAction();
02533         } else {
02534             e->setDropAction(Qt::CopyAction);
02535             e->accept();
02536         }
02537 
02538         if (e->mimeData()->hasFormat("text/uri-list")) {
02539 
02540             SVDEBUG << "accepting... data is \"" << e->mimeData()->data("text/uri-list").data() << "\"" << endl;
02541             emit dropAccepted(QString::fromLocal8Bit
02542                               (e->mimeData()->data("text/uri-list").data())
02543                               .split(QRegExp("[\\r\\n]+"), 
02544                                      QString::SkipEmptyParts));
02545         } else {
02546             emit dropAccepted(QString::fromLocal8Bit
02547                               (e->mimeData()->data("text/plain").data()));
02548         }
02549     }
02550 }
02551 
02552 bool
02553 Pane::editSelectionStart(QMouseEvent *e)
02554 {
02555     if (!m_identifyFeatures ||
02556         !m_manager ||
02557         m_manager->getToolModeFor(this) != ViewManager::EditMode) {
02558         return false;
02559     }
02560 
02561     bool closeToLeft, closeToRight;
02562     Selection s(getSelectionAt(e->x(), closeToLeft, closeToRight));
02563     if (s.isEmpty()) return false;
02564     m_editingSelection = s;
02565     m_editingSelectionEdge = (closeToLeft ? -1 : closeToRight ? 1 : 0);
02566     m_mousePos = e->pos();
02567     return true;
02568 }
02569 
02570 bool
02571 Pane::editSelectionDrag(QMouseEvent *e)
02572 {
02573     if (m_editingSelection.isEmpty()) return false;
02574     m_mousePos = e->pos();
02575     update();
02576     return true;
02577 }
02578 
02579 bool
02580 Pane::editSelectionEnd(QMouseEvent *)
02581 {
02582     if (m_editingSelection.isEmpty()) return false;
02583 
02584     int offset = m_mousePos.x() - m_clickPos.x();
02585     Layer *layer = getInteractionLayer();
02586 
02587     if (offset == 0 || !layer) {
02588         m_editingSelection = Selection();
02589         return true;
02590     }
02591 
02592     int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset;
02593     int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset;
02594 
02595     int f0 = getFrameForX(p0);
02596     int f1 = getFrameForX(p1);
02597 
02598     Selection newSelection(f0, f1);
02599     
02600     if (m_editingSelectionEdge == 0) {
02601     
02602         CommandHistory::getInstance()->startCompoundOperation
02603             (tr("Drag Selection"), true);
02604 
02605         layer->moveSelection(m_editingSelection, f0);
02606     
02607     } else {
02608     
02609         CommandHistory::getInstance()->startCompoundOperation
02610             (tr("Resize Selection"), true);
02611 
02612         if (m_editingSelectionEdge < 0) {
02613             f1 = m_editingSelection.getEndFrame();
02614         } else {
02615             f0 = m_editingSelection.getStartFrame();
02616         }
02617 
02618         newSelection = Selection(f0, f1);
02619         layer->resizeSelection(m_editingSelection, newSelection);
02620     }
02621     
02622     m_manager->removeSelection(m_editingSelection);
02623     m_manager->addSelection(newSelection);
02624 
02625     CommandHistory::getInstance()->endCompoundOperation();
02626 
02627     m_editingSelection = Selection();
02628     return true;
02629 }
02630 
02631 void
02632 Pane::toolModeChanged()
02633 {
02634     ViewManager::ToolMode mode = m_manager->getToolModeFor(this);
02635 //    SVDEBUG << "Pane::toolModeChanged(" << mode << ")" << endl;
02636 
02637     if (mode == ViewManager::MeasureMode && !m_measureCursor1) {
02638         m_measureCursor1 = new QCursor(QBitmap(":/icons/measure1cursor.xbm"),
02639                                        QBitmap(":/icons/measure1mask.xbm"),
02640                                        15, 14);
02641         m_measureCursor2 = new QCursor(QBitmap(":/icons/measure2cursor.xbm"),
02642                                        QBitmap(":/icons/measure2mask.xbm"),
02643                                        16, 17);
02644     }
02645 
02646     switch (mode) {
02647 
02648     case ViewManager::NavigateMode:
02649         setCursor(Qt::PointingHandCursor);
02650         break;
02651     
02652     case ViewManager::SelectMode:
02653         setCursor(Qt::ArrowCursor);
02654         break;
02655     
02656     case ViewManager::EditMode:
02657         setCursor(Qt::UpArrowCursor);
02658         break;
02659     
02660     case ViewManager::DrawMode:
02661         setCursor(Qt::CrossCursor);
02662         break;
02663     
02664     case ViewManager::EraseMode:
02665         setCursor(Qt::CrossCursor);
02666         break;
02667 
02668     case ViewManager::MeasureMode:
02669         if (m_measureCursor1) setCursor(*m_measureCursor1);
02670         break;
02671 
02672         // GF: NoteEditMode uses the same default cursor as EditMode, but it will change in a context sensitive manner.
02673     case ViewManager::NoteEditMode:
02674         setCursor(Qt::UpArrowCursor);
02675         break;
02676 
02677 /*  
02678     case ViewManager::TextMode:
02679     setCursor(Qt::IBeamCursor);
02680     break;
02681 */
02682     }
02683 }
02684 
02685 void
02686 Pane::zoomWheelsEnabledChanged()
02687 {
02688     updateHeadsUpDisplay();
02689     update();
02690 }
02691 
02692 void
02693 Pane::viewZoomLevelChanged(View *v, int z, bool locked)
02694 {
02695 //    cerr << "Pane[" << this << "]::zoomLevelChanged (global now "
02696 //              << (m_manager ? m_manager->getGlobalZoom() : 0) << ")" << endl;
02697 
02698     View::viewZoomLevelChanged(v, z, locked);
02699 
02700     if (m_hthumb && !m_hthumb->isVisible()) return;
02701 
02702     if (v != this) {
02703         if (!locked || !m_followZoom) return;
02704     }
02705 
02706     if (m_manager && m_manager->getZoomWheelsEnabled()) {
02707         updateHeadsUpDisplay();
02708     }
02709 }
02710 
02711 void
02712 Pane::propertyContainerSelected(View *v, PropertyContainer *pc)
02713 {
02714     Layer *layer = 0;
02715 
02716     if (getLayerCount() > 0) {
02717         layer = getLayer(getLayerCount() - 1);
02718         disconnect(layer, SIGNAL(verticalZoomChanged()),
02719                    this, SLOT(verticalZoomChanged()));
02720     }
02721 
02722     View::propertyContainerSelected(v, pc);
02723     updateHeadsUpDisplay();
02724 
02725     if (m_vthumb) {
02726         RangeMapper *rm = 0;
02727         if (layer) rm = layer->getNewVerticalZoomRangeMapper();
02728         if (rm) m_vthumb->setRangeMapper(rm);
02729     }
02730 
02731     if (getLayerCount() > 0) {
02732         layer = getLayer(getLayerCount() - 1);
02733         connect(layer, SIGNAL(verticalZoomChanged()),
02734                 this, SLOT(verticalZoomChanged()));
02735     }
02736 }
02737 
02738 void
02739 Pane::verticalZoomChanged()
02740 {
02741     Layer *layer = 0;
02742 
02743     if (getLayerCount() > 0) {
02744 
02745         layer = getLayer(getLayerCount() - 1);
02746 
02747         if (m_vthumb && m_vthumb->isVisible()) {
02748             m_vthumb->setValue(layer->getCurrentVerticalZoomStep());
02749         }
02750     }
02751 }
02752 
02753 void
02754 Pane::updateContextHelp(const QPoint *pos)
02755 {
02756     QString help = "";
02757 
02758     if (m_clickedInRange) {
02759         emit contextHelpChanged("");
02760         return;
02761     }
02762 
02763     ViewManager::ToolMode mode = ViewManager::NavigateMode;
02764     if (m_manager) mode = m_manager->getToolModeFor(this);
02765 
02766     bool editable = false;
02767     Layer *layer = getInteractionLayer();
02768     if (layer && layer->isLayerEditable()) {
02769         editable = true;
02770     }
02771         
02772     if (mode == ViewManager::NavigateMode) {
02773 
02774         help = tr("Click and drag to navigate");
02775         
02776     } else if (mode == ViewManager::SelectMode) {
02777 
02778         if (!hasTopLayerTimeXAxis()) return;
02779 
02780         bool haveSelection = (m_manager && !m_manager->getSelections().empty());
02781 
02782         if (haveSelection) {
02783 #ifdef Q_OS_MAC
02784             if (editable) {
02785                 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");
02786             } else {
02787                 help = tr("Click and drag to select a range; hold Cmd for multi-select; middle-click and drag to navigate");
02788             }
02789 #else
02790             if (editable) {
02791                 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");
02792             } else {
02793                 help = tr("Click and drag to select a range; hold Ctrl for multi-select; middle-click and drag to navigate");
02794             }                
02795 #endif
02796 
02797             if (pos) {
02798                 bool closeToLeft = false, closeToRight = false;
02799                 Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
02800                 if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
02801                     
02802                     help = tr("Click and drag to move the selection boundary");
02803                 }
02804             }
02805         } else {
02806             if (editable) {
02807                 help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; middle-click to navigate");
02808             } else {
02809                 help = tr("Click and drag to select a range; middle-click and drag to navigate");
02810             }
02811         }
02812 
02813     } else if (mode == ViewManager::DrawMode) {
02814         
02816         if (editable) {
02817             help = tr("Click to add a new item in the active layer");
02818         }
02819 
02820     } else if (mode == ViewManager::EraseMode) {
02821         
02823         if (editable) {
02824             help = tr("Click to erase an item from the active layer");
02825         }
02826         
02827     } else if (mode == ViewManager::EditMode) {
02828         
02830         if (editable) {
02831             help = tr("Click and drag an item in the active layer to move it; hold Shift to override initial resistance");
02832             if (pos) {
02833                 bool closeToLeft = false, closeToRight = false;
02834                 Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
02835                 if (!selection.isEmpty()) {
02836                     help = tr("Click and drag to move all items in the selected range");
02837                 }
02838             }
02839         }
02840     }
02841 
02842     emit contextHelpChanged(help);
02843 }
02844 
02845 void
02846 Pane::mouseEnteredWidget()
02847 {
02848     QWidget *w = dynamic_cast<QWidget *>(sender());
02849     if (!w) return;
02850 
02851     if (w == m_vpan) {
02852         emit contextHelpChanged(tr("Click and drag to adjust the visible range of the vertical scale"));
02853     } else if (w == m_vthumb) {
02854         emit contextHelpChanged(tr("Click and drag to adjust the vertical zoom level"));
02855     } else if (w == m_hthumb) {
02856         emit contextHelpChanged(tr("Click and drag to adjust the horizontal zoom level"));
02857     } else if (w == m_reset) {
02858         emit contextHelpChanged(tr("Reset horizontal and vertical zoom levels to their defaults"));
02859     }
02860 }
02861 
02862 void
02863 Pane::mouseLeftWidget()
02864 {
02865     emit contextHelpChanged("");
02866 }
02867 
02868 void
02869 Pane::toXml(QTextStream &stream,
02870             QString indent, QString extraAttributes) const
02871 {
02872     View::toXml
02873         (stream, indent,
02874      QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3")
02875      .arg(m_centreLineVisible).arg(height()).arg(extraAttributes));
02876 }
02877 
02878