Fix crash when X11 is not available for VST UIs
[ardour.git] / gtk2_ardour / time_axis_view_item.cc
1 /*
2  * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
4  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
5  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
7  * Copyright (C) 2007 Doug McLain <doug@nostar.net>
8  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
9  * Copyright (C) 2014-2016 Nick Mainsbridge <mainsbridge@gmail.com>
10  * Copyright (C) 2014 Ben Loftis <ben@harrisonconsoles.com>
11  * Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include <utility>
29
30 #include "pbd/error.h"
31 #include "pbd/stacktrace.h"
32
33 #include "ardour/types.h"
34 #include "ardour/ardour.h"
35
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/gui_thread.h"
38
39 #include "canvas/container.h"
40 #include "canvas/rectangle.h"
41 #include "canvas/debug.h"
42 #include "canvas/text.h"
43 #include "gtkmm2ext/colors.h"
44
45 #include "ardour/profile.h"
46
47 #include "public_editor.h"
48 #include "time_axis_view_item.h"
49 #include "time_axis_view.h"
50 #include "ui_config.h"
51 #include "utils.h"
52 #include "rgb_macros.h"
53
54 #include "pbd/i18n.h"
55
56 using namespace std;
57 using namespace Editing;
58 using namespace Glib;
59 using namespace PBD;
60 using namespace ARDOUR;
61 using namespace ARDOUR_UI_UTILS;
62 using namespace Gtkmm2ext;
63
64 Pango::FontDescription TimeAxisViewItem::NAME_FONT;
65 const double TimeAxisViewItem::NAME_X_OFFSET = 15.0;
66 const double TimeAxisViewItem::GRAB_HANDLE_TOP = 0.0;
67 const double TimeAxisViewItem::GRAB_HANDLE_WIDTH = 10.0;
68
69 int    TimeAxisViewItem::NAME_HEIGHT;
70 double TimeAxisViewItem::NAME_Y_OFFSET;
71 double TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
72 double TimeAxisViewItem::NAME_HIGHLIGHT_THRESH;
73
74 void
75 TimeAxisViewItem::set_constant_heights ()
76 {
77         NAME_FONT = Pango::FontDescription (UIConfiguration::instance().get_SmallFont());
78
79         Gtk::Window win;
80         Gtk::Label foo;
81         win.add (foo);
82
83         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
84         int width = 0;
85         int height = 0;
86
87         layout->set_font_description (NAME_FONT);
88         get_pixel_size (layout, width, height);
89
90         layout = foo.create_pango_layout (X_("H")); /* just the ascender */
91
92         NAME_HEIGHT = height;
93
94         /* Config->get_show_name_highlight) == true:
95                 Y_OFFSET is measured from bottom of the time axis view item.
96            Config->get_show_name_highlight) == false:
97                 Y_OFFSET is measured from the top of the time axis view item.
98         */
99
100         if (UIConfiguration::instance().get_show_name_highlight()) {
101                 NAME_Y_OFFSET = height + 1;
102                 NAME_HIGHLIGHT_SIZE = height + 2;
103         } else {
104                 NAME_Y_OFFSET = 3;
105                 NAME_HIGHLIGHT_SIZE = 0;
106         }
107         NAME_HIGHLIGHT_THRESH = NAME_HIGHLIGHT_SIZE * 3;
108 }
109
110 /**
111  * Construct a new TimeAxisViewItem.
112  *
113  * @param it_name the unique name of this item
114  * @param parent the parent canvas group
115  * @param tv the TimeAxisView we are going to be added to
116  * @param spu samples per unit
117  * @param base_color
118  * @param start the start point of this item
119  * @param duration the duration of this item
120  * @param recording true if this is a recording region view
121  * @param automation true if this is an automation region view
122  */
123 TimeAxisViewItem::TimeAxisViewItem(
124         const string & it_name, ArdourCanvas::Item& parent, TimeAxisView& tv, double spu, uint32_t base_color,
125         samplepos_t start, samplecnt_t duration, bool recording, bool automation, Visibility vis
126         )
127         : trackview (tv)
128         , sample_position (-1)
129         , item_name (it_name)
130         , selection_frame (0)
131         , _height (1.0)
132         , _recregion (recording)
133         , _automation (automation)
134         , _dragging (false)
135         , _width (0.0)
136 {
137         init (&parent, spu, base_color, start, duration, vis, true, true);
138 }
139
140 TimeAxisViewItem::TimeAxisViewItem (const TimeAxisViewItem& other)
141         : trackable (other)
142         , Selectable (other)
143         , PBD::ScopedConnectionList()
144         , trackview (other.trackview)
145         , sample_position (-1)
146         , item_name (other.item_name)
147         , selection_frame (0)
148         , _height (1.0)
149         , _recregion (other._recregion)
150         , _automation (other._automation)
151         , _dragging (other._dragging)
152         , _width (0.0)
153 {
154         /* share the other's parent, but still create a new group */
155
156         ArdourCanvas::Item* parent = other.group->parent();
157
158         _selected = other._selected;
159
160         init (parent, other.samples_per_pixel, other.fill_color, other.sample_position,
161               other.item_duration, other.visibility, other.wide_enough_for_name, other.high_enough_for_name);
162 }
163
164 void
165 TimeAxisViewItem::init (ArdourCanvas::Item* parent, double fpp, uint32_t base_color,
166                         samplepos_t start, samplepos_t duration, Visibility vis,
167                         bool wide, bool high)
168 {
169         group = new ArdourCanvas::Container (parent);
170         CANVAS_DEBUG_NAME (group, string_compose ("TAVI group for %1", get_item_name()));
171
172         fill_color = base_color;
173         fill_color_name = "time axis view item base";
174         samples_per_pixel = fpp;
175         sample_position = start;
176         item_duration = duration;
177         name_connected = false;
178         position_locked = false;
179         max_item_duration = Temporal::max_samplepos;
180         min_item_duration = 0;
181         visibility = vis;
182         _sensitive = true;
183         name_text_width = 0;
184         last_item_width = 0;
185         wide_enough_for_name = wide;
186         high_enough_for_name = high;
187
188         if (duration == 0) {
189                 warning << "Time Axis Item Duration == 0" << endl;
190         }
191
192         if (visibility & ShowFrame) {
193                 frame = new ArdourCanvas::Rectangle (group,
194                                                      ArdourCanvas::Rect (0.0, 0.0,
195                                                                          trackview.editor().sample_to_pixel(duration),
196                                                                          trackview.current_height()));
197
198                 frame->set_outline_what (ArdourCanvas::Rectangle::What (ArdourCanvas::Rectangle::LEFT|ArdourCanvas::Rectangle::RIGHT));
199                 frame->show ();
200
201                 CANVAS_DEBUG_NAME (frame, string_compose ("frame for %1", get_item_name()));
202
203                 if (_recregion) {
204                         frame->set_outline_color (UIConfiguration::instance().color ("recording rect"));
205                 } else {
206                         frame->set_outline_color (UIConfiguration::instance().color ("time axis frame"));
207                 }
208         }
209
210         if (UIConfiguration::instance().get_show_name_highlight() && (visibility & ShowNameHighlight)) {
211
212                 /* rectangle size will be set in ::manage_name_highlight() */
213                 name_highlight = new ArdourCanvas::Rectangle (group);
214                 CANVAS_DEBUG_NAME (name_highlight, string_compose ("name highlight for %1", get_item_name()));
215                 name_highlight->set_data ("timeaxisviewitem", this);
216                 name_highlight->set_outline_what (ArdourCanvas::Rectangle::TOP);
217                 name_highlight->set_outline_color (RGBA_TO_UINT (0,0,0,255)); // this should use a theme color
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                 if (UIConfiguration::instance().get_show_name_highlight()) {
227                         name_text->set_position (ArdourCanvas::Duple (NAME_X_OFFSET, trackview.current_height() - NAME_Y_OFFSET));
228                 } else {
229                         name_text->set_position (ArdourCanvas::Duple (NAME_X_OFFSET, NAME_Y_OFFSET));
230                 }
231                 name_text->set_font_description (NAME_FONT);
232                 name_text->set_ignore_events (true);
233         } else {
234                 name_text = 0;
235         }
236
237         /* create our grab handles used for trimming/duration etc */
238         if (!_recregion && !_automation) {
239                 double top   = TimeAxisViewItem::GRAB_HANDLE_TOP;
240                 double width = TimeAxisViewItem::GRAB_HANDLE_WIDTH;
241
242                 frame_handle_start = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
243                 CANVAS_DEBUG_NAME (frame_handle_start, "TAVI frame handle start");
244                 frame_handle_start->set_outline (false);
245                 frame_handle_start->set_fill (false);
246                 frame_handle_start->Event.connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisViewItem::frame_handle_crossing), frame_handle_start));
247
248                 frame_handle_end = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
249                 CANVAS_DEBUG_NAME (frame_handle_end, "TAVI frame handle end");
250                 frame_handle_end->set_outline (false);
251                 frame_handle_end->set_fill (false);
252                 frame_handle_end->Event.connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisViewItem::frame_handle_crossing), frame_handle_end));
253         } else {
254                 frame_handle_start = frame_handle_end = 0;
255         }
256
257         //set_color (base_color);
258
259         //set_duration (item_duration, this);
260         //set_position (start, this);
261
262         group->Event.connect (sigc::mem_fun (*this, &TimeAxisViewItem::canvas_group_event));
263         //Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&TimeAxisViewItem::parameter_changed, this, _1), gui_context ());
264         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &TimeAxisViewItem::parameter_changed));
265 }
266
267 TimeAxisViewItem::~TimeAxisViewItem()
268 {
269         delete group;
270 }
271
272 bool
273 TimeAxisViewItem::canvas_group_event (GdkEvent* /*ev*/)
274 {
275         return false;
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(samplepos_t pos, void* src, double* delta)
288 {
289         if (position_locked) {
290                 return false;
291         }
292
293         sample_position = pos;
294
295         double new_unit_pos = trackview.editor().sample_to_pixel (pos);
296
297         if (delta) {
298                 (*delta) = new_unit_pos - group->position().x;
299                 if (*delta == 0.0) {
300                         return true;
301                 }
302         } else {
303                 if (new_unit_pos == group->position().x) {
304                         return true;
305                 }
306         }
307
308         group->set_x_position (new_unit_pos);
309         PositionChanged (sample_position, src); /* EMIT_SIGNAL */
310
311         return true;
312 }
313
314 /** @return position of this item on the timeline */
315 samplepos_t
316 TimeAxisViewItem::get_position() const
317 {
318         return sample_position;
319 }
320
321 /**
322  * Set the duration of this item.
323  *
324  * @param dur the new duration of this item
325  * @param src the identity of the object that initiated the change
326  * @return true on success
327  */
328
329 bool
330 TimeAxisViewItem::set_duration (samplecnt_t dur, void* src)
331 {
332         if ((dur > max_item_duration) || (dur < min_item_duration)) {
333                 warning << string_compose (
334                                 P_("new duration %1 frame is out of bounds for %2", "new duration of %1 samples is out of bounds for %2", dur),
335                                 get_item_name(), dur)
336                         << endmsg;
337                 return false;
338         }
339
340         if (dur == 0) {
341                 group->hide();
342         }
343
344         item_duration = dur;
345
346         double end_pixel = trackview.editor().sample_to_pixel (sample_position + dur);
347         double first_pixel = trackview.editor().sample_to_pixel (sample_position);
348
349         reset_width_dependent_items (end_pixel - first_pixel);
350
351         DurationChanged (dur, src); /* EMIT_SIGNAL */
352         return true;
353 }
354
355 /** @return duration of this item */
356 samplepos_t
357 TimeAxisViewItem::get_duration() const
358 {
359         return item_duration;
360 }
361
362 /**
363  * Set the maximum duration that this item can have.
364  *
365  * @param dur the new maximum duration
366  * @param src the identity of the object that initiated the change
367  */
368 void
369 TimeAxisViewItem::set_max_duration(samplecnt_t dur, void* src)
370 {
371         max_item_duration = dur;
372         MaxDurationChanged(max_item_duration, src); /* EMIT_SIGNAL */
373 }
374
375 /** @return the maximum duration that this item may have */
376 samplecnt_t
377 TimeAxisViewItem::get_max_duration() const
378 {
379         return max_item_duration;
380 }
381
382 /**
383  * Set the minimum duration that this item may have.
384  *
385  * @param the minimum duration that this item may be set to
386  * @param src the identity of the object that initiated the change
387  */
388 void
389 TimeAxisViewItem::set_min_duration(samplecnt_t dur, void* src)
390 {
391         min_item_duration = dur;
392         MinDurationChanged(max_item_duration, src); /* EMIT_SIGNAL */
393 }
394
395 /** @return the minimum duration that this item mey have */
396 samplecnt_t
397 TimeAxisViewItem::get_min_duration() const
398 {
399         return min_item_duration;
400 }
401
402 /**
403  * Set whether this item is locked to its current position.
404  * Locked items cannot be moved until the item is unlocked again.
405  *
406  * @param yn true to lock this item to its current position
407  * @param src the identity of the object that initiated the change
408  */
409 void
410 TimeAxisViewItem::set_position_locked(bool yn, void* src)
411 {
412         position_locked = yn;
413         set_trim_handle_colors();
414         PositionLockChanged (position_locked, src); /* EMIT_SIGNAL */
415 }
416
417 /** @return true if this item is locked to its current position */
418 bool
419 TimeAxisViewItem::get_position_locked() const
420 {
421         return position_locked;
422 }
423
424 /**
425  * Set whether the maximum duration constraint is active.
426  *
427  * @param active set true to enforce the max duration constraint
428  * @param src the identity of the object that initiated the change
429  */
430 void
431 TimeAxisViewItem::set_max_duration_active (bool active, void* /*src*/)
432 {
433         max_duration_active = active;
434 }
435
436 /** @return true if the maximum duration constraint is active */
437 bool
438 TimeAxisViewItem::get_max_duration_active() const
439 {
440         return max_duration_active;
441 }
442
443 /**
444  * Set whether the minimum duration constraint is active.
445  *
446  * @param active set true to enforce the min duration constraint
447  * @param src the identity of the object that initiated the change
448  */
449
450 void
451 TimeAxisViewItem::set_min_duration_active (bool active, void* /*src*/)
452 {
453         min_duration_active = active;
454 }
455
456 /** @return true if the maximum duration constraint is active */
457 bool
458 TimeAxisViewItem::get_min_duration_active() const
459 {
460         return min_duration_active;
461 }
462
463 /**
464  * Set the name of this item.
465  *
466  * @param new_name the new name of this item
467  * @param src the identity of the object that initiated the change
468  */
469
470 void
471 TimeAxisViewItem::set_item_name(std::string new_name, void* src)
472 {
473         if (new_name != item_name) {
474                 std::string temp_name = item_name;
475                 item_name = new_name;
476                 NameChanged (item_name, temp_name, src); /* EMIT_SIGNAL */
477         }
478 }
479
480 /** @return the name of this item */
481 std::string
482 TimeAxisViewItem::get_item_name() const
483 {
484         return item_name;
485 }
486
487 /**
488  * Set selection status.
489  *
490  * @param yn true if this item is currently selected
491  */
492 void
493 TimeAxisViewItem::set_selected(bool yn)
494 {
495         if (_selected == yn) {
496                 return;
497         }
498
499         Selectable::set_selected (yn);
500         set_frame_color ();
501         set_name_text_color ();
502
503         if (_selected && frame) {
504                 if (!selection_frame) {
505                         selection_frame = new ArdourCanvas::Rectangle (group);
506                         selection_frame->set_fill (false);
507                         selection_frame->set_outline_color (UIConfiguration::instance().color ("selected time axis frame"));
508                         selection_frame->set_ignore_events (true);
509                 }
510                 selection_frame->set (frame->get().shrink (1.0));
511                 selection_frame->show ();
512         } else {
513                 if (selection_frame) {
514                         selection_frame->hide ();
515                 }
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         name_text_width = pixel_width (new_name, NAME_FONT) + 2;
541         name_text->set (new_name);
542         manage_name_text ();
543         manage_name_highlight ();
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         manage_name_highlight ();
557
558         if (visibility & ShowNameText) {
559                 if (UIConfiguration::instance().get_show_name_highlight()) {
560                         name_text->set_y_position (height - NAME_Y_OFFSET);
561                 } else {
562                         name_text->set_y_position (NAME_Y_OFFSET);
563                 }
564         }
565
566         if (frame) {
567
568                 frame->set_y0 (0.0);
569                 frame->set_y1 (height);
570
571                 if (frame_handle_start) {
572                         frame_handle_start->set_y1 (height);
573                         frame_handle_end->set_y1 (height);
574                 }
575
576                 if (selection_frame) {
577                         selection_frame->set (frame->get().shrink (1.0));
578                 }
579         }
580 }
581
582 void
583 TimeAxisViewItem::manage_name_highlight ()
584 {
585         if (!name_highlight) {
586                 return;
587         }
588
589         if (_height < NAME_HIGHLIGHT_THRESH) {
590                 high_enough_for_name = false;
591         } else {
592                 high_enough_for_name = true;
593         }
594
595         if (_width < 2.0) {
596                 wide_enough_for_name = false;
597         } else {
598                 wide_enough_for_name = true;
599         }
600
601         if (name_highlight && wide_enough_for_name && high_enough_for_name) {
602
603                 name_highlight->show();
604                 // name_highlight->set_x_position (1.0);
605                 name_highlight->set (ArdourCanvas::Rect (0.0, (double) _height - NAME_HIGHLIGHT_SIZE,  _width - 2.0, _height));
606
607         } else {
608                 name_highlight->hide();
609         }
610
611         manage_name_text ();
612 }
613
614 void
615 TimeAxisViewItem::set_color (uint32_t base_color)
616 {
617         fill_color = base_color;
618         set_colors ();
619 }
620
621 ArdourCanvas::Item*
622 TimeAxisViewItem::get_canvas_frame()
623 {
624         return frame;
625 }
626
627 ArdourCanvas::Item*
628 TimeAxisViewItem::get_canvas_group()
629 {
630         return group;
631 }
632
633 ArdourCanvas::Item*
634 TimeAxisViewItem::get_name_highlight()
635 {
636         return name_highlight;
637 }
638
639 /**
640  * Convenience method to set the various canvas item colors
641  */
642 void
643 TimeAxisViewItem::set_colors()
644 {
645         set_frame_color ();
646
647         if (name_highlight) {
648                 name_highlight->set_fill_color (fill_color);
649         }
650
651         set_name_text_color ();
652         set_trim_handle_colors();
653 }
654
655 void
656 TimeAxisViewItem::set_name_text_color ()
657 {
658         if (!name_text) {
659                 return;
660         }
661
662
663         uint32_t f;
664
665         if (UIConfiguration::instance().get_show_name_highlight()) {
666                 /* name text will always be on top of name highlight, which
667                    will always use our fill color.
668                 */
669                 f = fill_color;
670         } else {
671                 /* name text will be on top of the item, whose color
672                    may vary depending on various conditions.
673                 */
674                 f = get_fill_color ();
675         }
676
677         name_text->set_color (contrasting_text_color (f));
678 }
679
680 Gtkmm2ext::Color
681 TimeAxisViewItem::get_fill_color () const
682 {
683         const std::string mod_name = (_dragging ? "dragging region" : fill_color_name);
684
685         if (_selected) {
686                 return UIConfiguration::instance().color ("selected region base");
687         } else if (_recregion) {
688                 return UIConfiguration::instance().color ("recording rect");
689         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
690                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
691                 return UIConfiguration::instance().color_mod (fill_color_name, mod_name);
692         }
693         return UIConfiguration::instance().color_mod (fill_color, mod_name);
694 }
695
696 /**
697  * Sets the frame color depending on whether this item is selected
698  */
699 void
700 TimeAxisViewItem::set_frame_color()
701 {
702         if (!frame) {
703                 return;
704         }
705
706         frame->set_fill_color (get_fill_color());
707         set_frame_gradient ();
708
709         if (!_recregion) {
710                 frame->set_outline_color (UIConfiguration::instance().color ("time axis frame"));
711         }
712 }
713
714 void
715 TimeAxisViewItem::set_frame_gradient ()
716 {
717         if (UIConfiguration::instance().get_timeline_item_gradient_depth() == 0.0) {
718                 frame->set_gradient (ArdourCanvas::Fill::StopList (), 0);
719                 return;
720         }
721
722         ArdourCanvas::Fill::StopList stops;
723         double r, g, b, a;
724         double h, s, v;
725         Color f (get_fill_color());
726
727         /* need to get alpha value */
728         color_to_rgba (f, r, g, b, a);
729
730         stops.push_back (std::make_pair (0.0, f));
731
732         /* now a darker version */
733
734         color_to_hsv (f, h, s, v);
735
736         v = min (1.0, v * (1.0 - UIConfiguration::instance().get_timeline_item_gradient_depth()));
737
738         Color darker = hsva_to_color (h, s, v, a);
739         stops.push_back (std::make_pair (1.0, darker));
740
741         frame->set_gradient (stops, true);
742 }
743
744 /**
745  * Set the colors of the start and end trim handle depending on object state
746  */
747 void
748 TimeAxisViewItem::set_trim_handle_colors()
749 {
750 #if 1
751         /* Leave them transparent for now */
752         if (frame_handle_start) {
753                 frame_handle_start->set_fill_color (0x00000000);
754                 frame_handle_end->set_fill_color (0x00000000);
755         }
756 #else
757         if (frame_handle_start) {
758                 if (position_locked) {
759                         frame_handle_start->set_fill_color (UIConfiguration::instance().get_TrimHandleLocked());
760                         frame_handle_end->set_fill_color (UIConfiguration::instance().get_TrimHandleLocked());
761                 } else {
762                         frame_handle_start->set_fill_color (UIConfiguration::instance().get_TrimHandle());
763                         frame_handle_end->set_fill_color (UIConfiguration::instance().get_TrimHandle());
764                 }
765         }
766 #endif
767 }
768
769 bool
770 TimeAxisViewItem::frame_handle_crossing (GdkEvent* ev, ArdourCanvas::Rectangle* item)
771 {
772         switch (ev->type) {
773         case GDK_LEAVE_NOTIFY:
774                 /* always hide the handle whenever we leave, no matter what mode */
775                 item->set_fill (false);
776                 break;
777         case GDK_ENTER_NOTIFY:
778                 if (trackview.editor().effective_mouse_mode() == Editing::MouseObject) {
779                         /* Never set this to be visible in other modes.  Note, however,
780                            that we do need to undo visibility (LEAVE_NOTIFY case above) no
781                            matter what the mode is. */
782                         item->set_fill (true);
783                 }
784                 break;
785         default:
786                 break;
787         }
788         return false;
789 }
790
791 /** @return the samples per pixel */
792 double
793 TimeAxisViewItem::get_samples_per_pixel () const
794 {
795         return samples_per_pixel;
796 }
797
798 /** Set the samples per pixel of this item.
799  *  This item is used to determine the relative visual size and position of this item
800  *  based upon its duration and start value.
801  *
802  *  @param fpp the new samples per pixel
803  */
804 void
805 TimeAxisViewItem::set_samples_per_pixel (double fpp)
806 {
807         samples_per_pixel = fpp;
808         set_position (this->get_position(), this);
809
810         double end_pixel = trackview.editor().sample_to_pixel (sample_position + get_duration());
811         double first_pixel = trackview.editor().sample_to_pixel (sample_position);
812
813         reset_width_dependent_items (end_pixel - first_pixel);
814 }
815
816 void
817 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
818 {
819         _width = pixel_width;
820
821         manage_name_highlight ();
822         manage_name_text ();
823
824         if (pixel_width < 2.0) {
825
826                 if (frame) {
827                         frame->set_outline (false);
828                         frame->set_x1 (std::max(1.0, pixel_width));
829                 }
830
831                 if (frame_handle_start) {
832                         frame_handle_start->hide();
833                         frame_handle_end->hide();
834                 }
835
836         } else {
837                 if (frame) {
838                         frame->set_outline (true);
839                         /* Note: x0 is always zero - the position is defined by
840                          * the position of the group, not the frame.
841                          */
842                         frame->set_x1 (pixel_width);
843
844                         if (selection_frame) {
845                                 selection_frame->set (frame->get().shrink (1.0));
846                         }
847                 }
848
849                 if (frame_handle_start) {
850                         if (pixel_width < (3 * TimeAxisViewItem::GRAB_HANDLE_WIDTH)) {
851                                 /*
852                                  * there's less than GRAB_HANDLE_WIDTH of the region between
853                                  * the right-hand end of frame_handle_start and the left-hand
854                                  * end of frame_handle_end, so disable the handles
855                                  */
856
857                                 frame_handle_start->hide();
858                                 frame_handle_end->hide();
859                         } else {
860                                 frame_handle_start->show();
861                                 frame_handle_end->set_x0 (pixel_width - (TimeAxisViewItem::GRAB_HANDLE_WIDTH));
862                                 frame_handle_end->set_x1 (pixel_width);
863                                 frame_handle_end->show();
864
865                                 frame_handle_start->raise_to_top ();
866                                 frame_handle_end->raise_to_top ();
867                         }
868                 }
869         }
870 }
871
872 void
873 TimeAxisViewItem::manage_name_text ()
874 {
875         int visible_name_width;
876
877         if (!name_text) {
878                 return;
879         }
880
881         if (!(visibility & ShowNameText) || (!wide_enough_for_name || !high_enough_for_name)) {
882                 name_text->hide ();
883                 return;
884         }
885
886         if (name_text->text().empty()) {
887                 name_text->hide ();
888         }
889
890         visible_name_width = name_text_width;
891
892         if (visible_name_width > _width - NAME_X_OFFSET) {
893                 visible_name_width = _width - NAME_X_OFFSET;
894         }
895
896         if (visible_name_width < 1) {
897                 name_text->hide ();
898         } else {
899                 name_text->clamp_width (visible_name_width);
900                 name_text->show ();
901         }
902 }
903
904 /**
905  * Callback used to remove this time axis item during the gtk idle loop.
906  * This is used to avoid deleting the obejct while inside the remove_this_item
907  * method.
908  *
909  * @param item the TimeAxisViewItem to remove.
910  * @param src the identity of the object that initiated the change.
911  */
912 gint
913 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
914 {
915         item->ItemRemoved (item->get_item_name(), src); /* EMIT_SIGNAL */
916         delete item;
917         item = 0;
918         return false;
919 }
920
921 void
922 TimeAxisViewItem::set_y (double y)
923 {
924         group->set_y_position (y);
925 }
926
927 void
928 TimeAxisViewItem::parameter_changed (string p)
929 {
930         if (p == "color-regions-using-track-color") {
931                 set_colors ();
932         } else if (p == "timeline-item-gradient-depth") {
933                 set_frame_gradient ();
934         }
935 }
936
937 void
938 TimeAxisViewItem::drag_start ()
939 {
940         _dragging = true;
941         set_frame_color ();
942 }
943
944 void
945 TimeAxisViewItem::drag_end ()
946 {
947         _dragging = false;
948         set_frame_color ();
949 }