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