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