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