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