DSP-Load Window: subscribe to newly added routes
[ardour.git] / gtk2_ardour / marker.cc
1 /*
2     Copyright (C) 2001 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <sigc++/bind.h>
21 #include "ardour/tempo.h"
22
23 #include "canvas/rectangle.h"
24 #include "canvas/container.h"
25 #include "canvas/line.h"
26 #include "canvas/polygon.h"
27 #include "canvas/text.h"
28 #include "canvas/canvas.h"
29 #include "canvas/scroll_group.h"
30 #include "canvas/debug.h"
31
32 #include "ui_config.h"
33 /*
34  * ardour_ui.h include was moved to the top of the list
35  * due to a conflicting definition of 'Rect' between
36  * Apple's MacTypes.h and GTK.
37  *
38  * Now that we are including ui_config.h and not ardour_ui.h
39  * the above comment may no longer apply and this comment
40  * can be removed and ui_config.h inclusion moved.
41  */
42
43 #include "marker.h"
44 #include "public_editor.h"
45 #include "utils.h"
46 #include "rgb_macros.h"
47
48 #include <gtkmm2ext/utils.h>
49
50 #include "pbd/i18n.h"
51
52 using namespace std;
53 using namespace ARDOUR;
54 using namespace ARDOUR_UI_UTILS;
55 using namespace Gtkmm2ext;
56
57 PBD::Signal1<void,ArdourMarker*> ArdourMarker::CatchDeletion;
58
59 static double marker_height = 13.0;
60
61 void ArdourMarker::setup_sizes(const double timebar_height)
62 {
63         marker_height = floor (timebar_height) - 2;
64 }
65
66 ArdourMarker::ArdourMarker (PublicEditor& ed, ArdourCanvas::Container& parent, guint32 rgba, const string& annotation,
67                 Type type, samplepos_t sample, bool handle_events)
68
69         : editor (ed)
70         , _parent (&parent)
71         , _track_canvas_line (0)
72         , _type (type)
73         , _selected (false)
74         , _shown (false)
75         , _line_shown (false)
76         , _color (rgba)
77         , _points_color (rgba)
78         , _left_label_limit (DBL_MAX)
79         , _right_label_limit (DBL_MAX)
80         , _label_offset (0)
81
82 {
83
84         const double MH = marker_height - .5;
85         const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
86         const double M6 = std::max(2.f, rintf(6.f * UIConfiguration::instance().get_ui_scale()));
87
88         /* Shapes we use:
89          *
90          * Mark:
91          *
92          *  (0,0)   ->  (6,0)
93          *    ^           |
94          *    |           V
95          * (0,MH*.4)  (6,MH*.4)
96          *     \         /
97          *        (3,MH)
98          *
99          *
100          * TempoMark:
101          * MeterMark:
102          *
103          *        (3,0)
104          *     /         \
105          * (0,MH*.6)  (6,MH.*.6)
106          *    ^           |
107          *    |           V
108          * (0,MH)   <-  (6,MH)
109          *
110          *
111          * SessionStart:
112          * RangeStart:
113          *
114          *       0,0\
115          *        |  \
116          *        |   \ 6,MH/2
117          *        |   /
118          *        |  /
119          *       0,MH
120          *
121          *
122          * SessionEnd:
123          * RangeEnd:
124          *
125          *         /12,0
126          *        /   |
127          * 6,MH/2/    |
128          *       \    |
129          *        \   |
130          *         \12,MH
131          *
132          *
133          * PunchIn:
134          *
135          *   0,0 ------> marker_height,0
136          *    |       /
137          *    |      /
138          *    |     /
139          *    |    /
140          *    |   /
141          *    |  /
142          *   0,MH
143          *
144          *
145          *   PunchOut
146          *
147          *   0,0 ------> MH,0
148          *    \        |
149          *     \       |
150          *      \      |
151          *       \     |
152          *        \    |
153          *         \   |
154          *          \  |
155          *   MH,MH
156          *
157          */
158
159         switch (type) {
160         case Mark:
161                 points = new ArdourCanvas::Points ();
162
163                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
164                 points->push_back (ArdourCanvas::Duple ( M6, 0.0));
165                 points->push_back (ArdourCanvas::Duple ( M6, MH * .4));
166                 points->push_back (ArdourCanvas::Duple ( M3, MH));
167                 points->push_back (ArdourCanvas::Duple (0.0, MH * .4));
168                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
169
170                 _shift = 3;
171                 _label_offset = 10.0;
172                 break;
173
174         case Tempo:
175         case Meter:
176                 points = new ArdourCanvas::Points ();
177                 points->push_back (ArdourCanvas::Duple ( M3, 0.0));
178                 points->push_back (ArdourCanvas::Duple ( M6, MH * .6));
179                 points->push_back (ArdourCanvas::Duple ( M6, MH));
180                 points->push_back (ArdourCanvas::Duple (0.0, MH));
181                 points->push_back (ArdourCanvas::Duple (0.0, MH * .6));
182                 points->push_back (ArdourCanvas::Duple ( M3, 0.0));
183
184                 _shift = 3;
185                 _label_offset = 8.0;
186                 break;
187
188         case SessionStart:
189         case RangeStart:
190                 points = new ArdourCanvas::Points ();
191                 points->push_back (ArdourCanvas::Duple (    0.0, 0.0));
192                 points->push_back (ArdourCanvas::Duple (M6 + .5, MH * .5));
193                 points->push_back (ArdourCanvas::Duple (    0.0, MH));
194                 points->push_back (ArdourCanvas::Duple (    0.0, 0.0));
195
196                 _shift = 0;
197                 _label_offset = 8.0;
198                 break;
199
200         case SessionEnd:
201         case RangeEnd:
202                 points = new ArdourCanvas::Points (); // leaks
203                 points->push_back (ArdourCanvas::Duple ( M6, 0.0));
204                 points->push_back (ArdourCanvas::Duple ( M6, MH));
205                 points->push_back (ArdourCanvas::Duple (0.0, MH * .5));
206                 points->push_back (ArdourCanvas::Duple ( M6, 0.0));
207
208                 _shift = M6;
209                 _label_offset = 0.0;
210                 break;
211
212         case LoopStart:
213                 points = new ArdourCanvas::Points ();
214                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
215                 points->push_back (ArdourCanvas::Duple (MH, MH));
216                 points->push_back (ArdourCanvas::Duple (0.0, MH));
217                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
218
219                 _shift = 0;
220                 _label_offset = MH;
221                 break;
222
223         case LoopEnd:
224                 points = new ArdourCanvas::Points ();
225                 points->push_back (ArdourCanvas::Duple (MH,  0.0));
226                 points->push_back (ArdourCanvas::Duple (MH, MH));
227                 points->push_back (ArdourCanvas::Duple (0.0, MH));
228                 points->push_back (ArdourCanvas::Duple (MH, 0.0));
229
230                 _shift = MH;
231                 _label_offset = 0.0;
232                 break;
233
234         case PunchIn:
235                 points = new ArdourCanvas::Points ();
236                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
237                 points->push_back (ArdourCanvas::Duple (MH, 0.0));
238                 points->push_back (ArdourCanvas::Duple (0.0, MH));
239                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
240
241                 _shift = 0;
242                 _label_offset = MH;
243                 break;
244
245         case PunchOut:
246                 points = new ArdourCanvas::Points ();
247                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
248                 points->push_back (ArdourCanvas::Duple (MH, 0.0));
249                 points->push_back (ArdourCanvas::Duple (MH, MH));
250                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
251
252                 _shift = MH;
253                 _label_offset = 0.0;
254                 break;
255
256         }
257
258         sample_position = sample;
259         unit_position = editor.sample_to_pixel (sample);
260         unit_position -= _shift;
261
262         group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple (unit_position, 1));
263 #ifdef CANVAS_DEBUG
264         group->name = string_compose ("Marker::group for %1", annotation);
265 #endif
266
267         _name_background = new ArdourCanvas::Rectangle (group);
268 #ifdef CANVAS_DEBUG
269         _name_background->name = string_compose ("Marker::_name_background for %1", annotation);
270 #endif
271
272         /* adjust to properly locate the tip */
273
274         mark = new ArdourCanvas::Polygon (group);
275         CANVAS_DEBUG_NAME (mark, string_compose ("Marker::mark for %1", annotation));
276
277         mark->set (*points);
278         set_color_rgba (rgba);
279
280         /* setup name pixbuf sizes */
281         name_font = get_font_for_style (N_("MarkerText"));
282
283         Gtk::Label foo;
284
285         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
286         int width;
287
288         layout->set_font_description (name_font);
289         Gtkmm2ext::get_ink_pixel_size (layout, width, name_height);
290
291         _name_item = new ArdourCanvas::Text (group);
292         CANVAS_DEBUG_NAME (_name_item, string_compose ("ArdourMarker::_name_item for %1", annotation));
293         _name_item->set_font_description (name_font);
294         _name_item->set_color (RGBA_TO_UINT (0,0,0,255));
295         _name_item->set_position (ArdourCanvas::Duple (_label_offset, (marker_height - name_height - 1) * .5 ));
296
297         set_name (annotation.c_str());
298
299         editor.ZoomChanged.connect (sigc::mem_fun (*this, &ArdourMarker::reposition));
300
301         /* events will be handled by both the group and the mark itself, so
302          * make sure they can both be used to lookup this object.
303          */
304
305         group->set_data ("marker", this);
306         mark->set_data ("marker", this);
307
308         if (handle_events) {
309                 group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
310         }
311 }
312
313 ArdourMarker::~ArdourMarker ()
314 {
315         CatchDeletion (this); /* EMIT SIGNAL */
316
317         /* destroying the parent group destroys its contents, namely any polygons etc. that we added */
318         delete group;
319         delete _track_canvas_line;
320         delete points;
321 }
322
323 void ArdourMarker::reparent(ArdourCanvas::Container & parent)
324 {
325         group->reparent (&parent);
326         _parent = &parent;
327 }
328
329 void
330 ArdourMarker::set_selected (bool s)
331 {
332         _selected = s;
333         setup_line ();
334 }
335
336 void
337 ArdourMarker::set_show_line (bool s)
338 {
339         _line_shown = s;
340         setup_line ();
341 }
342
343 void
344 ArdourMarker::setup_line ()
345 {
346         if (_shown && (_selected || _line_shown)) {
347
348                 if (_track_canvas_line == 0) {
349
350                         _track_canvas_line = new ArdourCanvas::Line (editor.get_hscroll_group());
351                         _track_canvas_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
352                 }
353
354                 ArdourCanvas::Duple g = group->canvas_origin();
355                 ArdourCanvas::Duple d = _track_canvas_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
356
357                 _track_canvas_line->set_x0 (d.x);
358                 _track_canvas_line->set_x1 (d.x);
359                 _track_canvas_line->set_y0 (d.y);
360                 _track_canvas_line->set_y1 (ArdourCanvas::COORD_MAX);
361                 _track_canvas_line->set_outline_color ( _selected ? UIConfiguration::instance().color ("entered marker") : _color );
362                 _track_canvas_line->raise_to_top ();
363                 _track_canvas_line->show ();
364
365         } else {
366                 if (_track_canvas_line) {
367                         _track_canvas_line->hide ();
368                 }
369         }
370 }
371
372 void
373 ArdourMarker::canvas_height_set (double h)
374 {
375         _canvas_height = h;
376         setup_line ();
377 }
378
379 ArdourCanvas::Item&
380 ArdourMarker::the_item() const
381 {
382         return *group;
383 }
384
385 void
386 ArdourMarker::set_name (const string& new_name)
387 {
388         _name = new_name;
389
390         mark->set_tooltip(new_name);
391         _name_background->set_tooltip(new_name);
392         _name_item->set_tooltip(new_name);
393
394         setup_name_display ();
395 }
396
397 /** @return true if our label is on the left of the mark, otherwise false */
398 bool
399 ArdourMarker::label_on_left () const
400 {
401         return (_type == SessionEnd || _type == RangeEnd || _type == LoopEnd || _type == PunchOut);
402 }
403
404 void
405 ArdourMarker::setup_name_display ()
406 {
407         double limit = DBL_MAX;
408
409         if (label_on_left ()) {
410                 limit = _left_label_limit;
411         } else {
412                 limit = _right_label_limit;
413         }
414
415         const float padding =  std::max(2.f, rintf(2.f * UIConfiguration::instance().get_ui_scale()));
416         const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
417
418         /* Work out how wide the name can be */
419         int name_width = min ((double) pixel_width (_name, name_font) + padding, limit);
420
421         if (name_width == 0) {
422                 _name_item->hide ();
423         } else {
424                 _name_item->show ();
425
426                 if (label_on_left ()) {
427                         _name_item->set_x_position (-name_width);
428                 }
429
430                 _name_item->clamp_width (name_width);
431                 _name_item->set (_name);
432
433                 if (label_on_left ()) {
434                         /* adjust right edge of background to fit text */
435                         _name_background->set_x0 (_name_item->position().x - padding);
436                         _name_background->set_x1 (_name_item->position().x + name_width + _shift);
437                 } else {
438                         /* right edge remains at zero (group-relative). Add
439                          * arbitrary 2 pixels of extra padding at the end
440                          */
441                         switch (_type) {
442                                 case Tempo:
443                                         _name_item->hide ();
444                                         // tip's x-pos is at "M3", box is 2x marker's
445                                         _name_background->set_x0 (-M3);
446                                         _name_background->set_x1 (3 * M3);
447                                         break;
448                                 case Mark:
449                                 case Meter:
450                                         _name_background->set_x0 (M3);
451                                         _name_background->set_x1 (_name_item->position().x + name_width + padding);
452                                         break;
453                                 default:
454                                         _name_background->set_x0 (0);
455                                         _name_background->set_x1 (_name_item->position().x + name_width + padding);
456                                         break;
457                         }
458                 }
459         }
460
461         _name_background->set_y0 (0);
462         _name_background->set_y1 (marker_height + 1);
463 }
464
465 void
466 ArdourMarker::set_position (samplepos_t sample)
467 {
468         unit_position = editor.sample_to_pixel (sample) - _shift;
469         group->set_x_position (unit_position);
470         setup_line ();
471         sample_position = sample;
472 }
473
474 void
475 ArdourMarker::reposition ()
476 {
477         set_position (sample_position);
478 }
479
480 void
481 ArdourMarker::show ()
482 {
483         _shown = true;
484
485         group->show ();
486         setup_line ();
487 }
488
489 void
490 ArdourMarker::hide ()
491 {
492         _shown = false;
493
494         group->hide ();
495         setup_line ();
496 }
497
498 void
499 ArdourMarker::set_points_color (uint32_t c)
500 {
501         _points_color = c;
502         mark->set_fill_color (_points_color);
503         mark->set_outline_color (_points_color);
504 }
505
506 void
507 ArdourMarker::set_color_rgba (uint32_t c)
508 {
509         _color = c;
510         mark->set_fill_color (_color);
511         mark->set_outline_color (_color);
512
513         if (_track_canvas_line && !_selected) {
514                 _track_canvas_line->set_outline_color (_color);
515         }
516
517         _name_background->set_fill (true);
518         _name_background->set_fill_color (UINT_RGBA_CHANGE_A (_color, 0x70));
519         _name_background->set_outline (false);
520 }
521
522 /** Set the number of pixels that are available for a label to the left of the centre of this marker */
523 void
524 ArdourMarker::set_left_label_limit (double p)
525 {
526         /* Account for the size of the marker */
527         _left_label_limit = p - marker_height;
528         if (_left_label_limit < 0) {
529                 _left_label_limit = 0;
530         }
531
532         if (label_on_left ()) {
533                 setup_name_display ();
534         }
535 }
536
537 /** Set the number of pixels that are available for a label to the right of the centre of this marker */
538 void
539 ArdourMarker::set_right_label_limit (double p)
540 {
541         /* Account for the size of the marker */
542         _right_label_limit = p - marker_height;
543         if (_right_label_limit < 0) {
544                 _right_label_limit = 0;
545         }
546
547         if (!label_on_left ()) {
548                 setup_name_display ();
549         }
550 }
551
552 /***********************************************************************/
553
554 TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
555                           ARDOUR::TempoSection& temp)
556         : ArdourMarker (editor, parent, rgba, text, Tempo, temp.sample(), false),
557           _tempo (temp)
558 {
559         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), group, this));
560 }
561
562 TempoMarker::~TempoMarker ()
563 {
564 }
565
566 void
567 TempoMarker::update_height_mark (const double ratio)
568 {
569         const double MH = marker_height - .5;
570         const double top = MH * (1 - ratio);
571         const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
572         const double M6 = std::max(2.f, rintf(6.f * UIConfiguration::instance().get_ui_scale()));
573
574         delete points;
575         points = new ArdourCanvas::Points ();
576         points->push_back (ArdourCanvas::Duple ( M3, top));
577         points->push_back (ArdourCanvas::Duple ( M6, min (top + (MH * .6), MH)));
578         points->push_back (ArdourCanvas::Duple ( M6, MH));
579         points->push_back (ArdourCanvas::Duple (0.0, MH));
580         points->push_back (ArdourCanvas::Duple (0.0, min (top + (MH * .6), MH)));
581         points->push_back (ArdourCanvas::Duple ( M3, top));
582
583         mark->set (*points);
584 }
585
586 /***********************************************************************/
587
588 MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
589                           ARDOUR::MeterSection& m)
590         : ArdourMarker (editor, parent, rgba, text, Meter, m.sample(), false),
591           _meter (m)
592 {
593         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_meter_marker_event), group, this));
594 }
595
596 MeterMarker::~MeterMarker ()
597 {
598 }