remove static Pango::FontDescriptions, they cause glib errors because of initializati...
[ardour.git] / gtk2_ardour / time_axis_view_item.cc
1 /*
2     Copyright (C) 2003 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 <pbd/error.h>
21 #include <pbd/stacktrace.h>
22
23 #include <ardour/types.h>
24 #include <ardour/ardour.h>
25
26 #include <gtkmm2ext/utils.h>
27
28 #include "public_editor.h"
29 #include "time_axis_view_item.h"
30 #include "time_axis_view.h"
31 #include "simplerect.h"
32 #include "utils.h"
33 #include "canvas_impl.h"
34 #include "rgb_macros.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace Editing;
40 using namespace Glib;
41 using namespace PBD;
42
43 //------------------------------------------------------------------------------
44 /** Initialize const static memeber data */
45
46 Pango::FontDescription* TimeAxisViewItem::NAME_FONT = 0;
47 bool TimeAxisViewItem::have_name_font = false;
48 const double TimeAxisViewItem::NAME_X_OFFSET = 15.0;
49 const double TimeAxisViewItem::GRAB_HANDLE_LENGTH = 6 ;
50
51 double TimeAxisViewItem::NAME_Y_OFFSET;
52 double TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
53 double TimeAxisViewItem::NAME_HIGHLIGHT_THRESH;
54
55
56 //---------------------------------------------------------------------------------------//
57 // Constructor / Desctructor
58
59 /**
60  * Constructs a new TimeAxisViewItem.
61  *
62  * @param it_name the unique name/Id of this item
63  * @param parant the parent canvas group
64  * @param tv the TimeAxisView we are going to be added to
65  * @param spu samples per unit
66  * @param base_color
67  * @param start the start point of this item
68  * @param duration the duration of this item
69  */
70 TimeAxisViewItem::TimeAxisViewItem(const string & it_name, ArdourCanvas::Group& parent, TimeAxisView& tv, double spu, Gdk::Color& base_color, 
71                                    nframes_t start, nframes_t duration,
72                                    Visibility vis)
73         : trackview (tv)
74 {
75         if (!have_name_font) {
76
77                 /* first constructed item sets up font info */
78
79                 NAME_FONT = get_font_for_style (N_("TimeAxisViewItemName"));
80                 
81                 Gtk::Window win;
82                 Gtk::Label foo;
83                 win.add (foo);
84
85                 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
86                 int width;
87                 int height;
88
89                 layout->set_font_description (*NAME_FONT);
90                 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
91
92                 NAME_Y_OFFSET = height + 6;
93                 NAME_HIGHLIGHT_SIZE = height + 6;
94                 NAME_HIGHLIGHT_THRESH = NAME_HIGHLIGHT_SIZE * 2;
95
96                 have_name_font = true;
97         }
98
99         group = new ArdourCanvas::Group (parent);
100         
101         init (it_name, spu, base_color, start, duration, vis);
102
103 }
104
105 TimeAxisViewItem::TimeAxisViewItem (const TimeAxisViewItem& other)
106         : trackview (other.trackview)
107 {
108
109         Gdk::Color c;
110         int r,g,b,a;
111
112         UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
113         c.set_rgb_p (r/255.0, g/255.0, b/255.0);
114
115         /* share the other's parent, but still create a new group */
116
117         Gnome::Canvas::Group* parent = other.group->property_parent();
118         
119         group = new ArdourCanvas::Group (*parent);
120
121         init (other.item_name, other.samples_per_unit, c, other.frame_position, other.item_duration, other.visibility);
122 }
123
124
125 void
126 TimeAxisViewItem::init (const string& it_name, double spu, Gdk::Color& base_color, nframes_t start, nframes_t duration, Visibility vis)
127 {
128         item_name = it_name ;
129         name_text_width = ::pixel_width (it_name, *NAME_FONT);
130         last_name_text_width = 0;
131         samples_per_unit = spu ;
132         should_show_selection = true;
133         frame_position = start ;
134         item_duration = duration ;
135         name_connected = false;
136         fill_opacity = 60;
137         position_locked = false ;
138         max_item_duration = ARDOUR::max_frames;
139         min_item_duration = 0 ;
140         show_vestigial = true;
141         visibility = vis;
142         _sensitive = true;
143
144         if (duration == 0) {
145                 warning << "Time Axis Item Duration == 0" << endl ;
146         }
147
148         vestigial_frame = new ArdourCanvas::SimpleRect (*group);
149         vestigial_frame->property_x1() = (double) 0.0;
150         vestigial_frame->property_y1() = (double) 1.0;
151         vestigial_frame->property_x2() = 2.0;
152         vestigial_frame->property_y2() = (double) trackview.height;
153         vestigial_frame->property_outline_color_rgba() = color_map[cVestigialFrameOutline];
154         vestigial_frame->property_fill_color_rgba() = color_map[cVestigialFrameFill];
155         vestigial_frame->hide ();
156
157         if (visibility & ShowFrame) {
158                 frame = new ArdourCanvas::SimpleRect (*group);
159                 frame->property_x1() = (double) 0.0;
160                 frame->property_y1() = (double) 1.0;
161                 frame->property_x2() = (double) trackview.editor.frame_to_pixel(duration);
162                 frame->property_y2() = (double) trackview.height;
163                 frame->property_outline_color_rgba() = color_map[cTimeAxisFrameOutline];
164                 frame->property_fill_color_rgba() = color_map[cTimeAxisFrameFill];
165
166                 /* by default draw all 4 edges */
167
168                 uint32_t outline_what = 0x1|0x2|0x4|0x8;
169
170                 if (visibility & HideFrameLeft) {
171                         outline_what &= ~(0x1);
172                 }
173
174                 if (visibility & HideFrameRight) {
175                         outline_what &= ~(0x2);
176                 }
177
178                 if (visibility & HideFrameTB) {
179                         outline_what &= ~(0x4 | 0x8);
180                 }
181
182                 frame->property_outline_what() = outline_what;
183                     
184         } else {
185                 frame = 0;
186         }
187
188         if (visibility & ShowNameHighlight) {
189                 name_highlight = new ArdourCanvas::SimpleRect (*group);
190                 if (visibility & FullWidthNameHighlight) {
191                         name_highlight->property_x1() = (double) 0.0;
192                         name_highlight->property_x2() = (double) (trackview.editor.frame_to_pixel(item_duration));
193                 } else {
194                         name_highlight->property_x1() = (double) 1.0;
195                         name_highlight->property_x2() = (double) (trackview.editor.frame_to_pixel(item_duration)) - 1;
196                 }
197                 name_highlight->property_y1() = (double) (trackview.height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
198                 name_highlight->property_y2() = (double) (trackview.height - 1);
199                 name_highlight->property_outline_color_rgba() = color_map[cNameHighlightFill];
200                 name_highlight->property_fill_color_rgba() = color_map[cNameHighlightOutline];
201
202                 name_highlight->set_data ("timeaxisviewitem", this);
203
204         } else {
205                 name_highlight = 0;
206         }
207
208         if (visibility & ShowNameText) {
209                 name_text = new ArdourCanvas::Text (*group);
210                 name_text->property_x() = (double) TimeAxisViewItem::NAME_X_OFFSET;
211                 /* trackview.height is the bottom of the trackview. subtract 1 to get back to the bottom of the highlight,
212                    then NAME_Y_OFFSET to position the text in the vertical center of the highlight
213                 */
214                 name_text->property_y() = (double) trackview.height - 1.0 - TimeAxisViewItem::NAME_Y_OFFSET;
215                 name_text->property_font_desc() = *NAME_FONT;
216                 name_text->property_anchor() = Gtk::ANCHOR_NW;
217
218                 name_text->set_data ("timeaxisviewitem", this);
219                 
220         } else {
221                 name_text = 0;
222         }
223
224         /* create our grab handles used for trimming/duration etc */
225
226         if (visibility & ShowHandles) {
227                 frame_handle_start = new ArdourCanvas::SimpleRect (*group);
228                 frame_handle_start->property_x1() = (double) 0.0;
229                 frame_handle_start->property_x2() = (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH;
230                 frame_handle_start->property_y1() = (double) 1.0;
231                 frame_handle_start->property_y2() = (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH+1;
232                 frame_handle_start->property_outline_color_rgba() = color_map[cFrameHandleStartOutline];
233                 frame_handle_start->property_fill_color_rgba() = color_map[cFrameHandleStartFill];
234                 
235                 frame_handle_end = new ArdourCanvas::SimpleRect (*group);
236                 frame_handle_end->property_x1() = (double) (trackview.editor.frame_to_pixel(get_duration())) - (TimeAxisViewItem::GRAB_HANDLE_LENGTH);
237                 frame_handle_end->property_x2() = (double) trackview.editor.frame_to_pixel(get_duration());
238                 frame_handle_end->property_y1() = (double) 1;
239                 frame_handle_end->property_y2() = (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH + 1;
240                 frame_handle_end->property_outline_color_rgba() = color_map[cFrameHandleEndOutline];
241                 frame_handle_end->property_fill_color_rgba() = color_map[cFrameHandleEndFill];
242
243         } else {
244                 frame_handle_start = 0;
245                 frame_handle_end = 0;
246         }
247
248         set_color (base_color) ;
249
250         set_duration (item_duration, this) ;
251         set_position (start, this) ;
252 }
253
254 /**
255  * Destructor
256  */
257 TimeAxisViewItem::~TimeAxisViewItem()
258 {
259         delete group;
260 }
261
262
263 //---------------------------------------------------------------------------------------//
264 // Position and duration Accessors/Mutators
265
266 /**
267  * Set the position of this item upon the timeline to the specified value
268  *
269  * @param pos the new position
270  * @param src the identity of the object that initiated the change
271  * @return true if the position change was a success, false otherwise
272  */
273 bool
274 TimeAxisViewItem::set_position(nframes_t pos, void* src, double* delta)
275 {
276         if (position_locked) {
277                 return false;
278         }
279
280         frame_position = pos;
281         
282         /*  This sucks. The GnomeCanvas version I am using
283             doesn't correctly implement gnome_canvas_group_set_arg(),
284             so that simply setting the "x" arg of the group
285             fails to move the group. Instead, we have to
286             use gnome_canvas_item_move(), which does the right
287             thing. I see that in GNOME CVS, the current (Sept 2001)
288             version of GNOME Canvas rectifies this issue cleanly.
289         */
290         
291         double old_unit_pos ;
292         double new_unit_pos = pos / samples_per_unit ;
293
294         old_unit_pos = group->property_x();
295
296         if (new_unit_pos != old_unit_pos) {
297                 group->move (new_unit_pos - old_unit_pos, 0.0);
298         }
299         
300         if (delta) {
301                 (*delta) = new_unit_pos - old_unit_pos;
302         }
303         
304         PositionChanged (frame_position, src) ; /* EMIT_SIGNAL */
305
306         return true;
307 }
308
309 /**
310  * Return the position of this item upon the timeline
311  *
312  * @return the position of this item
313  */
314 nframes_t
315 TimeAxisViewItem::get_position() const
316 {
317         return frame_position;
318 }
319
320 /**
321  * Sets the duration of this item
322  *
323  * @param dur the new duration of this item
324  * @param src the identity of the object that initiated the change
325  * @return true if the duration change was succesful, false otherwise
326  */
327 bool
328 TimeAxisViewItem::set_duration (nframes_t dur, void* src)
329 {
330         if ((dur > max_item_duration) || (dur < min_item_duration)) {
331                 warning << string_compose (_("new duration %1 frames is out of bounds for %2"), get_item_name(), dur)
332                         << endmsg;
333                 return false;
334         }
335
336         if (dur == 0) {
337                 group->hide();
338         }
339
340         item_duration = dur;
341         
342         reset_width_dependent_items (trackview.editor.frame_to_pixel (dur));
343         
344         DurationChanged (dur, src) ; /* EMIT_SIGNAL */
345         return true;
346 }
347
348 /**
349  * Returns the duration of this item
350  *
351  */
352 nframes_t
353 TimeAxisViewItem::get_duration() const
354 {
355         return (item_duration);
356 }
357
358 /**
359  * Sets the maximum duration that this item make have.
360  *
361  * @param dur the new maximum duration
362  * @param src the identity of the object that initiated the change
363  */
364 void
365 TimeAxisViewItem::set_max_duration(nframes_t dur, void* src)
366 {
367         max_item_duration = dur ;
368         MaxDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
369 }
370                 
371 /**
372  * Returns the maxmimum duration that this item may be set to
373  *
374  * @return the maximum duration that this item may be set to
375  */
376 nframes_t
377 TimeAxisViewItem::get_max_duration() const
378 {
379         return (max_item_duration) ;
380 }
381
382 /**
383  * Sets the minimu duration that this item may be set to
384  *
385  * @param the minimum duration that this item may be set to
386  * @param src the identity of the object that initiated the change
387  */
388 void
389 TimeAxisViewItem::set_min_duration(nframes_t dur, void* src)
390 {
391         min_item_duration = dur ;
392         MinDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
393 }
394                 
395 /**
396  * Returns the minimum duration that this item mey be set to
397  *
398  * @return the nimum duration that this item mey be set to
399  */
400 nframes_t
401 TimeAxisViewItem::get_min_duration() const
402 {
403         return(min_item_duration) ;
404 }
405
406 /**
407  * Sets whether the position of this Item is locked to its current position
408  * Locked items cannot be moved until the item is unlocked again.
409  *
410  * @param yn set to true to lock this item to its current position
411  * @param src the identity of the object that initiated the change
412  */
413 void
414 TimeAxisViewItem::set_position_locked(bool yn, void* src)
415 {
416         position_locked = yn ;
417         set_trim_handle_colors() ;
418         PositionLockChanged (position_locked, src); /* EMIT_SIGNAL */
419 }
420
421 /**
422  * Returns whether this item is locked to its current position
423  *
424  * @return true if this item is locked to its current posotion
425  *         false otherwise
426  */
427 bool
428 TimeAxisViewItem::get_position_locked() const
429 {
430         return (position_locked);
431 }
432
433 /**
434  * Sets whether the Maximum Duration constraint is active and should be enforced
435  *
436  * @param active set true to enforce the max duration constraint
437  * @param src the identity of the object that initiated the change
438  */
439 void
440 TimeAxisViewItem::set_max_duration_active(bool active, void* src)
441 {
442         max_duration_active = active ;
443 }
444                 
445 /**
446  * Returns whether the Maximum Duration constraint is active and should be enforced
447  *
448  * @return true if the maximum duration constraint is active, false otherwise
449  */
450 bool
451 TimeAxisViewItem::get_max_duration_active() const
452 {
453         return(max_duration_active) ;
454 }
455                 
456 /**
457  * Sets whether the Minimum Duration constraint is active and should be enforced
458  *
459  * @param active set true to enforce the min duration constraint
460  * @param src the identity of the object that initiated the change
461  */
462 void
463 TimeAxisViewItem::set_min_duration_active(bool active, void* src)
464 {
465         min_duration_active = active ;
466 }
467                 
468 /**
469  * Returns whether the Maximum Duration constraint is active and should be enforced
470  *
471  * @return true if the maximum duration constraint is active, false otherwise
472  */
473 bool
474 TimeAxisViewItem::get_min_duration_active() const
475 {
476         return(min_duration_active) ;
477 }
478
479 //---------------------------------------------------------------------------------------//
480 // Name/Id Accessors/Mutators
481
482 /**
483  * Set the name/Id of this item.
484  *
485  * @param new_name the new name of this item
486  * @param src the identity of the object that initiated the change
487  */
488 void
489 TimeAxisViewItem::set_item_name(std::string new_name, void* src)
490 {
491         if (new_name != item_name) {
492                 std::string temp_name = item_name ;
493                 item_name = new_name ;
494                 name_text_width = ::pixel_width (new_name, *NAME_FONT);
495                 NameChanged (item_name, temp_name, src) ; /* EMIT_SIGNAL */
496         }
497 }
498
499 /**
500  * Returns the name/id of this item
501  *
502  * @return the name/id of this item
503  */
504 std::string
505 TimeAxisViewItem::get_item_name() const
506 {
507         return(item_name) ;
508 }
509
510 //---------------------------------------------------------------------------------------//
511 // Selection Methods
512
513 /**
514  * Set to true to indicate that this item is currently selected
515  *
516  * @param yn true if this item is currently selected
517  * @param src the identity of the object that initiated the change
518  */
519 void
520 TimeAxisViewItem::set_selected(bool yn)
521 {
522         if (_selected != yn) {
523                 Selectable::set_selected (yn);
524                 set_frame_color ();
525         }
526 }
527
528 void 
529 TimeAxisViewItem::set_should_show_selection (bool yn)
530 {
531         if (should_show_selection != yn) {
532                 should_show_selection = yn;
533                 set_frame_color ();
534         }
535 }
536
537 //---------------------------------------------------------------------------------------//
538 // Parent Componenet Methods
539
540 /**
541  * Returns the TimeAxisView that this item is upon
542  *
543  * @return the timeAxisView that this item is placed upon
544  */
545 TimeAxisView&
546 TimeAxisViewItem::get_time_axis_view()
547 {
548         return trackview;
549 }               
550 //---------------------------------------------------------------------------------------//
551 // ui methods & data
552
553 /**
554  * Sets the displayed item text
555  * This item is the visual text name displayed on the canvas item, this can be different to the name of the item
556  *
557  * @param new_name the new name text to display
558  */
559 void
560 TimeAxisViewItem::set_name_text(const ustring& new_name)
561 {
562         if (name_text) {
563                 name_text->property_text() = new_name;
564                 name_text_width = pixel_width (new_name, *NAME_FONT);
565                 name_text_size_cache.clear ();
566         }
567 }
568
569 /**
570  * Set the height of this item
571  *
572  * @param h the new height
573  */             
574 void
575 TimeAxisViewItem::set_height (double height)
576 {
577         if (name_highlight) {
578                 if (height < NAME_HIGHLIGHT_THRESH) {
579                         name_highlight->hide();
580                         if (name_text) {
581                                 name_text->hide();
582                         }
583                 } else {
584                         name_highlight->show();
585                         if (name_text) {
586                                 name_text->show();
587                         }
588                 }
589
590                 if (height > NAME_HIGHLIGHT_SIZE) {
591                         name_highlight->property_y1() = (double) height+1 - NAME_HIGHLIGHT_SIZE;
592                         name_highlight->property_y2() = (double) height;
593                 }
594                 else {
595                         /* it gets hidden now anyway */
596                         name_highlight->property_y1() = (double) 1.0;
597                         name_highlight->property_y2() = (double) height;
598                 }
599         }
600
601         if (name_text) {
602                 name_text->property_y() = height+1 - NAME_Y_OFFSET;
603                 if (height < NAME_HIGHLIGHT_THRESH) {
604                         name_text->property_fill_color_rgba() =  fill_color;
605                 }
606                 else {
607                         name_text->property_fill_color_rgba() = label_color;
608                 }
609         }
610
611         if (frame) {
612                 frame->property_y2() = height+1;
613         }
614
615         vestigial_frame->property_y2() = height+1;
616 }
617
618 /**
619  * 
620  */
621 void
622 TimeAxisViewItem::set_color(Gdk::Color& base_color)
623 {
624         compute_colors (base_color);
625         set_colors ();
626 }
627
628 /**
629  * 
630  */
631 ArdourCanvas::Item*
632 TimeAxisViewItem::get_canvas_frame()
633 {
634         return(frame) ;
635 }
636
637 /**
638  * 
639  */
640 ArdourCanvas::Item*
641 TimeAxisViewItem::get_canvas_group()
642 {
643         return (group) ;
644 }
645
646 /**
647  * 
648  */
649 ArdourCanvas::Item*
650 TimeAxisViewItem::get_name_highlight()
651 {
652         return (name_highlight) ;
653 }
654
655 /**
656  * 
657  */
658 ArdourCanvas::Text*
659 TimeAxisViewItem::get_name_text()
660 {
661         return (name_text) ;
662 }
663
664 /**
665  * Calculates some contrasting color for displaying various parts of this item, based upon the base color
666  *
667  * @param color the base color of the item
668  */
669 void
670 TimeAxisViewItem::compute_colors(Gdk::Color& base_color)
671 {
672         unsigned char radius ;
673         char minor_shift ;
674         
675         unsigned char r,g,b ;
676
677         /* FILL: this is simple */
678         r = base_color.get_red()/256 ;
679         g = base_color.get_green()/256 ;
680         b = base_color.get_blue()/256 ;
681         fill_color = RGBA_TO_UINT(r,g,b,255) ;
682
683         /*  for minor colors:
684                 if the overall saturation is strong, make the minor colors light.
685                 if its weak, make them dark.
686   
687                 we do this by moving an equal distance to the other side of the
688                 central circle in the color wheel from where we started.
689         */
690
691         radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f)) ;
692         minor_shift = 125 - radius ;
693
694         /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
695
696         r = base_color.get_red()/256;
697         g = base_color.get_green()/256;
698         b = base_color.get_blue()/256;
699   
700         if (r > b)
701         {
702                 if (r > g)
703                 {
704                         /* red sector => green */
705                         swap (r,g);
706                 }
707                 else
708                 {
709                         /* green sector => blue */
710                         swap (g,b);
711                 } 
712         }
713         else
714         {
715                 if (b > g)
716                 {
717                         /* blue sector => red */
718                         swap (b,r);
719                 }
720                 else
721                 {
722                         /* green sector => blue */
723                         swap (g,b);
724                 }
725         }
726
727         r += minor_shift;
728         b += minor_shift;
729         g += minor_shift;
730   
731         label_color = RGBA_TO_UINT(r,g,b,255);
732         r = (base_color.get_red()/256)   + 127 ;
733         g = (base_color.get_green()/256) + 127 ;
734         b = (base_color.get_blue()/256)  + 127 ;
735   
736         label_color = RGBA_TO_UINT(r,g,b,255);
737
738         /* XXX can we do better than this ? */
739         /* We're trying ;) */
740         /* NUKECOLORS */
741         
742         //frame_color_r = 192;
743         //frame_color_g = 192;
744         //frame_color_b = 194;
745         
746         //selected_frame_color_r = 182;
747         //selected_frame_color_g = 145;
748         //selected_frame_color_b = 168;
749         
750         //handle_color_r = 25 ;
751         //handle_color_g = 0 ;
752         //handle_color_b = 255 ;
753         //lock_handle_color_r = 235 ;
754         //lock_handle_color_g = 16;
755         //lock_handle_color_b = 16;
756 }
757
758 /**
759  * Convenience method to set the various canvas item colors
760  */
761 void
762 TimeAxisViewItem::set_colors()
763 {
764         set_frame_color() ;
765         if (name_text) {
766                 double height = NAME_HIGHLIGHT_THRESH;
767
768                 if (frame) {
769                         height = frame->property_y2();
770                 }
771
772                 if (height < NAME_HIGHLIGHT_THRESH) {
773                         name_text->property_fill_color_rgba() =  fill_color;
774                 }
775                 else {
776                         name_text->property_fill_color_rgba() = label_color;
777                 }
778         }
779
780         if (name_highlight) {
781                 name_highlight->property_fill_color_rgba() = fill_color;
782                 name_highlight->property_outline_color_rgba() = fill_color;
783         }
784         set_trim_handle_colors() ;
785 }
786
787 /**
788  * Sets the frame color depending on whether this item is selected
789  */
790 void
791 TimeAxisViewItem::set_frame_color()
792 {
793         if (frame) {
794                 uint32_t r,g,b,a;
795                 
796                 if (_selected && should_show_selection) {
797                         UINT_TO_RGBA(color_map[cSelectedFrameBase], &r, &g, &b, &a);
798                         frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity);
799                 } else {
800                         UINT_TO_RGBA(color_map[cFrameBase], &r, &g, &b, &a);
801                         frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity);
802                 }
803         }
804 }
805
806 /**
807  * Sets the colors of the start and end trim handle depending on object state
808  *
809  */
810 void
811 TimeAxisViewItem::set_trim_handle_colors()
812 {
813         if (frame_handle_start) {
814                 if (position_locked) {
815                         frame_handle_start->property_fill_color_rgba() = color_map[cTrimHandleLockedStart];
816                         frame_handle_end->property_fill_color_rgba() = color_map[cTrimHandleLockedEnd];
817                 } else {
818                         frame_handle_start->property_fill_color_rgba() = color_map[cTrimHandleStart];
819                         frame_handle_end->property_fill_color_rgba() = color_map[cTrimHandleEnd];
820                 }
821         }
822 }
823
824 double
825 TimeAxisViewItem::get_samples_per_unit()
826 {
827         return(samples_per_unit) ;
828 }
829
830 void
831 TimeAxisViewItem::set_samples_per_unit (double spu)
832 {
833         samples_per_unit = spu ;
834         set_position (this->get_position(), this);
835         reset_width_dependent_items ((double)get_duration() / samples_per_unit);
836 }
837
838 void
839 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
840 {
841         if (pixel_width < GRAB_HANDLE_LENGTH * 2) {
842
843                 if (frame_handle_start) {
844                         frame_handle_start->hide();
845                         frame_handle_end->hide();
846                 }
847
848         } if (pixel_width < 2.0) {
849
850                 if (show_vestigial) {
851                         vestigial_frame->show();
852                 }
853
854                 if (name_highlight) {
855                         name_highlight->hide();
856                         if (name_text) {
857                                 name_text->hide();
858                         }
859                 }
860
861                 if (frame) {
862                         frame->hide();
863                 }
864
865                 if (frame_handle_start) {
866                         frame_handle_start->hide();
867                         frame_handle_end->hide();
868                 }
869                 
870         } else {
871                 vestigial_frame->hide();
872
873                 if (name_highlight) {
874
875                         double height = name_highlight->property_y2 ();
876
877                         if (height < NAME_HIGHLIGHT_THRESH) {
878                                 name_highlight->hide();
879                                 if (name_text) {
880                                         name_text->hide();
881                                 }
882                         } else {
883                                 name_highlight->show();
884                                 if (name_text && !get_item_name().empty()) {
885                                         name_text->show();
886                                         reset_name_width (pixel_width);
887                                 }
888                         }
889
890                         if (visibility & FullWidthNameHighlight) {
891                                 name_highlight->property_x2() = pixel_width;
892                         } else {
893                                 name_highlight->property_x2() = pixel_width - 1.0;
894                         }
895
896                 }
897
898                 if (frame) {
899                         frame->show();
900                         frame->property_x2() = pixel_width;
901                 }
902
903                 if (frame_handle_start) {
904                         if (pixel_width < (2*TimeAxisViewItem::GRAB_HANDLE_LENGTH)) {
905                                 frame_handle_start->hide();
906                                 frame_handle_end->hide();
907                         }
908                         frame_handle_start->show();
909                         frame_handle_end->property_x1() = pixel_width - (TimeAxisViewItem::GRAB_HANDLE_LENGTH);
910                         frame_handle_end->show();
911                         frame_handle_end->property_x2() = pixel_width;
912                 }
913         }
914 }
915
916 void
917 TimeAxisViewItem::reset_name_width (double pixel_width)
918 {
919         if (name_text == 0) {
920                 return;
921         }
922
923         int limit = (int) floor (pixel_width - NAME_X_OFFSET);
924         bool shrinking = (last_name_text_width > pixel_width);
925         int actual_width;
926         ustring ustr;
927         ustring::size_type n;
928
929         if ((last_name_text_width &&                                       // we did this once
930              shrinking &&                                                  // we're getting smaller
931              (name_text_width <= limit) &&                                 // fits the new size
932              (name_text_width <= last_name_text_width - NAME_X_OFFSET))) { // fit into the old size too
933                 last_name_text_width = pixel_width;
934                 return;
935         }
936
937         /* now check the cache of existing truncations */
938
939         Gtk::Label foo;
940         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
941
942         for (n = item_name.length(); n > 0; --n) {
943                 
944                 map<ustring::size_type,int>::iterator i;
945
946                 if ((i = name_text_size_cache.find (n)) != name_text_size_cache.end()) {
947
948                         /* we know the length of this substring already */
949                         
950                         if ((actual_width = (*i).second) < limit) {
951
952                                 /* it fits, use it */
953                                 
954                                 ustr = item_name.substr (0, n);
955                                 break;
956                         }
957                                                 
958                 } else {
959                         
960                         /* we don't know the length of this substring already, so compute
961                            it and put it into the cache.
962                          */
963
964                         layout->set_text (item_name.substr (0, n));
965                         
966                         int width, height;
967                         Gtkmm2ext::get_ink_pixel_size (layout, width, height);
968                         
969                         name_text_size_cache[n] = width;
970
971                         if ((actual_width = width) < limit) {
972                                 ustr = item_name.substr (0, n);
973                                 break;
974                         }
975                 }
976         }
977
978         if (n == 0) {
979                 name_text->property_text() = "";
980                 last_name_text_width = pixel_width;
981                 return;
982         } 
983
984         /* don't use name for event handling if it leaves no room
985            for trimming to work.
986         */
987         
988         if (pixel_width - actual_width < (NAME_X_OFFSET * 2.0)) {
989                 if (name_connected) {
990                         name_connected = false;
991                 }
992         } else {
993                 if (!name_connected) {
994                         name_connected = true;
995                 }
996         }
997         
998         name_text->property_text() = ustr;
999         name_text_width = actual_width;
1000         name_text->show();
1001         last_name_text_width = pixel_width;
1002
1003 }
1004
1005
1006 //---------------------------------------------------------------------------------------//
1007 // Handle time axis removal
1008
1009 /**
1010  * Handles the Removal of this time axis item
1011  * This _needs_ to be called to alert others of the removal properly, ie where the source
1012  * of the removal came from.
1013  *
1014  * XXX Although im not too happy about this method of doing things, I cant think of a cleaner method
1015  *     just now to capture the source of the removal
1016  *
1017  * @param src the identity of the object that initiated the change
1018  */
1019 void
1020 TimeAxisViewItem::remove_this_item(void* src)
1021 {
1022         /*
1023            defer to idle loop, otherwise we'll delete this object
1024            while we're still inside this function ...
1025         */
1026         Glib::signal_idle().connect(bind (sigc::ptr_fun (&TimeAxisViewItem::idle_remove_this_item), this, src));
1027 }
1028
1029 /**
1030  * Callback used to remove this time axis item during the gtk idle loop
1031  * This is used to avoid deleting the obejct while inside the remove_this_item
1032  * method
1033  *
1034  * @param item the TimeAxisViewItem to remove
1035  * @param src the identity of the object that initiated the change
1036  */
1037 gint
1038 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
1039 {
1040         item->ItemRemoved (item->get_item_name(), src) ; /* EMIT_SIGNAL */
1041         delete item;
1042         item = 0;
1043         return false;
1044 }