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