From 93850c202de8b073a1ce1dd8bd246d407bce4e2f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 30 Sep 2008 16:50:21 +0000 Subject: Flatten ingen source directory heirarchy a bit. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@1551 a436a847-0d15-0410-975c-d299462d15a1 --- src/gui/LoadPluginWindow.cpp | 464 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 464 insertions(+) create mode 100644 src/gui/LoadPluginWindow.cpp (limited to 'src/gui/LoadPluginWindow.cpp') diff --git a/src/gui/LoadPluginWindow.cpp b/src/gui/LoadPluginWindow.cpp new file mode 100644 index 00000000..28815ba9 --- /dev/null +++ b/src/gui/LoadPluginWindow.cpp @@ -0,0 +1,464 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "LoadPluginWindow.hpp" +#include "PatchWindow.hpp" +#include "PatchView.hpp" +#include "PatchCanvas.hpp" + +using namespace std; + + +namespace Ingen { +namespace GUI { + +LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::Window(cobject) + , _plugin_name_offset(0) + , _has_shown(false) + , _refresh_list(true) +{ + xml->get_widget("load_plugin_plugins_treeview", _plugins_treeview); + xml->get_widget("load_plugin_polyphonic_checkbutton", _polyphonic_checkbutton); + xml->get_widget("load_plugin_name_entry", _node_name_entry); + xml->get_widget("load_plugin_clear_button", _clear_button); + xml->get_widget("load_plugin_add_button", _add_button); + //xml->get_widget("load_plugin_close_button", _close_button); + //xml->get_widget("load_plugin_ok_button", _add_button); + + xml->get_widget("load_plugin_filter_combo", _filter_combo); + xml->get_widget("load_plugin_search_entry", _search_entry); + + // Set up the plugins list + _plugins_liststore = Gtk::ListStore::create(_plugins_columns); + _plugins_treeview->set_model(_plugins_liststore); + _plugins_treeview->append_column("", _plugins_columns._col_icon); + _plugins_treeview->append_column("Name", _plugins_columns._col_name); + _plugins_treeview->append_column("Type", _plugins_columns._col_type); + _plugins_treeview->append_column("URI", _plugins_columns._col_uri); + //m_plugins_treeview->append_column("Library", _plugins_columns._col_library); + //m_plugins_treeview->append_column("Label", _plugins_columns._col_label); + + // This could be nicer.. store the TreeViewColumns locally maybe? + _plugins_treeview->get_column(1)->set_sort_column(_plugins_columns._col_name); + _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_type); + _plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_uri); + //m_plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_library); + //m_plugins_treeview->get_column(4)->set_sort_column(_plugins_columns._col_label); + for (int i=0; i < 3; ++i) + _plugins_treeview->get_column(i)->set_resizable(true); + + _plugins_liststore->set_default_sort_func(sigc::mem_fun(this, &LoadPluginWindow::plugin_compare)); + + // Set up the search criteria combobox + _criteria_liststore = Gtk::ListStore::create(_criteria_columns); + _filter_combo->set_model(_criteria_liststore); + Gtk::TreeModel::iterator iter = _criteria_liststore->append(); + Gtk::TreeModel::Row row = *iter; + row[_criteria_columns._col_label] = "Name contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::NAME; + _filter_combo->set_active(iter); + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Type contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::TYPE; + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "URI contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::URI; + /*iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Library contains: "; + row[_criteria_columns._col_criteria] = CriteriaColumns::LIBRARY; + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Label contains: "; + row[_criteria_columns._col_criteria] = CriteriaColumns::LABEL;*/ + + _clear_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::clear_clicked)); + _add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); + //m_close_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::close_clicked)); + //m_add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::ok_clicked)); + _plugins_treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_activated)); + _search_entry->signal_activate().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); + _search_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::filter_changed)); + _node_name_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::name_changed)); + + _selection = _plugins_treeview->get_selection(); + _selection->signal_changed().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_selection_changed)); + + //m_add_button->grab_default(); +} + + +void +LoadPluginWindow::present(SharedPtr patch, GraphObject::Variables data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + + +/** Called every time the user types into the name input box. + * Used to display warning messages, and enable/disable the OK button. + */ +void +LoadPluginWindow::name_changed() +{ + string name = _node_name_entry->get_text(); + if (!Path::is_valid_name(name)) { + //m_message_label->set_text("Name contains invalid characters."); + _add_button->property_sensitive() = false; + } else if (App::instance().store()->find_child(_patch, name)) { + //m_message_label->set_text("An object already exists with that name."); + _add_button->property_sensitive() = false; + } else if (name.length() == 0) { + //m_message_label->set_text(""); + _add_button->property_sensitive() = false; + } else { + //m_message_label->set_text(""); + _add_button->property_sensitive() = true; + } +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadPluginWindow::set_patch(SharedPtr patch) +{ + if (_patch) { + _patch = patch; + plugin_selection_changed(); + } else { + _patch = patch; + } + + /*if (patch->poly() <= 1) + _polyphonic_checkbutton->property_sensitive() = false; + else + _polyphonic_checkbutton->property_sensitive() = true;*/ +} + + +/** Populates the plugin list on the first show. + * + * This is done here instead of construction time as the list population is + * really expensive and bogs down creation of a patch. This is especially + * important when many patch notifications are sent at one time from the + * engine. + */ +void +LoadPluginWindow::on_show() +{ + if (!_has_shown) { + App::instance().store()->signal_new_plugin.connect( + sigc::mem_fun(this, &LoadPluginWindow::add_plugin)); + _has_shown = true; + } + + if (_refresh_list) { + set_plugins(App::instance().store()->plugins()); + _refresh_list = false; + } + + Gtk::Window::on_show(); +} + + +int +LoadPluginWindow::plugin_compare(const Gtk::TreeModel::iterator& a_i, + const Gtk::TreeModel::iterator& b_i) +{ + SharedPtr a = a_i->get_value(_plugins_columns._col_plugin_model); + SharedPtr b = b_i->get_value(_plugins_columns._col_plugin_model); + + // FIXME: haaack + if (!a && !b) + return 0; + else if (!a) + return 1; + else if (!b) + return -1; + + if (a->type() == b->type()) + return strcmp(a->name().c_str(), b->name().c_str()); + else + return ((int)a->type() < (int)b->type()) ? -1 : 1; +} + + +void +LoadPluginWindow::set_plugins(SharedPtr m) +{ + _plugins_liststore->clear(); + + for (ClientStore::Plugins::const_iterator i = m->begin(); i != m->end(); ++i) { + SharedPtr plugin = (*i).second; + + Gtk::TreeModel::iterator iter = _plugins_liststore->append(); + Gtk::TreeModel::Row row = *iter; + + row[_plugins_columns._col_icon] = App::instance().icon_from_path(plugin->icon_path(), 20); + row[_plugins_columns._col_name] = plugin->name(); + //row[_plugins_columns._col_label] = plugin->plug_label(); + if (!strcmp(plugin->type_uri(), "ingen:Internal")) + row[_plugins_columns._col_type] = "Internal"; + else if (!strcmp(plugin->type_uri(), "ingen:LV2")) + row[_plugins_columns._col_type] = "LV2"; + else if (!strcmp(plugin->type_uri(), "ingen:LADSPA")) + row[_plugins_columns._col_type] = "LADSPA"; + else + row[_plugins_columns._col_type] = plugin->type_uri(); + row[_plugins_columns._col_uri] = plugin->uri(); + row[_plugins_columns._col_label] = plugin->name(); + //row[_plugins_columns._col_library] = plugin->lib_name(); + row[_plugins_columns._col_plugin_model] = plugin; + } + + _plugins_liststore->set_sort_column(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING); + + _plugins_treeview->columns_autosize(); +} + + +void +LoadPluginWindow::new_plugin(SharedPtr pm) +{ + if (is_visible()) + add_plugin(pm); + else + _refresh_list = true; +} + + +void +LoadPluginWindow::add_plugin(SharedPtr plugin) +{ + Gtk::TreeModel::iterator iter = _plugins_liststore->append(); + Gtk::TreeModel::Row row = *iter; + + row[_plugins_columns._col_name] = plugin->name(); + //row[_plugins_columns._col_label] = plugin->plug_label(); + row[_plugins_columns._col_type] = plugin->type_uri(); + row[_plugins_columns._col_uri] = plugin->uri(); + row[_plugins_columns._col_label] = plugin->name(); + //row[_plugins_columns._col_library] = plugin->lib_name(); + row[_plugins_columns._col_plugin_model] = plugin; +} + + + +///// Event Handlers ////// + + +void +LoadPluginWindow::plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col) +{ + add_clicked(); +} + + +void +LoadPluginWindow::plugin_selection_changed() +{ + Gtk::TreeModel::iterator iter = _selection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + boost::shared_ptr p = row.get_value(_plugins_columns._col_plugin_model); + _plugin_name_offset = App::instance().store()->child_name_offset( + _patch->path(), p->default_node_symbol()); + _node_name_entry->set_text(generate_module_name(_plugin_name_offset)); + } else { + _plugin_name_offset = 0; + _node_name_entry->set_text(""); + } +} + + +/** Generate an automatic name for this Node. + * + * Offset is an offset of the number that will be appended to the plugin's + * label, needed if the user adds multiple plugins faster than the engine + * sends the notification back. + */ +string +LoadPluginWindow::generate_module_name(int offset) +{ + string name = ""; + + Gtk::TreeModel::iterator iter = _selection->get_selected(); + + if (iter) { + Gtk::TreeModel::Row row = *iter; + SharedPtr plugin = row.get_value(_plugins_columns._col_plugin_model); + std::stringstream ss; + ss << plugin->default_node_symbol(); + if (offset != 0) + ss << "_" << offset + 1; + name = ss.str(); + } + + return name; +} + + +void +LoadPluginWindow::add_clicked() +{ + Gtk::TreeModel::iterator iter = _selection->get_selected(); + bool polyphonic = _polyphonic_checkbutton->get_active(); + + if (iter) { // If anything is selected + Gtk::TreeModel::Row row = *iter; + SharedPtr plugin = row.get_value(_plugins_columns._col_plugin_model); + string name = _node_name_entry->get_text(); + if (name == "") { + name = generate_module_name(); + } + if (name == "") { + Gtk::MessageDialog dialog(*this, + "Unable to chose a default name for this node. Please enter a name.", + false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + + dialog.run(); + } else { + Path path = _patch->path().base() + Path::nameify(name); + App::instance().engine()->new_node(path, plugin->uri()); + App::instance().engine()->set_property(path, "ingen:polyphonic", polyphonic); + for (GraphObject::Variables::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); + _node_name_entry->set_text(generate_module_name(++_plugin_name_offset)); + + // Cascade + Atom& x = _initial_data["ingenuity:canvas-x"]; + x = Atom(x.get_float() + 20.0f); + Atom& y = _initial_data["ingenuity:canvas-y"]; + y = Atom(y.get_float() + 20.0f); + } + } +} + + +/* +void +LoadPluginWindow::close_clicked() +{ + hide(); +} + + +void +LoadPluginWindow::ok_clicked() +{ + add_clicked(); + close_clicked(); +} +*/ + +void +LoadPluginWindow::filter_changed() +{ + _plugins_liststore->clear(); + + string search = _search_entry->get_text(); + transform(search.begin(), search.end(), search.begin(), ::toupper); + + // Get selected criteria + const Gtk::TreeModel::Row row = *(_filter_combo->get_active()); + CriteriaColumns::Criteria criteria = row[_criteria_columns._col_criteria]; + + string field; + + Gtk::TreeModel::Row model_row; + Gtk::TreeModel::iterator model_iter; + size_t num_visible = 0; + + + for (ClientStore::Plugins::const_iterator i = App::instance().store()->plugins()->begin(); + i != App::instance().store()->plugins()->end(); ++i) { + + const SharedPtr plugin = (*i).second; + + switch (criteria) { + case CriteriaColumns::NAME: + field = plugin->name(); break; + case CriteriaColumns::TYPE: + field = plugin->type_uri(); break; + case CriteriaColumns::URI: + field = plugin->uri(); break; + /*case CriteriaColumns::LIBRARY: + field = plugin->lib_name(); break; + case CriteriaColumns::LABEL: + field = plugin->plug_label(); break;*/ + default: + throw; + } + + transform(field.begin(), field.end(), field.begin(), ::toupper); + + if (field.find(search) != string::npos) { + model_iter = _plugins_liststore->append(); + model_row = *model_iter; + + model_row[_plugins_columns._col_name] = plugin->name(); + //model_row[_plugins_columns._col_label] = plugin->plug_label(); + model_row[_plugins_columns._col_type] = plugin->type_uri(); + model_row[_plugins_columns._col_uri] = plugin->uri(); + model_row[_plugins_columns._col_plugin_model] = plugin; + + ++num_visible; + } + } + + if (num_visible == 1) { + _selection->unselect_all(); + _selection->select(model_iter); + } +} + + +void +LoadPluginWindow::clear_clicked() +{ + _search_entry->set_text(""); + set_plugins(App::instance().store()->plugins()); +} + + +bool +LoadPluginWindow::on_key_press_event(GdkEventKey* event) +{ + if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) { + hide(); + return true; + } else { + return Gtk::Window::on_key_press_event(event); + } +} + + +} // namespace GUI +} // namespace Ingen -- cgit v1.2.1