/* This file is part of Ingen. Copyright 2014-2015 David Robillard 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 . */ #include "PluginMenu.hpp" #include "ingen/Log.hpp" #include "ingen/client/PluginModel.hpp" #include namespace ingen { namespace gui { PluginMenu::PluginMenu(ingen::World& world) : _world(world) , _classless_menu(nullptr, nullptr) { clear(); } void PluginMenu::clear() { 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); // Empty completely _classless_menu = MenuRecord(nullptr, nullptr); _class_menus.clear(); items().clear(); // Build skeleton 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.emplace(lilv_node_as_string(p), c); } std::set 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 = Gtk::manage(new Gtk::Menu()); _classless_menu.item->set_submenu(*_classless_menu.menu); _classless_menu.item->hide(); } void PluginMenu::add_plugin(SPtr p) { using iterator = ClassMenus::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 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 (auto 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& 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 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("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.emplace(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 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 weak_plugin) { signal_load_plugin.emit(weak_plugin); } } // namespace gui } // namespace ingen