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