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