summaryrefslogtreecommitdiffstats
path: root/src/gui/Parameter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/Parameter.cpp')
-rw-r--r--src/gui/Parameter.cpp520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/gui/Parameter.cpp b/src/gui/Parameter.cpp
new file mode 100644
index 00000000..464a9e67
--- /dev/null
+++ b/src/gui/Parameter.cpp
@@ -0,0 +1,520 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2016 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 <cassert>
+#include <string>
+
+#include "ganv/Module.hpp"
+#include "ingen/Configuration.hpp"
+#include "ingen/Interface.hpp"
+#include "ingen/Log.hpp"
+#include "ingen/client/GraphModel.hpp"
+#include "ingen/client/ParameterModel.hpp"
+
+#include "App.hpp"
+#include "GraphWindow.hpp"
+#include "Parameter.hpp"
+// #include "ParameterMenu.hpp"
+#include "RDFS.hpp"
+#include "Style.hpp"
+#include "WidgetFactory.hpp"
+#include "WindowFactory.hpp"
+#include "ingen_config.h"
+
+using namespace Ingen::Client;
+using namespace std;
+
+namespace Ingen {
+namespace GUI {
+
+Parameter*
+Parameter::create(App& app,
+ Ganv::Module& module,
+ SPtr<const ParameterModel> pm,
+ bool flip)
+{
+ return new Parameter(app, module, pm, parameter_label(app, pm), flip);
+}
+
+/** @param flip Make an input parameter appear as an output parameter, and vice versa.
+ */
+Parameter::Parameter(App& app,
+ Ganv::Module& module,
+ SPtr<const ParameterModel> pm,
+ const string& name,
+ bool flip)
+ : Ganv::Port(module, name,
+ true, //flip ? (!pm->is_input()) : pm->is_input(),
+ 0x8888FF) //app.style()->get_parameter_color(pm.get()))
+ , _app(app)
+ , _parameter_model(pm)
+ , _entered(false)
+ , _flipped(flip)
+{
+ assert(pm);
+
+ // if (app.can_control(pm.get())) {
+ show_control();
+ pm->signal_value_changed().connect(
+ sigc::mem_fun(this, &Parameter::value_changed));
+ // }
+
+ parameter_properties_changed();
+
+ pm->signal_property().connect(
+ sigc::mem_fun(this, &Parameter::property_changed));
+ pm->signal_property_removed().connect(
+ sigc::mem_fun(this, &Parameter::property_removed));
+ pm->signal_moved().connect(
+ sigc::mem_fun(this, &Parameter::moved));
+
+ signal_value_changed.connect(
+ sigc::mem_fun(this, &Parameter::on_value_changed));
+
+ signal_event().connect(
+ sigc::mem_fun(this, &Parameter::on_event));
+
+ // set_is_controllable(pm->is_numeric() && pm->is_input());
+ set_is_controllable(true);
+
+ // Ganv::Port::set_beveled(model()->is_a(_app.uris().lv2_ControlParameter) ||
+ // model()->has_property(_app.uris().atom_bufferType,
+ // _app.uris().atom_Sequence));
+
+ update_metadata();
+ value_changed(pm->value());
+}
+
+Parameter::~Parameter()
+{
+ // _app.activity_parameter_destroyed(this);
+}
+
+std::string
+Parameter::parameter_label(App& app, SPtr<const ParameterModel> pm)
+{
+ if (!pm) {
+ return "";
+ }
+
+ std::string label;
+ if (app.world()->conf().option("port-labels").get<int32_t>()) {
+ if (app.world()->conf().option("human-names").get<int32_t>()) {
+ const Atom& name = pm->get_property(app.uris().rdfs_label);
+ if (name.type() == app.forge().String) {
+ label = name.ptr<char>();
+ } else {
+ const SPtr<const BlockModel> parent(
+ dynamic_ptr_cast<const BlockModel>(pm->parent()));
+ // if (parent && parent->plugin_model())
+ // label = parent->plugin_model()->parameter_human_name(pm->index());
+ }
+ } else {
+ label = pm->path().symbol();
+ }
+ }
+ return label;
+}
+
+void
+Parameter::ensure_label()
+{
+ if (!get_label()) {
+ set_label(parameter_label(_app, _parameter_model.lock()).c_str());
+ }
+}
+
+void
+Parameter::update_metadata()
+{
+ // Get default range from static data
+ // const URIs& uris = _app.uris();
+ // LilvWorld* lworld = _app.world()->lilv_world();
+ // LilvNode* min = lilv_world_get(lworld, property, uris.lv2_minimum, NULL);
+ // LilvNOde* max = lilv_world_get(lworld, property, uris.lv2_maximum, NULL);
+
+ set_control_min(0.0);
+ set_control_max(1.0);
+
+ // SPtr<const ParameterModel> pm = _parameter_model.lock();
+ // if (pm && _app.can_control(pm.get()) && pm->is_numeric()) {
+ // SPtr<const BlockModel> parent = dynamic_ptr_cast<const BlockModel>(pm->parent());
+ // if (parent) {
+ // float min = 0.0f;
+ // float max = 1.0f;
+ // parent->parameter_value_range(pm, min, max, _app.sample_rate());
+ // set_control_min(min);
+ // set_control_max(max);
+ // }
+ // }
+}
+
+bool
+Parameter::show_menu(GdkEventButton* ev)
+{
+ // ParameterMenu* menu = NULL;
+ // WidgetFactory::get_widget_derived("object_menu", menu);
+ // if (!menu) {
+ // _app.log().error("Failed to load parameter menu widget\n");
+ // return false;
+ // }
+
+ // menu->init(_app, model(), _flipped);
+ // menu->popup(ev->button, ev->time);
+ return true;
+}
+
+void
+Parameter::moved()
+{
+ if (_app.world()->conf().option("port-labels").get<int32_t>() &&
+ !_app.world()->conf().option("human-names").get<int32_t>()) {
+ set_label(model()->symbol().c_str());
+ }
+}
+
+void
+Parameter::on_value_changed(double value)
+{
+ const URIs& uris = _app.uris();
+ const Atom& current_value = model()->value();
+ // if (current_value.type() != uris.forge.Float) {
+ // fprintf(stderr, "Non-float\n");
+ // return; // Non-float, unsupported
+ // }
+
+ // if (current_value.get<float>() == (float)value) {
+ // fprintf(stderr, "No change\n");
+ // return; // No change
+ // }
+
+ fprintf(stderr, "Change param\n");
+ const Atom atom = _app.forge().make(float(value));
+ _app.set_property(model()->uri(),
+ _app.world()->uris().ingen_value,
+ atom);
+
+ // if (_entered) {
+ // GraphBox* box = get_graph_box();
+ // if (box) {
+ // box->show_port_status(model().get(), atom);
+ // }
+ // }
+}
+
+void
+Parameter::value_changed(const Atom& value)
+{
+ if (value.type() == _app.forge().Float && !get_grabbed()) {
+ Ganv::Port::set_control_value(value.get<float>());
+ }
+}
+
+void
+Parameter::on_scale_point_activated(float f)
+{
+ _app.set_property(model()->uri(),
+ _app.world()->uris().ingen_value,
+ _app.world()->forge().make(f));
+}
+
+Gtk::Menu*
+Parameter::build_enum_menu()
+{
+ SPtr<const BlockModel> block = dynamic_ptr_cast<BlockModel>(model()->parent());
+ Gtk::Menu* menu = Gtk::manage(new Gtk::Menu());
+
+#if 0
+ PluginModel::ScalePoints points = block->plugin_model()->parameter_scale_points(
+ model()->index());
+ for (PluginModel::ScalePoints::iterator i = points.begin();
+ i != points.end(); ++i) {
+ menu->items().push_back(Gtk::Menu_Helpers::MenuElem(i->second));
+ Gtk::MenuItem* menu_item = &(menu->items().back());
+ menu_item->signal_activate().connect(
+ sigc::bind(sigc::mem_fun(this, &Parameter::on_scale_point_activated),
+ i->first));
+ }
+
+#endif
+ return menu;
+}
+
+void
+Parameter::on_uri_activated(const Raul::URI& uri)
+{
+ _app.set_property(model()->uri(),
+ _app.world()->uris().ingen_value,
+ _app.world()->forge().make_urid(
+ _app.world()->uri_map().map_uri(uri.c_str())));
+}
+
+Gtk::Menu*
+Parameter::build_uri_menu()
+{
+ World* world = _app.world();
+ SPtr<const BlockModel> block = dynamic_ptr_cast<BlockModel>(model()->parent());
+ Gtk::Menu* menu = Gtk::manage(new Gtk::Menu());
+
+ // Get the parameter designation, which should be a rdf:Property
+ const Atom& designation_atom = model()->get_property(
+ _app.uris().lv2_designation);
+ if (!designation_atom.is_valid()) {
+ return NULL;
+ }
+
+ LilvNode* designation = lilv_new_uri(
+ world->lilv_world(), world->forge().str(designation_atom, false).c_str());
+ LilvNode* rdfs_range = lilv_new_uri(
+ world->lilv_world(), LILV_NS_RDFS "range");
+
+ // Get every class in the range of the parameter's property
+ RDFS::URISet ranges;
+ LilvNodes* range = lilv_world_find_nodes(
+ world->lilv_world(), designation, rdfs_range, NULL);
+ LILV_FOREACH(nodes, r, range) {
+ ranges.insert(Raul::URI(lilv_node_as_string(lilv_nodes_get(range, r))));
+ }
+ RDFS::classes(world, ranges, false);
+
+ // Get all objects in range
+ RDFS::Objects values = RDFS::instances(world, ranges);
+
+ // Add a menu item for each such class
+ for (const auto& v : values) {
+ 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, &Parameter::on_uri_activated),
+ v.second));
+ }
+ }
+
+ return menu;
+}
+
+bool
+Parameter::on_event(GdkEvent* ev)
+{
+ GraphBox* box = NULL;
+ switch (ev->type) {
+ case GDK_ENTER_NOTIFY:
+ _entered = true;
+ if ((box = get_graph_box())) {
+ box->object_entered(model().get());
+ }
+ return false;
+ case GDK_LEAVE_NOTIFY:
+ _entered = false;
+ if ((box = get_graph_box())) {
+ box->object_left(model().get());
+ }
+ return false;
+ case GDK_BUTTON_PRESS:
+ if (ev->button.button == 1) {
+ // if (model()->is_enumeration()) {
+ // Gtk::Menu* menu = build_enum_menu();
+ // menu->popup(ev->button.button, ev->button.time);
+ // return true;
+ // } else if (model()->is_uri()) {
+ // Gtk::Menu* menu = build_uri_menu();
+ // if (menu) {
+ // menu->popup(ev->button.button, ev->button.time);
+ // return true;
+ // }
+ // }
+ } else if (ev->button.button == 3) {
+ return show_menu(&ev->button);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+GraphBox*
+Parameter::get_graph_box() const
+{
+ SPtr<const GraphModel> graph = dynamic_ptr_cast<const GraphModel>(model()->parent());
+ if (!graph) {
+ graph = dynamic_ptr_cast<const GraphModel>(model()->parent()->parent());
+ }
+
+ return _app.window_factory()->graph_box(graph);
+}
+
+void
+Parameter::set_type_tag()
+{
+#if 0
+ const URIs& uris = _app.uris();
+ std::string tag;
+ if (model()->is_a(_app.uris().lv2_AudioParameter)) {
+ tag = "~";
+ } else if (model()->is_a(_app.uris().lv2_CVParameter)) {
+ tag = "ℝ̰";
+ } else if (model()->is_a(_app.uris().lv2_ControlParameter)) {
+ if (model()->is_enumeration()) {
+ tag = "…";
+ } else if (model()->is_integer()) {
+ tag = "ℤ";
+ } else if (model()->is_toggle()) {
+ tag = ((model()->value() != _app.uris().forge.make(0.0f))
+ ? "☑" : "☐");
+
+ } else {
+ tag = "ℝ";
+ }
+ } else if (model()->is_a(_app.uris().atom_AtomParameter)) {
+ if (model()->supports(_app.uris().atom_Float)) {
+ if (model()->is_toggle()) {
+ tag = ((model()->value() != _app.uris().forge.make(0.0f))
+ ? "☑" : "☐");
+ } else {
+ tag = "ℝ";
+ }
+ }
+ if (model()->supports(_app.uris().atom_Int)) {
+ tag += "ℤ";
+ }
+ if (model()->supports(_app.uris().midi_MidiEvent)) {
+ tag += "𝕄";
+ }
+ if (model()->supports(_app.uris().patch_Message)) {
+ if (tag.empty()) {
+ tag += "=";
+ } else {
+ tag += "̿";
+ }
+ }
+ if (tag.empty()) {
+ tag = "*";
+ }
+
+ if (model()->has_property(uris.atom_bufferType, uris.atom_Sequence)) {
+ tag += "̤";
+ }
+ }
+
+ if (!tag.empty()) {
+ set_value_label(tag.c_str());
+ }
+#endif
+}
+
+void
+Parameter::parameter_properties_changed()
+{
+ // if (model()->is_toggle()) {
+ // set_control_is_toggle(true);
+ // } else if (model()->is_integer()) {
+ // set_control_is_integer(true);
+ // }
+ // set_type_tag();
+}
+
+void
+Parameter::property_changed(const Raul::URI& key, const Atom& value)
+{
+#if 0
+ const URIs& uris = _app.uris();
+ if (value.type() == uris.forge.Float) {
+ float val = value.get<float>();
+ if (key == uris.ingen_value && !get_grabbed()) {
+ Ganv::Port::set_control_value(val);
+ if (model()->is_toggle()) {
+ std::string tag = (val == 0.0f) ? "☐" : "☑";
+ if (model()->is_a(_app.uris().lv2_CVParameter)) {
+ tag += "̰";
+ } else if (model()->has_property(uris.atom_bufferType,
+ uris.atom_Sequence)) {
+ tag += "̤";
+ }
+ set_value_label(tag.c_str());
+ }
+ } else if (key == uris.lv2_minimum) {
+ if (model()->parameter_property(uris.lv2_sampleRate)) {
+ val *= _app.sample_rate();
+ }
+ set_control_min(val);
+ } else if (key == uris.lv2_maximum) {
+ if (model()->parameter_property(uris.lv2_sampleRate)) {
+ val *= _app.sample_rate();
+ }
+ set_control_max(val);
+ }
+ } else if (key == uris.lv2_parameterProperty) {
+ parameter_properties_changed();
+ } else if (key == uris.lv2_name) {
+ if (value.type() == uris.forge.String &&
+ _app.world()->conf().option("parameter-labels").get<int32_t>() &&
+ _app.world()->conf().option("human-names").get<int32_t>()) {
+ set_label(value.ptr<char>());
+ }
+ } else if (key == uris.rdf_type || key == uris.atom_bufferType) {
+ Ganv::Port::set_beveled(model()->is_a(uris.lv2_ControlParameter) ||
+ model()->has_property(uris.atom_bufferType,
+ uris.atom_Sequence));
+ }
+#endif
+}
+
+void
+Parameter::property_removed(const Raul::URI& key, const Atom& value)
+{
+ const URIs& uris = _app.uris();
+ if (key == uris.lv2_minimum || key == uris.lv2_maximum) {
+ update_metadata();
+#if 0
+ } else if (key == uris.rdf_type || key == uris.atom_bufferType) {
+ Ganv::Port::set_beveled(model()->is_a(uris.lv2_ControlParameter) ||
+ model()->has_property(uris.atom_bufferType,
+ uris.atom_Sequence));
+#endif
+ }
+}
+
+bool
+Parameter::on_selected(gboolean b)
+{
+#if 0
+ if (b) {
+ SPtr<const ParameterModel> pm = _parameter_model.lock();
+ if (pm) {
+ SPtr<const BlockModel> block = dynamic_ptr_cast<const BlockModel>(pm->parent());
+ GraphWindow* win = _app.window_factory()->parent_graph_window(block);
+ if (win && win->documentation_is_visible() && block->plugin_model()) {
+ bool html = false;
+#ifdef HAVE_WEBKIT
+ html = true;
+#endif
+ const std::string& doc = block->plugin_model()->parameter_documentation(
+ pm->index(), html);
+ win->set_documentation(doc, html);
+ }
+ }
+ }
+#endif
+ return true;
+}
+
+} // namespace GUI
+} // namespace Ingen