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