allow libsndfile to open BWF files; fix -Woverload-virtuals being in CCFLAGS instead...
[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)
488 {
489         if (_selected != yn) {
490                 Selectable::set_selected (yn);
491                 set_frame_color ();
492         }
493 }
494
495 void 
496 TimeAxisViewItem::set_should_show_selection (bool yn)
497 {
498         if (should_show_selection != yn) {
499                 should_show_selection = yn;
500                 set_frame_color ();
501         }
502 }
503
504 //---------------------------------------------------------------------------------------//
505 // Parent Componenet Methods
506
507 /**
508  * Returns the TimeAxisView that this item is upon
509  *
510  * @return the timeAxisView that this item is placed upon
511  */
512 TimeAxisView&
513 TimeAxisViewItem::get_time_axis_view()
514 {
515         return trackview;
516 }               
517 //---------------------------------------------------------------------------------------//
518 // ui methods & data
519
520 /**
521  * Sets the displayed item text
522  * This item is the visual text name displayed on the canvas item, this can be different to the name of the item
523  *
524  * @param new_name the new name text to display
525  */
526 void
527 TimeAxisViewItem::set_name_text(std::string new_name)
528 {
529         if (name_text) {
530                 name_text->property_text() = new_name.c_str();
531         }
532 }
533
534 /**
535  * Set the height of this item
536  *
537  * @param h the new height
538  */             
539 void
540 TimeAxisViewItem::set_height(double height)
541 {
542         if (name_highlight) {
543                 if (height < NAME_HIGHLIGHT_THRESH) {
544                         name_highlight->hide();
545                         if (name_text) {
546                                 name_text->hide();
547                         }
548                 } else {
549                         name_highlight->show();
550                         if (name_text) {
551                                 name_text->show();
552                         }
553                 }
554
555                 if (height > NAME_HIGHLIGHT_SIZE) {
556                         name_highlight->property_y1() = (double) height+1 - NAME_HIGHLIGHT_SIZE;
557                         name_highlight->property_y2() = (double) height;
558                 }
559                 else {
560                         /* it gets hidden now anyway */
561                         name_highlight->property_y1() = (double) 1.0;
562                         name_highlight->property_y2() = (double) height;
563                 }
564         }
565
566         if (name_text) {
567                 name_text->property_y() = height+1 - NAME_Y_OFFSET;
568                 if (height < NAME_HIGHLIGHT_THRESH) {
569                         name_text->property_fill_color_rgba() =  fill_color;
570                 }
571                 else {
572                         name_text->property_fill_color_rgba() = label_color;
573                 }
574         }
575
576         if (frame) {
577                 frame->property_y2() = height+1;
578         }
579
580         vestigial_frame->property_y2() = height+1;
581 }
582
583 /**
584  * 
585  */
586 void
587 TimeAxisViewItem::set_color(Gdk::Color& base_color)
588 {
589         compute_colors (base_color);
590         set_colors ();
591 }
592
593 /**
594  * 
595  */
596 ArdourCanvas::Item*
597 TimeAxisViewItem::get_canvas_frame()
598 {
599         return(frame) ;
600 }
601
602 /**
603  * 
604  */
605 ArdourCanvas::Item*
606 TimeAxisViewItem::get_canvas_group()
607 {
608         return (group) ;
609 }
610
611 /**
612  * 
613  */
614 ArdourCanvas::Item*
615 TimeAxisViewItem::get_name_highlight()
616 {
617         return (name_highlight) ;
618 }
619
620 /**
621  * 
622  */
623 ArdourCanvas::Text*
624 TimeAxisViewItem::get_name_text()
625 {
626         return (name_text) ;
627 }
628
629 /**
630  * Calculates some contrasting color for displaying various parts of this item, based upon the base color
631  *
632  * @param color the base color of the item
633  */
634 void
635 TimeAxisViewItem::compute_colors(Gdk::Color& base_color)
636 {
637         unsigned char radius ;
638         char minor_shift ;
639         
640         unsigned char r,g,b ;
641
642         /* FILL: this is simple */
643         r = base_color.get_red()/256 ;
644         g = base_color.get_green()/256 ;
645         b = base_color.get_blue()/256 ;
646         fill_color = RGBA_TO_UINT(r,g,b,255) ;
647
648         /*  for minor colors:
649                 if the overall saturation is strong, make the minor colors light.
650                 if its weak, make them dark.
651   
652                 we do this by moving an equal distance to the other side of the
653                 central circle in the color wheel from where we started.
654         */
655
656         radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f)) ;
657         minor_shift = 125 - radius ;
658
659         /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
660
661         r = base_color.get_red()/256;
662         g = base_color.get_green()/256;
663         b = base_color.get_blue()/256;
664   
665         if (r > b)
666         {
667                 if (r > g)
668                 {
669                         /* red sector => green */
670                         swap (r,g);
671                 }
672                 else
673                 {
674                         /* green sector => blue */
675                         swap (g,b);
676                 } 
677         }
678         else
679         {
680                 if (b > g)
681                 {
682                         /* blue sector => red */
683                         swap (b,r);
684                 }
685                 else
686                 {
687                         /* green sector => blue */
688                         swap (g,b);
689                 }
690         }
691
692         r += minor_shift;
693         b += minor_shift;
694         g += minor_shift;
695   
696         label_color = RGBA_TO_UINT(r,g,b,255);
697         r = (base_color.get_red()/256)   + 127 ;
698         g = (base_color.get_green()/256) + 127 ;
699         b = (base_color.get_blue()/256)  + 127 ;
700   
701         label_color = RGBA_TO_UINT(r,g,b,255);
702
703         /* XXX can we do better than this ? */
704         /* We're trying ;) */
705         /* NUKECOLORS */
706         
707         //frame_color_r = 192;
708         //frame_color_g = 192;
709         //frame_color_b = 194;
710         
711         //selected_frame_color_r = 182;
712         //selected_frame_color_g = 145;
713         //selected_frame_color_b = 168;
714         
715         //handle_color_r = 25 ;
716         //handle_color_g = 0 ;
717         //handle_color_b = 255 ;
718         //lock_handle_color_r = 235 ;
719         //lock_handle_color_g = 16;
720         //lock_handle_color_b = 16;
721 }
722
723 /**
724  * Convenience method to set the various canvas item colors
725  */
726 void
727 TimeAxisViewItem::set_colors()
728 {
729         set_frame_color() ;
730         if (name_text) {
731                 double height = NAME_HIGHLIGHT_THRESH;
732
733                 if (frame) {
734                         height = frame->property_y2();
735                 }
736
737                 if (height < NAME_HIGHLIGHT_THRESH) {
738                         name_text->property_fill_color_rgba() =  fill_color;
739                 }
740                 else {
741                         name_text->property_fill_color_rgba() = label_color;
742                 }
743         }
744
745         if (name_highlight) {
746                 name_highlight->property_fill_color_rgba() = fill_color;
747                 name_highlight->property_outline_color_rgba() = fill_color;
748         }
749         set_trim_handle_colors() ;
750 }
751
752 /**
753  * Sets the frame color depending on whether this item is selected
754  */
755 void
756 TimeAxisViewItem::set_frame_color()
757 {
758         if (frame) {
759                 uint32_t r,g,b,a;
760                 
761                 if (_selected && should_show_selection) {
762                         UINT_TO_RGBA(color_map[cSelectedFrameBase], &r, &g, &b, &a);
763                         frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity);
764                 } else {
765                         UINT_TO_RGBA(color_map[cFrameBase], &r, &g, &b, &a);
766                         frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity);
767                 }
768         }
769 }
770
771 /**
772  * Sets the colors of the start and end trim handle depending on object state
773  *
774  */
775 void
776 TimeAxisViewItem::set_trim_handle_colors()
777 {
778         if (frame_handle_start) {
779                 if (position_locked) {
780                         frame_handle_start->property_fill_color_rgba() = color_map[cTrimHandleLockedStart];
781                         frame_handle_end->property_fill_color_rgba() = color_map[cTrimHandleLockedEnd];
782                 } else {
783                         frame_handle_start->property_fill_color_rgba() = color_map[cTrimHandleStart];
784                         frame_handle_end->property_fill_color_rgba() = color_map[cTrimHandleEnd];
785                 }
786         }
787 }
788
789 double
790 TimeAxisViewItem::get_samples_per_unit()
791 {
792         return(samples_per_unit) ;
793 }
794
795 void
796 TimeAxisViewItem::set_samples_per_unit (double spu)
797 {
798         samples_per_unit = spu ;
799         set_position (this->get_position(), this);
800         reset_width_dependent_items ((double)get_duration() / samples_per_unit);
801 }
802
803 void
804 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
805 {
806         if (pixel_width < GRAB_HANDLE_LENGTH * 2) {
807
808                 if (frame_handle_start) {
809                         frame_handle_start->hide();
810                         frame_handle_end->hide();
811                 }
812
813         } if (pixel_width < 2.0) {
814
815                 if (show_vestigial) {
816                         vestigial_frame->show();
817                 }
818
819                 if (name_highlight) {
820                         name_highlight->hide();
821                         if (name_text) {
822                                 name_text->hide();
823                         }
824                 }
825
826                 if (frame) {
827                         frame->hide();
828                 }
829
830                 if (frame_handle_start) {
831                         frame_handle_start->hide();
832                         frame_handle_end->hide();
833                 }
834                 
835         } else {
836                 vestigial_frame->hide();
837
838                 if (name_highlight) {
839
840                         double height = name_highlight->property_y2 ();
841
842                         if (height < NAME_HIGHLIGHT_THRESH) {
843                                 name_highlight->hide();
844                                 if (name_text) {
845                                         name_text->hide();
846                                 }
847                         } else {
848                                 name_highlight->show();
849                                 if (name_text) {
850                                         name_text->show();
851                                         reset_name_width (pixel_width);
852                                 }
853                         }
854
855                         if (visibility & FullWidthNameHighlight) {
856                                 name_highlight->property_x2() = pixel_width;
857                         } else {
858                                 name_highlight->property_x2() = pixel_width - 1.0;
859                         }
860
861                 }
862
863                 if (frame) {
864                         frame->show();
865                         frame->property_x2() = pixel_width;
866                 }
867
868                 if (frame_handle_start) {
869                         if (pixel_width < (2*TimeAxisViewItem::GRAB_HANDLE_LENGTH)) {
870                                 frame_handle_start->hide();
871                                 frame_handle_end->hide();
872                         }
873                         frame_handle_start->show();
874                         frame_handle_end->property_x1() = pixel_width - (TimeAxisViewItem::GRAB_HANDLE_LENGTH);
875                         frame_handle_end->show();
876                         frame_handle_end->property_x2() = pixel_width;
877                 }
878         }
879 }
880
881 void
882 TimeAxisViewItem::reset_name_width (double pixel_width)
883 {
884         if (name_text == 0) {
885                 return;
886         }
887                        
888         int width;
889         
890         ustring ustr = fit_to_pixels (item_name, (int) floor (pixel_width - NAME_X_OFFSET), NAME_FONT, width);
891
892         if (ustr.empty()) {
893                 
894                 name_text->hide ();
895                 
896         } else {
897                 
898                 /* don't use name for event handling if it leaves no room
899                    for trimming to work.
900                 */
901                 
902                 if (pixel_width - width < (NAME_X_OFFSET * 2.0)) {
903                         if (name_connected) {
904                                 name_connected = false;
905                         }
906                 } else {
907                         if (!name_connected) {
908                                 name_connected = true;
909                         }
910                 }
911                 
912                 name_text->property_text() = ustr;
913                 name_text->show();
914         }
915 }
916
917
918 //---------------------------------------------------------------------------------------//
919 // Handle time axis removal
920
921 /**
922  * Handles the Removal of this time axis item
923  * This _needs_ to be called to alert others of the removal properly, ie where the source
924  * of the removal came from.
925  *
926  * XXX Although im not too happy about this method of doing things, I cant think of a cleaner method
927  *     just now to capture the source of the removal
928  *
929  * @param src the identity of the object that initiated the change
930  */
931 void
932 TimeAxisViewItem::remove_this_item(void* src)
933 {
934         /*
935            defer to idle loop, otherwise we'll delete this object
936            while we're still inside this function ...
937         */
938         Glib::signal_idle().connect(bind (sigc::ptr_fun (&TimeAxisViewItem::idle_remove_this_item), this, src));
939 }
940
941 /**
942  * Callback used to remove this time axis item during the gtk idle loop
943  * This is used to avoid deleting the obejct while inside the remove_this_item
944  * method
945  *
946  * @param item the TimeAxisViewItem to remove
947  * @param src the identity of the object that initiated the change
948  */
949 gint
950 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
951 {
952         item->ItemRemoved (item->get_item_name(), src) ; /* EMIT_SIGNAL */
953         delete item;
954         item = 0;
955         return false;
956 }