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