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