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