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