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