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.
20 #include <pbd/error.h>
21 #include <pbd/stacktrace.h>
23 #include <ardour/types.h>
24 #include <ardour/ardour.h>
26 #include <gtkmm2ext/utils.h>
28 #include "public_editor.h"
29 #include "time_axis_view_item.h"
30 #include "time_axis_view.h"
31 #include "simplerect.h"
33 #include "canvas_impl.h"
34 #include "rgb_macros.h"
35 #include "ardour_ui.h"
40 using namespace Editing;
44 //------------------------------------------------------------------------------
45 /** Initialize const static memeber data */
47 Pango::FontDescription* TimeAxisViewItem::NAME_FONT = 0;
48 bool TimeAxisViewItem::have_name_font = false;
49 const double TimeAxisViewItem::NAME_X_OFFSET = 15.0;
50 const double TimeAxisViewItem::GRAB_HANDLE_LENGTH = 6 ;
52 double TimeAxisViewItem::NAME_Y_OFFSET;
53 double TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
54 double TimeAxisViewItem::NAME_HIGHLIGHT_THRESH;
57 convert_color_channel (guint8 src,
60 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
64 convert_bgra_to_rgba (guint8 const* src,
69 guint8 const* src_pixel = src;
70 guint8* dst_pixel = dst;
72 for (int y = 0; y < height; y++)
73 for (int x = 0; x < width; x++)
75 dst_pixel[0] = convert_color_channel (src_pixel[2],
77 dst_pixel[1] = convert_color_channel (src_pixel[1],
79 dst_pixel[2] = convert_color_channel (src_pixel[0],
81 dst_pixel[3] = src_pixel[3];
88 //---------------------------------------------------------------------------------------//
89 // Constructor / Desctructor
92 * Constructs a new TimeAxisViewItem.
94 * @param it_name the unique name/Id of this item
95 * @param parant the parent canvas group
96 * @param tv the TimeAxisView we are going to be added to
97 * @param spu samples per unit
99 * @param start the start point of this item
100 * @param duration the duration of this item
102 TimeAxisViewItem::TimeAxisViewItem(const string & it_name, ArdourCanvas::Group& parent, TimeAxisView& tv, double spu, Gdk::Color& base_color,
103 nframes_t start, nframes_t duration, bool recording,
105 : trackview (tv), _recregion(recording)
107 if (!have_name_font) {
109 /* first constructed item sets up font info */
111 NAME_FONT = get_font_for_style (N_("TimeAxisViewItemName"));
117 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (X_("Hg")); /* ascender + descender */
121 layout->set_font_description (*NAME_FONT);
122 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
124 NAME_Y_OFFSET = height + 6;
125 NAME_HIGHLIGHT_SIZE = height + 6;
126 NAME_HIGHLIGHT_THRESH = NAME_HIGHLIGHT_SIZE * 2;
128 have_name_font = true;
131 group = new ArdourCanvas::Group (parent);
133 init (it_name, spu, base_color, start, duration, vis);
137 TimeAxisViewItem::TimeAxisViewItem (const TimeAxisViewItem& other)
138 : trackview (other.trackview)
144 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
145 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
147 /* share the other's parent, but still create a new group */
149 Gnome::Canvas::Group* parent = other.group->property_parent();
151 group = new ArdourCanvas::Group (*parent);
153 init (other.item_name, other.samples_per_unit, c, other.frame_position, other.item_duration, other.visibility);
157 TimeAxisViewItem::init (const string& it_name, double spu, Gdk::Color& base_color, nframes_t start, nframes_t duration, Visibility vis)
159 item_name = it_name ;
160 samples_per_unit = spu ;
161 should_show_selection = true;
162 frame_position = start ;
163 item_duration = duration ;
164 name_connected = false;
166 position_locked = false ;
167 max_item_duration = ARDOUR::max_frames;
168 min_item_duration = 0 ;
169 show_vestigial = true;
174 warning << "Time Axis Item Duration == 0" << endl ;
177 vestigial_frame = new ArdourCanvas::SimpleRect (*group);
178 vestigial_frame->property_x1() = (double) 0.0;
179 vestigial_frame->property_y1() = (double) 1.0;
180 vestigial_frame->property_x2() = (double) 2.0;
181 vestigial_frame->property_y2() = (double) trackview.current_height();
182 vestigial_frame->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_VestigialFrame.get();
183 vestigial_frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_VestigialFrame.get();
184 vestigial_frame->hide ();
186 if (visibility & ShowFrame) {
187 frame = new ArdourCanvas::SimpleRect (*group);
188 frame->property_x1() = (double) 0.0;
189 frame->property_y1() = (double) 1.0;
190 frame->property_x2() = (double) trackview.editor.frame_to_pixel(duration);
191 frame->property_y2() = (double) trackview.current_height();
192 frame->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeAxisFrame.get();
194 /* by default draw all 4 edges */
196 uint32_t outline_what = 0x1|0x2|0x4|0x8;
198 if (visibility & HideFrameLeft) {
199 outline_what &= ~(0x1);
202 if (visibility & HideFrameRight) {
203 outline_what &= ~(0x2);
206 if (visibility & HideFrameTB) {
207 outline_what &= ~(0x4 | 0x8);
210 frame->property_outline_what() = outline_what;
216 if (visibility & ShowNameHighlight) {
217 name_highlight = new ArdourCanvas::SimpleRect (*group);
218 if (visibility & FullWidthNameHighlight) {
219 name_highlight->property_x1() = (double) 0.0;
220 name_highlight->property_x2() = (double) (trackview.editor.frame_to_pixel(item_duration));
222 name_highlight->property_x1() = (double) 1.0;
223 name_highlight->property_x2() = (double) (trackview.editor.frame_to_pixel(item_duration)) - 1;
225 name_highlight->property_y1() = (double) (trackview.current_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
226 name_highlight->property_y2() = (double) (trackview.current_height() - 1);
228 name_highlight->set_data ("timeaxisviewitem", this);
234 /* create our grab handles used for trimming/duration etc */
236 if (visibility & ShowHandles) {
237 frame_handle_start = new ArdourCanvas::SimpleRect (*group);
238 frame_handle_start->property_x1() = (double) 0.0;
239 frame_handle_start->property_x2() = (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH;
240 frame_handle_start->property_y1() = (double) 1.0;
241 frame_handle_start->property_y2() = (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH+1;
242 frame_handle_start->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_FrameHandle.get();
244 frame_handle_end = new ArdourCanvas::SimpleRect (*group);
245 frame_handle_end->property_x1() = (double) (trackview.editor.frame_to_pixel(get_duration())) - (TimeAxisViewItem::GRAB_HANDLE_LENGTH);
246 frame_handle_end->property_x2() = (double) trackview.editor.frame_to_pixel(get_duration());
247 frame_handle_end->property_y1() = (double) 1;
248 frame_handle_end->property_y2() = (double) TimeAxisViewItem::GRAB_HANDLE_LENGTH + 1;
249 frame_handle_end->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_FrameHandle.get();
252 frame_handle_start = 0;
253 frame_handle_end = 0;
256 if (visibility & ShowNameText) {
257 name_pixbuf = new ArdourCanvas::Pixbuf(*group);
258 name_pixbuf->property_x() = NAME_X_OFFSET;
259 name_pixbuf->property_y() = trackview.current_height() - 1.0 - NAME_Y_OFFSET;
262 set_color (base_color) ;
264 set_duration (item_duration, this) ;
265 set_position (start, this) ;
271 TimeAxisViewItem::~TimeAxisViewItem()
277 //---------------------------------------------------------------------------------------//
278 // Position and duration Accessors/Mutators
281 * Set the position of this item upon the timeline to the specified value
283 * @param pos the new position
284 * @param src the identity of the object that initiated the change
285 * @return true if the position change was a success, false otherwise
288 TimeAxisViewItem::set_position(nframes_t pos, void* src, double* delta)
290 if (position_locked) {
294 frame_position = pos;
296 /* This sucks. The GnomeCanvas version I am using
297 doesn't correctly implement gnome_canvas_group_set_arg(),
298 so that simply setting the "x" arg of the group
299 fails to move the group. Instead, we have to
300 use gnome_canvas_item_move(), which does the right
301 thing. I see that in GNOME CVS, the current (Sept 2001)
302 version of GNOME Canvas rectifies this issue cleanly.
305 double old_unit_pos ;
306 double new_unit_pos = pos / samples_per_unit ;
308 old_unit_pos = group->property_x();
310 if (new_unit_pos != old_unit_pos) {
311 group->move (new_unit_pos - old_unit_pos, 0.0);
315 (*delta) = new_unit_pos - old_unit_pos;
318 PositionChanged (frame_position, src) ; /* EMIT_SIGNAL */
324 * Return the position of this item upon the timeline
326 * @return the position of this item
329 TimeAxisViewItem::get_position() const
331 return frame_position;
335 * Sets the duration of this item
337 * @param dur the new duration of this item
338 * @param src the identity of the object that initiated the change
339 * @return true if the duration change was succesful, false otherwise
342 TimeAxisViewItem::set_duration (nframes_t dur, void* src)
344 if ((dur > max_item_duration) || (dur < min_item_duration)) {
345 warning << string_compose (_("new duration %1 frames is out of bounds for %2"), get_item_name(), dur)
356 reset_width_dependent_items (trackview.editor.frame_to_pixel (dur));
358 DurationChanged (dur, src) ; /* EMIT_SIGNAL */
363 * Returns the duration of this item
367 TimeAxisViewItem::get_duration() const
369 return (item_duration);
373 * Sets the maximum duration that this item make have.
375 * @param dur the new maximum duration
376 * @param src the identity of the object that initiated the change
379 TimeAxisViewItem::set_max_duration(nframes_t dur, void* src)
381 max_item_duration = dur ;
382 MaxDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
386 * Returns the maxmimum duration that this item may be set to
388 * @return the maximum duration that this item may be set to
391 TimeAxisViewItem::get_max_duration() const
393 return (max_item_duration) ;
397 * Sets the minimu duration that this item may be set to
399 * @param the minimum duration that this item may be set to
400 * @param src the identity of the object that initiated the change
403 TimeAxisViewItem::set_min_duration(nframes_t dur, void* src)
405 min_item_duration = dur ;
406 MinDurationChanged(max_item_duration, src) ; /* EMIT_SIGNAL */
410 * Returns the minimum duration that this item mey be set to
412 * @return the nimum duration that this item mey be set to
415 TimeAxisViewItem::get_min_duration() const
417 return(min_item_duration) ;
421 * Sets whether the position of this Item is locked to its current position
422 * Locked items cannot be moved until the item is unlocked again.
424 * @param yn set to true to lock this item to its current position
425 * @param src the identity of the object that initiated the change
428 TimeAxisViewItem::set_position_locked(bool yn, void* src)
430 position_locked = yn ;
431 set_trim_handle_colors() ;
432 PositionLockChanged (position_locked, src); /* EMIT_SIGNAL */
436 * Returns whether this item is locked to its current position
438 * @return true if this item is locked to its current posotion
442 TimeAxisViewItem::get_position_locked() const
444 return (position_locked);
448 * Sets whether the Maximum Duration constraint is active and should be enforced
450 * @param active set true to enforce the max duration constraint
451 * @param src the identity of the object that initiated the change
454 TimeAxisViewItem::set_max_duration_active(bool active, void* src)
456 max_duration_active = active ;
460 * Returns whether the Maximum Duration constraint is active and should be enforced
462 * @return true if the maximum duration constraint is active, false otherwise
465 TimeAxisViewItem::get_max_duration_active() const
467 return(max_duration_active) ;
471 * Sets whether the Minimum Duration constraint is active and should be enforced
473 * @param active set true to enforce the min duration constraint
474 * @param src the identity of the object that initiated the change
477 TimeAxisViewItem::set_min_duration_active(bool active, void* src)
479 min_duration_active = active ;
483 * Returns whether the Maximum Duration constraint is active and should be enforced
485 * @return true if the maximum duration constraint is active, false otherwise
488 TimeAxisViewItem::get_min_duration_active() const
490 return(min_duration_active) ;
493 //---------------------------------------------------------------------------------------//
494 // Name/Id Accessors/Mutators
497 * Set the name/Id of this item.
499 * @param new_name the new name of this item
500 * @param src the identity of the object that initiated the change
503 TimeAxisViewItem::set_item_name(std::string new_name, void* src)
505 if (new_name != item_name) {
506 std::string temp_name = item_name ;
507 item_name = new_name ;
508 NameChanged (item_name, temp_name, src) ; /* EMIT_SIGNAL */
513 * Returns the name/id of this item
515 * @return the name/id of this item
518 TimeAxisViewItem::get_item_name() const
523 //---------------------------------------------------------------------------------------//
527 * Set to true to indicate that this item is currently selected
529 * @param yn true if this item is currently selected
530 * @param src the identity of the object that initiated the change
533 TimeAxisViewItem::set_selected(bool yn)
535 if (_selected != yn) {
536 Selectable::set_selected (yn);
542 TimeAxisViewItem::set_should_show_selection (bool yn)
544 if (should_show_selection != yn) {
545 should_show_selection = yn;
550 //---------------------------------------------------------------------------------------//
551 // Parent Componenet Methods
554 * Returns the TimeAxisView that this item is upon
556 * @return the timeAxisView that this item is placed upon
559 TimeAxisViewItem::get_time_axis_view()
563 //---------------------------------------------------------------------------------------//
567 * Sets the displayed item text
568 * This item is the visual text name displayed on the canvas item, this can be different to the name of the item
570 * @param new_name the new name text to display
573 TimeAxisViewItem::set_name_text(const ustring& new_name)
575 uint32_t pb_width, it_width;
578 font_size = NAME_FONT->get_size() / Pango::SCALE;
579 it_width = trackview.editor.frame_to_pixel(item_duration);
580 pb_width = new_name.length() * font_size;
582 if (pb_width > it_width - NAME_X_OFFSET) {
583 pb_width = it_width - NAME_X_OFFSET;
586 if (pb_width <= 0 || it_width < NAME_X_OFFSET) {
593 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, pb_width, NAME_HIGHLIGHT_SIZE);
595 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pb_width, NAME_HIGHLIGHT_SIZE );
596 cairo_t *cr = cairo_create (surface);
597 cairo_text_extents_t te;
598 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
599 cairo_select_font_face (cr, NAME_FONT->get_family().c_str(),
600 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
601 cairo_set_font_size (cr, font_size);
602 cairo_text_extents (cr, new_name.c_str(), &te);
604 cairo_move_to (cr, 0.5,
605 0.5 - te.height / 2 - te.y_bearing + NAME_HIGHLIGHT_SIZE / 2);
606 cairo_show_text (cr, new_name.c_str());
608 unsigned char* src = cairo_image_surface_get_data (surface);
609 convert_bgra_to_rgba(src, buf->get_pixels(), pb_width, NAME_HIGHLIGHT_SIZE);
612 name_pixbuf->property_pixbuf() = buf;
616 * Set the height of this item
618 * @param h the new height
621 TimeAxisViewItem::set_height (double height)
623 if (name_highlight) {
624 if (height < NAME_HIGHLIGHT_THRESH) {
625 name_highlight->hide();
629 name_highlight->show();
634 if (height > NAME_HIGHLIGHT_SIZE) {
635 name_highlight->property_y1() = (double) height+1 - NAME_HIGHLIGHT_SIZE;
636 name_highlight->property_y2() = (double) height;
638 /* it gets hidden now anyway */
639 //name_highlight->property_y1() = (double) 1.0;
640 //name_highlight->property_y2() = (double) height;
644 if (visibility & ShowNameText) {
645 name_pixbuf->property_y() = height+1 - NAME_Y_OFFSET;
649 frame->property_y2() = height+1;
652 vestigial_frame->property_y2() = height+1;
659 TimeAxisViewItem::set_color(Gdk::Color& base_color)
661 compute_colors (base_color);
669 TimeAxisViewItem::get_canvas_frame()
678 TimeAxisViewItem::get_canvas_group()
687 TimeAxisViewItem::get_name_highlight()
689 return (name_highlight) ;
695 ArdourCanvas::Pixbuf*
696 TimeAxisViewItem::get_name_pixbuf()
698 return (name_pixbuf) ;
702 * Calculates some contrasting color for displaying various parts of this item, based upon the base color
704 * @param color the base color of the item
707 TimeAxisViewItem::compute_colors(Gdk::Color& base_color)
709 unsigned char radius ;
712 unsigned char r,g,b ;
714 /* FILL: this is simple */
715 r = base_color.get_red()/256 ;
716 g = base_color.get_green()/256 ;
717 b = base_color.get_blue()/256 ;
718 fill_color = RGBA_TO_UINT(r,g,b,255) ;
721 if the overall saturation is strong, make the minor colors light.
722 if its weak, make them dark.
724 we do this by moving an equal distance to the other side of the
725 central circle in the color wheel from where we started.
728 radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f)) ;
729 minor_shift = 125 - radius ;
731 /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
733 r = base_color.get_red()/256;
734 g = base_color.get_green()/256;
735 b = base_color.get_blue()/256;
741 /* red sector => green */
746 /* green sector => blue */
754 /* blue sector => red */
759 /* green sector => blue */
768 label_color = RGBA_TO_UINT(r,g,b,255);
769 r = (base_color.get_red()/256) + 127 ;
770 g = (base_color.get_green()/256) + 127 ;
771 b = (base_color.get_blue()/256) + 127 ;
773 label_color = RGBA_TO_UINT(r,g,b,255);
775 /* XXX can we do better than this ? */
776 /* We're trying ;) */
779 //frame_color_r = 192;
780 //frame_color_g = 192;
781 //frame_color_b = 194;
783 //selected_frame_color_r = 182;
784 //selected_frame_color_g = 145;
785 //selected_frame_color_b = 168;
787 //handle_color_r = 25 ;
788 //handle_color_g = 0 ;
789 //handle_color_b = 255 ;
790 //lock_handle_color_r = 235 ;
791 //lock_handle_color_g = 16;
792 //lock_handle_color_b = 16;
796 * Convenience method to set the various canvas item colors
799 TimeAxisViewItem::set_colors()
803 if (name_highlight) {
804 name_highlight->property_fill_color_rgba() = fill_color;
805 name_highlight->property_outline_color_rgba() = fill_color;
807 set_trim_handle_colors() ;
811 * Sets the frame color depending on whether this item is selected
814 TimeAxisViewItem::set_frame_color()
819 if (_selected && should_show_selection) {
820 UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get(), &r, &g, &b, &a);
821 frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity ? fill_opacity : a);// Lets still use the theme's opacity value if Opaque is not set
824 UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_RecordingRect.get(), &r, &g, &b, &a);
825 frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, a);
827 UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_FrameBase.get(), &r, &g, &b, &a);
828 frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity ? fill_opacity : a);
835 * Sets the colors of the start and end trim handle depending on object state
839 TimeAxisViewItem::set_trim_handle_colors()
841 if (frame_handle_start) {
842 if (position_locked) {
843 frame_handle_start->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TrimHandleLocked.get();
844 frame_handle_end->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TrimHandleLocked.get();
846 frame_handle_start->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TrimHandle.get();
847 frame_handle_end->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TrimHandle.get();
853 TimeAxisViewItem::get_samples_per_unit()
855 return(samples_per_unit) ;
859 TimeAxisViewItem::set_samples_per_unit (double spu)
861 samples_per_unit = spu ;
862 set_position (this->get_position(), this);
863 reset_width_dependent_items ((double)get_duration() / samples_per_unit);
867 TimeAxisViewItem::reset_width_dependent_items (double pixel_width)
869 if (pixel_width < 2.0) {
871 if (show_vestigial) {
872 vestigial_frame->show();
875 if (name_highlight) {
876 name_highlight->hide();
884 if (frame_handle_start) {
885 frame_handle_start->hide();
886 frame_handle_end->hide();
890 vestigial_frame->hide();
892 if (name_highlight) {
894 double height = name_highlight->property_y2 ();
896 if (height < NAME_HIGHLIGHT_THRESH) {
897 name_highlight->hide();
900 name_highlight->show();
901 if (!get_item_name().empty()) {
902 reset_name_width (pixel_width);
906 if (visibility & FullWidthNameHighlight) {
907 name_highlight->property_x2() = pixel_width;
909 name_highlight->property_x2() = pixel_width - 1.0;
916 frame->property_x2() = pixel_width;
919 if (frame_handle_start) {
920 if (pixel_width < (2*TimeAxisViewItem::GRAB_HANDLE_LENGTH)) {
921 frame_handle_start->hide();
922 frame_handle_end->hide();
924 frame_handle_start->show();
925 frame_handle_end->property_x1() = pixel_width - (TimeAxisViewItem::GRAB_HANDLE_LENGTH);
926 frame_handle_end->property_x2() = pixel_width;
927 frame_handle_end->show();
934 TimeAxisViewItem::reset_name_width (double pixel_width)
936 set_name_text (item_name);
940 //---------------------------------------------------------------------------------------//
941 // Handle time axis removal
944 * Handles the Removal of this time axis item
945 * This _needs_ to be called to alert others of the removal properly, ie where the source
946 * of the removal came from.
948 * XXX Although im not too happy about this method of doing things, I cant think of a cleaner method
949 * just now to capture the source of the removal
951 * @param src the identity of the object that initiated the change
954 TimeAxisViewItem::remove_this_item(void* src)
957 defer to idle loop, otherwise we'll delete this object
958 while we're still inside this function ...
960 Glib::signal_idle().connect(bind (sigc::ptr_fun (&TimeAxisViewItem::idle_remove_this_item), this, src));
964 * Callback used to remove this time axis item during the gtk idle loop
965 * This is used to avoid deleting the obejct while inside the remove_this_item
968 * @param item the TimeAxisViewItem to remove
969 * @param src the identity of the object that initiated the change
972 TimeAxisViewItem::idle_remove_this_item(TimeAxisViewItem* item, void* src)
974 item->ItemRemoved (item->get_item_name(), src) ; /* EMIT_SIGNAL */