2 Copyright (C) 2003 Paul Davis
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.
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.
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.
21 #include <pbd/error.h>
23 #include <ardour/types.h>
24 #include <ardour/ardour.h>
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"
32 #include "rgb_macros.h"
37 using namespace Editing;
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 ;
49 //---------------------------------------------------------------------------------------//
50 // Constructor / Desctructor
53 * Constructs a new TimeAxisViewItem.
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
60 * @param start the start point of this item
61 * @param duration the duration of this item
63 TimeAxisViewItem::TimeAxisViewItem(std::string it_name, GnomeCanvasGroup* parent, TimeAxisView& tv, double spu, GdkColor& base_color,
64 jack_nframes_t start, jack_nframes_t duration,
65 Visibility visibility)
68 if (NAME_FONT.empty()) {
69 NAME_FONT = get_font_for_style (N_("TimeAxisViewItemName"));
73 samples_per_unit = spu ;
74 should_show_selection = true;
75 frame_position = start ;
76 item_duration = duration ;
77 name_connected = false;
79 position_locked = false ;
80 max_item_duration = ARDOUR::max_frames;
81 min_item_duration = 0 ;
82 show_vestigial = true;
85 warning << "Time Axis Item Duration == 0" << endl ;
88 group = gnome_canvas_item_new(GNOME_CANVAS_GROUP(parent),gnome_canvas_group_get_type(),NULL) ;
90 vestigial_frame = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
91 gnome_canvas_simplerect_get_type(),
95 "y2", (double) trackview.height,
96 "outline_color_rgba", color_map[cVestigialFrameOutline],
97 "fill_color_rgba", color_map[cVestigialFrameFill],
99 gnome_canvas_item_hide (vestigial_frame);
101 if (visibility & ShowFrame) {
102 frame = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
103 gnome_canvas_simplerect_get_type(),
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],
115 if (visibility & ShowNameHighlight) {
116 name_highlight = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
117 gnome_canvas_simplerect_get_type(),
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],
125 gtk_object_set_data(GTK_OBJECT(name_highlight), "timeaxisviewitem", this) ;
130 if (visibility & ShowNameText) {
131 name_text = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
132 gnome_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,
138 gtk_object_set_data(GTK_OBJECT(name_text), "timeaxisviewitem", this) ;
144 /* create our grab handles used for trimming/duration etc */
146 if (visibility & ShowHandles) {
147 frame_handle_start = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
148 gnome_canvas_simplerect_get_type(),
150 "x2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH,
152 "y2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH+1,
153 "outline_color_rgba", color_map[cFrameHandleStartOutline],
154 "fill_color_rgba", color_map[cFrameHandleStartFill],
157 frame_handle_end = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
158 gnome_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()),
162 "y2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH + 1,
163 "outline_color_rgba", color_map[cFrameHandleEndOutline],
164 "fill_color_rgba", color_map[cFrameHandleEndFill],
167 frame_handle_start = 0;
168 frame_handle_end = 0;
171 set_color (base_color) ;
173 set_duration (item_duration, this) ;
174 set_position (start, this) ;
181 TimeAxisViewItem::~TimeAxisViewItem()
183 gtk_object_destroy (GTK_OBJECT(group));
187 //---------------------------------------------------------------------------------------//
188 // Position and duration Accessors/Mutators
191 * Set the position of this item upon the timeline to the specified value
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
198 TimeAxisViewItem::set_position(jack_nframes_t pos, void* src, double* delta)
200 if (position_locked) {
204 frame_position = pos;
206 /* This sucks. The GnomeCanvas version I am using
207 doesn't correctly implement gnome_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 gnome_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.
216 double old_unit_pos ;
217 double new_unit_pos = pos / samples_per_unit ;
220 gtk_object_getv (GTK_OBJECT(group), 1, args) ;
221 old_unit_pos = GTK_VALUE_DOUBLE (args[0]) ;
223 if (new_unit_pos != old_unit_pos) {
224 gnome_canvas_item_move (group, new_unit_pos - old_unit_pos, 0.0) ;
228 (*delta) = new_unit_pos - old_unit_pos;
231 PositionChanged (frame_position, src) ; /* EMIT_SIGNAL */
237 * Return the position of this item upon the timeline
239 * @return the position of this item
242 TimeAxisViewItem::get_position() const
244 return frame_position;
248 * Sets the duration of this item
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
255 TimeAxisViewItem::set_duration (jack_nframes_t dur, void* src)
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)
264 gnome_canvas_item_hide (group);
269 double pixel_width = trackview.editor.frame_to_pixel (dur);
271 reset_width_dependent_items (pixel_width);
273 DurationChanged (dur, src) ; /* EMIT_SIGNAL */
278 * Returns the duration of this item
282 TimeAxisViewItem::get_duration() const
284 return (item_duration);
288 * Sets the maximum duration that this item make have.
290 * @param dur the new maximum duration
291 * @param src the identity of the object that initiated the change
294 TimeAxisViewItem::set_max_duration(jack_nframes_t dur, void* src)
296 max_item_duration = dur ;
297 MaxDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
301 * Returns the maxmimum duration that this item may be set to
303 * @return the maximum duration that this item may be set to
306 TimeAxisViewItem::get_max_duration() const
308 return(max_item_duration) ;
312 * Sets the minimu duration that this item may be set to
314 * @param the minimum duration that this item may be set to
315 * @param src the identity of the object that initiated the change
318 TimeAxisViewItem::set_min_duration(jack_nframes_t dur, void* src)
320 min_item_duration = dur ;
321 MinDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
325 * Returns the minimum duration that this item mey be set to
327 * @return the nimum duration that this item mey be set to
330 TimeAxisViewItem::get_min_duration() const
332 return(min_item_duration) ;
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.
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
343 TimeAxisViewItem::set_position_locked(bool yn, void* src)
345 position_locked = yn ;
346 set_trim_handle_colors() ;
347 PositionLockChanged (position_locked, src); /* EMIT_SIGNAL */
351 * Returns whether this item is locked to its current position
353 * @return true if this item is locked to its current posotion
357 TimeAxisViewItem::get_position_locked() const
359 return (position_locked);
363 * Sets whether the Maximum Duration constraint is active and should be enforced
365 * @param active set true to enforce the max duration constraint
366 * @param src the identity of the object that initiated the change
369 TimeAxisViewItem::set_max_duration_active(bool active, void* src)
371 max_duration_active = active ;
375 * Returns whether the Maximum Duration constraint is active and should be enforced
377 * @return true if the maximum duration constraint is active, false otherwise
380 TimeAxisViewItem::get_max_duration_active() const
382 return(max_duration_active) ;
386 * Sets whether the Minimum Duration constraint is active and should be enforced
388 * @param active set true to enforce the min duration constraint
389 * @param src the identity of the object that initiated the change
392 TimeAxisViewItem::set_min_duration_active(bool active, void* src)
394 min_duration_active = active ;
398 * Returns whether the Maximum Duration constraint is active and should be enforced
400 * @return true if the maximum duration constraint is active, false otherwise
403 TimeAxisViewItem::get_min_duration_active() const
405 return(min_duration_active) ;
408 //---------------------------------------------------------------------------------------//
409 // Name/Id Accessors/Mutators
412 * Set the name/Id of this item.
414 * @param new_name the new name of this item
415 * @param src the identity of the object that initiated the change
418 TimeAxisViewItem::set_item_name(std::string new_name, void* src)
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 */
428 * Returns the name/id of this item
430 * @return the name/id of this item
433 TimeAxisViewItem::get_item_name() const
438 //---------------------------------------------------------------------------------------//
442 * Set to true to indicate that this item is currently selected
444 * @param yn true if this item is currently selected
445 * @param src the identity of the object that initiated the change
448 TimeAxisViewItem::set_selected(bool yn, void* src)
450 if (_selected != yn) {
453 Selected (_selected) ; /* EMIT_SIGNAL */
458 * Returns whether this item is currently selected.
460 * @return true if this item is currently selected, false otherwise
463 TimeAxisViewItem::get_selected() const
469 TimeAxisViewItem::set_should_show_selection (bool yn)
471 if (should_show_selection != yn) {
472 should_show_selection = yn;
477 //---------------------------------------------------------------------------------------//
478 // Parent Componenet Methods
481 * Returns the TimeAxisView that this item is upon
483 * @return the timeAxisView that this item is placed upon
486 TimeAxisViewItem::get_time_axis_view()
490 //---------------------------------------------------------------------------------------//
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
497 * @param new_name the new name text to display
500 TimeAxisViewItem::set_name_text(std::string new_name)
503 gnome_canvas_item_set (name_text, "text", new_name.c_str(), NULL);
508 * Set the height of this item
510 * @param h the new height
513 TimeAxisViewItem::set_height(double height)
515 if (name_highlight) {
516 if (height < NAME_HIGHLIGHT_THRESH) {
517 gnome_canvas_item_hide (name_highlight);
518 gnome_canvas_item_hide (name_text);
520 gnome_canvas_item_show (name_highlight);
521 gnome_canvas_item_show (name_text);
524 if (height > NAME_HIGHLIGHT_SIZE) {
525 gnome_canvas_item_set (name_highlight,
526 "y1", (double) height+1 - NAME_HIGHLIGHT_SIZE,
527 "y2", (double) height,
531 /* it gets hidden now anyway */
532 gnome_canvas_item_set (name_highlight,
534 "y2", (double) height,
540 gnome_canvas_item_set (name_text, "y", height+1 - NAME_Y_OFFSET, NULL);
541 if (height < NAME_HIGHLIGHT_THRESH) {
542 gnome_canvas_item_set(name_text, "fill_color_rgba", fill_color, NULL) ;
545 gnome_canvas_item_set(name_text, "fill_color_rgba", label_color, NULL) ;
550 gnome_canvas_item_set (frame, "y2", height+1, NULL) ;
553 gnome_canvas_item_set (vestigial_frame, "y2", height+1, NULL) ;
560 TimeAxisViewItem::set_color(GdkColor& base_color)
562 compute_colors (base_color);
570 TimeAxisViewItem::get_canvas_frame()
579 TimeAxisViewItem::get_canvas_group()
588 TimeAxisViewItem::get_name_highlight()
590 return(name_highlight) ;
597 TimeAxisViewItem::get_name_text()
603 * Calculates some contrasting color for displaying various parts of this item, based upon the base color
605 * @param color the base color of the item
608 TimeAxisViewItem::compute_colors(GdkColor& base_color)
610 unsigned char radius ;
613 unsigned char r,g,b ;
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) ;
622 if the overall saturation is strong, make the minor colors light.
623 if its weak, make them dark.
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.
629 radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f)) ;
630 minor_shift = 125 - radius ;
632 /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
634 r = base_color.red/256;
635 g = base_color.green/256;
636 b = base_color.blue/256;
642 /* red sector => green */
647 /* green sector => blue */
655 /* blue sector => red */
660 /* green sector => blue */
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 ;
674 label_color = RGBA_TO_UINT(r,g,b,255);
676 /* XXX can we do better than this ? */
677 /* We're trying ;) */
680 //frame_color_r = 192;
681 //frame_color_g = 192;
682 //frame_color_b = 194;
684 //selected_frame_color_r = 182;
685 //selected_frame_color_g = 145;
686 //selected_frame_color_b = 168;
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;
697 * Convenience method to set the various canvas item colors
700 TimeAxisViewItem::set_colors()
704 double height = NAME_HIGHLIGHT_THRESH;
708 args[0].name = "y2" ;
709 gtk_object_getv (GTK_OBJECT(frame), 1, args);
710 height = GTK_VALUE_DOUBLE (args[0]);
713 if (height < NAME_HIGHLIGHT_THRESH) {
714 gnome_canvas_item_set(name_text, "fill_color_rgba", fill_color, NULL) ;
717 gnome_canvas_item_set(name_text, "fill_color_rgba", label_color, NULL) ;
721 if (name_highlight) {
722 gnome_canvas_item_set(name_highlight, "fill_color_rgba", fill_color, NULL) ;
723 gnome_canvas_item_set(name_highlight, "outline_color_rgba", fill_color, NULL) ;
725 set_trim_handle_colors() ;
729 * Sets the frame color depending on whether this item is selected
732 TimeAxisViewItem::set_frame_color()
737 if (_selected && should_show_selection) {
738 UINT_TO_RGBA(color_map[cSelectedFrameBase], &r, &g, &b, &a);
739 gnome_canvas_item_set(frame, "fill_color_rgba", RGBA_TO_UINT(r, g, b, fill_opacity), NULL) ;
741 UINT_TO_RGBA(color_map[cFrameBase], &r, &g, &b, &a);
742 gnome_canvas_item_set(frame, "fill_color_rgba", RGBA_TO_UINT(r, g, b, fill_opacity), NULL) ;
748 * Sets the colors of the start and end trim handle depending on object state
752 TimeAxisViewItem::set_trim_handle_colors()
754 if (frame_handle_start) {
755 if (position_locked) {
756 gnome_canvas_item_set(frame_handle_start, "fill_color_rgba", color_map[cTrimHandleLockedStart], NULL);
757 gnome_canvas_item_set(frame_handle_end, "fill_color_rgba", color_map[cTrimHandleLockedEnd], NULL) ;
759 gnome_canvas_item_set(frame_handle_start, "fill_color_rgba", color_map[cTrimHandleStart], NULL) ;
760 gnome_canvas_item_set(frame_handle_end, "fill_color_rgba", color_map[cTrimHandleEnd], NULL) ;
766 TimeAxisViewItem::get_samples_per_unit()
768 return(samples_per_unit) ;
772 TimeAxisViewItem::set_samples_per_unit (double spu)
774 samples_per_unit = spu ;
775 set_position (this->get_position(), this);
776 reset_width_dependent_items ((double)get_duration() / samples_per_unit);
780 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
782 if (pixel_width < GRAB_HANDLE_LENGTH * 2) {
784 if (frame_handle_start) {
785 gnome_canvas_item_hide (frame_handle_start);
786 gnome_canvas_item_hide (frame_handle_end);
789 } if (pixel_width < 2.0) {
791 if (show_vestigial) {
792 gnome_canvas_item_show (vestigial_frame);
795 if (name_highlight) {
796 gnome_canvas_item_hide (name_highlight);
797 gnome_canvas_item_hide (name_text);
801 gnome_canvas_item_hide (frame);
804 if (frame_handle_start) {
805 gnome_canvas_item_hide (frame_handle_start);
806 gnome_canvas_item_hide (frame_handle_end);
810 gnome_canvas_item_hide (vestigial_frame);
812 if (name_highlight) {
815 args[0].name = "y2" ;
816 gtk_object_getv (GTK_OBJECT(name_highlight), 1, args);
817 double height = GTK_VALUE_DOUBLE (args[0]);
819 if (height < NAME_HIGHLIGHT_THRESH) {
820 gnome_canvas_item_hide (name_highlight);
821 gnome_canvas_item_hide (name_text);
823 gnome_canvas_item_show (name_highlight);
824 gnome_canvas_item_show (name_text);
825 reset_name_width (pixel_width);
828 gnome_canvas_item_set (name_highlight, "x2", pixel_width - 1.0, NULL);
832 gnome_canvas_item_show (frame);
833 gnome_canvas_item_set (frame, "x2", pixel_width, NULL);
836 if (frame_handle_start) {
837 if (pixel_width < (2*TimeAxisViewItem::GRAB_HANDLE_LENGTH)) {
838 gnome_canvas_item_hide (frame_handle_start);
839 gnome_canvas_item_hide (frame_handle_end);
841 gnome_canvas_item_show (frame_handle_start);
842 gnome_canvas_item_set(GNOME_CANVAS_ITEM(frame_handle_end), "x1", pixel_width - (TimeAxisViewItem::GRAB_HANDLE_LENGTH ), NULL) ;
843 gnome_canvas_item_show (frame_handle_end);
844 gnome_canvas_item_set(GNOME_CANVAS_ITEM(frame_handle_end), "x2", pixel_width, NULL) ;
850 TimeAxisViewItem::reset_name_width (double pixel_width)
857 Gdk_Font font (NAME_FONT);
859 if (name_text == 0) {
863 int namelen = item_name.length();
864 char cstr[namelen+1];
865 strcpy (cstr, item_name.c_str());
869 gdk_string_extents (font,
877 if (width < (pixel_width - NAME_X_OFFSET)) {
882 cstr[namelen] = '\0';
888 gnome_canvas_item_hide (name_text);
892 /* don't use name for event handling if it leaves no room
893 for trimming to work.
896 if (pixel_width - width < (NAME_X_OFFSET * 2.0)) {
897 if (name_connected) {
898 name_connected = false;
901 if (!name_connected) {
902 name_connected = true;
906 gnome_canvas_item_set (name_text, "text", cstr, NULL);
907 gnome_canvas_item_show (name_text);
912 //---------------------------------------------------------------------------------------//
913 // Handle time axis removal
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.
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
923 * @param src the identity of the object that initiated the change
926 TimeAxisViewItem::remove_this_item(void* src)
929 defer to idle loop, otherwise we'll delete this object
930 while we're still inside this function ...
932 Gtk::Main::idle.connect(bind(mem_fun(&TimeAxisViewItem::idle_remove_this_item), this, src));
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
940 * @param item the ImageFrameTimeAxisGroup to remove
941 * @param src the identity of the object that initiated the change
944 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
946 item->ItemRemoved(item->get_item_name(), src) ; /* EMIT_SIGNAL */