comparison layer/TextLayer.cpp @ 44:ad214997dddb

* Refactor Layer classes so as no longer to store a single View pointer; instead they need to be able to draw themselves on any View on demand. Layers with caches (e.g. spectrogram) will need to be further refactored so as to maintain a per-View cache * Begin refactoring MainWindow by pulling out the document stuff (set of layers, models etc) into a Document class. Not yet in use. This revision is fairly unstable.
author Chris Cannam
date Thu, 02 Mar 2006 16:58:49 +0000
parents 78515b1e29eb
children 01ab51f72e84
comparison
equal deleted inserted replaced
43:78515b1e29eb 44:ad214997dddb
21 #include <QInputDialog> 21 #include <QInputDialog>
22 22
23 #include <iostream> 23 #include <iostream>
24 #include <cmath> 24 #include <cmath>
25 25
26 TextLayer::TextLayer(View *w) : 26 TextLayer::TextLayer() :
27 Layer(w), 27 Layer(),
28 m_model(0), 28 m_model(0),
29 m_editing(false), 29 m_editing(false),
30 m_originalPoint(0, 0.0, tr("Empty Label")), 30 m_originalPoint(0, 0.0, tr("Empty Label")),
31 m_editingPoint(0, 0.0, tr("Empty Label")), 31 m_editingPoint(0, 0.0, tr("Empty Label")),
32 m_editingCommand(0), 32 m_editingCommand(0),
33 m_colour(255, 150, 50) // orange 33 m_colour(255, 150, 50) // orange
34 { 34 {
35 m_view->addLayer(this); 35
36 } 36 }
37 37
38 void 38 void
39 TextLayer::setModel(TextModel *model) 39 TextLayer::setModel(TextModel *model)
40 { 40 {
136 m_colour = colour; 136 m_colour = colour;
137 emit layerParametersChanged(); 137 emit layerParametersChanged();
138 } 138 }
139 139
140 bool 140 bool
141 TextLayer::isLayerScrollable() const 141 TextLayer::isLayerScrollable(const View *v) const
142 { 142 {
143 QPoint discard; 143 QPoint discard;
144 return !m_view->shouldIlluminateLocalFeatures(this, discard); 144 return !v->shouldIlluminateLocalFeatures(this, discard);
145 } 145 }
146 146
147 147
148 TextModel::PointList 148 TextModel::PointList
149 TextLayer::getLocalPoints(int x, int y) const 149 TextLayer::getLocalPoints(View *v, int x, int y) const
150 { 150 {
151 if (!m_model) return TextModel::PointList(); 151 if (!m_model) return TextModel::PointList();
152 152
153 long frame0 = getFrameForX(-150); 153 long frame0 = v->getFrameForX(-150);
154 long frame1 = getFrameForX(m_view->width() + 150); 154 long frame1 = v->getFrameForX(v->width() + 150);
155 155
156 TextModel::PointList points(m_model->getPoints(frame0, frame1)); 156 TextModel::PointList points(m_model->getPoints(frame0, frame1));
157 157
158 TextModel::PointList rv; 158 TextModel::PointList rv;
159 QFontMetrics metrics = QPainter().fontMetrics(); 159 QFontMetrics metrics = QPainter().fontMetrics();
161 for (TextModel::PointList::iterator i = points.begin(); 161 for (TextModel::PointList::iterator i = points.begin();
162 i != points.end(); ++i) { 162 i != points.end(); ++i) {
163 163
164 const TextModel::Point &p(*i); 164 const TextModel::Point &p(*i);
165 165
166 int px = getXForFrame(p.frame); 166 int px = v->getXForFrame(p.frame);
167 int py = getYForHeight(p.height); 167 int py = getYForHeight(v, p.height);
168 168
169 QString label = p.label; 169 QString label = p.label;
170 if (label == "") { 170 if (label == "") {
171 label = tr("<no text>"); 171 label = tr("<no text>");
172 } 172 }
173 173
174 QRect rect = metrics.boundingRect 174 QRect rect = metrics.boundingRect
175 (QRect(0, 0, 150, 200), 175 (QRect(0, 0, 150, 200),
176 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); 176 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
177 177
178 if (py + rect.height() > m_view->height()) { 178 if (py + rect.height() > v->height()) {
179 if (rect.height() > m_view->height()) py = 0; 179 if (rect.height() > v->height()) py = 0;
180 else py = m_view->height() - rect.height() - 1; 180 else py = v->height() - rect.height() - 1;
181 } 181 }
182 182
183 if (x >= px && x < px + rect.width() && 183 if (x >= px && x < px + rect.width() &&
184 y >= py && y < py + rect.height()) { 184 y >= py && y < py + rect.height()) {
185 rv.insert(p); 185 rv.insert(p);
188 188
189 return rv; 189 return rv;
190 } 190 }
191 191
192 QString 192 QString
193 TextLayer::getFeatureDescription(QPoint &pos) const 193 TextLayer::getFeatureDescription(View *v, QPoint &pos) const
194 { 194 {
195 int x = pos.x(); 195 int x = pos.x();
196 196
197 if (!m_model || !m_model->getSampleRate()) return ""; 197 if (!m_model || !m_model->getSampleRate()) return "";
198 198
199 TextModel::PointList points = getLocalPoints(x, pos.y()); 199 TextModel::PointList points = getLocalPoints(v, x, pos.y());
200 200
201 if (points.empty()) { 201 if (points.empty()) {
202 if (!m_model->isReady()) { 202 if (!m_model->isReady()) {
203 return tr("In progress"); 203 return tr("In progress");
204 } else { 204 } else {
217 .arg(rt.toText(true).c_str()) 217 .arg(rt.toText(true).c_str())
218 .arg(points.begin()->height) 218 .arg(points.begin()->height)
219 .arg(points.begin()->label); 219 .arg(points.begin()->label);
220 } 220 }
221 221
222 pos = QPoint(getXForFrame(useFrame), getYForHeight(points.begin()->height)); 222 pos = QPoint(v->getXForFrame(useFrame),
223 getYForHeight(v, points.begin()->height));
223 return text; 224 return text;
224 } 225 }
225 226
226 227
227 //!!! too much overlap with TimeValueLayer/TimeInstantLayer 228 //!!! too much overlap with TimeValueLayer/TimeInstantLayer
228 229
229 bool 230 bool
230 TextLayer::snapToFeatureFrame(int &frame, 231 TextLayer::snapToFeatureFrame(View *v, int &frame,
231 size_t &resolution, 232 size_t &resolution,
232 SnapType snap) const 233 SnapType snap) const
233 { 234 {
234 if (!m_model) { 235 if (!m_model) {
235 return Layer::snapToFeatureFrame(frame, resolution, snap); 236 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
236 } 237 }
237 238
238 resolution = m_model->getResolution(); 239 resolution = m_model->getResolution();
239 TextModel::PointList points; 240 TextModel::PointList points;
240 241
241 if (snap == SnapNeighbouring) { 242 if (snap == SnapNeighbouring) {
242 243
243 points = getLocalPoints(getXForFrame(frame), -1); 244 points = getLocalPoints(v, v->getXForFrame(frame), -1);
244 if (points.empty()) return false; 245 if (points.empty()) return false;
245 frame = points.begin()->frame; 246 frame = points.begin()->frame;
246 return true; 247 return true;
247 } 248 }
248 249
297 frame = snapped; 298 frame = snapped;
298 return found; 299 return found;
299 } 300 }
300 301
301 int 302 int
302 TextLayer::getYForHeight(float height) const 303 TextLayer::getYForHeight(View *v, float height) const
303 { 304 {
304 int h = m_view->height(); 305 int h = v->height();
305 return h - int(height * h); 306 return h - int(height * h);
306 } 307 }
307 308
308 float 309 float
309 TextLayer::getHeightForY(int y) const 310 TextLayer::getHeightForY(View *v, int y) const
310 { 311 {
311 int h = m_view->height(); 312 int h = v->height();
312 return float(h - y) / h; 313 return float(h - y) / h;
313 } 314 }
314 315
315 void 316 void
316 TextLayer::paint(QPainter &paint, QRect rect) const 317 TextLayer::paint(View *v, QPainter &paint, QRect rect) const
317 { 318 {
318 if (!m_model || !m_model->isOK()) return; 319 if (!m_model || !m_model->isOK()) return;
319 320
320 int sampleRate = m_model->getSampleRate(); 321 int sampleRate = m_model->getSampleRate();
321 if (!sampleRate) return; 322 if (!sampleRate) return;
322 323
323 // Profiler profiler("TextLayer::paint", true); 324 // Profiler profiler("TextLayer::paint", true);
324 325
325 int x0 = rect.left(), x1 = rect.right(); 326 int x0 = rect.left(), x1 = rect.right();
326 long frame0 = getFrameForX(x0); 327 long frame0 = v->getFrameForX(x0);
327 long frame1 = getFrameForX(x1); 328 long frame1 = v->getFrameForX(x1);
328 329
329 TextModel::PointList points(m_model->getPoints(frame0, frame1)); 330 TextModel::PointList points(m_model->getPoints(frame0, frame1));
330 if (points.empty()) return; 331 if (points.empty()) return;
331 332
332 QColor brushColour(m_colour); 333 QColor brushColour(m_colour);
333 334
334 int h, s, v; 335 int h, s, val;
335 brushColour.getHsv(&h, &s, &v); 336 brushColour.getHsv(&h, &s, &val);
336 brushColour.setHsv(h, s, 255, 100); 337 brushColour.setHsv(h, s, 255, 100);
337 338
338 QColor penColour; 339 QColor penColour;
339 if (m_view->hasLightBackground()) { 340 if (v->hasLightBackground()) {
340 penColour = Qt::black; 341 penColour = Qt::black;
341 } else { 342 } else {
342 penColour = Qt::white; 343 penColour = Qt::white;
343 } 344 }
344 345
346 // << m_model->getResolution() << " frames" << std::endl; 347 // << m_model->getResolution() << " frames" << std::endl;
347 348
348 QPoint localPos; 349 QPoint localPos;
349 long illuminateFrame = -1; 350 long illuminateFrame = -1;
350 351
351 if (m_view->shouldIlluminateLocalFeatures(this, localPos)) { 352 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
352 TextModel::PointList localPoints = getLocalPoints(localPos.x(), 353 TextModel::PointList localPoints = getLocalPoints(v, localPos.x(),
353 localPos.y()); 354 localPos.y());
354 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; 355 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
355 } 356 }
356 357
357 int boxMaxWidth = 150; 358 int boxMaxWidth = 150;
358 int boxMaxHeight = 200; 359 int boxMaxHeight = 200;
359 360
360 paint.save(); 361 paint.save();
361 paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, m_view->height()); 362 paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->height());
362 363
363 for (TextModel::PointList::const_iterator i = points.begin(); 364 for (TextModel::PointList::const_iterator i = points.begin();
364 i != points.end(); ++i) { 365 i != points.end(); ++i) {
365 366
366 const TextModel::Point &p(*i); 367 const TextModel::Point &p(*i);
367 368
368 int x = getXForFrame(p.frame); 369 int x = v->getXForFrame(p.frame);
369 int y = getYForHeight(p.height); 370 int y = getYForHeight(v, p.height);
370 371
371 if (illuminateFrame == p.frame) { 372 if (illuminateFrame == p.frame) {
372 paint.setBrush(penColour); 373 paint.setBrush(penColour);
373 if (m_view->hasLightBackground()) { 374 if (v->hasLightBackground()) {
374 paint.setPen(Qt::white); 375 paint.setPen(Qt::white);
375 } else { 376 } else {
376 paint.setPen(Qt::black); 377 paint.setPen(Qt::black);
377 } 378 }
378 } else { 379 } else {
390 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); 391 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
391 392
392 QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height()); 393 QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height());
393 boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2); 394 boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2);
394 395
395 if (y + boxRect.height() > m_view->height()) { 396 if (y + boxRect.height() > v->height()) {
396 if (boxRect.height() > m_view->height()) y = 0; 397 if (boxRect.height() > v->height()) y = 0;
397 else y = m_view->height() - boxRect.height() - 1; 398 else y = v->height() - boxRect.height() - 1;
398 } 399 }
399 400
400 boxRect = QRect(x, y, boxRect.width(), boxRect.height()); 401 boxRect = QRect(x, y, boxRect.width(), boxRect.height());
401 textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); 402 textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height());
402 403
421 // looks like save/restore doesn't deal with this: 422 // looks like save/restore doesn't deal with this:
422 paint.setRenderHint(QPainter::Antialiasing, false); 423 paint.setRenderHint(QPainter::Antialiasing, false);
423 } 424 }
424 425
425 void 426 void
426 TextLayer::drawStart(QMouseEvent *e) 427 TextLayer::drawStart(View *v, QMouseEvent *e)
427 { 428 {
428 // std::cerr << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; 429 // std::cerr << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
429 430
430 if (!m_model) { 431 if (!m_model) {
431 std::cerr << "TextLayer::drawStart: no model" << std::endl; 432 std::cerr << "TextLayer::drawStart: no model" << std::endl;
432 return; 433 return;
433 } 434 }
434 435
435 long frame = getFrameForX(e->x()); 436 long frame = v->getFrameForX(e->x());
436 if (frame < 0) frame = 0; 437 if (frame < 0) frame = 0;
437 frame = frame / m_model->getResolution() * m_model->getResolution(); 438 frame = frame / m_model->getResolution() * m_model->getResolution();
438 439
439 float height = getHeightForY(e->y()); 440 float height = getHeightForY(v, e->y());
440 441
441 m_editingPoint = TextModel::Point(frame, height, ""); 442 m_editingPoint = TextModel::Point(frame, height, "");
442 m_originalPoint = m_editingPoint; 443 m_originalPoint = m_editingPoint;
443 444
444 if (m_editingCommand) m_editingCommand->finish(); 445 if (m_editingCommand) m_editingCommand->finish();
447 448
448 m_editing = true; 449 m_editing = true;
449 } 450 }
450 451
451 void 452 void
452 TextLayer::drawDrag(QMouseEvent *e) 453 TextLayer::drawDrag(View *v, QMouseEvent *e)
453 { 454 {
454 // std::cerr << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; 455 // std::cerr << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
455 456
456 if (!m_model || !m_editing) return; 457 if (!m_model || !m_editing) return;
457 458
458 long frame = getFrameForX(e->x()); 459 long frame = v->getFrameForX(e->x());
459 if (frame < 0) frame = 0; 460 if (frame < 0) frame = 0;
460 frame = frame / m_model->getResolution() * m_model->getResolution(); 461 frame = frame / m_model->getResolution() * m_model->getResolution();
461 462
462 float height = getHeightForY(e->y()); 463 float height = getHeightForY(v, e->y());
463 464
464 m_editingCommand->deletePoint(m_editingPoint); 465 m_editingCommand->deletePoint(m_editingPoint);
465 m_editingPoint.frame = frame; 466 m_editingPoint.frame = frame;
466 m_editingPoint.height = height; 467 m_editingPoint.height = height;
467 m_editingCommand->addPoint(m_editingPoint); 468 m_editingCommand->addPoint(m_editingPoint);
468 } 469 }
469 470
470 void 471 void
471 TextLayer::drawEnd(QMouseEvent *e) 472 TextLayer::drawEnd(View *v, QMouseEvent *e)
472 { 473 {
473 // std::cerr << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; 474 // std::cerr << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
474 if (!m_model || !m_editing) return; 475 if (!m_model || !m_editing) return;
475 476
476 bool ok = false; 477 bool ok = false;
477 QString label = QInputDialog::getText(m_view, tr("Enter label"), 478 QString label = QInputDialog::getText(v, tr("Enter label"),
478 tr("Please enter a new label:"), 479 tr("Please enter a new label:"),
479 QLineEdit::Normal, "", &ok); 480 QLineEdit::Normal, "", &ok);
480 481
481 if (ok) { 482 if (ok) {
482 TextModel::RelabelCommand *command = 483 TextModel::RelabelCommand *command =
488 m_editingCommand = 0; 489 m_editingCommand = 0;
489 m_editing = false; 490 m_editing = false;
490 } 491 }
491 492
492 void 493 void
493 TextLayer::editStart(QMouseEvent *e) 494 TextLayer::editStart(View *v, QMouseEvent *e)
494 { 495 {
495 // std::cerr << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; 496 // std::cerr << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
496 497
497 if (!m_model) return; 498 if (!m_model) return;
498 499
499 TextModel::PointList points = getLocalPoints(e->x(), e->y()); 500 TextModel::PointList points = getLocalPoints(v, e->x(), e->y());
500 if (points.empty()) return; 501 if (points.empty()) return;
501 502
502 m_editOrigin = e->pos(); 503 m_editOrigin = e->pos();
503 m_editingPoint = *points.begin(); 504 m_editingPoint = *points.begin();
504 m_originalPoint = m_editingPoint; 505 m_originalPoint = m_editingPoint;
510 511
511 m_editing = true; 512 m_editing = true;
512 } 513 }
513 514
514 void 515 void
515 TextLayer::editDrag(QMouseEvent *e) 516 TextLayer::editDrag(View *v, QMouseEvent *e)
516 { 517 {
517 if (!m_model || !m_editing) return; 518 if (!m_model || !m_editing) return;
518 519
519 long frameDiff = getFrameForX(e->x()) - getFrameForX(m_editOrigin.x()); 520 long frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x());
520 float heightDiff = getHeightForY(e->y()) - getHeightForY(m_editOrigin.y()); 521 float heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y());
521 522
522 long frame = m_originalPoint.frame + frameDiff; 523 long frame = m_originalPoint.frame + frameDiff;
523 float height = m_originalPoint.height + heightDiff; 524 float height = m_originalPoint.height + heightDiff;
524 525
525 // long frame = getFrameForX(e->x()); 526 // long frame = v->getFrameForX(e->x());
526 if (frame < 0) frame = 0; 527 if (frame < 0) frame = 0;
527 frame = (frame / m_model->getResolution()) * m_model->getResolution(); 528 frame = (frame / m_model->getResolution()) * m_model->getResolution();
528 529
529 // float height = getHeightForY(e->y()); 530 // float height = getHeightForY(v, e->y());
530 531
531 if (!m_editingCommand) { 532 if (!m_editingCommand) {
532 m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); 533 m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label"));
533 } 534 }
534 535
537 m_editingPoint.height = height; 538 m_editingPoint.height = height;
538 m_editingCommand->addPoint(m_editingPoint); 539 m_editingCommand->addPoint(m_editingPoint);
539 } 540 }
540 541
541 void 542 void
542 TextLayer::editEnd(QMouseEvent *e) 543 TextLayer::editEnd(View *v, QMouseEvent *e)
543 { 544 {
544 // std::cerr << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; 545 // std::cerr << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
545 if (!m_model || !m_editing) return; 546 if (!m_model || !m_editing) return;
546 547
547 if (m_editingCommand) { 548 if (m_editingCommand) {
565 m_editingCommand = 0; 566 m_editingCommand = 0;
566 m_editing = false; 567 m_editing = false;
567 } 568 }
568 569
569 void 570 void
570 TextLayer::editOpen(QMouseEvent *e) 571 TextLayer::editOpen(View *v, QMouseEvent *e)
571 { 572 {
572 std::cerr << "TextLayer::editOpen" << std::endl; 573 std::cerr << "TextLayer::editOpen" << std::endl;
573 574
574 if (!m_model) return; 575 if (!m_model) return;
575 576
576 TextModel::PointList points = getLocalPoints(e->x(), e->y()); 577 TextModel::PointList points = getLocalPoints(v, e->x(), e->y());
577 if (points.empty()) return; 578 if (points.empty()) return;
578 579
579 QString label = points.begin()->label; 580 QString label = points.begin()->label;
580 581
581 bool ok = false; 582 bool ok = false;
582 label = QInputDialog::getText(m_view, tr("Enter label"), 583 label = QInputDialog::getText(v, tr("Enter label"),
583 tr("Please enter a new label:"), 584 tr("Please enter a new label:"),
584 QLineEdit::Normal, label, &ok); 585 QLineEdit::Normal, label, &ok);
585 if (ok && label != points.begin()->label) { 586 if (ok && label != points.begin()->label) {
586 TextModel::RelabelCommand *command = 587 TextModel::RelabelCommand *command =
587 new TextModel::RelabelCommand(m_model, *points.begin(), label); 588 new TextModel::RelabelCommand(m_model, *points.begin(), label);