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