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