comparison layer/TextLayer.cpp @ 1459:42c87368287c

Merge from branch single-point
author Chris Cannam
date Fri, 17 May 2019 10:02:52 +0100
parents e2b6a13a1f69
children 696e569ff21b
comparison
equal deleted inserted replaced
1441:8d5bf4ab98ef 1459:42c87368287c
106 { 106 {
107 QPoint discard; 107 QPoint discard;
108 return !v->shouldIlluminateLocalFeatures(this, discard); 108 return !v->shouldIlluminateLocalFeatures(this, discard);
109 } 109 }
110 110
111 111 EventVector
112 TextModel::PointList
113 TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const 112 TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const
114 { 113 {
115 if (!m_model) return TextModel::PointList(); 114 if (!m_model) return {};
116 115
117 sv_frame_t frame0 = v->getFrameForX(-150); 116 int overlap = ViewManager::scalePixelSize(150);
118 sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + 150); 117
119 118 sv_frame_t frame0 = v->getFrameForX(-overlap);
120 TextModel::PointList points(m_model->getPoints(frame0, frame1)); 119 sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + overlap);
121 120
122 TextModel::PointList rv; 121 EventVector points(m_model->getEventsSpanning(frame0, frame1 - frame0));
122
123 EventVector rv;
123 QFontMetrics metrics = QFontMetrics(QFont()); 124 QFontMetrics metrics = QFontMetrics(QFont());
124 125
125 for (TextModel::PointList::iterator i = points.begin(); 126 for (EventVector::iterator i = points.begin(); i != points.end(); ++i) {
126 i != points.end(); ++i) { 127
127 128 Event p(*i);
128 const TextModel::Point &p(*i); 129
129 130 int px = v->getXForFrame(p.getFrame());
130 int px = v->getXForFrame(p.frame); 131 int py = getYForHeight(v, p.getValue());
131 int py = getYForHeight(v, p.height); 132
132 133 QString label = p.getLabel();
133 QString label = p.label;
134 if (label == "") { 134 if (label == "") {
135 label = tr("<no text>"); 135 label = tr("<no text>");
136 } 136 }
137 137
138 QRect rect = metrics.boundingRect 138 QRect rect = metrics.boundingRect
144 else py = v->getPaintHeight() - rect.height() - 1; 144 else py = v->getPaintHeight() - rect.height() - 1;
145 } 145 }
146 146
147 if (x >= px && x < px + rect.width() && 147 if (x >= px && x < px + rect.width() &&
148 y >= py && y < py + rect.height()) { 148 y >= py && y < py + rect.height()) {
149 rv.insert(p); 149 rv.push_back(p);
150 } 150 }
151 } 151 }
152 152
153 return rv; 153 return rv;
154 } 154 }
155 155
156 bool 156 bool
157 TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &p) const 157 TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &p) const
158 { 158 {
159 if (!m_model) return false; 159 if (!m_model) return false;
160 160
161 sv_frame_t a = v->getFrameForX(x - 120); 161 sv_frame_t a = v->getFrameForX(x - ViewManager::scalePixelSize(120));
162 sv_frame_t b = v->getFrameForX(x + 10); 162 sv_frame_t b = v->getFrameForX(x + ViewManager::scalePixelSize(10));
163 TextModel::PointList onPoints = m_model->getPoints(a, b); 163 EventVector onPoints = m_model->getEventsWithin(a, b);
164 if (onPoints.empty()) return false; 164 if (onPoints.empty()) return false;
165 165
166 double nearestDistance = -1; 166 double nearestDistance = -1;
167 167
168 for (TextModel::PointList::const_iterator i = onPoints.begin(); 168 for (EventVector::const_iterator i = onPoints.begin();
169 i != onPoints.end(); ++i) { 169 i != onPoints.end(); ++i) {
170 170
171 double yd = getYForHeight(v, (*i).height) - y; 171 double yd = getYForHeight(v, i->getValue()) - y;
172 double xd = v->getXForFrame((*i).frame) - x; 172 double xd = v->getXForFrame(i->getFrame()) - x;
173 double distance = sqrt(yd*yd + xd*xd); 173 double distance = sqrt(yd*yd + xd*xd);
174 174
175 if (nearestDistance == -1 || distance < nearestDistance) { 175 if (nearestDistance == -1 || distance < nearestDistance) {
176 nearestDistance = distance; 176 nearestDistance = distance;
177 p = *i; 177 p = *i;
186 { 186 {
187 int x = pos.x(); 187 int x = pos.x();
188 188
189 if (!m_model || !m_model->getSampleRate()) return ""; 189 if (!m_model || !m_model->getSampleRate()) return "";
190 190
191 TextModel::PointList points = getLocalPoints(v, x, pos.y()); 191 EventVector points = getLocalPoints(v, x, pos.y());
192 192
193 if (points.empty()) { 193 if (points.empty()) {
194 if (!m_model->isReady()) { 194 if (!m_model->isReady()) {
195 return tr("In progress"); 195 return tr("In progress");
196 } else { 196 } else {
197 return ""; 197 return "";
198 } 198 }
199 } 199 }
200 200
201 sv_frame_t useFrame = points.begin()->frame; 201 sv_frame_t useFrame = points.begin()->getFrame();
202 202
203 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); 203 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
204 204
205 QString text; 205 QString text;
206 206
207 if (points.begin()->label == "") { 207 if (points.begin()->getLabel() == "") {
208 text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3")) 208 text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3"))
209 .arg(rt.toText(true).c_str()) 209 .arg(rt.toText(true).c_str())
210 .arg(points.begin()->height) 210 .arg(points.begin()->getValue())
211 .arg(points.begin()->label); 211 .arg(points.begin()->getLabel());
212 } 212 }
213 213
214 pos = QPoint(v->getXForFrame(useFrame), 214 pos = QPoint(v->getXForFrame(useFrame),
215 getYForHeight(v, points.begin()->height)); 215 getYForHeight(v, points.begin()->getValue()));
216 return text; 216 return text;
217 } 217 }
218 218
219 219
220 //!!! too much overlap with TimeValueLayer/TimeInstantLayer 220 //!!! too much overlap with TimeValueLayer/TimeInstantLayer
226 { 226 {
227 if (!m_model) { 227 if (!m_model) {
228 return Layer::snapToFeatureFrame(v, frame, resolution, snap); 228 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
229 } 229 }
230 230
231 // SnapLeft / SnapRight: return frame of nearest feature in that
232 // direction no matter how far away
233 //
234 // SnapNeighbouring: return frame of feature that would be used in
235 // an editing operation, i.e. closest feature in either direction
236 // but only if it is "close enough"
237
231 resolution = m_model->getResolution(); 238 resolution = m_model->getResolution();
232 TextModel::PointList points;
233 239
234 if (snap == SnapNeighbouring) { 240 if (snap == SnapNeighbouring) {
235 241 EventVector points = getLocalPoints(v, v->getXForFrame(frame), -1);
236 points = getLocalPoints(v, v->getXForFrame(frame), -1);
237 if (points.empty()) return false; 242 if (points.empty()) return false;
238 frame = points.begin()->frame; 243 frame = points.begin()->getFrame();
239 return true; 244 return true;
240 } 245 }
241 246
242 points = m_model->getPoints(frame, frame); 247 Event e;
243 sv_frame_t snapped = frame; 248 if (m_model->getNearestEventMatching
244 bool found = false; 249 (frame,
245 250 [](Event) { return true; },
246 for (TextModel::PointList::const_iterator i = points.begin(); 251 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
247 i != points.end(); ++i) { 252 e)) {
248 253 frame = e.getFrame();
249 if (snap == SnapRight) { 254 return true;
250 255 }
251 if (i->frame > frame) { 256
252 snapped = i->frame; 257 return false;
253 found = true;
254 break;
255 }
256
257 } else if (snap == SnapLeft) {
258
259 if (i->frame <= frame) {
260 snapped = i->frame;
261 found = true; // don't break, as the next may be better
262 } else {
263 break;
264 }
265
266 } else { // nearest
267
268 TextModel::PointList::const_iterator j = i;
269 ++j;
270
271 if (j == points.end()) {
272
273 snapped = i->frame;
274 found = true;
275 break;
276
277 } else if (j->frame >= frame) {
278
279 if (j->frame - frame < frame - i->frame) {
280 snapped = j->frame;
281 } else {
282 snapped = i->frame;
283 }
284 found = true;
285 break;
286 }
287 }
288 }
289
290 frame = snapped;
291 return found;
292 } 258 }
293 259
294 int 260 int
295 TextLayer::getYForHeight(LayerGeometryProvider *v, double height) const 261 TextLayer::getYForHeight(LayerGeometryProvider *v, double height) const
296 { 262 {
314 if (!sampleRate) return; 280 if (!sampleRate) return;
315 281
316 // Profiler profiler("TextLayer::paint", true); 282 // Profiler profiler("TextLayer::paint", true);
317 283
318 int x0 = rect.left(), x1 = rect.right(); 284 int x0 = rect.left(), x1 = rect.right();
319 sv_frame_t frame0 = v->getFrameForX(x0); 285 int overlap = ViewManager::scalePixelSize(150);
320 sv_frame_t frame1 = v->getFrameForX(x1); 286 sv_frame_t frame0 = v->getFrameForX(x0 - overlap);
321 287 sv_frame_t frame1 = v->getFrameForX(x1 + overlap);
322 TextModel::PointList points(m_model->getPoints(frame0, frame1)); 288
289 EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0, 2));
323 if (points.empty()) return; 290 if (points.empty()) return;
324 291
325 QColor brushColour(getBaseQColor()); 292 QColor brushColour(getBaseQColor());
326 293
327 int h, s, val; 294 int h, s, val;
333 300
334 // SVDEBUG << "TextLayer::paint: resolution is " 301 // SVDEBUG << "TextLayer::paint: resolution is "
335 // << m_model->getResolution() << " frames" << endl; 302 // << m_model->getResolution() << " frames" << endl;
336 303
337 QPoint localPos; 304 QPoint localPos;
338 TextModel::Point illuminatePoint(0); 305 Event illuminatePoint(0);
339 bool shouldIlluminate = false; 306 bool shouldIlluminate = false;
340 307
341 if (v->shouldIlluminateLocalFeatures(this, localPos)) { 308 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
342 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), 309 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
343 illuminatePoint); 310 illuminatePoint);
347 int boxMaxHeight = 200; 314 int boxMaxHeight = 200;
348 315
349 paint.save(); 316 paint.save();
350 paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight()); 317 paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight());
351 318
352 for (TextModel::PointList::const_iterator i = points.begin(); 319 for (EventVector::const_iterator i = points.begin();
353 i != points.end(); ++i) { 320 i != points.end(); ++i) {
354 321
355 const TextModel::Point &p(*i); 322 Event p(*i);
356 323
357 int x = v->getXForFrame(p.frame); 324 int x = v->getXForFrame(p.getFrame());
358 int y = getYForHeight(v, p.height); 325 int y = getYForHeight(v, p.getValue());
359 326
360 if (!shouldIlluminate || 327 if (!shouldIlluminate || illuminatePoint != p) {
361 // "illuminatePoint != p"
362 TextModel::Point::Comparator()(illuminatePoint, p) ||
363 TextModel::Point::Comparator()(p, illuminatePoint)) {
364 paint.setPen(penColour); 328 paint.setPen(penColour);
365 paint.setBrush(brushColour); 329 paint.setBrush(brushColour);
366 } else { 330 } else {
367 paint.setBrush(penColour); 331 paint.setBrush(penColour);
368 paint.setPen(v->getBackground()); 332 paint.setPen(v->getBackground());
369 } 333 }
370 334
371 QString label = p.label; 335 QString label = p.getLabel();
372 if (label == "") { 336 if (label == "") {
373 label = tr("<no text>"); 337 label = tr("<no text>");
374 } 338 }
375 339
376 QRect boxRect = paint.fontMetrics().boundingRect 340 QRect boxRect = paint.fontMetrics().boundingRect
397 paint.setRenderHint(QPainter::Antialiasing, true); 361 paint.setRenderHint(QPainter::Antialiasing, true);
398 paint.drawText(textRect, 362 paint.drawText(textRect,
399 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, 363 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
400 label); 364 label);
401 365
402 /// if (p.label != "") { 366 /// if (p.getLabel() != "") {
403 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); 367 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.getLabel());
404 /// } 368 /// }
405 } 369 }
406 370
407 paint.restore(); 371 paint.restore();
408 372
424 if (frame < 0) frame = 0; 388 if (frame < 0) frame = 0;
425 frame = frame / m_model->getResolution() * m_model->getResolution(); 389 frame = frame / m_model->getResolution() * m_model->getResolution();
426 390
427 double height = getHeightForY(v, e->y()); 391 double height = getHeightForY(v, e->y());
428 392
429 m_editingPoint = TextModel::Point(frame, float(height), ""); 393 m_editingPoint = Event(frame, float(height), "");
430 m_originalPoint = m_editingPoint; 394 m_originalPoint = m_editingPoint;
431 395
432 if (m_editingCommand) finish(m_editingCommand); 396 if (m_editingCommand) finish(m_editingCommand);
433 m_editingCommand = new TextModel::EditCommand(m_model, "Add Label"); 397 m_editingCommand = new ChangeEventsCommand(m_model, "Add Label");
434 m_editingCommand->addPoint(m_editingPoint); 398 m_editingCommand->add(m_editingPoint);
435 399
436 m_editing = true; 400 m_editing = true;
437 } 401 }
438 402
439 void 403 void
447 if (frame < 0) frame = 0; 411 if (frame < 0) frame = 0;
448 frame = frame / m_model->getResolution() * m_model->getResolution(); 412 frame = frame / m_model->getResolution() * m_model->getResolution();
449 413
450 double height = getHeightForY(v, e->y()); 414 double height = getHeightForY(v, e->y());
451 415
452 m_editingCommand->deletePoint(m_editingPoint); 416 m_editingCommand->remove(m_editingPoint);
453 m_editingPoint.frame = frame; 417 m_editingPoint = m_editingPoint
454 m_editingPoint.height = float(height); 418 .withFrame(frame)
455 m_editingCommand->addPoint(m_editingPoint); 419 .withValue(float(height));
420 m_editingCommand->add(m_editingPoint);
456 } 421 }
457 422
458 void 423 void
459 TextLayer::drawEnd(LayerGeometryProvider *v, QMouseEvent *) 424 TextLayer::drawEnd(LayerGeometryProvider *v, QMouseEvent *)
460 { 425 {
464 bool ok = false; 429 bool ok = false;
465 QString label = QInputDialog::getText(v->getView(), tr("Enter label"), 430 QString label = QInputDialog::getText(v->getView(), tr("Enter label"),
466 tr("Please enter a new label:"), 431 tr("Please enter a new label:"),
467 QLineEdit::Normal, "", &ok); 432 QLineEdit::Normal, "", &ok);
468 433
434 m_editingCommand->remove(m_editingPoint);
435
469 if (ok) { 436 if (ok) {
470 TextModel::RelabelCommand *command = 437 m_editingPoint = m_editingPoint
471 new TextModel::RelabelCommand(m_model, m_editingPoint, label); 438 .withLabel(label);
472 m_editingCommand->addCommand(command); 439 m_editingCommand->add(m_editingPoint);
473 } else {
474 m_editingCommand->deletePoint(m_editingPoint);
475 } 440 }
476 441
477 finish(m_editingCommand); 442 finish(m_editingCommand);
478 m_editingCommand = nullptr; 443 m_editingCommand = nullptr;
479 m_editing = false; 444 m_editing = false;
504 { 469 {
505 if (!m_model || !m_editing) return; 470 if (!m_model || !m_editing) return;
506 471
507 m_editing = false; 472 m_editing = false;
508 473
509 TextModel::Point p(0); 474 Event p;
510 if (!getPointToDrag(v, e->x(), e->y(), p)) return; 475 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
511 if (p.frame != m_editingPoint.frame || p.height != m_editingPoint.height) return; 476 if (p.getFrame() != m_editingPoint.getFrame() ||
512 477 p.getValue() != m_editingPoint.getValue()) return;
513 m_editingCommand = new TextModel::EditCommand 478
514 (m_model, tr("Erase Point")); 479 m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point"));
515 480 m_editingCommand->remove(m_editingPoint);
516 m_editingCommand->deletePoint(m_editingPoint);
517
518 finish(m_editingCommand); 481 finish(m_editingCommand);
519 m_editingCommand = nullptr; 482 m_editingCommand = nullptr;
520 m_editing = false; 483 m_editing = false;
521 } 484 }
522 485
545 void 508 void
546 TextLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) 509 TextLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
547 { 510 {
548 if (!m_model || !m_editing) return; 511 if (!m_model || !m_editing) return;
549 512
550 sv_frame_t frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); 513 sv_frame_t frameDiff =
551 double heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); 514 v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x());
552 515 double heightDiff =
553 sv_frame_t frame = m_originalPoint.frame + frameDiff; 516 getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y());
554 double height = m_originalPoint.height + heightDiff; 517
555 518 sv_frame_t frame = m_originalPoint.getFrame() + frameDiff;
556 // sv_frame_t frame = v->getFrameForX(e->x()); 519 double height = m_originalPoint.getValue() + heightDiff;
520
557 if (frame < 0) frame = 0; 521 if (frame < 0) frame = 0;
558 frame = (frame / m_model->getResolution()) * m_model->getResolution(); 522 frame = (frame / m_model->getResolution()) * m_model->getResolution();
559 523
560 // double height = getHeightForY(v, e->y());
561
562 if (!m_editingCommand) { 524 if (!m_editingCommand) {
563 m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); 525 m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Label"));
564 } 526 }
565 527
566 m_editingCommand->deletePoint(m_editingPoint); 528 m_editingCommand->remove(m_editingPoint);
567 m_editingPoint.frame = frame; 529 m_editingPoint = m_editingPoint
568 m_editingPoint.height = float(height); 530 .withFrame(frame)
569 m_editingCommand->addPoint(m_editingPoint); 531 .withValue(float(height));
532 m_editingCommand->add(m_editingPoint);
570 } 533 }
571 534
572 void 535 void
573 TextLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) 536 TextLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
574 { 537 {
577 540
578 if (m_editingCommand) { 541 if (m_editingCommand) {
579 542
580 QString newName = m_editingCommand->getName(); 543 QString newName = m_editingCommand->getName();
581 544
582 if (m_editingPoint.frame != m_originalPoint.frame) { 545 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
583 if (m_editingPoint.height != m_originalPoint.height) { 546 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
584 newName = tr("Move Label"); 547 newName = tr("Move Label");
585 } else { 548 } else {
586 newName = tr("Move Label Horizontally"); 549 newName = tr("Move Label Horizontally");
587 } 550 }
588 } else { 551 } else {
600 bool 563 bool
601 TextLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) 564 TextLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
602 { 565 {
603 if (!m_model) return false; 566 if (!m_model) return false;
604 567
605 TextModel::Point text(0); 568 Event text;
606 if (!getPointToDrag(v, e->x(), e->y(), text)) return false; 569 if (!getPointToDrag(v, e->x(), e->y(), text)) return false;
607 570
608 QString label = text.label; 571 QString label = text.getLabel();
609 572
610 bool ok = false; 573 bool ok = false;
611 label = QInputDialog::getText(v->getView(), tr("Enter label"), 574 label = QInputDialog::getText(v->getView(), tr("Enter label"),
612 tr("Please enter a new label:"), 575 tr("Please enter a new label:"),
613 QLineEdit::Normal, label, &ok); 576 QLineEdit::Normal, label, &ok);
614 if (ok && label != text.label) { 577 if (ok && label != text.getLabel()) {
615 TextModel::RelabelCommand *command = 578 ChangeEventsCommand *command =
616 new TextModel::RelabelCommand(m_model, text, label); 579 new ChangeEventsCommand(m_model, tr("Re-Label Point"));
617 CommandHistory::getInstance()->addCommand(command); 580 command->remove(text);
581 command->add(text.withLabel(label));
582 finish(command);
618 } 583 }
619 584
620 return true; 585 return true;
621 } 586 }
622 587
623 void 588 void
624 TextLayer::moveSelection(Selection s, sv_frame_t newStartFrame) 589 TextLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
625 { 590 {
626 if (!m_model) return; 591 if (!m_model) return;
627 592
628 TextModel::EditCommand *command = 593 ChangeEventsCommand *command =
629 new TextModel::EditCommand(m_model, tr("Drag Selection")); 594 new ChangeEventsCommand(m_model, tr("Drag Selection"));
630 595
631 TextModel::PointList points = 596 EventVector points =
632 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 597 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
633 598
634 for (TextModel::PointList::iterator i = points.begin(); 599 for (Event p: points) {
635 i != points.end(); ++i) { 600 command->remove(p);
636 601 Event moved = p.withFrame(p.getFrame() +
637 if (s.contains(i->frame)) { 602 newStartFrame - s.getStartFrame());
638 TextModel::Point newPoint(*i); 603 command->add(moved);
639 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
640 command->deletePoint(*i);
641 command->addPoint(newPoint);
642 }
643 } 604 }
644 605
645 finish(command); 606 finish(command);
646 } 607 }
647 608
648 void 609 void
649 TextLayer::resizeSelection(Selection s, Selection newSize) 610 TextLayer::resizeSelection(Selection s, Selection newSize)
650 { 611 {
651 if (!m_model) return; 612 if (!m_model) return;
652 613
653 TextModel::EditCommand *command = 614 ChangeEventsCommand *command =
654 new TextModel::EditCommand(m_model, tr("Resize Selection")); 615 new ChangeEventsCommand(m_model, tr("Resize Selection"));
655 616
656 TextModel::PointList points = 617 EventVector points =
657 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 618 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
658 619
659 double ratio = 620 double ratio = double(newSize.getDuration()) / double(s.getDuration());
660 double(newSize.getEndFrame() - newSize.getStartFrame()) / 621 double oldStart = double(s.getStartFrame());
661 double(s.getEndFrame() - s.getStartFrame()); 622 double newStart = double(newSize.getStartFrame());
662 623
663 for (TextModel::PointList::iterator i = points.begin(); 624 for (Event p: points) {
664 i != points.end(); ++i) { 625
665 626 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
666 if (s.contains(i->frame)) { 627
667 628 Event newPoint = p
668 double target = double(i->frame); 629 .withFrame(lrint(newFrame));
669 target = double(newSize.getStartFrame()) + 630 command->remove(p);
670 target - double(s.getStartFrame()) * ratio; 631 command->add(newPoint);
671
672 TextModel::Point newPoint(*i);
673 newPoint.frame = lrint(target);
674 command->deletePoint(*i);
675 command->addPoint(newPoint);
676 }
677 } 632 }
678 633
679 finish(command); 634 finish(command);
680 } 635 }
681 636
682 void 637 void
683 TextLayer::deleteSelection(Selection s) 638 TextLayer::deleteSelection(Selection s)
684 { 639 {
685 if (!m_model) return; 640 if (!m_model) return;
686 641
687 TextModel::EditCommand *command = 642 ChangeEventsCommand *command =
688 new TextModel::EditCommand(m_model, tr("Delete Selection")); 643 new ChangeEventsCommand(m_model, tr("Delete Selection"));
689 644
690 TextModel::PointList points = 645 EventVector points =
691 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 646 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
692 647
693 for (TextModel::PointList::iterator i = points.begin(); 648 for (Event p: points) {
694 i != points.end(); ++i) { 649 command->remove(p);
695 if (s.contains(i->frame)) command->deletePoint(*i);
696 } 650 }
697 651
698 finish(command); 652 finish(command);
699 } 653 }
700 654
701 void 655 void
702 TextLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) 656 TextLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
703 { 657 {
704 if (!m_model) return; 658 if (!m_model) return;
705 659
706 TextModel::PointList points = 660 EventVector points =
707 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 661 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
708 662
709 for (TextModel::PointList::iterator i = points.begin(); 663 for (Event p: points) {
710 i != points.end(); ++i) { 664 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
711 if (s.contains(i->frame)) {
712 Clipboard::Point point(i->frame, i->height, i->label);
713 point.setReferenceFrame(alignToReference(v, i->frame));
714 to.addPoint(point);
715 }
716 } 665 }
717 } 666 }
718 667
719 bool 668 bool
720 TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */) 669 TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
721 { 670 {
722 if (!m_model) return false; 671 if (!m_model) return false;
723 672
724 const Clipboard::PointList &points = from.getPoints(); 673 const EventVector &points = from.getPoints();
725 674
726 bool realign = false; 675 bool realign = false;
727 676
728 if (clipboardHasDifferentAlignment(v, from)) { 677 if (clipboardHasDifferentAlignment(v, from)) {
729 678
740 if (button == QMessageBox::Yes) { 689 if (button == QMessageBox::Yes) {
741 realign = true; 690 realign = true;
742 } 691 }
743 } 692 }
744 693
745 TextModel::EditCommand *command = 694 ChangeEventsCommand *command =
746 new TextModel::EditCommand(m_model, tr("Paste")); 695 new ChangeEventsCommand(m_model, tr("Paste"));
747 696
748 double valueMin = 0.0, valueMax = 1.0; 697 double valueMin = 0.0, valueMax = 1.0;
749 for (Clipboard::PointList::const_iterator i = points.begin(); 698 for (EventVector::const_iterator i = points.begin();
750 i != points.end(); ++i) { 699 i != points.end(); ++i) {
751 if (i->haveValue()) { 700 if (i->hasValue()) {
752 if (i->getValue() < valueMin) valueMin = i->getValue(); 701 if (i->getValue() < valueMin) valueMin = i->getValue();
753 if (i->getValue() > valueMax) valueMax = i->getValue(); 702 if (i->getValue() > valueMax) valueMax = i->getValue();
754 } 703 }
755 } 704 }
756 if (valueMax < valueMin + 1.0) valueMax = valueMin + 1.0; 705 if (valueMax < valueMin + 1.0) valueMax = valueMin + 1.0;
757 706
758 for (Clipboard::PointList::const_iterator i = points.begin(); 707 for (EventVector::const_iterator i = points.begin();
759 i != points.end(); ++i) { 708 i != points.end(); ++i) {
760 709
761 if (!i->haveFrame()) continue;
762 sv_frame_t frame = 0; 710 sv_frame_t frame = 0;
763 711
764 if (!realign) { 712 if (!realign) {
765 713
766 frame = i->getFrame(); 714 frame = i->getFrame();
767 715
768 } else { 716 } else {
769 717
770 if (i->haveReferenceFrame()) { 718 if (i->hasReferenceFrame()) {
771 frame = i->getReferenceFrame(); 719 frame = i->getReferenceFrame();
772 frame = alignFromReference(v, frame); 720 frame = alignFromReference(v, frame);
773 } else { 721 } else {
774 frame = i->getFrame(); 722 frame = i->getFrame();
775 } 723 }
776 } 724 }
777 725
778 TextModel::Point newPoint(frame); 726 Event p = *i;
779 727 Event newPoint = p;
780 if (i->haveValue()) { 728 if (p.hasValue()) {
781 newPoint.height = float((i->getValue() - valueMin) / (valueMax - valueMin)); 729 newPoint = newPoint.withValue(float((i->getValue() - valueMin) /
730 (valueMax - valueMin)));
782 } else { 731 } else {
783 newPoint.height = 0.5f; 732 newPoint = newPoint.withValue(0.5f);
784 } 733 }
785 734
786 if (i->haveLabel()) { 735 if (!p.hasLabel()) {
787 newPoint.label = i->getLabel(); 736 if (p.hasValue()) {
788 } else if (i->haveValue()) { 737 newPoint = newPoint.withLabel(QString("%1").arg(p.getValue()));
789 newPoint.label = QString("%1").arg(i->getValue()); 738 } else {
790 } else { 739 newPoint = newPoint.withLabel(tr("New Point"));
791 newPoint.label = tr("New Point"); 740 }
792 } 741 }
793 742
794 command->addPoint(newPoint); 743 command->add(newPoint);
795 } 744 }
796 745
797 finish(command); 746 finish(command);
798 return true; 747 return true;
799 } 748 }