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