| Chris@173 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@173 | 2 | 
| Chris@173 | 3 /* | 
| Chris@173 | 4     Sonic Visualiser | 
| Chris@173 | 5     An audio file viewer and annotation editor. | 
| Chris@173 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@182 | 7     This file copyright 2006 Chris Cannam and QMUL. | 
| Chris@173 | 8 | 
| Chris@173 | 9     This program is free software; you can redistribute it and/or | 
| Chris@173 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@173 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@173 | 12     License, or (at your option) any later version.  See the file | 
| Chris@173 | 13     COPYING included with this distribution for more information. | 
| Chris@173 | 14 */ | 
| Chris@173 | 15 | 
| Chris@173 | 16 #include "Overview.h" | 
| Chris@173 | 17 #include "layer/Layer.h" | 
| Chris@173 | 18 #include "data/model/Model.h" | 
| Chris@173 | 19 #include "base/ZoomConstraint.h" | 
| Chris@173 | 20 | 
| Chris@173 | 21 #include <QPaintEvent> | 
| Chris@173 | 22 #include <QPainter> | 
| Chris@173 | 23 #include <iostream> | 
| Chris@173 | 24 | 
| Chris@643 | 25 //#define DEBUG_OVERVIEW 1 | 
| Chris@642 | 26 | 
| Chris@682 | 27 | 
| Chris@173 | 28 Overview::Overview(QWidget *w) : | 
| Chris@173 | 29     View(w, false), | 
| Chris@854 | 30     m_clickedInRange(false), | 
| Chris@854 | 31     m_dragCentreFrame(0) | 
| Chris@173 | 32 { | 
| Chris@173 | 33     setObjectName(tr("Overview")); | 
| Chris@173 | 34     m_followPan = false; | 
| Chris@173 | 35     m_followZoom = false; | 
| Chris@211 | 36     setPlaybackFollow(PlaybackIgnore); | 
| Chris@274 | 37     m_modelTestTime.start(); | 
| Chris@173 | 38 } | 
| Chris@173 | 39 | 
| Chris@173 | 40 void | 
| Chris@908 | 41 Overview::modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame) | 
| Chris@173 | 42 { | 
| Chris@253 | 43     bool zoomChanged = false; | 
| Chris@253 | 44 | 
| Chris@908 | 45     sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame(); | 
| Chris@908 | 46     int zoomLevel = int(frameCount / width()); | 
| Chris@253 | 47     if (zoomLevel < 1) zoomLevel = 1; | 
| Chris@253 | 48     zoomLevel = getZoomConstraintBlockSize(zoomLevel, | 
| Chris@253 | 49 					   ZoomConstraint::RoundUp); | 
| Chris@253 | 50     if (zoomLevel != m_zoomLevel) { | 
| Chris@253 | 51         zoomChanged = true; | 
| Chris@253 | 52     } | 
| Chris@253 | 53 | 
| Chris@253 | 54     if (!zoomChanged) { | 
| Chris@274 | 55         if (m_modelTestTime.elapsed() < 1000) { | 
| Chris@835 | 56             for (LayerList::const_iterator i = m_layerStack.begin(); | 
| Chris@835 | 57                  i != m_layerStack.end(); ++i) { | 
| Chris@274 | 58                 if ((*i)->getModel() && | 
| Chris@389 | 59                     (!(*i)->getModel()->isOK() || | 
| Chris@389 | 60                      !(*i)->getModel()->isReady())) { | 
| Chris@274 | 61                     return; | 
| Chris@274 | 62                 } | 
| Chris@253 | 63             } | 
| Chris@274 | 64         } else { | 
| Chris@274 | 65             m_modelTestTime.restart(); | 
| Chris@253 | 66         } | 
| Chris@253 | 67     } | 
| Chris@253 | 68 | 
| Chris@806 | 69     View::modelChangedWithin(startFrame, endFrame); | 
| Chris@173 | 70 } | 
| Chris@173 | 71 | 
| Chris@173 | 72 void | 
| Chris@173 | 73 Overview::modelReplaced() | 
| Chris@173 | 74 { | 
| Chris@339 | 75     m_playPointerFrame = getAlignedPlaybackFrame(); | 
| Chris@173 | 76     View::modelReplaced(); | 
| Chris@173 | 77 } | 
| Chris@173 | 78 | 
| Chris@173 | 79 void | 
| Chris@211 | 80 Overview::registerView(View *view) | 
| Chris@173 | 81 { | 
| Chris@211 | 82     m_views.insert(view); | 
| Chris@173 | 83     update(); | 
| Chris@173 | 84 } | 
| Chris@173 | 85 | 
| Chris@173 | 86 void | 
| Chris@211 | 87 Overview::unregisterView(View *view) | 
| Chris@173 | 88 { | 
| Chris@211 | 89     m_views.erase(view); | 
| Chris@173 | 90     update(); | 
| Chris@173 | 91 } | 
| Chris@173 | 92 | 
| Chris@173 | 93 void | 
| Chris@908 | 94 Overview::globalCentreFrameChanged(sv_frame_t | 
| Chris@806 | 95 #ifdef DEBUG_OVERVIEW | 
| Chris@806 | 96                                    f | 
| Chris@806 | 97 #endif | 
| Chris@806 | 98     ) | 
| Chris@173 | 99 { | 
| Chris@642 | 100 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 101     cerr << "Overview::globalCentreFrameChanged: " << f << endl; | 
| Chris@642 | 102 #endif | 
| Chris@211 | 103     update(); | 
| Chris@211 | 104 } | 
| Chris@173 | 105 | 
| Chris@211 | 106 void | 
| Chris@908 | 107 Overview::viewCentreFrameChanged(View *v, sv_frame_t | 
| Chris@806 | 108 #ifdef DEBUG_OVERVIEW | 
| Chris@806 | 109                                  f | 
| Chris@806 | 110 #endif | 
| Chris@806 | 111     ) | 
| Chris@211 | 112 { | 
| Chris@642 | 113 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 114     cerr << "Overview[" << this << "]::viewCentreFrameChanged(" << v << "): " << f << endl; | 
| Chris@642 | 115 #endif | 
| Chris@211 | 116     if (m_views.find(v) != m_views.end()) { | 
| Chris@173 | 117 	update(); | 
| Chris@173 | 118     } | 
| Chris@211 | 119 } | 
| Chris@173 | 120 | 
| Chris@173 | 121 void | 
| Chris@806 | 122 Overview::viewZoomLevelChanged(View *v, int, bool) | 
| Chris@173 | 123 { | 
| Chris@222 | 124     if (v == this) return; | 
| Chris@211 | 125     if (m_views.find(v) != m_views.end()) { | 
| Chris@173 | 126 	update(); | 
| Chris@173 | 127     } | 
| Chris@173 | 128 } | 
| Chris@173 | 129 | 
| Chris@173 | 130 void | 
| Chris@908 | 131 Overview::viewManagerPlaybackFrameChanged(sv_frame_t f) | 
| Chris@173 | 132 { | 
| Chris@642 | 133 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 134     cerr << "Overview[" << this << "]::viewManagerPlaybackFrameChanged(" << f << "): " << f << endl; | 
| Chris@642 | 135 #endif | 
| Chris@642 | 136 | 
| Chris@173 | 137     bool changed = false; | 
| Chris@173 | 138 | 
| Chris@339 | 139     f = getAlignedPlaybackFrame(); | 
| Chris@339 | 140 | 
| Chris@173 | 141     if (getXForFrame(m_playPointerFrame) != getXForFrame(f)) changed = true; | 
| Chris@173 | 142     m_playPointerFrame = f; | 
| Chris@173 | 143 | 
| Chris@173 | 144     if (changed) update(); | 
| Chris@173 | 145 } | 
| Chris@173 | 146 | 
| Chris@173 | 147 void | 
| Chris@173 | 148 Overview::paintEvent(QPaintEvent *e) | 
| Chris@173 | 149 { | 
| Chris@173 | 150     // Recalculate zoom in case the size of the widget has changed. | 
| Chris@173 | 151 | 
| Chris@642 | 152 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 153     cerr << "Overview::paintEvent: width is " << width() << ", centre frame " << m_centreFrame << endl; | 
| Chris@642 | 154 #endif | 
| Chris@214 | 155 | 
| Chris@908 | 156     sv_frame_t startFrame = getModelsStartFrame(); | 
| Chris@908 | 157     sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame(); | 
| Chris@908 | 158     int zoomLevel = int(frameCount / width()); | 
| Chris@173 | 159     if (zoomLevel < 1) zoomLevel = 1; | 
| Chris@173 | 160     zoomLevel = getZoomConstraintBlockSize(zoomLevel, | 
| Chris@173 | 161 					   ZoomConstraint::RoundUp); | 
| Chris@173 | 162     if (zoomLevel != m_zoomLevel) { | 
| Chris@173 | 163 	m_zoomLevel = zoomLevel; | 
| Chris@222 | 164 	emit zoomLevelChanged(m_zoomLevel, m_followZoom); | 
| Chris@173 | 165     } | 
| Chris@253 | 166 | 
| Chris@908 | 167     sv_frame_t centreFrame = startFrame + m_zoomLevel * (width() / 2); | 
| Chris@173 | 168     if (centreFrame > (startFrame + getModelsEndFrame())/2) { | 
| Chris@173 | 169 	centreFrame = (startFrame + getModelsEndFrame())/2; | 
| Chris@173 | 170     } | 
| Chris@173 | 171     if (centreFrame != m_centreFrame) { | 
| Chris@642 | 172 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 173         cerr << "Overview::paintEvent: Centre frame changed from " | 
| Chris@642 | 174                   << m_centreFrame << " to " << centreFrame << " and thus start frame from " << getStartFrame(); | 
| Chris@642 | 175 #endif | 
| Chris@173 | 176 	m_centreFrame = centreFrame; | 
| Chris@642 | 177 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 178         cerr << " to " << getStartFrame() << endl; | 
| Chris@642 | 179 #endif | 
| Chris@211 | 180 	emit centreFrameChanged(m_centreFrame, false, PlaybackIgnore); | 
| Chris@173 | 181     } | 
| Chris@173 | 182 | 
| Chris@173 | 183     View::paintEvent(e); | 
| Chris@173 | 184 | 
| Chris@173 | 185     QPainter paint; | 
| Chris@173 | 186     paint.begin(this); | 
| Chris@173 | 187 | 
| Chris@173 | 188     QRect r(rect()); | 
| Chris@173 | 189 | 
| Chris@173 | 190     if (e) { | 
| Chris@173 | 191 	r = e->rect(); | 
| Chris@173 | 192 	paint.setClipRect(r); | 
| Chris@173 | 193     } | 
| Chris@173 | 194 | 
| Chris@287 | 195     paint.setPen(getForeground()); | 
| Chris@173 | 196 | 
| Chris@173 | 197     int y = 0; | 
| Chris@173 | 198 | 
| Chris@908 | 199     sv_frame_t prevx0 = -10; | 
| Chris@908 | 200     sv_frame_t prevx1 = -10; | 
| Chris@173 | 201 | 
| Chris@211 | 202     for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { | 
| Chris@173 | 203 	if (!*i) continue; | 
| Chris@173 | 204 | 
| Chris@173 | 205 	View *w = (View *)*i; | 
| Chris@173 | 206 | 
| Chris@908 | 207 	sv_frame_t f0 = w->getFrameForX(0); | 
| Chris@908 | 208 	sv_frame_t f1 = w->getFrameForX(w->width()); | 
| Chris@173 | 209 | 
| Chris@339 | 210         if (f0 >= 0) { | 
| Chris@908 | 211             sv_frame_t rf0 = w->alignToReference(f0); | 
| Chris@339 | 212             f0 = alignFromReference(rf0); | 
| Chris@339 | 213         } | 
| Chris@339 | 214         if (f1 >= 0) { | 
| Chris@908 | 215             sv_frame_t rf1 = w->alignToReference(f1); | 
| Chris@339 | 216             f1 = alignFromReference(rf1); | 
| Chris@339 | 217         } | 
| Chris@339 | 218 | 
| Chris@173 | 219 	int x0 = getXForFrame(f0); | 
| Chris@173 | 220 	int x1 = getXForFrame(f1); | 
| Chris@173 | 221 | 
| Chris@173 | 222 	if (x0 != prevx0 || x1 != prevx1) { | 
| Chris@173 | 223 	    y += height() / 10 + 1; | 
| Chris@173 | 224 	    prevx0 = x0; | 
| Chris@173 | 225 	    prevx1 = x1; | 
| Chris@173 | 226 	} | 
| Chris@173 | 227 | 
| Chris@173 | 228 	if (x1 <= x0) x1 = x0 + 1; | 
| Chris@173 | 229 | 
| Chris@173 | 230 	paint.drawRect(x0, y, x1 - x0, height() - 2 * y); | 
| Chris@173 | 231     } | 
| Chris@173 | 232 | 
| Chris@173 | 233     paint.end(); | 
| Chris@173 | 234 } | 
| Chris@173 | 235 | 
| Chris@173 | 236 void | 
| Chris@173 | 237 Overview::mousePressEvent(QMouseEvent *e) | 
| Chris@173 | 238 { | 
| Chris@173 | 239     m_clickPos = e->pos(); | 
| Chris@908 | 240     sv_frame_t clickFrame = getFrameForX(m_clickPos.x()); | 
| Chris@339 | 241     if (clickFrame > 0) m_dragCentreFrame = clickFrame; | 
| Chris@339 | 242     else m_dragCentreFrame = 0; | 
| Chris@339 | 243     m_clickedInRange = true; | 
| Chris@339 | 244 | 
| Chris@211 | 245     for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { | 
| Chris@339 | 246 	if (*i && (*i)->getAligningModel() == getAligningModel()) { | 
| Chris@339 | 247             m_dragCentreFrame = (*i)->getCentreFrame(); | 
| Chris@339 | 248             break; | 
| Chris@339 | 249         } | 
| Chris@173 | 250     } | 
| Chris@173 | 251 } | 
| Chris@173 | 252 | 
| Chris@173 | 253 void | 
| Chris@173 | 254 Overview::mouseReleaseEvent(QMouseEvent *e) | 
| Chris@173 | 255 { | 
| Chris@173 | 256     if (m_clickedInRange) { | 
| Chris@173 | 257 	mouseMoveEvent(e); | 
| Chris@173 | 258     } | 
| Chris@173 | 259     m_clickedInRange = false; | 
| Chris@173 | 260 } | 
| Chris@173 | 261 | 
| Chris@173 | 262 void | 
| Chris@173 | 263 Overview::mouseMoveEvent(QMouseEvent *e) | 
| Chris@173 | 264 { | 
| Chris@173 | 265     if (!m_clickedInRange) return; | 
| Chris@173 | 266 | 
| Chris@806 | 267     int xoff = int(e->x()) - int(m_clickPos.x()); | 
| Chris@908 | 268     sv_frame_t frameOff = xoff * m_zoomLevel; | 
| Chris@173 | 269 | 
| Chris@908 | 270     sv_frame_t newCentreFrame = m_dragCentreFrame; | 
| Chris@173 | 271     if (frameOff > 0) { | 
| Chris@173 | 272 	newCentreFrame += frameOff; | 
| Chris@908 | 273     } else if (newCentreFrame >= -frameOff) { | 
| Chris@173 | 274 	newCentreFrame += frameOff; | 
| Chris@173 | 275     } else { | 
| Chris@173 | 276 	newCentreFrame = 0; | 
| Chris@173 | 277     } | 
| Chris@173 | 278 | 
| Chris@173 | 279     if (newCentreFrame >= getModelsEndFrame()) { | 
| Chris@173 | 280 	newCentreFrame = getModelsEndFrame(); | 
| Chris@173 | 281 	if (newCentreFrame > 0) --newCentreFrame; | 
| Chris@173 | 282     } | 
| Chris@173 | 283 | 
| Chris@173 | 284     if (std::max(m_centreFrame, newCentreFrame) - | 
| Chris@908 | 285 	std::min(m_centreFrame, newCentreFrame) > m_zoomLevel) { | 
| Chris@908 | 286         sv_frame_t rf = alignToReference(newCentreFrame); | 
| Chris@643 | 287 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 288         cerr << "Overview::mouseMoveEvent: x " << e->x() << " and click x " << m_clickPos.x() << " -> frame " << newCentreFrame << " -> rf " << rf << endl; | 
| Chris@643 | 289 #endif | 
| Chris@817 | 290         if (m_followPlay == PlaybackScrollContinuous || | 
| Chris@817 | 291             m_followPlay == PlaybackScrollPageWithCentre) { | 
| Chris@817 | 292             emit centreFrameChanged(rf, true, PlaybackScrollContinuous); | 
| Chris@817 | 293         } else { | 
| Chris@817 | 294             emit centreFrameChanged(rf, true, PlaybackIgnore); | 
| Chris@817 | 295         } | 
| Chris@173 | 296     } | 
| Chris@173 | 297 } | 
| Chris@173 | 298 | 
| Chris@189 | 299 void | 
| Chris@189 | 300 Overview::mouseDoubleClickEvent(QMouseEvent *e) | 
| Chris@189 | 301 { | 
| Chris@908 | 302     sv_frame_t frame = getFrameForX(e->x()); | 
| Chris@908 | 303     sv_frame_t rf = 0; | 
| Chris@339 | 304     if (frame > 0) rf = alignToReference(frame); | 
| Chris@643 | 305 #ifdef DEBUG_OVERVIEW | 
| Chris@682 | 306     cerr << "Overview::mouseDoubleClickEvent: frame " << frame << " -> rf " << rf << endl; | 
| Chris@643 | 307 #endif | 
| Chris@643 | 308     m_clickedInRange = false; // we're not starting a drag with the second click | 
| Chris@339 | 309     emit centreFrameChanged(rf, true, PlaybackScrollContinuous); | 
| Chris@189 | 310 } | 
| Chris@173 | 311 | 
| Chris@189 | 312 void | 
| Chris@189 | 313 Overview::enterEvent(QEvent *) | 
| Chris@189 | 314 { | 
| Chris@189 | 315     emit contextHelpChanged(tr("Click and drag to navigate; double-click to jump")); | 
| Chris@189 | 316 } | 
| Chris@189 | 317 | 
| Chris@189 | 318 void | 
| Chris@189 | 319 Overview::leaveEvent(QEvent *) | 
| Chris@189 | 320 { | 
| Chris@189 | 321     emit contextHelpChanged(""); | 
| Chris@189 | 322 } | 
| Chris@189 | 323 | 
| Chris@189 | 324 |