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