summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ingen/URIMap.hpp1
-rw-r--r--ingen/URIs.hpp1
-rw-r--r--src/URIs.cpp1
-rw-r--r--src/gui/App.cpp10
-rw-r--r--src/gui/App.hpp15
-rw-r--r--src/gui/Port.cpp8
-rw-r--r--src/gui/PropertiesWindow.cpp485
-rw-r--r--src/gui/PropertiesWindow.hpp29
-rw-r--r--src/gui/RDFS.cpp52
-rw-r--r--src/gui/RDFS.hpp25
-rw-r--r--src/gui/URIEntry.cpp172
-rw-r--r--src/gui/URIEntry.hpp70
-rw-r--r--src/gui/ingen_gui.ui30
-rw-r--r--src/gui/wscript1
14 files changed, 551 insertions, 349 deletions
diff --git a/ingen/URIMap.hpp b/ingen/URIMap.hpp
index d17c42e1..eb5a3833 100644
--- a/ingen/URIMap.hpp
+++ b/ingen/URIMap.hpp
@@ -38,6 +38,7 @@ public:
virtual ~URIMap() {}
uint32_t map_uri(const char* uri);
+ uint32_t map_uri(const std::string& uri) { return map_uri(uri.c_str()); }
const char* unmap_uri(uint32_t urid) const;
class Feature : public LV2Features::Feature {
diff --git a/ingen/URIs.hpp b/ingen/URIs.hpp
index ee41d80c..e5ea4d49 100644
--- a/ingen/URIs.hpp
+++ b/ingen/URIs.hpp
@@ -154,6 +154,7 @@ public:
const Quark pset_preset;
const Quark pprops_logarithmic;
const Quark rdf_type;
+ const Quark rdfs_Class;
const Quark rdfs_seeAlso;
const Quark rsz_minimumSize;
const Quark time_Position;
diff --git a/src/URIs.cpp b/src/URIs.cpp
index e5d4d9ff..5ff80e64 100644
--- a/src/URIs.cpp
+++ b/src/URIs.cpp
@@ -144,6 +144,7 @@ URIs::URIs(Forge& f, URIMap* map)
, pset_preset (forge, map, LV2_PRESETS__preset)
, pprops_logarithmic (forge, map, LV2_PORT_PROPS__logarithmic)
, rdf_type (forge, map, NS_RDF "type")
+ , rdfs_Class (forge, map, NS_RDFS "Class")
, rdfs_seeAlso (forge, map, NS_RDFS "seeAlso")
, rsz_minimumSize (forge, map, LV2_RESIZE_PORT__minimumSize)
, time_Position (forge, map, LV2_TIME__Position)
diff --git a/src/gui/App.cpp b/src/gui/App.cpp
index 1bb9ba4d..65f5c90a 100644
--- a/src/gui/App.cpp
+++ b/src/gui/App.cpp
@@ -44,6 +44,7 @@
#include "MessagesWindow.hpp"
#include "NodeModule.hpp"
#include "Port.hpp"
+#include "RDFS.hpp"
#include "Style.hpp"
#include "SubgraphModule.hpp"
#include "ThreadedLoader.hpp"
@@ -236,6 +237,15 @@ App::set_property(const Raul::URI& subject,
}
void
+App::set_tooltip(Gtk::Widget* widget, const LilvNode* node)
+{
+ const std::string comment = RDFS::comment(_world, node);
+ if (!comment.empty()) {
+ widget->set_tooltip_text(comment);
+ }
+}
+
+void
App::property_change(const Raul::URI& subject,
const Raul::URI& key,
const Atom& value)
diff --git a/src/gui/App.hpp b/src/gui/App.hpp
index 0e3a4079..db7af77b 100644
--- a/src/gui/App.hpp
+++ b/src/gui/App.hpp
@@ -1,6 +1,6 @@
/*
This file is part of Ingen.
- Copyright 2007-2012 David Robillard <http://drobilla.net/>
+ Copyright 2007-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
@@ -17,10 +17,8 @@
#ifndef INGEN_GUI_APP_HPP
#define INGEN_GUI_APP_HPP
-#include <cassert>
#include <map>
#include <string>
-#include <utility>
#include <gtkmm/aboutdialog.h>
#include <gtkmm/main.h>
@@ -31,25 +29,27 @@
#include "ingen/World.hpp"
#include "ingen/ingen.h"
#include "ingen/types.hpp"
+#include "lilv/lilv.h"
#include "raul/Deletable.hpp"
#include "raul/URI.hpp"
namespace Ingen {
+
class Interface;
class Log;
class Port;
class Serialiser;
class World;
+
namespace Client {
+
class ClientStore;
class GraphModel;
class PluginModel;
class PortModel;
class SigClientInterface;
-}
-}
-namespace Ingen {
+}
namespace GUI {
@@ -101,6 +101,9 @@ public:
const Raul::URI& key,
const Atom& value);
+ /** Set the tooltip for a widget from its RDF documentation. */
+ void set_tooltip(Gtk::Widget* widget, const LilvNode* node);
+
uint32_t sample_rate() const;
ConnectWindow* connect_window() const { return _connect_window; }
diff --git a/src/gui/Port.cpp b/src/gui/Port.cpp
index 8076eb60..e7f32fbe 100644
--- a/src/gui/Port.cpp
+++ b/src/gui/Port.cpp
@@ -284,14 +284,14 @@ Port::build_uri_menu()
// Add a menu item for each such class
for (const auto& v : values) {
- if (!v.second.empty()) {
- Glib::ustring label = world->rdf_world()->prefixes().qualify(v.first)
- + " - " + v.second;
+ if (!v.first.empty()) {
+ const std::string qname = world->rdf_world()->prefixes().qualify(v.second);
+ const std::string label = qname + " - " + v.first;
menu->items().push_back(Gtk::Menu_Helpers::MenuElem(label));
Gtk::MenuItem* menu_item = &(menu->items().back());
menu_item->signal_activate().connect(
sigc::bind(sigc::mem_fun(this, &Port::on_uri_activated),
- v.first));
+ v.second));
}
}
diff --git a/src/gui/PropertiesWindow.cpp b/src/gui/PropertiesWindow.cpp
index 1eb92b10..6db19e65 100644
--- a/src/gui/PropertiesWindow.cpp
+++ b/src/gui/PropertiesWindow.cpp
@@ -31,6 +31,7 @@
#include "App.hpp"
#include "PropertiesWindow.hpp"
#include "RDFS.hpp"
+#include "URIEntry.hpp"
using namespace std;
@@ -45,13 +46,13 @@ typedef std::set<Raul::URI> URISet;
PropertiesWindow::PropertiesWindow(BaseObjectType* cobject,
const Glib::RefPtr<Gtk::Builder>& xml)
: Window(cobject)
+ , _value_type(0)
{
xml->get_widget("properties_vbox", _vbox);
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_entry", _value_entry);
- xml->get_widget("properties_value_button", _value_button);
+ xml->get_widget("properties_value_bin", _value_bin);
xml->get_widget("properties_add_button", _add_button);
xml->get_widget("properties_cancel_button", _cancel_button);
xml->get_widget("properties_apply_button", _apply_button);
@@ -64,9 +65,6 @@ PropertiesWindow::PropertiesWindow(BaseObjectType* cobject,
_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 +85,6 @@ PropertiesWindow::reset()
_property_removed_connection.disconnect();
_key_store->clear();
- _value_entry->set_text("");
_records.clear();
_model.reset();
@@ -105,7 +102,7 @@ PropertiesWindow::present(SPtr<const ObjectModel> model)
}
void
-PropertiesWindow::add_property(const Raul::URI& uri, const Atom& value)
+PropertiesWindow::add_property(const Raul::URI& key, const Atom& value)
{
World* world = _app->world();
@@ -113,33 +110,36 @@ 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());
- Glib::ustring lab_text = RDFS::label(world, prop);
- if (lab_text.empty()) {
- lab_text = world->rdf_world()->prefixes().qualify(uri);
+ LilvNode* prop = lilv_new_uri(world->lilv_world(), key.c_str());
+ std::string name = RDFS::label(world, prop);
+ if (name.empty()) {
+ name = world->rdf_world()->prefixes().qualify(key);
}
- lab_text = Glib::ustring("<a href=\"") + uri + "\">"
- + 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::Label* label = new Gtk::Label(
+ std::string("<a href=\"") + key + "\">" + name + "</a>", 1.0, 0.5);
+ label->set_use_markup(true);
+ _app->set_tooltip(label, prop);
+ _table->attach(*Gtk::manage(label), 0, 1, n_rows, n_rows + 1,
Gtk::FILL|Gtk::SHRINK, Gtk::SHRINK);
// Column 1: Value
- Gtk::Alignment* align = manage(new Gtk::Alignment(0.0, 0.5, 1.0, 0.0));
+ Gtk::Alignment* align = manage(new Gtk::Alignment(0.0, 0.5, 1.0, 1.0));
Gtk::CheckButton* present = manage(new Gtk::CheckButton());
- Gtk::Widget* val_widget = create_value_widget(uri, value);
- present->set_active(true);
+ const char* type = _app->world()->uri_map().unmap_uri(value.type());
+ Gtk::Widget* val_widget = create_value_widget(key, type, value);
+
+ present->set_active();
if (val_widget) {
- align->add(*val_widget);
- set_tooltip(val_widget, prop);
+ align->add(*Gtk::manage(val_widget));
+ _app->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)));
+ _records.insert(make_pair(key, Record(value, align, n_rows, present)));
+ _table->show_all();
lilv_node_free(prop);
}
@@ -157,30 +157,22 @@ PropertiesWindow::set_object(SPtr<const ObjectModel> model)
World* world = _app->world();
- LilvNode* rdfs_range = lilv_new_uri(
- world->lilv_world(), LILV_NS_RDFS "range");
LilvNode* rdf_type = lilv_new_uri(
world->lilv_world(), LILV_NS_RDF "type");
// Populate key combo
- const URISet props = RDFS::properties(world, model);
+ const URISet props = RDFS::properties(world, model);
+ std::map<std::string, Raul::URI> entries;
for (const auto& p : props) {
- LilvNode* prop = lilv_new_uri(world->lilv_world(), p.c_str());
- const Glib::ustring label = RDFS::label(world, prop);
+ LilvNode* prop = lilv_new_uri(world->lilv_world(), p.c_str());
+ const std::string label = RDFS::label(world, prop);
if (label.empty()) {
continue;
}
- // Get all classes in the range of this property (including sub-classes)
- LilvNodes* range = lilv_world_find_nodes(
- world->lilv_world(), prop, rdfs_range, NULL);
- URISet ranges;
- LILV_FOREACH(nodes, r, range) {
- ranges.insert(Raul::URI(lilv_node_as_string(lilv_nodes_get(range, r))));
- }
- RDFS::classes(world, ranges, false);
-
- bool show = false;
+ // Get all classes in the range of this property (including subclasses)
+ URISet ranges = RDFS::range(world, prop, true);
+ bool show = false;
for (const auto& r : ranges) {
LilvNode* range = lilv_new_uri(world->lilv_world(), r.c_str());
LilvNodes* objects = lilv_world_find_nodes(
@@ -191,21 +183,24 @@ PropertiesWindow::set_object(SPtr<const ObjectModel> model)
lilv_nodes_free(objects);
lilv_node_free(range);
if (show) {
- break; // At least one appliable object
+ break; // At least one applicable object
}
}
if (show || ranges.empty()) {
- Gtk::ListStore::iterator ki = _key_store->append();
- Gtk::ListStore::Row row = *ki;
- row[_combo_columns.uri_col] = p;
- row[_combo_columns.label_col] = label;
+ entries.insert(std::make_pair(label, p));
}
lilv_node_free(prop);
}
- lilv_node_free(rdfs_range);
+ for (const auto& e : entries) {
+ Gtk::ListStore::iterator ki = _key_store->append();
+ Gtk::ListStore::Row row = *ki;
+ row[_combo_columns.uri_col] = e.second;
+ row[_combo_columns.label_col] = e.first;
+ }
+
lilv_node_free(rdf_type);
for (const auto& p : model->properties()) {
@@ -215,71 +210,98 @@ PropertiesWindow::set_object(SPtr<const ObjectModel> model)
_table->show_all();
_property_connection = model->signal_property().connect(
- sigc::mem_fun(this, &PropertiesWindow::property_changed));
+ sigc::mem_fun(this, &PropertiesWindow::add_property));
_property_removed_connection = model->signal_property_removed().connect(
- sigc::mem_fun(this, &PropertiesWindow::property_removed));
+ sigc::mem_fun(this, &PropertiesWindow::remove_property));
}
Gtk::Widget*
-PropertiesWindow::create_value_widget(const Raul::URI& uri, const Atom& value)
+PropertiesWindow::create_value_widget(const Raul::URI& key,
+ const char* type_uri,
+ const Atom& value)
{
- Ingen::Forge& forge = _app->forge();
- if (value.type() == forge.Int) {
+ if (!type_uri || !Raul::URI::is_valid(type_uri)) {
+ return NULL;
+ }
+
+ const Raul::URI type(type_uri);
+ Ingen::World* world = _app->world();
+ LilvWorld* lworld = world->lilv_world();
+
+ if (type == _app->uris().atom_Int) {
Gtk::SpinButton* widget = manage(new Gtk::SpinButton(0.0, 0));
widget->property_numeric() = true;
widget->set_range(INT_MIN, INT_MAX);
widget->set_increments(1, 10);
- widget->set_value(value.get<int32_t>());
+ if (value.is_valid()) {
+ widget->set_value(value.get<int32_t>());
+ }
widget->signal_value_changed().connect(
- sigc::bind(sigc::mem_fun(this, &PropertiesWindow::value_edited),
- uri));
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key));
return widget;
- } else if (value.type() == forge.Float) {
+ } else if (type == _app->uris().atom_Float) {
Gtk::SpinButton* widget = manage(new Gtk::SpinButton(0.0, 4));
widget->property_numeric() = true;
widget->set_snap_to_ticks(false);
widget->set_range(-FLT_MAX, FLT_MAX);
- widget->set_value(value.get<float>());
widget->set_increments(0.1, 1.0);
+ if (value.is_valid()) {
+ widget->set_value(value.get<float>());
+ }
widget->signal_value_changed().connect(
- sigc::bind(sigc::mem_fun(this, &PropertiesWindow::value_edited),
- uri));
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key));
return widget;
- } else if (value.type() == forge.Bool) {
+ } else if (type == _app->uris().atom_Bool) {
Gtk::CheckButton* widget = manage(new Gtk::CheckButton());
- widget->set_active(value.get<int32_t>());
+ if (value.is_valid()) {
+ widget->set_active(value.get<int32_t>());
+ }
widget->signal_toggled().connect(
- sigc::bind(sigc::mem_fun(this, &PropertiesWindow::value_edited),
- uri));
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key));
return widget;
- } else if (value.type() == forge.URI) {
+ } else if (type == _app->uris().atom_String) {
Gtk::Entry* widget = manage(new Gtk::Entry());
- widget->set_text(value.ptr<char>());
+ if (value.is_valid()) {
+ widget->set_text(value.ptr<char>());
+ }
widget->signal_changed().connect(
- sigc::bind(sigc::mem_fun(this, &PropertiesWindow::value_edited),
- uri));
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key));
return widget;
- } else if (value.type() == forge.URID) {
- const char* val_uri = _app->world()->uri_map().unmap_uri(value.get<int32_t>());
- Gtk::Entry* widget = manage(new Gtk::Entry());
- if (val_uri) {
- widget->set_text(val_uri);
- }
+ } else if (type == _app->uris().atom_URID) {
+ const char* str = (value.is_valid()
+ ? world->uri_map().unmap_uri(value.get<int32_t>())
+ : "");
+
+ LilvNode* pred = lilv_new_uri(lworld, key.c_str());
+ URISet ranges = RDFS::range(world, pred, true);
+ URIEntry* widget = manage(new URIEntry(_app, ranges, str ? str : ""));
widget->signal_changed().connect(
- sigc::bind(sigc::mem_fun(this, &PropertiesWindow::value_edited),
- uri));
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key));
+ lilv_node_free(pred);
return widget;
- } else if (value.type() == forge.String) {
- Gtk::Entry* widget = manage(new Gtk::Entry());
- widget->set_text(value.ptr<char>());
+ }
+
+ LilvNode* type_node = lilv_new_uri(lworld, type.c_str());
+ LilvNode* rdfs_Class = lilv_new_uri(lworld, LILV_NS_RDFS "Class");
+ const bool is_class = RDFS::is_a(world, type_node, rdfs_Class);
+ lilv_node_free(rdfs_Class);
+ lilv_node_free(type_node);
+
+ if (type == _app->uris().atom_URI ||
+ type == _app->uris().rdfs_Class ||
+ is_class) {
+ LilvNode* pred = lilv_new_uri(lworld, key.c_str());
+ URISet ranges = RDFS::range(world, pred, true);
+ const char* str = value.is_valid() ? value.ptr<const char>() : "";
+ URIEntry* widget = manage(new URIEntry(_app, ranges, str));
widget->signal_changed().connect(
- sigc::bind(sigc::mem_fun(this, &PropertiesWindow::value_edited),
- uri));
+ sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key));
+ lilv_node_free(pred);
return widget;
}
- _app->log().error(fmt("Unable to create widget for value %1% type %2%\n")
- % forge.str(value) % value.type());
+ _app->log().error(fmt("No widget for value type %1%\n") % type);
+
return NULL;
}
@@ -291,16 +313,17 @@ PropertiesWindow::on_show()
int width = 0;
int height = 0;
- Gtk::Requisition req;
for (const auto& c : _vbox->children()) {
- req = c.get_widget()->size_request();
+ const Gtk::Requisition& req = c.get_widget()->size_request();
+
width = std::max(width, req.width);
height += req.height + VBOX_PAD;
}
- req = _table->size_request();
- width = 1.6 * std::max(width, req.width + 128);
+ const Gtk::Requisition& req = _table->size_request();
+
+ width = 1.2 * std::max(width, req.width + 128);
height += req.height;
set_default_size(width + WIN_PAD, height + WIN_PAD);
@@ -309,30 +332,30 @@ PropertiesWindow::on_show()
}
void
-PropertiesWindow::property_changed(const Raul::URI& predicate,
- const Atom& value)
+PropertiesWindow::change_property(const Raul::URI& key, const Atom& value)
{
- Records::iterator r = _records.find(predicate);
+ Records::iterator r = _records.find(key);
if (r == _records.end()) {
- add_property(predicate, value);
+ add_property(key, value);
_table->show_all();
return;
}
- Record& record = r->second;
- Gtk::Widget* value_widget = create_value_widget(predicate, value);
+ Record& record = r->second;
+ const char* type = _app->world()->uri_map().unmap_uri(value.type());
+ Gtk::Widget* val_widget = create_value_widget(key, type, value);
- record.value_widget->remove();
- if (value_widget) {
- record.value_widget->add(*value_widget);
- value_widget->show();
+ if (val_widget) {
+ record.value_widget->remove();
+ record.value_widget->add(*Gtk::manage(val_widget));
+ val_widget->show_all();
}
+
record.value = value;
}
void
-PropertiesWindow::property_removed(const Raul::URI& predicate,
- const Atom& value)
+PropertiesWindow::remove_property(const Raul::URI& key, const Atom& value)
{
// Bleh, there doesn't seem to be an easy way to remove a Gtk::Table row...
_records.clear();
@@ -346,229 +369,135 @@ PropertiesWindow::property_removed(const Raul::URI& predicate,
_table->show_all();
}
-void
-PropertiesWindow::value_edited(const Raul::URI& predicate)
+Atom
+PropertiesWindow::get_value(LV2_URID type, Gtk::Widget* value_widget)
{
- Records::iterator r = _records.find(predicate);
- if (r == _records.end()) {
- _app->log().error(fmt("Unknown property `%1%' edited\n")
- % predicate);
- return;
- }
+ Forge& forge = _app->forge();
- Forge& forge = _app->forge();
- Record& record = r->second;
- LV2_URID type = record.value.type();
if (type == forge.Int) {
- Gtk::SpinButton* widget = dynamic_cast<Gtk::SpinButton*>(record.value_widget->get_child());
- if (!widget) goto bad_type;
- record.value = _app->forge().make(widget->get_value_as_int());
+ Gtk::SpinButton* spin = dynamic_cast<Gtk::SpinButton*>(value_widget);
+ if (spin) {
+ return _app->forge().make(spin->get_value_as_int());
+ }
} else if (type == forge.Float) {
- Gtk::SpinButton* widget = dynamic_cast<Gtk::SpinButton*>(record.value_widget->get_child());
- if (!widget) goto bad_type;
- record.value = _app->forge().make(static_cast<float>(widget->get_value()));
+ Gtk::SpinButton* spin = dynamic_cast<Gtk::SpinButton*>(value_widget);
+ if (spin) {
+ return _app->forge().make(static_cast<float>(spin->get_value()));
+ }
} else if (type == forge.Bool) {
- Gtk::CheckButton* widget = dynamic_cast<Gtk::CheckButton*>(record.value_widget->get_child());
- if (!widget) goto bad_type;
- record.value = _app->forge().make(widget->get_active());
+ Gtk::CheckButton* check = dynamic_cast<Gtk::CheckButton*>(value_widget);
+ if (check) {
+ return _app->forge().make(check->get_active());
+ }
} else if (type == forge.URI) {
- Gtk::Entry* widget = dynamic_cast<Gtk::Entry*>(record.value_widget->get_child());
- if (!widget) goto bad_type;
- record.value = _app->forge().alloc_uri(widget->get_text());
+ URIEntry* uri_entry = dynamic_cast<URIEntry*>(value_widget);
+ if (uri_entry) {
+ return _app->forge().alloc_uri(uri_entry->get_text());
+ }
+ } else if (type == forge.URID) {
+ URIEntry* uri_entry = dynamic_cast<URIEntry*>(value_widget);
+ if (uri_entry) {
+ return _app->forge().make_urid(
+ _app->world()->uri_map().map_uri(uri_entry->get_text()));
+ }
} else if (type == forge.String) {
- Gtk::Entry* widget = dynamic_cast<Gtk::Entry*>(record.value_widget->get_child());
- if (!widget) goto bad_type;
- record.value = _app->forge().alloc(widget->get_text());
+ Gtk::Entry* entry = dynamic_cast<Gtk::Entry*>(value_widget);
+ if (entry) {
+ return _app->forge().alloc(entry->get_text());
+ }
}
- return;
-
-bad_type:
- _app->log().error(fmt("Property `%1%' value widget has wrong type\n")
- % predicate);
- return;
-}
-
-std::string
-PropertiesWindow::active_property() const
-{
- const Gtk::ListStore::iterator iter = _key_combo->get_active();
- if (!iter) {
- return "";
+ URIEntry* uri_entry = dynamic_cast<URIEntry*>(value_widget);
+ if (uri_entry) {
+ return _app->forge().alloc_uri(uri_entry->get_text());
}
- Glib::ustring prop_uri = (*iter)[_combo_columns.uri_col];
- return prop_uri;
+ return Atom();
}
void
-PropertiesWindow::key_changed()
+PropertiesWindow::on_change(const Raul::URI& key)
{
- /* 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);
+ Records::iterator r = _records.find(key);
+ if (r == _records.end()) {
+ return;
}
-}
-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);
+ Record& record = r->second;
+ const Atom value = get_value(record.value.type(),
+ record.value_widget->get_child());
+
+ if (value.is_valid()) {
+ record.value = value;
} 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)))));
+ _app->log().error(fmt("Failed to get `%1%' value from widget\n") % key);
}
- set_tooltip(&(menu->items().back()), klass);
}
-Gtk::Menu*
-PropertiesWindow::build_subclass_menu(const LilvNode* klass)
+std::string
+PropertiesWindow::active_key() const
{
- 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;
- }
-
- 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));
+ const Gtk::ListStore::iterator iter = _key_combo->get_active();
+ if (!iter) {
+ return "";
}
- lilv_nodes_free(subclasses);
- return menu;
-}
-
-void
-PropertiesWindow::build_value_menu(Gtk::Menu* menu, const LilvNodes* ranges)
-{
- World* world = _app->world();
- LilvWorld* lworld = world->lilv_world();
-
- 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");
- 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);
- }
- }
- }
+ Glib::ustring prop_uri = (*iter)[_combo_columns.uri_col];
+ return prop_uri;
}
void
-PropertiesWindow::uri_chosen(const std::string& uri)
-{
- _value_entry->set_text(uri);
-}
-
-bool
-PropertiesWindow::value_clicked(GdkEvent* ev)
+PropertiesWindow::key_changed()
{
- if (ev->type != GDK_BUTTON_PRESS) {
- return false;
+ if (!_key_combo->get_active()) {
+ _value_bin->remove();
+ return;
}
- // Get currently selected property (key) to add
- const std::string prop_uri = active_property();
- if (prop_uri.empty()) {
- return false;
+ LilvWorld* lworld = _app->world()->lilv_world();
+ const Gtk::ListStore::Row key_row = *(_key_combo->get_active());
+ const Glib::ustring key_uri = key_row[_combo_columns.uri_col];
+ LilvNode* prop = lilv_new_uri(lworld, key_uri.c_str());
+
+ // Try to create a value widget in the range of this property
+ const URISet ranges = RDFS::range(_app->world(), prop, true);
+ for (const auto& r : ranges) {
+ Gtk::Widget* value_widget = create_value_widget(
+ Raul::URI(key_uri), r.c_str(), Atom());
+
+ if (value_widget) {
+ _add_button->set_sensitive(true);
+ _value_type = _app->world()->uri_map().map_uri(r);
+ _value_bin->remove();
+ _value_bin->add(*Gtk::manage(value_widget));
+ _value_bin->show_all();
+ break;
+ }
}
- 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;
+ lilv_node_free(prop);
}
void
PropertiesWindow::add_clicked()
{
- if (!_key_combo->get_active() || _value_entry->get_text().empty()) {
+ if (!_key_combo->get_active() || !_value_type || !_value_bin->get_child()) {
return;
}
- const Gtk::ListStore::Row krow = *(_key_combo->get_active());
- 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);
-
- Resource::Properties properties;
- properties.insert(make_pair(Raul::URI(key_uri.c_str()),
- Resource::Property(value)));
- _app->interface()->put(_model->uri(), properties);
+ // Get selected key URI
+ const Gtk::ListStore::Row key_row = *(_key_combo->get_active());
+ const Glib::ustring key_uri = key_row[_combo_columns.uri_col];
+
+ // Try to get value from value widget
+ const Atom& value = get_value(_value_type, _value_bin->get_child());
+ if (value.is_valid()) {
+ // Send property to engine
+ Resource::Properties properties;
+ properties.insert(make_pair(Raul::URI(key_uri.c_str()),
+ Resource::Property(value)));
+ _app->interface()->put(_model->uri(), properties);
+ }
}
void
diff --git a/src/gui/PropertiesWindow.hpp b/src/gui/PropertiesWindow.hpp
index 7111269a..15842c48 100644
--- a/src/gui/PropertiesWindow.hpp
+++ b/src/gui/PropertiesWindow.hpp
@@ -76,28 +76,23 @@ private:
Gtk::TreeModelColumn<Glib::ustring> uri_col;
};
- std::string active_property() const;
+ void add_property(const Raul::URI& key, const Atom& value);
+ void change_property(const Raul::URI& key, const Atom& value);
+ void remove_property(const Raul::URI& key, const Atom& value);
+ void on_change(const Raul::URI& key);
- void add_property(const Raul::URI& uri,
- const Atom& value);
+ Gtk::Widget* create_value_widget(const Raul::URI& key,
+ const char* type_uri,
+ const Atom& value = Atom());
- Gtk::Widget* create_value_widget(const Raul::URI& uri,
- const Atom& value);
-
- Gtk::Menu* build_subclass_menu(const LilvNode* klass);
- void add_class_menu_item(Gtk::Menu* menu, const LilvNode* klass);
- void build_value_menu(Gtk::Menu* menu, const LilvNodes* ranges);
- void set_tooltip(Gtk::Widget* widget, const LilvNode* node);
+ Atom get_value(LV2_URID type, Gtk::Widget* value_widget);
void reset();
void on_show();
- void property_changed(const Raul::URI& predicate, const Atom& value);
- void property_removed(const Raul::URI& predicate, const Atom& value);
- void value_edited(const Raul::URI& predicate);
+ std::string active_key() const;
+
void key_changed();
- void uri_chosen(const std::string& uri);
- bool value_clicked(GdkEvent* ev);
void add_clicked();
void cancel_clicked();
void apply_clicked();
@@ -115,8 +110,8 @@ private:
Gtk::ScrolledWindow* _scrolledwindow;
Gtk::Table* _table;
Gtk::ComboBox* _key_combo;
- Gtk::Entry* _value_entry;
- Gtk::Button* _value_button;
+ LV2_URID _value_type;
+ Gtk::Bin* _value_bin;
Gtk::Button* _add_button;
Gtk::Button* _cancel_button;
Gtk::Button* _apply_button;
diff --git a/src/gui/RDFS.cpp b/src/gui/RDFS.cpp
index a24ae002..458c0bc5 100644
--- a/src/gui/RDFS.cpp
+++ b/src/gui/RDFS.cpp
@@ -25,7 +25,7 @@ namespace Ingen {
namespace GUI {
namespace RDFS {
-Glib::ustring
+std::string
label(World* world, const LilvNode* node)
{
LilvNode* rdfs_label = lilv_new_uri(
@@ -34,14 +34,14 @@ label(World* world, const LilvNode* node)
world->lilv_world(), node, rdfs_label, NULL);
const LilvNode* first = lilv_nodes_get_first(labels);
- Glib::ustring label = first ? lilv_node_as_string(first) : "";
+ std::string label = first ? lilv_node_as_string(first) : "";
lilv_nodes_free(labels);
lilv_node_free(rdfs_label);
return label;
}
-Glib::ustring
+std::string
comment(World* world, const LilvNode* node)
{
LilvNode* rdfs_comment = lilv_new_uri(
@@ -49,8 +49,8 @@ comment(World* world, const LilvNode* node)
LilvNodes* comments = lilv_world_find_nodes(
world->lilv_world(), node, rdfs_comment, NULL);
- const LilvNode* first = lilv_nodes_get_first(comments);
- Glib::ustring comment = first ? lilv_node_as_string(first) : "";
+ const LilvNode* first = lilv_nodes_get_first(comments);
+ std::string comment = first ? lilv_node_as_string(first) : "";
lilv_nodes_free(comments);
lilv_node_free(rdfs_comment);
@@ -178,11 +178,10 @@ instances(World* world, const URISet& types)
if (!lilv_node_is_uri(object)) {
continue;
}
- const Glib::ustring label = RDFS::label(world, object);
+ const std::string label = RDFS::label(world, object);
result.insert(
- std::make_pair(
- Raul::URI(lilv_node_as_string(object)),
- label));
+ std::make_pair(label,
+ Raul::URI(lilv_node_as_string(object))));
}
lilv_node_free(type);
}
@@ -191,6 +190,41 @@ instances(World* world, const URISet& types)
return result;
}
+URISet
+range(World* world, const LilvNode* prop, bool recursive)
+{
+ LilvNode* rdfs_range = lilv_new_uri(
+ world->lilv_world(), LILV_NS_RDFS "range");
+
+ LilvNodes* nodes = lilv_world_find_nodes(
+ world->lilv_world(), prop, rdfs_range, NULL);
+
+ URISet ranges;
+ LILV_FOREACH(nodes, n, nodes) {
+ ranges.insert(Raul::URI(lilv_node_as_string(lilv_nodes_get(nodes, n))));
+ }
+
+ if (recursive) {
+ RDFS::classes(world, ranges, false);
+ }
+
+ lilv_nodes_free(nodes);
+ lilv_node_free(rdfs_range);
+ return ranges;
+}
+
+bool
+is_a(World* world, const LilvNode* inst, const LilvNode* klass)
+{
+ LilvNode* rdf_type = lilv_new_uri(world->lilv_world(), LILV_NS_RDF "type");
+
+ const bool is_instance = lilv_world_ask(
+ world->lilv_world(), inst, rdf_type, klass);
+
+ lilv_node_free(rdf_type);
+ return is_instance;
+}
+
} // namespace RDFS
} // namespace GUI
} // namespace Ingen
diff --git a/src/gui/RDFS.hpp b/src/gui/RDFS.hpp
index d9506dbc..52931aaf 100644
--- a/src/gui/RDFS.hpp
+++ b/src/gui/RDFS.hpp
@@ -19,10 +19,10 @@
#include <map>
#include <set>
-
-#include <glibmm/ustring.h>
+#include <string>
#include "ingen/types.hpp"
+#include "lilv/lilv.h"
#include "raul/URI.hpp"
namespace Ingen {
@@ -38,16 +38,14 @@ namespace RDFS {
/** Set of URIs. */
typedef std::set<Raul::URI> URISet;
-/** Map of object labels, keyed by object URI. */
-typedef std::map<Raul::URI, Glib::ustring> Objects;
+/** Label => Resource map. */
+typedef std::map<std::string, Raul::URI> Objects;
/** Return the label of `node`. */
-Glib::ustring
-label(World* world, const LilvNode* node);
+std::string label(World* world, const LilvNode* node);
/** Return the comment of `node`. */
-Glib::ustring
-comment(World* world, const LilvNode* node);
+std::string comment(World* world, const LilvNode* node);
/** Set `types` to its super/sub class closure.
* @param super If true, find all superclasses, otherwise all subclasses
@@ -55,8 +53,7 @@ comment(World* world, const LilvNode* node);
void classes(World* world, URISet& types, bool super);
/** Get all instances of any class in `types`. */
-Objects
-instances(World* world, const URISet& types);
+Objects instances(World* world, const URISet& types);
/** Get all the types which `model` is an instance of. */
URISet types(World* world, SPtr<const Client::ObjectModel> model);
@@ -64,6 +61,14 @@ URISet types(World* world, SPtr<const Client::ObjectModel> model);
/** Get all the properties with domains appropriate for `model`. */
URISet properties(World* world, SPtr<const Client::ObjectModel> model);
+/** Return the range (value types) of `prop`.
+ * @param recursive If true, include all subclasses.
+ */
+URISet range(World* world, const LilvNode* prop, bool recursive);
+
+/** Return true iff `inst` is-a `klass`. */
+bool is_a(World* world, const LilvNode* inst, const LilvNode* klass);
+
} // namespace RDFS
} // namespace GUI
} // namespace Ingen
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
diff --git a/src/gui/URIEntry.hpp b/src/gui/URIEntry.hpp
new file mode 100644
index 00000000..d8f830d0
--- /dev/null
+++ b/src/gui/URIEntry.hpp
@@ -0,0 +1,70 @@
+/*
+ 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/>.
+*/
+
+#ifndef INGEN_GUI_URI_ENTRY_HPP
+#define INGEN_GUI_URI_ENTRY_HPP
+
+#include <gtkmm/box.h>
+#include <gtkmm/button.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/menu.h>
+
+#include "lilv/lilv.h"
+
+namespace Ingen {
+namespace GUI {
+
+class App;
+
+class URIEntry : public Gtk::HBox {
+public:
+ /** Create a widget for entering URIs.
+ *
+ * If `types` is given, then a menu button will be shown which pops up a
+ * enu for easily choosing known values with valid types.
+ */
+ URIEntry(App* app,
+ const std::set<Raul::URI>& types,
+ const std::string& value);
+
+ std::string get_text() { return _entry->get_text(); }
+ Glib::SignalProxy0<void> signal_changed() { return _entry->signal_changed(); }
+
+private:
+ Gtk::Menu* build_value_menu();
+ Gtk::Menu* build_subclass_menu(const LilvNode* klass);
+
+ void add_leaf_menu_item(Gtk::Menu* menu,
+ const LilvNode* klass,
+ const std::string& label);
+
+ void add_class_menu_item(Gtk::Menu* menu,
+ const LilvNode* klass,
+ const std::string& label);
+
+ void uri_chosen(const std::string& uri);
+ bool menu_button_event(GdkEvent* ev);
+
+ App* _app;
+ const std::set<Raul::URI> _types;
+ Gtk::Button* _menu_button;
+ Gtk::Entry* _entry;
+};
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // INGEN_GUI_URI_ENTRY_HPP
diff --git a/src/gui/ingen_gui.ui b/src/gui/ingen_gui.ui
index 8fa4f35e..9399300b 100644
--- a/src/gui/ingen_gui.ui
+++ b/src/gui/ingen_gui.ui
@@ -2430,35 +2430,14 @@ See COPYING file included with this distribution, or http://www.gnu.org/licenses
</packing>
</child>
<child>
- <object class="GtkButton" id="properties_value_button">
- <property name="label">☰</property>
- <property name="use_action_appearance">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="properties_value_entry">
+ <object class="GtkAlignment" id="properties_value_bin">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">●</property>
- <property name="activates_default">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">True</property>
- <property name="secondary_icon_sensitive">True</property>
+ <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
@@ -2466,6 +2445,7 @@ See COPYING file included with this distribution, or http://www.gnu.org/licenses
<property name="label">gtk-add</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
+ <property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
@@ -2473,7 +2453,7 @@ See COPYING file included with this distribution, or http://www.gnu.org/licenses
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">3</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
diff --git a/src/gui/wscript b/src/gui/wscript
index 203d7570..1beba44b 100644
--- a/src/gui/wscript
+++ b/src/gui/wscript
@@ -74,6 +74,7 @@ def build(bld):
Style.cpp
SubgraphModule.cpp
ThreadedLoader.cpp
+ URIEntry.cpp
WidgetFactory.cpp
WindowFactory.cpp
ingen_gui.cpp