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