A different fix for record crash, use pixfufs instead of canvas text in markers,...
[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 "marker.h"
24 #include "public_editor.h"
25 #include "utils.h"
26 #include "canvas_impl.h"
27 #include "ardour_ui.h"
28 #include "simpleline.h"
29
30 #include <gtkmm2ext/utils.h>
31
32 #include "i18n.h"
33
34 using namespace std;
35 using namespace ARDOUR;
36
37 Marker::Marker (PublicEditor& ed, ArdourCanvas::Group& parent, guint32 rgba, const string& annotation, 
38                 Type type, nframes_t frame, bool handle_events)
39
40         : editor (ed), _parent(&parent), _type(type)
41 {
42         double label_offset = 0;
43
44         /* Shapes we use:
45
46           Mark:
47
48            (0,0) -> (6,0)
49              ^        |
50              |        V
51            (0,5)    (6,5)
52                \    / 
53                (3,10)
54
55
56            TempoMark:
57            MeterMark:
58
59                (3,0)
60               /      \
61            (0,5) -> (6,5)
62              ^        |
63              |        V
64            (0,10)<-(6,10)
65
66
67            Start:
68
69            0,0\ 
70             |  \        
71             |   \ 6,6
72             |   /
73             |  /
74            0,12 
75
76            End:
77
78                /12,0
79               /     | 
80              /      |
81            6,6      |
82              \      |
83               \     |
84                \    |
85                12,12
86
87              
88            TransportStart:
89
90              0,0
91               | \ 
92               |  \ 
93               |   \ 
94               |    \
95               |     \  
96              0,13 --- 13,13
97
98            TransportEnd:
99
100                     /13,0
101                    /   |
102                   /    |
103                  /     |
104                 /      |
105                /       |
106              0,13 ------ 13,13
107              
108
109              PunchIn:
110
111              0,0 ------> 13,0
112               |       /
113               |      /
114               |     /
115               |    / 
116               |   / 
117               |  / 
118              0,13
119
120              PunchOut
121
122            0,0 -->-13,0
123             \       | 
124              \      |
125               \     |
126                \    |
127                 \   |
128                  \  |
129                  13,13
130              
131            
132         */
133
134         switch (type) {
135         case Mark:
136                 points = new ArdourCanvas::Points ();
137
138                 points->push_back (Gnome::Art::Point (0.0, 0.0));
139                 points->push_back (Gnome::Art::Point (6.0, 0.0));
140                 points->push_back (Gnome::Art::Point (6.0, 5.0));
141                 points->push_back (Gnome::Art::Point (3.0, 10.0));              
142                 points->push_back (Gnome::Art::Point (0.0, 5.0));               
143                 points->push_back (Gnome::Art::Point (0.0, 0.0));               
144                 
145                 shift = 3;
146                 label_offset = 8.0;
147                 break;
148
149         case Tempo:
150         case Meter:
151
152                 points = new ArdourCanvas::Points ();
153                 points->push_back (Gnome::Art::Point (3.0, 0.0));
154                 points->push_back (Gnome::Art::Point (6.0, 5.0));               
155                 points->push_back (Gnome::Art::Point (6.0, 10.0));              
156                 points->push_back (Gnome::Art::Point (0.0, 10.0));              
157                 points->push_back (Gnome::Art::Point (0.0, 5.0));               
158                 points->push_back (Gnome::Art::Point (3.0, 0.0));               
159
160                 shift = 3;
161                 label_offset = 8.0;
162                 break;
163
164         case Start:
165                 points = new ArdourCanvas::Points ();
166                 points->push_back (Gnome::Art::Point (0.0, 0.0));               
167                 points->push_back (Gnome::Art::Point (6.5, 6.5));               
168                 points->push_back (Gnome::Art::Point (0.0, 13.0));              
169                 points->push_back (Gnome::Art::Point (0.0, 0.0));       
170
171                 shift = 0;
172                 label_offset = 13.0;
173                 break;
174
175         case End:
176                 points = new ArdourCanvas::Points ();
177                 points->push_back (Gnome::Art::Point (6.5, 6.5));
178                 points->push_back (Gnome::Art::Point (13.0, 0.0));              
179                 points->push_back (Gnome::Art::Point (13.0, 13.0));                     
180                 points->push_back (Gnome::Art::Point (6.5, 6.5));               
181                 
182                 shift = 13;
183                 label_offset = 6.0;
184                 break;
185
186         case LoopStart:
187                 points = new ArdourCanvas::Points ();
188                 points->push_back (Gnome::Art::Point (0.0, 0.0));       
189                 points->push_back (Gnome::Art::Point (13.0, 13.0));             
190                 points->push_back (Gnome::Art::Point (0.0, 13.0));              
191                 points->push_back (Gnome::Art::Point (0.0, 0.0));               
192                 
193                 shift = 0;
194                 label_offset = 12.0;
195                 break;
196
197         case LoopEnd:
198                 points = new ArdourCanvas::Points ();
199                 points->push_back (Gnome::Art::Point (13.0,  0.0));
200                 points->push_back (Gnome::Art::Point (13.0, 13.0));     
201                 points->push_back (Gnome::Art::Point (0.0, 13.0));              
202                 points->push_back (Gnome::Art::Point (13.0, 0.0));
203                 
204                 shift = 13;
205                 label_offset = 0.0;
206                 break;
207
208         case  PunchIn:
209                 points = new ArdourCanvas::Points ();
210                 points->push_back (Gnome::Art::Point (0.0, 0.0));
211                 points->push_back (Gnome::Art::Point (13.0, 0.0));              
212                 points->push_back (Gnome::Art::Point (0.0, 13.0));      
213                 points->push_back (Gnome::Art::Point (0.0, 0.0));       
214
215                 shift = 0;
216                 label_offset = 13.0;
217                 break;
218                 
219         case  PunchOut:
220                 points = new ArdourCanvas::Points ();
221                 points->push_back (Gnome::Art::Point (0.0, 0.0));
222                 points->push_back (Gnome::Art::Point (12.0, 0.0));                      
223                 points->push_back (Gnome::Art::Point (12.0, 12.0));             
224                 points->push_back (Gnome::Art::Point (0.0, 0.0));               
225
226                 shift = 13;
227                 label_offset = 0.0;
228                 break;
229                 
230         }
231
232         frame_position = frame;
233         unit_position = editor.frame_to_unit (frame);
234
235         /* adjust to properly locate the tip */
236
237         unit_position -= shift;
238
239         group = new Group (parent, unit_position, 1.0);
240
241         mark = new Polygon (*group);
242         mark->property_points() = *points;
243         mark->property_fill_color_rgba() = rgba;
244         mark->property_outline_color_rgba() = rgba;
245         mark->property_width_pixels() = 1;
246
247         /* setup name pixbuf sizes */
248         name_font = get_font_for_style (N_("MarkerText"));
249         
250         Gtk::Window win;
251         Gtk::Label foo;
252         win.add (foo);
253         
254         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
255         int width;
256         int height;
257         
258         layout->set_font_description (*name_font);
259         Gtkmm2ext::get_ink_pixel_size (layout, width, height);
260         name_height = height + 6;
261         
262         name_pixbuf = new ArdourCanvas::Pixbuf(*group);
263         name_pixbuf->property_x() = label_offset;
264         
265         set_name (annotation.c_str());
266         
267         editor.ZoomChanged.connect (mem_fun (*this, &Marker::reposition));
268
269         mark->set_data ("marker", this);
270
271         if (handle_events) {
272                 group->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_marker_event), mark, this));
273         }
274
275         line = 0;
276
277 }
278
279
280 Marker::~Marker ()
281 {
282         drop_references ();
283
284         /* destroying the parent group destroys its contents, namely any polygons etc. that we added */
285         delete name_pixbuf;
286         delete mark;
287         delete points;
288
289         delete line;
290         line = 0;
291 }
292
293 void Marker::reparent(ArdourCanvas::Group & parent)
294 {
295         group->reparent(parent);
296         _parent = &parent;
297 }
298
299
300 void
301 Marker::set_line_vpos (double pos, double height)
302 {
303         if (line) {
304                 line->property_y1() = pos;
305                 line->property_y2() = pos + height;
306         }
307 }
308
309 void
310 Marker::add_line (ArdourCanvas::Group* group, double y_origin, double initial_height)
311 {
312         if (!line) {
313
314                 line = new ArdourCanvas::SimpleLine (*group);
315                 line->property_color_rgba() = ARDOUR_UI::config()->canvasvar_EditPoint.get();
316                 line->property_x1() = unit_position + shift;
317                 line->property_y1() = y_origin;
318                 line->property_x2() = unit_position + shift;
319                 line->property_y2() = y_origin + initial_height;
320
321                 line->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_marker_event), mark, this));
322         }
323
324         show_line ();
325 }
326
327 void
328 Marker::show_line ()
329 {
330         if (line) {
331                 line->raise_to_top();
332                 line->show ();
333         }
334 }
335
336 void 
337 Marker::hide_line ()
338 {
339         if (line) {
340                 line->hide ();
341         }
342 }
343
344 ArdourCanvas::Item&
345 Marker::the_item() const
346 {
347         return *mark;
348 }
349
350 void
351 Marker::set_name (const string& new_name)
352 {
353         uint32_t pb_width;
354         double font_size;
355         
356         font_size = name_font->get_size() / Pango::SCALE;
357         pb_width = new_name.length() * font_size;
358         
359         Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, pb_width, name_height);
360         
361         cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pb_width, name_height);
362         cairo_t *cr = cairo_create (surface);
363         cairo_text_extents_t te;
364         cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
365         cairo_select_font_face (cr, name_font->get_family().c_str(),
366                                 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
367         cairo_set_font_size (cr, font_size);
368         cairo_text_extents (cr, new_name.c_str(), &te);
369         
370         cairo_move_to (cr, 0.5,
371                        0.5 - te.height / 2 - te.y_bearing + name_height / 2);
372         cairo_show_text (cr, new_name.c_str());
373         
374         unsigned char* src = cairo_image_surface_get_data (surface);
375         convert_bgra_to_rgba(src, buf->get_pixels(), pb_width, name_height);
376         
377         cairo_destroy(cr);
378         name_pixbuf->property_pixbuf() = buf;
379         
380         if (_type == End || _type == LoopEnd || _type == PunchOut) {
381                 name_pixbuf->property_x() = -(te.width);
382         }
383         
384 }
385
386 void
387 Marker::set_position (nframes_t frame)
388 {
389         double new_unit_position = editor.frame_to_unit (frame);
390         new_unit_position -= shift;
391         group->move (new_unit_position - unit_position, 0.0);
392         frame_position = frame;
393         unit_position = new_unit_position;
394
395         if (line) {
396                 line->property_x1() = unit_position + shift;
397                 line->property_x2() = unit_position + shift;
398         }
399 }
400
401 void
402 Marker::reposition ()
403 {
404         set_position (frame_position);
405 }       
406
407 void
408 Marker::show ()
409 {
410         group->show();
411 }
412
413 void
414 Marker::hide ()
415 {
416         group->hide();
417 }
418
419 void
420 Marker::set_color_rgba (uint32_t color)
421 {
422         mark->property_fill_color_rgba() = color;
423         mark->property_outline_color_rgba() = color;
424 }
425
426 /***********************************************************************/
427
428 TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text, 
429                           ARDOUR::TempoSection& temp)
430         : Marker (editor, parent, rgba, text, Tempo, 0, false),
431           _tempo (temp)
432 {
433         set_position (_tempo.frame());
434         group->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_tempo_marker_event), mark, this));
435 }
436
437 TempoMarker::~TempoMarker ()
438 {
439 }
440
441 /***********************************************************************/
442
443 MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text, 
444                           ARDOUR::MeterSection& m) 
445         : Marker (editor, parent, rgba, text, Meter, 0, false),
446           _meter (m)
447 {
448         set_position (_meter.frame());
449         group->signal_event().connect (bind (mem_fun (editor, &PublicEditor::canvas_meter_marker_event), mark, this));
450 }
451
452 MeterMarker::~MeterMarker ()
453 {
454 }
455