Chris@411
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@411
|
2
|
Chris@411
|
3 /*
|
Chris@411
|
4 Sonic Visualiser
|
Chris@411
|
5 An audio file viewer and annotation editor.
|
Chris@411
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@411
|
7 This file copyright 2006-2008 Chris Cannam and QMUL.
|
Chris@411
|
8
|
Chris@411
|
9 This program is free software; you can redistribute it and/or
|
Chris@411
|
10 modify it under the terms of the GNU General Public License as
|
Chris@411
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@411
|
12 License, or (at your option) any later version. See the file
|
Chris@411
|
13 COPYING included with this distribution for more information.
|
Chris@411
|
14 */
|
Chris@411
|
15
|
Chris@411
|
16 #include "RegionLayer.h"
|
Chris@411
|
17
|
Chris@411
|
18 #include "data/model/Model.h"
|
Chris@411
|
19 #include "base/RealTime.h"
|
Chris@411
|
20 #include "base/Profiler.h"
|
Chris@411
|
21 #include "base/LogRange.h"
|
Chris@411
|
22 #include "ColourDatabase.h"
|
Chris@427
|
23 #include "ColourMapper.h"
|
Chris@411
|
24 #include "view/View.h"
|
Chris@411
|
25
|
Chris@411
|
26 #include "data/model/RegionModel.h"
|
Chris@411
|
27
|
Chris@411
|
28 #include "widgets/ItemEditDialog.h"
|
Chris@411
|
29
|
Chris@411
|
30 #include <QPainter>
|
Chris@411
|
31 #include <QPainterPath>
|
Chris@411
|
32 #include <QMouseEvent>
|
Chris@411
|
33 #include <QTextStream>
|
Chris@411
|
34 #include <QMessageBox>
|
Chris@411
|
35
|
Chris@411
|
36 #include <iostream>
|
Chris@411
|
37 #include <cmath>
|
Chris@411
|
38
|
Chris@411
|
39 RegionLayer::RegionLayer() :
|
Chris@411
|
40 SingleColourLayer(),
|
Chris@411
|
41 m_model(0),
|
Chris@411
|
42 m_editing(false),
|
Chris@543
|
43 m_originalPoint(0, 0.0, 0, tr("New Region")),
|
Chris@543
|
44 m_editingPoint(0, 0.0, 0, tr("New Region")),
|
Chris@411
|
45 m_editingCommand(0),
|
Chris@433
|
46 m_verticalScale(EqualSpaced),
|
Chris@427
|
47 m_colourMap(0),
|
Chris@412
|
48 m_plotStyle(PlotLines)
|
Chris@411
|
49 {
|
Chris@411
|
50
|
Chris@411
|
51 }
|
Chris@411
|
52
|
Chris@411
|
53 void
|
Chris@411
|
54 RegionLayer::setModel(RegionModel *model)
|
Chris@411
|
55 {
|
Chris@411
|
56 if (m_model == model) return;
|
Chris@411
|
57 m_model = model;
|
Chris@411
|
58
|
Chris@411
|
59 connectSignals(m_model);
|
Chris@411
|
60
|
Chris@433
|
61 connect(m_model, SIGNAL(modelChanged()), this, SLOT(recalcSpacing()));
|
Chris@433
|
62 recalcSpacing();
|
Chris@433
|
63
|
Chris@587
|
64 // SVDEBUG << "RegionLayer::setModel(" << model << ")" << endl;
|
Chris@411
|
65
|
Chris@610
|
66 if (m_model && m_model->getRDFTypeURI().endsWith("Segment")) {
|
Chris@610
|
67 setPlotStyle(PlotSegmentation);
|
Chris@610
|
68 }
|
Chris@610
|
69 if (m_model && m_model->getRDFTypeURI().endsWith("Change")) {
|
Chris@610
|
70 setPlotStyle(PlotSegmentation);
|
Chris@610
|
71 }
|
Chris@610
|
72
|
Chris@411
|
73 emit modelReplaced();
|
Chris@411
|
74 }
|
Chris@411
|
75
|
Chris@411
|
76 Layer::PropertyList
|
Chris@411
|
77 RegionLayer::getProperties() const
|
Chris@411
|
78 {
|
Chris@411
|
79 PropertyList list = SingleColourLayer::getProperties();
|
Chris@411
|
80 list.push_back("Vertical Scale");
|
Chris@411
|
81 list.push_back("Scale Units");
|
Chris@412
|
82 list.push_back("Plot Type");
|
Chris@411
|
83 return list;
|
Chris@411
|
84 }
|
Chris@411
|
85
|
Chris@411
|
86 QString
|
Chris@411
|
87 RegionLayer::getPropertyLabel(const PropertyName &name) const
|
Chris@411
|
88 {
|
Chris@411
|
89 if (name == "Vertical Scale") return tr("Vertical Scale");
|
Chris@411
|
90 if (name == "Scale Units") return tr("Scale Units");
|
Chris@412
|
91 if (name == "Plot Type") return tr("Plot Type");
|
Chris@411
|
92 return SingleColourLayer::getPropertyLabel(name);
|
Chris@411
|
93 }
|
Chris@411
|
94
|
Chris@411
|
95 Layer::PropertyType
|
Chris@411
|
96 RegionLayer::getPropertyType(const PropertyName &name) const
|
Chris@411
|
97 {
|
Chris@411
|
98 if (name == "Scale Units") return UnitsProperty;
|
Chris@411
|
99 if (name == "Vertical Scale") return ValueProperty;
|
Chris@412
|
100 if (name == "Plot Type") return ValueProperty;
|
Chris@427
|
101 if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty;
|
Chris@411
|
102 return SingleColourLayer::getPropertyType(name);
|
Chris@411
|
103 }
|
Chris@411
|
104
|
Chris@411
|
105 QString
|
Chris@411
|
106 RegionLayer::getPropertyGroupName(const PropertyName &name) const
|
Chris@411
|
107 {
|
Chris@411
|
108 if (name == "Vertical Scale" || name == "Scale Units") {
|
Chris@411
|
109 return tr("Scale");
|
Chris@411
|
110 }
|
Chris@411
|
111 return SingleColourLayer::getPropertyGroupName(name);
|
Chris@411
|
112 }
|
Chris@411
|
113
|
Chris@411
|
114 int
|
Chris@411
|
115 RegionLayer::getPropertyRangeAndValue(const PropertyName &name,
|
Chris@411
|
116 int *min, int *max, int *deflt) const
|
Chris@411
|
117 {
|
Chris@411
|
118 int val = 0;
|
Chris@411
|
119
|
Chris@427
|
120 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
|
Chris@427
|
121
|
Chris@427
|
122 if (min) *min = 0;
|
Chris@427
|
123 if (max) *max = ColourMapper::getColourMapCount() - 1;
|
Chris@427
|
124 if (deflt) *deflt = 0;
|
Chris@427
|
125
|
Chris@427
|
126 val = m_colourMap;
|
Chris@427
|
127
|
Chris@427
|
128 } else if (name == "Plot Type") {
|
Chris@412
|
129
|
Chris@412
|
130 if (min) *min = 0;
|
Chris@412
|
131 if (max) *max = 1;
|
Chris@412
|
132 if (deflt) *deflt = 0;
|
Chris@412
|
133
|
Chris@412
|
134 val = int(m_plotStyle);
|
Chris@412
|
135
|
Chris@412
|
136 } else if (name == "Vertical Scale") {
|
Chris@411
|
137
|
Chris@411
|
138 if (min) *min = 0;
|
Chris@411
|
139 if (max) *max = 3;
|
Chris@433
|
140 if (deflt) *deflt = int(EqualSpaced);
|
Chris@411
|
141
|
Chris@411
|
142 val = int(m_verticalScale);
|
Chris@411
|
143
|
Chris@411
|
144 } else if (name == "Scale Units") {
|
Chris@411
|
145
|
Chris@411
|
146 if (deflt) *deflt = 0;
|
Chris@411
|
147 if (m_model) {
|
Chris@411
|
148 val = UnitDatabase::getInstance()->getUnitId
|
Chris@411
|
149 (m_model->getScaleUnits());
|
Chris@411
|
150 }
|
Chris@411
|
151
|
Chris@411
|
152 } else {
|
Chris@411
|
153
|
Chris@411
|
154 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
|
Chris@411
|
155 }
|
Chris@411
|
156
|
Chris@411
|
157 return val;
|
Chris@411
|
158 }
|
Chris@411
|
159
|
Chris@411
|
160 QString
|
Chris@411
|
161 RegionLayer::getPropertyValueLabel(const PropertyName &name,
|
Chris@427
|
162 int value) const
|
Chris@411
|
163 {
|
Chris@427
|
164 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
|
Chris@427
|
165 return ColourMapper::getColourMapName(value);
|
Chris@427
|
166 } else if (name == "Plot Type") {
|
Chris@412
|
167
|
Chris@412
|
168 switch (value) {
|
Chris@412
|
169 default:
|
Chris@427
|
170 case 0: return tr("Bars");
|
Chris@412
|
171 case 1: return tr("Segmentation");
|
Chris@412
|
172 }
|
Chris@412
|
173
|
Chris@412
|
174 } else if (name == "Vertical Scale") {
|
Chris@411
|
175 switch (value) {
|
Chris@411
|
176 default:
|
Chris@411
|
177 case 0: return tr("Auto-Align");
|
Chris@433
|
178 case 1: return tr("Equal Spaced");
|
Chris@433
|
179 case 2: return tr("Linear");
|
Chris@433
|
180 case 3: return tr("Log");
|
Chris@411
|
181 }
|
Chris@411
|
182 }
|
Chris@411
|
183 return SingleColourLayer::getPropertyValueLabel(name, value);
|
Chris@411
|
184 }
|
Chris@411
|
185
|
Chris@411
|
186 void
|
Chris@411
|
187 RegionLayer::setProperty(const PropertyName &name, int value)
|
Chris@411
|
188 {
|
Chris@427
|
189 if (name == "Colour" && m_plotStyle == PlotSegmentation) {
|
Chris@427
|
190 setFillColourMap(value);
|
Chris@427
|
191 } else if (name == "Plot Type") {
|
Chris@412
|
192 setPlotStyle(PlotStyle(value));
|
Chris@412
|
193 } else if (name == "Vertical Scale") {
|
Chris@411
|
194 setVerticalScale(VerticalScale(value));
|
Chris@411
|
195 } else if (name == "Scale Units") {
|
Chris@411
|
196 if (m_model) {
|
Chris@411
|
197 m_model->setScaleUnits
|
Chris@411
|
198 (UnitDatabase::getInstance()->getUnitById(value));
|
Chris@411
|
199 emit modelChanged();
|
Chris@411
|
200 }
|
Chris@411
|
201 } else {
|
Chris@411
|
202 return SingleColourLayer::setProperty(name, value);
|
Chris@411
|
203 }
|
Chris@411
|
204 }
|
Chris@411
|
205
|
Chris@411
|
206 void
|
Chris@427
|
207 RegionLayer::setFillColourMap(int map)
|
Chris@427
|
208 {
|
Chris@427
|
209 if (m_colourMap == map) return;
|
Chris@427
|
210 m_colourMap = map;
|
Chris@427
|
211 emit layerParametersChanged();
|
Chris@427
|
212 }
|
Chris@427
|
213
|
Chris@427
|
214 void
|
Chris@412
|
215 RegionLayer::setPlotStyle(PlotStyle style)
|
Chris@412
|
216 {
|
Chris@412
|
217 if (m_plotStyle == style) return;
|
Chris@427
|
218 bool colourTypeChanged = (style == PlotSegmentation ||
|
Chris@427
|
219 m_plotStyle == PlotSegmentation);
|
Chris@412
|
220 m_plotStyle = style;
|
Chris@427
|
221 if (colourTypeChanged) {
|
Chris@427
|
222 emit layerParameterRangesChanged();
|
Chris@427
|
223 }
|
Chris@412
|
224 emit layerParametersChanged();
|
Chris@412
|
225 }
|
Chris@412
|
226
|
Chris@412
|
227 void
|
Chris@411
|
228 RegionLayer::setVerticalScale(VerticalScale scale)
|
Chris@411
|
229 {
|
Chris@411
|
230 if (m_verticalScale == scale) return;
|
Chris@411
|
231 m_verticalScale = scale;
|
Chris@411
|
232 emit layerParametersChanged();
|
Chris@411
|
233 }
|
Chris@411
|
234
|
Chris@411
|
235 bool
|
Chris@411
|
236 RegionLayer::isLayerScrollable(const View *v) const
|
Chris@411
|
237 {
|
Chris@411
|
238 QPoint discard;
|
Chris@411
|
239 return !v->shouldIlluminateLocalFeatures(this, discard);
|
Chris@411
|
240 }
|
Chris@411
|
241
|
Chris@433
|
242 void
|
Chris@433
|
243 RegionLayer::recalcSpacing()
|
Chris@433
|
244 {
|
Chris@433
|
245 m_spacingMap.clear();
|
Chris@551
|
246 m_distributionMap.clear();
|
Chris@433
|
247 if (!m_model) return;
|
Chris@433
|
248
|
Chris@587
|
249 // SVDEBUG << "RegionLayer::recalcSpacing" << endl;
|
Chris@433
|
250
|
Chris@433
|
251 for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin();
|
Chris@433
|
252 i != m_model->getPoints().end(); ++i) {
|
Chris@551
|
253 m_distributionMap[i->value]++;
|
Chris@587
|
254 // SVDEBUG << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" << endl;
|
Chris@433
|
255 }
|
Chris@433
|
256
|
Chris@433
|
257 int n = 0;
|
Chris@433
|
258
|
Chris@551
|
259 for (SpacingMap::const_iterator i = m_distributionMap.begin();
|
Chris@551
|
260 i != m_distributionMap.end(); ++i) {
|
Chris@551
|
261 m_spacingMap[i->first] = n++;
|
Chris@587
|
262 // SVDEBUG << "RegionLayer::recalcSpacing: " << i->first << " -> " << m_spacingMap[i->first] << endl;
|
Chris@433
|
263 }
|
Chris@433
|
264 }
|
Chris@433
|
265
|
Chris@411
|
266 bool
|
Chris@411
|
267 RegionLayer::getValueExtents(float &min, float &max,
|
Chris@411
|
268 bool &logarithmic, QString &unit) const
|
Chris@411
|
269 {
|
Chris@411
|
270 if (!m_model) return false;
|
Chris@411
|
271 min = m_model->getValueMinimum();
|
Chris@411
|
272 max = m_model->getValueMaximum();
|
Chris@411
|
273 unit = m_model->getScaleUnits();
|
Chris@411
|
274
|
Chris@411
|
275 if (m_verticalScale == LogScale) logarithmic = true;
|
Chris@411
|
276
|
Chris@411
|
277 return true;
|
Chris@411
|
278 }
|
Chris@411
|
279
|
Chris@411
|
280 bool
|
Chris@411
|
281 RegionLayer::getDisplayExtents(float &min, float &max) const
|
Chris@411
|
282 {
|
Chris@433
|
283 if (!m_model ||
|
Chris@433
|
284 m_verticalScale == AutoAlignScale ||
|
Chris@433
|
285 m_verticalScale == EqualSpaced) return false;
|
Chris@411
|
286
|
Chris@411
|
287 min = m_model->getValueMinimum();
|
Chris@411
|
288 max = m_model->getValueMaximum();
|
Chris@411
|
289
|
Chris@411
|
290 return true;
|
Chris@411
|
291 }
|
Chris@411
|
292
|
Chris@411
|
293 RegionModel::PointList
|
Chris@411
|
294 RegionLayer::getLocalPoints(View *v, int x) const
|
Chris@411
|
295 {
|
Chris@411
|
296 if (!m_model) return RegionModel::PointList();
|
Chris@411
|
297
|
Chris@411
|
298 long frame = v->getFrameForX(x);
|
Chris@411
|
299
|
Chris@411
|
300 RegionModel::PointList onPoints =
|
Chris@411
|
301 m_model->getPoints(frame);
|
Chris@411
|
302
|
Chris@411
|
303 if (!onPoints.empty()) {
|
Chris@411
|
304 return onPoints;
|
Chris@411
|
305 }
|
Chris@411
|
306
|
Chris@411
|
307 RegionModel::PointList prevPoints =
|
Chris@411
|
308 m_model->getPreviousPoints(frame);
|
Chris@411
|
309 RegionModel::PointList nextPoints =
|
Chris@411
|
310 m_model->getNextPoints(frame);
|
Chris@411
|
311
|
Chris@411
|
312 RegionModel::PointList usePoints = prevPoints;
|
Chris@411
|
313
|
Chris@411
|
314 if (prevPoints.empty()) {
|
Chris@411
|
315 usePoints = nextPoints;
|
Chris@411
|
316 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
|
Chris@411
|
317 !(nextPoints.begin()->frame > v->getEndFrame())) {
|
Chris@411
|
318 usePoints = nextPoints;
|
Chris@411
|
319 } else if (long(nextPoints.begin()->frame) - frame <
|
Chris@411
|
320 frame - long(prevPoints.begin()->frame)) {
|
Chris@411
|
321 usePoints = nextPoints;
|
Chris@411
|
322 }
|
Chris@411
|
323
|
Chris@411
|
324 if (!usePoints.empty()) {
|
Chris@411
|
325 int fuzz = 2;
|
Chris@411
|
326 int px = v->getXForFrame(usePoints.begin()->frame);
|
Chris@411
|
327 if ((px > x && px - x > fuzz) ||
|
Chris@411
|
328 (px < x && x - px > fuzz + 1)) {
|
Chris@411
|
329 usePoints.clear();
|
Chris@411
|
330 }
|
Chris@411
|
331 }
|
Chris@411
|
332
|
Chris@411
|
333 return usePoints;
|
Chris@411
|
334 }
|
Chris@411
|
335
|
Chris@550
|
336 bool
|
Chris@550
|
337 RegionLayer::getPointToDrag(View *v, int x, int y, RegionModel::Point &p) const
|
Chris@550
|
338 {
|
Chris@550
|
339 if (!m_model) return false;
|
Chris@550
|
340
|
Chris@550
|
341 long frame = v->getFrameForX(x);
|
Chris@550
|
342
|
Chris@550
|
343 RegionModel::PointList onPoints = m_model->getPoints(frame);
|
Chris@550
|
344 if (onPoints.empty()) return false;
|
Chris@550
|
345
|
Chris@550
|
346 int nearestDistance = -1;
|
Chris@550
|
347
|
Chris@550
|
348 for (RegionModel::PointList::const_iterator i = onPoints.begin();
|
Chris@550
|
349 i != onPoints.end(); ++i) {
|
Chris@550
|
350
|
Chris@550
|
351 int distance = getYForValue(v, (*i).value) - y;
|
Chris@550
|
352 if (distance < 0) distance = -distance;
|
Chris@550
|
353 if (nearestDistance == -1 || distance < nearestDistance) {
|
Chris@550
|
354 nearestDistance = distance;
|
Chris@550
|
355 p = *i;
|
Chris@550
|
356 }
|
Chris@550
|
357 }
|
Chris@550
|
358
|
Chris@550
|
359 return true;
|
Chris@550
|
360 }
|
Chris@550
|
361
|
Chris@411
|
362 QString
|
Chris@552
|
363 RegionLayer::getLabelPreceding(size_t frame) const
|
Chris@552
|
364 {
|
Chris@552
|
365 if (!m_model) return "";
|
Chris@552
|
366 RegionModel::PointList points = m_model->getPreviousPoints(frame);
|
Chris@552
|
367 for (RegionModel::PointList::const_iterator i = points.begin();
|
Chris@552
|
368 i != points.end(); ++i) {
|
Chris@552
|
369 if (i->label != "") return i->label;
|
Chris@552
|
370 }
|
Chris@552
|
371 return "";
|
Chris@552
|
372 }
|
Chris@552
|
373
|
Chris@552
|
374 QString
|
Chris@411
|
375 RegionLayer::getFeatureDescription(View *v, QPoint &pos) const
|
Chris@411
|
376 {
|
Chris@411
|
377 int x = pos.x();
|
Chris@411
|
378
|
Chris@411
|
379 if (!m_model || !m_model->getSampleRate()) return "";
|
Chris@411
|
380
|
Chris@411
|
381 RegionModel::PointList points = getLocalPoints(v, x);
|
Chris@411
|
382
|
Chris@411
|
383 if (points.empty()) {
|
Chris@411
|
384 if (!m_model->isReady()) {
|
Chris@411
|
385 return tr("In progress");
|
Chris@411
|
386 } else {
|
Chris@411
|
387 return tr("No local points");
|
Chris@411
|
388 }
|
Chris@411
|
389 }
|
Chris@411
|
390
|
Chris@411
|
391 RegionRec region(0);
|
Chris@411
|
392 RegionModel::PointList::iterator i;
|
Chris@411
|
393
|
Chris@413
|
394 //!!! harmonise with whatever decision is made about point y
|
Chris@413
|
395 //!!! coords in paint method
|
Chris@413
|
396
|
Chris@411
|
397 for (i = points.begin(); i != points.end(); ++i) {
|
Chris@411
|
398
|
Chris@411
|
399 int y = getYForValue(v, i->value);
|
Chris@411
|
400 int h = 3;
|
Chris@411
|
401
|
Chris@411
|
402 if (m_model->getValueQuantization() != 0.0) {
|
Chris@411
|
403 h = y - getYForValue(v, i->value + m_model->getValueQuantization());
|
Chris@411
|
404 if (h < 3) h = 3;
|
Chris@411
|
405 }
|
Chris@411
|
406
|
Chris@411
|
407 if (pos.y() >= y - h && pos.y() <= y) {
|
Chris@411
|
408 region = *i;
|
Chris@411
|
409 break;
|
Chris@411
|
410 }
|
Chris@411
|
411 }
|
Chris@411
|
412
|
Chris@411
|
413 if (i == points.end()) return tr("No local points");
|
Chris@411
|
414
|
Chris@411
|
415 RealTime rt = RealTime::frame2RealTime(region.frame,
|
Chris@411
|
416 m_model->getSampleRate());
|
Chris@411
|
417 RealTime rd = RealTime::frame2RealTime(region.duration,
|
Chris@411
|
418 m_model->getSampleRate());
|
Chris@411
|
419
|
Chris@411
|
420 QString valueText;
|
Chris@411
|
421
|
Chris@411
|
422 valueText = tr("%1 %2").arg(region.value).arg(m_model->getScaleUnits());
|
Chris@411
|
423
|
Chris@411
|
424 QString text;
|
Chris@411
|
425
|
Chris@411
|
426 if (region.label == "") {
|
Chris@411
|
427 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label"))
|
Chris@411
|
428 .arg(rt.toText(true).c_str())
|
Chris@411
|
429 .arg(valueText)
|
Chris@411
|
430 .arg(rd.toText(true).c_str());
|
Chris@411
|
431 } else {
|
Chris@411
|
432 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4"))
|
Chris@411
|
433 .arg(rt.toText(true).c_str())
|
Chris@411
|
434 .arg(valueText)
|
Chris@411
|
435 .arg(rd.toText(true).c_str())
|
Chris@411
|
436 .arg(region.label);
|
Chris@411
|
437 }
|
Chris@411
|
438
|
Chris@411
|
439 pos = QPoint(v->getXForFrame(region.frame),
|
Chris@411
|
440 getYForValue(v, region.value));
|
Chris@411
|
441 return text;
|
Chris@411
|
442 }
|
Chris@411
|
443
|
Chris@411
|
444 bool
|
Chris@411
|
445 RegionLayer::snapToFeatureFrame(View *v, int &frame,
|
Chris@414
|
446 size_t &resolution,
|
Chris@414
|
447 SnapType snap) const
|
Chris@411
|
448 {
|
Chris@411
|
449 if (!m_model) {
|
Chris@411
|
450 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
|
Chris@411
|
451 }
|
Chris@411
|
452
|
Chris@411
|
453 resolution = m_model->getResolution();
|
Chris@411
|
454 RegionModel::PointList points;
|
Chris@411
|
455
|
Chris@411
|
456 if (snap == SnapNeighbouring) {
|
Chris@411
|
457
|
Chris@411
|
458 points = getLocalPoints(v, v->getXForFrame(frame));
|
Chris@411
|
459 if (points.empty()) return false;
|
Chris@411
|
460 frame = points.begin()->frame;
|
Chris@411
|
461 return true;
|
Chris@411
|
462 }
|
Chris@411
|
463
|
Chris@411
|
464 points = m_model->getPoints(frame, frame);
|
Chris@411
|
465 int snapped = frame;
|
Chris@411
|
466 bool found = false;
|
Chris@411
|
467
|
Chris@411
|
468 for (RegionModel::PointList::const_iterator i = points.begin();
|
Chris@411
|
469 i != points.end(); ++i) {
|
Chris@411
|
470
|
Chris@411
|
471 if (snap == SnapRight) {
|
Chris@411
|
472
|
Chris@414
|
473 // The best frame to snap to is the end frame of whichever
|
Chris@414
|
474 // feature we would have snapped to the start frame of if
|
Chris@414
|
475 // we had been snapping left.
|
Chris@414
|
476
|
Chris@414
|
477 if (i->frame <= frame) {
|
Chris@414
|
478 if (i->frame + i->duration > frame) {
|
Chris@414
|
479 snapped = i->frame + i->duration;
|
Chris@414
|
480 found = true; // don't break, as the next may be better
|
Chris@414
|
481 }
|
Chris@414
|
482 } else {
|
Chris@414
|
483 if (!found) {
|
Chris@414
|
484 snapped = i->frame;
|
Chris@414
|
485 found = true;
|
Chris@414
|
486 }
|
Chris@414
|
487 break;
|
Chris@414
|
488 }
|
Chris@411
|
489
|
Chris@411
|
490 } else if (snap == SnapLeft) {
|
Chris@411
|
491
|
Chris@411
|
492 if (i->frame <= frame) {
|
Chris@411
|
493 snapped = i->frame;
|
Chris@411
|
494 found = true; // don't break, as the next may be better
|
Chris@411
|
495 } else {
|
Chris@411
|
496 break;
|
Chris@411
|
497 }
|
Chris@411
|
498
|
Chris@411
|
499 } else { // nearest
|
Chris@411
|
500
|
Chris@411
|
501 RegionModel::PointList::const_iterator j = i;
|
Chris@411
|
502 ++j;
|
Chris@411
|
503
|
Chris@411
|
504 if (j == points.end()) {
|
Chris@411
|
505
|
Chris@411
|
506 snapped = i->frame;
|
Chris@411
|
507 found = true;
|
Chris@411
|
508 break;
|
Chris@411
|
509
|
Chris@411
|
510 } else if (j->frame >= frame) {
|
Chris@411
|
511
|
Chris@411
|
512 if (j->frame - frame < frame - i->frame) {
|
Chris@411
|
513 snapped = j->frame;
|
Chris@411
|
514 } else {
|
Chris@411
|
515 snapped = i->frame;
|
Chris@411
|
516 }
|
Chris@411
|
517 found = true;
|
Chris@411
|
518 break;
|
Chris@411
|
519 }
|
Chris@411
|
520 }
|
Chris@411
|
521 }
|
Chris@411
|
522
|
Chris@411
|
523 frame = snapped;
|
Chris@411
|
524 return found;
|
Chris@411
|
525 }
|
Chris@411
|
526
|
Chris@559
|
527 bool
|
Chris@559
|
528 RegionLayer::snapToSimilarFeature(View *v, int &frame,
|
Chris@559
|
529 size_t &resolution,
|
Chris@559
|
530 SnapType snap) const
|
Chris@559
|
531 {
|
Chris@559
|
532 if (!m_model) {
|
Chris@559
|
533 return Layer::snapToSimilarFeature(v, frame, resolution, snap);
|
Chris@559
|
534 }
|
Chris@559
|
535
|
Chris@559
|
536 resolution = m_model->getResolution();
|
Chris@559
|
537
|
Chris@559
|
538 const RegionModel::PointList &points = m_model->getPoints();
|
Chris@559
|
539 RegionModel::PointList close = m_model->getPoints(frame, frame);
|
Chris@559
|
540
|
Chris@559
|
541 RegionModel::PointList::const_iterator i;
|
Chris@559
|
542
|
Chris@559
|
543 int matchframe = frame;
|
Chris@559
|
544 float matchvalue = 0.f;
|
Chris@559
|
545
|
Chris@559
|
546 for (i = close.begin(); i != close.end(); ++i) {
|
Chris@559
|
547 if (i->frame > frame) break;
|
Chris@559
|
548 matchvalue = i->value;
|
Chris@559
|
549 matchframe = i->frame;
|
Chris@559
|
550 }
|
Chris@559
|
551
|
Chris@559
|
552 int snapped = frame;
|
Chris@559
|
553 bool found = false;
|
Chris@559
|
554 bool distant = false;
|
Chris@559
|
555 float epsilon = 0.0001;
|
Chris@559
|
556
|
Chris@559
|
557 i = close.begin();
|
Chris@559
|
558
|
Chris@559
|
559 // Scan through the close points first, then the more distant ones
|
Chris@559
|
560 // if no suitable close one is found
|
Chris@559
|
561
|
Chris@559
|
562 while (i != points.end()) {
|
Chris@559
|
563
|
Chris@559
|
564 if (i == close.end()) {
|
Chris@559
|
565 i = points.begin();
|
Chris@559
|
566 distant = true;
|
Chris@559
|
567 }
|
Chris@559
|
568
|
Chris@559
|
569 if (snap == SnapRight) {
|
Chris@559
|
570
|
Chris@559
|
571 if (i->frame > matchframe &&
|
Chris@559
|
572 fabsf(i->value - matchvalue) < epsilon) {
|
Chris@559
|
573 snapped = i->frame;
|
Chris@559
|
574 found = true;
|
Chris@559
|
575 break;
|
Chris@559
|
576 }
|
Chris@559
|
577
|
Chris@559
|
578 } else if (snap == SnapLeft) {
|
Chris@559
|
579
|
Chris@559
|
580 if (i->frame < matchframe) {
|
Chris@559
|
581 if (fabsf(i->value - matchvalue) < epsilon) {
|
Chris@559
|
582 snapped = i->frame;
|
Chris@559
|
583 found = true; // don't break, as the next may be better
|
Chris@559
|
584 }
|
Chris@559
|
585 } else if (found || distant) {
|
Chris@559
|
586 break;
|
Chris@559
|
587 }
|
Chris@559
|
588
|
Chris@559
|
589 } else {
|
Chris@559
|
590 // no other snap types supported
|
Chris@559
|
591 }
|
Chris@559
|
592
|
Chris@559
|
593 ++i;
|
Chris@559
|
594 }
|
Chris@559
|
595
|
Chris@559
|
596 frame = snapped;
|
Chris@559
|
597 return found;
|
Chris@559
|
598 }
|
Chris@559
|
599
|
Chris@411
|
600 void
|
Chris@411
|
601 RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
|
Chris@411
|
602 {
|
Chris@411
|
603 min = 0.0;
|
Chris@411
|
604 max = 0.0;
|
Chris@411
|
605 log = false;
|
Chris@411
|
606
|
Chris@411
|
607 QString queryUnits;
|
Chris@411
|
608 queryUnits = m_model->getScaleUnits();
|
Chris@411
|
609
|
Chris@411
|
610 if (m_verticalScale == AutoAlignScale) {
|
Chris@411
|
611
|
Chris@411
|
612 if (!v->getValueExtents(queryUnits, min, max, log)) {
|
Chris@411
|
613
|
Chris@411
|
614 min = m_model->getValueMinimum();
|
Chris@411
|
615 max = m_model->getValueMaximum();
|
Chris@411
|
616
|
Chris@682
|
617 // cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
|
Chris@411
|
618
|
Chris@411
|
619 } else if (log) {
|
Chris@411
|
620
|
Chris@411
|
621 LogRange::mapRange(min, max);
|
Chris@411
|
622
|
Chris@682
|
623 // cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
|
Chris@411
|
624
|
Chris@411
|
625 }
|
Chris@411
|
626
|
Chris@433
|
627 } else if (m_verticalScale == EqualSpaced) {
|
Chris@433
|
628
|
Chris@433
|
629 if (!m_spacingMap.empty()) {
|
Chris@433
|
630 SpacingMap::const_iterator i = m_spacingMap.begin();
|
Chris@433
|
631 min = i->second;
|
Chris@433
|
632 i = m_spacingMap.end();
|
Chris@433
|
633 --i;
|
Chris@433
|
634 max = i->second;
|
Chris@682
|
635 // cerr << "RegionLayer[" << this << "]::getScaleExtents: equal spaced; min = " << min << ", max = " << max << ", log = " << log << endl;
|
Chris@433
|
636 }
|
Chris@433
|
637
|
Chris@411
|
638 } else {
|
Chris@411
|
639
|
Chris@411
|
640 min = m_model->getValueMinimum();
|
Chris@411
|
641 max = m_model->getValueMaximum();
|
Chris@411
|
642
|
Chris@411
|
643 if (m_verticalScale == LogScale) {
|
Chris@411
|
644 LogRange::mapRange(min, max);
|
Chris@411
|
645 log = true;
|
Chris@411
|
646 }
|
Chris@411
|
647 }
|
Chris@411
|
648
|
Chris@411
|
649 if (max == min) max = min + 1.0;
|
Chris@411
|
650 }
|
Chris@411
|
651
|
Chris@411
|
652 int
|
Chris@542
|
653 RegionLayer::spacingIndexToY(View *v, int i) const
|
Chris@542
|
654 {
|
Chris@542
|
655 int h = v->height();
|
Chris@542
|
656 int n = m_spacingMap.size();
|
Chris@542
|
657 // this maps from i (spacing of the value from the spacing
|
Chris@542
|
658 // map) and n (number of region types) to y
|
Chris@542
|
659 int y = h - (((h * i) / n) + (h / (2 * n)));
|
Chris@542
|
660 return y;
|
Chris@542
|
661 }
|
Chris@542
|
662
|
Chris@542
|
663 float
|
Chris@542
|
664 RegionLayer::yToSpacingIndex(View *v, int y) const
|
Chris@542
|
665 {
|
Chris@542
|
666 // we return an inexact result here (float rather than int)
|
Chris@542
|
667 int h = v->height();
|
Chris@542
|
668 int n = m_spacingMap.size();
|
Chris@551
|
669 // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i)
|
Chris@551
|
670 float vh = float(2*h*n - h - 2*n*y) / float(2*h);
|
Chris@542
|
671 return vh;
|
Chris@542
|
672 }
|
Chris@542
|
673
|
Chris@542
|
674 int
|
Chris@411
|
675 RegionLayer::getYForValue(View *v, float val) const
|
Chris@411
|
676 {
|
Chris@411
|
677 float min = 0.0, max = 0.0;
|
Chris@411
|
678 bool logarithmic = false;
|
Chris@411
|
679 int h = v->height();
|
Chris@411
|
680
|
Chris@433
|
681 if (m_verticalScale == EqualSpaced) {
|
Chris@433
|
682
|
Chris@433
|
683 if (m_spacingMap.empty()) return h/2;
|
Chris@433
|
684
|
Chris@433
|
685 SpacingMap::const_iterator i = m_spacingMap.lower_bound(val);
|
Chris@433
|
686 //!!! what now, if i->first != v?
|
Chris@433
|
687
|
Chris@542
|
688 int y = spacingIndexToY(v, i->second);
|
Chris@433
|
689
|
Chris@587
|
690 // SVDEBUG << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << endl;
|
Chris@542
|
691 return y;
|
Chris@433
|
692
|
Chris@433
|
693
|
Chris@433
|
694 } else {
|
Chris@433
|
695
|
Chris@433
|
696 getScaleExtents(v, min, max, logarithmic);
|
Chris@411
|
697
|
Chris@682
|
698 // cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
|
Chris@682
|
699 // cerr << "h = " << h << ", margin = " << margin << endl;
|
Chris@411
|
700
|
Chris@433
|
701 if (logarithmic) {
|
Chris@433
|
702 val = LogRange::map(val);
|
Chris@433
|
703 }
|
Chris@433
|
704
|
Chris@433
|
705 return int(h - ((val - min) * h) / (max - min));
|
Chris@411
|
706 }
|
Chris@411
|
707 }
|
Chris@411
|
708
|
Chris@542
|
709 float
|
Chris@551
|
710 RegionLayer::getValueForY(View *v, int y, int avoid) const
|
Chris@542
|
711 {
|
Chris@542
|
712 float min = 0.0, max = 0.0;
|
Chris@542
|
713 bool logarithmic = false;
|
Chris@542
|
714 int h = v->height();
|
Chris@542
|
715
|
Chris@542
|
716 if (m_verticalScale == EqualSpaced) {
|
Chris@542
|
717
|
Chris@542
|
718 // if we're equal spaced, we probably want to snap to the
|
Chris@542
|
719 // nearest item when close to it, and give some notification
|
Chris@542
|
720 // that we're doing so
|
Chris@542
|
721
|
Chris@542
|
722 if (m_spacingMap.empty()) return 1.f;
|
Chris@542
|
723
|
Chris@542
|
724 // n is the number of distinct regions. if we are close to
|
Chris@542
|
725 // one of the m/n divisions in the y scale, we should snap to
|
Chris@542
|
726 // the value of the mth region.
|
Chris@542
|
727
|
Chris@542
|
728 float vh = yToSpacingIndex(v, y);
|
Chris@542
|
729
|
Chris@542
|
730 // spacings in the map are integral, so find the closest one,
|
Chris@542
|
731 // map it back to its y coordinate, and see how far we are
|
Chris@542
|
732 // from it
|
Chris@542
|
733
|
Chris@542
|
734 int n = m_spacingMap.size();
|
Chris@542
|
735 int ivh = lrintf(vh);
|
Chris@542
|
736 if (ivh < 0) ivh = 0;
|
Chris@542
|
737 if (ivh > n-1) ivh = n-1;
|
Chris@542
|
738 int iy = spacingIndexToY(v, ivh);
|
Chris@542
|
739
|
Chris@542
|
740 int dist = iy - y;
|
Chris@542
|
741 int gap = h / n; // between region lines
|
Chris@542
|
742
|
Chris@682
|
743 // cerr << "getValueForY: y = " << y << ", vh = " << vh << ", ivh = " << ivh << " of " << n << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << endl;
|
Chris@542
|
744
|
Chris@542
|
745 SpacingMap::const_iterator i = m_spacingMap.begin();
|
Chris@542
|
746 while (i != m_spacingMap.end()) {
|
Chris@542
|
747 if (i->second == ivh) break;
|
Chris@542
|
748 ++i;
|
Chris@542
|
749 }
|
Chris@542
|
750 if (i == m_spacingMap.end()) i = m_spacingMap.begin();
|
Chris@542
|
751
|
Chris@682
|
752 // cerr << "nearest existing value = " << i->first << " at " << iy << endl;
|
Chris@551
|
753
|
Chris@542
|
754 float val = 0;
|
Chris@542
|
755
|
Chris@682
|
756 // cerr << "note: avoid = " << avoid << ", i->second = " << i->second << endl;
|
Chris@551
|
757
|
Chris@551
|
758 if (dist < -gap/3 &&
|
Chris@551
|
759 ((avoid == -1) ||
|
Chris@551
|
760 (avoid != i->second && avoid != i->second - 1))) {
|
Chris@542
|
761 // bisect gap to prior
|
Chris@542
|
762 if (i == m_spacingMap.begin()) {
|
Chris@542
|
763 val = i->first - 1.f;
|
Chris@682
|
764 // cerr << "extended down to " << val << endl;
|
Chris@542
|
765 } else {
|
Chris@542
|
766 SpacingMap::const_iterator j = i;
|
Chris@542
|
767 --j;
|
Chris@542
|
768 val = (i->first + j->first) / 2;
|
Chris@682
|
769 // cerr << "bisected down to " << val << endl;
|
Chris@542
|
770 }
|
Chris@551
|
771 } else if (dist > gap/3 &&
|
Chris@551
|
772 ((avoid == -1) ||
|
Chris@551
|
773 (avoid != i->second && avoid != i->second + 1))) {
|
Chris@542
|
774 // bisect gap to following
|
Chris@542
|
775 SpacingMap::const_iterator j = i;
|
Chris@542
|
776 ++j;
|
Chris@542
|
777 if (j == m_spacingMap.end()) {
|
Chris@542
|
778 val = i->first + 1.f;
|
Chris@682
|
779 // cerr << "extended up to " << val << endl;
|
Chris@542
|
780 } else {
|
Chris@542
|
781 val = (i->first + j->first) / 2;
|
Chris@682
|
782 // cerr << "bisected up to " << val << endl;
|
Chris@542
|
783 }
|
Chris@551
|
784 } else {
|
Chris@551
|
785 // snap
|
Chris@551
|
786 val = i->first;
|
Chris@682
|
787 // cerr << "snapped to " << val << endl;
|
Chris@542
|
788 }
|
Chris@542
|
789
|
Chris@542
|
790 return val;
|
Chris@542
|
791
|
Chris@542
|
792 } else {
|
Chris@542
|
793
|
Chris@542
|
794 getScaleExtents(v, min, max, logarithmic);
|
Chris@542
|
795
|
Chris@542
|
796 float val = min + (float(h - y) * float(max - min)) / h;
|
Chris@542
|
797
|
Chris@542
|
798 if (logarithmic) {
|
Chris@542
|
799 val = powf(10.f, val);
|
Chris@542
|
800 }
|
Chris@542
|
801
|
Chris@542
|
802 return val;
|
Chris@542
|
803 }
|
Chris@542
|
804 }
|
Chris@542
|
805
|
Chris@427
|
806 QColor
|
Chris@427
|
807 RegionLayer::getColourForValue(View *v, float val) const
|
Chris@427
|
808 {
|
Chris@427
|
809 float min, max;
|
Chris@427
|
810 bool log;
|
Chris@427
|
811 getScaleExtents(v, min, max, log);
|
Chris@427
|
812
|
Chris@427
|
813 if (min > max) std::swap(min, max);
|
Chris@427
|
814 if (max == min) max = min + 1;
|
Chris@427
|
815
|
Chris@427
|
816 if (log) {
|
Chris@427
|
817 LogRange::mapRange(min, max);
|
Chris@427
|
818 val = LogRange::map(val);
|
Chris@427
|
819 }
|
Chris@427
|
820
|
Chris@587
|
821 // SVDEBUG << "RegionLayer::getColourForValue: min " << min << ", max "
|
Chris@585
|
822 // << max << ", log " << log << ", value " << val << endl;
|
Chris@427
|
823
|
Chris@427
|
824 QColor solid = ColourMapper(m_colourMap, min, max).map(val);
|
Chris@427
|
825 return QColor(solid.red(), solid.green(), solid.blue(), 120);
|
Chris@427
|
826 }
|
Chris@427
|
827
|
Chris@427
|
828 int
|
Chris@427
|
829 RegionLayer::getDefaultColourHint(bool darkbg, bool &impose)
|
Chris@427
|
830 {
|
Chris@427
|
831 impose = false;
|
Chris@427
|
832 return ColourDatabase::getInstance()->getColourIndex
|
Chris@427
|
833 (QString(darkbg ? "Bright Blue" : "Blue"));
|
Chris@427
|
834 }
|
Chris@427
|
835
|
Chris@411
|
836 void
|
Chris@411
|
837 RegionLayer::paint(View *v, QPainter &paint, QRect rect) const
|
Chris@411
|
838 {
|
Chris@411
|
839 if (!m_model || !m_model->isOK()) return;
|
Chris@411
|
840
|
Chris@411
|
841 int sampleRate = m_model->getSampleRate();
|
Chris@411
|
842 if (!sampleRate) return;
|
Chris@411
|
843
|
Chris@411
|
844 // Profiler profiler("RegionLayer::paint", true);
|
Chris@411
|
845
|
Chris@552
|
846 int x0 = rect.left() - 40, x1 = rect.right();
|
Chris@411
|
847 long frame0 = v->getFrameForX(x0);
|
Chris@411
|
848 long frame1 = v->getFrameForX(x1);
|
Chris@411
|
849
|
Chris@411
|
850 RegionModel::PointList points(m_model->getPoints(frame0, frame1));
|
Chris@411
|
851 if (points.empty()) return;
|
Chris@411
|
852
|
Chris@411
|
853 paint.setPen(getBaseQColor());
|
Chris@411
|
854
|
Chris@411
|
855 QColor brushColour(getBaseQColor());
|
Chris@411
|
856 brushColour.setAlpha(80);
|
Chris@411
|
857
|
Chris@587
|
858 // SVDEBUG << "RegionLayer::paint: resolution is "
|
Chris@585
|
859 // << m_model->getResolution() << " frames" << endl;
|
Chris@411
|
860
|
Chris@411
|
861 float min = m_model->getValueMinimum();
|
Chris@411
|
862 float max = m_model->getValueMaximum();
|
Chris@411
|
863 if (max == min) max = min + 1.0;
|
Chris@411
|
864
|
Chris@411
|
865 QPoint localPos;
|
Chris@551
|
866 RegionModel::Point illuminatePoint(0);
|
Chris@551
|
867 bool shouldIlluminate = false;
|
Chris@411
|
868
|
Chris@411
|
869 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
|
Chris@551
|
870 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
|
Chris@551
|
871 illuminatePoint);
|
Chris@411
|
872 }
|
Chris@411
|
873
|
Chris@411
|
874 paint.save();
|
Chris@411
|
875 paint.setRenderHint(QPainter::Antialiasing, false);
|
Chris@411
|
876
|
Chris@413
|
877 //!!! point y coords if model does not haveDistinctValues() should
|
Chris@413
|
878 //!!! be assigned to avoid overlaps
|
Chris@413
|
879
|
Chris@413
|
880 //!!! if it does have distinct values, we should still ensure y
|
Chris@413
|
881 //!!! coord is never completely flat on the top or bottom
|
Chris@413
|
882
|
Chris@550
|
883 int fontHeight = paint.fontMetrics().height();
|
Chris@550
|
884 int fontAscent = paint.fontMetrics().ascent();
|
Chris@550
|
885
|
Chris@411
|
886 for (RegionModel::PointList::const_iterator i = points.begin();
|
Chris@411
|
887 i != points.end(); ++i) {
|
Chris@411
|
888
|
Chris@411
|
889 const RegionModel::Point &p(*i);
|
Chris@411
|
890
|
Chris@411
|
891 int x = v->getXForFrame(p.frame);
|
Chris@411
|
892 int y = getYForValue(v, p.value);
|
Chris@411
|
893 int w = v->getXForFrame(p.frame + p.duration) - x;
|
Chris@413
|
894 int h = 9;
|
Chris@433
|
895 int ex = x + w;
|
Chris@427
|
896
|
Chris@427
|
897 RegionModel::PointList::const_iterator j = i;
|
Chris@427
|
898 ++j;
|
Chris@427
|
899
|
Chris@427
|
900 if (j != points.end()) {
|
Chris@427
|
901 const RegionModel::Point &q(*j);
|
Chris@433
|
902 int nx = v->getXForFrame(q.frame);
|
Chris@433
|
903 if (nx < ex) ex = nx;
|
Chris@427
|
904 }
|
Chris@427
|
905
|
Chris@411
|
906 if (m_model->getValueQuantization() != 0.0) {
|
Chris@411
|
907 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
|
Chris@411
|
908 if (h < 3) h = 3;
|
Chris@411
|
909 }
|
Chris@411
|
910
|
Chris@411
|
911 if (w < 1) w = 1;
|
Chris@411
|
912
|
Chris@427
|
913 if (m_plotStyle == PlotSegmentation) {
|
Chris@427
|
914 paint.setPen(getForegroundQColor(v));
|
Chris@427
|
915 paint.setBrush(getColourForValue(v, p.value));
|
Chris@427
|
916 } else {
|
Chris@427
|
917 paint.setPen(getBaseQColor());
|
Chris@427
|
918 paint.setBrush(brushColour);
|
Chris@427
|
919 }
|
Chris@427
|
920
|
Chris@427
|
921 if (m_plotStyle == PlotSegmentation) {
|
Chris@427
|
922
|
Chris@433
|
923 if (ex <= x) continue;
|
Chris@427
|
924
|
Chris@551
|
925 if (!shouldIlluminate ||
|
Chris@551
|
926 // "illuminatePoint != p"
|
Chris@551
|
927 RegionModel::Point::Comparator()(illuminatePoint, p) ||
|
Chris@551
|
928 RegionModel::Point::Comparator()(p, illuminatePoint)) {
|
Chris@551
|
929
|
Chris@559
|
930 paint.setPen(QPen(getForegroundQColor(v), 1));
|
Chris@552
|
931 paint.drawLine(x, 0, x, v->height());
|
Chris@551
|
932 paint.setPen(Qt::NoPen);
|
Chris@552
|
933
|
Chris@552
|
934 } else {
|
Chris@552
|
935 paint.setPen(QPen(getForegroundQColor(v), 2));
|
Chris@551
|
936 }
|
Chris@427
|
937
|
Chris@559
|
938 paint.drawRect(x, -1, ex - x, v->height() + 2);
|
Chris@427
|
939
|
Chris@427
|
940 } else {
|
Chris@427
|
941
|
Chris@551
|
942 if (shouldIlluminate &&
|
Chris@551
|
943 // "illuminatePoint == p"
|
Chris@551
|
944 !RegionModel::Point::Comparator()(illuminatePoint, p) &&
|
Chris@551
|
945 !RegionModel::Point::Comparator()(p, illuminatePoint)) {
|
Chris@551
|
946
|
Chris@551
|
947 paint.setPen(v->getForeground());
|
Chris@551
|
948 paint.setBrush(v->getForeground());
|
Chris@551
|
949
|
Chris@551
|
950 QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
|
Chris@551
|
951 v->drawVisibleText(paint,
|
Chris@551
|
952 x - paint.fontMetrics().width(vlabel) - 2,
|
Chris@551
|
953 y + paint.fontMetrics().height()/2
|
Chris@551
|
954 - paint.fontMetrics().descent(),
|
Chris@551
|
955 vlabel, View::OutlinedText);
|
Chris@551
|
956
|
Chris@551
|
957 QString hlabel = RealTime::frame2RealTime
|
Chris@551
|
958 (p.frame, m_model->getSampleRate()).toText(true).c_str();
|
Chris@551
|
959 v->drawVisibleText(paint,
|
Chris@551
|
960 x,
|
Chris@551
|
961 y - h/2 - paint.fontMetrics().descent() - 2,
|
Chris@551
|
962 hlabel, View::OutlinedText);
|
Chris@427
|
963 }
|
Chris@551
|
964
|
Chris@427
|
965 paint.drawLine(x, y-1, x + w, y-1);
|
Chris@427
|
966 paint.drawLine(x, y+1, x + w, y+1);
|
Chris@427
|
967 paint.drawLine(x, y - h/2, x, y + h/2);
|
Chris@427
|
968 paint.drawLine(x+w, y - h/2, x + w, y + h/2);
|
Chris@427
|
969 }
|
Chris@552
|
970 }
|
Chris@552
|
971
|
Chris@552
|
972 int nextLabelMinX = -100;
|
Chris@552
|
973 int lastLabelY = 0;
|
Chris@552
|
974
|
Chris@552
|
975 for (RegionModel::PointList::const_iterator i = points.begin();
|
Chris@552
|
976 i != points.end(); ++i) {
|
Chris@552
|
977
|
Chris@552
|
978 const RegionModel::Point &p(*i);
|
Chris@552
|
979
|
Chris@552
|
980 int x = v->getXForFrame(p.frame);
|
Chris@552
|
981 int y = getYForValue(v, p.value);
|
Chris@552
|
982
|
Chris@552
|
983 bool illuminated = false;
|
Chris@552
|
984
|
Chris@552
|
985 if (m_plotStyle != PlotSegmentation) {
|
Chris@552
|
986
|
Chris@552
|
987 if (shouldIlluminate &&
|
Chris@552
|
988 // "illuminatePoint == p"
|
Chris@552
|
989 !RegionModel::Point::Comparator()(illuminatePoint, p) &&
|
Chris@552
|
990 !RegionModel::Point::Comparator()(p, illuminatePoint)) {
|
Chris@552
|
991
|
Chris@552
|
992 illuminated = true;
|
Chris@552
|
993 }
|
Chris@552
|
994 }
|
Chris@427
|
995
|
Chris@551
|
996 if (!illuminated) {
|
Chris@551
|
997 QString label = p.label;
|
Chris@551
|
998 if (label == "") {
|
Chris@551
|
999 label = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
|
Chris@551
|
1000 }
|
Chris@551
|
1001
|
Chris@552
|
1002 int labelX, labelY;
|
Chris@552
|
1003
|
Chris@551
|
1004 if (m_plotStyle != PlotSegmentation) {
|
Chris@552
|
1005 labelX = x - paint.fontMetrics().width(label) - 2;
|
Chris@552
|
1006 labelY = y + paint.fontMetrics().height()/2
|
Chris@552
|
1007 - paint.fontMetrics().descent();
|
Chris@551
|
1008 } else {
|
Chris@552
|
1009 labelX = x + 5;
|
Chris@552
|
1010 labelY = v->getTextLabelHeight(this, paint);
|
Chris@552
|
1011 if (labelX < nextLabelMinX) {
|
Chris@552
|
1012 if (lastLabelY < v->height()/2) {
|
Chris@552
|
1013 labelY = lastLabelY + fontHeight;
|
Chris@552
|
1014 }
|
Chris@552
|
1015 }
|
Chris@552
|
1016 lastLabelY = labelY;
|
Chris@552
|
1017 nextLabelMinX = labelX + paint.fontMetrics().width(label);
|
Chris@551
|
1018 }
|
Chris@552
|
1019
|
Chris@552
|
1020 v->drawVisibleText(paint, labelX, labelY, label, View::OutlinedText);
|
Chris@550
|
1021 }
|
Chris@411
|
1022 }
|
Chris@411
|
1023
|
Chris@411
|
1024 paint.restore();
|
Chris@411
|
1025 }
|
Chris@411
|
1026
|
Chris@411
|
1027 void
|
Chris@411
|
1028 RegionLayer::drawStart(View *v, QMouseEvent *e)
|
Chris@411
|
1029 {
|
Chris@411
|
1030 if (!m_model) return;
|
Chris@411
|
1031
|
Chris@411
|
1032 long frame = v->getFrameForX(e->x());
|
Chris@411
|
1033 if (frame < 0) frame = 0;
|
Chris@411
|
1034 frame = frame / m_model->getResolution() * m_model->getResolution();
|
Chris@411
|
1035
|
Chris@411
|
1036 float value = getValueForY(v, e->y());
|
Chris@411
|
1037
|
Chris@550
|
1038 m_editingPoint = RegionModel::Point(frame, value, 0, "");
|
Chris@411
|
1039 m_originalPoint = m_editingPoint;
|
Chris@411
|
1040
|
Chris@411
|
1041 if (m_editingCommand) finish(m_editingCommand);
|
Chris@411
|
1042 m_editingCommand = new RegionModel::EditCommand(m_model,
|
Chris@543
|
1043 tr("Draw Region"));
|
Chris@411
|
1044 m_editingCommand->addPoint(m_editingPoint);
|
Chris@411
|
1045
|
Chris@550
|
1046 recalcSpacing();
|
Chris@550
|
1047
|
Chris@411
|
1048 m_editing = true;
|
Chris@411
|
1049 }
|
Chris@411
|
1050
|
Chris@411
|
1051 void
|
Chris@411
|
1052 RegionLayer::drawDrag(View *v, QMouseEvent *e)
|
Chris@411
|
1053 {
|
Chris@411
|
1054 if (!m_model || !m_editing) return;
|
Chris@411
|
1055
|
Chris@411
|
1056 long frame = v->getFrameForX(e->x());
|
Chris@411
|
1057 if (frame < 0) frame = 0;
|
Chris@411
|
1058 frame = frame / m_model->getResolution() * m_model->getResolution();
|
Chris@411
|
1059
|
Chris@542
|
1060 float newValue = m_editingPoint.value;
|
Chris@542
|
1061 if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y());
|
Chris@411
|
1062
|
Chris@411
|
1063 long newFrame = m_editingPoint.frame;
|
Chris@411
|
1064 long newDuration = frame - newFrame;
|
Chris@411
|
1065 if (newDuration < 0) {
|
Chris@411
|
1066 newFrame = frame;
|
Chris@411
|
1067 newDuration = -newDuration;
|
Chris@411
|
1068 } else if (newDuration == 0) {
|
Chris@411
|
1069 newDuration = 1;
|
Chris@411
|
1070 }
|
Chris@411
|
1071
|
Chris@411
|
1072 m_editingCommand->deletePoint(m_editingPoint);
|
Chris@411
|
1073 m_editingPoint.frame = newFrame;
|
Chris@411
|
1074 m_editingPoint.value = newValue;
|
Chris@411
|
1075 m_editingPoint.duration = newDuration;
|
Chris@411
|
1076 m_editingCommand->addPoint(m_editingPoint);
|
Chris@550
|
1077
|
Chris@551
|
1078 recalcSpacing();
|
Chris@411
|
1079 }
|
Chris@411
|
1080
|
Chris@411
|
1081 void
|
Chris@411
|
1082 RegionLayer::drawEnd(View *, QMouseEvent *)
|
Chris@411
|
1083 {
|
Chris@411
|
1084 if (!m_model || !m_editing) return;
|
Chris@411
|
1085 finish(m_editingCommand);
|
Chris@411
|
1086 m_editingCommand = 0;
|
Chris@411
|
1087 m_editing = false;
|
Chris@550
|
1088
|
Chris@550
|
1089 recalcSpacing();
|
Chris@411
|
1090 }
|
Chris@411
|
1091
|
Chris@411
|
1092 void
|
Chris@411
|
1093 RegionLayer::eraseStart(View *v, QMouseEvent *e)
|
Chris@411
|
1094 {
|
Chris@411
|
1095 if (!m_model) return;
|
Chris@411
|
1096
|
Chris@550
|
1097 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
|
Chris@411
|
1098
|
Chris@411
|
1099 if (m_editingCommand) {
|
Chris@411
|
1100 finish(m_editingCommand);
|
Chris@411
|
1101 m_editingCommand = 0;
|
Chris@411
|
1102 }
|
Chris@411
|
1103
|
Chris@411
|
1104 m_editing = true;
|
Chris@550
|
1105 recalcSpacing();
|
Chris@411
|
1106 }
|
Chris@411
|
1107
|
Chris@411
|
1108 void
|
Chris@411
|
1109 RegionLayer::eraseDrag(View *v, QMouseEvent *e)
|
Chris@411
|
1110 {
|
Chris@411
|
1111 }
|
Chris@411
|
1112
|
Chris@411
|
1113 void
|
Chris@411
|
1114 RegionLayer::eraseEnd(View *v, QMouseEvent *e)
|
Chris@411
|
1115 {
|
Chris@411
|
1116 if (!m_model || !m_editing) return;
|
Chris@411
|
1117
|
Chris@411
|
1118 m_editing = false;
|
Chris@551
|
1119
|
Chris@550
|
1120 RegionModel::Point p(0);
|
Chris@550
|
1121 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
|
Chris@550
|
1122 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
|
Chris@411
|
1123
|
Chris@411
|
1124 m_editingCommand = new RegionModel::EditCommand
|
Chris@543
|
1125 (m_model, tr("Erase Region"));
|
Chris@411
|
1126
|
Chris@411
|
1127 m_editingCommand->deletePoint(m_editingPoint);
|
Chris@411
|
1128
|
Chris@411
|
1129 finish(m_editingCommand);
|
Chris@411
|
1130 m_editingCommand = 0;
|
Chris@411
|
1131 m_editing = false;
|
Chris@550
|
1132 recalcSpacing();
|
Chris@411
|
1133 }
|
Chris@411
|
1134
|
Chris@411
|
1135 void
|
Chris@411
|
1136 RegionLayer::editStart(View *v, QMouseEvent *e)
|
Chris@411
|
1137 {
|
Chris@411
|
1138 if (!m_model) return;
|
Chris@411
|
1139
|
Chris@550
|
1140 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
|
Chris@550
|
1141 return;
|
Chris@550
|
1142 }
|
Chris@550
|
1143
|
Chris@551
|
1144 m_dragPointX = v->getXForFrame(m_editingPoint.frame);
|
Chris@551
|
1145 m_dragPointY = getYForValue(v, m_editingPoint.value);
|
Chris@551
|
1146
|
Chris@411
|
1147 m_originalPoint = m_editingPoint;
|
Chris@411
|
1148
|
Chris@411
|
1149 if (m_editingCommand) {
|
Chris@411
|
1150 finish(m_editingCommand);
|
Chris@411
|
1151 m_editingCommand = 0;
|
Chris@411
|
1152 }
|
Chris@411
|
1153
|
Chris@411
|
1154 m_editing = true;
|
Chris@551
|
1155 m_dragStartX = e->x();
|
Chris@551
|
1156 m_dragStartY = e->y();
|
Chris@550
|
1157 recalcSpacing();
|
Chris@411
|
1158 }
|
Chris@411
|
1159
|
Chris@411
|
1160 void
|
Chris@411
|
1161 RegionLayer::editDrag(View *v, QMouseEvent *e)
|
Chris@411
|
1162 {
|
Chris@411
|
1163 if (!m_model || !m_editing) return;
|
Chris@411
|
1164
|
Chris@551
|
1165 int xdist = e->x() - m_dragStartX;
|
Chris@551
|
1166 int ydist = e->y() - m_dragStartY;
|
Chris@551
|
1167 int newx = m_dragPointX + xdist;
|
Chris@551
|
1168 int newy = m_dragPointY + ydist;
|
Chris@551
|
1169
|
Chris@551
|
1170 long frame = v->getFrameForX(newx);
|
Chris@411
|
1171 if (frame < 0) frame = 0;
|
Chris@411
|
1172 frame = frame / m_model->getResolution() * m_model->getResolution();
|
Chris@411
|
1173
|
Chris@551
|
1174 // Do not bisect between two values, if one of those values is
|
Chris@551
|
1175 // that of the point we're actually moving ...
|
Chris@551
|
1176 int avoid = m_spacingMap[m_editingPoint.value];
|
Chris@551
|
1177
|
Chris@551
|
1178 // ... unless there are other points with the same value
|
Chris@551
|
1179 if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1;
|
Chris@551
|
1180
|
Chris@551
|
1181 float value = getValueForY(v, newy, avoid);
|
Chris@411
|
1182
|
Chris@411
|
1183 if (!m_editingCommand) {
|
Chris@411
|
1184 m_editingCommand = new RegionModel::EditCommand(m_model,
|
Chris@543
|
1185 tr("Drag Region"));
|
Chris@411
|
1186 }
|
Chris@411
|
1187
|
Chris@411
|
1188 m_editingCommand->deletePoint(m_editingPoint);
|
Chris@411
|
1189 m_editingPoint.frame = frame;
|
Chris@411
|
1190 m_editingPoint.value = value;
|
Chris@411
|
1191 m_editingCommand->addPoint(m_editingPoint);
|
Chris@550
|
1192 recalcSpacing();
|
Chris@411
|
1193 }
|
Chris@411
|
1194
|
Chris@411
|
1195 void
|
Chris@550
|
1196 RegionLayer::editEnd(View *, QMouseEvent *e)
|
Chris@411
|
1197 {
|
Chris@411
|
1198 if (!m_model || !m_editing) return;
|
Chris@411
|
1199
|
Chris@411
|
1200 if (m_editingCommand) {
|
Chris@411
|
1201
|
Chris@411
|
1202 QString newName = m_editingCommand->getName();
|
Chris@411
|
1203
|
Chris@411
|
1204 if (m_editingPoint.frame != m_originalPoint.frame) {
|
Chris@411
|
1205 if (m_editingPoint.value != m_originalPoint.value) {
|
Chris@543
|
1206 newName = tr("Edit Region");
|
Chris@411
|
1207 } else {
|
Chris@543
|
1208 newName = tr("Relocate Region");
|
Chris@411
|
1209 }
|
Chris@411
|
1210 } else {
|
Chris@411
|
1211 newName = tr("Change Point Value");
|
Chris@411
|
1212 }
|
Chris@411
|
1213
|
Chris@411
|
1214 m_editingCommand->setName(newName);
|
Chris@411
|
1215 finish(m_editingCommand);
|
Chris@411
|
1216 }
|
Chris@411
|
1217
|
Chris@411
|
1218 m_editingCommand = 0;
|
Chris@411
|
1219 m_editing = false;
|
Chris@550
|
1220 recalcSpacing();
|
Chris@411
|
1221 }
|
Chris@411
|
1222
|
Chris@411
|
1223 bool
|
Chris@411
|
1224 RegionLayer::editOpen(View *v, QMouseEvent *e)
|
Chris@411
|
1225 {
|
Chris@411
|
1226 if (!m_model) return false;
|
Chris@411
|
1227
|
Chris@550
|
1228 RegionModel::Point region(0);
|
Chris@550
|
1229 if (!getPointToDrag(v, e->x(), e->y(), region)) return false;
|
Chris@550
|
1230
|
Chris@411
|
1231 ItemEditDialog *dialog = new ItemEditDialog
|
Chris@411
|
1232 (m_model->getSampleRate(),
|
Chris@411
|
1233 ItemEditDialog::ShowTime |
|
Chris@411
|
1234 ItemEditDialog::ShowDuration |
|
Chris@411
|
1235 ItemEditDialog::ShowValue |
|
Chris@411
|
1236 ItemEditDialog::ShowText,
|
Chris@411
|
1237 m_model->getScaleUnits());
|
Chris@411
|
1238
|
Chris@411
|
1239 dialog->setFrameTime(region.frame);
|
Chris@411
|
1240 dialog->setValue(region.value);
|
Chris@411
|
1241 dialog->setFrameDuration(region.duration);
|
Chris@411
|
1242 dialog->setText(region.label);
|
Chris@411
|
1243
|
Chris@411
|
1244 if (dialog->exec() == QDialog::Accepted) {
|
Chris@411
|
1245
|
Chris@411
|
1246 RegionModel::Point newRegion = region;
|
Chris@411
|
1247 newRegion.frame = dialog->getFrameTime();
|
Chris@411
|
1248 newRegion.value = dialog->getValue();
|
Chris@411
|
1249 newRegion.duration = dialog->getFrameDuration();
|
Chris@411
|
1250 newRegion.label = dialog->getText();
|
Chris@411
|
1251
|
Chris@411
|
1252 RegionModel::EditCommand *command = new RegionModel::EditCommand
|
Chris@543
|
1253 (m_model, tr("Edit Region"));
|
Chris@411
|
1254 command->deletePoint(region);
|
Chris@411
|
1255 command->addPoint(newRegion);
|
Chris@411
|
1256 finish(command);
|
Chris@411
|
1257 }
|
Chris@411
|
1258
|
Chris@411
|
1259 delete dialog;
|
Chris@550
|
1260 recalcSpacing();
|
Chris@411
|
1261 return true;
|
Chris@411
|
1262 }
|
Chris@411
|
1263
|
Chris@411
|
1264 void
|
Chris@411
|
1265 RegionLayer::moveSelection(Selection s, size_t newStartFrame)
|
Chris@411
|
1266 {
|
Chris@411
|
1267 if (!m_model) return;
|
Chris@411
|
1268
|
Chris@411
|
1269 RegionModel::EditCommand *command =
|
Chris@411
|
1270 new RegionModel::EditCommand(m_model, tr("Drag Selection"));
|
Chris@411
|
1271
|
Chris@411
|
1272 RegionModel::PointList points =
|
Chris@411
|
1273 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
|
Chris@411
|
1274
|
Chris@411
|
1275 for (RegionModel::PointList::iterator i = points.begin();
|
Chris@411
|
1276 i != points.end(); ++i) {
|
Chris@411
|
1277
|
Chris@411
|
1278 if (s.contains(i->frame)) {
|
Chris@411
|
1279 RegionModel::Point newPoint(*i);
|
Chris@411
|
1280 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
|
Chris@411
|
1281 command->deletePoint(*i);
|
Chris@411
|
1282 command->addPoint(newPoint);
|
Chris@411
|
1283 }
|
Chris@411
|
1284 }
|
Chris@411
|
1285
|
Chris@411
|
1286 finish(command);
|
Chris@550
|
1287 recalcSpacing();
|
Chris@411
|
1288 }
|
Chris@411
|
1289
|
Chris@411
|
1290 void
|
Chris@411
|
1291 RegionLayer::resizeSelection(Selection s, Selection newSize)
|
Chris@411
|
1292 {
|
Chris@411
|
1293 if (!m_model) return;
|
Chris@411
|
1294
|
Chris@411
|
1295 RegionModel::EditCommand *command =
|
Chris@411
|
1296 new RegionModel::EditCommand(m_model, tr("Resize Selection"));
|
Chris@411
|
1297
|
Chris@411
|
1298 RegionModel::PointList points =
|
Chris@411
|
1299 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
|
Chris@411
|
1300
|
Chris@411
|
1301 double ratio =
|
Chris@411
|
1302 double(newSize.getEndFrame() - newSize.getStartFrame()) /
|
Chris@411
|
1303 double(s.getEndFrame() - s.getStartFrame());
|
Chris@411
|
1304
|
Chris@411
|
1305 for (RegionModel::PointList::iterator i = points.begin();
|
Chris@411
|
1306 i != points.end(); ++i) {
|
Chris@411
|
1307
|
Chris@411
|
1308 if (s.contains(i->frame)) {
|
Chris@411
|
1309
|
Chris@411
|
1310 double targetStart = i->frame;
|
Chris@411
|
1311 targetStart = newSize.getStartFrame() +
|
Chris@411
|
1312 double(targetStart - s.getStartFrame()) * ratio;
|
Chris@411
|
1313
|
Chris@411
|
1314 double targetEnd = i->frame + i->duration;
|
Chris@411
|
1315 targetEnd = newSize.getStartFrame() +
|
Chris@411
|
1316 double(targetEnd - s.getStartFrame()) * ratio;
|
Chris@411
|
1317
|
Chris@411
|
1318 RegionModel::Point newPoint(*i);
|
Chris@411
|
1319 newPoint.frame = lrint(targetStart);
|
Chris@411
|
1320 newPoint.duration = lrint(targetEnd - targetStart);
|
Chris@411
|
1321 command->deletePoint(*i);
|
Chris@411
|
1322 command->addPoint(newPoint);
|
Chris@411
|
1323 }
|
Chris@411
|
1324 }
|
Chris@411
|
1325
|
Chris@411
|
1326 finish(command);
|
Chris@550
|
1327 recalcSpacing();
|
Chris@411
|
1328 }
|
Chris@411
|
1329
|
Chris@411
|
1330 void
|
Chris@411
|
1331 RegionLayer::deleteSelection(Selection s)
|
Chris@411
|
1332 {
|
Chris@411
|
1333 if (!m_model) return;
|
Chris@411
|
1334
|
Chris@411
|
1335 RegionModel::EditCommand *command =
|
Chris@411
|
1336 new RegionModel::EditCommand(m_model, tr("Delete Selected Points"));
|
Chris@411
|
1337
|
Chris@411
|
1338 RegionModel::PointList points =
|
Chris@411
|
1339 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
|
Chris@411
|
1340
|
Chris@411
|
1341 for (RegionModel::PointList::iterator i = points.begin();
|
Chris@411
|
1342 i != points.end(); ++i) {
|
Chris@411
|
1343
|
Chris@411
|
1344 if (s.contains(i->frame)) {
|
Chris@411
|
1345 command->deletePoint(*i);
|
Chris@411
|
1346 }
|
Chris@411
|
1347 }
|
Chris@411
|
1348
|
Chris@411
|
1349 finish(command);
|
Chris@550
|
1350 recalcSpacing();
|
Chris@411
|
1351 }
|
Chris@411
|
1352
|
Chris@411
|
1353 void
|
Chris@411
|
1354 RegionLayer::copy(View *v, Selection s, Clipboard &to)
|
Chris@411
|
1355 {
|
Chris@411
|
1356 if (!m_model) return;
|
Chris@411
|
1357
|
Chris@411
|
1358 RegionModel::PointList points =
|
Chris@411
|
1359 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
|
Chris@411
|
1360
|
Chris@411
|
1361 for (RegionModel::PointList::iterator i = points.begin();
|
Chris@411
|
1362 i != points.end(); ++i) {
|
Chris@411
|
1363 if (s.contains(i->frame)) {
|
Chris@411
|
1364 Clipboard::Point point(i->frame, i->value, i->duration, i->label);
|
Chris@411
|
1365 point.setReferenceFrame(alignToReference(v, i->frame));
|
Chris@411
|
1366 to.addPoint(point);
|
Chris@411
|
1367 }
|
Chris@411
|
1368 }
|
Chris@411
|
1369 }
|
Chris@411
|
1370
|
Chris@411
|
1371 bool
|
Chris@411
|
1372 RegionLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
|
Chris@411
|
1373 {
|
Chris@411
|
1374 if (!m_model) return false;
|
Chris@411
|
1375
|
Chris@411
|
1376 const Clipboard::PointList &points = from.getPoints();
|
Chris@411
|
1377
|
Chris@411
|
1378 bool realign = false;
|
Chris@411
|
1379
|
Chris@411
|
1380 if (clipboardHasDifferentAlignment(v, from)) {
|
Chris@411
|
1381
|
Chris@411
|
1382 QMessageBox::StandardButton button =
|
Chris@411
|
1383 QMessageBox::question(v, tr("Re-align pasted items?"),
|
Chris@411
|
1384 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?"),
|
Chris@411
|
1385 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
|
Chris@411
|
1386 QMessageBox::Yes);
|
Chris@411
|
1387
|
Chris@411
|
1388 if (button == QMessageBox::Cancel) {
|
Chris@411
|
1389 return false;
|
Chris@411
|
1390 }
|
Chris@411
|
1391
|
Chris@411
|
1392 if (button == QMessageBox::Yes) {
|
Chris@411
|
1393 realign = true;
|
Chris@411
|
1394 }
|
Chris@411
|
1395 }
|
Chris@411
|
1396
|
Chris@411
|
1397 RegionModel::EditCommand *command =
|
Chris@411
|
1398 new RegionModel::EditCommand(m_model, tr("Paste"));
|
Chris@411
|
1399
|
Chris@411
|
1400 for (Clipboard::PointList::const_iterator i = points.begin();
|
Chris@411
|
1401 i != points.end(); ++i) {
|
Chris@411
|
1402
|
Chris@411
|
1403 if (!i->haveFrame()) continue;
|
Chris@411
|
1404 size_t frame = 0;
|
Chris@411
|
1405
|
Chris@411
|
1406 if (!realign) {
|
Chris@411
|
1407
|
Chris@411
|
1408 frame = i->getFrame();
|
Chris@411
|
1409
|
Chris@411
|
1410 } else {
|
Chris@411
|
1411
|
Chris@411
|
1412 if (i->haveReferenceFrame()) {
|
Chris@411
|
1413 frame = i->getReferenceFrame();
|
Chris@411
|
1414 frame = alignFromReference(v, frame);
|
Chris@411
|
1415 } else {
|
Chris@411
|
1416 frame = i->getFrame();
|
Chris@411
|
1417 }
|
Chris@411
|
1418 }
|
Chris@411
|
1419
|
Chris@411
|
1420 RegionModel::Point newPoint(frame);
|
Chris@411
|
1421
|
Chris@411
|
1422 if (i->haveLabel()) newPoint.label = i->getLabel();
|
Chris@411
|
1423 if (i->haveValue()) newPoint.value = i->getValue();
|
Chris@411
|
1424 else newPoint.value = (m_model->getValueMinimum() +
|
Chris@411
|
1425 m_model->getValueMaximum()) / 2;
|
Chris@411
|
1426 if (i->haveDuration()) newPoint.duration = i->getDuration();
|
Chris@411
|
1427 else {
|
Chris@411
|
1428 size_t nextFrame = frame;
|
Chris@411
|
1429 Clipboard::PointList::const_iterator j = i;
|
Chris@411
|
1430 for (; j != points.end(); ++j) {
|
Chris@411
|
1431 if (!j->haveFrame()) continue;
|
Chris@411
|
1432 if (j != i) break;
|
Chris@411
|
1433 }
|
Chris@411
|
1434 if (j != points.end()) {
|
Chris@411
|
1435 nextFrame = j->getFrame();
|
Chris@411
|
1436 }
|
Chris@411
|
1437 if (nextFrame == frame) {
|
Chris@411
|
1438 newPoint.duration = m_model->getResolution();
|
Chris@411
|
1439 } else {
|
Chris@411
|
1440 newPoint.duration = nextFrame - frame;
|
Chris@411
|
1441 }
|
Chris@411
|
1442 }
|
Chris@411
|
1443
|
Chris@411
|
1444 command->addPoint(newPoint);
|
Chris@411
|
1445 }
|
Chris@411
|
1446
|
Chris@411
|
1447 finish(command);
|
Chris@550
|
1448 recalcSpacing();
|
Chris@411
|
1449 return true;
|
Chris@411
|
1450 }
|
Chris@411
|
1451
|
Chris@411
|
1452 void
|
Chris@411
|
1453 RegionLayer::toXml(QTextStream &stream,
|
Chris@411
|
1454 QString indent, QString extraAttributes) const
|
Chris@411
|
1455 {
|
Chris@411
|
1456 SingleColourLayer::toXml(stream, indent, extraAttributes +
|
Chris@412
|
1457 QString(" verticalScale=\"%1\" plotStyle=\"%2\"")
|
Chris@412
|
1458 .arg(m_verticalScale)
|
Chris@412
|
1459 .arg(m_plotStyle));
|
Chris@411
|
1460 }
|
Chris@411
|
1461
|
Chris@411
|
1462 void
|
Chris@411
|
1463 RegionLayer::setProperties(const QXmlAttributes &attributes)
|
Chris@411
|
1464 {
|
Chris@411
|
1465 SingleColourLayer::setProperties(attributes);
|
Chris@411
|
1466
|
Chris@411
|
1467 bool ok;
|
Chris@411
|
1468 VerticalScale scale = (VerticalScale)
|
Chris@411
|
1469 attributes.value("verticalScale").toInt(&ok);
|
Chris@411
|
1470 if (ok) setVerticalScale(scale);
|
Chris@412
|
1471 PlotStyle style = (PlotStyle)
|
Chris@412
|
1472 attributes.value("plotStyle").toInt(&ok);
|
Chris@412
|
1473 if (ok) setPlotStyle(style);
|
Chris@411
|
1474 }
|
Chris@411
|
1475
|
Chris@411
|
1476
|