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