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