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