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