comparison layer/IntervalLayer.cpp @ 18:d8e6709e9075

add - EasaierSessionManager - Easaier menus - Interval model
author lbajardsilogic
date Mon, 14 May 2007 13:13:14 +0000
parents
children 66af7c1b10d9
comparison
equal deleted inserted replaced
17:e59b19407b6d 18:d8e6709e9075
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sound Access
5 EASAIER client application.
6 Silogic 2007. Luc Barthélémy.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version. See the file
12 COPYING included with this distribution for more information.
13 */
14
15 #include <QObject>
16 #include <QColor>
17 #include <QPainter>
18 #include <QMouseEvent>
19 #include <QInputDialog>
20
21 #include "layer/IntervalLayer.h"
22 #include "view/View.h"
23 #include "system/System.h"
24 #include "widgets/ItemEditDialog.h"
25
26
27 const int gIntervalHeight = 30;
28
29 //****************************************************************
30 //* Function: Constructor
31 //* Description: declaration and initialisation of all private parameters
32 //****************************************************************
33 IntervalLayer::IntervalLayer():
34 Layer(),
35 m_model(0),
36 m_editing(false),
37 m_colour(Qt::darkRed),
38 m_editingCommand(0)
39 {
40 }
41
42 IntervalLayer::~IntervalLayer()
43 {}
44
45 void IntervalLayer::setModel(IntervalModel* 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 emit modelReplaced();
58 }
59
60 Layer::PropertyList
61 IntervalLayer::getProperties() const
62 {
63 PropertyList list;
64 list.push_back("Colour");
65 return list;
66 }
67
68 Layer::PropertyType
69 IntervalLayer::getPropertyType(const PropertyName &name) const
70 {
71 return ValueProperty;
72 }
73
74 int
75 IntervalLayer::getPropertyRangeAndValue(const PropertyName &name,
76 int *min, int *max, int *deflt) const
77 {
78 //!!! factor this colour handling stuff out into a colour manager class
79 int val = 0;
80
81 if (name == "Colour") {
82
83 if (min) *min = 0;
84 if (max) *max = 5;
85 if (deflt) *deflt = 0;
86
87 if (m_colour == Qt::black) val = 0;
88 else if (m_colour == Qt::darkRed) val = 1;
89 else if (m_colour == Qt::darkBlue) val = 2;
90 else if (m_colour == Qt::darkGreen) val = 3;
91 else if (m_colour == QColor(200, 50, 255)) val = 4;
92 else if (m_colour == QColor(255, 150, 50)) val = 5;
93 }
94
95 return val;
96 }
97
98 QString
99 IntervalLayer::getPropertyValueLabel(const PropertyName &name,
100 int value) const
101 {
102 if (name == "Colour") {
103 switch (value) {
104 default:
105 case 0: return tr("Black");
106 case 1: return tr("Red");
107 case 2: return tr("Blue");
108 case 3: return tr("Green");
109 case 4: return tr("Purple");
110 case 5: return tr("Orange");
111 }
112 }
113 return tr("<unknown>");
114 }
115
116
117 void IntervalLayer::setProperties(const QXmlAttributes &attributes)
118 {
119 QString colourSpec = attributes.value("colour");
120 if (colourSpec != "") {
121 QColor colour(colourSpec);
122 if (colour.isValid()) {
123 setBaseColour(QColor(colourSpec));
124 }
125 }
126
127 }
128
129 void
130 IntervalLayer::setProperty(const PropertyName &name, int value)
131 {
132 if (name == "Colour") {
133 switch (value) {
134 default:
135 case 0: setBaseColour(Qt::black); break;
136 case 1: setBaseColour(Qt::darkRed); break;
137 case 2: setBaseColour(Qt::darkBlue); break;
138 case 3: setBaseColour(Qt::darkGreen); break;
139 case 4: setBaseColour(QColor(200, 50, 255)); break;
140 case 5: setBaseColour(QColor(255, 150, 50)); break;
141 }
142 }
143 }
144
145 void
146 IntervalLayer::setBaseColour(QColor colour)
147 {
148 if (m_colour == colour) return;
149 m_colour = colour;
150 emit layerParametersChanged();
151 }
152
153 QString
154 IntervalLayer::toXmlString(QString indent, QString extraAttributes) const
155 {
156 return Layer::toXmlString(indent, extraAttributes +
157 QString(" colour=\"%1\"")
158 .arg(encodeColour(m_colour)));
159 }
160
161 QString
162 IntervalLayer::toEasaierXmlString(QString indent, QString extraAttributes) const
163 {
164 return Layer::toEasaierXmlString(indent, extraAttributes +
165 QString(" colour=\"%1\"")
166 .arg(encodeColour(m_colour)));
167 }
168
169 QString
170 IntervalLayer::getPropertyLabel(const PropertyName &name) const
171 {
172 if (name == "Colour") return tr("Colour");
173 return "";
174 }
175
176 int
177 IntervalLayer::getYForHeight(View *v, float height) const
178 {
179 int h = v->height();
180 return h - int(height * h);
181 }
182
183 float
184 IntervalLayer::getHeightForY(View *v, int y) const
185 {
186 int h = v->height();
187 return float(h - y) / h;
188 }
189
190
191 void
192 IntervalLayer::paint(View *v, QPainter &paint, QRect rect) const
193 {
194 if (!m_model || !m_model->isOK()) return;
195
196 int sampleRate = m_model->getSampleRate();
197 if (!sampleRate) return;
198
199 int x0 = rect.left(), x1 = rect.right();
200 long frame0 = v->getFrameForX(x0);
201 long frame1 = v->getFrameForX(x1);
202
203 paint.save();
204
205 QColor brushColour(m_colour);
206 brushColour.setAlpha(100);
207 QColor penColour = Qt::black;
208
209 paint.setPen(m_colour);
210
211 IntervalList& intervals = m_model->intervals();
212
213 int xS,xE,y;
214 bool draw, drawText, drawStart, drawEnd;
215
216 QPoint localPos;
217 long illuminateX = -1;
218
219 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
220 // should highlight one endpoint ?
221 illuminateX = localPos.x();
222 }
223
224 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
225 {
226 draw = drawText = drawStart = drawEnd = false;
227
228 TimeIntervalPtr ti = (*i);
229
230 if (ti->start() >= frame0 && ti->start() < frame1)
231 {
232 xS = v->getXForFrame(ti->start());
233 xE = v->getXForFrame(ti->end());
234 if (xE <= x1)
235 drawEnd = true;
236 else
237 xE = x1;
238 draw = drawText = drawStart = true;
239 }
240 else if (ti->end() > frame0 && ti->end <= frame1)
241 {
242 xS = v->getXForFrame(ti->start());
243 if (xS < x0)
244 xS = x0;
245 xE = v->getXForFrame(ti->end());
246 draw = drawEnd = true;
247 }
248 else if (ti->start <= frame0 && ti->end >= frame1)
249 {
250 xS = x0;
251 xE = x1;
252 draw = true;
253 }
254 if (draw)
255 {
256 y = getYForHeight(v, ti->value());
257 paint.setBrush(brushColour);
258 paint.fillRect(xS, y-gIntervalHeight/2, (xE-xS+1), gIntervalHeight, brushColour);
259 }
260 if (drawStart)
261 {
262 if (abs(illuminateX - xS) < 3)
263 paint.setPen(Qt::black);
264 else
265 paint.setPen(brushColour);
266 paint.drawLine(xS, 0, xS, v->height() - 1);
267 }
268 if (drawEnd)
269 {
270 if (abs (illuminateX - xE) < 3)
271 paint.setPen(Qt::black);
272 else
273 paint.setPen(brushColour);
274 paint.drawLine(xE, 0, xE, v->height() - 1);
275 }
276 if (drawText)
277 {
278 paint.setPen(penColour);
279 paint.drawText(xS+10, y, ti->label());
280 }
281 }
282
283 paint.restore();
284 }
285
286
287 bool
288 IntervalLayer::getValueExtents(float &min, float &max,
289 bool &logarithmic, QString &unit) const
290 {
291 return false;
292 }
293
294 IntervalList
295 IntervalLayer::getIntervalAt(View *v, int x, int y) // y < 0 means you don't make any test on it
296 {
297 IntervalList result;
298 IntervalList& intervals = m_model->intervals();
299
300 for (IntervalListIterator i = intervals.begin(); i != intervals.end(); ++i)
301 {
302 TimeIntervalPtr ti = (*i);
303
304 int yT = getYForHeight(v, ti->value());
305 int xS = v->getXForFrame(ti->start());
306 int xE = v->getXForFrame(ti->end());
307 if ((x >= xS) && (x <= xE))
308 {
309 if ((y < 0) || (abs(y - yT) <= gIntervalHeight/2))
310 result.push_back(ti);
311 }
312 }
313
314 return result;
315 }
316
317 IntervalList
318 IntervalLayer::getInterval(long start, long end)
319 {
320 IntervalList result;
321 IntervalList& intervals = m_model->intervals();
322
323 for (IntervalListIterator i = intervals.begin(); i != intervals.end(); ++i)
324 {
325 TimeIntervalPtr ti = (*i);
326
327 if ( (start <= ti->start()) && (ti->start() <= end) &&
328 (start <= ti->end()) && (ti->end() <= end) )
329 {
330 result.push_back(ti);
331 }
332 }
333
334 return result;
335 }
336
337 QString
338 IntervalLayer::getFeatureDescription(View *v, QPoint& pos) const
339 {
340 if (!m_model || !m_model->getSampleRate()) return "";
341
342 QString description;
343
344 int x = pos.x();
345 int y = pos.y();
346
347 long frame = v->getFrameForX(x);
348
349 IntervalList& intervals = m_model->intervals();
350 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
351 {
352 TimeIntervalPtr ti = (*i);
353
354 if (ti->start() <= frame && ti->end() >= frame)
355 {
356 int y = getYForHeight(v, ti->value());
357
358 if (abs(y - pos.y()) <= gIntervalHeight/2)
359 {
360 RealTime rtStart = RealTime::frame2RealTime(ti->start(), m_model->getSampleRate());
361 RealTime rtEnd = RealTime::frame2RealTime(ti->end(), m_model->getSampleRate());
362 RealTime rtDuration = rtEnd - rtStart;
363
364 description = QString(tr("Interval:\t%1\nStart:\t%2 End:\t%3\tDuration:\t%4"))
365 .arg(ti->label())
366 .arg(rtStart.toText(true).c_str())
367 .arg(rtEnd.toText(true).c_str())
368 .arg(rtDuration.toText(true).c_str());
369 break;
370 }
371 }
372 }
373
374 return description;
375 }
376
377
378 void
379 IntervalLayer::drawStart(View *v, QMouseEvent *e)
380 {
381 if (!m_model) {
382 std::cerr << "IntervalLayer::drawStart: no model" << std::endl;
383 return;
384 }
385
386 long frame = v->getFrameForX(e->x());
387
388 if (frame < 0)
389 frame = 0;
390
391 frame = frame / m_model->getResolution() * m_model->getResolution();
392
393 int height = (int) (getHeightForY(v, e->y())*100+0.5);
394 float value = ((float) height)/100;
395
396 m_editingInterval = new TimeInterval(frame, frame, "", value);
397
398 m_model->addInterval(m_editingInterval);
399
400 if (m_editingCommand)
401 {
402 CommandHistory::getInstance()->addCommand(m_editingCommand, false);
403 m_editingCommand = 0;
404 }
405
406 m_editingCommand = new IntervalModel::IntervalCommand(m_model, m_editingInterval,
407 IntervalModel::IntervalCommand::Creation ,
408 frame, frame, value, "");
409
410 m_editing = 1;
411 }
412
413 void
414 IntervalLayer::drawDrag(View *v, QMouseEvent *e)
415 {
416 if (!m_model || !m_editing) return;
417
418 long frame = v->getFrameForX(e->x());
419
420 if (frame < 0)
421 frame = 0;
422
423 frame = frame / m_model->getResolution() * m_model->getResolution();
424
425 long start = m_editingInterval->start();
426 long end = frame;
427
428 if (start < end)
429 {
430 m_editing = 2;
431 m_editingInterval->end(frame);
432 m_editingCommand->newEnd(frame);
433 } else {
434 m_editing = 1;
435 m_editingInterval->start(frame);
436 m_editingCommand->newStart(frame);
437 }
438 }
439
440 void
441 IntervalLayer::drawEnd(View *v, QMouseEvent *e)
442 {
443 if (!m_model || !m_editing)
444 return;
445
446 bool ok = false;
447 QString label = QInputDialog::getText(v, tr("Enter label"),
448 tr("Please enter a new label:"),
449 QLineEdit::Normal, "", &ok);
450
451 if (ok) {
452 m_editingInterval->label(label);
453 m_editingCommand->newLabel(label);
454 }
455
456 if (m_editingCommand)
457 {
458 CommandHistory::getInstance()->addCommand(m_editingCommand, false);
459 m_editingCommand = 0;
460 }
461
462 m_editing = false;
463 }
464
465 void
466 IntervalLayer::editStart(View *view, QMouseEvent *evt)
467 {
468 if (!m_model)
469 return;
470
471 IntervalList intervals = getIntervalAt(view, evt->x(), evt->y());
472
473 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
474 {
475 TimeIntervalPtr ti = (*i);
476
477 int xS = view->getXForFrame(ti->start());
478 int xE = view->getXForFrame(ti->end());
479
480 if (abs(xS - evt->x()) < 5)
481 {
482 m_editing = 1; // editing start
483 m_editingInterval = ti;
484 break;
485 }
486 else if (abs(xE - evt->x()) < 5)
487 {
488 m_editing = 2; // editing end
489 m_editingInterval = ti;
490 break;
491 }
492 else
493 {
494 m_editing = 0;
495 }
496 }
497
498 if (m_editing)
499 view->setCursor(Qt::SizeHorCursor);
500
501 if (m_editingCommand)
502 {
503 CommandHistory::getInstance()->addCommand(m_editingCommand, false);
504 m_editingCommand = 0;
505 }
506
507 }
508
509 void
510 IntervalLayer::editDrag(View *view, QMouseEvent *evt)
511 {
512 if (!m_model || !m_editing)
513 return;
514
515 long frame = view->getFrameForX(evt->x());
516
517 if (!m_editingCommand)
518 {
519 m_editingCommand = new IntervalModel::IntervalCommand(m_model, m_editingInterval, IntervalModel::IntervalCommand::Edition,
520 m_editingInterval->start(),
521 m_editingInterval->end(),
522 m_editingInterval->value(),
523 m_editingInterval->label());
524 }
525
526 if (m_editing == 1) // start
527 {
528 if (frame < m_editingInterval->end())
529 m_editingCommand->newStart(frame);
530 }
531 else if (m_editing == 2) // end
532 {
533 if (frame > m_editingInterval->start())
534 m_editingCommand->newEnd(frame);
535 }
536 }
537
538 void
539 IntervalLayer::editEnd(View *view, QMouseEvent *evt)
540 {
541 if (!m_model || !m_editing)
542 return;
543
544 view->setCursor(Qt::UpArrowCursor);
545
546 if (m_editingCommand)
547 {
548 CommandHistory::getInstance()->addCommand(m_editingCommand, false);
549 m_editingCommand = 0;
550 }
551
552
553 m_editing = false;
554 }
555
556 void
557 IntervalLayer::editOpen(View *view, QMouseEvent *evt) // on double-click
558 {
559 if (!m_model) return;
560
561 IntervalList intervals = getIntervalAt(view, evt->x(), evt->y());
562 if (! intervals.empty())
563 {
564 TimeIntervalPtr ti = intervals.front();
565
566 bool ok = false;
567 ItemEditDialog *dialog = new ItemEditDialog
568 (m_model->getSampleRate(),
569 ItemEditDialog::ShowTime |
570 ItemEditDialog::ShowDuration |
571 ItemEditDialog::ShowText |
572 ItemEditDialog::ShowValue);
573
574 dialog->setFrameTime(ti->start());
575 dialog->setFrameDuration(ti->end() - ti->start());
576 dialog->setText(ti->label());
577 dialog->setValue((int)(ti->value()*100+0.5));
578
579 if (dialog->exec() == QDialog::Accepted)
580 {
581
582 IntervalModel::IntervalCommand *command =
583 new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Edition,
584 dialog->getFrameTime(),
585 dialog->getFrameTime() + dialog->getFrameDuration(),
586 dialog->getValue()/100,
587 dialog->getText());
588
589 CommandHistory::getInstance()->addCommand(command);
590
591 }
592 }
593 }
594
595 void
596 IntervalLayer::moveSelection(Selection s, size_t newStartFrame)
597 {
598 if (!m_model) return;
599
600 IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame());
601
602 MacroCommand * command = new MacroCommand("Drag Selection");
603
604 long newStart = 0;
605 long newEnd = 0;
606
607 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
608 {
609 TimeIntervalPtr ti = (*i);
610
611 newStart = ti->start() + ((long) (newStartFrame - s.getStartFrame()));
612 newEnd = ti->end() + ((long) (newStartFrame - s.getStartFrame()));
613
614 IntervalModel::IntervalCommand *intervalCommand =
615 new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Edition,
616 newStart,
617 newEnd,
618 ti->value(),
619 ti->label());
620
621 m_model->changeInterval(ti, newStart, newEnd, ti->value(), ti->label() );
622
623 command->addCommand(intervalCommand);
624 }
625
626 CommandHistory::getInstance()->addCommand(command, false);
627 }
628
629 void
630 IntervalLayer::resizeSelection(Selection s, Selection newSize)
631 {
632 if (!m_model) return;
633
634 IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame());
635
636 MacroCommand * command = new MacroCommand("Resize Selection");
637
638 long newStart = 0;
639 long newEnd = 0;
640
641 double ratio = ((double) (newSize.getEndFrame() - newSize.getStartFrame())) /
642 ((double) (s.getEndFrame() - s.getStartFrame()));
643
644 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
645 {
646 TimeIntervalPtr ti = (*i);
647
648 newStart = (long) newSize.getStartFrame() + (long) ( ((double) ti->start() - (double) s.getStartFrame()) * ratio );
649 newEnd = (long) newSize.getStartFrame() + (long) ( ((double) ti->end() - (double) s.getStartFrame()) * ratio );
650
651 IntervalModel::IntervalCommand *intervalCommand =
652 new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Edition,
653 newStart,
654 newEnd,
655 ti->value(),
656 ti->label());
657
658 m_model->changeInterval(ti, newStart, newEnd, ti->value(), ti->label() );
659
660 command->addCommand(intervalCommand);
661 }
662
663 CommandHistory::getInstance()->addCommand(command, false);
664 }
665
666 void
667 IntervalLayer::deleteSelection(Selection s)
668 {
669 if (!m_model) return;
670
671 IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame());
672
673 MacroCommand * command = new MacroCommand("Delete Selected Intervals");
674
675 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
676 {
677 TimeIntervalPtr ti = (*i);
678
679 m_model->removeInterval(ti);
680
681 IntervalModel::IntervalCommand *intervalCommand =
682 new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Deletion,
683 ti->start(),
684 ti->end(),
685 ti->value(),
686 ti->label());
687
688 command->addCommand(intervalCommand);
689 }
690
691 CommandHistory::getInstance()->addCommand(command, false);
692 }
693
694 void
695 IntervalLayer::paste(const Clipboard &from, int frameOffset)
696 {
697 if (!m_model) return;
698
699 const Clipboard::PointList &points = from.getPoints();
700
701 MacroCommand * command = new MacroCommand("Paste");
702
703 for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i)
704 {
705 if (!i->haveFrame())
706 continue;
707
708 size_t frame = 0;
709
710 if (frameOffset > 0 || -frameOffset < i->getFrame()) {
711 frame = i->getFrame() + frameOffset;
712 }
713
714 TimeIntervalPtr ti = new TimeInterval((long) frame, (long) (frame+i->getDuration()), i->getLabel(), i->getValue());
715
716 m_model->addInterval(ti);
717
718 IntervalModel::IntervalCommand *intervalCommand =
719 new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Creation,
720 ti->start(),
721 ti->end(),
722 ti->value(),
723 ti->label());
724
725 command->addCommand(intervalCommand);
726
727 }
728
729 CommandHistory::getInstance()->addCommand(command, false);
730 }
731
732 void
733 IntervalLayer::copy(Selection s, Clipboard &to)
734 {
735 if (!m_model) return;
736
737 IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame());
738
739 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
740 {
741 TimeIntervalPtr ti = (*i);
742
743 Clipboard::Point point(ti->start(), ti->value(), ti->end()-ti->start(), ti->label());
744 to.addPoint(point);
745 }
746 }
747
748 bool
749 IntervalLayer::snapToFeatureFrame(View *v, int &frame,
750 size_t &resolution,
751 SnapType snap) const
752 {
753 if (!m_model)
754 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
755
756 resolution = m_model->getResolution();
757
758 IntervalList& intervals = m_model->intervals();
759
760 int snapped, best = frame;
761 bool found = false;
762
763 unsigned int dist, distMin;
764
765 dist = distMin = m_model->getEndFrame();
766
767 for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i)
768 {
769 TimeIntervalPtr ti = (*i);
770 switch (snap)
771 {
772 case SnapRight:
773 if (ti->start() >= frame)
774 {
775 dist = ti->start() - frame;
776 snapped = ti->start();
777 }
778 else if (ti->end() >= frame)
779 {
780 dist = ti->end() - frame;
781 snapped = ti->end();
782 }
783 break;
784
785 case SnapLeft:
786 if (ti->end() <= frame)
787 {
788 dist = frame - ti->end();
789 snapped = ti->end();
790 }
791 else if (ti->start() <= frame)
792 {
793 dist = frame - ti->start();
794 snapped = ti->start();
795 }
796 break;
797
798 case SnapNearest:
799 {
800 int distS = abs(ti->start() - frame);
801 int distE = abs(ti->end() - frame);
802 if (distS < distE)
803 {
804 dist = distS;
805 snapped = ti->start();
806 } else
807 {
808 dist = distE;
809 snapped = ti->end();
810 }
811 }
812 break;
813
814 case SnapNeighbouring:
815 {
816 int distS = abs(ti->start() - frame);
817 int distE = abs(ti->end() - frame);
818 if (distS < 5)
819 {
820 dist = distS;
821 snapped = ti->start();
822 } else if (distE < 5)
823 {
824 dist = distE;
825 snapped = ti->end();
826 }
827 }
828 break;
829 }
830
831 if (dist < distMin)
832 {
833 found = true;
834 distMin = dist;
835 best = snapped;
836 }
837
838 }
839
840
841 if (found)
842 {
843 frame = best;
844 }
845
846 return found;
847 }