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