99d5cec4d9ff0a664bfc1238e223f667296e30d0
[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     $Id$
19 */
20
21 #include <pbd/error.h>
22
23 #include <ardour/types.h>
24 #include <ardour/ardour.h>
25
26 #include "public_editor.h"
27 #include "time_axis_view_item.h"
28 #include "time_axis_view.h"
29 #include "canvas-simplerect.h"
30 #include "canvas-imageframe.h"
31 #include "utils.h"
32 #include "rgb_macros.h"
33
34 #include "i18n.h"
35
36 using namespace std;
37 using namespace Editing;
38
39 //------------------------------------------------------------------------------
40 /** Initialize static memeber data */
41 std::string TimeAxisViewItem::NAME_FONT;
42 const double TimeAxisViewItem::NAME_X_OFFSET = 15.0;
43 const double TimeAxisViewItem::NAME_Y_OFFSET = 15.0 ;           /* XXX depends a lot on the font size, sigh. */
44 const double TimeAxisViewItem::NAME_HIGHLIGHT_SIZE = 15.0 ;     /* ditto */
45 const double TimeAxisViewItem::NAME_HIGHLIGHT_THRESH = 32.0 ;     /* ditto */
46 const double TimeAxisViewItem::GRAB_HANDLE_LENGTH = 6 ;
47
48
49 //---------------------------------------------------------------------------------------//
50 // Constructor / Desctructor
51
52 /**
53  * Constructs a new TimeAxisViewItem.
54  *
55  * @param it_name the unique name/Id of this item
56  * @param parant the parent canvas group
57  * @param tv the TimeAxisView we are going to be added to
58  * @param spu samples per unit
59  * @param base_color
60  * @param start the start point of this item
61  * @param duration the duration of this item
62  */
63 TimeAxisViewItem::TimeAxisViewItem(std::string it_name, GtkCanvasGroup* parent, TimeAxisView& tv, double spu, GdkColor& base_color, 
64                                    jack_nframes_t start, jack_nframes_t duration,
65                                    Visibility visibility)
66         : trackview (tv)
67 {
68         if (NAME_FONT.empty()) {
69                 NAME_FONT = get_font_for_style (N_("TimeAxisViewItemName"));
70         }
71
72         item_name = it_name ;
73         samples_per_unit = spu ;
74         should_show_selection = true;
75         frame_position = start ;
76         item_duration = duration ;
77         name_connected = false;
78         fill_opacity = 50;
79         position_locked = false ;
80         max_item_duration = ARDOUR::max_frames;
81         min_item_duration = 0 ;
82         show_vestigial = true;
83
84         if (duration == 0) {
85                 warning << "Time Axis Item Duration == 0" << endl ;
86         }
87
88         group = gtk_canvas_item_new(GTK_CANVAS_GROUP(parent),gtk_canvas_group_get_type(),NULL) ;
89
90         vestigial_frame = gtk_canvas_item_new(GTK_CANVAS_GROUP(group),
91                                               gtk_canvas_simplerect_get_type(),
92                                               "x1", (double) 0.0,
93                                               "y1", (double) 1.0,
94                                               "x2", 2.0,
95                                               "y2", (double) trackview.height,
96                                               "outline_color_rgba", color_map[cVestigialFrameOutline],
97                                               "fill_color_rgba", color_map[cVestigialFrameFill],
98                                               NULL);
99         gtk_canvas_item_hide (vestigial_frame);
100
101         if (visibility & ShowFrame) {
102                 frame = gtk_canvas_item_new(GTK_CANVAS_GROUP(group),
103                                             gtk_canvas_simplerect_get_type(),
104                                             "x1", (double) 0.0,
105                                             "y1", (double) 1.0,
106                                             "x2", (double) trackview.editor.frame_to_pixel(duration),
107                                             "y2", (double) trackview.height,
108                                             "outline_color_rgba", color_map[cTimeAxisFrameOutline],
109                                             "fill_color_rgba", color_map[cTimeAxisFrameFill],
110                                             NULL);
111         } else {
112                 frame = 0;
113         }
114
115         if (visibility & ShowNameHighlight) {
116                 name_highlight = gtk_canvas_item_new(GTK_CANVAS_GROUP(group),
117                                                      gtk_canvas_simplerect_get_type(),
118                                                      "x1", (double) 1.0,
119                                                      "x2", (double) (trackview.editor.frame_to_pixel(item_duration)) - 1,
120                                                      "y1", (double) (trackview.height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE),
121                                                      "y2", (double) (trackview.height - 1),
122                                                      "outline_color_rgba", color_map[cNameHighlightFill],
123                                                      "fill_color_rgba", color_map[cNameHighlightOutline],
124                                                      NULL) ;
125                 gtk_object_set_data(GTK_OBJECT(name_highlight), "timeaxisviewitem", this) ;
126         } else {
127                 name_highlight = 0;
128         }
129
130         if (visibility & ShowNameText) {
131                 name_text = gtk_canvas_item_new(GTK_CANVAS_GROUP(group),
132                                                 gtk_canvas_text_get_type(),
133                                                 "x", (double) TimeAxisViewItem::NAME_X_OFFSET,
134                                                 "y", (double) trackview.height + 1.0 - TimeAxisViewItem::NAME_Y_OFFSET,
135                                                 "font", NAME_FONT.c_str(),
136                                                 "anchor", GTK_ANCHOR_NW,
137                                                 NULL) ;
138                 gtk_object_set_data(GTK_OBJECT(name_text), "timeaxisviewitem", this) ;
139                 
140         } else {
141                 name_text = 0;
142         }
143
144         /* create our grab handles used for trimming/duration etc */
145
146         if (visibility & ShowHandles) {
147                 frame_handle_start = gtk_canvas_item_new(GTK_CANVAS_GROUP(group),
148                                                          gtk_canvas_simplerect_get_type(),
149                                                          "x1", (double) 0.0,
150                                                          "x2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH,
151                                                          "y1", (double) 1.0,
152                                                          "y2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH+1,
153                                                          "outline_color_rgba", color_map[cFrameHandleStartOutline],
154                                                          "fill_color_rgba", color_map[cFrameHandleStartFill],
155                                                          NULL) ;
156                 
157                 frame_handle_end = gtk_canvas_item_new(GTK_CANVAS_GROUP(group),
158                                                        gtk_canvas_simplerect_get_type(),
159                                                        "x1", (double) (trackview.editor.frame_to_pixel(get_duration())) - (TimeAxisViewItem::GRAB_HANDLE_LENGTH),
160                                                        "x2", (double) trackview.editor.frame_to_pixel(get_duration()),
161                                                        "y1", (double) 1,
162                                                        "y2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH + 1,
163                                                        "outline_color_rgba", color_map[cFrameHandleEndOutline],
164                                                        "fill_color_rgba", color_map[cFrameHandleEndFill],
165                                                        NULL) ;
166         } else {
167                 frame_handle_start = 0;
168                 frame_handle_end = 0;
169         }
170
171         set_color (base_color) ;
172
173         set_duration (item_duration, this) ;
174         set_position (start, this) ;
175 }
176
177
178 /**
179  * Destructor
180  */
181 TimeAxisViewItem::~TimeAxisViewItem()
182 {
183         gtk_object_destroy (GTK_OBJECT(group));
184 }
185
186
187 //---------------------------------------------------------------------------------------//
188 // Position and duration Accessors/Mutators
189
190 /**
191  * Set the position of this item upon the timeline to the specified value
192  *
193  * @param pos the new position
194  * @param src the identity of the object that initiated the change
195  * @return true if the position change was a success, false otherwise
196  */
197 bool
198 TimeAxisViewItem::set_position(jack_nframes_t pos, void* src, double* delta)
199 {
200         if (position_locked) {
201                 return false;
202         }
203
204         frame_position = pos;
205         
206         /*  This sucks. The GtkCanvas version I am using
207             doesn't correctly implement gtk_canvas_group_set_arg(),
208             so that simply setting the "x" arg of the group
209             fails to move the group. Instead, we have to
210             use gtk_canvas_item_move(), which does the right
211             thing. I see that in GNOME CVS, the current (Sept 2001)
212             version of GNOME Canvas rectifies this issue cleanly.
213         */
214         
215         GtkArg args[1] ;
216         double old_unit_pos ;
217         double new_unit_pos = pos / samples_per_unit ;
218
219         args[0].name = "x" ;
220         gtk_object_getv (GTK_OBJECT(group), 1, args) ;
221         old_unit_pos = GTK_VALUE_DOUBLE (args[0]) ;
222
223         if (new_unit_pos != old_unit_pos) {
224                 gtk_canvas_item_move (group, new_unit_pos - old_unit_pos, 0.0) ;
225         }
226
227         if (delta) {
228                 (*delta) = new_unit_pos - old_unit_pos;
229         }
230         
231          PositionChanged (frame_position, src) ; /* EMIT_SIGNAL */
232
233         return true;
234 }
235
236 /**
237  * Return the position of this item upon the timeline
238  *
239  * @return the position of this item
240  */
241 jack_nframes_t
242 TimeAxisViewItem::get_position() const
243 {
244         return frame_position;
245 }
246
247 /**
248  * Sets the duration of this item
249  *
250  * @param dur the new duration of this item
251  * @param src the identity of the object that initiated the change
252  * @return true if the duration change was succesful, false otherwise
253  */
254 bool
255 TimeAxisViewItem::set_duration (jack_nframes_t dur, void* src)
256 {
257         if ((dur > max_item_duration) || (dur < min_item_duration)) {
258                 warning << compose (_("new duration %1 frames is out of bounds for %2"), get_item_name(), dur)
259                         << endmsg;
260                 return false;
261         }
262
263         if (dur == 0) {
264                 gtk_canvas_item_hide (group);
265         }
266
267         item_duration = dur;
268         
269         double pixel_width = trackview.editor.frame_to_pixel (dur);
270
271         reset_width_dependent_items (pixel_width);
272         
273          DurationChanged (dur, src) ; /* EMIT_SIGNAL */
274         return true;
275 }
276
277 /**
278  * Returns the duration of this item
279  *
280  */
281 jack_nframes_t
282 TimeAxisViewItem::get_duration() const
283 {
284         return (item_duration);
285 }
286
287 /**
288  * Sets the maximum duration that this item make have.
289  *
290  * @param dur the new maximum duration
291  * @param src the identity of the object that initiated the change
292  */
293 void
294 TimeAxisViewItem::set_max_duration(jack_nframes_t dur, void* src)
295 {
296         max_item_duration = dur ;
297          MaxDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
298 }
299                 
300 /**
301  * Returns the maxmimum duration that this item may be set to
302  *
303  * @return the maximum duration that this item may be set to
304  */
305 jack_nframes_t
306 TimeAxisViewItem::get_max_duration() const
307 {
308         return(max_item_duration) ;
309 }
310
311 /**
312  * Sets the minimu duration that this item may be set to
313  *
314  * @param the minimum duration that this item may be set to
315  * @param src the identity of the object that initiated the change
316  */
317 void
318 TimeAxisViewItem::set_min_duration(jack_nframes_t dur, void* src)
319 {
320         min_item_duration = dur ;
321          MinDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
322 }
323                 
324 /**
325  * Returns the minimum duration that this item mey be set to
326  *
327  * @return the nimum duration that this item mey be set to
328  */
329 jack_nframes_t
330 TimeAxisViewItem::get_min_duration() const
331 {
332         return(min_item_duration) ;
333 }
334
335 /**
336  * Sets whether the position of this Item is locked to its current position
337  * Locked items cannot be moved until the item is unlocked again.
338  *
339  * @param yn set to true to lock this item to its current position
340  * @param src the identity of the object that initiated the change
341  */
342 void
343 TimeAxisViewItem::set_position_locked(bool yn, void* src)
344 {
345         position_locked = yn ;
346         set_trim_handle_colors() ;
347         PositionLockChanged (position_locked, src); /* EMIT_SIGNAL */
348 }
349
350 /**
351  * Returns whether this item is locked to its current position
352  *
353  * @return true if this item is locked to its current posotion
354  *         false otherwise
355  */
356 bool
357 TimeAxisViewItem::get_position_locked() const
358 {
359         return (position_locked);
360 }
361
362 /**
363  * Sets whether the Maximum Duration constraint is active and should be enforced
364  *
365  * @param active set true to enforce the max duration constraint
366  * @param src the identity of the object that initiated the change
367  */
368 void
369 TimeAxisViewItem::set_max_duration_active(bool active, void* src)
370 {
371         max_duration_active = active ;
372 }
373                 
374 /**
375  * Returns whether the Maximum Duration constraint is active and should be enforced
376  *
377  * @return true if the maximum duration constraint is active, false otherwise
378  */
379 bool
380 TimeAxisViewItem::get_max_duration_active() const
381 {
382         return(max_duration_active) ;
383 }
384                 
385 /**
386  * Sets whether the Minimum Duration constraint is active and should be enforced
387  *
388  * @param active set true to enforce the min duration constraint
389  * @param src the identity of the object that initiated the change
390  */
391 void
392 TimeAxisViewItem::set_min_duration_active(bool active, void* src)
393 {
394         min_duration_active = active ;
395 }
396                 
397 /**
398  * Returns whether the Maximum Duration constraint is active and should be enforced
399  *
400  * @return true if the maximum duration constraint is active, false otherwise
401  */
402 bool
403 TimeAxisViewItem::get_min_duration_active() const
404 {
405         return(min_duration_active) ;
406 }
407
408 //---------------------------------------------------------------------------------------//
409 // Name/Id Accessors/Mutators
410
411 /**
412  * Set the name/Id of this item.
413  *
414  * @param new_name the new name of this item
415  * @param src the identity of the object that initiated the change
416  */
417 void
418 TimeAxisViewItem::set_item_name(std::string new_name, void* src)
419 {
420         if (new_name != item_name) {
421                 std::string temp_name = item_name ;
422                 item_name = new_name ;
423                 NameChanged (item_name, temp_name, src) ; /* EMIT_SIGNAL */
424         }
425 }
426
427 /**
428  * Returns the name/id of this item
429  *
430  * @return the name/id of this item
431  */
432 std::string
433 TimeAxisViewItem::get_item_name() const
434 {
435         return(item_name) ;
436 }
437
438 //---------------------------------------------------------------------------------------//
439 // Selection Methods
440
441 /**
442  * Set to true to indicate that this item is currently selected
443  *
444  * @param yn true if this item is currently selected
445  * @param src the identity of the object that initiated the change
446  */
447 void
448 TimeAxisViewItem::set_selected(bool yn, void* src)
449 {
450         if (_selected != yn) {
451                 _selected = yn ;
452                 set_frame_color ();
453                 Selected (_selected) ; /* EMIT_SIGNAL */
454         }
455 }
456
457 /**
458  * Returns whether this item is currently selected.
459  *
460  * @return true if this item is currently selected, false otherwise
461  */
462 bool
463 TimeAxisViewItem::get_selected() const
464 {
465         return (_selected) ;
466 }
467
468 void 
469 TimeAxisViewItem::set_should_show_selection (bool yn)
470 {
471         if (should_show_selection != yn) {
472                 should_show_selection = yn;
473                 set_frame_color ();
474         }
475 }
476
477 //---------------------------------------------------------------------------------------//
478 // Parent Componenet Methods
479
480 /**
481  * Returns the TimeAxisView that this item is upon
482  *
483  * @return the timeAxisView that this item is placed upon
484  */
485 TimeAxisView&
486 TimeAxisViewItem::get_time_axis_view()
487 {
488         return trackview;
489 }               
490 //---------------------------------------------------------------------------------------//
491 // ui methods & data
492
493 /**
494  * Sets the displayed item text
495  * This item is the visual text name displayed on the canvas item, this can be different to the name of the item
496  *
497  * @param new_name the new name text to display
498  */
499 void
500 TimeAxisViewItem::set_name_text(std::string new_name)
501 {
502         if (name_text) {
503                 gtk_canvas_item_set (name_text, "text", new_name.c_str(), NULL);
504         }
505 }
506
507 /**
508  * Set the height of this item
509  *
510  * @param h the new height
511  */             
512 void
513 TimeAxisViewItem::set_height(double height)
514 {
515         if (name_highlight) {
516                 if (height < NAME_HIGHLIGHT_THRESH) {
517                         gtk_canvas_item_hide (name_highlight);
518                         gtk_canvas_item_hide (name_text);
519                 } else {
520                         gtk_canvas_item_show (name_highlight);
521                         gtk_canvas_item_show (name_text);
522                 }
523
524                 if (height > NAME_HIGHLIGHT_SIZE) {
525                         gtk_canvas_item_set (name_highlight, 
526                                              "y1", (double) height+1 - NAME_HIGHLIGHT_SIZE,
527                                              "y2", (double) height,
528                                              NULL);
529                 }
530                 else {
531                         /* it gets hidden now anyway */
532                         gtk_canvas_item_set (name_highlight, 
533                                              "y1", (double) 1.0,
534                                              "y2", (double) height,
535                                              NULL);
536                 }
537         }
538
539         if (name_text) {
540                 gtk_canvas_item_set (name_text, "y", height+1 - NAME_Y_OFFSET, NULL);
541                 if (height < NAME_HIGHLIGHT_THRESH) {
542                         gtk_canvas_item_set(name_text, "fill_color_rgba",  fill_color, NULL) ;
543                 }
544                 else {
545                         gtk_canvas_item_set(name_text, "fill_color_rgba", label_color, NULL) ;
546                 }
547         }
548
549         if (frame) {
550                 gtk_canvas_item_set (frame, "y2", height+1, NULL) ;
551         }
552
553         gtk_canvas_item_set (vestigial_frame, "y2", height+1, NULL) ;
554 }
555
556 /**
557  * 
558  */
559 void
560 TimeAxisViewItem::set_color(GdkColor& base_color)
561 {
562         compute_colors (base_color);
563         set_colors ();
564 }
565
566 /**
567  * 
568  */
569 GtkCanvasItem*
570 TimeAxisViewItem::get_canvas_frame()
571 {
572         return(frame) ;
573 }
574
575 /**
576  * 
577  */
578 GtkCanvasItem*
579 TimeAxisViewItem::get_canvas_group()
580 {
581         return(group) ;
582 }
583
584 /**
585  * 
586  */
587 GtkCanvasItem*
588 TimeAxisViewItem::get_name_highlight()
589 {
590         return(name_highlight) ;
591 }
592
593 /**
594  * 
595  */
596 GtkCanvasItem*
597 TimeAxisViewItem::get_name_text()
598 {
599         return(name_text) ;
600 }
601
602 /**
603  * Calculates some contrasting color for displaying various parts of this item, based upon the base color
604  *
605  * @param color the base color of the item
606  */
607 void
608 TimeAxisViewItem::compute_colors(GdkColor& base_color)
609 {
610         unsigned char radius ;
611         char minor_shift ;
612         
613         unsigned char r,g,b ;
614
615         /* FILL: this is simple */
616         r = base_color.red/256 ;
617         g = base_color.green/256 ;
618         b = base_color.blue/256 ;
619         fill_color = RGBA_TO_UINT(r,g,b,255) ;
620
621         /*  for minor colors:
622                 if the overall saturation is strong, make the minor colors light.
623                 if its weak, make them dark.
624   
625                 we do this by moving an equal distance to the other side of the
626                 central circle in the color wheel from where we started.
627         */
628
629         radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f)) ;
630         minor_shift = 125 - radius ;
631
632         /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
633
634         r = base_color.red/256;
635         g = base_color.green/256;
636         b = base_color.blue/256;
637   
638         if (r > b)
639         {
640                 if (r > g)
641                 {
642                         /* red sector => green */
643                         swap (r,g);
644                 }
645                 else
646                 {
647                         /* green sector => blue */
648                         swap (g,b);
649                 } 
650         }
651         else
652         {
653                 if (b > g)
654                 {
655                         /* blue sector => red */
656                         swap (b,r);
657                 }
658                 else
659                 {
660                         /* green sector => blue */
661                         swap (g,b);
662                 }
663         }
664
665         r += minor_shift;
666         b += minor_shift;
667         g += minor_shift;
668   
669         label_color = RGBA_TO_UINT(r,g,b,255);
670         r = (base_color.red/256)   + 127 ;
671         g = (base_color.green/256) + 127 ;
672         b = (base_color.blue/256)  + 127 ;
673   
674         label_color = RGBA_TO_UINT(r,g,b,255);
675
676         /* XXX can we do better than this ? */
677         /* We're trying ;) */
678         /* NUKECOLORS */
679         
680         //frame_color_r = 192;
681         //frame_color_g = 192;
682         //frame_color_b = 194;
683         
684         //selected_frame_color_r = 182;
685         //selected_frame_color_g = 145;
686         //selected_frame_color_b = 168;
687         
688         //handle_color_r = 25 ;
689         //handle_color_g = 0 ;
690         //handle_color_b = 255 ;
691         //lock_handle_color_r = 235 ;
692         //lock_handle_color_g = 16;
693         //lock_handle_color_b = 16;
694 }
695
696 /**
697  * Convenience method to set the various canvas item colors
698  */
699 void
700 TimeAxisViewItem::set_colors()
701 {
702         set_frame_color() ;
703         if (name_text) {
704                 double height = NAME_HIGHLIGHT_THRESH;
705
706                 if (frame) {
707                         GtkArg args[1] ;
708                         args[0].name = "y2" ;
709                         gtk_object_getv (GTK_OBJECT(frame), 1, args);
710                         height = GTK_VALUE_DOUBLE (args[0]);
711                 }
712
713                 if (height < NAME_HIGHLIGHT_THRESH) {
714                         gtk_canvas_item_set(name_text, "fill_color_rgba",  fill_color, NULL) ;
715                 }
716                 else {
717                         gtk_canvas_item_set(name_text, "fill_color_rgba", label_color, NULL) ;
718                 }
719         }
720
721         if (name_highlight) {
722                 gtk_canvas_item_set(name_highlight, "fill_color_rgba", fill_color, NULL) ;
723                 gtk_canvas_item_set(name_highlight, "outline_color_rgba", fill_color, NULL) ;
724         }
725         set_trim_handle_colors() ;
726 }
727
728 /**
729  * Sets the frame color depending on whether this item is selected
730  */
731 void
732 TimeAxisViewItem::set_frame_color()
733 {
734         if (frame) {
735                 uint32_t r,g,b,a;
736                 
737                 if (_selected && should_show_selection) {
738                         UINT_TO_RGBA(color_map[cSelectedFrameBase], &r, &g, &b, &a);
739                         gtk_canvas_item_set(frame, "fill_color_rgba", RGBA_TO_UINT(r, g, b, fill_opacity), NULL) ;
740                 } else {
741                         UINT_TO_RGBA(color_map[cFrameBase], &r, &g, &b, &a);
742                         gtk_canvas_item_set(frame, "fill_color_rgba", RGBA_TO_UINT(r, g, b, fill_opacity), NULL) ;
743                 }
744         }
745 }
746
747 /**
748  * Sets the colors of the start and end trim handle depending on object state
749  *
750  */
751 void
752 TimeAxisViewItem::set_trim_handle_colors()
753 {
754         if (frame_handle_start) {
755                 if (position_locked) {
756                         gtk_canvas_item_set(frame_handle_start, "fill_color_rgba", color_map[cTrimHandleLockedStart], NULL);
757                         gtk_canvas_item_set(frame_handle_end, "fill_color_rgba", color_map[cTrimHandleLockedEnd], NULL) ;
758                 } else {
759                         gtk_canvas_item_set(frame_handle_start, "fill_color_rgba", color_map[cTrimHandleStart], NULL) ;
760                         gtk_canvas_item_set(frame_handle_end, "fill_color_rgba", color_map[cTrimHandleEnd], NULL) ;
761                 }
762         }
763 }
764
765 double
766 TimeAxisViewItem::get_samples_per_unit()
767 {
768         return(samples_per_unit) ;
769 }
770
771 void
772 TimeAxisViewItem::set_samples_per_unit (double spu)
773 {
774         samples_per_unit = spu ;
775         set_position (this->get_position(), this);
776         reset_width_dependent_items ((double)get_duration() / samples_per_unit);
777 }
778
779 void
780 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
781 {
782         if (pixel_width < GRAB_HANDLE_LENGTH * 2) {
783
784                 if (frame_handle_start) {
785                         gtk_canvas_item_hide (frame_handle_start);
786                         gtk_canvas_item_hide (frame_handle_end);
787                 }
788
789         } if (pixel_width < 2.0) {
790
791                 if (show_vestigial) {
792                         gtk_canvas_item_show (vestigial_frame);
793                 }
794
795                 if (name_highlight) {
796                         gtk_canvas_item_hide (name_highlight);
797                         gtk_canvas_item_hide (name_text);
798                 }
799
800                 if (frame) {
801                         gtk_canvas_item_hide (frame);
802                 }
803
804                 if (frame_handle_start) {
805                         gtk_canvas_item_hide (frame_handle_start);
806                         gtk_canvas_item_hide (frame_handle_end);
807                 }
808                 
809         } else {
810                 gtk_canvas_item_hide (vestigial_frame);
811
812                 if (name_highlight) {
813
814                         GtkArg args[1] ;
815                         args[0].name = "y2" ;
816                         gtk_object_getv (GTK_OBJECT(name_highlight), 1, args);
817                         double height = GTK_VALUE_DOUBLE (args[0]);
818
819                         if (height < NAME_HIGHLIGHT_THRESH) {
820                                 gtk_canvas_item_hide (name_highlight);
821                                 gtk_canvas_item_hide (name_text);
822                         } else {
823                                 gtk_canvas_item_show (name_highlight);
824                                 gtk_canvas_item_show (name_text);
825                                 reset_name_width (pixel_width);
826                         }
827
828                         gtk_canvas_item_set (name_highlight, "x2", pixel_width - 1.0, NULL);
829                 }
830
831                 if (frame) {
832                         gtk_canvas_item_show (frame);
833                         gtk_canvas_item_set (frame, "x2", pixel_width, NULL);
834                 }
835
836                 if (frame_handle_start) {
837                         if (pixel_width < (2*TimeAxisViewItem::GRAB_HANDLE_LENGTH)) {
838                                 gtk_canvas_item_hide (frame_handle_start);
839                                 gtk_canvas_item_hide (frame_handle_end);
840                         }
841                         gtk_canvas_item_show (frame_handle_start);
842                         gtk_canvas_item_set(GTK_CANVAS_ITEM(frame_handle_end), "x1", pixel_width - (TimeAxisViewItem::GRAB_HANDLE_LENGTH ), NULL) ;
843                         gtk_canvas_item_show (frame_handle_end);
844                         gtk_canvas_item_set(GTK_CANVAS_ITEM(frame_handle_end), "x2", pixel_width, NULL) ;
845                 }
846         }
847 }
848
849 void
850 TimeAxisViewItem::reset_name_width (double pixel_width)
851 {
852         gint width;
853         gint lbearing;
854         gint rbearing;
855         gint ascent;
856         gint descent;
857         Gdk_Font font (NAME_FONT);
858
859         if (name_text == 0) {
860                 return;
861         }
862
863         int namelen = item_name.length();
864         char cstr[namelen+1];
865         strcpy (cstr, item_name.c_str());
866         
867         while (namelen) {
868                 
869                 gdk_string_extents (font,
870                                     cstr,
871                                     &lbearing,
872                                     &rbearing,
873                                     &width,
874                                     &ascent,
875                                     &descent);
876
877                 if (width < (pixel_width - NAME_X_OFFSET)) {
878                         break;
879                 }
880
881                 --namelen;
882                 cstr[namelen] = '\0';
883
884         }
885
886         if (namelen == 0) {
887                 
888                 gtk_canvas_item_hide (name_text);
889                 
890         } else {
891                 
892                 /* don't use name for event handling if it leaves no room
893                    for trimming to work.
894                 */
895                 
896                 if (pixel_width - width < (NAME_X_OFFSET * 2.0)) {
897                         if (name_connected) {
898                                 name_connected = false;
899                         }
900                 } else {
901                         if (!name_connected) {
902                                 name_connected = true;
903                         }
904                 }
905                 
906                 gtk_canvas_item_set (name_text, "text", cstr, NULL);
907                 gtk_canvas_item_show (name_text);
908         }
909 }
910
911
912 //---------------------------------------------------------------------------------------//
913 // Handle time axis removal
914
915 /**
916  * Handles the Removal of this time axis item
917  * This _needs_ to be called to alert others of the removal properly, ie where the source
918  * of the removal came from.
919  *
920  * XXX Although im not too happy about this method of doing things, I cant think of a cleaner method
921  *     just now to capture the source of the removal
922  *
923  * @param src the identity of the object that initiated the change
924  */
925 void
926 TimeAxisViewItem::remove_this_item(void* src)
927 {
928         /*
929            defer to idle loop, otherwise we'll delete this object
930            while we're still inside this function ...
931         */
932         Gtk::Main::idle.connect(bind(mem_fun(&TimeAxisViewItem::idle_remove_this_item), this, src));
933 }
934
935 /**
936  * Callback used to remove this time axis item during the gtk idle loop
937  * This is used to avoid deleting the obejct while inside the remove_this_item
938  * method
939  *
940  * @param item the ImageFrameTimeAxisGroup to remove
941  * @param src the identity of the object that initiated the change
942  */
943 gint
944 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
945 {
946          item->ItemRemoved(item->get_item_name(), src) ; /* EMIT_SIGNAL */
947         delete item ;
948         item = 0 ;
949         return(false) ;
950 }