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