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