NO-OP: whitespace
[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->set_outline_color (UIConfiguration::instance().color ("edit point"));
352                         _track_canvas_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
353                 }
354
355                 ArdourCanvas::Duple g = group->canvas_origin();
356                 ArdourCanvas::Duple d = _track_canvas_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
357
358                 _track_canvas_line->set_x0 (d.x);
359                 _track_canvas_line->set_x1 (d.x);
360                 _track_canvas_line->set_y0 (d.y);
361                 _track_canvas_line->set_y1 (ArdourCanvas::COORD_MAX);
362                 _track_canvas_line->set_outline_color (_selected ? UIConfiguration::instance().color ("edit point") : _color);
363                 _track_canvas_line->raise_to_top ();
364                 _track_canvas_line->show ();
365
366         } else {
367                 if (_track_canvas_line) {
368                         _track_canvas_line->hide ();
369                 }
370         }
371 }
372
373 void
374 ArdourMarker::canvas_height_set (double h)
375 {
376         _canvas_height = h;
377         setup_line ();
378 }
379
380 ArdourCanvas::Item&
381 ArdourMarker::the_item() const
382 {
383         return *group;
384 }
385
386 void
387 ArdourMarker::set_name (const string& new_name)
388 {
389         _name = new_name;
390
391         mark->set_tooltip(new_name);
392         _name_background->set_tooltip(new_name);
393         _name_item->set_tooltip(new_name);
394
395         setup_name_display ();
396 }
397
398 /** @return true if our label is on the left of the mark, otherwise false */
399 bool
400 ArdourMarker::label_on_left () const
401 {
402         return (_type == SessionEnd || _type == RangeEnd || _type == LoopEnd || _type == PunchOut);
403 }
404
405 void
406 ArdourMarker::setup_name_display ()
407 {
408         double limit = DBL_MAX;
409
410         if (label_on_left ()) {
411                 limit = _left_label_limit;
412         } else {
413                 limit = _right_label_limit;
414         }
415
416         const float padding =  std::max(2.f, rintf(2.f * UIConfiguration::instance().get_ui_scale()));
417         const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
418
419         /* Work out how wide the name can be */
420         int name_width = min ((double) pixel_width (_name, name_font) + padding, limit);
421
422         if (name_width == 0) {
423                 _name_item->hide ();
424         } else {
425                 _name_item->show ();
426
427                 if (label_on_left ()) {
428                         _name_item->set_x_position (-name_width);
429                 }
430
431                 _name_item->clamp_width (name_width);
432                 _name_item->set (_name);
433
434                 if (label_on_left ()) {
435                         /* adjust right edge of background to fit text */
436                         _name_background->set_x0 (_name_item->position().x - padding);
437                         _name_background->set_x1 (_name_item->position().x + name_width + _shift);
438                 } else {
439                         /* right edge remains at zero (group-relative). Add
440                          * arbitrary 2 pixels of extra padding at the end
441                          */
442                         switch (_type) {
443                                 case Tempo:
444                                         _name_item->hide ();
445                                         // tip's x-pos is at "M3", box is 2x marker's
446                                         _name_background->set_x0 (-M3);
447                                         _name_background->set_x1 (3 * M3);
448                                         break;
449                                 case Mark:
450                                 case Meter:
451                                         _name_background->set_x0 (M3);
452                                         _name_background->set_x1 (_name_item->position().x + name_width + padding);
453                                         break;
454                                 default:
455                                         _name_background->set_x0 (0);
456                                         _name_background->set_x1 (_name_item->position().x + name_width + padding);
457                                         break;
458                         }
459                 }
460         }
461
462         _name_background->set_y0 (0);
463         _name_background->set_y1 (marker_height + 1);
464 }
465
466 void
467 ArdourMarker::set_position (samplepos_t sample)
468 {
469         unit_position = editor.sample_to_pixel (sample) - _shift;
470         group->set_x_position (unit_position);
471         setup_line ();
472         sample_position = sample;
473 }
474
475 void
476 ArdourMarker::reposition ()
477 {
478         set_position (sample_position);
479 }
480
481 void
482 ArdourMarker::show ()
483 {
484         _shown = true;
485
486         group->show ();
487         setup_line ();
488 }
489
490 void
491 ArdourMarker::hide ()
492 {
493         _shown = false;
494
495         group->hide ();
496         setup_line ();
497 }
498
499 void
500 ArdourMarker::set_points_color (uint32_t c)
501 {
502         _points_color = c;
503         mark->set_fill_color (_points_color);
504         mark->set_outline_color (_points_color);
505 }
506
507 void
508 ArdourMarker::set_color_rgba (uint32_t c)
509 {
510         _color = c;
511         mark->set_fill_color (_color);
512         mark->set_outline_color (_color);
513
514         if (_track_canvas_line && !_selected) {
515                 _track_canvas_line->set_outline_color (_color);
516         }
517
518         _name_background->set_fill (true);
519         _name_background->set_fill_color (UINT_RGBA_CHANGE_A (_color, 0x70));
520         _name_background->set_outline (false);
521 }
522
523 /** Set the number of pixels that are available for a label to the left of the centre of this marker */
524 void
525 ArdourMarker::set_left_label_limit (double p)
526 {
527         /* Account for the size of the marker */
528         _left_label_limit = p - marker_height;
529         if (_left_label_limit < 0) {
530                 _left_label_limit = 0;
531         }
532
533         if (label_on_left ()) {
534                 setup_name_display ();
535         }
536 }
537
538 /** Set the number of pixels that are available for a label to the right of the centre of this marker */
539 void
540 ArdourMarker::set_right_label_limit (double p)
541 {
542         /* Account for the size of the marker */
543         _right_label_limit = p - marker_height;
544         if (_right_label_limit < 0) {
545                 _right_label_limit = 0;
546         }
547
548         if (!label_on_left ()) {
549                 setup_name_display ();
550         }
551 }
552
553 /***********************************************************************/
554
555 TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
556                           ARDOUR::TempoSection& temp)
557         : ArdourMarker (editor, parent, rgba, text, Tempo, temp.sample(), false),
558           _tempo (temp)
559 {
560         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), group, this));
561 }
562
563 TempoMarker::~TempoMarker ()
564 {
565 }
566
567 void
568 TempoMarker::update_height_mark (const double ratio)
569 {
570         const double MH = marker_height - .5;
571         const double top = MH * (1 - ratio);
572         const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
573         const double M6 = std::max(2.f, rintf(6.f * UIConfiguration::instance().get_ui_scale()));
574
575         delete points;
576         points = new ArdourCanvas::Points ();
577         points->push_back (ArdourCanvas::Duple ( M3, top));
578         points->push_back (ArdourCanvas::Duple ( M6, min (top + (MH * .6), MH)));
579         points->push_back (ArdourCanvas::Duple ( M6, MH));
580         points->push_back (ArdourCanvas::Duple (0.0, MH));
581         points->push_back (ArdourCanvas::Duple (0.0, min (top + (MH * .6), MH)));
582         points->push_back (ArdourCanvas::Duple ( M3, top));
583
584         mark->set (*points);
585 }
586
587 /***********************************************************************/
588
589 MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
590                           ARDOUR::MeterSection& m)
591         : ArdourMarker (editor, parent, rgba, text, Meter, m.sample(), false),
592           _meter (m)
593 {
594         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_meter_marker_event), group, this));
595 }
596
597 MeterMarker::~MeterMarker ()
598 {
599 }