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