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 Pango::FontDescription TimeAxisViewItem::NAME_FONT;
42 bool TimeAxisViewItem::have_name_font = false;
43 const double TimeAxisViewItem::NAME_X_OFFSET = 15.0;
44 const double TimeAxisViewItem::NAME_Y_OFFSET = 15.0 ; /* XXX depends a lot on the font size, sigh. */
45 const double TimeAxisViewItem::NAME_HIGHLIGHT_SIZE = 15.0 ; /* ditto */
46 const double TimeAxisViewItem::NAME_HIGHLIGHT_THRESH = 32.0 ; /* ditto */
47 const double TimeAxisViewItem::GRAB_HANDLE_LENGTH = 6 ;
50 //---------------------------------------------------------------------------------------//
51 // Constructor / Desctructor
54 * Constructs a new TimeAxisViewItem.
56 * @param it_name the unique name/Id of this item
57 * @param parant the parent canvas group
58 * @param tv the TimeAxisView we are going to be added to
59 * @param spu samples per unit
61 * @param start the start point of this item
62 * @param duration the duration of this item
64 TimeAxisViewItem::TimeAxisViewItem(std::string it_name, GnomeCanvasGroup* parent, TimeAxisView& tv, double spu, Gdk::Color& base_color,
65 jack_nframes_t start, jack_nframes_t duration,
66 Visibility visibility)
69 if (!have_name_font) {
70 NAME_FONT = get_font_for_style (N_("TimeAxisViewItemName"));
71 have_name_font = true;
75 samples_per_unit = spu ;
76 should_show_selection = true;
77 frame_position = start ;
78 item_duration = duration ;
79 name_connected = false;
81 position_locked = false ;
82 max_item_duration = ARDOUR::max_frames;
83 min_item_duration = 0 ;
84 show_vestigial = true;
87 warning << "Time Axis Item Duration == 0" << endl ;
90 group = gnome_canvas_item_new(GNOME_CANVAS_GROUP(parent),gnome_canvas_group_get_type(),NULL) ;
92 vestigial_frame = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
93 gnome_canvas_simplerect_get_type(),
97 "y2", (double) trackview.height,
98 "outline_color_rgba", color_map[cVestigialFrameOutline],
99 "fill_color_rgba", color_map[cVestigialFrameFill],
101 gnome_canvas_item_hide (vestigial_frame);
103 if (visibility & ShowFrame) {
104 frame = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
105 gnome_canvas_simplerect_get_type(),
108 "x2", (double) trackview.editor.frame_to_pixel(duration),
109 "y2", (double) trackview.height,
110 "outline_color_rgba", color_map[cTimeAxisFrameOutline],
111 "fill_color_rgba", color_map[cTimeAxisFrameFill],
117 if (visibility & ShowNameHighlight) {
118 name_highlight = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
119 gnome_canvas_simplerect_get_type(),
121 "x2", (double) (trackview.editor.frame_to_pixel(item_duration)) - 1,
122 "y1", (double) (trackview.height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE),
123 "y2", (double) (trackview.height - 1),
124 "outline_color_rgba", color_map[cNameHighlightFill],
125 "fill_color_rgba", color_map[cNameHighlightOutline],
127 gtk_object_set_data(GTK_OBJECT(name_highlight), "timeaxisviewitem", this) ;
132 if (visibility & ShowNameText) {
133 name_text = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
134 gnome_canvas_text_get_type(),
135 "x", (double) TimeAxisViewItem::NAME_X_OFFSET,
136 "y", (double) trackview.height + 1.0 - TimeAxisViewItem::NAME_Y_OFFSET,
137 "font", NAME_FONT.c_str(),
138 "anchor", GTK_ANCHOR_NW,
140 gtk_object_set_data(GTK_OBJECT(name_text), "timeaxisviewitem", this) ;
146 /* create our grab handles used for trimming/duration etc */
148 if (visibility & ShowHandles) {
149 frame_handle_start = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
150 gnome_canvas_simplerect_get_type(),
152 "x2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH,
154 "y2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH+1,
155 "outline_color_rgba", color_map[cFrameHandleStartOutline],
156 "fill_color_rgba", color_map[cFrameHandleStartFill],
159 frame_handle_end = gnome_canvas_item_new(GNOME_CANVAS_GROUP(group),
160 gnome_canvas_simplerect_get_type(),
161 "x1", (double) (trackview.editor.frame_to_pixel(get_duration())) - (TimeAxisViewItem::GRAB_HANDLE_LENGTH),
162 "x2", (double) trackview.editor.frame_to_pixel(get_duration()),
164 "y2", (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH + 1,
165 "outline_color_rgba", color_map[cFrameHandleEndOutline],
166 "fill_color_rgba", color_map[cFrameHandleEndFill],
169 frame_handle_start = 0;
170 frame_handle_end = 0;
173 set_color (base_color) ;
175 set_duration (item_duration, this) ;
176 set_position (start, this) ;
183 TimeAxisViewItem::~TimeAxisViewItem()
185 gtk_object_destroy (GTK_OBJECT(group));
189 //---------------------------------------------------------------------------------------//
190 // Position and duration Accessors/Mutators
193 * Set the position of this item upon the timeline to the specified value
195 * @param pos the new position
196 * @param src the identity of the object that initiated the change
197 * @return true if the position change was a success, false otherwise
200 TimeAxisViewItem::set_position(jack_nframes_t pos, void* src, double* delta)
202 if (position_locked) {
206 frame_position = pos;
208 /* This sucks. The GnomeCanvas version I am using
209 doesn't correctly implement gnome_canvas_group_set_arg(),
210 so that simply setting the "x" arg of the group
211 fails to move the group. Instead, we have to
212 use gnome_canvas_item_move(), which does the right
213 thing. I see that in GNOME CVS, the current (Sept 2001)
214 version of GNOME Canvas rectifies this issue cleanly.
218 double old_unit_pos ;
219 double new_unit_pos = pos / samples_per_unit ;
222 gtk_object_getv (GTK_OBJECT(group), 1, args) ;
223 old_unit_pos = GTK_VALUE_DOUBLE (args[0]) ;
225 if (new_unit_pos != old_unit_pos) {
226 gnome_canvas_item_move (group, new_unit_pos - old_unit_pos, 0.0) ;
230 (*delta) = new_unit_pos - old_unit_pos;
233 PositionChanged (frame_position, src) ; /* EMIT_SIGNAL */
239 * Return the position of this item upon the timeline
241 * @return the position of this item
244 TimeAxisViewItem::get_position() const
246 return frame_position;
250 * Sets the duration of this item
252 * @param dur the new duration of this item
253 * @param src the identity of the object that initiated the change
254 * @return true if the duration change was succesful, false otherwise
257 TimeAxisViewItem::set_duration (jack_nframes_t dur, void* src)
259 if ((dur > max_item_duration) || (dur < min_item_duration)) {
260 warning << string_compose (_("new duration %1 frames is out of bounds for %2"), get_item_name(), dur)
266 gnome_canvas_item_hide (group);
271 double pixel_width = trackview.editor.frame_to_pixel (dur);
273 reset_width_dependent_items (pixel_width);
275 DurationChanged (dur, src) ; /* EMIT_SIGNAL */
280 * Returns the duration of this item
284 TimeAxisViewItem::get_duration() const
286 return (item_duration);
290 * Sets the maximum duration that this item make have.
292 * @param dur the new maximum duration
293 * @param src the identity of the object that initiated the change
296 TimeAxisViewItem::set_max_duration(jack_nframes_t dur, void* src)
298 max_item_duration = dur ;
299 MaxDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
303 * Returns the maxmimum duration that this item may be set to
305 * @return the maximum duration that this item may be set to
308 TimeAxisViewItem::get_max_duration() const
310 return(max_item_duration) ;
314 * Sets the minimu duration that this item may be set to
316 * @param the minimum duration that this item may be set to
317 * @param src the identity of the object that initiated the change
320 TimeAxisViewItem::set_min_duration(jack_nframes_t dur, void* src)
322 min_item_duration = dur ;
323 MinDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
327 * Returns the minimum duration that this item mey be set to
329 * @return the nimum duration that this item mey be set to
332 TimeAxisViewItem::get_min_duration() const
334 return(min_item_duration) ;
338 * Sets whether the position of this Item is locked to its current position
339 * Locked items cannot be moved until the item is unlocked again.
341 * @param yn set to true to lock this item to its current position
342 * @param src the identity of the object that initiated the change
345 TimeAxisViewItem::set_position_locked(bool yn, void* src)
347 position_locked = yn ;
348 set_trim_handle_colors() ;
349 PositionLockChanged (position_locked, src); /* EMIT_SIGNAL */
353 * Returns whether this item is locked to its current position
355 * @return true if this item is locked to its current posotion
359 TimeAxisViewItem::get_position_locked() const
361 return (position_locked);
365 * Sets whether the Maximum Duration constraint is active and should be enforced
367 * @param active set true to enforce the max duration constraint
368 * @param src the identity of the object that initiated the change
371 TimeAxisViewItem::set_max_duration_active(bool active, void* src)
373 max_duration_active = active ;
377 * Returns whether the Maximum Duration constraint is active and should be enforced
379 * @return true if the maximum duration constraint is active, false otherwise
382 TimeAxisViewItem::get_max_duration_active() const
384 return(max_duration_active) ;
388 * Sets whether the Minimum Duration constraint is active and should be enforced
390 * @param active set true to enforce the min duration constraint
391 * @param src the identity of the object that initiated the change
394 TimeAxisViewItem::set_min_duration_active(bool active, void* src)
396 min_duration_active = active ;
400 * Returns whether the Maximum Duration constraint is active and should be enforced
402 * @return true if the maximum duration constraint is active, false otherwise
405 TimeAxisViewItem::get_min_duration_active() const
407 return(min_duration_active) ;
410 //---------------------------------------------------------------------------------------//
411 // Name/Id Accessors/Mutators
414 * Set the name/Id of this item.
416 * @param new_name the new name of this item
417 * @param src the identity of the object that initiated the change
420 TimeAxisViewItem::set_item_name(std::string new_name, void* src)
422 if (new_name != item_name) {
423 std::string temp_name = item_name ;
424 item_name = new_name ;
425 NameChanged (item_name, temp_name, src) ; /* EMIT_SIGNAL */
430 * Returns the name/id of this item
432 * @return the name/id of this item
435 TimeAxisViewItem::get_item_name() const
440 //---------------------------------------------------------------------------------------//
444 * Set to true to indicate that this item is currently selected
446 * @param yn true if this item is currently selected
447 * @param src the identity of the object that initiated the change
450 TimeAxisViewItem::set_selected(bool yn, void* src)
452 if (_selected != yn) {
455 Selected (_selected) ; /* EMIT_SIGNAL */
460 * Returns whether this item is currently selected.
462 * @return true if this item is currently selected, false otherwise
465 TimeAxisViewItem::get_selected() const
471 TimeAxisViewItem::set_should_show_selection (bool yn)
473 if (should_show_selection != yn) {
474 should_show_selection = yn;
479 //---------------------------------------------------------------------------------------//
480 // Parent Componenet Methods
483 * Returns the TimeAxisView that this item is upon
485 * @return the timeAxisView that this item is placed upon
488 TimeAxisViewItem::get_time_axis_view()
492 //---------------------------------------------------------------------------------------//
496 * Sets the displayed item text
497 * This item is the visual text name displayed on the canvas item, this can be different to the name of the item
499 * @param new_name the new name text to display
502 TimeAxisViewItem::set_name_text(std::string new_name)
505 gnome_canvas_item_set (name_text, "text", new_name.c_str(), NULL);
510 * Set the height of this item
512 * @param h the new height
515 TimeAxisViewItem::set_height(double height)
517 if (name_highlight) {
518 if (height < NAME_HIGHLIGHT_THRESH) {
519 gnome_canvas_item_hide (name_highlight);
520 gnome_canvas_item_hide (name_text);
522 gnome_canvas_item_show (name_highlight);
523 gnome_canvas_item_show (name_text);
526 if (height > NAME_HIGHLIGHT_SIZE) {
527 gnome_canvas_item_set (name_highlight,
528 "y1", (double) height+1 - NAME_HIGHLIGHT_SIZE,
529 "y2", (double) height,
533 /* it gets hidden now anyway */
534 gnome_canvas_item_set (name_highlight,
536 "y2", (double) height,
542 gnome_canvas_item_set (name_text, "y", height+1 - NAME_Y_OFFSET, NULL);
543 if (height < NAME_HIGHLIGHT_THRESH) {
544 gnome_canvas_item_set(name_text, "fill_color_rgba", fill_color, NULL) ;
547 gnome_canvas_item_set(name_text, "fill_color_rgba", label_color, NULL) ;
552 gnome_canvas_item_set (frame, "y2", height+1, NULL) ;
555 gnome_canvas_item_set (vestigial_frame, "y2", height+1, NULL) ;
562 TimeAxisViewItem::set_color(Gdk::Color& base_color)
564 compute_colors (base_color);
572 TimeAxisViewItem::get_canvas_frame()
581 TimeAxisViewItem::get_canvas_group()
590 TimeAxisViewItem::get_name_highlight()
592 return(name_highlight) ;
599 TimeAxisViewItem::get_name_text()
605 * Calculates some contrasting color for displaying various parts of this item, based upon the base color
607 * @param color the base color of the item
610 TimeAxisViewItem::compute_colors(Gdk::Color& base_color)
612 unsigned char radius ;
615 unsigned char r,g,b ;
617 /* FILL: this is simple */
618 r = base_color.get_red()/256 ;
619 g = base_color.get_green()/256 ;
620 b = base_color.get_blue()/256 ;
621 fill_color = RGBA_TO_UINT(r,g,b,255) ;
624 if the overall saturation is strong, make the minor colors light.
625 if its weak, make them dark.
627 we do this by moving an equal distance to the other side of the
628 central circle in the color wheel from where we started.
631 radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f)) ;
632 minor_shift = 125 - radius ;
634 /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
636 r = base_color.get_red()/256;
637 g = base_color.get_green()/256;
638 b = base_color.get_blue()/256;
644 /* red sector => green */
649 /* green sector => blue */
657 /* blue sector => red */
662 /* green sector => blue */
671 label_color = RGBA_TO_UINT(r,g,b,255);
672 r = (base_color.get_red()/256) + 127 ;
673 g = (base_color.get_green()/256) + 127 ;
674 b = (base_color.get_blue()/256) + 127 ;
676 label_color = RGBA_TO_UINT(r,g,b,255);
678 /* XXX can we do better than this ? */
679 /* We're trying ;) */
682 //frame_color_r = 192;
683 //frame_color_g = 192;
684 //frame_color_b = 194;
686 //selected_frame_color_r = 182;
687 //selected_frame_color_g = 145;
688 //selected_frame_color_b = 168;
690 //handle_color_r = 25 ;
691 //handle_color_g = 0 ;
692 //handle_color_b = 255 ;
693 //lock_handle_color_r = 235 ;
694 //lock_handle_color_g = 16;
695 //lock_handle_color_b = 16;
699 * Convenience method to set the various canvas item colors
702 TimeAxisViewItem::set_colors()
706 double height = NAME_HIGHLIGHT_THRESH;
710 args[0].name = "y2" ;
711 gtk_object_getv (GTK_OBJECT(frame), 1, args);
712 height = GTK_VALUE_DOUBLE (args[0]);
715 if (height < NAME_HIGHLIGHT_THRESH) {
716 gnome_canvas_item_set(name_text, "fill_color_rgba", fill_color, NULL) ;
719 gnome_canvas_item_set(name_text, "fill_color_rgba", label_color, NULL) ;
723 if (name_highlight) {
724 gnome_canvas_item_set(name_highlight, "fill_color_rgba", fill_color, NULL) ;
725 gnome_canvas_item_set(name_highlight, "outline_color_rgba", fill_color, NULL) ;
727 set_trim_handle_colors() ;
731 * Sets the frame color depending on whether this item is selected
734 TimeAxisViewItem::set_frame_color()
739 if (_selected && should_show_selection) {
740 UINT_TO_RGBA(color_map[cSelectedFrameBase], &r, &g, &b, &a);
741 gnome_canvas_item_set(frame, "fill_color_rgba", RGBA_TO_UINT(r, g, b, fill_opacity), NULL) ;
743 UINT_TO_RGBA(color_map[cFrameBase], &r, &g, &b, &a);
744 gnome_canvas_item_set(frame, "fill_color_rgba", RGBA_TO_UINT(r, g, b, fill_opacity), NULL) ;
750 * Sets the colors of the start and end trim handle depending on object state
754 TimeAxisViewItem::set_trim_handle_colors()
756 if (frame_handle_start) {
757 if (position_locked) {
758 gnome_canvas_item_set(frame_handle_start, "fill_color_rgba", color_map[cTrimHandleLockedStart], NULL);
759 gnome_canvas_item_set(frame_handle_end, "fill_color_rgba", color_map[cTrimHandleLockedEnd], NULL) ;
761 gnome_canvas_item_set(frame_handle_start, "fill_color_rgba", color_map[cTrimHandleStart], NULL) ;
762 gnome_canvas_item_set(frame_handle_end, "fill_color_rgba", color_map[cTrimHandleEnd], NULL) ;
768 TimeAxisViewItem::get_samples_per_unit()
770 return(samples_per_unit) ;
774 TimeAxisViewItem::set_samples_per_unit (double spu)
776 samples_per_unit = spu ;
777 set_position (this->get_position(), this);
778 reset_width_dependent_items ((double)get_duration() / samples_per_unit);
782 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
784 if (pixel_width < GRAB_HANDLE_LENGTH * 2) {
786 if (frame_handle_start) {
787 gnome_canvas_item_hide (frame_handle_start);
788 gnome_canvas_item_hide (frame_handle_end);
791 } if (pixel_width < 2.0) {
793 if (show_vestigial) {
794 gnome_canvas_item_show (vestigial_frame);
797 if (name_highlight) {
798 gnome_canvas_item_hide (name_highlight);
799 gnome_canvas_item_hide (name_text);
803 gnome_canvas_item_hide (frame);
806 if (frame_handle_start) {
807 gnome_canvas_item_hide (frame_handle_start);
808 gnome_canvas_item_hide (frame_handle_end);
812 gnome_canvas_item_hide (vestigial_frame);
814 if (name_highlight) {
817 args[0].name = "y2" ;
818 gtk_object_getv (GTK_OBJECT(name_highlight), 1, args);
819 double height = GTK_VALUE_DOUBLE (args[0]);
821 if (height < NAME_HIGHLIGHT_THRESH) {
822 gnome_canvas_item_hide (name_highlight);
823 gnome_canvas_item_hide (name_text);
825 gnome_canvas_item_show (name_highlight);
826 gnome_canvas_item_show (name_text);
827 reset_name_width (pixel_width);
830 gnome_canvas_item_set (name_highlight, "x2", pixel_width - 1.0, NULL);
834 gnome_canvas_item_show (frame);
835 gnome_canvas_item_set (frame, "x2", pixel_width, NULL);
838 if (frame_handle_start) {
839 if (pixel_width < (2*TimeAxisViewItem::GRAB_HANDLE_LENGTH)) {
840 gnome_canvas_item_hide (frame_handle_start);
841 gnome_canvas_item_hide (frame_handle_end);
843 gnome_canvas_item_show (frame_handle_start);
844 gnome_canvas_item_set(GNOME_CANVAS_ITEM(frame_handle_end), "x1", pixel_width - (TimeAxisViewItem::GRAB_HANDLE_LENGTH ), NULL) ;
845 gnome_canvas_item_show (frame_handle_end);
846 gnome_canvas_item_set(GNOME_CANVAS_ITEM(frame_handle_end), "x2", pixel_width, NULL) ;
852 TimeAxisViewItem::reset_name_width (double pixel_width)
859 Gdk_Font font (NAME_FONT);
861 if (name_text == 0) {
865 int namelen = item_name.length();
866 char cstr[namelen+1];
867 strcpy (cstr, item_name.c_str());
871 gdk_string_extents (font,
879 if (width < (pixel_width - NAME_X_OFFSET)) {
884 cstr[namelen] = '\0';
890 gnome_canvas_item_hide (name_text);
894 /* don't use name for event handling if it leaves no room
895 for trimming to work.
898 if (pixel_width - width < (NAME_X_OFFSET * 2.0)) {
899 if (name_connected) {
900 name_connected = false;
903 if (!name_connected) {
904 name_connected = true;
908 gnome_canvas_item_set (name_text, "text", cstr, NULL);
909 gnome_canvas_item_show (name_text);
914 //---------------------------------------------------------------------------------------//
915 // Handle time axis removal
918 * Handles the Removal of this time axis item
919 * This _needs_ to be called to alert others of the removal properly, ie where the source
920 * of the removal came from.
922 * XXX Although im not too happy about this method of doing things, I cant think of a cleaner method
923 * just now to capture the source of the removal
925 * @param src the identity of the object that initiated the change
928 TimeAxisViewItem::remove_this_item(void* src)
931 defer to idle loop, otherwise we'll delete this object
932 while we're still inside this function ...
934 Glib::signal_idle().connect(bind (sigc::ptr_fun (&TimeAxisViewItem::idle_remove_this_item), this, src));
938 * Callback used to remove this time axis item during the gtk idle loop
939 * This is used to avoid deleting the obejct while inside the remove_this_item
942 * @param item the ImageFrameTimeAxisGroup to remove
943 * @param src the identity of the object that initiated the change
946 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
948 item->ItemRemoved(item->get_item_name(), src) ; /* EMIT_SIGNAL */