the endless quest to plug memory leaks -- episode 379
[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         , _left_label_limit (DBL_MAX)
78         , _right_label_limit (DBL_MAX)
79         , _label_offset (0)
80
81 {
82
83         const double MH = marker_height - 1;
84         const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
85         const double M6 = std::max(2.f, rintf(6.f * UIConfiguration::instance().get_ui_scale()));
86
87         /* Shapes we use:
88          *
89          * Mark:
90          *
91          *  (0,0)   ->  (6,0)
92          *    ^           |
93          *    |           V
94          * (0,MH*.4)  (6,MH*.4)
95          *     \         /
96          *        (3,MH)
97          *
98          *
99          * TempoMark:
100          * MeterMark:
101          *
102          *        (3,0)
103          *     /         \
104          * (0,MH*.6)  (6,MH.*.6)
105          *    ^           |
106          *    |           V
107          * (0,MH)   <-  (6,MH)
108          *
109          *
110          * SessionStart:
111          * RangeStart:
112          *
113          *       0,0\
114          *        |  \
115          *        |   \ 6,MH/2
116          *        |   /
117          *        |  /
118          *       0,MH
119          *
120          *
121          * SessionEnd:
122          * RangeEnd:
123          *
124          *         /12,0
125          *        /   |
126          * 6,MH/2/    |
127          *       \    |
128          *        \   |
129          *         \12,MH
130          *
131          *
132          * PunchIn:
133          *
134          *   0,0 ------> marker_height,0
135          *    |       /
136          *    |      /
137          *    |     /
138          *    |    /
139          *    |   /
140          *    |  /
141          *   0,marker_height
142          *
143          *
144          *   PunchOut
145          *
146          *   0,0 ------> marker_height,0
147          *    \        |
148          *     \       |
149          *      \      |
150          *       \     |
151          *        \    |
152          *         \   |
153          *          \  |
154          *   marker_height,marker_height
155          *
156          */
157
158         switch (type) {
159         case Mark:
160                 points = new ArdourCanvas::Points ();
161
162                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
163                 points->push_back (ArdourCanvas::Duple ( M6, 0.0));
164                 points->push_back (ArdourCanvas::Duple ( M6, MH * .4));
165                 points->push_back (ArdourCanvas::Duple ( M3, MH));
166                 points->push_back (ArdourCanvas::Duple (0.0, MH * .4));
167                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
168
169                 _shift = 3;
170                 _label_offset = 10.0;
171                 break;
172
173         case Tempo:
174         case Meter:
175                 points = new ArdourCanvas::Points ();
176                 points->push_back (ArdourCanvas::Duple ( M3, 0.0));
177                 points->push_back (ArdourCanvas::Duple ( M6, MH * .6));
178                 points->push_back (ArdourCanvas::Duple ( M6, MH));
179                 points->push_back (ArdourCanvas::Duple (0.0, MH));
180                 points->push_back (ArdourCanvas::Duple (0.0, MH * .6));
181                 points->push_back (ArdourCanvas::Duple ( M3, 0.0));
182
183                 _shift = 3;
184                 _label_offset = 8.0;
185                 break;
186
187         case SessionStart:
188         case RangeStart:
189                 points = new ArdourCanvas::Points ();
190                 points->push_back (ArdourCanvas::Duple (    0.0, 0.0));
191                 points->push_back (ArdourCanvas::Duple (M6 + .5, MH * .5));
192                 points->push_back (ArdourCanvas::Duple (    0.0, MH));
193                 points->push_back (ArdourCanvas::Duple (    0.0, 0.0));
194
195                 _shift = 0;
196                 _label_offset = 8.0;
197                 break;
198
199         case SessionEnd:
200         case RangeEnd:
201                 points = new ArdourCanvas::Points (); // leaks
202                 points->push_back (ArdourCanvas::Duple ( M6, 0.0));
203                 points->push_back (ArdourCanvas::Duple ( M6, MH));
204                 points->push_back (ArdourCanvas::Duple (0.0, MH * .5));
205                 points->push_back (ArdourCanvas::Duple ( M6, 0.0));
206
207                 _shift = M6;
208                 _label_offset = 0.0;
209                 break;
210
211         case LoopStart:
212                 points = new ArdourCanvas::Points ();
213                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
214                 points->push_back (ArdourCanvas::Duple (marker_height, marker_height));
215                 points->push_back (ArdourCanvas::Duple (0.0, marker_height));
216                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
217
218                 _shift = 0;
219                 _label_offset = marker_height;
220                 break;
221
222         case LoopEnd:
223                 points = new ArdourCanvas::Points ();
224                 points->push_back (ArdourCanvas::Duple (marker_height,  0.0));
225                 points->push_back (ArdourCanvas::Duple (marker_height, marker_height));
226                 points->push_back (ArdourCanvas::Duple (0.0, marker_height));
227                 points->push_back (ArdourCanvas::Duple (marker_height, 0.0));
228
229                 _shift = marker_height;
230                 _label_offset = 0.0;
231                 break;
232
233         case PunchIn:
234                 points = new ArdourCanvas::Points ();
235                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
236                 points->push_back (ArdourCanvas::Duple (marker_height, 0.0));
237                 points->push_back (ArdourCanvas::Duple (0.0, marker_height));
238                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
239
240                 _shift = 0;
241                 _label_offset = marker_height;
242                 break;
243
244         case PunchOut:
245                 points = new ArdourCanvas::Points ();
246                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
247                 points->push_back (ArdourCanvas::Duple (marker_height, 0.0));
248                 points->push_back (ArdourCanvas::Duple (marker_height, marker_height));
249                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
250
251                 _shift = marker_height;
252                 _label_offset = 0.0;
253                 break;
254
255         }
256
257         frame_position = frame;
258         unit_position = editor.sample_to_pixel (frame);
259         unit_position -= _shift;
260
261         group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple (unit_position, 0));
262 #ifdef CANVAS_DEBUG
263         group->name = string_compose ("Marker::group for %1", annotation);
264 #endif
265
266         _name_background = new ArdourCanvas::Rectangle (group);
267 #ifdef CANVAS_DEBUG
268         _name_background->name = string_compose ("Marker::_name_background for %1", annotation);
269 #endif
270
271         /* adjust to properly locate the tip */
272
273         mark = new ArdourCanvas::Polygon (group);
274         CANVAS_DEBUG_NAME (mark, string_compose ("Marker::mark for %1", annotation));
275
276         mark->set (*points);
277         set_color_rgba (rgba);
278
279         /* setup name pixbuf sizes */
280         name_font = get_font_for_style (N_("MarkerText"));
281
282         Gtk::Label foo;
283
284         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
285         int width;
286
287         layout->set_font_description (name_font);
288         Gtkmm2ext::get_ink_pixel_size (layout, width, name_height);
289
290         _name_item = new ArdourCanvas::Text (group);
291         CANVAS_DEBUG_NAME (_name_item, string_compose ("ArdourMarker::_name_item for %1", annotation));
292         _name_item->set_font_description (name_font);
293         _name_item->set_color (RGBA_TO_UINT (0,0,0,255));
294         _name_item->set_position (ArdourCanvas::Duple (_label_offset, (marker_height - name_height - 1) * .5 ));
295
296         set_name (annotation.c_str());
297
298         editor.ZoomChanged.connect (sigc::mem_fun (*this, &ArdourMarker::reposition));
299
300         /* events will be handled by both the group and the mark itself, so
301          * make sure they can both be used to lookup this object.
302          */
303
304         group->set_data ("marker", this);
305         mark->set_data ("marker", this);
306
307         if (handle_events) {
308                 group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
309         }
310 }
311
312 ArdourMarker::~ArdourMarker ()
313 {
314         CatchDeletion (this); /* EMIT SIGNAL */
315
316         /* destroying the parent group destroys its contents, namely any polygons etc. that we added */
317         delete group;
318         delete _track_canvas_line;
319         delete points;
320 }
321
322 void ArdourMarker::reparent(ArdourCanvas::Container & parent)
323 {
324         group->reparent (&parent);
325         _parent = &parent;
326 }
327
328 void
329 ArdourMarker::set_selected (bool s)
330 {
331         _selected = s;
332         setup_line ();
333 }
334
335 void
336 ArdourMarker::set_show_line (bool s)
337 {
338         _line_shown = s;
339         setup_line ();
340 }
341
342 void
343 ArdourMarker::setup_line ()
344 {
345         if (_shown && (_selected || _line_shown)) {
346
347                 if (_track_canvas_line == 0) {
348
349                         _track_canvas_line = new ArdourCanvas::Line (editor.get_hscroll_group());
350                         _track_canvas_line->set_outline_color (UIConfiguration::instance().color ("edit point"));
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 ("edit point") : _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         setup_name_display ();
391 }
392
393 /** @return true if our label is on the left of the mark, otherwise false */
394 bool
395 ArdourMarker::label_on_left () const
396 {
397         return (_type == SessionEnd || _type == RangeEnd || _type == LoopEnd || _type == PunchOut);
398 }
399
400 void
401 ArdourMarker::setup_name_display ()
402 {
403         double limit = DBL_MAX;
404
405         if (label_on_left ()) {
406                 limit = _left_label_limit;
407         } else {
408                 limit = _right_label_limit;
409         }
410
411         const float padding =  std::max(2.f, rintf(2.f * UIConfiguration::instance().get_ui_scale()));
412
413         /* Work out how wide the name can be */
414         int name_width = min ((double) pixel_width (_name, name_font) + padding, limit);
415
416         if (name_width == 0) {
417                 _name_item->hide ();
418         } else {
419                 _name_item->show ();
420
421                 if (label_on_left ()) {
422                         _name_item->set_x_position (-name_width);
423                 }
424
425                 _name_item->clamp_width (name_width);
426                 _name_item->set (_name);
427
428                 if (label_on_left ()) {
429                         /* adjust right edge of background to fit text */
430                         _name_background->set_x0 (_name_item->position().x - padding);
431                         _name_background->set_x1 (_name_item->position().x + name_width + _shift);
432                 } else {
433                         /* right edge remains at zero (group-relative). Add
434                          * arbitrary 2 pixels of extra padding at the end
435                          */
436                         _name_background->set_x1 (_name_item->position().x + name_width + padding);
437                 }
438         }
439
440         _name_background->set_y0 (0);
441         /* unfortunate hard coding - this has to * match the marker bars height */
442         _name_background->set_y1 (marker_height + 1.0);
443 }
444
445 void
446 ArdourMarker::set_position (framepos_t frame)
447 {
448         unit_position = editor.sample_to_pixel (frame) - _shift;
449         group->set_x_position (unit_position);
450         setup_line ();
451         frame_position = frame;
452 }
453
454 void
455 ArdourMarker::reposition ()
456 {
457         set_position (frame_position);
458 }
459
460 void
461 ArdourMarker::show ()
462 {
463         _shown = true;
464
465         group->show ();
466         setup_line ();
467 }
468
469 void
470 ArdourMarker::hide ()
471 {
472         _shown = false;
473
474         group->hide ();
475         setup_line ();
476 }
477
478 void
479 ArdourMarker::set_color_rgba (uint32_t c)
480 {
481         _color = c;
482         mark->set_fill_color (_color);
483         mark->set_outline_color (_color);
484
485         if (_track_canvas_line && !_selected) {
486                 _track_canvas_line->set_outline_color (_color);
487         }
488
489         _name_background->set_fill (true);
490         _name_background->set_fill_color (UINT_RGBA_CHANGE_A (_color, 0x70));
491         _name_background->set_outline_color (_color);
492 }
493
494 /** Set the number of pixels that are available for a label to the left of the centre of this marker */
495 void
496 ArdourMarker::set_left_label_limit (double p)
497 {
498         /* Account for the size of the marker */
499         _left_label_limit = p - marker_height;
500         if (_left_label_limit < 0) {
501                 _left_label_limit = 0;
502         }
503
504         if (label_on_left ()) {
505                 setup_name_display ();
506         }
507 }
508
509 /** Set the number of pixels that are available for a label to the right of the centre of this marker */
510 void
511 ArdourMarker::set_right_label_limit (double p)
512 {
513         /* Account for the size of the marker */
514         _right_label_limit = p - marker_height;
515         if (_right_label_limit < 0) {
516                 _right_label_limit = 0;
517         }
518
519         if (!label_on_left ()) {
520                 setup_name_display ();
521         }
522 }
523
524 /***********************************************************************/
525
526 TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
527                           ARDOUR::TempoSection& temp)
528         : ArdourMarker (editor, parent, rgba, text, Tempo, 0, false),
529           _tempo (temp)
530 {
531         set_position (_tempo.frame());
532         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), group, this));
533 }
534
535 TempoMarker::~TempoMarker ()
536 {
537 }
538
539 void
540 TempoMarker::update_height_mark (const double& ratio)
541 {
542         const double MH = marker_height;
543         const double top = MH * (1 - ratio);
544         const double M3 = std::max(1.f, rintf(3.f * UIConfiguration::instance().get_ui_scale()));
545         const double M6 = std::max(2.f, rintf(6.f * UIConfiguration::instance().get_ui_scale()));
546
547         delete points;
548         points = new ArdourCanvas::Points ();
549         points->push_back (ArdourCanvas::Duple ( M3, top));
550         points->push_back (ArdourCanvas::Duple ( M6, min (top + (MH * .6), MH)));
551         points->push_back (ArdourCanvas::Duple ( M6, MH));
552         points->push_back (ArdourCanvas::Duple (0.0, MH));
553         points->push_back (ArdourCanvas::Duple (0.0, min (top + (MH * .6), MH)));
554         points->push_back (ArdourCanvas::Duple ( M3, top));
555
556         mark->set (*points);
557 }
558
559 /***********************************************************************/
560
561 MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
562                           ARDOUR::MeterSection& m)
563         : ArdourMarker (editor, parent, rgba, text, Meter, 0, false),
564           _meter (m)
565 {
566         set_position (_meter.frame());
567         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_meter_marker_event), group, this));
568 }
569
570 MeterMarker::~MeterMarker ()
571 {
572 }
573