f36e5f4eb525b98cb692a8b8e40801a072701cc7
[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), _height (1.0), _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         _height = height;
562
563         if (name_highlight) {
564                 if (height < NAME_HIGHLIGHT_THRESH) {
565                         name_highlight->hide ();
566                         high_enough_for_name = false;
567
568                 } else {
569                         name_highlight->show();
570                         high_enough_for_name = true;
571                 }
572
573                 if (height > NAME_HIGHLIGHT_SIZE) {
574                         name_highlight->property_y1() = (double) height - 1 - NAME_HIGHLIGHT_SIZE;
575                         name_highlight->property_y2() = (double) height - 2;
576                 }
577                 else {
578                         /* it gets hidden now anyway */
579                         name_highlight->property_y1() = (double) 1.0;
580                         name_highlight->property_y2() = (double) height;
581                 }
582         }
583
584         if (visibility & ShowNameText) {
585                 name_pixbuf->property_y() =  height + 1 - NAME_Y_OFFSET;
586         }
587
588         if (frame) {
589                 frame->property_y2() = height - 1;
590                 frame_handle_start->property_y2() = height - 1;
591                 frame_handle_end->property_y2() = height - 1;
592         }
593
594         vestigial_frame->property_y2() = height - 1;
595
596         update_name_pixbuf_visibility ();
597 }
598
599 /**
600  *
601  */
602 void
603 TimeAxisViewItem::set_color (Gdk::Color const & base_color)
604 {
605         compute_colors (base_color);
606         set_colors ();
607 }
608
609 /**
610  *
611  */
612 ArdourCanvas::Item*
613 TimeAxisViewItem::get_canvas_frame()
614 {
615         return(frame) ;
616 }
617
618 /**
619  *
620  */
621 ArdourCanvas::Group*
622 TimeAxisViewItem::get_canvas_group()
623 {
624         return (group) ;
625 }
626
627 /**
628  *
629  */
630 ArdourCanvas::Item*
631 TimeAxisViewItem::get_name_highlight()
632 {
633         return (name_highlight) ;
634 }
635
636 /**
637  *
638  */
639 ArdourCanvas::Pixbuf*
640 TimeAxisViewItem::get_name_pixbuf()
641 {
642         return (name_pixbuf) ;
643 }
644
645 /**
646  * Calculates some contrasting color for displaying various parts of this item, based upon the base color
647  *
648  * @param color the base color of the item
649  */
650 void
651 TimeAxisViewItem::compute_colors (Gdk::Color const & base_color)
652 {
653         unsigned char radius ;
654         char minor_shift ;
655
656         unsigned char r,g,b ;
657
658         /* FILL: this is simple */
659         r = base_color.get_red()/256 ;
660         g = base_color.get_green()/256 ;
661         b = base_color.get_blue()/256 ;
662         fill_color = RGBA_TO_UINT(r,g,b,160) ;
663
664         /*  for minor colors:
665                 if the overall saturation is strong, make the minor colors light.
666                 if its weak, make them dark.
667
668                 we do this by moving an equal distance to the other side of the
669                 central circle in the color wheel from where we started.
670         */
671
672         radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f)) ;
673         minor_shift = 125 - radius ;
674
675         /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
676
677         r = base_color.get_red()/256;
678         g = base_color.get_green()/256;
679         b = base_color.get_blue()/256;
680
681         if (r > b)
682         {
683                 if (r > g)
684                 {
685                         /* red sector => green */
686                         swap (r,g);
687                 }
688                 else
689                 {
690                         /* green sector => blue */
691                         swap (g,b);
692                 }
693         }
694         else
695         {
696                 if (b > g)
697                 {
698                         /* blue sector => red */
699                         swap (b,r);
700                 }
701                 else
702                 {
703                         /* green sector => blue */
704                         swap (g,b);
705                 }
706         }
707
708         r += minor_shift;
709         b += minor_shift;
710         g += minor_shift;
711
712         label_color = RGBA_TO_UINT(r,g,b,255);
713         r = (base_color.get_red()/256)   + 127 ;
714         g = (base_color.get_green()/256) + 127 ;
715         b = (base_color.get_blue()/256)  + 127 ;
716
717         label_color = RGBA_TO_UINT(r,g,b,255);
718
719         /* XXX can we do better than this ? */
720         /* We're trying ;) */
721         /* NUKECOLORS */
722
723         //frame_color_r = 192;
724         //frame_color_g = 192;
725         //frame_color_b = 194;
726
727         //selected_frame_color_r = 182;
728         //selected_frame_color_g = 145;
729         //selected_frame_color_b = 168;
730
731         //handle_color_r = 25 ;
732         //handle_color_g = 0 ;
733         //handle_color_b = 255 ;
734         //lock_handle_color_r = 235 ;
735         //lock_handle_color_g = 16;
736         //lock_handle_color_b = 16;
737 }
738
739 /**
740  * Convenience method to set the various canvas item colors
741  */
742 void
743 TimeAxisViewItem::set_colors()
744 {
745         set_frame_color() ;
746
747         if (name_highlight) {
748                 name_highlight->property_fill_color_rgba() = fill_color;
749                 name_highlight->property_outline_color_rgba() = fill_color;
750         }
751         set_trim_handle_colors() ;
752 }
753
754 /**
755  * Sets the frame color depending on whether this item is selected
756  */
757 void
758 TimeAxisViewItem::set_frame_color()
759 {
760         if (frame) {
761                 uint32_t r,g,b,a;
762
763                 if (_selected && should_show_selection) {
764                         UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get(), &r, &g, &b, &a);
765                         frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, a);
766                 } else {
767                         if (_recregion) {
768                                 UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_RecordingRect.get(), &r, &g, &b, &a);
769                                 frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, a);
770                         } else {
771                                 UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_FrameBase.get(), &r, &g, &b, &a);
772                                 frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity ? fill_opacity : a);
773                         }
774                 }
775         }
776 }
777
778 /**
779  * Sets the colors of the start and end trim handle depending on object state
780  *
781  */
782 void
783 TimeAxisViewItem::set_trim_handle_colors()
784 {
785         if (frame_handle_start) {
786                 if (position_locked) {
787                         frame_handle_start->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TrimHandleLocked.get();
788                         frame_handle_end->property_fill_color_rgba() =  ARDOUR_UI::config()->canvasvar_TrimHandleLocked.get();
789                 } else {
790                         frame_handle_start->property_fill_color_rgba() = RGBA_TO_UINT(1, 1, 1, 0); //ARDOUR_UI::config()->canvasvar_TrimHandle.get();
791                         frame_handle_end->property_fill_color_rgba() = RGBA_TO_UINT(1, 1, 1, 0); //ARDOUR_UI::config()->canvasvar_TrimHandle.get();
792                 }
793         }
794 }
795
796 double
797 TimeAxisViewItem::get_samples_per_unit()
798 {
799         return(samples_per_unit) ;
800 }
801
802 void
803 TimeAxisViewItem::set_samples_per_unit (double spu)
804 {
805         samples_per_unit = spu ;
806         set_position (this->get_position(), this);
807         reset_width_dependent_items ((double)get_duration() / samples_per_unit);
808 }
809
810 void
811 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
812 {
813         if (pixel_width < GRAB_HANDLE_LENGTH * 2) {
814
815                 if (frame_handle_start) {
816                         frame_handle_start->hide();
817                         frame_handle_end->hide();
818                 }
819
820         } if (pixel_width < 2.0) {
821
822                 if (show_vestigial) {
823                         vestigial_frame->show();
824                 }
825
826                 if (name_highlight) {
827                         name_highlight->hide();
828                 }
829
830                 if (frame) {
831                         frame->hide();
832                 }
833
834                 if (frame_handle_start) {
835                         frame_handle_start->hide();
836                         frame_handle_end->hide();
837                 }
838
839                 wide_enough_for_name = false;
840
841         } else {
842                 vestigial_frame->hide();
843
844                 if (name_highlight) {
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         update_name_pixbuf_visibility ();
883 }
884
885 void
886 TimeAxisViewItem::reset_name_width (double /*pixel_width*/)
887 {
888         uint32_t it_width;
889         int pb_width;
890         bool pixbuf_holds_full_name;
891
892         if (!name_pixbuf) {
893                 return;
894         }
895
896         it_width = trackview.editor().frame_to_pixel(item_duration);
897         pb_width = name_pixbuf_width;
898
899         pixbuf_holds_full_name = last_item_width > pb_width + NAME_X_OFFSET;
900         last_item_width = it_width;
901
902         if (pixbuf_holds_full_name && (it_width >= pb_width + NAME_X_OFFSET)) {
903                 /*
904                   we've previously had the full name length showing 
905                   and its still showing.
906                 */
907                 return;
908         }
909         
910         if (pb_width > it_width - NAME_X_OFFSET) {
911                 pb_width = it_width - NAME_X_OFFSET;
912         }
913         
914         if (it_width <= NAME_X_OFFSET) {
915                 wide_enough_for_name = false;
916         } else {
917                 wide_enough_for_name = true;
918         }
919
920         update_name_pixbuf_visibility ();
921         if (pb_width > 0) {
922                 name_pixbuf->property_pixbuf() = pixbuf_from_ustring(item_name, NAME_FONT, pb_width, NAME_HEIGHT, Gdk::Color ("#000000"));
923         }
924 }
925
926
927 //---------------------------------------------------------------------------------------//
928 // Handle time axis removal
929
930 /**
931  * Handles the Removal of this time axis item
932  * This _needs_ to be called to alert others of the removal properly, ie where the source
933  * of the removal came from.
934  *
935  * XXX Although im not too happy about this method of doing things, I cant think of a cleaner method
936  *     just now to capture the source of the removal
937  *
938  * @param src the identity of the object that initiated the change
939  */
940 void
941 TimeAxisViewItem::remove_this_item(void* src)
942 {
943         /*
944            defer to idle loop, otherwise we'll delete this object
945            while we're still inside this function ...
946         */
947         Glib::signal_idle().connect(sigc::bind (sigc::ptr_fun (&TimeAxisViewItem::idle_remove_this_item), this, src));
948 }
949
950 /**
951  * Callback used to remove this time axis item during the gtk idle loop
952  * This is used to avoid deleting the obejct while inside the remove_this_item
953  * method
954  *
955  * @param item the TimeAxisViewItem to remove
956  * @param src the identity of the object that initiated the change
957  */
958 gint
959 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
960 {
961         item->ItemRemoved (item->get_item_name(), src) ; /* EMIT_SIGNAL */
962         delete item;
963         item = 0;
964         return false;
965 }
966
967 void
968 TimeAxisViewItem::set_y (double y)
969 {
970         double const old = group->property_y ();
971         if (y != old) {
972                 group->move (0, y - old);
973         }
974 }
975
976 void
977 TimeAxisViewItem::update_name_pixbuf_visibility ()
978 {
979         if (!name_pixbuf) {
980                 return;
981         }
982         
983         if (wide_enough_for_name && high_enough_for_name) {
984                 name_pixbuf->show ();
985         } else {
986                 name_pixbuf->hide ();
987         }
988 }
989