summaryrefslogtreecommitdiffstats
path: root/src/gui/PropertiesWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/PropertiesWindow.cpp')
-rw-r--r--src/gui/PropertiesWindow.cpp202
1 files changed, 158 insertions, 44 deletions
diff --git a/src/gui/PropertiesWindow.cpp b/src/gui/PropertiesWindow.cpp
index 64dea4d1..1eb92b10 100644
--- a/src/gui/PropertiesWindow.cpp
+++ b/src/gui/PropertiesWindow.cpp
@@ -50,7 +50,8 @@ PropertiesWindow::PropertiesWindow(BaseObjectType* cobject,
xml->get_widget("properties_scrolledwindow", _scrolledwindow);
xml->get_widget("properties_table", _table);
xml->get_widget("properties_key_combo", _key_combo);
- xml->get_widget("properties_value_combo", _value_combo);
+ xml->get_widget("properties_value_entry", _value_entry);
+ xml->get_widget("properties_value_button", _value_button);
xml->get_widget("properties_add_button", _add_button);
xml->get_widget("properties_cancel_button", _cancel_button);
xml->get_widget("properties_apply_button", _apply_button);
@@ -60,13 +61,12 @@ PropertiesWindow::PropertiesWindow(BaseObjectType* cobject,
_key_combo->set_model(_key_store);
_key_combo->pack_start(_combo_columns.label_col);
- _value_store = Gtk::ListStore::create(_combo_columns);
- _value_combo->set_model(_value_store);
- _value_combo->pack_start(_combo_columns.label_col);
-
_key_combo->signal_changed().connect(
sigc::mem_fun(this, &PropertiesWindow::key_changed));
+ _value_button->signal_event().connect(
+ sigc::mem_fun(this, &PropertiesWindow::value_clicked));
+
_add_button->signal_clicked().connect(
sigc::mem_fun(this, &PropertiesWindow::add_clicked));
@@ -87,7 +87,7 @@ PropertiesWindow::reset()
_property_removed_connection.disconnect();
_key_store->clear();
- _value_store->clear();
+ _value_entry->set_text("");
_records.clear();
_model.reset();
@@ -113,7 +113,7 @@ PropertiesWindow::add_property(const Raul::URI& uri, const Atom& value)
_table->property_n_rows() = n_rows;
// Column 0: Property
- LilvNode* prop = lilv_new_uri(world->lilv_world(), uri.c_str());
+ LilvNode* prop = lilv_new_uri(world->lilv_world(), uri.c_str());
Glib::ustring lab_text = RDFS::label(world, prop);
if (lab_text.empty()) {
lab_text = world->rdf_world()->prefixes().qualify(uri);
@@ -122,9 +122,9 @@ PropertiesWindow::add_property(const Raul::URI& uri, const Atom& value)
+ lab_text + "</a>";
Gtk::Label* lab = manage(new Gtk::Label(lab_text, 1.0, 0.5));
lab->set_use_markup(true);
+ set_tooltip(lab, prop);
_table->attach(*lab, 0, 1, n_rows, n_rows + 1,
Gtk::FILL|Gtk::SHRINK, Gtk::SHRINK);
- lilv_node_free(prop);
// Column 1: Value
Gtk::Alignment* align = manage(new Gtk::Alignment(0.0, 0.5, 1.0, 0.0));
@@ -133,12 +133,15 @@ PropertiesWindow::add_property(const Raul::URI& uri, const Atom& value)
present->set_active(true);
if (val_widget) {
align->add(*val_widget);
+ set_tooltip(val_widget, prop);
}
_table->attach(*align, 1, 2, n_rows, n_rows + 1,
Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK);
_table->attach(*present, 2, 3, n_rows, n_rows + 1,
Gtk::FILL, Gtk::SHRINK);
_records.insert(make_pair(uri, Record(value, align, n_rows, present)));
+
+ lilv_node_free(prop);
}
/** Set the node this window is associated with.
@@ -297,7 +300,7 @@ PropertiesWindow::on_show()
}
req = _table->size_request();
- width = std::max(width, req.width + 128);
+ width = 1.6 * std::max(width, req.width + 128);
height += req.height;
set_default_size(width + WIN_PAD, height + WIN_PAD);
@@ -386,68 +389,179 @@ bad_type:
return;
}
+std::string
+PropertiesWindow::active_property() const
+{
+ const Gtk::ListStore::iterator iter = _key_combo->get_active();
+ if (!iter) {
+ return "";
+ }
+
+ Glib::ustring prop_uri = (*iter)[_combo_columns.uri_col];
+ return prop_uri;
+}
+
void
PropertiesWindow::key_changed()
{
+ /* TODO: Clear value? Build new selector widget, once one for things other than
+ URIs actually exists. At the moment, clicking the menu button will
+ generate the appropriate menu anyway. */
+}
+
+void
+PropertiesWindow::set_tooltip(Gtk::Widget* widget, const LilvNode* node)
+{
+ const Glib::ustring comment = RDFS::comment(_app->world(), node);
+ if (!comment.empty()) {
+ widget->set_tooltip_text(comment);
+ }
+}
+
+void
+PropertiesWindow::add_class_menu_item(Gtk::Menu* menu, const LilvNode* klass)
+{
+ const Glib::ustring label = RDFS::label(_app->world(), klass);
+ Gtk::Menu* submenu = build_subclass_menu(klass);
+
+ if (submenu) {
+ menu->items().push_back(Gtk::Menu_Helpers::MenuElem(label));
+ Gtk::MenuItem* menu_item = &(menu->items().back());
+ set_tooltip(menu_item, klass);
+ menu_item->set_submenu(*submenu);
+ } else {
+ menu->items().push_back(
+ Gtk::Menu_Helpers::MenuElem(
+ label,
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::uri_chosen),
+ std::string(lilv_node_as_uri(klass)))));
+ }
+ set_tooltip(&(menu->items().back()), klass);
+}
+
+Gtk::Menu*
+PropertiesWindow::build_subclass_menu(const LilvNode* klass)
+{
World* world = _app->world();
- _value_store->clear();
+ 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);
- const Gtk::ListStore::iterator iter = _key_combo->get_active();
- if (!iter) {
- return;
+ if (lilv_nodes_size(subclasses) == 0) {
+ return NULL;
}
- const Gtk::ListStore::Row row = *iter;
- Glib::ustring prop_uri = row[_combo_columns.uri_col];
- if (prop_uri.empty()) {
- return;
+ const Glib::ustring label = RDFS::label(world, klass);
+ Gtk::Menu* menu = new Gtk::Menu();
+
+ // Add "header" item for choosing this class itself
+ menu->items().push_back(
+ Gtk::Menu_Helpers::MenuElem(
+ label,
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::uri_chosen),
+ std::string(lilv_node_as_uri(klass)))));
+ menu->items().push_back(Gtk::Menu_Helpers::SeparatorElem());
+ set_tooltip(&(menu->items().back()), klass);
+
+ // Add an item (and maybe submenu) for each subclass
+ LILV_FOREACH(nodes, s, subclasses) {
+ add_class_menu_item(menu, lilv_nodes_get(subclasses, s));
}
+ lilv_nodes_free(subclasses);
+ return menu;
+}
- LilvNode* rdfs_range = lilv_new_uri(
- world->lilv_world(), LILV_NS_RDFS "range");
- LilvNode* prop = lilv_new_uri(
- world->lilv_world(), prop_uri.c_str());
+void
+PropertiesWindow::build_value_menu(Gtk::Menu* menu, const LilvNodes* ranges)
+{
+ World* world = _app->world();
+ LilvWorld* lworld = world->lilv_world();
- LilvNodes* range = lilv_world_find_nodes(
- world->lilv_world(), prop, rdfs_range, NULL);
+ 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");
- // Get all classes in the range of this property (including sub-classes)
- URISet ranges;
- LILV_FOREACH(nodes, r, range) {
- ranges.insert(Raul::URI(lilv_node_as_string(lilv_nodes_get(range, r))));
+ LILV_FOREACH(nodes, r, ranges) {
+ const LilvNode* klass = lilv_nodes_get(ranges, r);
+ if (!lilv_node_is_uri(klass)) {
+ continue;
+ }
+ const char* uri = lilv_node_as_string(klass);
+
+ // Add items for instances of this class
+ RDFS::URISet ranges_uris;
+ ranges_uris.insert(Raul::URI(uri));
+ RDFS::Objects values = RDFS::instances(world, ranges_uris);
+ for (const auto& v : values) {
+ const LilvNode* inst = lilv_new_uri(lworld, v.first.c_str());
+ Glib::ustring label = RDFS::label(world, inst);
+ if (label.empty()) {
+ label = lilv_node_as_string(inst);
+ }
+
+ if (lilv_world_ask(world->lilv_world(), inst, rdf_type, rdfs_Class)) {
+ if (!lilv_world_ask(lworld, inst, rdfs_subClassOf, NULL) ||
+ lilv_world_ask(lworld, inst, rdfs_subClassOf, inst)) {
+ add_class_menu_item(menu, inst);
+ }
+ } else {
+ menu->items().push_back(
+ Gtk::Menu_Helpers::MenuElem(
+ label,
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::uri_chosen),
+ std::string(lilv_node_as_uri(inst)))));
+ set_tooltip(&(menu->items().back()), inst);
+ }
+ }
}
- RDFS::classes(world, ranges, false);
+}
- // Get all objects in range
- RDFS::Objects values = RDFS::instances(world, ranges);
+void
+PropertiesWindow::uri_chosen(const std::string& uri)
+{
+ _value_entry->set_text(uri);
+}
- // Fill value selector with suitable objects
- for (const auto& v : values) {
- if (!v.second.empty()) {
- Gtk::ListStore::iterator vi = _value_store->append();
- Gtk::ListStore::Row vrow = *vi;
+bool
+PropertiesWindow::value_clicked(GdkEvent* ev)
+{
+ if (ev->type != GDK_BUTTON_PRESS) {
+ return false;
+ }
- vrow[_combo_columns.uri_col] = v.first;
- vrow[_combo_columns.label_col] = v.second;
- }
+ // Get currently selected property (key) to add
+ const std::string prop_uri = active_property();
+ if (prop_uri.empty()) {
+ return false;
}
- lilv_node_free(prop);
- lilv_node_free(rdfs_range);
+ World* world = _app->world();
+
+ LilvNode* rdfs_range = lilv_new_uri(
+ world->lilv_world(), LILV_NS_RDFS "range");
+
+ LilvNode* prop = lilv_new_uri(world->lilv_world(), prop_uri.c_str());
+ LilvNodes* ranges = lilv_world_find_nodes(
+ world->lilv_world(), prop, rdfs_range, NULL);
+
+ Gtk::Menu* menu = new Gtk::Menu();
+ build_value_menu(menu, ranges);
+ menu->popup(ev->button.button, ev->button.time);
+ return true;
}
void
PropertiesWindow::add_clicked()
{
- if (!_key_combo->get_active() || !_value_combo->get_active()) {
+ if (!_key_combo->get_active() || _value_entry->get_text().empty()) {
return;
}
const Gtk::ListStore::Row krow = *(_key_combo->get_active());
- const Gtk::ListStore::Row vrow = *(_value_combo->get_active());
- Glib::ustring key_uri = krow[_combo_columns.uri_col];
- Glib::ustring value_uri = vrow[_combo_columns.uri_col];
+ const Glib::ustring key_uri = krow[_combo_columns.uri_col];
+ const Glib::ustring value_uri = _value_entry->get_text();
Atom value = _app->forge().alloc_uri(value_uri);