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