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