diff options
Diffstat (limited to 'src/gui/URIEntry.cpp')
-rw-r--r-- | src/gui/URIEntry.cpp | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/gui/URIEntry.cpp b/src/gui/URIEntry.cpp new file mode 100644 index 00000000..7cd15cd8 --- /dev/null +++ b/src/gui/URIEntry.cpp @@ -0,0 +1,172 @@ +/* + This file is part of Ingen. + Copyright 2015 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 <unordered_map> + +#include "App.hpp" +#include "RDFS.hpp" +#include "URIEntry.hpp" + +namespace Ingen { +namespace GUI { + +URIEntry::URIEntry(App* app, + const std::set<Raul::URI>& types, + const std::string& value) + : Gtk::HBox(false, 4) + , _app(app) + , _types(types) + , _menu_button(Gtk::manage(new Gtk::Button("≡"))) + , _entry(Gtk::manage(new Gtk::Entry())) +{ + pack_start(*_entry, true, true); + pack_start(*_menu_button, false, true); + + _entry->set_text(value); + + _menu_button->signal_event().connect( + sigc::mem_fun(this, &URIEntry::menu_button_event)); +} + +Gtk::Menu* +URIEntry::build_value_menu() +{ + World* world = _app->world(); + LilvWorld* lworld = world->lilv_world(); + Gtk::Menu* menu = new Gtk::Menu(); + + LilvNode* rdf_type = lilv_new_uri(lworld, LILV_NS_RDF "type"); + LilvNode* rdfs_Class = lilv_new_uri(lworld, LILV_NS_RDFS "Class"); + LilvNode* rdfs_subClassOf = lilv_new_uri(lworld, LILV_NS_RDFS "subClassOf"); + + RDFS::Objects values = RDFS::instances(world, _types); + + for (const auto& v : values) { + const LilvNode* inst = lilv_new_uri(lworld, v.second.c_str()); + std::string label = v.first; + if (label.empty()) { + // No label, show raw URI + label = lilv_node_as_string(inst); + } + + if (lilv_world_ask(world->lilv_world(), inst, rdf_type, rdfs_Class)) { + // This value is a class... + if (!lilv_world_ask(lworld, inst, rdfs_subClassOf, NULL)) { + // ... which is not a subclass, add menu + add_class_menu_item(menu, inst, label); + } + } else { + // Value is not a class, add item + menu->items().push_back( + Gtk::Menu_Helpers::MenuElem( + label, + sigc::bind(sigc::mem_fun(this, &URIEntry::uri_chosen), + std::string(lilv_node_as_uri(inst))))); + _app->set_tooltip(&menu->items().back(), inst); + } + } + + return menu; +} + +Gtk::Menu* +URIEntry::build_subclass_menu(const LilvNode* klass) +{ + World* world = _app->world(); + + LilvNode* rdfs_subClassOf = lilv_new_uri( + world->lilv_world(), LILV_NS_RDFS "subClassOf"); + LilvNodes* subclasses = lilv_world_find_nodes( + world->lilv_world(), NULL, rdfs_subClassOf, klass); + + if (lilv_nodes_size(subclasses) == 0) { + return NULL; + } + + Gtk::Menu* menu = new Gtk::Menu(); + + // Add "header" item for choosing this class itself + add_leaf_menu_item(menu, klass, RDFS::label(world, klass)); + menu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); + + // Put subclasses in a map keyed by label (to sort menu) + std::map<std::string, const LilvNode*> entries; + LILV_FOREACH(nodes, s, subclasses) { + const LilvNode* node = lilv_nodes_get(subclasses, s); + entries.insert(std::make_pair(RDFS::label(world, node), node)); + } + + // Add an item (possibly with a submenu) for each subclass + for (const auto& e : entries) { + add_class_menu_item(menu, e.second, e.first); + } + lilv_nodes_free(subclasses); + + return menu; +} + +void +URIEntry::add_leaf_menu_item(Gtk::Menu* menu, + const LilvNode* node, + const std::string& label) +{ + menu->items().push_back( + Gtk::Menu_Helpers::MenuElem( + label, + sigc::bind(sigc::mem_fun(this, &URIEntry::uri_chosen), + std::string(lilv_node_as_uri(node))))); + + _app->set_tooltip(&menu->items().back(), node); +} + +void +URIEntry::add_class_menu_item(Gtk::Menu* menu, + const LilvNode* klass, + const std::string& label) +{ + Gtk::Menu* submenu = build_subclass_menu(klass); + + if (submenu) { + menu->items().push_back(Gtk::Menu_Helpers::MenuElem(label)); + menu->items().back().set_submenu(*Gtk::manage(submenu)); + } else { + add_leaf_menu_item(menu, klass, label); + } + + _app->set_tooltip(&menu->items().back(), klass); +} + +void +URIEntry::uri_chosen(const std::string& uri) +{ + _entry->set_text(uri); +} + +bool +URIEntry::menu_button_event(GdkEvent* ev) +{ + if (ev->type != GDK_BUTTON_PRESS) { + return false; + } + + Gtk::Menu* menu = Gtk::manage(build_value_menu()); + menu->popup(ev->button.button, ev->button.time); + + return true; +} + +} // namespace GUI +} // namespace Ingen |