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