/*
This file is part of Ingen.
Copyright 2007-2012 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
#include
#include "raul/log.hpp"
#include "ingen/shared/World.hpp"
#include "ingen/client/NodeModel.hpp"
#include "ingen/client/PluginModel.hpp"
#include "App.hpp"
#include "PropertiesWindow.hpp"
#define LOG(s) s << "[PropertiesWindow] "
using namespace std;
using namespace Raul;
namespace Ingen {
namespace GUI {
PropertiesWindow::PropertiesWindow(BaseObjectType* cobject,
const Glib::RefPtr& xml)
: Window(cobject)
{
xml->get_widget("properties_vbox", _vbox);
xml->get_widget("properties_scrolledwindow", _scrolledwindow);
xml->get_widget("properties_table", _table);
xml->get_widget("properties_cancel_button", _cancel_button);
xml->get_widget("properties_apply_button", _apply_button);
xml->get_widget("properties_ok_button", _ok_button);
_cancel_button->signal_clicked().connect(
sigc::mem_fun(this, &PropertiesWindow::cancel_clicked));
_apply_button->signal_clicked().connect(
sigc::mem_fun(this, &PropertiesWindow::apply_clicked));
_ok_button->signal_clicked().connect(
sigc::mem_fun(this, &PropertiesWindow::ok_clicked));
}
void
PropertiesWindow::reset()
{
_table->children().clear();
_table->resize(1, 3);
_table->property_n_rows() = 1;
_records.clear();
_property_connection.disconnect();
_model.reset();
}
void
PropertiesWindow::present(SharedPtr model)
{
set_object(model);
Gtk::Window::present();
}
/** Set the node this window is associated with.
* This function MUST be called before using this object in any way.
*/
void
PropertiesWindow::set_object(SharedPtr model)
{
reset();
_model = model;
set_title(model->path().chop_scheme() + " Properties - Ingen");
Shared::World* world = _app->world();
ostringstream ss;
unsigned n_rows = 0;
for (ObjectModel::Properties::const_iterator i = model->properties().begin();
i != model->properties().end(); ++i) {
_table->property_n_rows() = ++n_rows;
const Raul::Atom& value = i->second;
// Column 0: Property
Gtk::Label* lab = manage(
new Gtk::Label(world->rdf_world()->prefixes().qualify(i->first.str()),
0.0, 0.5));
_table->attach(*lab, 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::Widget* value_widget = create_value_widget(i->first, value);
if (value_widget)
align->add(*value_widget);
_table->attach(*align, 1, 2, n_rows, n_rows + 1,
Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK);
_records.insert(make_pair(i->first, Record(value, align, n_rows)));
}
_table->show_all();
_property_connection = model->signal_property().connect(
sigc::mem_fun(this, &PropertiesWindow::property_changed));
}
Gtk::Widget*
PropertiesWindow::create_value_widget(const Raul::URI& uri, const Raul::Atom& value)
{
Ingen::Forge& forge = _app->forge();
if (value.type() == forge.Int) {
Gtk::SpinButton* widget = manage(new Gtk::SpinButton(0.0, 0));
widget->property_numeric() = true;
widget->set_value(value.get_int32());
widget->set_snap_to_ticks(true);
widget->set_range(INT_MIN, INT_MAX);
widget->set_increments(1, 10);
widget->signal_value_changed().connect(sigc::bind(
sigc::mem_fun(this, &PropertiesWindow::value_edited),
uri));
return widget;
} else if (value.type() == forge.Float) {
Gtk::SpinButton* widget = manage(new Gtk::SpinButton(0.0, 4));
widget->property_numeric() = true;
widget->set_snap_to_ticks(false);
widget->set_range(DBL_MIN, DBL_MAX);
widget->set_value(value.get_float());
widget->set_increments(0.1, 1.0);
widget->signal_value_changed().connect(sigc::bind(
sigc::mem_fun(this, &PropertiesWindow::value_edited),
uri));
return widget;
} else if (value.type() == forge.Bool) {
Gtk::CheckButton* widget = manage(new Gtk::CheckButton());
widget->set_active(value.get_bool());
widget->signal_toggled().connect(sigc::bind(
sigc::mem_fun(this, &PropertiesWindow::value_edited),
uri));
return widget;
} else if (value.type() == forge.URI) {
Gtk::Entry* widget = manage(new Gtk::Entry());
widget->set_text(value.get_uri());
widget->signal_changed().connect(sigc::bind(
sigc::mem_fun(this, &PropertiesWindow::value_edited),
uri));
return widget;
} else if (value.type() == forge.String) {
Gtk::Entry* widget = manage(new Gtk::Entry());
widget->set_text(value.get_string());
widget->signal_changed().connect(sigc::bind(
sigc::mem_fun(this, &PropertiesWindow::value_edited),
uri));
return widget;
}
LOG(error) << "Unable to create widget for value " << forge.str(value) << endl;
return NULL;
}
void
PropertiesWindow::on_show()
{
static const int WIN_PAD = 64;
static const int VBOX_PAD = 16;
int width = 0;
int height = 0;
Gtk::Requisition req;
typedef Gtk::Box_Helpers::BoxList Children;
for (Children::const_iterator i = _vbox->children().begin(); i != _vbox->children().end(); ++i) {
req = (*i).get_widget()->size_request();
if ((*i).get_widget() != _scrolledwindow) {
width = std::max(width, req.width);
height += req.height + VBOX_PAD;
}
}
req = _table->size_request();
width = std::max(width, req.width);
height += req.height;
set_default_size(width + WIN_PAD, height + WIN_PAD);
resize(width + WIN_PAD, height + WIN_PAD);
Gtk::Window::on_show();
}
void
PropertiesWindow::property_changed(const Raul::URI& predicate, const Raul::Atom& value)
{
Records::iterator r = _records.find(predicate);
if (r == _records.end()) {
LOG(error) << "Unknown property `" << predicate << "' changed" << endl;
return;
}
Record& record = r->second;
Gtk::Widget* value_widget = create_value_widget(predicate, value);
record.value_widget->remove();
if (value_widget) {
record.value_widget->add(*value_widget);
value_widget->show();
}
record.value = value;
}
void
PropertiesWindow::value_edited(const Raul::URI& predicate)
{
Records::iterator r = _records.find(predicate);
if (r == _records.end()) {
LOG(error) << "Unknown property `" << predicate << "' edited" << endl;
return;
}
Forge& forge = _app->forge();
Record& record = r->second;
Raul::Atom::TypeID type = record.value.type();
if (type == forge.Int) {
Gtk::SpinButton* widget = dynamic_cast(record.value_widget->get_child());
if (!widget) goto bad_type;
record.value = _app->forge().make(widget->get_value_as_int());
} else if (type == forge.Float) {
Gtk::SpinButton* widget = dynamic_cast(record.value_widget->get_child());
if (!widget) goto bad_type;
record.value = _app->forge().make(static_cast(widget->get_value()));
} else if (type == forge.Bool) {
Gtk::CheckButton* widget = dynamic_cast(record.value_widget->get_child());
if (!widget) goto bad_type;
record.value = _app->forge().make(widget->get_active());
} else if (type == forge.URI) {
Gtk::Entry* widget = dynamic_cast(record.value_widget->get_child());
if (!widget) goto bad_type;
record.value = _app->forge().alloc_uri(widget->get_text());
} else if (type == forge.String) {
Gtk::Entry* widget = dynamic_cast(record.value_widget->get_child());
if (!widget) goto bad_type;
record.value = _app->forge().alloc(widget->get_text());
}
return;
bad_type:
LOG(error) << "Property `" << predicate << "' value widget has wrong type" << endl;
return;
}
void
PropertiesWindow::cancel_clicked()
{
reset();
Gtk::Window::hide();
}
void
PropertiesWindow::apply_clicked()
{
LOG(debug) << "apply {" << endl;
Resource::Properties properties;
for (Records::const_iterator r = _records.begin(); r != _records.end(); ++r) {
const Raul::URI& uri = r->first;
const Record& record = r->second;
if (!_model->has_property(uri, record.value)) {
LOG(debug) << "\t" << uri
<< " = " << _app->forge().str(record.value) << endl;
properties.insert(make_pair(uri, record.value));
}
}
_app->engine()->put(_model->path(), properties);
LOG(debug) << "}" << endl;
}
void
PropertiesWindow::ok_clicked()
{
apply_clicked();
cancel_clicked();
}
} // namespace GUI
} // namespace Ingen