#include <gtkmm2ext/tearoff.h>
#include <gtkmm2ext/window_title.h>
+#include "ardour/amp.h"
#include "ardour/debug.h"
#include "ardour/midi_track.h"
#include "ardour/plugin_manager.h"
group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
group_display_frame.add (group_display_vbox);
- rhs_pane1.pack1 (track_display_frame);
- rhs_pane1.pack2 (group_display_frame);
-
- list_vpacker.pack_start (rhs_pane1, true, true);
+ favorite_plugins_model = PluginTreeStore::create (favorite_plugins_columns);
+ favorite_plugins_display.set_model (favorite_plugins_model);
+ favorite_plugins_display.append_column (_("Favorite Plugins"), favorite_plugins_columns.name);
+ favorite_plugins_display.set_name ("EditGroupList");
+ favorite_plugins_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
+ favorite_plugins_display.set_reorderable (false);
+ favorite_plugins_display.set_headers_visible (true);
+ favorite_plugins_display.set_rules_hint (true);
+ favorite_plugins_display.set_can_focus (false);
+ favorite_plugins_display.add_object_drag (favorite_plugins_columns.plugin.index(), "PluginPresetPtr");
+ favorite_plugins_display.set_drag_column (favorite_plugins_columns.name.index());
+ favorite_plugins_display.signal_row_activated().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_activated));
+ favorite_plugins_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_button_press), false);
+ favorite_plugins_display.signal_drop.connect (sigc::mem_fun (*this, &Mixer_UI::plugin_drop));
+
+ favorite_plugins_scroller.add (favorite_plugins_display);
+ favorite_plugins_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
+
+ favorite_plugins_frame.set_name ("BaseFrame");
+ favorite_plugins_frame.set_shadow_type (Gtk::SHADOW_IN);
+ favorite_plugins_frame.add (favorite_plugins_scroller);
+
+ rhs_pane1.pack1 (favorite_plugins_frame, false, true);
+ rhs_pane1.pack2 (track_display_frame);
+ rhs_pane2.pack1 (rhs_pane1);
+ rhs_pane2.pack2 (group_display_frame);
+
+ list_vpacker.pack_start (rhs_pane2, true, true);
global_hpacker.pack_start (scroller, true, true);
global_hpacker.pack_start (out_packer, false, false, 0);
rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
static_cast<Gtk::Paned*> (&rhs_pane1)));
+ rhs_pane2.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
+ static_cast<Gtk::Paned*> (&rhs_pane2)));
list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
static_cast<Gtk::Paned*> (&list_hpane)));
group_display_button_label.show();
group_display_button.show();
group_display_scroller.show();
+ favorite_plugins_scroller.show();
group_display_vbox.show();
group_display_frame.show();
+ favorite_plugins_frame.show();
rhs_pane1.show();
+ rhs_pane2.show();
strip_packer.show();
out_packer.show();
list_hpane.show();
group_display.show();
+ favorite_plugins_display.show();
MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
#ifndef DEFER_PLUGIN_SELECTOR_LOAD
_plugin_selector = new PluginSelector (PluginManager::instance ());
+#else
+#error implement deferred Plugin-Favorite list
#endif
+ PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
+ PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
+ ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
}
Mixer_UI::~Mixer_UI ()
show_window();
}
+ refill_favorite_plugins();
start_updating ();
}
move (m_root_x, m_root_y);
}
- void
+void
Mixer_UI::get_window_pos_and_size ()
{
get_position(m_root_x, m_root_y);
get_size(m_width, m_height);
}
+struct PluginStateSorter {
+public:
+ bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
+ std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
+ std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
+ if (aiter != _user.end() && biter != _user.end()) {
+ return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
+ }
+ if (aiter != _user.end()) {
+ return true;
+ }
+ if (biter != _user.end()) {
+ return false;
+ }
+ return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
+ }
+
+ PluginStateSorter(std::list<std::string> user) : _user (user) {}
+private:
+ std::list<std::string> _user;
+};
+
int
Mixer_UI::set_state (const XMLNode& node)
{
}
+ XMLNode* plugin_order;
+ if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
+ store_current_favorite_order ();
+ std::list<string> order;
+ const XMLNodeList& kids = plugin_order->children("PluginInfo");
+ XMLNodeConstIterator i;
+ for (i = kids.begin(); i != kids.end(); ++i) {
+ if ((prop = (*i)->property ("unique-id"))) {
+ std::string unique_id = prop->value();
+ order.push_back (unique_id);
+ if ((prop = (*i)->property ("expanded"))) {
+ favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
+ }
+ }
+ }
+ PluginStateSorter cmp (order);
+ favorite_order.sort (cmp);
+ sync_treeview_from_favorite_order ();
+ }
+
return 0;
}
snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
+ snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane2)->gobj()));
+ geometry->add_property(X_("mixer_rhs_pane2_pos"), string(buf));
snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
node->add_property ("maximised", _maximised ? "yes" : "no");
+ store_current_favorite_order ();
+ XMLNode* plugin_order = new XMLNode ("PluginOrder");
+ int cnt = 0;
+ for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
+ XMLNode* p = new XMLNode ("PluginInfo");
+ p->add_property ("sort", cnt);
+ p->add_property ("unique-id", (*i)->unique_id);
+ if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
+ p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
+ }
+ plugin_order->add_child_nocopy (*p);
+ ;
+ }
+ node->add_child_nocopy (*plugin_order);
return *node;
}
{
int pos;
XMLProperty* prop = 0;
- char buf[32];
XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
XMLNode* geometry;
int height;
if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
pos = height / 3;
- snprintf (buf, sizeof(buf), "%d", pos);
} else {
pos = atoi (prop->value());
}
rhs_pane1.set_position (pos);
}
+ } else if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
+ if (done[1]) {
+ return;
+ }
+
+ if (!geometry || (prop = geometry->property("mixer-rhs-pane2-pos")) == 0) {
+ pos = 2 * height / 3;
+ } else {
+ pos = atoi (prop->value());
+ }
+
+ if ((done[1] = GTK_WIDGET(rhs_pane2.gobj())->allocation.height > pos)) {
+ rhs_pane2.set_position (pos);
+ }
} else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
if (done[2]) {
}
if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
- pos = 75;
- snprintf (buf, sizeof(buf), "%d", pos);
+ pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
} else {
pos = max (36, atoi (prop->value ()));
}
monitor_section_detached ();
out_packer.remove (_monitor_section->tearoff());
_monitor_section->set_session (0);
+ delete _monitor_section;
+ _monitor_section = 0;
}
}
Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
act->set_sensitive (false);
}
+
+void
+Mixer_UI::store_current_favorite_order ()
+{
+ typedef Gtk::TreeModel::Children type_children;
+ type_children children = favorite_plugins_model->children();
+ favorite_order.clear();
+ for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
+ {
+ Gtk::TreeModel::Row row = *iter;
+ ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
+ favorite_order.push_back (ppp->_pip);
+ std::string name = row[favorite_plugins_columns.name];
+ favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
+ }
+}
+
+void
+Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
+{
+ PluginManager& manager (PluginManager::instance());
+ for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
+ if (manager.get_status (*i) != PluginManager::Favorite) {
+ continue;
+ }
+ result.push_back (*i);
+ }
+}
+
+struct PluginCustomSorter {
+public:
+ bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
+ PluginInfoList::const_iterator aiter = std::find(_user.begin(), _user.end(), a);
+ PluginInfoList::const_iterator biter = std::find(_user.begin(), _user.end(), b);
+
+ if (aiter != _user.end() && biter != _user.end()) {
+ return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
+ }
+ if (aiter != _user.end()) {
+ return true;
+ }
+ if (biter != _user.end()) {
+ return false;
+ }
+ return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
+ }
+ PluginCustomSorter(PluginInfoList user) : _user (user) {}
+private:
+ PluginInfoList _user;
+};
+
+void
+Mixer_UI::refill_favorite_plugins ()
+{
+ PluginInfoList plugs;
+ PluginManager& mgr (PluginManager::instance());
+
+#ifdef LV2_SUPPORT
+ refiller (plugs, mgr.lv2_plugin_info ());
+#endif
+#ifdef WINDOWS_VST_SUPPORT
+ refiller (plugs, mgr.windows_vst_plugin_info ());
+#endif
+#ifdef LXVST_SUPPORT
+ refiller (plugs, mgr.lxvst_plugin_info ());
+#endif
+#ifdef AUDIOUNIT_SUPPORT
+ refiller (plugs, mgr.au_plugin_info ());
+#endif
+ refiller (plugs, mgr.ladspa_plugin_info ());
+
+ store_current_favorite_order ();
+
+ PluginCustomSorter cmp (favorite_order);
+ plugs.sort (cmp);
+
+ favorite_order = plugs;
+
+ sync_treeview_from_favorite_order ();
+}
+
+void
+Mixer_UI::sync_treeview_from_favorite_order ()
+{
+ favorite_plugins_model->clear ();
+ for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
+ PluginInfoPtr pip = (*i);
+
+ TreeModel::Row newrow = *(favorite_plugins_model->append());
+ newrow[favorite_plugins_columns.name] = (*i)->name;
+ newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
+ if (!_session) {
+ continue;
+ }
+
+ PluginPtr plugin = (*i)->load (*_session);
+
+ vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets();
+ for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
+ Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
+ child_row[favorite_plugins_columns.name] = (*j).label;
+ child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
+ }
+ if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
+ if (favorite_ui_state[pip->unique_id]) {
+ favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
+ }
+ }
+ }
+}
+
+void
+Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
+{
+ using namespace Gtk::Menu_Helpers;
+
+ Gtk::Menu* m = manage (new Menu);
+ MenuList& items = m->items ();
+
+ if (_selection.routes.empty()) {
+ items.push_back (MenuElem (_("No Track/Bus is selected.")));
+ } else {
+ items.push_back (MenuElem (_("Add at the top"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
+ items.push_back (MenuElem (_("Add Pre-Fader"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
+ items.push_back (MenuElem (_("Add Post-Fader"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
+ items.push_back (MenuElem (_("Add at the end"),
+ sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
+ }
+
+ items.push_back (SeparatorElem());
+
+ items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
+
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (ppp && ppp->_preset.valid) {
+ items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
+ }
+
+ m->popup (ev->button, ev->time);
+}
+
+bool
+Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
+{
+ if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
+ TreeModel::Path path;
+ TreeViewColumn* column;
+ int cellx, celly;
+ if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
+ Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
+ if (selection) {
+ selection->unselect_all();
+ selection->select(path);
+ }
+ }
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (ppp) {
+ popup_note_context_menu (ev);
+ }
+ }
+ return false;
+}
+
+
+PluginPresetPtr
+Mixer_UI::selected_plugin ()
+{
+ Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
+ if (!selection) {
+ return PluginPresetPtr();
+ }
+ Gtk::TreeModel::iterator iter = selection->get_selected();
+ if (!iter) {
+ return PluginPresetPtr();
+ }
+ return (*iter)[favorite_plugins_columns.plugin];
+}
+
+void
+Mixer_UI::add_selected_processor (ProcessorPosition pos)
+{
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (ppp) {
+ add_favorite_processor (ppp, pos);
+ }
+}
+
+void
+Mixer_UI::delete_selected_preset ()
+{
+ if (!_session) {
+ return;
+ }
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (!ppp || !ppp->_preset.valid) {
+ return;
+ }
+ PluginPtr plugin = ppp->_pip->load (*_session);
+ plugin->get_presets();
+ plugin->remove_preset (ppp->_preset.label);
+}
+
+void
+Mixer_UI::remove_selected_from_favorites ()
+{
+ ARDOUR::PluginPresetPtr ppp = selected_plugin();
+ if (!ppp) {
+ return;
+ }
+ PluginManager::PluginStatusType status = PluginManager::Normal;
+ PluginManager& manager (PluginManager::instance());
+
+ manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
+ manager.save_statuses ();
+}
+
+void
+Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
+{
+ TreeIter iter;
+ if (!(iter = favorite_plugins_model->get_iter (path))) {
+ return;
+ }
+ ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
+ add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
+}
+
+void
+Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
+{
+ if (!_session || _selection.routes.empty()) {
+ return;
+ }
+
+ PluginInfoPtr pip = ppp->_pip;
+ for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
+ boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
+ if (!rt) { continue; }
+
+ PluginPtr p = pip->load (*_session);
+ if (!p) { continue; }
+
+ if (ppp->_preset.valid) {
+ p->load_preset (ppp->_preset);
+ }
+
+ Route::ProcessorStreams err;
+ boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
+
+ switch (pos) {
+ case AddTop:
+ rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
+ break;
+ case AddPreFader:
+ rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
+ break;
+ case AddPostFader:
+ {
+ int idx = 0;
+ for (;;++idx) {
+ boost::shared_ptr<Processor> np = rt->nth_processor (idx);
+ if (!np || boost::dynamic_pointer_cast<Amp> (np)) {
+ break;
+ }
+ }
+ rt->add_processor_by_index (processor, ++idx, &err, Config->get_new_plugins_active ());
+ }
+ break;
+ case AddBottom:
+ rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
+ break;
+ }
+ }
+}
+
+bool
+PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
+{
+ if (data.get_target() != "GTK_TREE_MODEL_ROW") {
+ return false;
+ }
+ Gtk::TreeModel::Path _dest = dest; // un const
+ const bool is_child = _dest.up (); // explicit bool for clang
+ if (!is_child || _dest.empty ()) {
+ return true;
+ }
+ return false;
+}
+
+void
+Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
+{
+ if (data.get_target() != "PluginPresetPtr") {
+ return;
+ }
+ if (data.get_length() != sizeof (PluginPresetPtr)) {
+ return;
+ }
+ const void *d = data.get_data();
+ const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
+
+ PluginManager::PluginStatusType status = PluginManager::Favorite;
+ PluginManager& manager (PluginManager::instance());
+
+ manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
+ manager.save_statuses ();
+}