Mercurial > hg > easaier-soundaccess
comparison layer/TextLayer.cpp @ 0:fc9323a41f5a
start base : Sonic Visualiser sv1-1.0rc1
author | lbajardsilogic |
---|---|
date | Fri, 11 May 2007 09:08:14 +0000 |
parents | |
children | d8e6709e9075 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fc9323a41f5a |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 This file copyright 2006 Chris Cannam. | |
8 | |
9 This program is free software; you can redistribute it and/or | |
10 modify it under the terms of the GNU General Public License as | |
11 published by the Free Software Foundation; either version 2 of the | |
12 License, or (at your option) any later version. See the file | |
13 COPYING included with this distribution for more information. | |
14 */ | |
15 | |
16 #include "TextLayer.h" | |
17 | |
18 #include "data/model/Model.h" | |
19 #include "base/RealTime.h" | |
20 #include "base/Profiler.h" | |
21 #include "view/View.h" | |
22 | |
23 #include "data/model/TextModel.h" | |
24 | |
25 #include <QPainter> | |
26 #include <QMouseEvent> | |
27 #include <QInputDialog> | |
28 | |
29 #include <iostream> | |
30 #include <cmath> | |
31 | |
32 TextLayer::TextLayer() : | |
33 Layer(), | |
34 m_model(0), | |
35 m_editing(false), | |
36 m_originalPoint(0, 0.0, tr("Empty Label")), | |
37 m_editingPoint(0, 0.0, tr("Empty Label")), | |
38 m_editingCommand(0), | |
39 m_colour(255, 150, 50) // orange | |
40 { | |
41 | |
42 } | |
43 | |
44 void | |
45 TextLayer::setModel(TextModel *model) | |
46 { | |
47 if (m_model == model) return; | |
48 m_model = model; | |
49 | |
50 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | |
51 connect(m_model, SIGNAL(modelChanged(size_t, size_t)), | |
52 this, SIGNAL(modelChanged(size_t, size_t))); | |
53 | |
54 connect(m_model, SIGNAL(completionChanged()), | |
55 this, SIGNAL(modelCompletionChanged())); | |
56 | |
57 // std::cerr << "TextLayer::setModel(" << model << ")" << std::endl; | |
58 | |
59 emit modelReplaced(); | |
60 } | |
61 | |
62 Layer::PropertyList | |
63 TextLayer::getProperties() const | |
64 { | |
65 PropertyList list; | |
66 list.push_back("Colour"); | |
67 return list; | |
68 } | |
69 | |
70 QString | |
71 TextLayer::getPropertyLabel(const PropertyName &name) const | |
72 { | |
73 if (name == "Colour") return tr("Colour"); | |
74 return ""; | |
75 } | |
76 | |
77 Layer::PropertyType | |
78 TextLayer::getPropertyType(const PropertyName &) const | |
79 { | |
80 return ValueProperty; | |
81 } | |
82 | |
83 int | |
84 TextLayer::getPropertyRangeAndValue(const PropertyName &name, | |
85 int *min, int *max, int *deflt) const | |
86 { | |
87 //!!! factor this colour handling stuff out into a colour manager class | |
88 | |
89 int val = 0; | |
90 | |
91 if (name == "Colour") { | |
92 | |
93 if (min) *min = 0; | |
94 if (max) *max = 5; | |
95 if (deflt) *deflt = 0; | |
96 | |
97 if (m_colour == Qt::black) val = 0; | |
98 else if (m_colour == Qt::darkRed) val = 1; | |
99 else if (m_colour == Qt::darkBlue) val = 2; | |
100 else if (m_colour == Qt::darkGreen) val = 3; | |
101 else if (m_colour == QColor(200, 50, 255)) val = 4; | |
102 else if (m_colour == QColor(255, 150, 50)) val = 5; | |
103 | |
104 } else { | |
105 | |
106 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); | |
107 } | |
108 | |
109 return val; | |
110 } | |
111 | |
112 QString | |
113 TextLayer::getPropertyValueLabel(const PropertyName &name, | |
114 int value) const | |
115 { | |
116 if (name == "Colour") { | |
117 switch (value) { | |
118 default: | |
119 case 0: return tr("Black"); | |
120 case 1: return tr("Red"); | |
121 case 2: return tr("Blue"); | |
122 case 3: return tr("Green"); | |
123 case 4: return tr("Purple"); | |
124 case 5: return tr("Orange"); | |
125 } | |
126 } | |
127 return tr("<unknown>"); | |
128 } | |
129 | |
130 void | |
131 TextLayer::setProperty(const PropertyName &name, int value) | |
132 { | |
133 if (name == "Colour") { | |
134 switch (value) { | |
135 default: | |
136 case 0: setBaseColour(Qt::black); break; | |
137 case 1: setBaseColour(Qt::darkRed); break; | |
138 case 2: setBaseColour(Qt::darkBlue); break; | |
139 case 3: setBaseColour(Qt::darkGreen); break; | |
140 case 4: setBaseColour(QColor(200, 50, 255)); break; | |
141 case 5: setBaseColour(QColor(255, 150, 50)); break; | |
142 } | |
143 } | |
144 } | |
145 | |
146 bool | |
147 TextLayer::getValueExtents(float &, float &, bool &, QString &) const | |
148 { | |
149 return false; | |
150 } | |
151 | |
152 void | |
153 TextLayer::setBaseColour(QColor colour) | |
154 { | |
155 if (m_colour == colour) return; | |
156 m_colour = colour; | |
157 emit layerParametersChanged(); | |
158 } | |
159 | |
160 bool | |
161 TextLayer::isLayerScrollable(const View *v) const | |
162 { | |
163 QPoint discard; | |
164 return !v->shouldIlluminateLocalFeatures(this, discard); | |
165 } | |
166 | |
167 | |
168 TextModel::PointList | |
169 TextLayer::getLocalPoints(View *v, int x, int y) const | |
170 { | |
171 if (!m_model) return TextModel::PointList(); | |
172 | |
173 long frame0 = v->getFrameForX(-150); | |
174 long frame1 = v->getFrameForX(v->width() + 150); | |
175 | |
176 TextModel::PointList points(m_model->getPoints(frame0, frame1)); | |
177 | |
178 TextModel::PointList rv; | |
179 QFontMetrics metrics = QPainter().fontMetrics(); | |
180 | |
181 for (TextModel::PointList::iterator i = points.begin(); | |
182 i != points.end(); ++i) { | |
183 | |
184 const TextModel::Point &p(*i); | |
185 | |
186 int px = v->getXForFrame(p.frame); | |
187 int py = getYForHeight(v, p.height); | |
188 | |
189 QString label = p.label; | |
190 if (label == "") { | |
191 label = tr("<no text>"); | |
192 } | |
193 | |
194 QRect rect = metrics.boundingRect | |
195 (QRect(0, 0, 150, 200), | |
196 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); | |
197 | |
198 if (py + rect.height() > v->height()) { | |
199 if (rect.height() > v->height()) py = 0; | |
200 else py = v->height() - rect.height() - 1; | |
201 } | |
202 | |
203 if (x >= px && x < px + rect.width() && | |
204 y >= py && y < py + rect.height()) { | |
205 rv.insert(p); | |
206 } | |
207 } | |
208 | |
209 return rv; | |
210 } | |
211 | |
212 QString | |
213 TextLayer::getFeatureDescription(View *v, QPoint &pos) const | |
214 { | |
215 int x = pos.x(); | |
216 | |
217 if (!m_model || !m_model->getSampleRate()) return ""; | |
218 | |
219 TextModel::PointList points = getLocalPoints(v, x, pos.y()); | |
220 | |
221 if (points.empty()) { | |
222 if (!m_model->isReady()) { | |
223 return tr("In progress"); | |
224 } else { | |
225 return ""; | |
226 } | |
227 } | |
228 | |
229 long useFrame = points.begin()->frame; | |
230 | |
231 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); | |
232 | |
233 QString text; | |
234 | |
235 if (points.begin()->label == "") { | |
236 text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3")) | |
237 .arg(rt.toText(true).c_str()) | |
238 .arg(points.begin()->height) | |
239 .arg(points.begin()->label); | |
240 } | |
241 | |
242 pos = QPoint(v->getXForFrame(useFrame), | |
243 getYForHeight(v, points.begin()->height)); | |
244 return text; | |
245 } | |
246 | |
247 | |
248 //!!! too much overlap with TimeValueLayer/TimeInstantLayer | |
249 | |
250 bool | |
251 TextLayer::snapToFeatureFrame(View *v, int &frame, | |
252 size_t &resolution, | |
253 SnapType snap) const | |
254 { | |
255 if (!m_model) { | |
256 return Layer::snapToFeatureFrame(v, frame, resolution, snap); | |
257 } | |
258 | |
259 resolution = m_model->getResolution(); | |
260 TextModel::PointList points; | |
261 | |
262 if (snap == SnapNeighbouring) { | |
263 | |
264 points = getLocalPoints(v, v->getXForFrame(frame), -1); | |
265 if (points.empty()) return false; | |
266 frame = points.begin()->frame; | |
267 return true; | |
268 } | |
269 | |
270 points = m_model->getPoints(frame, frame); | |
271 int snapped = frame; | |
272 bool found = false; | |
273 | |
274 for (TextModel::PointList::const_iterator i = points.begin(); | |
275 i != points.end(); ++i) { | |
276 | |
277 if (snap == SnapRight) { | |
278 | |
279 if (i->frame > frame) { | |
280 snapped = i->frame; | |
281 found = true; | |
282 break; | |
283 } | |
284 | |
285 } else if (snap == SnapLeft) { | |
286 | |
287 if (i->frame <= frame) { | |
288 snapped = i->frame; | |
289 found = true; // don't break, as the next may be better | |
290 } else { | |
291 break; | |
292 } | |
293 | |
294 } else { // nearest | |
295 | |
296 TextModel::PointList::const_iterator j = i; | |
297 ++j; | |
298 | |
299 if (j == points.end()) { | |
300 | |
301 snapped = i->frame; | |
302 found = true; | |
303 break; | |
304 | |
305 } else if (j->frame >= frame) { | |
306 | |
307 if (j->frame - frame < frame - i->frame) { | |
308 snapped = j->frame; | |
309 } else { | |
310 snapped = i->frame; | |
311 } | |
312 found = true; | |
313 break; | |
314 } | |
315 } | |
316 } | |
317 | |
318 frame = snapped; | |
319 return found; | |
320 } | |
321 | |
322 int | |
323 TextLayer::getYForHeight(View *v, float height) const | |
324 { | |
325 int h = v->height(); | |
326 return h - int(height * h); | |
327 } | |
328 | |
329 float | |
330 TextLayer::getHeightForY(View *v, int y) const | |
331 { | |
332 int h = v->height(); | |
333 return float(h - y) / h; | |
334 } | |
335 | |
336 void | |
337 TextLayer::paint(View *v, QPainter &paint, QRect rect) const | |
338 { | |
339 if (!m_model || !m_model->isOK()) return; | |
340 | |
341 int sampleRate = m_model->getSampleRate(); | |
342 if (!sampleRate) return; | |
343 | |
344 // Profiler profiler("TextLayer::paint", true); | |
345 | |
346 int x0 = rect.left(), x1 = rect.right(); | |
347 long frame0 = v->getFrameForX(x0); | |
348 long frame1 = v->getFrameForX(x1); | |
349 | |
350 TextModel::PointList points(m_model->getPoints(frame0, frame1)); | |
351 if (points.empty()) return; | |
352 | |
353 QColor brushColour(m_colour); | |
354 | |
355 int h, s, val; | |
356 brushColour.getHsv(&h, &s, &val); | |
357 brushColour.setHsv(h, s, 255, 100); | |
358 | |
359 QColor penColour; | |
360 if (v->hasLightBackground()) { | |
361 penColour = Qt::black; | |
362 } else { | |
363 penColour = Qt::white; | |
364 } | |
365 | |
366 // std::cerr << "TextLayer::paint: resolution is " | |
367 // << m_model->getResolution() << " frames" << std::endl; | |
368 | |
369 QPoint localPos; | |
370 long illuminateFrame = -1; | |
371 | |
372 if (v->shouldIlluminateLocalFeatures(this, localPos)) { | |
373 TextModel::PointList localPoints = getLocalPoints(v, localPos.x(), | |
374 localPos.y()); | |
375 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; | |
376 } | |
377 | |
378 int boxMaxWidth = 150; | |
379 int boxMaxHeight = 200; | |
380 | |
381 paint.save(); | |
382 paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->height()); | |
383 | |
384 for (TextModel::PointList::const_iterator i = points.begin(); | |
385 i != points.end(); ++i) { | |
386 | |
387 const TextModel::Point &p(*i); | |
388 | |
389 int x = v->getXForFrame(p.frame); | |
390 int y = getYForHeight(v, p.height); | |
391 | |
392 if (illuminateFrame == p.frame) { | |
393 paint.setBrush(penColour); | |
394 if (v->hasLightBackground()) { | |
395 paint.setPen(Qt::white); | |
396 } else { | |
397 paint.setPen(Qt::black); | |
398 } | |
399 } else { | |
400 paint.setPen(penColour); | |
401 paint.setBrush(brushColour); | |
402 } | |
403 | |
404 QString label = p.label; | |
405 if (label == "") { | |
406 label = tr("<no text>"); | |
407 } | |
408 | |
409 QRect boxRect = paint.fontMetrics().boundingRect | |
410 (QRect(0, 0, boxMaxWidth, boxMaxHeight), | |
411 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); | |
412 | |
413 QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height()); | |
414 boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2); | |
415 | |
416 if (y + boxRect.height() > v->height()) { | |
417 if (boxRect.height() > v->height()) y = 0; | |
418 else y = v->height() - boxRect.height() - 1; | |
419 } | |
420 | |
421 boxRect = QRect(x, y, boxRect.width(), boxRect.height()); | |
422 textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); | |
423 | |
424 // boxRect = QRect(x, y, boxRect.width(), boxRect.height()); | |
425 // textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); | |
426 | |
427 paint.setRenderHint(QPainter::Antialiasing, false); | |
428 paint.drawRect(boxRect); | |
429 | |
430 paint.setRenderHint(QPainter::Antialiasing, true); | |
431 paint.drawText(textRect, | |
432 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, | |
433 label); | |
434 | |
435 /// if (p.label != "") { | |
436 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); | |
437 /// } | |
438 } | |
439 | |
440 paint.restore(); | |
441 | |
442 // looks like save/restore doesn't deal with this: | |
443 paint.setRenderHint(QPainter::Antialiasing, false); | |
444 } | |
445 | |
446 void | |
447 TextLayer::drawStart(View *v, QMouseEvent *e) | |
448 { | |
449 // std::cerr << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
450 | |
451 if (!m_model) { | |
452 std::cerr << "TextLayer::drawStart: no model" << std::endl; | |
453 return; | |
454 } | |
455 | |
456 long frame = v->getFrameForX(e->x()); | |
457 if (frame < 0) frame = 0; | |
458 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
459 | |
460 float height = getHeightForY(v, e->y()); | |
461 | |
462 m_editingPoint = TextModel::Point(frame, height, ""); | |
463 m_originalPoint = m_editingPoint; | |
464 | |
465 if (m_editingCommand) m_editingCommand->finish(); | |
466 m_editingCommand = new TextModel::EditCommand(m_model, "Add Label"); | |
467 m_editingCommand->addPoint(m_editingPoint); | |
468 | |
469 m_editing = true; | |
470 } | |
471 | |
472 void | |
473 TextLayer::drawDrag(View *v, QMouseEvent *e) | |
474 { | |
475 // std::cerr << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
476 | |
477 if (!m_model || !m_editing) return; | |
478 | |
479 long frame = v->getFrameForX(e->x()); | |
480 if (frame < 0) frame = 0; | |
481 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
482 | |
483 float height = getHeightForY(v, e->y()); | |
484 | |
485 m_editingCommand->deletePoint(m_editingPoint); | |
486 m_editingPoint.frame = frame; | |
487 m_editingPoint.height = height; | |
488 m_editingCommand->addPoint(m_editingPoint); | |
489 } | |
490 | |
491 void | |
492 TextLayer::drawEnd(View *v, QMouseEvent *) | |
493 { | |
494 // std::cerr << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
495 if (!m_model || !m_editing) return; | |
496 | |
497 bool ok = false; | |
498 QString label = QInputDialog::getText(v, tr("Enter label"), | |
499 tr("Please enter a new label:"), | |
500 QLineEdit::Normal, "", &ok); | |
501 | |
502 if (ok) { | |
503 TextModel::RelabelCommand *command = | |
504 new TextModel::RelabelCommand(m_model, m_editingPoint, label); | |
505 m_editingCommand->addCommand(command); | |
506 } | |
507 | |
508 m_editingCommand->finish(); | |
509 m_editingCommand = 0; | |
510 m_editing = false; | |
511 } | |
512 | |
513 void | |
514 TextLayer::editStart(View *v, QMouseEvent *e) | |
515 { | |
516 // std::cerr << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
517 | |
518 if (!m_model) return; | |
519 | |
520 TextModel::PointList points = getLocalPoints(v, e->x(), e->y()); | |
521 if (points.empty()) return; | |
522 | |
523 m_editOrigin = e->pos(); | |
524 m_editingPoint = *points.begin(); | |
525 m_originalPoint = m_editingPoint; | |
526 | |
527 if (m_editingCommand) { | |
528 m_editingCommand->finish(); | |
529 m_editingCommand = 0; | |
530 } | |
531 | |
532 m_editing = true; | |
533 } | |
534 | |
535 void | |
536 TextLayer::editDrag(View *v, QMouseEvent *e) | |
537 { | |
538 if (!m_model || !m_editing) return; | |
539 | |
540 long frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); | |
541 float heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); | |
542 | |
543 long frame = m_originalPoint.frame + frameDiff; | |
544 float height = m_originalPoint.height + heightDiff; | |
545 | |
546 // long frame = v->getFrameForX(e->x()); | |
547 if (frame < 0) frame = 0; | |
548 frame = (frame / m_model->getResolution()) * m_model->getResolution(); | |
549 | |
550 // float height = getHeightForY(v, e->y()); | |
551 | |
552 if (!m_editingCommand) { | |
553 m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); | |
554 } | |
555 | |
556 m_editingCommand->deletePoint(m_editingPoint); | |
557 m_editingPoint.frame = frame; | |
558 m_editingPoint.height = height; | |
559 m_editingCommand->addPoint(m_editingPoint); | |
560 } | |
561 | |
562 void | |
563 TextLayer::editEnd(View *, QMouseEvent *) | |
564 { | |
565 // std::cerr << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
566 if (!m_model || !m_editing) return; | |
567 | |
568 if (m_editingCommand) { | |
569 | |
570 QString newName = m_editingCommand->getName(); | |
571 | |
572 if (m_editingPoint.frame != m_originalPoint.frame) { | |
573 if (m_editingPoint.height != m_originalPoint.height) { | |
574 newName = tr("Move Label"); | |
575 } else { | |
576 newName = tr("Move Label Horizontally"); | |
577 } | |
578 } else { | |
579 newName = tr("Move Label Vertically"); | |
580 } | |
581 | |
582 m_editingCommand->setName(newName); | |
583 m_editingCommand->finish(); | |
584 } | |
585 | |
586 m_editingCommand = 0; | |
587 m_editing = false; | |
588 } | |
589 | |
590 void | |
591 TextLayer::editOpen(View *v, QMouseEvent *e) | |
592 { | |
593 std::cerr << "TextLayer::editOpen" << std::endl; | |
594 | |
595 if (!m_model) return; | |
596 | |
597 TextModel::PointList points = getLocalPoints(v, e->x(), e->y()); | |
598 if (points.empty()) return; | |
599 | |
600 QString label = points.begin()->label; | |
601 | |
602 bool ok = false; | |
603 label = QInputDialog::getText(v, tr("Enter label"), | |
604 tr("Please enter a new label:"), | |
605 QLineEdit::Normal, label, &ok); | |
606 if (ok && label != points.begin()->label) { | |
607 TextModel::RelabelCommand *command = | |
608 new TextModel::RelabelCommand(m_model, *points.begin(), label); | |
609 CommandHistory::getInstance()->addCommand(command); | |
610 } | |
611 } | |
612 | |
613 void | |
614 TextLayer::moveSelection(Selection s, size_t newStartFrame) | |
615 { | |
616 if (!m_model) return; | |
617 | |
618 TextModel::EditCommand *command = | |
619 new TextModel::EditCommand(m_model, tr("Drag Selection")); | |
620 | |
621 TextModel::PointList points = | |
622 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
623 | |
624 for (TextModel::PointList::iterator i = points.begin(); | |
625 i != points.end(); ++i) { | |
626 | |
627 if (s.contains(i->frame)) { | |
628 TextModel::Point newPoint(*i); | |
629 newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); | |
630 command->deletePoint(*i); | |
631 command->addPoint(newPoint); | |
632 } | |
633 } | |
634 | |
635 command->finish(); | |
636 } | |
637 | |
638 void | |
639 TextLayer::resizeSelection(Selection s, Selection newSize) | |
640 { | |
641 if (!m_model) return; | |
642 | |
643 TextModel::EditCommand *command = | |
644 new TextModel::EditCommand(m_model, tr("Resize Selection")); | |
645 | |
646 TextModel::PointList points = | |
647 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
648 | |
649 double ratio = | |
650 double(newSize.getEndFrame() - newSize.getStartFrame()) / | |
651 double(s.getEndFrame() - s.getStartFrame()); | |
652 | |
653 for (TextModel::PointList::iterator i = points.begin(); | |
654 i != points.end(); ++i) { | |
655 | |
656 if (s.contains(i->frame)) { | |
657 | |
658 double target = i->frame; | |
659 target = newSize.getStartFrame() + | |
660 double(target - s.getStartFrame()) * ratio; | |
661 | |
662 TextModel::Point newPoint(*i); | |
663 newPoint.frame = lrint(target); | |
664 command->deletePoint(*i); | |
665 command->addPoint(newPoint); | |
666 } | |
667 } | |
668 | |
669 command->finish(); | |
670 } | |
671 | |
672 void | |
673 TextLayer::deleteSelection(Selection s) | |
674 { | |
675 if (!m_model) return; | |
676 | |
677 TextModel::EditCommand *command = | |
678 new TextModel::EditCommand(m_model, tr("Delete Selection")); | |
679 | |
680 TextModel::PointList points = | |
681 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
682 | |
683 for (TextModel::PointList::iterator i = points.begin(); | |
684 i != points.end(); ++i) { | |
685 if (s.contains(i->frame)) command->deletePoint(*i); | |
686 } | |
687 | |
688 command->finish(); | |
689 } | |
690 | |
691 void | |
692 TextLayer::copy(Selection s, Clipboard &to) | |
693 { | |
694 if (!m_model) return; | |
695 | |
696 TextModel::PointList points = | |
697 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
698 | |
699 for (TextModel::PointList::iterator i = points.begin(); | |
700 i != points.end(); ++i) { | |
701 if (s.contains(i->frame)) { | |
702 Clipboard::Point point(i->frame, i->height, i->label); | |
703 to.addPoint(point); | |
704 } | |
705 } | |
706 } | |
707 | |
708 bool | |
709 TextLayer::paste(const Clipboard &from, int frameOffset, bool /* interactive */) | |
710 { | |
711 if (!m_model) return false; | |
712 | |
713 const Clipboard::PointList &points = from.getPoints(); | |
714 | |
715 TextModel::EditCommand *command = | |
716 new TextModel::EditCommand(m_model, tr("Paste")); | |
717 | |
718 float valueMin = 0.0, valueMax = 1.0; | |
719 for (Clipboard::PointList::const_iterator i = points.begin(); | |
720 i != points.end(); ++i) { | |
721 if (i->haveValue()) { | |
722 if (i->getValue() < valueMin) valueMin = i->getValue(); | |
723 if (i->getValue() > valueMax) valueMax = i->getValue(); | |
724 } | |
725 } | |
726 if (valueMax < valueMin + 1.0) valueMax = valueMin + 1.0; | |
727 | |
728 for (Clipboard::PointList::const_iterator i = points.begin(); | |
729 i != points.end(); ++i) { | |
730 | |
731 if (!i->haveFrame()) continue; | |
732 size_t frame = 0; | |
733 if (frameOffset > 0 || -frameOffset < i->getFrame()) { | |
734 frame = i->getFrame() + frameOffset; | |
735 } | |
736 TextModel::Point newPoint(frame); | |
737 | |
738 if (i->haveValue()) { | |
739 newPoint.height = (i->getValue() - valueMin) / (valueMax - valueMin); | |
740 } else { | |
741 newPoint.height = 0.5; | |
742 } | |
743 | |
744 if (i->haveLabel()) { | |
745 newPoint.label = i->getLabel(); | |
746 } else if (i->haveValue()) { | |
747 newPoint.label = QString("%1").arg(i->getValue()); | |
748 } else { | |
749 newPoint.label = tr("New Point"); | |
750 } | |
751 | |
752 command->addPoint(newPoint); | |
753 } | |
754 | |
755 command->finish(); | |
756 return true; | |
757 } | |
758 | |
759 QString | |
760 TextLayer::toXmlString(QString indent, QString extraAttributes) const | |
761 { | |
762 return Layer::toXmlString(indent, extraAttributes + | |
763 QString(" colour=\"%1\"") | |
764 .arg(encodeColour(m_colour))); | |
765 } | |
766 | |
767 void | |
768 TextLayer::setProperties(const QXmlAttributes &attributes) | |
769 { | |
770 QString colourSpec = attributes.value("colour"); | |
771 if (colourSpec != "") { | |
772 QColor colour(colourSpec); | |
773 if (colour.isValid()) { | |
774 setBaseColour(QColor(colourSpec)); | |
775 } | |
776 } | |
777 } | |
778 |