| Chris@0 | 1 /* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@0 | 2 | 
| Chris@0 | 3 /* | 
| Chris@0 | 4     A waveform viewer and audio annotation editor. | 
| Chris@5 | 5     Chris Cannam, Queen Mary University of London, 2005-2006 | 
| Chris@0 | 6 | 
| Chris@0 | 7     This is experimental software.  Not for distribution. | 
| Chris@0 | 8 */ | 
| Chris@0 | 9 | 
| Chris@0 | 10 #include "widgets/Pane.h" | 
| Chris@0 | 11 #include "base/Layer.h" | 
| Chris@0 | 12 #include "base/Model.h" | 
| Chris@0 | 13 #include "base/ZoomConstraint.h" | 
| Chris@0 | 14 #include "base/RealTime.h" | 
| Chris@0 | 15 #include "base/Profiler.h" | 
| Chris@13 | 16 #include "base/ViewManager.h" | 
| Chris@0 | 17 | 
| Chris@0 | 18 #include <QPaintEvent> | 
| Chris@0 | 19 #include <QPainter> | 
| Chris@0 | 20 #include <iostream> | 
| Chris@0 | 21 #include <cmath> | 
| Chris@0 | 22 | 
| Chris@0 | 23 using std::cerr; | 
| Chris@0 | 24 using std::endl; | 
| Chris@0 | 25 | 
| Chris@0 | 26 Pane::Pane(QWidget *w) : | 
| Chris@0 | 27     View(w, true), | 
| Chris@0 | 28     m_identifyFeatures(false), | 
| Chris@0 | 29     m_clickedInRange(false), | 
| Chris@0 | 30     m_shiftPressed(false), | 
| Chris@13 | 31     m_ctrlPressed(false), | 
| Chris@17 | 32     m_navigating(false), | 
| Chris@17 | 33     m_resizing(false), | 
| Chris@0 | 34     m_centreLineVisible(true) | 
| Chris@0 | 35 { | 
| Chris@0 | 36     setObjectName("Pane"); | 
| Chris@0 | 37     setMouseTracking(true); | 
| Chris@0 | 38 } | 
| Chris@0 | 39 | 
| Chris@0 | 40 bool | 
| Chris@0 | 41 Pane::shouldIlluminateLocalFeatures(const Layer *layer, QPoint &pos) | 
| Chris@0 | 42 { | 
| Chris@27 | 43     if (layer == getSelectedLayer()) { | 
| Chris@27 | 44 	pos = m_identifyPoint; | 
| Chris@27 | 45 	return m_identifyFeatures; | 
| Chris@27 | 46     } | 
| Chris@0 | 47 | 
| Chris@0 | 48     return false; | 
| Chris@0 | 49 } | 
| Chris@0 | 50 | 
| Chris@0 | 51 void | 
| Chris@0 | 52 Pane::setCentreLineVisible(bool visible) | 
| Chris@0 | 53 { | 
| Chris@0 | 54     m_centreLineVisible = visible; | 
| Chris@0 | 55     update(); | 
| Chris@0 | 56 } | 
| Chris@0 | 57 | 
| Chris@0 | 58 void | 
| Chris@0 | 59 Pane::paintEvent(QPaintEvent *e) | 
| Chris@0 | 60 { | 
| Chris@0 | 61     QPainter paint; | 
| Chris@0 | 62 | 
| Chris@0 | 63     QRect r(rect()); | 
| Chris@0 | 64 | 
| Chris@0 | 65     if (e) { | 
| Chris@0 | 66 	r = e->rect(); | 
| Chris@0 | 67     } | 
| Chris@0 | 68 /* | 
| Chris@0 | 69     paint.begin(this); | 
| Chris@0 | 70     paint.setClipRect(r); | 
| Chris@0 | 71 | 
| Chris@0 | 72     if (hasLightBackground()) { | 
| Chris@0 | 73 	paint.setPen(Qt::white); | 
| Chris@0 | 74 	paint.setBrush(Qt::white); | 
| Chris@0 | 75     } else { | 
| Chris@0 | 76 	paint.setPen(Qt::black); | 
| Chris@0 | 77 	paint.setBrush(Qt::black); | 
| Chris@0 | 78     } | 
| Chris@0 | 79     paint.drawRect(r); | 
| Chris@0 | 80 | 
| Chris@0 | 81     paint.end(); | 
| Chris@0 | 82 */ | 
| Chris@0 | 83     View::paintEvent(e); | 
| Chris@0 | 84 | 
| Chris@0 | 85     paint.begin(this); | 
| Chris@0 | 86 | 
| Chris@0 | 87     if (e) { | 
| Chris@0 | 88 	paint.setClipRect(r); | 
| Chris@0 | 89     } | 
| Chris@0 | 90 | 
| Chris@0 | 91     for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { | 
| Chris@0 | 92 	--vi; | 
| Chris@0 | 93 | 
| Chris@0 | 94 	int sw = (*vi)->getVerticalScaleWidth(paint); | 
| Chris@0 | 95 | 
| Chris@0 | 96 	if (sw > 0 && r.left() < sw) { | 
| Chris@0 | 97 | 
| Chris@0 | 98 //	    Profiler profiler("Pane::paintEvent - painting vertical scale", true); | 
| Chris@0 | 99 | 
| Chris@0 | 100 //	    std::cerr << "Pane::paintEvent: calling paint.save() in vertical scale block" << std::endl; | 
| Chris@0 | 101 	    paint.save(); | 
| Chris@0 | 102 | 
| Chris@0 | 103 	    paint.setPen(Qt::black); | 
| Chris@0 | 104 	    paint.setBrush(Qt::white); | 
| Chris@0 | 105 	    paint.drawRect(0, 0, sw, height()); | 
| Chris@0 | 106 | 
| Chris@0 | 107 	    paint.setBrush(Qt::NoBrush); | 
| Chris@0 | 108 	    (*vi)->paintVerticalScale(paint, QRect(0, 0, sw, height())); | 
| Chris@0 | 109 | 
| Chris@0 | 110 	    paint.restore(); | 
| Chris@0 | 111 	} | 
| Chris@0 | 112 | 
| Chris@0 | 113 	if (m_identifyFeatures) { | 
| Chris@25 | 114 | 
| Chris@25 | 115 	    QPoint pos = m_identifyPoint; | 
| Chris@25 | 116 	    QString desc = (*vi)->getFeatureDescription(pos); | 
| Chris@25 | 117 | 
| Chris@25 | 118 	    if (desc != "") { | 
| Chris@25 | 119 | 
| Chris@25 | 120 		paint.save(); | 
| Chris@25 | 121 | 
| Chris@25 | 122 		int tabStop = | 
| Chris@25 | 123 		    paint.fontMetrics().width(tr("Some lengthy prefix:")); | 
| Chris@25 | 124 | 
| Chris@25 | 125 		QRect boundingRect = | 
| Chris@25 | 126 		    paint.fontMetrics().boundingRect | 
| Chris@25 | 127 		    (rect(), | 
| Chris@25 | 128 		     Qt::AlignRight | Qt::AlignTop | Qt::TextExpandTabs, | 
| Chris@25 | 129 		     desc, tabStop); | 
| Chris@25 | 130 | 
| Chris@25 | 131 		if (hasLightBackground()) { | 
| Chris@25 | 132 		    paint.setPen(Qt::NoPen); | 
| Chris@25 | 133 		    paint.setBrush(QColor(250, 250, 250, 200)); | 
| Chris@25 | 134 		} else { | 
| Chris@25 | 135 		    paint.setPen(Qt::NoPen); | 
| Chris@25 | 136 		    paint.setBrush(QColor(50, 50, 50, 200)); | 
| Chris@25 | 137 		} | 
| Chris@25 | 138 | 
| Chris@25 | 139 		int extra = paint.fontMetrics().descent(); | 
| Chris@25 | 140 		paint.drawRect(width() - boundingRect.width() - 10 - extra, | 
| Chris@25 | 141 			       10 - extra, | 
| Chris@25 | 142 			       boundingRect.width() + 2 * extra, | 
| Chris@25 | 143 			       boundingRect.height() + extra); | 
| Chris@25 | 144 | 
| Chris@25 | 145 		if (hasLightBackground()) { | 
| Chris@25 | 146 		    paint.setPen(QColor(150, 20, 0)); | 
| Chris@25 | 147 		} else { | 
| Chris@25 | 148 		    paint.setPen(QColor(255, 150, 100)); | 
| Chris@25 | 149 		} | 
| Chris@25 | 150 | 
| Chris@25 | 151 		QTextOption option; | 
| Chris@25 | 152 		option.setWrapMode(QTextOption::NoWrap); | 
| Chris@25 | 153 		option.setAlignment(Qt::AlignRight | Qt::AlignTop); | 
| Chris@25 | 154 		option.setTabStop(tabStop); | 
| Chris@25 | 155 		paint.drawText(QRectF(width() - boundingRect.width() - 10, 10, | 
| Chris@25 | 156 				      boundingRect.width(), | 
| Chris@25 | 157 				      boundingRect.height()), | 
| Chris@25 | 158 			       desc, | 
| Chris@25 | 159 			       option); | 
| Chris@25 | 160 | 
| Chris@25 | 161 		paint.restore(); | 
| Chris@25 | 162 	    } | 
| Chris@0 | 163 	} | 
| Chris@0 | 164 | 
| Chris@0 | 165 	break; | 
| Chris@0 | 166     } | 
| Chris@0 | 167 | 
| Chris@0 | 168     if (m_centreLineVisible) { | 
| Chris@0 | 169 | 
| Chris@0 | 170 	if (hasLightBackground()) { | 
| Chris@0 | 171 	    paint.setPen(QColor(50, 50, 50)); | 
| Chris@0 | 172 	} else { | 
| Chris@0 | 173 	    paint.setPen(QColor(200, 200, 200)); | 
| Chris@0 | 174 	} | 
| Chris@0 | 175 	paint.setBrush(Qt::NoBrush); | 
| Chris@0 | 176 	paint.drawLine(width() / 2, 0, width() / 2, height() - 1); | 
| Chris@0 | 177 | 
| Chris@0 | 178 //    QFont font(paint.font()); | 
| Chris@0 | 179 //    font.setBold(true); | 
| Chris@0 | 180 //    paint.setFont(font); | 
| Chris@0 | 181 | 
| Chris@0 | 182 	int sampleRate = getModelsSampleRate(); | 
| Chris@0 | 183 	int y = height() - paint.fontMetrics().height() | 
| Chris@0 | 184 	    + paint.fontMetrics().ascent() - 6; | 
| Chris@0 | 185 | 
| Chris@0 | 186 	LayerList::iterator vi = m_layers.end(); | 
| Chris@0 | 187 | 
| Chris@0 | 188 	if (vi != m_layers.begin()) { | 
| Chris@0 | 189 | 
| Chris@0 | 190 	    switch ((*--vi)->getPreferredFrameCountPosition()) { | 
| Chris@0 | 191 | 
| Chris@0 | 192 	    case Layer::PositionTop: | 
| Chris@0 | 193 		y = paint.fontMetrics().ascent() + 6; | 
| Chris@0 | 194 		break; | 
| Chris@0 | 195 | 
| Chris@0 | 196 	    case Layer::PositionMiddle: | 
| Chris@0 | 197 		y = (height() - paint.fontMetrics().height()) / 2 | 
| Chris@0 | 198 		    + paint.fontMetrics().ascent(); | 
| Chris@0 | 199 		break; | 
| Chris@0 | 200 | 
| Chris@0 | 201 	    case Layer::PositionBottom: | 
| Chris@0 | 202 		// y already set correctly | 
| Chris@0 | 203 		break; | 
| Chris@0 | 204 	    } | 
| Chris@0 | 205 	} | 
| Chris@0 | 206 | 
| Chris@0 | 207 	if (sampleRate) { | 
| Chris@0 | 208 | 
| Chris@0 | 209 	    QString text(QString::fromStdString | 
| Chris@0 | 210 			 (RealTime::frame2RealTime | 
| Chris@0 | 211 			  (m_centreFrame, sampleRate).toText(true))); | 
| Chris@0 | 212 | 
| Chris@0 | 213 	    int tw = paint.fontMetrics().width(text); | 
| Chris@0 | 214 	    int x = width()/2 - 4 - tw; | 
| Chris@0 | 215 | 
| Chris@0 | 216 	    if (hasLightBackground()) { | 
| Chris@0 | 217 		paint.setPen(palette().background().color()); | 
| Chris@0 | 218 		for (int dx = -1; dx <= 1; ++dx) { | 
| Chris@0 | 219 		    for (int dy = -1; dy <= 1; ++dy) { | 
| Chris@0 | 220 			if ((dx && dy) || !(dx || dy)) continue; | 
| Chris@0 | 221 			paint.drawText(x + dx, y + dy, text); | 
| Chris@0 | 222 		    } | 
| Chris@0 | 223 		} | 
| Chris@0 | 224 		paint.setPen(QColor(50, 50, 50)); | 
| Chris@0 | 225 	    } else { | 
| Chris@0 | 226 		paint.setPen(QColor(200, 200, 200)); | 
| Chris@0 | 227 	    } | 
| Chris@0 | 228 | 
| Chris@0 | 229 	    paint.drawText(x, y, text); | 
| Chris@0 | 230 	} | 
| Chris@0 | 231 | 
| Chris@0 | 232 	QString text = QString("%1").arg(m_centreFrame); | 
| Chris@0 | 233 | 
| Chris@0 | 234 	int tw = paint.fontMetrics().width(text); | 
| Chris@0 | 235 	int x = width()/2 + 4; | 
| Chris@0 | 236 | 
| Chris@0 | 237 	if (hasLightBackground()) { | 
| Chris@0 | 238 	    paint.setPen(palette().background().color()); | 
| Chris@0 | 239 	    for (int dx = -1; dx <= 1; ++dx) { | 
| Chris@0 | 240 		for (int dy = -1; dy <= 1; ++dy) { | 
| Chris@0 | 241 		    if ((dx && dy) || !(dx || dy)) continue; | 
| Chris@0 | 242 		    paint.drawText(x + dx, y + dy, text); | 
| Chris@0 | 243 		} | 
| Chris@0 | 244 	    } | 
| Chris@0 | 245 	    paint.setPen(QColor(50, 50, 50)); | 
| Chris@0 | 246 	} else { | 
| Chris@0 | 247 	    paint.setPen(QColor(200, 200, 200)); | 
| Chris@0 | 248 	} | 
| Chris@0 | 249 	paint.drawText(x, y, text); | 
| Chris@0 | 250     } | 
| Chris@0 | 251 | 
| Chris@0 | 252     if (m_clickedInRange && m_shiftPressed) { | 
| Chris@19 | 253 	if (m_manager && (m_manager->getToolMode() == ViewManager::NavigateMode)) { | 
| Chris@19 | 254 	    //!!! be nice if this looked a bit more in keeping with the | 
| Chris@19 | 255 	    //selection block | 
| Chris@19 | 256 	    paint.setPen(Qt::blue); | 
| Chris@19 | 257 	    paint.drawRect(m_clickPos.x(), m_clickPos.y(), | 
| Chris@19 | 258 			   m_mousePos.x() - m_clickPos.x(), | 
| Chris@19 | 259 			   m_mousePos.y() - m_clickPos.y()); | 
| Chris@19 | 260 	} | 
| Chris@0 | 261     } | 
| Chris@0 | 262 | 
| Chris@0 | 263     paint.end(); | 
| Chris@0 | 264 } | 
| Chris@0 | 265 | 
| Chris@17 | 266 Selection | 
| Chris@17 | 267 Pane::getSelectionAt(int x, bool &closeToLeftEdge, bool &closeToRightEdge) | 
| Chris@17 | 268 { | 
| Chris@17 | 269     closeToLeftEdge = closeToRightEdge = false; | 
| Chris@17 | 270 | 
| Chris@17 | 271     if (!m_manager) return Selection(); | 
| Chris@17 | 272 | 
| Chris@20 | 273     long testFrame = getFrameForX(x - 5); | 
| Chris@17 | 274     if (testFrame < 0) { | 
| Chris@20 | 275 	testFrame = getFrameForX(x); | 
| Chris@17 | 276 	if (testFrame < 0) return Selection(); | 
| Chris@17 | 277     } | 
| Chris@17 | 278 | 
| Chris@17 | 279     Selection selection = m_manager->getContainingSelection(testFrame, true); | 
| Chris@17 | 280     if (selection.isEmpty()) return selection; | 
| Chris@17 | 281 | 
| Chris@20 | 282     int lx = getXForFrame(selection.getStartFrame()); | 
| Chris@20 | 283     int rx = getXForFrame(selection.getEndFrame()); | 
| Chris@17 | 284 | 
| Chris@17 | 285     int fuzz = 2; | 
| Chris@17 | 286     if (x < lx - fuzz || x > rx + fuzz) return Selection(); | 
| Chris@17 | 287 | 
| Chris@17 | 288     int width = rx - lx; | 
| Chris@17 | 289     fuzz = 3; | 
| Chris@17 | 290     if (width < 12) fuzz = width / 4; | 
| Chris@17 | 291     if (fuzz < 1) fuzz = 1; | 
| Chris@17 | 292 | 
| Chris@17 | 293     if (x < lx + fuzz) closeToLeftEdge = true; | 
| Chris@17 | 294     if (x > rx - fuzz) closeToRightEdge = true; | 
| Chris@17 | 295 | 
| Chris@17 | 296     return selection; | 
| Chris@17 | 297 } | 
| Chris@17 | 298 | 
| Chris@0 | 299 void | 
| Chris@0 | 300 Pane::mousePressEvent(QMouseEvent *e) | 
| Chris@0 | 301 { | 
| Chris@0 | 302     m_clickPos = e->pos(); | 
| Chris@0 | 303     m_clickedInRange = true; | 
| Chris@0 | 304     m_shiftPressed = (e->modifiers() & Qt::ShiftModifier); | 
| Chris@13 | 305     m_ctrlPressed = (e->modifiers() & Qt::ControlModifier); | 
| Chris@13 | 306 | 
| Chris@13 | 307     ViewManager::ToolMode mode = ViewManager::NavigateMode; | 
| Chris@13 | 308     if (m_manager) mode = m_manager->getToolMode(); | 
| Chris@13 | 309 | 
| Chris@17 | 310     m_navigating = false; | 
| Chris@13 | 311 | 
| Chris@17 | 312     if (mode == ViewManager::NavigateMode || (e->buttons() & Qt::MidButton)) { | 
| Chris@17 | 313 | 
| Chris@17 | 314 	if (mode != ViewManager::NavigateMode) { | 
| Chris@17 | 315 	    setCursor(Qt::PointingHandCursor); | 
| Chris@17 | 316 	} | 
| Chris@17 | 317 | 
| Chris@17 | 318 	m_navigating = true; | 
| Chris@13 | 319 	m_dragCentreFrame = m_centreFrame; | 
| Chris@13 | 320 | 
| Chris@13 | 321     } else if (mode == ViewManager::SelectMode) { | 
| Chris@13 | 322 | 
| Chris@17 | 323 	bool closeToLeft = false, closeToRight = false; | 
| Chris@17 | 324 	Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight); | 
| Chris@17 | 325 | 
| Chris@17 | 326 	if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { | 
| Chris@17 | 327 | 
| Chris@17 | 328 	    m_manager->removeSelection(selection); | 
| Chris@17 | 329 | 
| Chris@17 | 330 	    if (closeToLeft) { | 
| Chris@17 | 331 		m_selectionStartFrame = selection.getEndFrame(); | 
| Chris@17 | 332 	    } else { | 
| Chris@17 | 333 		m_selectionStartFrame = selection.getStartFrame(); | 
| Chris@17 | 334 	    } | 
| Chris@17 | 335 | 
| Chris@17 | 336 	    m_manager->setInProgressSelection(selection, false); | 
| Chris@17 | 337 	    m_resizing = true; | 
| Chris@13 | 338 | 
| Chris@17 | 339 	} else { | 
| Chris@17 | 340 | 
| Chris@20 | 341 	    int mouseFrame = getFrameForX(e->x()); | 
| Chris@17 | 342 	    size_t resolution = 1; | 
| Chris@17 | 343 	    int snapFrame = mouseFrame; | 
| Chris@17 | 344 | 
| Chris@17 | 345 	    Layer *layer = getSelectedLayer(); | 
| Chris@17 | 346 	    if (layer) { | 
| Chris@28 | 347 		layer->snapToFeatureFrame(snapFrame, resolution, Layer::SnapLeft); | 
| Chris@17 | 348 	    } | 
| Chris@17 | 349 | 
| Chris@17 | 350 	    if (snapFrame < 0) snapFrame = 0; | 
| Chris@17 | 351 	    m_selectionStartFrame = snapFrame; | 
| Chris@17 | 352 	    if (m_manager) { | 
| Chris@17 | 353 		m_manager->setInProgressSelection(Selection(snapFrame, | 
| Chris@17 | 354 							    snapFrame + resolution), | 
| Chris@17 | 355 						  !m_ctrlPressed); | 
| Chris@17 | 356 	    } | 
| Chris@17 | 357 | 
| Chris@17 | 358 	    m_resizing = false; | 
| Chris@17 | 359 	} | 
| Chris@17 | 360 | 
| Chris@17 | 361 	update(); | 
| Chris@17 | 362 | 
| Chris@17 | 363     } else if (mode == ViewManager::DrawMode) { | 
| Chris@17 | 364 | 
| Chris@13 | 365 	Layer *layer = getSelectedLayer(); | 
| Chris@23 | 366 	if (layer && layer->isLayerEditable()) { | 
| Chris@17 | 367 	    layer->drawStart(e); | 
| Chris@13 | 368 	} | 
| Chris@18 | 369 | 
| Chris@18 | 370     } else if (mode == ViewManager::EditMode) { | 
| Chris@18 | 371 | 
| Chris@18 | 372 	Layer *layer = getSelectedLayer(); | 
| Chris@23 | 373 	if (layer && layer->isLayerEditable()) { | 
| Chris@18 | 374 	    layer->editStart(e); | 
| Chris@18 | 375 	} | 
| Chris@13 | 376     } | 
| Chris@0 | 377 | 
| Chris@0 | 378     emit paneInteractedWith(); | 
| Chris@0 | 379 } | 
| Chris@0 | 380 | 
| Chris@0 | 381 void | 
| Chris@0 | 382 Pane::mouseReleaseEvent(QMouseEvent *e) | 
| Chris@0 | 383 { | 
| Chris@13 | 384     ViewManager::ToolMode mode = ViewManager::NavigateMode; | 
| Chris@13 | 385     if (m_manager) mode = m_manager->getToolMode(); | 
| Chris@13 | 386 | 
| Chris@0 | 387     if (m_clickedInRange) { | 
| Chris@0 | 388 	mouseMoveEvent(e); | 
| Chris@0 | 389     } | 
| Chris@0 | 390 | 
| Chris@17 | 391     if (m_navigating || mode == ViewManager::NavigateMode) { | 
| Chris@17 | 392 | 
| Chris@17 | 393 	m_navigating = false; | 
| Chris@17 | 394 | 
| Chris@17 | 395 	if (mode != ViewManager::NavigateMode) { | 
| Chris@17 | 396 	    // restore cursor | 
| Chris@17 | 397 	    toolModeChanged(); | 
| Chris@17 | 398 	} | 
| Chris@0 | 399 | 
| Chris@13 | 400 	if (m_shiftPressed) { | 
| Chris@0 | 401 | 
| Chris@13 | 402 	    int x0 = std::min(m_clickPos.x(), m_mousePos.x()); | 
| Chris@13 | 403 	    int x1 = std::max(m_clickPos.x(), m_mousePos.x()); | 
| Chris@13 | 404 	    int w = x1 - x0; | 
| Chris@13 | 405 | 
| Chris@20 | 406 	    long newStartFrame = getFrameForX(x0); | 
| Chris@13 | 407 | 
| Chris@20 | 408 	    long visibleFrames = getEndFrame() - getStartFrame(); | 
| Chris@20 | 409 	    if (newStartFrame <= -visibleFrames) { | 
| Chris@20 | 410 		newStartFrame  = -visibleFrames + 1; | 
| Chris@13 | 411 	    } | 
| Chris@13 | 412 | 
| Chris@13 | 413 	    if (newStartFrame >= long(getModelsEndFrame())) { | 
| Chris@13 | 414 		newStartFrame  = getModelsEndFrame() - 1; | 
| Chris@13 | 415 	    } | 
| Chris@13 | 416 | 
| Chris@13 | 417 	    float ratio = float(w) / float(width()); | 
| Chris@13 | 418 //	std::cerr << "ratio: " << ratio << std::endl; | 
| Chris@13 | 419 	    size_t newZoomLevel = (size_t)nearbyint(m_zoomLevel * ratio); | 
| Chris@13 | 420 	    if (newZoomLevel < 1) newZoomLevel = 1; | 
| Chris@13 | 421 | 
| Chris@13 | 422 //	std::cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << std::endl; | 
| Chris@13 | 423 	    setZoomLevel(getZoomConstraintBlockSize(newZoomLevel)); | 
| Chris@13 | 424 	    setStartFrame(newStartFrame); | 
| Chris@13 | 425 | 
| Chris@13 | 426 	    //cerr << "mouseReleaseEvent: start frame now " << m_startFrame << endl; | 
| Chris@13 | 427 //	update(); | 
| Chris@0 | 428 	} | 
| Chris@0 | 429 | 
| Chris@13 | 430     } else if (mode == ViewManager::SelectMode) { | 
| Chris@13 | 431 | 
| Chris@13 | 432 	if (m_manager && m_manager->haveInProgressSelection()) { | 
| Chris@13 | 433 | 
| Chris@13 | 434 	    bool exclusive; | 
| Chris@13 | 435 	    Selection selection = m_manager->getInProgressSelection(exclusive); | 
| Chris@13 | 436 | 
| Chris@13 | 437 	    if (selection.getEndFrame() < selection.getStartFrame() + 2) { | 
| Chris@13 | 438 		selection = Selection(); | 
| Chris@13 | 439 	    } | 
| Chris@13 | 440 | 
| Chris@13 | 441 	    m_manager->clearInProgressSelection(); | 
| Chris@13 | 442 | 
| Chris@13 | 443 	    if (exclusive) { | 
| Chris@13 | 444 		m_manager->setSelection(selection); | 
| Chris@13 | 445 	    } else { | 
| Chris@13 | 446 		m_manager->addSelection(selection); | 
| Chris@13 | 447 	    } | 
| Chris@0 | 448 	} | 
| Chris@13 | 449 | 
| Chris@13 | 450 	update(); | 
| Chris@17 | 451 | 
| Chris@17 | 452     } else if (mode == ViewManager::DrawMode) { | 
| Chris@17 | 453 | 
| Chris@17 | 454 	Layer *layer = getSelectedLayer(); | 
| Chris@23 | 455 	if (layer && layer->isLayerEditable()) { | 
| Chris@17 | 456 	    layer->drawEnd(e); | 
| Chris@17 | 457 	    update(); | 
| Chris@17 | 458 	} | 
| Chris@18 | 459 | 
| Chris@18 | 460     } else if (mode == ViewManager::EditMode) { | 
| Chris@18 | 461 | 
| Chris@18 | 462 	Layer *layer = getSelectedLayer(); | 
| Chris@23 | 463 	if (layer && layer->isLayerEditable()) { | 
| Chris@18 | 464 	    layer->editEnd(e); | 
| Chris@18 | 465 	    update(); | 
| Chris@18 | 466 	} | 
| Chris@17 | 467     } | 
| Chris@0 | 468 | 
| Chris@0 | 469     m_clickedInRange = false; | 
| Chris@0 | 470 | 
| Chris@0 | 471     emit paneInteractedWith(); | 
| Chris@0 | 472 } | 
| Chris@0 | 473 | 
| Chris@0 | 474 void | 
| Chris@0 | 475 Pane::mouseMoveEvent(QMouseEvent *e) | 
| Chris@0 | 476 { | 
| Chris@13 | 477     ViewManager::ToolMode mode = ViewManager::NavigateMode; | 
| Chris@13 | 478     if (m_manager) mode = m_manager->getToolMode(); | 
| Chris@13 | 479 | 
| Chris@28 | 480     QPoint prevPoint = m_identifyPoint; | 
| Chris@28 | 481     m_identifyPoint = e->pos(); | 
| Chris@28 | 482 | 
| Chris@0 | 483     if (!m_clickedInRange) { | 
| Chris@0 | 484 | 
| Chris@17 | 485 	if (mode == ViewManager::SelectMode) { | 
| Chris@17 | 486 	    bool closeToLeft = false, closeToRight = false; | 
| Chris@17 | 487 	    getSelectionAt(e->x(), closeToLeft, closeToRight); | 
| Chris@17 | 488 	    if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { | 
| Chris@17 | 489 		setCursor(Qt::SizeHorCursor); | 
| Chris@17 | 490 	    } else { | 
| Chris@17 | 491 		setCursor(Qt::ArrowCursor); | 
| Chris@17 | 492 	    } | 
| Chris@17 | 493 	} | 
| Chris@0 | 494 | 
| Chris@35 | 495 //!!!	if (mode != ViewManager::DrawMode) { | 
| Chris@0 | 496 | 
| Chris@17 | 497 	    bool previouslyIdentifying = m_identifyFeatures; | 
| Chris@17 | 498 	    m_identifyFeatures = true; | 
| Chris@17 | 499 | 
| Chris@17 | 500 	    if (m_identifyFeatures != previouslyIdentifying || | 
| Chris@17 | 501 		m_identifyPoint != prevPoint) { | 
| Chris@17 | 502 		update(); | 
| Chris@17 | 503 	    } | 
| Chris@35 | 504 //	} | 
| Chris@0 | 505 | 
| Chris@13 | 506 	return; | 
| Chris@13 | 507     } | 
| Chris@0 | 508 | 
| Chris@17 | 509     if (m_navigating || mode == ViewManager::NavigateMode) { | 
| Chris@0 | 510 | 
| Chris@13 | 511 	if (m_shiftPressed) { | 
| Chris@0 | 512 | 
| Chris@13 | 513 	    m_mousePos = e->pos(); | 
| Chris@13 | 514 	    update(); | 
| Chris@0 | 515 | 
| Chris@0 | 516 	} else { | 
| Chris@13 | 517 | 
| Chris@20 | 518 	    long frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x()); | 
| Chris@20 | 519 | 
| Chris@13 | 520 	    size_t newCentreFrame = m_dragCentreFrame; | 
| Chris@13 | 521 | 
| Chris@13 | 522 	    if (frameOff < 0) { | 
| Chris@13 | 523 		newCentreFrame -= frameOff; | 
| Chris@13 | 524 	    } else if (newCentreFrame >= size_t(frameOff)) { | 
| Chris@13 | 525 		newCentreFrame -= frameOff; | 
| Chris@13 | 526 	    } else { | 
| Chris@13 | 527 		newCentreFrame = 0; | 
| Chris@13 | 528 	    } | 
| Chris@13 | 529 | 
| Chris@13 | 530 	    if (newCentreFrame >= getModelsEndFrame()) { | 
| Chris@13 | 531 		newCentreFrame = getModelsEndFrame(); | 
| Chris@13 | 532 		if (newCentreFrame > 0) --newCentreFrame; | 
| Chris@13 | 533 	    } | 
| Chris@20 | 534 | 
| Chris@20 | 535 	    if (getXForFrame(m_centreFrame) != getXForFrame(newCentreFrame)) { | 
| Chris@13 | 536 		setCentreFrame(newCentreFrame); | 
| Chris@13 | 537 	    } | 
| Chris@0 | 538 	} | 
| Chris@0 | 539 | 
| Chris@13 | 540     } else if (mode == ViewManager::SelectMode) { | 
| Chris@13 | 541 | 
| Chris@20 | 542 	int mouseFrame = getFrameForX(e->x()); | 
| Chris@13 | 543 	size_t resolution = 1; | 
| Chris@13 | 544 	int snapFrameLeft = mouseFrame; | 
| Chris@13 | 545 	int snapFrameRight = mouseFrame; | 
| Chris@13 | 546 | 
| Chris@13 | 547 	Layer *layer = getSelectedLayer(); | 
| Chris@13 | 548 	if (layer) { | 
| Chris@28 | 549 	    layer->snapToFeatureFrame(snapFrameLeft, resolution, Layer::SnapLeft); | 
| Chris@28 | 550 	    layer->snapToFeatureFrame(snapFrameRight, resolution, Layer::SnapRight); | 
| Chris@13 | 551 	} | 
| Chris@13 | 552 | 
| Chris@28 | 553 	std::cerr << "snap: frame = " << mouseFrame << ", start frame = " << m_selectionStartFrame << ", left = " << snapFrameLeft << ", right = " << snapFrameRight << std::endl; | 
| Chris@28 | 554 | 
| Chris@13 | 555 	if (snapFrameLeft < 0) snapFrameLeft = 0; | 
| Chris@13 | 556 	if (snapFrameRight < 0) snapFrameRight = 0; | 
| Chris@13 | 557 | 
| Chris@13 | 558 	size_t min, max; | 
| Chris@13 | 559 | 
| Chris@13 | 560 	if (m_selectionStartFrame > snapFrameLeft) { | 
| Chris@13 | 561 	    min = snapFrameLeft; | 
| Chris@13 | 562 	    max = m_selectionStartFrame; | 
| Chris@13 | 563 	} else if (snapFrameRight > m_selectionStartFrame) { | 
| Chris@13 | 564 	    min = m_selectionStartFrame; | 
| Chris@13 | 565 	    max = snapFrameRight; | 
| Chris@13 | 566 	} else { | 
| Chris@13 | 567 	    min = snapFrameLeft; | 
| Chris@13 | 568 	    max = snapFrameRight; | 
| Chris@0 | 569 	} | 
| Chris@0 | 570 | 
| Chris@13 | 571 	if (m_manager) { | 
| Chris@13 | 572 	    m_manager->setInProgressSelection(Selection(min, max), | 
| Chris@17 | 573 					      !m_resizing && !m_ctrlPressed); | 
| Chris@0 | 574 	} | 
| Chris@15 | 575 | 
| Chris@15 | 576 	bool doScroll = false; | 
| Chris@15 | 577 	if (!m_manager) doScroll = true; | 
| Chris@15 | 578 	if (!m_manager->isPlaying()) doScroll = true; | 
| Chris@15 | 579 	if (m_followPlay != PlaybackScrollContinuous) doScroll = true; | 
| Chris@15 | 580 | 
| Chris@15 | 581 	if (doScroll) { | 
| Chris@13 | 582 	    int offset = mouseFrame - getStartFrame(); | 
| Chris@13 | 583 	    int available = getEndFrame() - getStartFrame(); | 
| Chris@15 | 584 	    if (offset >= available * 0.95) { | 
| Chris@15 | 585 		int move = int(offset - available * 0.95) + 1; | 
| Chris@14 | 586 		setCentreFrame(m_centreFrame + move); | 
| Chris@15 | 587 	    } else if (offset <= available * 0.10) { | 
| Chris@15 | 588 		int move = int(available * 0.10 - offset) + 1; | 
| Chris@14 | 589 		if (m_centreFrame > move) { | 
| Chris@14 | 590 		    setCentreFrame(m_centreFrame - move); | 
| Chris@14 | 591 		} else { | 
| Chris@14 | 592 		    setCentreFrame(0); | 
| Chris@14 | 593 		} | 
| Chris@13 | 594 	    } | 
| Chris@13 | 595 	} | 
| Chris@13 | 596 | 
| Chris@13 | 597 	update(); | 
| Chris@17 | 598 | 
| Chris@17 | 599     } else if (mode == ViewManager::DrawMode) { | 
| Chris@17 | 600 | 
| Chris@17 | 601 	Layer *layer = getSelectedLayer(); | 
| Chris@23 | 602 	if (layer && layer->isLayerEditable()) { | 
| Chris@17 | 603 	    layer->drawDrag(e); | 
| Chris@17 | 604 	} | 
| Chris@18 | 605 | 
| Chris@18 | 606     } else if (mode == ViewManager::EditMode) { | 
| Chris@18 | 607 | 
| Chris@18 | 608 	Layer *layer = getSelectedLayer(); | 
| Chris@23 | 609 	if (layer && layer->isLayerEditable()) { | 
| Chris@18 | 610 	    layer->editDrag(e); | 
| Chris@18 | 611 	} | 
| Chris@0 | 612     } | 
| Chris@0 | 613 } | 
| Chris@0 | 614 | 
| Chris@0 | 615 void | 
| Chris@0 | 616 Pane::mouseDoubleClickEvent(QMouseEvent *e) | 
| Chris@0 | 617 { | 
| Chris@0 | 618     std::cerr << "mouseDoubleClickEvent" << std::endl; | 
| Chris@0 | 619 } | 
| Chris@0 | 620 | 
| Chris@0 | 621 void | 
| Chris@0 | 622 Pane::leaveEvent(QEvent *) | 
| Chris@0 | 623 { | 
| Chris@0 | 624     bool previouslyIdentifying = m_identifyFeatures; | 
| Chris@0 | 625     m_identifyFeatures = false; | 
| Chris@0 | 626     if (previouslyIdentifying) update(); | 
| Chris@0 | 627 } | 
| Chris@0 | 628 | 
| Chris@0 | 629 void | 
| Chris@0 | 630 Pane::wheelEvent(QWheelEvent *e) | 
| Chris@0 | 631 { | 
| Chris@0 | 632     //std::cerr << "wheelEvent, delta " << e->delta() << std::endl; | 
| Chris@0 | 633 | 
| Chris@0 | 634     int count = e->delta(); | 
| Chris@0 | 635 | 
| Chris@0 | 636     if (count > 0) { | 
| Chris@0 | 637 	if (count >= 120) count /= 120; | 
| Chris@0 | 638 	else count = 1; | 
| Chris@0 | 639     } | 
| Chris@0 | 640 | 
| Chris@0 | 641     if (count < 0) { | 
| Chris@0 | 642 	if (count <= -120) count /= 120; | 
| Chris@0 | 643 	else count = -1; | 
| Chris@0 | 644     } | 
| Chris@17 | 645 | 
| Chris@17 | 646     if (e->modifiers() & Qt::ControlModifier) { | 
| Chris@17 | 647 | 
| Chris@20 | 648 	// Scroll left or right, rapidly | 
| Chris@20 | 649 | 
| Chris@17 | 650 	if (getStartFrame() < 0 && | 
| Chris@17 | 651 	    getEndFrame() >= getModelsEndFrame()) return; | 
| Chris@17 | 652 | 
| Chris@17 | 653 	long delta = ((width() / 2) * count * m_zoomLevel); | 
| Chris@17 | 654 | 
| Chris@17 | 655 	if (int(m_centreFrame) < delta) { | 
| Chris@17 | 656 	    setCentreFrame(0); | 
| Chris@17 | 657 	} else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { | 
| Chris@17 | 658 	    setCentreFrame(getModelsEndFrame()); | 
| Chris@17 | 659 	} else { | 
| Chris@17 | 660 	    setCentreFrame(m_centreFrame - delta); | 
| Chris@17 | 661 	} | 
| Chris@17 | 662 | 
| Chris@17 | 663     } else { | 
| Chris@17 | 664 | 
| Chris@20 | 665 	// Zoom in or out | 
| Chris@20 | 666 | 
| Chris@17 | 667 	int newZoomLevel = m_zoomLevel; | 
| Chris@0 | 668 | 
| Chris@17 | 669 	while (count > 0) { | 
| Chris@17 | 670 	    if (newZoomLevel <= 2) { | 
| Chris@17 | 671 		newZoomLevel = 1; | 
| Chris@17 | 672 		break; | 
| Chris@17 | 673 	    } | 
| Chris@17 | 674 	    newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, | 
| Chris@17 | 675 						      ZoomConstraint::RoundDown); | 
| Chris@17 | 676 	    --count; | 
| Chris@0 | 677 	} | 
| Chris@17 | 678 | 
| Chris@17 | 679 	while (count < 0) { | 
| Chris@17 | 680 	    newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, | 
| Chris@17 | 681 						      ZoomConstraint::RoundUp); | 
| Chris@17 | 682 	    ++count; | 
| Chris@17 | 683 	} | 
| Chris@17 | 684 | 
| Chris@17 | 685 	if (newZoomLevel != m_zoomLevel) { | 
| Chris@17 | 686 	    setZoomLevel(newZoomLevel); | 
| Chris@17 | 687 	} | 
| Chris@0 | 688     } | 
| Chris@0 | 689 | 
| Chris@0 | 690     emit paneInteractedWith(); | 
| Chris@0 | 691 } | 
| Chris@8 | 692 | 
| Chris@13 | 693 void | 
| Chris@13 | 694 Pane::toolModeChanged() | 
| Chris@13 | 695 { | 
| Chris@13 | 696     ViewManager::ToolMode mode = m_manager->getToolMode(); | 
| Chris@13 | 697     std::cerr << "Pane::toolModeChanged(" << mode << ")" << std::endl; | 
| Chris@13 | 698 | 
| Chris@13 | 699     switch (mode) { | 
| Chris@13 | 700 | 
| Chris@13 | 701     case ViewManager::NavigateMode: | 
| Chris@13 | 702 	setCursor(Qt::PointingHandCursor); | 
| Chris@13 | 703 	break; | 
| Chris@13 | 704 | 
| Chris@13 | 705     case ViewManager::SelectMode: | 
| Chris@13 | 706 	setCursor(Qt::ArrowCursor); | 
| Chris@13 | 707 	break; | 
| Chris@13 | 708 | 
| Chris@13 | 709     case ViewManager::EditMode: | 
| Chris@19 | 710 	setCursor(Qt::UpArrowCursor); | 
| Chris@13 | 711 	break; | 
| Chris@13 | 712 | 
| Chris@13 | 713     case ViewManager::DrawMode: | 
| Chris@13 | 714 	setCursor(Qt::CrossCursor); | 
| Chris@13 | 715 	break; | 
| Chris@13 | 716 | 
| Chris@13 | 717     case ViewManager::TextMode: | 
| Chris@13 | 718 	setCursor(Qt::IBeamCursor); | 
| Chris@13 | 719 	break; | 
| Chris@13 | 720     } | 
| Chris@13 | 721 } | 
| Chris@13 | 722 | 
| Chris@8 | 723 QString | 
| Chris@8 | 724 Pane::toXmlString(QString indent, QString extraAttributes) const | 
| Chris@8 | 725 { | 
| Chris@8 | 726     return View::toXmlString | 
| Chris@8 | 727 	(indent, | 
| Chris@8 | 728 	 QString("type=\"pane\" centreLineVisible=\"%1\" %2") | 
| Chris@8 | 729 	 .arg(m_centreLineVisible).arg(extraAttributes)); | 
| Chris@8 | 730 } | 
| Chris@8 | 731 | 
| Chris@0 | 732 | 
| Chris@0 | 733 #ifdef INCLUDE_MOCFILES | 
| Chris@0 | 734 #include "Pane.moc.cpp" | 
| Chris@0 | 735 #endif | 
| Chris@0 | 736 |