initial commit of hand merging, plus getting "ancient" waf script to work correctly
[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/pixbuf.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_pixbuf = new ArdourCanvas::Pixbuf (group);
273 #ifdef CANVAS_DEBUG
274         name_pixbuf->name = string_compose ("Marker::name_pixbuf for %1", annotation);
275 #endif  
276         name_pixbuf->set_position (ArdourCanvas::Duple (_label_offset, 13 / 2 - name_height / 2));
277
278         set_name (annotation.c_str());
279
280         editor.ZoomChanged.connect (sigc::mem_fun (*this, &Marker::reposition));
281
282         group->set_data ("marker", this);
283
284         if (handle_events) {
285                 group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
286         }
287
288 }
289
290
291 Marker::~Marker ()
292 {
293         CatchDeletion (this); /* EMIT SIGNAL */
294
295         /* destroying the parent group destroys its contents, namely any polygons etc. that we added */
296         delete group;
297         delete _line;
298 }
299
300 void Marker::reparent(ArdourCanvas::Group & parent)
301 {
302         group->reparent (&parent);
303         _parent = &parent;
304 }
305
306 void
307 Marker::set_selected (bool s)
308 {
309         _selected = s;
310         setup_line ();
311 }
312
313 void
314 Marker::set_show_line (bool s)
315 {
316         _line_shown = s;
317         setup_line ();
318 }
319
320 void
321 Marker::setup_line ()
322 {
323         if (_shown && (_selected || _line_shown)) {
324
325                 if (_line == 0) {
326
327                         _line = new ArdourCanvas::Line (*group);
328                         _line->set_outline_color (ARDOUR_UI::config()->canvasvar_EditPoint.get());
329
330                         _line->signal_event().connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), mark, this));
331                 }
332
333                 /* work out where to start the line from so that it extends from the top of the canvas */
334                 double yo = 0;
335                 double xo = 0;
336
337                 _line->item_to_canvas (xo, yo);
338
339                 _line->set_x0 (_shift);
340                 _line->set_x1 (_shift);
341                 _line->set_y0 (-yo); // zero in world coordinates, negative in item/parent coordinate space
342                 _line->set_y1 (-yo + _canvas_height);
343
344                 _line->set_outline_color (_selected ? ARDOUR_UI::config()->canvasvar_EditPoint.get() : _color);
345                 _line->raise_to_top ();
346                 _line->show ();
347
348         } else {
349                 if (_line) {
350                         _line->hide ();
351                 }
352         }
353 }
354
355 void
356 Marker::canvas_height_set (double h)
357 {
358         _canvas_height = h;
359         setup_line ();
360 }
361
362 ArdourCanvas::Item&
363 Marker::the_item() const
364 {
365         return *mark;
366 }
367
368 void
369 Marker::set_name (const string& new_name)
370 {
371         _name = new_name;
372
373         setup_name_display ();
374 }
375
376 /** @return true if our label is on the left of the mark, otherwise false */
377 bool
378 Marker::label_on_left () const
379 {
380         return (_type == SessionEnd || _type == RangeEnd || _type == LoopEnd || _type == PunchOut);
381 }
382
383 void
384 Marker::setup_name_display ()
385 {
386         double limit = DBL_MAX;
387
388         if (label_on_left ()) {
389                 limit = _left_label_limit;
390         } else {
391                 limit = _right_label_limit;
392         }
393
394         /* Work out how wide the name can be */
395         int name_width = min ((double) pixel_width (_name, name_font) + 2, limit);
396         if (name_width == 0) {
397                 name_width = 1;
398         }
399
400         if (label_on_left ()) {
401                 name_pixbuf->set_x_position (-name_width);
402         }
403
404         name_pixbuf->set (pixbuf_from_string (_name, name_font, name_width, name_height, Gdk::Color ("#000000")));
405
406         if (label_on_left ()) {
407                 _name_background->set_x0 (name_pixbuf->position().x - 2);
408                 _name_background->property_x1() = name_pixbuf->position().x + name_width + _shift;
409         } else {
410                 _name_background->x0 (name_pixbuf->position().x - _label_offset + 2);
411                 _name_background->property_x1() = name_pixbuf->position().x + name_width;
412         }
413
414         _name_background->set_y0 (0);
415         _name_background->set_y1 (13);
416 }
417
418 void
419 Marker::set_position (framepos_t frame)
420 {
421         unit_position = editor.frame_to_unit (frame) - _shift;
422         group->set_x_position (unit_position);
423         frame_position = frame;
424 }
425
426 void
427 Marker::reposition ()
428 {
429         set_position (frame_position);
430 }
431
432 void
433 Marker::show ()
434 {
435         _shown = true;
436
437         group->show ();
438         setup_line ();
439 }
440
441 void
442 Marker::hide ()
443 {
444         _shown = false;
445
446         group->hide ();
447         setup_line ();
448 }
449
450 void
451 Marker::set_color_rgba (uint32_t c)
452 {
453         _color = c;
454         mark->set_fill_color (_color);
455         mark->set_outline_color (_color);
456
457         if (_line && !_selected) {
458                 _line->set_outline_color (_color);
459         }
460
461         _name_background->set_fill (true);
462         _name_background->set_fill_color (UINT_RGBA_CHANGE_A (_color, 0x70));
463         _name_background->set_outline_color (_color);
464 }
465
466 /** Set the number of pixels that are available for a label to the left of the centre of this marker */
467 void
468 Marker::set_left_label_limit (double p)
469 {
470         /* Account for the size of the marker */
471         _left_label_limit = p - 13;
472         if (_left_label_limit < 0) {
473                 _left_label_limit = 0;
474         }
475
476         if (label_on_left ()) {
477                 setup_name_display ();
478         }
479 }
480
481 /** Set the number of pixels that are available for a label to the right of the centre of this marker */
482 void
483 Marker::set_right_label_limit (double p)
484 {
485         /* Account for the size of the marker */
486         _right_label_limit = p - 13;
487         if (_right_label_limit < 0) {
488                 _right_label_limit = 0;
489         }
490
491         if (!label_on_left ()) {
492                 setup_name_display ();
493         }
494 }
495
496 /***********************************************************************/
497
498 TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
499                           ARDOUR::TempoSection& temp)
500         : Marker (editor, parent, rgba, text, Tempo, 0, false),
501           _tempo (temp)
502 {
503         set_position (_tempo.frame());
504         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), mark, this));
505 }
506
507 TempoMarker::~TempoMarker ()
508 {
509 }
510
511 /***********************************************************************/
512
513 MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
514                           ARDOUR::MeterSection& m)
515         : Marker (editor, parent, rgba, text, Meter, 0, false),
516           _meter (m)
517 {
518         set_position (_meter.frame());
519         group->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_meter_marker_event), mark, this));
520 }
521
522 MeterMarker::~MeterMarker ()
523 {
524 }
525