Mercurial > hg > svgui
comparison layer/RegionLayer.cpp @ 411:96e4d7b9e165
* Add region model and layer; improve assignment of model types to
feature extraction transforms with duration
author | Chris Cannam |
---|---|
date | Thu, 18 Sep 2008 16:08:14 +0000 |
parents | |
children | d332ad1ca66b |
comparison
equal
deleted
inserted
replaced
410:33b7f5e54d60 | 411:96e4d7b9e165 |
---|---|
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-2008 Chris Cannam and QMUL. | |
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 "RegionLayer.h" | |
17 | |
18 #include "data/model/Model.h" | |
19 #include "base/RealTime.h" | |
20 #include "base/Profiler.h" | |
21 #include "base/LogRange.h" | |
22 #include "ColourDatabase.h" | |
23 #include "view/View.h" | |
24 | |
25 #include "data/model/RegionModel.h" | |
26 | |
27 #include "widgets/ItemEditDialog.h" | |
28 | |
29 #include "SpectrogramLayer.h" // for optional frequency alignment | |
30 | |
31 #include <QPainter> | |
32 #include <QPainterPath> | |
33 #include <QMouseEvent> | |
34 #include <QTextStream> | |
35 #include <QMessageBox> | |
36 | |
37 #include <iostream> | |
38 #include <cmath> | |
39 | |
40 RegionLayer::RegionLayer() : | |
41 SingleColourLayer(), | |
42 m_model(0), | |
43 m_editing(false), | |
44 m_originalPoint(0, 0.0, 0, tr("New Point")), | |
45 m_editingPoint(0, 0.0, 0, tr("New Point")), | |
46 m_editingCommand(0), | |
47 m_verticalScale(AutoAlignScale) | |
48 { | |
49 | |
50 } | |
51 | |
52 void | |
53 RegionLayer::setModel(RegionModel *model) | |
54 { | |
55 if (m_model == model) return; | |
56 m_model = model; | |
57 | |
58 connectSignals(m_model); | |
59 | |
60 // std::cerr << "RegionLayer::setModel(" << model << ")" << std::endl; | |
61 | |
62 emit modelReplaced(); | |
63 } | |
64 | |
65 Layer::PropertyList | |
66 RegionLayer::getProperties() const | |
67 { | |
68 PropertyList list = SingleColourLayer::getProperties(); | |
69 list.push_back("Vertical Scale"); | |
70 list.push_back("Scale Units"); | |
71 return list; | |
72 } | |
73 | |
74 QString | |
75 RegionLayer::getPropertyLabel(const PropertyName &name) const | |
76 { | |
77 if (name == "Vertical Scale") return tr("Vertical Scale"); | |
78 if (name == "Scale Units") return tr("Scale Units"); | |
79 return SingleColourLayer::getPropertyLabel(name); | |
80 } | |
81 | |
82 Layer::PropertyType | |
83 RegionLayer::getPropertyType(const PropertyName &name) const | |
84 { | |
85 if (name == "Scale Units") return UnitsProperty; | |
86 if (name == "Vertical Scale") return ValueProperty; | |
87 return SingleColourLayer::getPropertyType(name); | |
88 } | |
89 | |
90 QString | |
91 RegionLayer::getPropertyGroupName(const PropertyName &name) const | |
92 { | |
93 if (name == "Vertical Scale" || name == "Scale Units") { | |
94 return tr("Scale"); | |
95 } | |
96 return SingleColourLayer::getPropertyGroupName(name); | |
97 } | |
98 | |
99 int | |
100 RegionLayer::getPropertyRangeAndValue(const PropertyName &name, | |
101 int *min, int *max, int *deflt) const | |
102 { | |
103 int val = 0; | |
104 | |
105 if (name == "Vertical Scale") { | |
106 | |
107 if (min) *min = 0; | |
108 if (max) *max = 3; | |
109 if (deflt) *deflt = int(AutoAlignScale); | |
110 | |
111 val = int(m_verticalScale); | |
112 | |
113 } else if (name == "Scale Units") { | |
114 | |
115 if (deflt) *deflt = 0; | |
116 if (m_model) { | |
117 val = UnitDatabase::getInstance()->getUnitId | |
118 (m_model->getScaleUnits()); | |
119 } | |
120 | |
121 } else { | |
122 | |
123 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); | |
124 } | |
125 | |
126 return val; | |
127 } | |
128 | |
129 QString | |
130 RegionLayer::getPropertyValueLabel(const PropertyName &name, | |
131 int value) const | |
132 { | |
133 if (name == "Vertical Scale") { | |
134 switch (value) { | |
135 default: | |
136 case 0: return tr("Auto-Align"); | |
137 case 1: return tr("Linear"); | |
138 case 2: return tr("Log"); | |
139 } | |
140 } | |
141 return SingleColourLayer::getPropertyValueLabel(name, value); | |
142 } | |
143 | |
144 void | |
145 RegionLayer::setProperty(const PropertyName &name, int value) | |
146 { | |
147 if (name == "Vertical Scale") { | |
148 setVerticalScale(VerticalScale(value)); | |
149 } else if (name == "Scale Units") { | |
150 if (m_model) { | |
151 m_model->setScaleUnits | |
152 (UnitDatabase::getInstance()->getUnitById(value)); | |
153 emit modelChanged(); | |
154 } | |
155 } else { | |
156 return SingleColourLayer::setProperty(name, value); | |
157 } | |
158 } | |
159 | |
160 void | |
161 RegionLayer::setVerticalScale(VerticalScale scale) | |
162 { | |
163 if (m_verticalScale == scale) return; | |
164 m_verticalScale = scale; | |
165 emit layerParametersChanged(); | |
166 } | |
167 | |
168 bool | |
169 RegionLayer::isLayerScrollable(const View *v) const | |
170 { | |
171 QPoint discard; | |
172 return !v->shouldIlluminateLocalFeatures(this, discard); | |
173 } | |
174 | |
175 bool | |
176 RegionLayer::getValueExtents(float &min, float &max, | |
177 bool &logarithmic, QString &unit) const | |
178 { | |
179 if (!m_model) return false; | |
180 min = m_model->getValueMinimum(); | |
181 max = m_model->getValueMaximum(); | |
182 unit = m_model->getScaleUnits(); | |
183 | |
184 if (m_verticalScale == LogScale) logarithmic = true; | |
185 | |
186 return true; | |
187 } | |
188 | |
189 bool | |
190 RegionLayer::getDisplayExtents(float &min, float &max) const | |
191 { | |
192 if (!m_model || m_verticalScale == AutoAlignScale) return false; | |
193 | |
194 min = m_model->getValueMinimum(); | |
195 max = m_model->getValueMaximum(); | |
196 | |
197 return true; | |
198 } | |
199 | |
200 RegionModel::PointList | |
201 RegionLayer::getLocalPoints(View *v, int x) const | |
202 { | |
203 if (!m_model) return RegionModel::PointList(); | |
204 | |
205 long frame = v->getFrameForX(x); | |
206 | |
207 RegionModel::PointList onPoints = | |
208 m_model->getPoints(frame); | |
209 | |
210 if (!onPoints.empty()) { | |
211 return onPoints; | |
212 } | |
213 | |
214 RegionModel::PointList prevPoints = | |
215 m_model->getPreviousPoints(frame); | |
216 RegionModel::PointList nextPoints = | |
217 m_model->getNextPoints(frame); | |
218 | |
219 RegionModel::PointList usePoints = prevPoints; | |
220 | |
221 if (prevPoints.empty()) { | |
222 usePoints = nextPoints; | |
223 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && | |
224 !(nextPoints.begin()->frame > v->getEndFrame())) { | |
225 usePoints = nextPoints; | |
226 } else if (long(nextPoints.begin()->frame) - frame < | |
227 frame - long(prevPoints.begin()->frame)) { | |
228 usePoints = nextPoints; | |
229 } | |
230 | |
231 if (!usePoints.empty()) { | |
232 int fuzz = 2; | |
233 int px = v->getXForFrame(usePoints.begin()->frame); | |
234 if ((px > x && px - x > fuzz) || | |
235 (px < x && x - px > fuzz + 1)) { | |
236 usePoints.clear(); | |
237 } | |
238 } | |
239 | |
240 return usePoints; | |
241 } | |
242 | |
243 QString | |
244 RegionLayer::getFeatureDescription(View *v, QPoint &pos) const | |
245 { | |
246 int x = pos.x(); | |
247 | |
248 if (!m_model || !m_model->getSampleRate()) return ""; | |
249 | |
250 RegionModel::PointList points = getLocalPoints(v, x); | |
251 | |
252 if (points.empty()) { | |
253 if (!m_model->isReady()) { | |
254 return tr("In progress"); | |
255 } else { | |
256 return tr("No local points"); | |
257 } | |
258 } | |
259 | |
260 RegionRec region(0); | |
261 RegionModel::PointList::iterator i; | |
262 | |
263 for (i = points.begin(); i != points.end(); ++i) { | |
264 | |
265 int y = getYForValue(v, i->value); | |
266 int h = 3; | |
267 | |
268 if (m_model->getValueQuantization() != 0.0) { | |
269 h = y - getYForValue(v, i->value + m_model->getValueQuantization()); | |
270 if (h < 3) h = 3; | |
271 } | |
272 | |
273 if (pos.y() >= y - h && pos.y() <= y) { | |
274 region = *i; | |
275 break; | |
276 } | |
277 } | |
278 | |
279 if (i == points.end()) return tr("No local points"); | |
280 | |
281 RealTime rt = RealTime::frame2RealTime(region.frame, | |
282 m_model->getSampleRate()); | |
283 RealTime rd = RealTime::frame2RealTime(region.duration, | |
284 m_model->getSampleRate()); | |
285 | |
286 QString valueText; | |
287 | |
288 valueText = tr("%1 %2").arg(region.value).arg(m_model->getScaleUnits()); | |
289 | |
290 QString text; | |
291 | |
292 if (region.label == "") { | |
293 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label")) | |
294 .arg(rt.toText(true).c_str()) | |
295 .arg(valueText) | |
296 .arg(rd.toText(true).c_str()); | |
297 } else { | |
298 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4")) | |
299 .arg(rt.toText(true).c_str()) | |
300 .arg(valueText) | |
301 .arg(rd.toText(true).c_str()) | |
302 .arg(region.label); | |
303 } | |
304 | |
305 pos = QPoint(v->getXForFrame(region.frame), | |
306 getYForValue(v, region.value)); | |
307 return text; | |
308 } | |
309 | |
310 bool | |
311 RegionLayer::snapToFeatureFrame(View *v, int &frame, | |
312 size_t &resolution, | |
313 SnapType snap) const | |
314 { | |
315 if (!m_model) { | |
316 return Layer::snapToFeatureFrame(v, frame, resolution, snap); | |
317 } | |
318 | |
319 resolution = m_model->getResolution(); | |
320 RegionModel::PointList points; | |
321 | |
322 if (snap == SnapNeighbouring) { | |
323 | |
324 points = getLocalPoints(v, v->getXForFrame(frame)); | |
325 if (points.empty()) return false; | |
326 frame = points.begin()->frame; | |
327 return true; | |
328 } | |
329 | |
330 points = m_model->getPoints(frame, frame); | |
331 int snapped = frame; | |
332 bool found = false; | |
333 | |
334 for (RegionModel::PointList::const_iterator i = points.begin(); | |
335 i != points.end(); ++i) { | |
336 | |
337 if (snap == SnapRight) { | |
338 | |
339 if (i->frame > frame) { | |
340 snapped = i->frame; | |
341 found = true; | |
342 break; | |
343 } | |
344 | |
345 } else if (snap == SnapLeft) { | |
346 | |
347 if (i->frame <= frame) { | |
348 snapped = i->frame; | |
349 found = true; // don't break, as the next may be better | |
350 } else { | |
351 break; | |
352 } | |
353 | |
354 } else { // nearest | |
355 | |
356 RegionModel::PointList::const_iterator j = i; | |
357 ++j; | |
358 | |
359 if (j == points.end()) { | |
360 | |
361 snapped = i->frame; | |
362 found = true; | |
363 break; | |
364 | |
365 } else if (j->frame >= frame) { | |
366 | |
367 if (j->frame - frame < frame - i->frame) { | |
368 snapped = j->frame; | |
369 } else { | |
370 snapped = i->frame; | |
371 } | |
372 found = true; | |
373 break; | |
374 } | |
375 } | |
376 } | |
377 | |
378 frame = snapped; | |
379 return found; | |
380 } | |
381 | |
382 void | |
383 RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const | |
384 { | |
385 min = 0.0; | |
386 max = 0.0; | |
387 log = false; | |
388 | |
389 QString queryUnits; | |
390 queryUnits = m_model->getScaleUnits(); | |
391 | |
392 if (m_verticalScale == AutoAlignScale) { | |
393 | |
394 if (!v->getValueExtents(queryUnits, min, max, log)) { | |
395 | |
396 min = m_model->getValueMinimum(); | |
397 max = m_model->getValueMaximum(); | |
398 | |
399 std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl; | |
400 | |
401 } else if (log) { | |
402 | |
403 LogRange::mapRange(min, max); | |
404 | |
405 std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl; | |
406 | |
407 } | |
408 | |
409 } else { | |
410 | |
411 min = m_model->getValueMinimum(); | |
412 max = m_model->getValueMaximum(); | |
413 | |
414 if (m_verticalScale == LogScale) { | |
415 LogRange::mapRange(min, max); | |
416 log = true; | |
417 } | |
418 } | |
419 | |
420 if (max == min) max = min + 1.0; | |
421 } | |
422 | |
423 int | |
424 RegionLayer::getYForValue(View *v, float val) const | |
425 { | |
426 float min = 0.0, max = 0.0; | |
427 bool logarithmic = false; | |
428 int h = v->height(); | |
429 | |
430 getScaleExtents(v, min, max, logarithmic); | |
431 | |
432 // std::cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl; | |
433 | |
434 if (logarithmic) { | |
435 val = LogRange::map(val); | |
436 // std::cerr << "logarithmic true, val now = " << val << std::endl; | |
437 } | |
438 | |
439 int y = int(h - ((val - min) * h) / (max - min)) - 1; | |
440 // std::cerr << "y = " << y << std::endl; | |
441 return y; | |
442 } | |
443 | |
444 float | |
445 RegionLayer::getValueForY(View *v, int y) const | |
446 { | |
447 float min = 0.0, max = 0.0; | |
448 bool logarithmic = false; | |
449 int h = v->height(); | |
450 | |
451 getScaleExtents(v, min, max, logarithmic); | |
452 | |
453 float val = min + (float(h - y) * float(max - min)) / h; | |
454 | |
455 if (logarithmic) { | |
456 val = powf(10.f, val); | |
457 } | |
458 | |
459 return val; | |
460 } | |
461 | |
462 void | |
463 RegionLayer::paint(View *v, QPainter &paint, QRect rect) const | |
464 { | |
465 if (!m_model || !m_model->isOK()) return; | |
466 | |
467 int sampleRate = m_model->getSampleRate(); | |
468 if (!sampleRate) return; | |
469 | |
470 // Profiler profiler("RegionLayer::paint", true); | |
471 | |
472 int x0 = rect.left(), x1 = rect.right(); | |
473 long frame0 = v->getFrameForX(x0); | |
474 long frame1 = v->getFrameForX(x1); | |
475 | |
476 RegionModel::PointList points(m_model->getPoints(frame0, frame1)); | |
477 if (points.empty()) return; | |
478 | |
479 paint.setPen(getBaseQColor()); | |
480 | |
481 QColor brushColour(getBaseQColor()); | |
482 brushColour.setAlpha(80); | |
483 | |
484 // std::cerr << "RegionLayer::paint: resolution is " | |
485 // << m_model->getResolution() << " frames" << std::endl; | |
486 | |
487 float min = m_model->getValueMinimum(); | |
488 float max = m_model->getValueMaximum(); | |
489 if (max == min) max = min + 1.0; | |
490 | |
491 QPoint localPos; | |
492 long illuminateFrame = -1; | |
493 | |
494 if (v->shouldIlluminateLocalFeatures(this, localPos)) { | |
495 RegionModel::PointList localPoints = | |
496 getLocalPoints(v, localPos.x()); | |
497 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; | |
498 } | |
499 | |
500 paint.save(); | |
501 paint.setRenderHint(QPainter::Antialiasing, false); | |
502 | |
503 for (RegionModel::PointList::const_iterator i = points.begin(); | |
504 i != points.end(); ++i) { | |
505 | |
506 const RegionModel::Point &p(*i); | |
507 | |
508 int x = v->getXForFrame(p.frame); | |
509 int y = getYForValue(v, p.value); | |
510 int w = v->getXForFrame(p.frame + p.duration) - x; | |
511 int h = 3; | |
512 | |
513 if (m_model->getValueQuantization() != 0.0) { | |
514 h = y - getYForValue(v, p.value + m_model->getValueQuantization()); | |
515 if (h < 3) h = 3; | |
516 } | |
517 | |
518 if (w < 1) w = 1; | |
519 paint.setPen(getBaseQColor()); | |
520 paint.setBrush(brushColour); | |
521 | |
522 if (illuminateFrame == p.frame) { | |
523 if (localPos.y() >= y - h && localPos.y() < y) { | |
524 paint.setPen(v->getForeground()); | |
525 paint.setBrush(v->getForeground()); | |
526 } | |
527 } | |
528 | |
529 paint.drawLine(x, y, x + w, y); | |
530 paint.drawLine(x, y - h/2, x, y + h/2); | |
531 paint.drawLine(x + w, y - h/2, x + w, y + h/2); | |
532 | |
533 /// if (p.label != "") { | |
534 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); | |
535 /// } | |
536 } | |
537 | |
538 paint.restore(); | |
539 } | |
540 | |
541 void | |
542 RegionLayer::drawStart(View *v, QMouseEvent *e) | |
543 { | |
544 // std::cerr << "RegionLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
545 | |
546 if (!m_model) return; | |
547 | |
548 long frame = v->getFrameForX(e->x()); | |
549 if (frame < 0) frame = 0; | |
550 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
551 | |
552 float value = getValueForY(v, e->y()); | |
553 | |
554 m_editingPoint = RegionModel::Point(frame, value, 0, tr("New Point")); | |
555 m_originalPoint = m_editingPoint; | |
556 | |
557 if (m_editingCommand) finish(m_editingCommand); | |
558 m_editingCommand = new RegionModel::EditCommand(m_model, | |
559 tr("Draw Point")); | |
560 m_editingCommand->addPoint(m_editingPoint); | |
561 | |
562 m_editing = true; | |
563 } | |
564 | |
565 void | |
566 RegionLayer::drawDrag(View *v, QMouseEvent *e) | |
567 { | |
568 // std::cerr << "RegionLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
569 | |
570 if (!m_model || !m_editing) return; | |
571 | |
572 long frame = v->getFrameForX(e->x()); | |
573 if (frame < 0) frame = 0; | |
574 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
575 | |
576 float newValue = getValueForY(v, e->y()); | |
577 | |
578 long newFrame = m_editingPoint.frame; | |
579 long newDuration = frame - newFrame; | |
580 if (newDuration < 0) { | |
581 newFrame = frame; | |
582 newDuration = -newDuration; | |
583 } else if (newDuration == 0) { | |
584 newDuration = 1; | |
585 } | |
586 | |
587 m_editingCommand->deletePoint(m_editingPoint); | |
588 m_editingPoint.frame = newFrame; | |
589 m_editingPoint.value = newValue; | |
590 m_editingPoint.duration = newDuration; | |
591 m_editingCommand->addPoint(m_editingPoint); | |
592 } | |
593 | |
594 void | |
595 RegionLayer::drawEnd(View *, QMouseEvent *) | |
596 { | |
597 // std::cerr << "RegionLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
598 if (!m_model || !m_editing) return; | |
599 finish(m_editingCommand); | |
600 m_editingCommand = 0; | |
601 m_editing = false; | |
602 } | |
603 | |
604 void | |
605 RegionLayer::eraseStart(View *v, QMouseEvent *e) | |
606 { | |
607 if (!m_model) return; | |
608 | |
609 RegionModel::PointList points = getLocalPoints(v, e->x()); | |
610 if (points.empty()) return; | |
611 | |
612 m_editingPoint = *points.begin(); | |
613 | |
614 if (m_editingCommand) { | |
615 finish(m_editingCommand); | |
616 m_editingCommand = 0; | |
617 } | |
618 | |
619 m_editing = true; | |
620 } | |
621 | |
622 void | |
623 RegionLayer::eraseDrag(View *v, QMouseEvent *e) | |
624 { | |
625 } | |
626 | |
627 void | |
628 RegionLayer::eraseEnd(View *v, QMouseEvent *e) | |
629 { | |
630 if (!m_model || !m_editing) return; | |
631 | |
632 m_editing = false; | |
633 | |
634 RegionModel::PointList points = getLocalPoints(v, e->x()); | |
635 if (points.empty()) return; | |
636 if (points.begin()->frame != m_editingPoint.frame || | |
637 points.begin()->value != m_editingPoint.value) return; | |
638 | |
639 m_editingCommand = new RegionModel::EditCommand | |
640 (m_model, tr("Erase Point")); | |
641 | |
642 m_editingCommand->deletePoint(m_editingPoint); | |
643 | |
644 finish(m_editingCommand); | |
645 m_editingCommand = 0; | |
646 m_editing = false; | |
647 } | |
648 | |
649 void | |
650 RegionLayer::editStart(View *v, QMouseEvent *e) | |
651 { | |
652 // std::cerr << "RegionLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
653 | |
654 if (!m_model) return; | |
655 | |
656 RegionModel::PointList points = getLocalPoints(v, e->x()); | |
657 if (points.empty()) return; | |
658 | |
659 m_editingPoint = *points.begin(); | |
660 m_originalPoint = m_editingPoint; | |
661 | |
662 if (m_editingCommand) { | |
663 finish(m_editingCommand); | |
664 m_editingCommand = 0; | |
665 } | |
666 | |
667 m_editing = true; | |
668 } | |
669 | |
670 void | |
671 RegionLayer::editDrag(View *v, QMouseEvent *e) | |
672 { | |
673 // std::cerr << "RegionLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
674 | |
675 if (!m_model || !m_editing) return; | |
676 | |
677 long frame = v->getFrameForX(e->x()); | |
678 if (frame < 0) frame = 0; | |
679 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
680 | |
681 float value = getValueForY(v, e->y()); | |
682 | |
683 if (!m_editingCommand) { | |
684 m_editingCommand = new RegionModel::EditCommand(m_model, | |
685 tr("Drag Point")); | |
686 } | |
687 | |
688 m_editingCommand->deletePoint(m_editingPoint); | |
689 m_editingPoint.frame = frame; | |
690 m_editingPoint.value = value; | |
691 m_editingCommand->addPoint(m_editingPoint); | |
692 } | |
693 | |
694 void | |
695 RegionLayer::editEnd(View *, QMouseEvent *) | |
696 { | |
697 // std::cerr << "RegionLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
698 if (!m_model || !m_editing) return; | |
699 | |
700 if (m_editingCommand) { | |
701 | |
702 QString newName = m_editingCommand->getName(); | |
703 | |
704 if (m_editingPoint.frame != m_originalPoint.frame) { | |
705 if (m_editingPoint.value != m_originalPoint.value) { | |
706 newName = tr("Edit Point"); | |
707 } else { | |
708 newName = tr("Relocate Point"); | |
709 } | |
710 } else { | |
711 newName = tr("Change Point Value"); | |
712 } | |
713 | |
714 m_editingCommand->setName(newName); | |
715 finish(m_editingCommand); | |
716 } | |
717 | |
718 m_editingCommand = 0; | |
719 m_editing = false; | |
720 } | |
721 | |
722 bool | |
723 RegionLayer::editOpen(View *v, QMouseEvent *e) | |
724 { | |
725 if (!m_model) return false; | |
726 | |
727 RegionModel::PointList points = getLocalPoints(v, e->x()); | |
728 if (points.empty()) return false; | |
729 | |
730 RegionModel::Point region = *points.begin(); | |
731 | |
732 ItemEditDialog *dialog = new ItemEditDialog | |
733 (m_model->getSampleRate(), | |
734 ItemEditDialog::ShowTime | | |
735 ItemEditDialog::ShowDuration | | |
736 ItemEditDialog::ShowValue | | |
737 ItemEditDialog::ShowText, | |
738 m_model->getScaleUnits()); | |
739 | |
740 dialog->setFrameTime(region.frame); | |
741 dialog->setValue(region.value); | |
742 dialog->setFrameDuration(region.duration); | |
743 dialog->setText(region.label); | |
744 | |
745 if (dialog->exec() == QDialog::Accepted) { | |
746 | |
747 RegionModel::Point newRegion = region; | |
748 newRegion.frame = dialog->getFrameTime(); | |
749 newRegion.value = dialog->getValue(); | |
750 newRegion.duration = dialog->getFrameDuration(); | |
751 newRegion.label = dialog->getText(); | |
752 | |
753 RegionModel::EditCommand *command = new RegionModel::EditCommand | |
754 (m_model, tr("Edit Point")); | |
755 command->deletePoint(region); | |
756 command->addPoint(newRegion); | |
757 finish(command); | |
758 } | |
759 | |
760 delete dialog; | |
761 return true; | |
762 } | |
763 | |
764 void | |
765 RegionLayer::moveSelection(Selection s, size_t newStartFrame) | |
766 { | |
767 if (!m_model) return; | |
768 | |
769 RegionModel::EditCommand *command = | |
770 new RegionModel::EditCommand(m_model, tr("Drag Selection")); | |
771 | |
772 RegionModel::PointList points = | |
773 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
774 | |
775 for (RegionModel::PointList::iterator i = points.begin(); | |
776 i != points.end(); ++i) { | |
777 | |
778 if (s.contains(i->frame)) { | |
779 RegionModel::Point newPoint(*i); | |
780 newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); | |
781 command->deletePoint(*i); | |
782 command->addPoint(newPoint); | |
783 } | |
784 } | |
785 | |
786 finish(command); | |
787 } | |
788 | |
789 void | |
790 RegionLayer::resizeSelection(Selection s, Selection newSize) | |
791 { | |
792 if (!m_model) return; | |
793 | |
794 RegionModel::EditCommand *command = | |
795 new RegionModel::EditCommand(m_model, tr("Resize Selection")); | |
796 | |
797 RegionModel::PointList points = | |
798 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
799 | |
800 double ratio = | |
801 double(newSize.getEndFrame() - newSize.getStartFrame()) / | |
802 double(s.getEndFrame() - s.getStartFrame()); | |
803 | |
804 for (RegionModel::PointList::iterator i = points.begin(); | |
805 i != points.end(); ++i) { | |
806 | |
807 if (s.contains(i->frame)) { | |
808 | |
809 double targetStart = i->frame; | |
810 targetStart = newSize.getStartFrame() + | |
811 double(targetStart - s.getStartFrame()) * ratio; | |
812 | |
813 double targetEnd = i->frame + i->duration; | |
814 targetEnd = newSize.getStartFrame() + | |
815 double(targetEnd - s.getStartFrame()) * ratio; | |
816 | |
817 RegionModel::Point newPoint(*i); | |
818 newPoint.frame = lrint(targetStart); | |
819 newPoint.duration = lrint(targetEnd - targetStart); | |
820 command->deletePoint(*i); | |
821 command->addPoint(newPoint); | |
822 } | |
823 } | |
824 | |
825 finish(command); | |
826 } | |
827 | |
828 void | |
829 RegionLayer::deleteSelection(Selection s) | |
830 { | |
831 if (!m_model) return; | |
832 | |
833 RegionModel::EditCommand *command = | |
834 new RegionModel::EditCommand(m_model, tr("Delete Selected Points")); | |
835 | |
836 RegionModel::PointList points = | |
837 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
838 | |
839 for (RegionModel::PointList::iterator i = points.begin(); | |
840 i != points.end(); ++i) { | |
841 | |
842 if (s.contains(i->frame)) { | |
843 command->deletePoint(*i); | |
844 } | |
845 } | |
846 | |
847 finish(command); | |
848 } | |
849 | |
850 void | |
851 RegionLayer::copy(View *v, Selection s, Clipboard &to) | |
852 { | |
853 if (!m_model) return; | |
854 | |
855 RegionModel::PointList points = | |
856 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
857 | |
858 for (RegionModel::PointList::iterator i = points.begin(); | |
859 i != points.end(); ++i) { | |
860 if (s.contains(i->frame)) { | |
861 Clipboard::Point point(i->frame, i->value, i->duration, i->label); | |
862 point.setReferenceFrame(alignToReference(v, i->frame)); | |
863 to.addPoint(point); | |
864 } | |
865 } | |
866 } | |
867 | |
868 bool | |
869 RegionLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) | |
870 { | |
871 if (!m_model) return false; | |
872 | |
873 const Clipboard::PointList &points = from.getPoints(); | |
874 | |
875 bool realign = false; | |
876 | |
877 if (clipboardHasDifferentAlignment(v, from)) { | |
878 | |
879 QMessageBox::StandardButton button = | |
880 QMessageBox::question(v, tr("Re-align pasted items?"), | |
881 tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), | |
882 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, | |
883 QMessageBox::Yes); | |
884 | |
885 if (button == QMessageBox::Cancel) { | |
886 return false; | |
887 } | |
888 | |
889 if (button == QMessageBox::Yes) { | |
890 realign = true; | |
891 } | |
892 } | |
893 | |
894 RegionModel::EditCommand *command = | |
895 new RegionModel::EditCommand(m_model, tr("Paste")); | |
896 | |
897 for (Clipboard::PointList::const_iterator i = points.begin(); | |
898 i != points.end(); ++i) { | |
899 | |
900 if (!i->haveFrame()) continue; | |
901 size_t frame = 0; | |
902 | |
903 if (!realign) { | |
904 | |
905 frame = i->getFrame(); | |
906 | |
907 } else { | |
908 | |
909 if (i->haveReferenceFrame()) { | |
910 frame = i->getReferenceFrame(); | |
911 frame = alignFromReference(v, frame); | |
912 } else { | |
913 frame = i->getFrame(); | |
914 } | |
915 } | |
916 | |
917 RegionModel::Point newPoint(frame); | |
918 | |
919 if (i->haveLabel()) newPoint.label = i->getLabel(); | |
920 if (i->haveValue()) newPoint.value = i->getValue(); | |
921 else newPoint.value = (m_model->getValueMinimum() + | |
922 m_model->getValueMaximum()) / 2; | |
923 if (i->haveDuration()) newPoint.duration = i->getDuration(); | |
924 else { | |
925 size_t nextFrame = frame; | |
926 Clipboard::PointList::const_iterator j = i; | |
927 for (; j != points.end(); ++j) { | |
928 if (!j->haveFrame()) continue; | |
929 if (j != i) break; | |
930 } | |
931 if (j != points.end()) { | |
932 nextFrame = j->getFrame(); | |
933 } | |
934 if (nextFrame == frame) { | |
935 newPoint.duration = m_model->getResolution(); | |
936 } else { | |
937 newPoint.duration = nextFrame - frame; | |
938 } | |
939 } | |
940 | |
941 command->addPoint(newPoint); | |
942 } | |
943 | |
944 finish(command); | |
945 return true; | |
946 } | |
947 | |
948 int | |
949 RegionLayer::getDefaultColourHint(bool darkbg, bool &impose) | |
950 { | |
951 impose = false; | |
952 return ColourDatabase::getInstance()->getColourIndex | |
953 (QString(darkbg ? "White" : "Black")); | |
954 } | |
955 | |
956 void | |
957 RegionLayer::toXml(QTextStream &stream, | |
958 QString indent, QString extraAttributes) const | |
959 { | |
960 SingleColourLayer::toXml(stream, indent, extraAttributes + | |
961 QString(" verticalScale=\"%1\"") | |
962 .arg(m_verticalScale)); | |
963 } | |
964 | |
965 void | |
966 RegionLayer::setProperties(const QXmlAttributes &attributes) | |
967 { | |
968 SingleColourLayer::setProperties(attributes); | |
969 | |
970 bool ok; | |
971 VerticalScale scale = (VerticalScale) | |
972 attributes.value("verticalScale").toInt(&ok); | |
973 if (ok) setVerticalScale(scale); | |
974 } | |
975 | |
976 |