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