diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 4662d5f599..26ec5f6c24 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -497,7 +497,7 @@ ProcessorBox::ProcessorBox (ARDOUR::Session* sess, boost::functionsignal_unmap().connect (sigc::mem_fun (*this, &ProcessorBox::processor_menu_unmapped)); } Gtk::MenuItem* plugin_menu_item = dynamic_cast(ActionManager::get_widget("/ProcessorMenu/newplugin")); @@ -714,6 +715,13 @@ ProcessorBox::show_processor_menu (gint arg) paste_action->set_sensitive (!_rr_selection.processors.empty()); processor_menu->popup (1, arg); + + /* Add a placeholder gap to the processor list to indicate where a processor would be + inserted were one chosen from the menu. + */ + int x, y; + processor_display.get_pointer (x, y); + _placement = processor_display.add_placeholder (y); } bool @@ -884,20 +892,6 @@ ProcessorBox::processor_button_release_event (GdkEventButton *ev, ProcessorEntry } else if (Keyboard::is_context_menu_event (ev)) { - /* figure out if we are above or below the fader/amp processor, - and set the next insert position appropriately. - */ - - if (processor) { - if (_route->processor_is_prefader (processor)) { - _placement = PreFader; - } else { - _placement = PostFader; - } - } else { - _placement = PostFader; - } - show_processor_menu (ev->time); } else if (processor && Keyboard::is_button2_event (ev) @@ -977,7 +971,7 @@ ProcessorBox::use_plugins (const SelectedPlugins& plugins) Route::ProcessorStreams err_streams; - if (_route->add_processor (processor, _placement, &err_streams, Config->get_new_plugins_active ())) { + if (_route->add_processor_by_index (processor, _placement, &err_streams, Config->get_new_plugins_active ())) { weird_plugin_dialog (**p, err_streams); return true; // XXX SHAREDPTR delete plugin here .. do we even need to care? @@ -1042,7 +1036,7 @@ void ProcessorBox::choose_insert () { boost::shared_ptr processor (new PortInsert (*_session, _route->pannable(), _route->mute_master())); - _route->add_processor (processor, _placement); + _route->add_processor_by_index (processor, _placement); } /* Caller must not hold process lock */ @@ -1104,7 +1098,7 @@ ProcessorBox::send_io_finished (IOSelector::Result r, boost::weak_ptr break; case IOSelector::Accepted: - _route->add_processor (processor, _placement); + _route->add_processor_by_index (processor, _placement); if (Profile->get_sae()) { processor->activate (); } @@ -1132,7 +1126,7 @@ ProcessorBox::return_io_finished (IOSelector::Result r, boost::weak_ptradd_processor (processor, _placement); + _route->add_processor_by_index (processor, _placement); if (Profile->get_sae()) { processor->activate (); } @@ -2401,6 +2395,12 @@ ProcessorBox::hide_things () } } +void +ProcessorBox::processor_menu_unmapped () +{ + processor_display.remove_placeholder (); +} + ProcessorWindowProxy::ProcessorWindowProxy ( string const & name, XMLNode const * node, diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 17fb494268..eabe32a631 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -240,7 +240,10 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD boost::shared_ptr _processor_being_created; - ARDOUR::Placement _placement; + /** Index at which to place a new plugin (based on where the menu was opened), or -1 to + * put at the end of the plugin list. + */ + int _placement; RouteRedirectSelection& _rr_selection; @@ -265,7 +268,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD gint processor_menu_map_handler (GdkEventAny *ev); Gtk::Menu * build_processor_menu (); void build_processor_tooltip (Gtk::EventBox&, std::string); - void show_processor_menu (gint arg); + void show_processor_menu (int); Gtk::Menu* build_possible_aux_menu(); void choose_aux (boost::weak_ptr); @@ -289,6 +292,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD void reordered (); void report_failed_reorder (); void route_processors_changed (ARDOUR::RouteProcessorChange); + void processor_menu_unmapped (); void processors_reordered (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&, int*); void compute_processor_sort_keys (); diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 66464e4457..91b95d33e7 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -237,6 +237,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, }; int add_processor (boost::shared_ptr, Placement placement, ProcessorStreams* err = 0, bool activation_allowed = true); + int add_processor_by_index (boost::shared_ptr, int, ProcessorStreams* err = 0, bool activation_allowed = true); int add_processor (boost::shared_ptr, ProcessorList::iterator iter, ProcessorStreams* err = 0, bool activation_allowed = true); int add_processors (const ProcessorList&, boost::shared_ptr before, ProcessorStreams* err = 0); int remove_processor (boost::shared_ptr, ProcessorStreams* err = 0); diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 47e09cd2a7..97a6e200ba 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -853,6 +853,34 @@ Route::add_processor (boost::shared_ptr processor, Placement placemen } +/** Add a processor to a route such that it ends up with a given index into the visible processors. + * @param index Index to add the processor at, or -1 to add at the end of the list. + */ + +int +Route::add_processor_by_index (boost::shared_ptr processor, int index, ProcessorStreams* err, bool activation_allowed) +{ + /* XXX this is not thread safe - we don't hold the lock across determining the iter + to add before and actually doing the insertion. dammit. + */ + + if (index == -1) { + return add_processor (processor, _processors.end(), err, activation_allowed); + } + + ProcessorList::iterator i = _processors.begin (); + int j = 0; + while (i != _processors.end() && j < index) { + if ((*i)->display_to_user()) { + ++j; + } + + ++i; + } + + return add_processor (processor, i, err, activation_allowed); +} + /** Add a processor to the route. * @param iter an iterator in _processors; the new processor will be inserted immediately before this location. */ diff --git a/libs/gtkmm2ext/gtkmm2ext/dndvbox.h b/libs/gtkmm2ext/gtkmm2ext/dndvbox.h index 7853a5c30d..95a6315de7 100644 --- a/libs/gtkmm2ext/gtkmm2ext/dndvbox.h +++ b/libs/gtkmm2ext/gtkmm2ext/dndvbox.h @@ -45,7 +45,7 @@ template class DnDVBox : public Gtk::EventBox { public: - DnDVBox () : _active (0), _drag_icon (0), _expecting_unwanted_button_event (false), _drag_placeholder (0) + DnDVBox () : _active (0), _drag_icon (0), _expecting_unwanted_button_event (false), _placeholder (0) { _targets.push_back (Gtk::TargetEntry ("processor")); @@ -165,6 +165,59 @@ public: SelectionChanged (); /* EMIT SIGNAL */ } + /** @param y y coordinate. + * @return Pair consisting of the child under y (or 0) and the (fractional) index of the child under y (or -1) + */ + std::pair get_child_at_position (int y) const + { + T* before; + T* after; + + std::pair r; + + r.second = get_children_around_position (y, &before, &r.first, &after); + + return r; + } + + void set_spacing (int s) { + _internal_vbox.set_spacing (s); + } + + void remove_placeholder () + { + if (_placeholder) { + _internal_vbox.remove (*_placeholder); + _placeholder = 0; + } + } + + /** Add a placeholder where a child would be put if it were added at the given y position. + * @param y y position within the DnDVBox. + * @return index of child that the placeholder represents, or -1 if it is at the end of all children. + */ + int add_placeholder (double y) + { + return create_or_update_placeholder (get_child_at_position (y).second); + } + + /** Children have been reordered by a drag */ + sigc::signal Reordered; + + /** A button has been pressed over the widget */ + sigc::signal ButtonPress; + + /** A button has been release over the widget */ + sigc::signal ButtonRelease; + + /** A child has been dropped onto this DnDVBox from another one; + * Parameters are the source DnDVBox, our child which the other one was dropped on (or 0) and the DragContext. + */ + sigc::signal const & > DropFromAnotherBox; + sigc::signal SelectionChanged; + +private: + /** Look at a y coordinate and find the children below y, and the ones either side. * @param y y position. * @param before Filled in with the child before, or 0. @@ -188,7 +241,8 @@ public: /* top of current child */ double top = 0; /* bottom of current child */ - double bottom = (*j)->widget().get_allocation().get_height (); + Gtk::Allocation const a = (*j)->widget().get_allocation(); + double bottom = a.get_y() + a.get_height(); while (y >= bottom && j != _children.end()) { @@ -199,7 +253,8 @@ public: ++j; if (j != _children.end()) { - bottom += (*j)->widget().get_allocation().get_height (); + Gtk::Allocation const a = (*j)->widget().get_allocation(); + bottom = a.get_y() + a.get_height(); } } @@ -217,41 +272,6 @@ public: return i + ((y - top) / (*at)->widget().get_allocation().get_height()); } - /** @param y y coordinate. - * @return Pair consisting of the child under y (or 0) and the (fractional) index of the child under y (or -1) - */ - std::pair get_child_at_position (int y) const - { - T* before; - T* after; - - std::pair r; - - r.second = get_children_around_position (y, &before, &r.first, &after); - - return r; - } - - void set_spacing (int s) { - _internal_vbox.set_spacing (s); - } - - /** Children have been reordered by a drag */ - sigc::signal Reordered; - - /** A button has been pressed over the widget */ - sigc::signal ButtonPress; - - /** A button has been release over the widget */ - sigc::signal ButtonRelease; - - /** A child has been dropped onto this DnDVBox from another one; - * Parameters are the source DnDVBox, our child which the other one was dropped on (or 0) and the DragContext. - */ - sigc::signal const & > DropFromAnotherBox; - sigc::signal SelectionChanged; - -private: void drag_begin (Glib::RefPtr const & context, T* child) { _drag_child = child; @@ -356,17 +376,27 @@ private: _drag_icon = 0; _drag_child = 0; - remove_drag_placeholder (); + remove_placeholder (); Reordered (); /* EMIT SIGNAL */ } - void remove_drag_placeholder () + /** Insert a placeholder at a given fractional child position, creating it if necessary. + * @param c Fractional child position. + * @return index of child that the placeholder represents, or -1 if it is at the end of all children. + */ + int create_or_update_placeholder (double c) { - if (_drag_placeholder) { - _internal_vbox.remove (*_drag_placeholder); - _drag_placeholder = 0; + if (_placeholder == 0) { + _placeholder = manage (new Gtk::Label ("")); + _internal_vbox.pack_start (*_placeholder, false, false); + _placeholder->show (); } + + /* round up the index, unless we're off the end of the children */ + int const n = c < 0 ? -1 : int (c + 0.5); + _internal_vbox.reorder_child (*_placeholder, n); + return n; } bool drag_motion (Glib::RefPtr const &, int /*x*/, int y, guint) @@ -387,37 +417,23 @@ private: if (top_half && (before == _drag_child || at == _drag_child)) { /* dropping here would have no effect, so remove the visual cue */ - remove_drag_placeholder (); + remove_placeholder (); return false; } if (!top_half && (at == _drag_child || after == _drag_child)) { /* dropping here would have no effect, so remove the visual cue */ - remove_drag_placeholder (); + remove_placeholder (); return false; } - /* the index that the placeholder should be put at */ - int const n = int (c + 0.5); - - if (_drag_placeholder == 0) { - _drag_placeholder = manage (new Gtk::Label ("")); - _internal_vbox.pack_start (*_drag_placeholder, false, false); - _drag_placeholder->show (); - } - - if (c < 0) { - _internal_vbox.reorder_child (*_drag_placeholder, -1); - } else { - _internal_vbox.reorder_child (*_drag_placeholder, n); - } - + create_or_update_placeholder (c); return false; } void drag_leave (Glib::RefPtr const &, guint) { - remove_drag_placeholder (); + remove_placeholder (); } bool button_press (GdkEventButton* ev, T* child) @@ -489,7 +505,6 @@ private: } } - return ButtonPress (ev, child); /* EMIT SIGNAL */ } @@ -566,10 +581,10 @@ private: T* _active; Gtk::Window* _drag_icon; bool _expecting_unwanted_button_event; - /** A blank label used as a placeholder to indicate where a dragged item would - * go if it were dropped now. + /** A blank label used as a placeholder to indicate where an item would + * go if it were dropped or inserted "now". */ - Gtk::Label* _drag_placeholder; + Gtk::Label* _placeholder; /** Our child being dragged, or 0 */ T* _drag_child;