Mercurial > hg > svgui
view view/Pane.cpp @ 174:96b8a790730a
* Wire up the Panner widget in the pane, and do some tidying in Pane
author | Chris Cannam |
---|---|
date | Thu, 19 Oct 2006 13:05:39 +0000 |
parents | 9c40dc10c88c |
children | 42118892f428 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2006 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "Pane.h" #include "layer/Layer.h" #include "data/model/Model.h" #include "base/ZoomConstraint.h" #include "base/RealTime.h" #include "base/Profiler.h" #include "ViewManager.h" #include "base/CommandHistory.h" #include "layer/WaveformLayer.h" #include <QPaintEvent> #include <QPainter> #include <iostream> #include <cmath> //!!! for HUD -- pull out into a separate class #include <QFrame> #include <QGridLayout> #include <QPushButton> #include "widgets/Thumbwheel.h" #include "widgets/Panner.h" using std::cerr; using std::endl; Pane::Pane(QWidget *w) : View(w, true), m_identifyFeatures(false), m_clickedInRange(false), m_shiftPressed(false), m_ctrlPressed(false), m_navigating(false), m_resizing(false), m_centreLineVisible(true), m_headsUpDisplay(0) { setObjectName("Pane"); setMouseTracking(true); updateHeadsUpDisplay(); } void Pane::updateHeadsUpDisplay() { /* int count = 0; int currentLevel = 1; int level = 1; while (true) { if (getZoomLevel() == level) currentLevel = count; int newLevel = getZoomConstraintBlockSize(level + 1, ZoomConstraint::RoundUp); if (newLevel == level) break; if (newLevel == 131072) break; //!!! just because level = newLevel; ++count; } std::cerr << "Have " << count+1 << " zoom levels" << std::endl; */ if (!m_headsUpDisplay) { m_headsUpDisplay = new QFrame(this); QGridLayout *layout = new QGridLayout; layout->setMargin(0); layout->setSpacing(0); m_headsUpDisplay->setLayout(layout); m_hthumb = new Thumbwheel(Qt::Horizontal); layout->addWidget(m_hthumb, 1, 0, 1, 2); m_hthumb->setFixedWidth(70); m_hthumb->setFixedHeight(16); m_hthumb->setDefaultValue(0); m_hthumb->setSpeed(0.6); connect(m_hthumb, SIGNAL(valueChanged(int)), this, SLOT(horizontalThumbwheelMoved(int))); m_vpan = new Panner; layout->addWidget(m_vpan, 0, 1); m_vpan->setFixedWidth(12); m_vpan->setFixedHeight(70); m_vpan->setAlpha(80, 130); connect(m_vpan, SIGNAL(rectExtentsChanged(float, float, float, float)), this, SLOT(verticalPannerMoved(float, float, float, float))); m_vthumb = new Thumbwheel(Qt::Vertical); layout->addWidget(m_vthumb, 0, 2); m_vthumb->setFixedWidth(16); m_vthumb->setFixedHeight(70); connect(m_vthumb, SIGNAL(valueChanged(int)), this, SLOT(verticalThumbwheelMoved(int))); QPushButton *reset = new QPushButton; reset->setFixedHeight(16); reset->setFixedWidth(16); layout->addWidget(reset, 1, 2); connect(reset, SIGNAL(clicked()), m_hthumb, SLOT(resetToDefault())); connect(reset, SIGNAL(clicked()), m_vthumb, SLOT(resetToDefault())); } int count = 0; int current = 0; int level = 1; //!!! pull out into function (presumably in View) bool haveConstraint = false; for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) { haveConstraint = true; break; } } if (haveConstraint) { while (true) { if (getZoomLevel() == level) current = count; int newLevel = getZoomConstraintBlockSize(level + 1, ZoomConstraint::RoundUp); if (newLevel == level) break; level = newLevel; if (++count == 50) break; } } else { // if we have no particular constraints, we can really spread out while (true) { if (getZoomLevel() >= level) current = count; int step = level / 10; int pwr = 0; while (step > 0) { ++pwr; step /= 2; } step = 1; while (pwr > 0) { step *= 2; --pwr; } // std::cerr << level << std::endl; level += step; if (++count == 100 || level > 262144) break; } } // std::cerr << "Have " << count << " zoom levels" << std::endl; m_hthumb->setMinimumValue(0); m_hthumb->setMaximumValue(count); m_hthumb->setValue(count - current); // std::cerr << "set value to " << count-current << std::endl; // std::cerr << "default value is " << m_hthumb->getDefaultValue() << std::endl; if (count != 50 && m_hthumb->getDefaultValue() == 0) { m_hthumb->setDefaultValue(count - current); // std::cerr << "set default value to " << m_hthumb->getDefaultValue() << std::endl; } Layer *layer = 0; if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1); if (layer) { int defaultStep = 0; int max = layer->getVerticalZoomSteps(defaultStep); if (max == 0) { m_vthumb->hide(); } else { m_vthumb->show(); m_vthumb->setMinimumValue(0); m_vthumb->setMaximumValue(max); m_vthumb->setDefaultValue(defaultStep); m_vthumb->setValue(layer->getCurrentVerticalZoomStep()); std::cerr << "Vertical thumbwheel: min 0, max " << max << ", default " << defaultStep << ", value " << m_vthumb->getValue() << std::endl; } } updateVerticalPanner(); if (m_manager && m_manager->getZoomWheelsEnabled() && width() > 120 && height() > 100) { if (!m_headsUpDisplay->isVisible()) { m_headsUpDisplay->show(); connect(m_manager, SIGNAL(zoomLevelChanged()), this, SLOT(zoomLevelChanged())); } if (m_vthumb->isVisible()) { m_headsUpDisplay->move(width() - 86, height() - 86); } else { m_headsUpDisplay->move(width() - 86, height() - 51); } } else { m_headsUpDisplay->hide(); if (m_manager) { disconnect(m_manager, SIGNAL(zoomLevelChanged()), this, SLOT(zoomLevelChanged())); } } } void Pane::updateVerticalPanner() { if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return; float vmin, vmax, dmin, dmax; if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax) && vmax != vmin) { float y0 = (dmin - vmin) / (vmax - vmin); float y1 = (dmax - vmin) / (vmax - vmin); m_vpan->blockSignals(true); m_vpan->setRectExtents(0, 1.0 - y1, 1, y1 - y0); m_vpan->blockSignals(false); m_vpan->show(); } else { m_vpan->hide(); } } bool Pane::shouldIlluminateLocalFeatures(const Layer *layer, QPoint &pos) const { QPoint discard; bool b0, b1; if (layer == getSelectedLayer() && !shouldIlluminateLocalSelection(discard, b0, b1)) { pos = m_identifyPoint; return m_identifyFeatures; } return false; } bool Pane::shouldIlluminateLocalSelection(QPoint &pos, bool &closeToLeft, bool &closeToRight) const { if (m_identifyFeatures && m_manager && m_manager->getToolMode() == ViewManager::EditMode && !m_manager->getSelections().empty() && !selectionIsBeingEdited()) { Selection s(getSelectionAt(m_identifyPoint.x(), closeToLeft, closeToRight)); if (!s.isEmpty()) { if (getSelectedLayer() && getSelectedLayer()->isLayerEditable()) { pos = m_identifyPoint; return true; } } } return false; } bool Pane::selectionIsBeingEdited() const { if (!m_editingSelection.isEmpty()) { if (m_mousePos != m_clickPos && getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) { return true; } } return false; } void Pane::setCentreLineVisible(bool visible) { m_centreLineVisible = visible; update(); } void Pane::paintEvent(QPaintEvent *e) { // Profiler profiler("Pane::paintEvent", true); QPainter paint; QRect r(rect()); if (e) { r = e->rect(); } /* paint.begin(this); paint.setClipRect(r); if (hasLightBackground()) { paint.setPen(Qt::white); paint.setBrush(Qt::white); } else { paint.setPen(Qt::black); paint.setBrush(Qt::black); } paint.drawRect(r); paint.end(); */ View::paintEvent(e); paint.begin(this); if (e) { paint.setClipRect(r); } const Model *waveformModel = 0; // just for reporting purposes int verticalScaleWidth = 0; int fontHeight = paint.fontMetrics().height(); int fontAscent = paint.fontMetrics().ascent(); if (m_manager && !m_manager->isPlaying() && m_manager->getToolMode() == ViewManager::SelectMode) { for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { --vi; std::vector<QRect> crosshairExtents; if ((*vi)->getCrosshairExtents(this, paint, m_identifyPoint, crosshairExtents)) { (*vi)->paintCrosshairs(this, paint, m_identifyPoint); break; } else if ((*vi)->isLayerOpaque()) { break; } } } for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { --vi; if (dynamic_cast<WaveformLayer *>(*vi)) { waveformModel = (*vi)->getModel(); } if (!m_manager || m_manager->getOverlayMode() == ViewManager::NoOverlays) { break; } verticalScaleWidth = (*vi)->getVerticalScaleWidth(this, paint); if (verticalScaleWidth > 0 && r.left() < verticalScaleWidth) { // Profiler profiler("Pane::paintEvent - painting vertical scale", true); // std::cerr << "Pane::paintEvent: calling paint.save() in vertical scale block" << std::endl; paint.save(); paint.setPen(Qt::black); paint.setBrush(Qt::white); paint.drawRect(0, -1, verticalScaleWidth, height()+1); paint.setBrush(Qt::NoBrush); (*vi)->paintVerticalScale (this, paint, QRect(0, 0, verticalScaleWidth, height())); paint.restore(); } if (m_identifyFeatures) { QPoint pos = m_identifyPoint; QString desc = (*vi)->getFeatureDescription(this, pos); if (desc != "") { paint.save(); int tabStop = paint.fontMetrics().width(tr("Some lengthy prefix:")); QRect boundingRect = paint.fontMetrics().boundingRect (rect(), Qt::AlignRight | Qt::AlignTop | Qt::TextExpandTabs, desc, tabStop); if (hasLightBackground()) { paint.setPen(Qt::NoPen); paint.setBrush(QColor(250, 250, 250, 200)); } else { paint.setPen(Qt::NoPen); paint.setBrush(QColor(50, 50, 50, 200)); } int extra = paint.fontMetrics().descent(); paint.drawRect(width() - boundingRect.width() - 10 - extra, 10 - extra, boundingRect.width() + 2 * extra, boundingRect.height() + extra); if (hasLightBackground()) { paint.setPen(QColor(150, 20, 0)); } else { paint.setPen(QColor(255, 150, 100)); } QTextOption option; option.setWrapMode(QTextOption::NoWrap); option.setAlignment(Qt::AlignRight | Qt::AlignTop); option.setTabStop(tabStop); paint.drawText(QRectF(width() - boundingRect.width() - 10, 10, boundingRect.width(), boundingRect.height()), desc, option); paint.restore(); } } break; } int sampleRate = getModelsSampleRate(); paint.setBrush(Qt::NoBrush); if (m_centreLineVisible) { if (hasLightBackground()) { paint.setPen(QColor(50, 50, 50)); } else { paint.setPen(QColor(200, 200, 200)); } paint.drawLine(width() / 2, 0, width() / 2, height() - 1); paint.setPen(QColor(50, 50, 50)); int y = height() - fontHeight + fontAscent - 6; LayerList::iterator vi = m_layers.end(); if (vi != m_layers.begin()) { switch ((*--vi)->getPreferredFrameCountPosition()) { case Layer::PositionTop: y = fontAscent + 6; break; case Layer::PositionMiddle: y = (height() - fontHeight) / 2 + fontAscent; break; case Layer::PositionBottom: // y already set correctly break; } } if (m_manager && m_manager->getOverlayMode() != ViewManager::NoOverlays) { if (sampleRate) { QString text(QString::fromStdString (RealTime::frame2RealTime (m_centreFrame, sampleRate).toText(true))); int tw = paint.fontMetrics().width(text); int x = width()/2 - 4 - tw; drawVisibleText(paint, x, y, text, OutlinedText); } QString text = QString("%1").arg(m_centreFrame); int tw = paint.fontMetrics().width(text); int x = width()/2 + 4; drawVisibleText(paint, x, y, text, OutlinedText); } } else { paint.setPen(QColor(50, 50, 50)); } if (waveformModel && m_manager && m_manager->getOverlayMode() != ViewManager::NoOverlays && r.y() + r.height() >= height() - fontHeight - 6) { size_t modelRate = waveformModel->getSampleRate(); size_t mainModelRate = m_manager->getMainModelSampleRate(); size_t playbackRate = m_manager->getPlaybackSampleRate(); QString srNote = ""; // Show (R) for waveform models that will be resampled on // playback, and (X) for waveform models that will be played // at the wrong rate because their rate differs from that of // the main model. if (modelRate == mainModelRate) { if (modelRate != playbackRate) srNote = " " + tr("(R)"); } else { // std::cerr << "Sample rate = " << modelRate << ", main model rate = " << mainModelRate << std::endl; srNote = " " + tr("(X)"); } QString desc = tr("%1 / %2Hz%3") .arg(RealTime::frame2RealTime(waveformModel->getEndFrame(), sampleRate) .toText(false).c_str()) .arg(modelRate) .arg(srNote); if (r.x() < verticalScaleWidth + 5 + paint.fontMetrics().width(desc)) { drawVisibleText(paint, verticalScaleWidth + 5, height() - fontHeight + fontAscent - 6, desc, OutlinedText); } } if (m_manager && m_manager->getOverlayMode() == ViewManager::AllOverlays && r.y() + r.height() >= height() - m_layers.size() * fontHeight - 6) { std::vector<QString> texts; int maxTextWidth = 0; for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { QString text = (*i)->getLayerPresentationName(); int tw = paint.fontMetrics().width(text); bool reduced = false; while (tw > width() / 3 && text.length() > 4) { if (!reduced && text.length() > 8) { text = text.left(text.length() - 4); } else { text = text.left(text.length() - 2); } reduced = true; tw = paint.fontMetrics().width(text + "..."); } if (reduced) { texts.push_back(text + "..."); } else { texts.push_back(text); } if (tw > maxTextWidth) maxTextWidth = tw; } int lly = height() - 6; int llx = width() - maxTextWidth - 5; if (m_manager->getZoomWheelsEnabled()) { lly -= 20; llx -= 36; } if (r.x() + r.width() >= llx) { for (int i = 0; i < texts.size(); ++i) { if (i == texts.size() - 1) { paint.setPen(Qt::black); } drawVisibleText(paint, llx, lly - fontHeight + fontAscent, texts[i], OutlinedText); lly -= fontHeight; } } } if (m_clickedInRange && m_shiftPressed) { if (m_manager && (m_manager->getToolMode() == ViewManager::NavigateMode)) { //!!! be nice if this looked a bit more in keeping with the //selection block paint.setPen(Qt::blue); paint.drawRect(m_clickPos.x(), m_clickPos.y(), m_mousePos.x() - m_clickPos.x(), m_mousePos.y() - m_clickPos.y()); } } if (selectionIsBeingEdited()) { int offset = m_mousePos.x() - m_clickPos.x(); int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset; int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset; if (m_editingSelectionEdge < 0) { p1 = getXForFrame(m_editingSelection.getEndFrame()); } else if (m_editingSelectionEdge > 0) { p0 = getXForFrame(m_editingSelection.getStartFrame()); } paint.save(); if (hasLightBackground()) { paint.setPen(QPen(Qt::black, 2)); } else { paint.setPen(QPen(Qt::white, 2)); } //!!! duplicating display policy with View::drawSelections if (m_editingSelectionEdge < 0) { paint.drawLine(p0, 1, p1, 1); paint.drawLine(p0, 0, p0, height()); paint.drawLine(p0, height() - 1, p1, height() - 1); } else if (m_editingSelectionEdge > 0) { paint.drawLine(p0, 1, p1, 1); paint.drawLine(p1, 0, p1, height()); paint.drawLine(p0, height() - 1, p1, height() - 1); } else { paint.setBrush(Qt::NoBrush); paint.drawRect(p0, 1, p1 - p0, height() - 2); } paint.restore(); } paint.end(); } Selection Pane::getSelectionAt(int x, bool &closeToLeftEdge, bool &closeToRightEdge) const { closeToLeftEdge = closeToRightEdge = false; if (!m_manager) return Selection(); long testFrame = getFrameForX(x - 5); if (testFrame < 0) { testFrame = getFrameForX(x); if (testFrame < 0) return Selection(); } Selection selection = m_manager->getContainingSelection(testFrame, true); if (selection.isEmpty()) return selection; int lx = getXForFrame(selection.getStartFrame()); int rx = getXForFrame(selection.getEndFrame()); int fuzz = 2; if (x < lx - fuzz || x > rx + fuzz) return Selection(); int width = rx - lx; fuzz = 3; if (width < 12) fuzz = width / 4; if (fuzz < 1) fuzz = 1; if (x < lx + fuzz) closeToLeftEdge = true; if (x > rx - fuzz) closeToRightEdge = true; return selection; } bool Pane::canTopLayerMoveVertical() { float vmin, vmax, dmin, dmax; if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return false; if (dmin <= vmin && dmax >= vmax) return false; return true; } bool Pane::getTopLayerDisplayExtents(float &vmin, float &vmax, float &dmin, float &dmax) { Layer *layer = 0; if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1); if (!layer) return false; bool vlog; QString vunit; return (layer->getValueExtents(vmin, vmax, vlog, vunit) && layer->getDisplayExtents(dmin, dmax)); } bool Pane::setTopLayerDisplayExtents(float dmin, float dmax) { Layer *layer = 0; if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1); if (!layer) return false; return layer->setDisplayExtents(dmin, dmax); } void Pane::mousePressEvent(QMouseEvent *e) { if (e->buttons() & Qt::RightButton) { emit rightButtonMenuRequested(mapToGlobal(e->pos())); return; } m_clickPos = e->pos(); m_clickedInRange = true; m_editingSelection = Selection(); m_editingSelectionEdge = 0; m_shiftPressed = (e->modifiers() & Qt::ShiftModifier); m_ctrlPressed = (e->modifiers() & Qt::ControlModifier); m_dragMode = UnresolvedDrag; ViewManager::ToolMode mode = ViewManager::NavigateMode; if (m_manager) mode = m_manager->getToolMode(); m_navigating = false; if (mode == ViewManager::NavigateMode || (e->buttons() & Qt::MidButton)) { if (mode != ViewManager::NavigateMode) { setCursor(Qt::PointingHandCursor); } m_navigating = true; m_dragCentreFrame = m_centreFrame; m_dragStartMinValue = 0; float vmin, vmax, dmin, dmax; if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) { m_dragStartMinValue = dmin; } } else if (mode == ViewManager::SelectMode) { bool closeToLeft = false, closeToRight = false; Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight); if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { m_manager->removeSelection(selection); if (closeToLeft) { m_selectionStartFrame = selection.getEndFrame(); } else { m_selectionStartFrame = selection.getStartFrame(); } m_manager->setInProgressSelection(selection, false); m_resizing = true; } else { int mouseFrame = getFrameForX(e->x()); size_t resolution = 1; int snapFrame = mouseFrame; Layer *layer = getSelectedLayer(); if (layer && !m_shiftPressed) { layer->snapToFeatureFrame(this, snapFrame, resolution, Layer::SnapLeft); } if (snapFrame < 0) snapFrame = 0; m_selectionStartFrame = snapFrame; if (m_manager) { m_manager->setInProgressSelection(Selection(snapFrame, snapFrame + resolution), !m_ctrlPressed); } m_resizing = false; } update(); } else if (mode == ViewManager::DrawMode) { Layer *layer = getSelectedLayer(); if (layer && layer->isLayerEditable()) { layer->drawStart(this, e); } } else if (mode == ViewManager::EditMode) { if (!editSelectionStart(e)) { Layer *layer = getSelectedLayer(); if (layer && layer->isLayerEditable()) { layer->editStart(this, e); } } } emit paneInteractedWith(); } void Pane::mouseReleaseEvent(QMouseEvent *e) { if (e->buttons() & Qt::RightButton) { return; } ViewManager::ToolMode mode = ViewManager::NavigateMode; if (m_manager) mode = m_manager->getToolMode(); if (m_clickedInRange) { mouseMoveEvent(e); } if (m_navigating || mode == ViewManager::NavigateMode) { m_navigating = false; if (mode != ViewManager::NavigateMode) { // restore cursor toolModeChanged(); } if (m_shiftPressed) { int x0 = std::min(m_clickPos.x(), m_mousePos.x()); int x1 = std::max(m_clickPos.x(), m_mousePos.x()); int y0 = std::min(m_clickPos.y(), m_mousePos.y()); int y1 = std::max(m_clickPos.y(), m_mousePos.y()); zoomToRegion(x0, y0, x1, y1); } } else if (mode == ViewManager::SelectMode) { if (m_manager && m_manager->haveInProgressSelection()) { bool exclusive; Selection selection = m_manager->getInProgressSelection(exclusive); if (selection.getEndFrame() < selection.getStartFrame() + 2) { selection = Selection(); } m_manager->clearInProgressSelection(); if (exclusive) { m_manager->setSelection(selection); } else { m_manager->addSelection(selection); } } update(); } else if (mode == ViewManager::DrawMode) { Layer *layer = getSelectedLayer(); if (layer && layer->isLayerEditable()) { layer->drawEnd(this, e); update(); } } else if (mode == ViewManager::EditMode) { if (!editSelectionEnd(e)) { Layer *layer = getSelectedLayer(); if (layer && layer->isLayerEditable()) { layer->editEnd(this, e); update(); } } } m_clickedInRange = false; emit paneInteractedWith(); } void Pane::mouseMoveEvent(QMouseEvent *e) { if (e->buttons() & Qt::RightButton) { return; } ViewManager::ToolMode mode = ViewManager::NavigateMode; if (m_manager) mode = m_manager->getToolMode(); QPoint prevPoint = m_identifyPoint; m_identifyPoint = e->pos(); if (!m_clickedInRange) { if (mode == ViewManager::SelectMode) { bool closeToLeft = false, closeToRight = false; getSelectionAt(e->x(), closeToLeft, closeToRight); if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { setCursor(Qt::SizeHorCursor); } else { setCursor(Qt::ArrowCursor); } } if (!m_manager->isPlaying()) { if (getSelectedLayer()) { bool previouslyIdentifying = m_identifyFeatures; m_identifyFeatures = true; if (m_identifyFeatures != previouslyIdentifying || m_identifyPoint != prevPoint) { update(); } } } return; } if (m_navigating || mode == ViewManager::NavigateMode) { if (m_shiftPressed) { m_mousePos = e->pos(); update(); } else { dragTopLayer(e); } } else if (mode == ViewManager::SelectMode) { dragExtendSelection(e); } else if (mode == ViewManager::DrawMode) { Layer *layer = getSelectedLayer(); if (layer && layer->isLayerEditable()) { layer->drawDrag(this, e); } } else if (mode == ViewManager::EditMode) { if (!editSelectionDrag(e)) { Layer *layer = getSelectedLayer(); if (layer && layer->isLayerEditable()) { layer->editDrag(this, e); } } } } void Pane::zoomToRegion(int x0, int y0, int x1, int y1) { int w = x1 - x0; long newStartFrame = getFrameForX(x0); long visibleFrames = getEndFrame() - getStartFrame(); if (newStartFrame <= -visibleFrames) { newStartFrame = -visibleFrames + 1; } if (newStartFrame >= long(getModelsEndFrame())) { newStartFrame = getModelsEndFrame() - 1; } float ratio = float(w) / float(width()); // std::cerr << "ratio: " << ratio << std::endl; size_t newZoomLevel = (size_t)nearbyint(m_zoomLevel * ratio); if (newZoomLevel < 1) newZoomLevel = 1; // std::cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << std::endl; setZoomLevel(getZoomConstraintBlockSize(newZoomLevel)); setStartFrame(newStartFrame); QString unit; float min, max; bool log; Layer *layer = 0; for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { if ((*i)->getValueExtents(min, max, log, unit) && (*i)->getDisplayExtents(min, max)) { layer = *i; break; } } if (layer) { if (log) { min = (min < 0.0) ? -log10f(-min) : (min == 0.0) ? 0.0 : log10f(min); max = (max < 0.0) ? -log10f(-max) : (max == 0.0) ? 0.0 : log10f(max); } float rmin = min + ((max - min) * (height() - y1)) / height(); float rmax = min + ((max - min) * (height() - y0)) / height(); std::cerr << "min: " << min << ", max: " << max << ", y0: " << y0 << ", y1: " << y1 << ", h: " << height() << ", rmin: " << rmin << ", rmax: " << rmax << std::endl; if (log) { rmin = powf(10, rmin); rmax = powf(10, rmax); } std::cerr << "finally: rmin: " << rmin << ", rmax: " << rmax << " " << unit.toStdString() << std::endl; layer->setDisplayExtents(rmin, rmax); updateVerticalPanner(); } } void Pane::dragTopLayer(QMouseEvent *e) { // We need to avoid making it too easy to drag both // horizontally and vertically, in the case where the // mouse is moved "mostly" in horizontal or vertical axis // with only a small variation in the other axis. This is // particularly important during playback (when we want to // avoid small horizontal motions) or in slow refresh // layers like spectrogram (when we want to avoid small // vertical motions). // // To this end we have horizontal and vertical thresholds // and a series of states: unresolved, horizontally or // vertically constrained, free. // // When the mouse first moves, we're unresolved: we // restrict ourselves to whichever direction seems safest, // until the mouse has passed a small threshold distance // from the click point. Then we lock in to one of the // constrained modes, based on which axis that distance // was measured in first. Finally, if it turns out we've // also moved more than a certain larger distance in the // other direction as well, we may switch into free mode. // // If the top layer is incapable of being dragged // vertically, the logic is short circuited. int xdiff = e->x() - m_clickPos.x(); int ydiff = e->y() - m_clickPos.y(); int smallThreshold = 10, bigThreshold = 50; bool canMoveVertical = canTopLayerMoveVertical(); bool canMoveHorizontal = true; if (!canMoveHorizontal) { m_dragMode = HorizontalDrag; } if (m_dragMode == UnresolvedDrag) { if (abs(ydiff) > smallThreshold && abs(ydiff) > abs(xdiff) * 2) { m_dragMode = VerticalDrag; } else if (abs(xdiff) > smallThreshold && abs(xdiff) > abs(ydiff) * 2) { m_dragMode = HorizontalDrag; } else if (abs(xdiff) > smallThreshold && abs(ydiff) > smallThreshold) { m_dragMode = FreeDrag; } else { // When playing, we don't want to disturb the play // position too easily; when not playing, we don't // want to move up/down too easily if (m_manager && m_manager->isPlaying()) { canMoveHorizontal = false; } else { canMoveVertical = false; } } } if (m_dragMode == VerticalDrag) { if (abs(xdiff) > bigThreshold) m_dragMode = FreeDrag; else canMoveHorizontal = false; } if (m_dragMode == HorizontalDrag && canMoveVertical) { if (abs(ydiff) > bigThreshold) m_dragMode = FreeDrag; else canMoveVertical = false; } if (canMoveHorizontal) { long frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x()); size_t newCentreFrame = m_dragCentreFrame; if (frameOff < 0) { newCentreFrame -= frameOff; } else if (newCentreFrame >= size_t(frameOff)) { newCentreFrame -= frameOff; } else { newCentreFrame = 0; } if (newCentreFrame >= getModelsEndFrame()) { newCentreFrame = getModelsEndFrame(); if (newCentreFrame > 0) --newCentreFrame; } if (getXForFrame(m_centreFrame) != getXForFrame(newCentreFrame)) { setCentreFrame(newCentreFrame); } } if (canMoveVertical) { float vmin = 0.f, vmax = 0.f; float dmin = 0.f, dmax = 0.f; if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) { std::cerr << "ydiff = " << ydiff << std::endl; float perpix = (dmax - dmin) / height(); float valdiff = ydiff * perpix; std::cerr << "valdiff = " << valdiff << std::endl; float newmin = m_dragStartMinValue + valdiff; float newmax = m_dragStartMinValue + (dmax - dmin) + valdiff; if (newmin < vmin) { newmax += vmin - newmin; newmin += vmin - newmin; } if (newmax > vmax) { newmin -= newmax - vmax; newmax -= newmax - vmax; } std::cerr << "(" << dmin << ", " << dmax << ") -> (" << newmin << ", " << newmax << ") (drag start " << m_dragStartMinValue << ")" << std::endl; setTopLayerDisplayExtents(newmin, newmax); updateVerticalPanner(); } } } void Pane::dragExtendSelection(QMouseEvent *e) { int mouseFrame = getFrameForX(e->x()); size_t resolution = 1; int snapFrameLeft = mouseFrame; int snapFrameRight = mouseFrame; Layer *layer = getSelectedLayer(); if (layer && !m_shiftPressed) { layer->snapToFeatureFrame(this, snapFrameLeft, resolution, Layer::SnapLeft); layer->snapToFeatureFrame(this, snapFrameRight, resolution, Layer::SnapRight); } // std::cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << std::endl; if (snapFrameLeft < 0) snapFrameLeft = 0; if (snapFrameRight < 0) snapFrameRight = 0; size_t min, max; if (m_selectionStartFrame > snapFrameLeft) { min = snapFrameLeft; max = m_selectionStartFrame; } else if (snapFrameRight > m_selectionStartFrame) { min = m_selectionStartFrame; max = snapFrameRight; } else { min = snapFrameLeft; max = snapFrameRight; } if (m_manager) { m_manager->setInProgressSelection(Selection(min, max), !m_resizing && !m_ctrlPressed); } bool doScroll = false; if (!m_manager) doScroll = true; if (!m_manager->isPlaying()) doScroll = true; if (m_followPlay != PlaybackScrollContinuous) doScroll = true; if (doScroll) { int offset = mouseFrame - getStartFrame(); int available = getEndFrame() - getStartFrame(); if (offset >= available * 0.95) { int move = int(offset - available * 0.95) + 1; setCentreFrame(m_centreFrame + move); } else if (offset <= available * 0.10) { int move = int(available * 0.10 - offset) + 1; if (m_centreFrame > move) { setCentreFrame(m_centreFrame - move); } else { setCentreFrame(0); } } } update(); } void Pane::mouseDoubleClickEvent(QMouseEvent *e) { if (e->buttons() & Qt::RightButton) { return; } // std::cerr << "mouseDoubleClickEvent" << std::endl; m_clickPos = e->pos(); m_clickedInRange = true; m_shiftPressed = (e->modifiers() & Qt::ShiftModifier); m_ctrlPressed = (e->modifiers() & Qt::ControlModifier); ViewManager::ToolMode mode = ViewManager::NavigateMode; if (m_manager) mode = m_manager->getToolMode(); if (mode == ViewManager::NavigateMode || mode == ViewManager::EditMode) { Layer *layer = getSelectedLayer(); if (layer && layer->isLayerEditable()) { layer->editOpen(this, e); } } } void Pane::leaveEvent(QEvent *) { bool previouslyIdentifying = m_identifyFeatures; m_identifyFeatures = false; if (previouslyIdentifying) update(); } void Pane::resizeEvent(QResizeEvent *) { updateHeadsUpDisplay(); } void Pane::wheelEvent(QWheelEvent *e) { //std::cerr << "wheelEvent, delta " << e->delta() << std::endl; int count = e->delta(); if (count > 0) { if (count >= 120) count /= 120; else count = 1; } if (count < 0) { if (count <= -120) count /= 120; else count = -1; } if (e->modifiers() & Qt::ControlModifier) { // Scroll left or right, rapidly if (getStartFrame() < 0 && getEndFrame() >= getModelsEndFrame()) return; long delta = ((width() / 2) * count * m_zoomLevel); if (int(m_centreFrame) < delta) { setCentreFrame(0); } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { setCentreFrame(getModelsEndFrame()); } else { setCentreFrame(m_centreFrame - delta); } } else { // Zoom in or out int newZoomLevel = m_zoomLevel; while (count > 0) { if (newZoomLevel <= 2) { newZoomLevel = 1; break; } newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, ZoomConstraint::RoundDown); --count; } while (count < 0) { newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, ZoomConstraint::RoundUp); ++count; } if (newZoomLevel != m_zoomLevel) { setZoomLevel(newZoomLevel); } } emit paneInteractedWith(); } void Pane::horizontalThumbwheelMoved(int value) { //!!! dupe with updateHeadsUpDisplay int count = 0; int level = 1; //!!! pull out into function (presumably in View) bool haveConstraint = false; for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) { haveConstraint = true; break; } } if (haveConstraint) { while (true) { if (m_hthumb->getMaximumValue() - value == count) break; int newLevel = getZoomConstraintBlockSize(level + 1, ZoomConstraint::RoundUp); if (newLevel == level) break; level = newLevel; if (++count == 50) break; } } else { while (true) { if (m_hthumb->getMaximumValue() - value == count) break; int step = level / 10; int pwr = 0; while (step > 0) { ++pwr; step /= 2; } step = 1; while (pwr > 0) { step *= 2; --pwr; } // std::cerr << level << std::endl; level += step; if (++count == 100 || level > 262144) break; } } std::cerr << "new level is " << level << std::endl; setZoomLevel(level); } void Pane::verticalThumbwheelMoved(int value) { Layer *layer = 0; if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1); if (layer) { int defaultStep = 0; int max = layer->getVerticalZoomSteps(defaultStep); if (max == 0) { updateHeadsUpDisplay(); return; } if (value > max) { value = max; } layer->setVerticalZoomStep(value); updateVerticalPanner(); } } void Pane::verticalPannerMoved(float x0, float y0, float w, float h) { float vmin, vmax, dmin, dmax; if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return; float y1 = y0 + h; float newmax = vmin + ((1.0 - y0) * (vmax - vmin)); float newmin = vmin + ((1.0 - y1) * (vmax - vmin)); std::cerr << "verticalPannerMoved: (" << x0 << "," << y0 << "," << w << "," << h << ") -> (" << newmin << "," << newmax << ")" << std::endl; setTopLayerDisplayExtents(newmin, newmax); } bool Pane::editSelectionStart(QMouseEvent *e) { if (!m_identifyFeatures || !m_manager || m_manager->getToolMode() != ViewManager::EditMode) { return false; } bool closeToLeft, closeToRight; Selection s(getSelectionAt(e->x(), closeToLeft, closeToRight)); if (s.isEmpty()) return false; m_editingSelection = s; m_editingSelectionEdge = (closeToLeft ? -1 : closeToRight ? 1 : 0); m_mousePos = e->pos(); return true; } bool Pane::editSelectionDrag(QMouseEvent *e) { if (m_editingSelection.isEmpty()) return false; m_mousePos = e->pos(); update(); return true; } bool Pane::editSelectionEnd(QMouseEvent *e) { if (m_editingSelection.isEmpty()) return false; int offset = m_mousePos.x() - m_clickPos.x(); Layer *layer = getSelectedLayer(); if (offset == 0 || !layer) { m_editingSelection = Selection(); return true; } int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset; int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset; long f0 = getFrameForX(p0); long f1 = getFrameForX(p1); Selection newSelection(f0, f1); if (m_editingSelectionEdge == 0) { CommandHistory::getInstance()->startCompoundOperation (tr("Drag Selection"), true); layer->moveSelection(m_editingSelection, f0); } else { CommandHistory::getInstance()->startCompoundOperation (tr("Resize Selection"), true); if (m_editingSelectionEdge < 0) { f1 = m_editingSelection.getEndFrame(); } else { f0 = m_editingSelection.getStartFrame(); } newSelection = Selection(f0, f1); layer->resizeSelection(m_editingSelection, newSelection); } m_manager->removeSelection(m_editingSelection); m_manager->addSelection(newSelection); CommandHistory::getInstance()->endCompoundOperation(); m_editingSelection = Selection(); return true; } void Pane::toolModeChanged() { ViewManager::ToolMode mode = m_manager->getToolMode(); // std::cerr << "Pane::toolModeChanged(" << mode << ")" << std::endl; switch (mode) { case ViewManager::NavigateMode: setCursor(Qt::PointingHandCursor); break; case ViewManager::SelectMode: setCursor(Qt::ArrowCursor); break; case ViewManager::EditMode: setCursor(Qt::UpArrowCursor); break; case ViewManager::DrawMode: setCursor(Qt::CrossCursor); break; /* case ViewManager::TextMode: setCursor(Qt::IBeamCursor); break; */ } } void Pane::zoomWheelsEnabledChanged() { updateHeadsUpDisplay(); update(); } void Pane::zoomLevelChanged() { if (m_manager && m_manager->getZoomWheelsEnabled()) { updateHeadsUpDisplay(); } } void Pane::propertyContainerSelected(View *v, PropertyContainer *pc) { Layer *layer = 0; if (getLayerCount() > 0) { layer = getLayer(getLayerCount() - 1); disconnect(layer, SIGNAL(verticalZoomChanged()), this, SLOT(verticalZoomChanged())); } View::propertyContainerSelected(v, pc); updateHeadsUpDisplay(); if (getLayerCount() > 0) { layer = getLayer(getLayerCount() - 1); connect(layer, SIGNAL(verticalZoomChanged()), this, SLOT(verticalZoomChanged())); } } void Pane::verticalZoomChanged() { Layer *layer = 0; if (getLayerCount() > 0) { layer = getLayer(getLayerCount() - 1); if (m_vthumb && m_vthumb->isVisible()) { m_vthumb->setValue(layer->getCurrentVerticalZoomStep()); } } } QString Pane::toXmlString(QString indent, QString extraAttributes) const { return View::toXmlString (indent, QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3") .arg(m_centreLineVisible).arg(height()).arg(extraAttributes)); } #ifdef INCLUDE_MOCFILES #include "Pane.moc.cpp" #endif