Merge branch 'waveview_caching_for_upstream' of https://github.com/nmains/ardour...
[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/group.h"
25 #include "canvas/line.h"
26 #include "canvas/polygon.h"
27 #include "canvas/text.h"
28 #include "canvas/canvas.h"
29 #include "canvas/debug.h"
30
31 #include "ardour_ui.h"
32 /*
33  * ardour_ui.h include was moved to the top of the list
34  * due to a conflicting definition of 'Rect' between
35  * Apple's MacTypes.h and GTK.
36  */
37
38 #include "marker.h"
39 #include "public_editor.h"
40 #include "utils.h"
41 #include "rgb_macros.h"
42
43 #include <gtkmm2ext/utils.h>
44
45 #include "i18n.h"
46
47 using namespace std;
48 using namespace ARDOUR;
49 using namespace Gtkmm2ext;
50
51 PBD::Signal1<void,Marker*> Marker::CatchDeletion;
52
53 static const double marker_height = 13.0;
54
55 Marker::Marker (PublicEditor& ed, ArdourCanvas::Group& parent, guint32 rgba, const string& annotation,
56                 Type type, framepos_t frame, bool handle_events)
57
58         : editor (ed)
59         , _parent (&parent)
60         , _track_canvas_line (0)
61         , _type (type)
62         , _selected (false)
63         , _shown (false)
64         , _line_shown (false)
65         , _color (rgba)
66         , _left_label_limit (DBL_MAX)
67         , _right_label_limit (DBL_MAX)
68         , _label_offset (0)
69
70 {
71         /* Shapes we use:
72
73           Mark:
74
75            (0,0) -> (6,0)
76              ^        |
77              |        V
78            (0,5)    (6,5)
79                \    /
80                (3,marker_height)
81
82
83            TempoMark:
84            MeterMark:
85
86                (3,0)
87               /      \
88            (0,5) -> (6,5)
89              ^        |
90              |        V
91            (0,10)<-(6,10)
92
93
94            Start:
95
96            0,0\
97             |  \
98             |   \ 6,6
99             |   /
100             |  /
101            0,12
102
103            End:
104
105                /12,0
106               /     |
107              /      |
108            6,6      |
109              \      |
110               \     |
111                \    |
112                12,12
113
114              PunchIn:
115
116              0,0 ------> marker_height,0
117               |       /
118               |      /
119               |     /
120               |    /
121               |   /
122               |  /
123              0,marker_height
124
125              PunchOut
126
127            0,0 -->-marker_height,0
128             \       |
129              \      |
130               \     |
131                \    |
132                 \   |
133                  \  |
134                  marker_height,marker_height
135
136
137         */
138
139         switch (type) {
140         case Mark:
141                 points = new ArdourCanvas::Points ();
142
143                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
144                 points->push_back (ArdourCanvas::Duple (6.0, 0.0));
145                 points->push_back (ArdourCanvas::Duple (6.0, 5.0));
146                 points->push_back (ArdourCanvas::Duple (3.0, marker_height));
147                 points->push_back (ArdourCanvas::Duple (0.0, 5.0));
148                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
149
150                 _shift = 3;
151                 _label_offset = 8.0;
152                 break;
153
154         case Tempo:
155         case Meter:
156
157                 points = new ArdourCanvas::Points ();
158                 points->push_back (ArdourCanvas::Duple (3.0, 0.0));
159                 points->push_back (ArdourCanvas::Duple (6.0, 5.0));
160                 points->push_back (ArdourCanvas::Duple (6.0, 10.0));
161                 points->push_back (ArdourCanvas::Duple (0.0, 10.0));
162                 points->push_back (ArdourCanvas::Duple (0.0, 5.0));
163                 points->push_back (ArdourCanvas::Duple (3.0, 0.0));
164
165                 _shift = 3;
166                 _label_offset = 8.0;
167                 break;
168
169         case SessionStart:
170         case RangeStart:
171
172                 points = new ArdourCanvas::Points ();
173                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
174                 points->push_back (ArdourCanvas::Duple (6.5, 6.5));
175                 points->push_back (ArdourCanvas::Duple (0.0, marker_height));
176                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
177
178                 _shift = 0;
179                 _label_offset = marker_height;
180                 break;
181
182         case SessionEnd:
183         case RangeEnd:
184                 points = new ArdourCanvas::Points ();
185                 points->push_back (ArdourCanvas::Duple (6.5, 6.5));
186                 points->push_back (ArdourCanvas::Duple (marker_height, 0.0));
187                 points->push_back (ArdourCanvas::Duple (marker_height, marker_height));
188                 points->push_back (ArdourCanvas::Duple (6.5, 6.5));
189
190                 _shift = marker_height;
191                 _label_offset = 6.0;
192                 break;
193
194         case LoopStart:
195                 points = new ArdourCanvas::Points ();
196                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
197                 points->push_back (ArdourCanvas::Duple (marker_height, marker_height));
198                 points->push_back (ArdourCanvas::Duple (0.0, marker_height));
199                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
200
201                 _shift = 0;
202                 _label_offset = 12.0;
203                 break;
204
205         case LoopEnd:
206                 points = new ArdourCanvas::Points ();
207                 points->push_back (ArdourCanvas::Duple (marker_height,  0.0));
208                 points->push_back (ArdourCanvas::Duple (marker_height, marker_height));
209                 points->push_back (ArdourCanvas::Duple (0.0, marker_height));
210                 points->push_back (ArdourCanvas::Duple (marker_height, 0.0));
211
212                 _shift = marker_height;
213                 _label_offset = 0.0;
214                 break;
215
216         case  PunchIn:
217                 points = new ArdourCanvas::Points ();
218                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
219                 points->push_back (ArdourCanvas::Duple (marker_height, 0.0));
220                 points->push_back (ArdourCanvas::Duple (0.0, marker_height));
221                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
222
223                 _shift = 0;
224                 _label_offset = marker_height;
225                 break;
226
227         case  PunchOut:
228                 points = new ArdourCanvas::Points ();
229                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
230                 points->push_back (ArdourCanvas::Duple (12.0, 0.0));
231                 points->push_back (ArdourCanvas::Duple (12.0, 12.0));
232                 points->push_back (ArdourCanvas::Duple (0.0, 0.0));
233
234                 _shift = marker_height;
235                 _label_offset = 0.0;
236                 break;
237
238         }
239
240         frame_position = frame;
241         unit_position = editor.sample_to_pixel (frame);
242         unit_position -= _shift;
243
244         group = new ArdourCanvas::Group (&parent, ArdourCanvas::Duple (unit_position, 0));
245 #ifdef CANVAS_DEBUG
246         group->name = string_compose ("Marker::group for %1", annotation);
247 #endif  
248
249         _name_background = new ArdourCanvas::Rectangle (group);
250 #ifdef CANVAS_DEBUG
251         _name_background->name = string_compose ("Marker::_name_background for %1", annotation);
252 #endif  
253
254         /* adjust to properly locate the tip */
255
256         mark = new ArdourCanvas::Polygon (group);
257         CANVAS_DEBUG_NAME (mark, string_compose ("Marker::mark for %1", annotation));
258
259         mark->set (*points);
260         set_color_rgba (rgba);
261
262         /* setup name pixbuf sizes */
263         name_font = get_font_for_style (N_("MarkerText"));
264
265         Gtk::Label foo;
266
267         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
268         int width;
269
270         layout->set_font_description (name_font);
271         Gtkmm2ext::get_ink_pixel_size (layout, width, name_height);
272         
273         _name_item = new ArdourCanvas::Text (group);
274         CANVAS_DEBUG_NAME (_name_item, string_compose ("Marker::_name_item for %1", annotation));
275         _name_item->set_font_description (name_font);
276         _name_item->set_color (RGBA_TO_UINT (0,0,0,255));
277         _name_item->set_position (ArdourCanvas::Duple (_label_offset, (marker_height / 2.0) - (name_height / 2.0) - 2.0));
278
279         set_name (annotation.c_str());
280
281         editor.ZoomChanged.connect (sigc::mem_fun (*this, &Marker::reposition));
282
283         /* events will be handled by both the group and the mark itself, so
284          * make sure they can both be used to lookup this object.
285          */
286
287         group->set_data ("marker", this);
288         mark->set_data ("marker", this);
289         
290         if (handle_events) {
291                 group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
292         }
293 }
294
295 Marker::~Marker ()
296 {
297         CatchDeletion (this); /* EMIT SIGNAL */
298
299         /* destroying the parent group destroys its contents, namely any polygons etc. that we added */
300         delete group;
301         delete _track_canvas_line;
302 }
303
304 void Marker::reparent(ArdourCanvas::Group & parent)
305 {
306         group->reparent (&parent);
307         _parent = &parent;
308 }
309
310 void
311 Marker::set_selected (bool s)
312 {
313         _selected = s;
314         setup_line ();
315 }
316
317 void
318 Marker::set_show_line (bool s)
319 {
320         _line_shown = s;
321         setup_line ();
322 }
323
324 void
325 Marker::setup_line ()
326 {
327         if (_shown && (_selected || _line_shown)) {
328
329                 if (_track_canvas_line == 0) {
330
331                         _track_canvas_line = new ArdourCanvas::Line (editor.get_hscroll_group());
332                         _track_canvas_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EditPoint());
333                         _track_canvas_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
334                 }
335
336                 ArdourCanvas::Duple g = group->canvas_origin();
337                 ArdourCanvas::Duple d = _track_canvas_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
338
339                 _track_canvas_line->set_x0 (d.x);
340                 _track_canvas_line->set_x1 (d.x);
341                 _track_canvas_line->set_y0 (d.y);
342                 _track_canvas_line->set_y1 (ArdourCanvas::COORD_MAX);
343                 _track_canvas_line->set_outline_color (_selected ? ARDOUR_UI::config()->get_canvasvar_EditPoint() : _color);
344                 _track_canvas_line->raise_to_top ();
345                 _track_canvas_line->show ();
346
347         } else {
348                 if (_track_canvas_line) {
349                         _track_canvas_line->hide ();
350                 }
351         }
352 }
353
354 void
355 Marker::canvas_height_set (double h)
356 {
357         _canvas_height = h;
358         setup_line ();
359 }
360
361 ArdourCanvas::Item&
362 Marker::the_item() const
363 {
364         return *group;
365 }
366
367 void
368 Marker::set_name (const string& new_name)
369 {
370         _name = new_name;
371
372         setup_name_display ();
373 }
374
375 /** @return true if our label is on the left of the mark, otherwise false */
376 bool
377 Marker::label_on_left () const
378 {
379         return (_type == SessionEnd || _type == RangeEnd || _type == LoopEnd || _type == PunchOut);
380 }
381
382 void
383 Marker::setup_name_display ()
384 {
385         double limit = DBL_MAX;
386
387         if (label_on_left ()) {
388                 limit = _left_label_limit;
389         } else {
390                 limit = _right_label_limit;
391         }
392
393         /* Work out how wide the name can be */
394         int name_width = min ((double) pixel_width (_name, name_font) + 2, limit);
395
396         if (name_width == 0) {
397                 _name_item->hide ();
398         } else {
399                 _name_item->show ();
400
401                 if (label_on_left ()) {
402                         _name_item->set_x_position (-name_width);
403                 }
404                         
405                 _name_item->clamp_width (name_width);
406                 _name_item->set (_name);
407                 
408                 if (label_on_left ()) {
409                         _name_background->set_x0 (_name_item->position().x - 2);
410                         _name_background->set_x1 (_name_item->position().x + name_width + _shift);
411                 } else {
412                         _name_background->set_x0 (_name_item->position().x - _label_offset + 2);
413                         _name_background->set_x1 (_name_item->position().x + name_width);
414                 }
415         }
416
417         _name_background->set_y0 (0);
418         /* unfortunate hard coding - this has to * match the marker bars height */
419         _name_background->set_y1 (marker_height + 1.0); 
420 }
421
422 void
423 Marker::set_position (framepos_t frame)
424 {
425         unit_position = editor.sample_to_pixel (frame) - _shift;
426         group->set_x_position (unit_position);
427         setup_line ();
428         frame_position = frame;
429 }
430
431 void
432 Marker::reposition ()
433 {
434         set_position (frame_position);
435 }
436
437 void
438 Marker::show ()
439 {
440         _shown = true;
441
442         group->show ();
443         setup_line ();
444 }
445
446 void
447 Marker::hide ()
448 {
449         _shown = false;
450
451         group->hide ();
452         setup_line ();
453 }
454
455 void
456 Marker::set_color_rgba (uint32_t c)
457 {
458         _color = c;
459         mark->set_fill_color (_color);
460         mark->set_outline_color (_color);
461
462         if (_track_canvas_line && !_selected) {
463                 _track_canvas_line->set_outline_color (_color);
464         }
465
466         _name_background->set_fill (true);
467         _name_background->set_fill_color (UINT_RGBA_CHANGE_A (_color, 0x70));
468         _name_background->set_outline_color (_color);
469 }
470
471 /** Set the number of pixels that are available for a label to the left of the centre of this marker */
472 void
473 Marker::set_left_label_limit (double p)
474 {
475         /* Account for the size of the marker */
476         _left_label_limit = p - marker_height;
477         if (_left_label_limit < 0) {
478                 _left_label_limit = 0;
479         }
480
481         if (label_on_left ()) {
482                 setup_name_display ();
483         }
484 }
485
486 /** Set the number of pixels that are available for a label to the right of the centre of this marker */
487 void
488 Marker::set_right_label_limit (double p)
489 {
490         /* Account for the size of the marker */
491         _right_label_limit = p - marker_height;
492         if (_right_label_limit < 0) {
493                 _right_label_limit = 0;
494         }
495
496         if (!label_on_left ()) {
497                 setup_name_display ();
498         }
499 }
500
501 /***********************************************************************/
502
503 TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
504                           ARDOUR::TempoSection& temp)
505         : Marker (editor, parent, rgba, text, Tempo, 0, false),
506           _tempo (temp)
507 {
508         set_position (_tempo.frame());
509         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), group, this));
510 }
511
512 TempoMarker::~TempoMarker ()
513 {
514 }
515
516 /***********************************************************************/
517
518 MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
519                           ARDOUR::MeterSection& m)
520         : Marker (editor, parent, rgba, text, Meter, 0, false),
521           _meter (m)
522 {
523         set_position (_meter.frame());
524         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_meter_marker_event), group, this));
525 }
526
527 MeterMarker::~MeterMarker ()
528 {
529 }
530