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