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