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