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