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