diff options
author | David Robillard <d@drobilla.net> | 2014-01-07 04:53:47 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-01-07 04:53:47 +0000 |
commit | 2aa1cf33b26c76b024913d1994066c627075bbbd (patch) | |
tree | 3b9f05646e069345792ff214bf91ef57eab4927b /src | |
parent | d30907023903bc7b4a2de16d3fe5d7674861e394 (diff) | |
download | ingen-2aa1cf33b26c76b024913d1994066c627075bbbd.tar.gz ingen-2aa1cf33b26c76b024913d1994066c627075bbbd.tar.bz2 ingen-2aa1cf33b26c76b024913d1994066c627075bbbd.zip |
Factor out plugin menu code into a separate class.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@5297 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/GraphCanvas.cpp | 166 | ||||
-rw-r--r-- | src/gui/GraphCanvas.hpp | 32 | ||||
-rw-r--r-- | src/gui/PluginMenu.cpp | 163 | ||||
-rw-r--r-- | src/gui/PluginMenu.hpp | 79 | ||||
-rw-r--r-- | src/gui/wscript | 1 |
5 files changed, 265 insertions, 176 deletions
diff --git a/src/gui/GraphCanvas.cpp b/src/gui/GraphCanvas.cpp index 2ecbec80..bb7c968c 100644 --- a/src/gui/GraphCanvas.cpp +++ b/src/gui/GraphCanvas.cpp @@ -44,6 +44,7 @@ #include "LoadPluginWindow.hpp" #include "NewSubgraphWindow.hpp" #include "NodeModule.hpp" +#include "PluginMenu.hpp" #include "Port.hpp" #include "SubgraphModule.hpp" #include "ThreadedLoader.hpp" @@ -71,7 +72,6 @@ GraphCanvas::GraphCanvas(App& app, , _paste_count(0) , _menu(NULL) , _internal_menu(NULL) - , _classless_menu(NULL) , _plugin_menu(NULL) , _human_names(true) , _show_port_names(true) @@ -191,111 +191,24 @@ GraphCanvas::build_menus() } // Build skeleton LV2 plugin class heirarchy for 'Plugin' menu - if (!_plugin_menu) - build_plugin_menu(); - - // Build (or clear existing) uncategorized (classless, heh) plugins menu - if (_classless_menu) { - _classless_menu->items().clear(); - } else { - _plugin_menu->items().push_back(Gtk::Menu_Helpers::MenuElem("_Uncategorized")); - Gtk::MenuItem* classless_menu_item = &(_plugin_menu->items().back()); - _classless_menu = Gtk::manage(new Gtk::Menu()); - classless_menu_item->set_submenu(*_classless_menu); - _classless_menu->hide(); - } - - // Add known plugins to menu heirarchy - SPtr<const ClientStore::Plugins> plugins = _app.store()->plugins(); - for (const auto& p : *plugins.get()) - add_plugin(p.second); -} - -/** Recursively build the plugin class menu heirarchy rooted at - * @a plugin class into @a menu - */ -size_t -GraphCanvas::build_plugin_class_menu( - Gtk::Menu* menu, - const LilvPluginClass* plugin_class, - const LilvPluginClasses* classes, - const LV2Children& children, - std::set<const char*>& ancestors) -{ - size_t num_items = 0; - const LilvNode* class_uri = lilv_plugin_class_get_uri(plugin_class); - const char* class_uri_str = lilv_node_as_string(class_uri); - - const std::pair<LV2Children::const_iterator, LV2Children::const_iterator> kids - = children.equal_range(class_uri_str); - - if (kids.first == children.end()) - return 0; - - // Add submenus - ancestors.insert(class_uri_str); - for (LV2Children::const_iterator i = kids.first; i != kids.second; ++i) { - const LilvPluginClass* c = i->second; - const char* sub_label_str = lilv_node_as_string(lilv_plugin_class_get_label(c)); - const char* sub_uri_str = lilv_node_as_string(lilv_plugin_class_get_uri(c)); - if (ancestors.find(sub_uri_str) != ancestors.end()) { - _app.log().warn(fmt("Infinite LV2 class recursion: %1% <: %2%\n") - % class_uri_str % sub_uri_str); - return 0; - } - - Gtk::Menu_Helpers::MenuElem menu_elem = Gtk::Menu_Helpers::MenuElem( - std::string("_") + sub_label_str); - menu->items().push_back(menu_elem); - Gtk::MenuItem* menu_item = &(menu->items().back()); - - Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); - menu_item->set_submenu(*submenu); - - size_t num_child_items = build_plugin_class_menu( - submenu, c, classes, children, ancestors); - - _class_menus.insert(make_pair(sub_uri_str, MenuRecord(menu_item, submenu))); - if (num_child_items == 0) - menu_item->hide(); - - ++num_items; - } - ancestors.erase(class_uri_str); - - return num_items; -} - -void -GraphCanvas::build_plugin_menu() -{ - if (_plugin_menu) { - _plugin_menu->items().clear(); - } else { + if (!_plugin_menu) { + _plugin_menu = Gtk::manage(new PluginMenu(*_app.world())); _menu->items().push_back( Gtk::Menu_Helpers::ImageMenuElem( "_Plugin", *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); Gtk::MenuItem* plugin_menu_item = &(_menu->items().back()); - _plugin_menu = Gtk::manage(new Gtk::Menu()); plugin_menu_item->set_submenu(*_plugin_menu); _menu->reorder_child(*plugin_menu_item, 5); + _plugin_menu->signal_load_plugin.connect( + sigc::mem_fun(this, &GraphCanvas::load_plugin)); } - const LilvWorld* world = PluginModel::lilv_world(); - const LilvPluginClass* lv2_plugin = lilv_world_get_plugin_class(world); - const LilvPluginClasses* classes = lilv_world_get_plugin_classes(world); - - LV2Children children; - LILV_FOREACH(plugin_classes, i, classes) { - const LilvPluginClass* c = lilv_plugin_classes_get(classes, i); - const LilvNode* p = lilv_plugin_class_get_parent_uri(c); - if (!p) - p = lilv_plugin_class_get_uri(lv2_plugin); - children.insert(make_pair(lilv_node_as_string(p), c)); + // Add known plugins to menu heirarchy + SPtr<const ClientStore::Plugins> plugins = _app.store()->plugins(); + for (const auto& p : *plugins.get()) { + add_plugin(p.second); } - std::set<const char*> ancestors; - build_plugin_class_menu(_plugin_menu, lv2_plugin, classes, children, ancestors); } void @@ -351,61 +264,15 @@ GraphCanvas::show_port_names(bool b) } void -GraphCanvas::add_plugin_to_menu(Gtk::Menu* menu, SPtr<PluginModel> p) -{ - bool is_graph = false; - if (p->lilv_plugin()) { - const URIs& uris = _app.uris(); - LilvWorld* lworld = _app.world()->lilv_world(); - LilvNode* ingen_Graph = lilv_new_uri(lworld, uris.ingen_Graph.c_str()); - LilvNode* rdf_type = lilv_new_uri(lworld, uris.rdf_type.c_str()); - - is_graph = lilv_world_ask(_app.world()->lilv_world(), - lilv_plugin_get_uri(p->lilv_plugin()), - rdf_type, - ingen_Graph); - - lilv_node_free(rdf_type); - lilv_node_free(ingen_Graph); - } - - menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - std::string("_") + p->human_name() + (is_graph ? " ⚙" : ""), - sigc::bind(sigc::mem_fun(this, &GraphCanvas::load_plugin), p))); - - if (!menu->is_visible()) { - menu->show(); - } -} - -void GraphCanvas::add_plugin(SPtr<PluginModel> p) { - typedef ClassMenus::iterator iterator; if (_internal_menu && p->type() == Plugin::Internal) { - add_plugin_to_menu(_internal_menu, p); - } else if (_plugin_menu && p->type() == Plugin::LV2 && p->lilv_plugin()) { - if (lilv_plugin_is_replaced(p->lilv_plugin())) { - //info << (boost::format("[Menu] LV2 plugin <%s> hidden") % p->uri()) << endl; - return; - } - - const LilvPluginClass* pc = lilv_plugin_get_class(p->lilv_plugin()); - const LilvNode* class_uri = lilv_plugin_class_get_uri(pc); - const char* class_uri_str = lilv_node_as_string(class_uri); - - pair<iterator, iterator> range = _class_menus.equal_range(class_uri_str); - if (range.first == _class_menus.end() || range.first == range.second - || range.first->second.menu == _plugin_menu) { - // Add to uncategorized plugin menu - add_plugin_to_menu(_classless_menu, p); - } else { - // For each menu that represents plugin's class (possibly several) - for (iterator i = range.first; i != range.second ; ++i) { - add_plugin_to_menu(i->second.menu, p); - } - } + _internal_menu->items().push_back( + Gtk::Menu_Helpers::MenuElem( + std::string("_") + p->human_name(), + sigc::bind(sigc::mem_fun(this, &GraphCanvas::load_plugin), p))); + } else if (_plugin_menu) { + _plugin_menu->add_plugin(p); } } @@ -898,8 +765,7 @@ GraphCanvas::get_initial_data(Resource::Graph ctx) void GraphCanvas::menu_load_plugin() { - _app.window_factory()->present_load_plugin( - _graph, get_initial_data()); + _app.window_factory()->present_load_plugin(_graph, get_initial_data()); } void diff --git a/src/gui/GraphCanvas.hpp b/src/gui/GraphCanvas.hpp index 6592106b..47d267d2 100644 --- a/src/gui/GraphCanvas.hpp +++ b/src/gui/GraphCanvas.hpp @@ -39,6 +39,7 @@ namespace Client { class GraphModel; } namespace GUI { class NodeModule; +class PluginMenu; /** Graph canvas widget. * @@ -86,9 +87,10 @@ private: const std::string& sym_base, std::string& sym, const std::string& name_base, std::string& name); - void menu_add_port( - const std::string& sym_base, const std::string& name_base, - const Raul::URI& type, bool is_output); + void menu_add_port(const std::string& sym_base, + const std::string& name_base, + const Raul::URI& type, + bool is_output); void menu_load_plugin(); void menu_new_graph(); @@ -98,21 +100,10 @@ private: void build_menus(); - void build_internal_menu(); - void build_classless_menu(); - void add_plugin_to_menu(Gtk::Menu* menu, SPtr<Client::PluginModel> p); - void auto_menu_position(int& x, int& y, bool& push_in); typedef std::multimap<const std::string, const LilvPluginClass*> LV2Children; - void build_plugin_menu(); - size_t build_plugin_class_menu(Gtk::Menu* menu, - const LilvPluginClass* plugin_class, - const LilvPluginClasses* classes, - const LV2Children& children, - std::set<const char*>& ancestors); - Node::Properties get_initial_data(Resource::Graph ctx=Resource::Graph::DEFAULT); Ganv::Port* get_port_view(SPtr<Client::PortModel> port); @@ -139,20 +130,9 @@ private: // Track pasted objects so they can be selected when they arrive std::set<Raul::Path> _pastees; - struct MenuRecord { - MenuRecord(Gtk::MenuItem* i, Gtk::Menu* m) : item(i), menu(m) {} - Gtk::MenuItem* item; - Gtk::Menu* menu; - }; - - typedef std::multimap<const std::string, MenuRecord> ClassMenus; - - ClassMenus _class_menus; - Gtk::Menu* _menu; Gtk::Menu* _internal_menu; - Gtk::Menu* _classless_menu; - Gtk::Menu* _plugin_menu; + PluginMenu* _plugin_menu; Gtk::MenuItem* _menu_add_audio_input; Gtk::MenuItem* _menu_add_audio_output; Gtk::MenuItem* _menu_add_control_input; diff --git a/src/gui/PluginMenu.cpp b/src/gui/PluginMenu.cpp new file mode 100644 index 00000000..65dac0a1 --- /dev/null +++ b/src/gui/PluginMenu.cpp @@ -0,0 +1,163 @@ +/* + This file is part of Ingen. + Copyright 2014 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "PluginMenu.hpp" +#include "ingen/Log.hpp" +#include "ingen/client/PluginModel.hpp" + +namespace Ingen { +namespace GUI { + +PluginMenu::PluginMenu(Ingen::World& world) + : _world(world) + , _classless_menu(NULL, NULL) +{ + const LilvWorld* lworld = _world.lilv_world(); + const LilvPluginClass* lv2_plugin = lilv_world_get_plugin_class(lworld); + const LilvPluginClasses* classes = lilv_world_get_plugin_classes(lworld); + + LV2Children children; + LILV_FOREACH(plugin_classes, i, classes) { + const LilvPluginClass* c = lilv_plugin_classes_get(classes, i); + const LilvNode* p = lilv_plugin_class_get_parent_uri(c); + if (!p) { + p = lilv_plugin_class_get_uri(lv2_plugin); + } + children.insert(std::make_pair(lilv_node_as_string(p), c)); + } + + std::set<const char*> ancestors; + build_plugin_class_menu(this, lv2_plugin, classes, children, ancestors); + + items().push_back(Gtk::Menu_Helpers::MenuElem("_Uncategorized")); + _classless_menu.item = &(items().back()); + _classless_menu.menu = new Gtk::Menu(); + _classless_menu.item->set_submenu(*_classless_menu.menu); + _classless_menu.item->hide(); +} + +void +PluginMenu::add_plugin(SPtr<Client::PluginModel> p) +{ + typedef ClassMenus::iterator iterator; + + if (!p->lilv_plugin() || lilv_plugin_is_replaced(p->lilv_plugin())) { + return; + } + + const LilvPluginClass* pc = lilv_plugin_get_class(p->lilv_plugin()); + const LilvNode* class_uri = lilv_plugin_class_get_uri(pc); + const char* class_uri_str = lilv_node_as_string(class_uri); + + std::pair<iterator, iterator> range = _class_menus.equal_range(class_uri_str); + if (range.first == _class_menus.end() || range.first == range.second + || range.first->second.menu == this) { + // Add to uncategorized plugin menu + add_plugin_to_menu(_classless_menu, p); + } else { + // For each menu that represents plugin's class (possibly several) + for (iterator i = range.first; i != range.second ; ++i) { + add_plugin_to_menu(i->second, p); + } + } +} + +size_t +PluginMenu::build_plugin_class_menu(Gtk::Menu* menu, + const LilvPluginClass* plugin_class, + const LilvPluginClasses* classes, + const LV2Children& children, + std::set<const char*>& ancestors) +{ + size_t num_items = 0; + const LilvNode* class_uri = lilv_plugin_class_get_uri(plugin_class); + const char* class_uri_str = lilv_node_as_string(class_uri); + + const std::pair<LV2Children::const_iterator, LV2Children::const_iterator> kids + = children.equal_range(class_uri_str); + + if (kids.first == children.end()) + return 0; + + // Add submenus + ancestors.insert(class_uri_str); + for (LV2Children::const_iterator i = kids.first; i != kids.second; ++i) { + const LilvPluginClass* c = i->second; + const char* sub_label_str = lilv_node_as_string(lilv_plugin_class_get_label(c)); + const char* sub_uri_str = lilv_node_as_string(lilv_plugin_class_get_uri(c)); + if (ancestors.find(sub_uri_str) != ancestors.end()) { + _world.log().warn(fmt("Infinite LV2 class recursion: %1% <: %2%\n") + % class_uri_str % sub_uri_str); + return 0; + } + + Gtk::Menu_Helpers::MenuElem menu_elem = Gtk::Menu_Helpers::MenuElem( + std::string("_") + sub_label_str); + menu->items().push_back(menu_elem); + Gtk::MenuItem* menu_item = &(menu->items().back()); + + Gtk::Menu* submenu = new Gtk::Menu(); + menu_item->set_submenu(*submenu); + + size_t num_child_items = build_plugin_class_menu( + submenu, c, classes, children, ancestors); + + _class_menus.insert(std::make_pair(sub_uri_str, MenuRecord(menu_item, submenu))); + if (num_child_items == 0) { + menu_item->hide(); + } + + ++num_items; + } + ancestors.erase(class_uri_str); + + return num_items; +} + +void +PluginMenu::add_plugin_to_menu(MenuRecord& menu, SPtr<Client::PluginModel> p) +{ + const URIs& uris = _world.uris(); + LilvWorld* lworld = _world.lilv_world(); + LilvNode* ingen_Graph = lilv_new_uri(lworld, uris.ingen_Graph.c_str()); + LilvNode* rdf_type = lilv_new_uri(lworld, uris.rdf_type.c_str()); + + bool is_graph = lilv_world_ask(lworld, + lilv_plugin_get_uri(p->lilv_plugin()), + rdf_type, + ingen_Graph); + + menu.menu->items().push_back( + Gtk::Menu_Helpers::MenuElem( + std::string("_") + p->human_name() + (is_graph ? " ⚙" : ""), + sigc::bind(sigc::mem_fun(this, &PluginMenu::load_plugin), p))); + + if (!menu.item->is_visible()) { + menu.item->show(); + } + + lilv_node_free(rdf_type); + lilv_node_free(ingen_Graph); +} + +void +PluginMenu::load_plugin(WPtr<Client::PluginModel> weak_plugin) +{ + signal_load_plugin.emit(weak_plugin); +} + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PluginMenu.hpp b/src/gui/PluginMenu.hpp new file mode 100644 index 00000000..54624104 --- /dev/null +++ b/src/gui/PluginMenu.hpp @@ -0,0 +1,79 @@ +/* + This file is part of Ingen. + Copyright 2014 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INGEN_GUI_PLUGINMENU_HPP +#define INGEN_GUI_PLUGINMENU_HPP + +#include <map> +#include <set> +#include <string> + +#include <gtkmm/menu.h> + +#include "ingen/World.hpp" +#include "ingen/types.hpp" +#include "lilv/lilv.h" + +namespace Ingen { + +namespace Client { class PluginModel; } + +namespace GUI { + +/** + Type-hierarchical plugin menu. + + @ingroup GUI +*/ +class PluginMenu : public Gtk::Menu +{ +public: + PluginMenu(Ingen::World& world); + + void add_plugin(SPtr<Client::PluginModel> p); + + sigc::signal< void, WPtr<Client::PluginModel> > signal_load_plugin; + +private: + struct MenuRecord { + MenuRecord(Gtk::MenuItem* i, Gtk::Menu* m) : item(i), menu(m) {} + Gtk::MenuItem* item; + Gtk::Menu* menu; + }; + + typedef std::multimap<const std::string, const LilvPluginClass*> LV2Children; + typedef std::multimap<const std::string, MenuRecord> ClassMenus; + + /// Recursively add hierarchy rooted at @a plugin_class to @a menu. + size_t build_plugin_class_menu(Gtk::Menu* menu, + const LilvPluginClass* plugin_class, + const LilvPluginClasses* classes, + const LV2Children& children, + std::set<const char*>& ancestors); + + void add_plugin_to_menu(MenuRecord& menu, SPtr<Client::PluginModel> p); + + void load_plugin(WPtr<Client::PluginModel> weak_plugin); + + Ingen::World& _world; + MenuRecord _classless_menu; + ClassMenus _class_menus; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // INGEN_GUI_PLUGINMENU_HPP diff --git a/src/gui/wscript b/src/gui/wscript index 982c93d8..62731abe 100644 --- a/src/gui/wscript +++ b/src/gui/wscript @@ -44,6 +44,7 @@ def build(bld): NodeMenu.cpp NodeModule.cpp ObjectMenu.cpp + PluginMenu.cpp Port.cpp PortMenu.cpp PortPropertiesWindow.cpp |