tweak event/leave event delivery so that it applies to items being deleted as well...
[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 <utility>
21
22 #include "pbd/error.h"
23 #include "pbd/stacktrace.h"
24
25 #include "ardour/types.h"
26 #include "ardour/ardour.h"
27
28 #include "gtkmm2ext/utils.h"
29 #include "gtkmm2ext/gui_thread.h"
30
31 #include "canvas/group.h"
32 #include "canvas/rectangle.h"
33 #include "canvas/debug.h"
34 #include "canvas/text.h"
35 #include "canvas/utils.h"
36
37 #include "ardour_ui.h"
38 /*
39  * ardour_ui.h was moved up in the include list
40  * due to a conflicting definition of 'Rect' between
41  * Apple's MacTypes.h file and GTK
42  */
43
44 #include "public_editor.h"
45 #include "time_axis_view_item.h"
46 #include "time_axis_view.h"
47 #include "utils.h"
48 #include "rgb_macros.h"
49
50 #include "i18n.h"
51
52 using namespace std;
53 using namespace Editing;
54 using namespace Glib;
55 using namespace PBD;
56 using namespace ARDOUR;
57 using namespace Gtkmm2ext;
58
59 Pango::FontDescription TimeAxisViewItem::NAME_FONT;
60 const double TimeAxisViewItem::NAME_X_OFFSET = 15.0;
61 const double TimeAxisViewItem::GRAB_HANDLE_TOP = 6;
62 const double TimeAxisViewItem::GRAB_HANDLE_WIDTH = 5;
63
64 int    TimeAxisViewItem::NAME_HEIGHT;
65 double TimeAxisViewItem::NAME_Y_OFFSET;
66 double TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
67 double TimeAxisViewItem::NAME_HIGHLIGHT_THRESH;
68
69 void
70 TimeAxisViewItem::set_constant_heights ()
71 {
72         NAME_FONT = get_font_for_style (X_("TimeAxisViewItemName"));
73
74         Gtk::Window win;
75         Gtk::Label foo;
76         win.add (foo);
77
78         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
79         int width = 0;
80         int height = 0;
81
82         layout->set_font_description (NAME_FONT);
83         Gtkmm2ext::get_ink_pixel_size (layout, width, height);
84
85         NAME_HEIGHT = height;
86         NAME_Y_OFFSET = height + 5; // XXX this offset is magic
87         NAME_HIGHLIGHT_SIZE = height + 2;
88         NAME_HIGHLIGHT_THRESH = NAME_HIGHLIGHT_SIZE * 3;
89 }
90
91 /**
92  * Construct a new TimeAxisViewItem.
93  *
94  * @param it_name the unique name of this item
95  * @param parent 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  * @param recording true if this is a recording region view
102  * @param automation true if this is an automation region view
103  */
104 TimeAxisViewItem::TimeAxisViewItem(
105         const string & it_name, ArdourCanvas::Group& parent, TimeAxisView& tv, double spu, Gdk::Color const & base_color,
106         framepos_t start, framecnt_t duration, bool recording, bool automation, Visibility vis
107         )
108         : trackview (tv)
109         , _height (1.0)
110         , _recregion (recording)
111         , _automation (automation)
112         , _dragging (false)
113 {
114         group = new ArdourCanvas::Group (&parent);
115         CANVAS_DEBUG_NAME (group, string_compose ("TAVI group for %1", it_name));
116
117         init (it_name, spu, base_color, start, duration, vis, true, true);
118 }
119
120 TimeAxisViewItem::TimeAxisViewItem (const TimeAxisViewItem& other)
121         : trackable (other)
122         , Selectable (other)
123         , PBD::ScopedConnectionList()
124         , trackview (other.trackview)
125         , _recregion (other._recregion)
126         , _automation (other._automation)
127         , _dragging (other._dragging)
128 {
129
130         Gdk::Color c;
131         int r,g,b,a;
132
133         UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
134         c.set_rgb_p (r/255.0, g/255.0, b/255.0);
135
136         /* share the other's parent, but still create a new group */
137
138         ArdourCanvas::Group* parent = other.group->parent();
139
140         group = new ArdourCanvas::Group (parent);
141
142         _selected = other._selected;
143
144         init (other.item_name, other.samples_per_pixel, c, other.frame_position,
145               other.item_duration, other.visibility, other.wide_enough_for_name, other.high_enough_for_name);
146 }
147
148 void
149 TimeAxisViewItem::init (const string& it_name, double fpp, Gdk::Color const & base_color, framepos_t start, framepos_t duration, Visibility vis, bool wide, bool high)
150 {
151         item_name = it_name;
152         samples_per_pixel = fpp;
153         frame_position = start;
154         item_duration = duration;
155         name_connected = false;
156         fill_opacity = 60;
157         position_locked = false;
158         max_item_duration = ARDOUR::max_framepos;
159         min_item_duration = 0;
160         show_vestigial = true;
161         visibility = vis;
162         _sensitive = true;
163         name_text_width = 0;
164         last_item_width = 0;
165         wide_enough_for_name = wide;
166         high_enough_for_name = high;
167         rect_visible = true;
168
169         if (duration == 0) {
170                 warning << "Time Axis Item Duration == 0" << endl;
171         }
172
173         vestigial_frame = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, 1.0, 2.0, trackview.current_height()));
174         vestigial_frame->hide ();
175         vestigial_frame->set_outline_color (ARDOUR_UI::config()->get_canvasvar_VestigialFrame());
176         vestigial_frame->set_fill_color (ARDOUR_UI::config()->get_canvasvar_VestigialFrame());
177
178         if (visibility & ShowFrame) {
179                 frame = new ArdourCanvas::Rectangle (group, 
180                                                      ArdourCanvas::Rect (0.0, 1.0, 
181                                                                          trackview.editor().sample_to_pixel(duration), 
182                                                                          trackview.current_height()));
183
184                 if (_recregion) {
185                         frame->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RecordingRect());
186                 } else {
187                         frame->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeAxisFrame());
188                 }
189
190         } else {
191
192                 frame = 0;
193         }
194
195         if (visibility & ShowNameHighlight) {
196
197                 if (visibility & FullWidthNameHighlight) {
198                         name_highlight = new ArdourCanvas::Rectangle (group, 
199                                                                       ArdourCanvas::Rect (0.0, trackview.editor().sample_to_pixel(item_duration),
200                                                                                           trackview.current_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE, 
201                                                                                           trackview.current_height()));
202                 } else {
203                         name_highlight = new ArdourCanvas::Rectangle (group, 
204                                                                       ArdourCanvas::Rect (1.0, trackview.editor().sample_to_pixel(item_duration) - 1, 
205                                                                                           trackview.current_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE, 
206                                                                                           trackview.current_height()));
207                 }
208
209                 name_highlight->set_data ("timeaxisviewitem", this);
210                 name_highlight->set_outline_what (ArdourCanvas::Rectangle::TOP);
211                 /* we should really use a canvas color property here */
212                 name_highlight->set_outline_color (RGBA_TO_UINT (0,0,0,255));
213
214         } else {
215                 name_highlight = 0;
216         }
217
218         if (visibility & ShowNameText) {
219                 name_text = new ArdourCanvas::Text (group);
220                 name_text->set_position (ArdourCanvas::Duple (NAME_X_OFFSET, trackview.current_height() - NAME_Y_OFFSET));
221                 name_text->set_font_description (NAME_FONT);
222                 
223         } else {
224                 name_text = 0;
225         }
226
227         /* create our grab handles used for trimming/duration etc */
228         if (!_recregion && !_automation) {
229                 double top   = TimeAxisViewItem::GRAB_HANDLE_TOP;
230                 double width = TimeAxisViewItem::GRAB_HANDLE_WIDTH;
231
232                 frame_handle_start = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
233                 frame_handle_start->set_outline_what (ArdourCanvas::Rectangle::What (0));
234                 frame_handle_end = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
235                 frame_handle_end->set_outline_what (ArdourCanvas::Rectangle::What (0));
236         } else {
237                 frame_handle_start = frame_handle_end = 0;
238         }
239
240         set_color (base_color);
241
242         set_duration (item_duration, this);
243         set_position (start, this);
244
245         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&TimeAxisViewItem::parameter_changed, this, _1), gui_context ());
246         ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &TimeAxisViewItem::parameter_changed));
247 }
248
249 TimeAxisViewItem::~TimeAxisViewItem()
250 {
251         delete group;
252 }
253
254 void
255 TimeAxisViewItem::hide_rect ()
256 {
257         rect_visible = false;
258         set_frame_color ();
259
260         if (name_highlight) {
261                 name_highlight->set_outline_what (ArdourCanvas::Rectangle::What (0));
262                 name_highlight->set_fill_color (UINT_RGBA_CHANGE_A (fill_color, 64));
263         }
264 }
265
266 void
267 TimeAxisViewItem::show_rect ()
268 {
269         rect_visible = true;
270         set_frame_color ();
271
272         if (name_highlight) {
273                 name_highlight->set_outline_what (ArdourCanvas::Rectangle::TOP);
274                 name_highlight->set_fill_color (fill_color);
275         }
276 }
277
278 /**
279  * Set the position of this item on the timeline.
280  *
281  * @param pos the new position
282  * @param src the identity of the object that initiated the change
283  * @return true on success
284  */
285
286 bool
287 TimeAxisViewItem::set_position(framepos_t pos, void* src, double* delta)
288 {
289         if (position_locked) {
290                 return false;
291         }
292
293         frame_position = pos;
294
295         /*  This sucks. The GnomeCanvas version I am using
296             doesn't correctly implement gnome_canvas_group_set_arg(),
297             so that simply setting the "x" arg of the group
298             fails to move the group. Instead, we have to
299             use gnome_canvas_item_move(), which does the right
300             thing. I see that in GNOME CVS, the current (Sept 2001)
301             version of GNOME Canvas rectifies this issue cleanly.
302         */
303
304         double old_unit_pos;
305         double new_unit_pos = pos / samples_per_pixel;
306
307         old_unit_pos = group->position().x;
308
309         if (new_unit_pos != old_unit_pos) {
310                 group->set_x_position (new_unit_pos);
311         }
312
313         if (delta) {
314                 (*delta) = new_unit_pos - old_unit_pos;
315         }
316
317         PositionChanged (frame_position, src); /* EMIT_SIGNAL */
318
319         return true;
320 }
321
322 /** @return position of this item on the timeline */
323 framepos_t
324 TimeAxisViewItem::get_position() const
325 {
326         return frame_position;
327 }
328
329 /**
330  * Set the duration of this item.
331  *
332  * @param dur the new duration of this item
333  * @param src the identity of the object that initiated the change
334  * @return true on success
335  */
336
337 bool
338 TimeAxisViewItem::set_duration (framecnt_t dur, void* src)
339 {
340         if ((dur > max_item_duration) || (dur < min_item_duration)) {
341                 warning << string_compose (
342                                 P_("new duration %1 frame is out of bounds for %2", "new duration of %1 frames is out of bounds for %2", dur),
343                                 get_item_name(), dur)
344                         << endmsg;
345                 return false;
346         }
347
348         if (dur == 0) {
349                 group->hide();
350         }
351
352         item_duration = dur;
353
354         reset_width_dependent_items (trackview.editor().sample_to_pixel (dur));
355
356         DurationChanged (dur, src); /* EMIT_SIGNAL */
357         return true;
358 }
359
360 /** @return duration of this item */
361 framepos_t
362 TimeAxisViewItem::get_duration() const
363 {
364         return item_duration;
365 }
366
367 /**
368  * Set the maximum duration that this item can have.
369  *
370  * @param dur the new maximum duration
371  * @param src the identity of the object that initiated the change
372  */
373 void
374 TimeAxisViewItem::set_max_duration(framecnt_t dur, void* src)
375 {
376         max_item_duration = dur;
377         MaxDurationChanged(max_item_duration, src); /* EMIT_SIGNAL */
378 }
379
380 /** @return the maximum duration that this item may have */
381 framecnt_t
382 TimeAxisViewItem::get_max_duration() const
383 {
384         return max_item_duration;
385 }
386
387 /**
388  * Set the minimum duration that this item may have.
389  *
390  * @param the minimum duration that this item may be set to
391  * @param src the identity of the object that initiated the change
392  */
393 void
394 TimeAxisViewItem::set_min_duration(framecnt_t dur, void* src)
395 {
396         min_item_duration = dur;
397         MinDurationChanged(max_item_duration, src); /* EMIT_SIGNAL */
398 }
399
400 /** @return the minimum duration that this item mey have */
401 framecnt_t
402 TimeAxisViewItem::get_min_duration() const
403 {
404         return min_item_duration;
405 }
406
407 /**
408  * Set whether this item is locked to its current position.
409  * Locked items cannot be moved until the item is unlocked again.
410  *
411  * @param yn true to lock this item to its current position
412  * @param src the identity of the object that initiated the change
413  */
414 void
415 TimeAxisViewItem::set_position_locked(bool yn, void* src)
416 {
417         position_locked = yn;
418         set_trim_handle_colors();
419         PositionLockChanged (position_locked, src); /* EMIT_SIGNAL */
420 }
421
422 /** @return true if this item is locked to its current position */
423 bool
424 TimeAxisViewItem::get_position_locked() const
425 {
426         return position_locked;
427 }
428
429 /**
430  * Set whether the maximum duration constraint is active.
431  *
432  * @param active set true to enforce the max duration constraint
433  * @param src the identity of the object that initiated the change
434  */
435 void
436 TimeAxisViewItem::set_max_duration_active (bool active, void* /*src*/)
437 {
438         max_duration_active = active;
439 }
440
441 /** @return true if the maximum duration constraint is active */
442 bool
443 TimeAxisViewItem::get_max_duration_active() const
444 {
445         return max_duration_active;
446 }
447
448 /**
449  * Set whether the minimum duration constraint is active.
450  *
451  * @param active set true to enforce the min duration constraint
452  * @param src the identity of the object that initiated the change
453  */
454
455 void
456 TimeAxisViewItem::set_min_duration_active (bool active, void* /*src*/)
457 {
458         min_duration_active = active;
459 }
460
461 /** @return true if the maximum duration constraint is active */
462 bool
463 TimeAxisViewItem::get_min_duration_active() const
464 {
465         return min_duration_active;
466 }
467
468 /**
469  * Set the name of this item.
470  *
471  * @param new_name the new name of this item
472  * @param src the identity of the object that initiated the change
473  */
474
475 void
476 TimeAxisViewItem::set_item_name(std::string new_name, void* src)
477 {
478         if (new_name != item_name) {
479                 std::string temp_name = item_name;
480                 item_name = new_name;
481                 NameChanged (item_name, temp_name, src); /* EMIT_SIGNAL */
482         }
483 }
484
485 /** @return the name of this item */
486 std::string
487 TimeAxisViewItem::get_item_name() const
488 {
489         return item_name;
490 }
491
492 /**
493  * Set selection status.
494  *
495  * @param yn true if this item is currently selected
496  */
497 void
498 TimeAxisViewItem::set_selected(bool yn)
499 {
500         if (_selected != yn) {
501                 Selectable::set_selected (yn);
502                 set_frame_color ();
503         }
504 }
505
506 /** @return the TimeAxisView that this item is on */
507 TimeAxisView&
508 TimeAxisViewItem::get_time_axis_view () const
509 {
510         return trackview;
511 }
512
513 /**
514  * Set the displayed item text.
515  * This item is the visual text name displayed on the canvas item, this can be different to the name of the item.
516  *
517  * @param new_name the new name text to display
518  */
519
520 void
521 TimeAxisViewItem::set_name_text(const string& new_name)
522 {
523         if (!name_text) {
524                 return;
525         }
526
527         last_item_width = trackview.editor().sample_to_pixel(item_duration);
528         name_text_width = pixel_width (new_name, NAME_FONT) + 2;
529         name_text->set (new_name);
530
531 }
532
533 /**
534  * Set the height of this item.
535  *
536  * @param h new height
537  */
538 void
539 TimeAxisViewItem::set_height (double height)
540 {
541         _height = height;
542
543         if (name_highlight) {
544                 if (height < NAME_HIGHLIGHT_THRESH) {
545                         name_highlight->hide ();
546                         high_enough_for_name = false;
547
548                 } else {
549                         name_highlight->show();
550                         high_enough_for_name = true;
551                 }
552
553                 if (height > NAME_HIGHLIGHT_SIZE) {
554                         name_highlight->set_y0 ((double) height - 1 - NAME_HIGHLIGHT_SIZE);
555                         name_highlight->set_y1 ((double) height - 1);
556                 }
557                 else {
558                         /* it gets hidden now anyway */
559                         name_highlight->set_y0 (1);
560                         name_highlight->set_y1 (height);
561                 }
562         }
563
564         if (visibility & ShowNameText) {
565                 name_text->set_y_position (height + 1 - NAME_Y_OFFSET);
566         }
567
568         if (frame) {
569                 frame->set_y1 (height - 1);
570                 if (frame_handle_start) {
571                         frame_handle_start->set_y1 (height - 1);
572                         frame_handle_end->set_y1 (height - 1);
573                 }
574         }
575
576         vestigial_frame->set_y1 (height - 1);
577
578         update_name_text_visibility ();
579         set_colors ();
580 }
581
582 void
583 TimeAxisViewItem::set_color (Gdk::Color const & base_color)
584 {
585         compute_colors (base_color);
586         set_colors ();
587 }
588
589 ArdourCanvas::Item*
590 TimeAxisViewItem::get_canvas_frame()
591 {
592         return frame;
593 }
594
595 ArdourCanvas::Group*
596 TimeAxisViewItem::get_canvas_group()
597 {
598         return group;
599 }
600
601 ArdourCanvas::Item*
602 TimeAxisViewItem::get_name_highlight()
603 {
604         return name_highlight;
605 }
606
607 /**
608  * Calculate some contrasting color for displaying various parts of this item, based upon the base color.
609  *
610  * @param color the base color of the item
611  */
612 void
613 TimeAxisViewItem::compute_colors (Gdk::Color const & base_color)
614 {
615         unsigned char radius;
616         char minor_shift;
617
618         unsigned char r,g,b;
619
620         /* FILL: this is simple */
621         r = base_color.get_red()/256;
622         g = base_color.get_green()/256;
623         b = base_color.get_blue()/256;
624         fill_color = RGBA_TO_UINT(r,g,b,160);
625
626         /*  for minor colors:
627                 if the overall saturation is strong, make the minor colors light.
628                 if its weak, make them dark.
629
630                 we do this by moving an equal distance to the other side of the
631                 central circle in the color wheel from where we started.
632         */
633
634         radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f));
635         minor_shift = 125 - radius;
636
637         /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
638
639         r = base_color.get_red()/256;
640         g = base_color.get_green()/256;
641         b = base_color.get_blue()/256;
642
643         if (r > b)
644         {
645                 if (r > g)
646                 {
647                         /* red sector => green */
648                         swap (r,g);
649                 }
650                 else
651                 {
652                         /* green sector => blue */
653                         swap (g,b);
654                 }
655         }
656         else
657         {
658                 if (b > g)
659                 {
660                         /* blue sector => red */
661                         swap (b,r);
662                 }
663                 else
664                 {
665                         /* green sector => blue */
666                         swap (g,b);
667                 }
668         }
669
670         r += minor_shift;
671         b += minor_shift;
672         g += minor_shift;
673
674         label_color = RGBA_TO_UINT(r,g,b,255);
675         r = (base_color.get_red()/256)   + 127;
676         g = (base_color.get_green()/256) + 127;
677         b = (base_color.get_blue()/256)  + 127;
678
679         label_color = RGBA_TO_UINT(r,g,b,255);
680
681         /* XXX can we do better than this ? */
682         /* We're trying;) */
683         /* NUKECOLORS */
684
685         //frame_color_r = 192;
686         //frame_color_g = 192;
687         //frame_color_b = 194;
688
689         //selected_frame_color_r = 182;
690         //selected_frame_color_g = 145;
691         //selected_frame_color_b = 168;
692
693         //handle_color_r = 25;
694         //handle_color_g = 0;
695         //handle_color_b = 255;
696         //lock_handle_color_r = 235;
697         //lock_handle_color_g = 16;
698         //lock_handle_color_b = 16;
699 }
700
701 /**
702  * Convenience method to set the various canvas item colors
703  */
704 void
705 TimeAxisViewItem::set_colors()
706 {
707         set_frame_color();
708
709         if (name_highlight) {
710                 name_highlight->set_fill_color (fill_color);
711         }
712         set_trim_handle_colors();
713 }
714
715 /**
716  * Sets the frame color depending on whether this item is selected
717  */
718 void
719 TimeAxisViewItem::set_frame_color()
720 {
721         uint32_t f = 0;
722
723         if (!frame) {
724                 return;
725         }
726
727         if (_selected) {
728
729                 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
730
731                 if (fill_opacity) {
732                         f = UINT_RGBA_CHANGE_A (f, fill_opacity);
733                 }
734
735                 if (!rect_visible) {
736                         f = UINT_RGBA_CHANGE_A (f, 0);
737                 }
738
739         } else {
740
741                 if (_recregion) {
742                         f = ARDOUR_UI::config()->get_canvasvar_RecordingRect();
743                 } else {
744
745                         if (high_enough_for_name && !Config->get_color_regions_using_track_color()) {
746                                 f = ARDOUR_UI::config()->get_canvasvar_FrameBase();
747                         } else {
748                                 f = fill_color;
749                         }
750
751                         if (fill_opacity) {
752                                 f = UINT_RGBA_CHANGE_A (f, fill_opacity);
753                         }
754
755                         if (!rect_visible) {
756                                 f = UINT_RGBA_CHANGE_A (f, 0);
757                         }
758                 }
759         }
760
761         frame->set_fill_color (f);
762         set_frame_gradient ();
763
764         if (!_recregion) {
765                 if (_selected) {
766                         f = ARDOUR_UI::config()->get_canvasvar_SelectedTimeAxisFrame();
767                 } else {
768                         f = ARDOUR_UI::config()->get_canvasvar_TimeAxisFrame();
769                 }
770
771                 if (!rect_visible) {
772                         f = UINT_RGBA_CHANGE_A (f, 64);
773                 }
774
775                 frame->set_outline_color (f);
776         }
777 }
778
779 void
780 TimeAxisViewItem::set_frame_gradient ()
781 {
782         if (ARDOUR_UI::config()->get_timeline_item_gradient_depth() == 0.0) {
783                 frame->set_gradient (ArdourCanvas::Fill::StopList (), 0);
784                 return;
785         }
786                 
787         ArdourCanvas::Fill::StopList stops;
788         double r, g, b, a;
789         double h, s, v;
790         ArdourCanvas::Color f (frame->fill_color());
791
792         /* need to get alpha value */
793         ArdourCanvas::color_to_rgba (f, r, g, b, a);
794         
795         stops.push_back (std::make_pair (0.0, f));
796         
797         /* now a darker version */
798         
799         ArdourCanvas::color_to_hsv (f, h, s, v);
800         s *= ARDOUR_UI::config()->get_timeline_item_gradient_depth();
801         if (s > 1.0) {
802                 s = 1.0;
803         }
804         
805         ArdourCanvas::Color darker = ArdourCanvas::hsv_to_color (h, s, v, a);
806         stops.push_back (std::make_pair (1.0, darker));
807         
808         frame->set_gradient (stops, _height);
809 }
810
811 /**
812  * Set the colors of the start and end trim handle depending on object state
813  */
814 void
815 TimeAxisViewItem::set_trim_handle_colors()
816 {
817         if (frame_handle_start) {
818                 if (position_locked) {
819                         frame_handle_start->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TrimHandleLocked());
820                         frame_handle_end->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TrimHandleLocked());
821                 } else {
822                         frame_handle_start->set_fill_color (RGBA_TO_UINT (1, 1, 1, 0)); //ARDOUR_UI::config()->get_canvasvar_TrimHandle();
823                         frame_handle_end->set_fill_color (RGBA_TO_UINT (1, 1, 1, 0)); //ARDOUR_UI::config()->get_canvasvar_TrimHandle();
824                 }
825         }
826 }
827
828 /** @return the frames per pixel */
829 double
830 TimeAxisViewItem::get_samples_per_pixel () const
831 {
832         return samples_per_pixel;
833 }
834
835 /** Set the frames per pixel of this item.
836  *  This item is used to determine the relative visual size and position of this item
837  *  based upon its duration and start value.
838  *
839  *  @param fpp the new frames per pixel
840  */
841 void
842 TimeAxisViewItem::set_samples_per_pixel (double fpp)
843 {
844         samples_per_pixel = fpp;
845         set_position (this->get_position(), this);
846         reset_width_dependent_items ((double) get_duration() / samples_per_pixel);
847 }
848
849 void
850 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
851 {
852         if (pixel_width < 2.0) {
853
854                 if (show_vestigial) {
855                         vestigial_frame->show();
856                 }
857
858                 if (name_highlight) {
859                         name_highlight->hide();
860                 }
861
862                 if (frame) {
863                         frame->hide();
864                 }
865
866                 if (frame_handle_start) {
867                         frame_handle_start->hide();
868                         frame_handle_end->hide();
869                 }
870
871                 wide_enough_for_name = false;
872
873         } else {
874                 vestigial_frame->hide();
875
876                 if (name_highlight) {
877
878                         if (_height < NAME_HIGHLIGHT_THRESH) {
879                                 name_highlight->hide();
880                                 high_enough_for_name = false;
881                         } else {
882                                 name_highlight->show();
883                                 if (!get_item_name().empty()) {
884                                         reset_name_width (pixel_width);
885                                 }
886                                 high_enough_for_name = true;
887                         }
888
889                         name_highlight->set_x1 (pixel_width);
890                 }
891
892                 if (frame) {
893                         frame->show();
894                         frame->set_x1 (pixel_width);
895                 }
896
897                 if (frame_handle_start) {
898                         if (pixel_width < (3 * TimeAxisViewItem::GRAB_HANDLE_WIDTH)) {
899                                 /*
900                                  * there's less than GRAB_HANDLE_WIDTH of the region between 
901                                  * the right-hand end of frame_handle_start and the left-hand
902                                  * end of frame_handle_end, so disable the handles
903                                  */
904                                 frame_handle_start->hide();
905                                 frame_handle_end->hide();
906                         } else {
907                                 frame_handle_start->show();
908                                 frame_handle_end->set_x0 (pixel_width - (TimeAxisViewItem::GRAB_HANDLE_WIDTH));
909                                 frame_handle_end->set_x1 (pixel_width);
910                                 frame_handle_end->show();
911                         }
912                 }
913
914                 wide_enough_for_name = true;
915         }
916
917         update_name_text_visibility ();
918 }
919
920 void
921 TimeAxisViewItem::reset_name_width (double /*pixel_width*/)
922 {
923         uint32_t it_width;
924         int pb_width;
925         bool showing_full_name;
926
927         if (!name_text) {
928                 return;
929         }
930
931         it_width = trackview.editor().sample_to_pixel(item_duration);
932         pb_width = name_text_width;
933
934         showing_full_name = last_item_width > pb_width + NAME_X_OFFSET;
935         last_item_width = it_width;
936
937         if (showing_full_name && (it_width >= pb_width + NAME_X_OFFSET)) {
938                 /*
939                   we've previously had the full name length showing
940                   and its still showing.
941                 */
942                 return;
943         }
944
945         if (pb_width > it_width - NAME_X_OFFSET) {
946                 pb_width = it_width - NAME_X_OFFSET;
947         }
948
949         if (it_width <= NAME_X_OFFSET) {
950                 wide_enough_for_name = false;
951         } else {
952                 wide_enough_for_name = true;
953         }
954
955         update_name_text_visibility ();
956
957         if (pb_width < 1) {
958                 pb_width = 1;
959         }
960
961         name_text->set (item_name);
962         name_text->clamp_width (pb_width);
963 }
964
965 /**
966  * Callback used to remove this time axis item during the gtk idle loop.
967  * This is used to avoid deleting the obejct while inside the remove_this_item
968  * method.
969  *
970  * @param item the TimeAxisViewItem to remove.
971  * @param src the identity of the object that initiated the change.
972  */
973 gint
974 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
975 {
976         item->ItemRemoved (item->get_item_name(), src); /* EMIT_SIGNAL */
977         delete item;
978         item = 0;
979         return false;
980 }
981
982 void
983 TimeAxisViewItem::set_y (double y)
984 {
985         group->set_y_position (y);
986 }
987
988 void
989 TimeAxisViewItem::update_name_text_visibility ()
990 {
991         if (!name_text) {
992                 return;
993         }
994
995         if (wide_enough_for_name && high_enough_for_name) {
996                 name_text->show ();
997         } else {
998                 name_text->hide ();
999         }
1000 }
1001
1002 void
1003 TimeAxisViewItem::parameter_changed (string p)
1004 {
1005         if (p == "color-regions-using-track-color") {
1006                 set_frame_color ();
1007         } else if (p == "timeline-item-gradient-depth") {
1008                 set_frame_gradient ();
1009         }
1010 }