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