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