summaryrefslogtreecommitdiffstats
path: root/src/progs
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2006-06-10 02:43:54 +0000
committerDavid Robillard <d@drobilla.net>2006-06-10 02:43:54 +0000
commit47246db7e9d71ba694b719001033fba1766a58c4 (patch)
tree25614ea4f2c92033a3cd37f6413df742f09a5369 /src/progs
parent98fe0e7056e6697396249531785d3899f94d79be (diff)
downloadingen-47246db7e9d71ba694b719001033fba1766a58c4.tar.gz
ingen-47246db7e9d71ba694b719001033fba1766a58c4.tar.bz2
ingen-47246db7e9d71ba694b719001033fba1766a58c4.zip
More juggling
git-svn-id: http://svn.drobilla.net/lad/grauph@16 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/progs')
-rw-r--r--src/progs/console/ConsoleClientHooks.cpp33
-rw-r--r--src/progs/console/ConsoleClientHooks.h66
-rw-r--r--src/progs/console/Makefile.am12
-rw-r--r--src/progs/console/README4
-rw-r--r--src/progs/console/console_client.cpp110
-rw-r--r--src/progs/console/patches/COPYING2
-rw-r--r--src/progs/console/patches/dssi_test.omp19
-rw-r--r--src/progs/console/patches/filter_patch.omp54
-rw-r--r--src/progs/console/patches/old_super_simple_patch.omp36
-rw-r--r--src/progs/console/patches/send_test.omp2
-rw-r--r--src/progs/console/patches/simple_patch.omp54
-rw-r--r--src/progs/console/patches/super_simple_patch.omp36
-rw-r--r--src/progs/console/patches/test_patch.omp48
-rw-r--r--src/progs/demolition/DemolitionClientInterface.cpp109
-rw-r--r--src/progs/demolition/DemolitionClientInterface.h75
-rw-r--r--src/progs/demolition/DemolitionModel.cpp246
-rw-r--r--src/progs/demolition/DemolitionModel.h60
-rw-r--r--src/progs/demolition/Makefile.am17
-rw-r--r--src/progs/demolition/README7
-rw-r--r--src/progs/demolition/cmdline.c149
-rw-r--r--src/progs/demolition/cmdline.ggo7
-rw-r--r--src/progs/demolition/cmdline.h45
-rw-r--r--src/progs/demolition/demolition.cpp334
-rw-r--r--src/progs/gtk/App.cpp230
-rw-r--r--src/progs/gtk/App.h123
-rw-r--r--src/progs/gtk/BreadCrumb.h66
-rw-r--r--src/progs/gtk/ConfigWindow.cpp86
-rw-r--r--src/progs/gtk/ConfigWindow.h63
-rw-r--r--src/progs/gtk/Configuration.cpp184
-rw-r--r--src/progs/gtk/Configuration.h76
-rw-r--r--src/progs/gtk/ConnectWindow.cpp170
-rw-r--r--src/progs/gtk/ConnectWindow.h61
-rw-r--r--src/progs/gtk/ControlGroups.cpp419
-rw-r--r--src/progs/gtk/ControlGroups.h196
-rw-r--r--src/progs/gtk/ControlInterface.cpp302
-rw-r--r--src/progs/gtk/ControlInterface.h129
-rw-r--r--src/progs/gtk/ControlPanel.cpp279
-rw-r--r--src/progs/gtk/ControlPanel.h129
-rw-r--r--src/progs/gtk/Controller.cpp173
-rw-r--r--src/progs/gtk/Controller.h112
-rw-r--r--src/progs/gtk/DSSIController.cpp280
-rw-r--r--src/progs/gtk/DSSIController.h79
-rw-r--r--src/progs/gtk/DSSIModule.cpp38
-rw-r--r--src/progs/gtk/DSSIModule.h43
-rw-r--r--src/progs/gtk/GladeFactory.cpp69
-rw-r--r--src/progs/gtk/GladeFactory.h48
-rw-r--r--src/progs/gtk/GtkClientInterface.cpp81
-rw-r--r--src/progs/gtk/GtkClientInterface.h156
-rw-r--r--src/progs/gtk/GtkObjectController.cpp40
-rw-r--r--src/progs/gtk/GtkObjectController.h77
-rw-r--r--src/progs/gtk/LashController.cpp168
-rw-r--r--src/progs/gtk/LashController.h53
-rw-r--r--src/progs/gtk/LoadPatchWindow.cpp131
-rw-r--r--src/progs/gtk/LoadPatchWindow.h73
-rw-r--r--src/progs/gtk/LoadPluginWindow.cpp407
-rw-r--r--src/progs/gtk/LoadPluginWindow.h145
-rw-r--r--src/progs/gtk/LoadSubpatchWindow.cpp177
-rw-r--r--src/progs/gtk/LoadSubpatchWindow.h78
-rw-r--r--src/progs/gtk/Loader.cpp233
-rw-r--r--src/progs/gtk/Loader.h154
-rw-r--r--src/progs/gtk/Makefile.am98
-rw-r--r--src/progs/gtk/MessagesWindow.cpp64
-rw-r--r--src/progs/gtk/MessagesWindow.h55
-rw-r--r--src/progs/gtk/NewSubpatchWindow.cpp110
-rw-r--r--src/progs/gtk/NewSubpatchWindow.h67
-rw-r--r--src/progs/gtk/NodeControlWindow.cpp127
-rw-r--r--src/progs/gtk/NodeControlWindow.h69
-rw-r--r--src/progs/gtk/NodeController.cpp408
-rw-r--r--src/progs/gtk/NodeController.h114
-rw-r--r--src/progs/gtk/NodePropertiesWindow.cpp62
-rw-r--r--src/progs/gtk/NodePropertiesWindow.h55
-rw-r--r--src/progs/gtk/OmFlowCanvas.cpp166
-rw-r--r--src/progs/gtk/OmFlowCanvas.h70
-rw-r--r--src/progs/gtk/OmModule.cpp85
-rw-r--r--src/progs/gtk/OmModule.h77
-rw-r--r--src/progs/gtk/OmPort.cpp57
-rw-r--r--src/progs/gtk/OmPort.h59
-rw-r--r--src/progs/gtk/PatchController.cpp685
-rw-r--r--src/progs/gtk/PatchController.h127
-rw-r--r--src/progs/gtk/PatchDescriptionWindow.cpp72
-rw-r--r--src/progs/gtk/PatchDescriptionWindow.h59
-rw-r--r--src/progs/gtk/PatchTreeWindow.cpp252
-rw-r--r--src/progs/gtk/PatchTreeWindow.h105
-rw-r--r--src/progs/gtk/PatchView.cpp119
-rw-r--r--src/progs/gtk/PatchView.h86
-rw-r--r--src/progs/gtk/PatchWindow.cpp532
-rw-r--r--src/progs/gtk/PatchWindow.h142
-rw-r--r--src/progs/gtk/PortController.cpp143
-rw-r--r--src/progs/gtk/PortController.h75
-rw-r--r--src/progs/gtk/RenameWindow.cpp113
-rw-r--r--src/progs/gtk/RenameWindow.h57
-rw-r--r--src/progs/gtk/Store.cpp155
-rw-r--r--src/progs/gtk/Store.h75
-rw-r--r--src/progs/gtk/SubpatchModule.cpp102
-rw-r--r--src/progs/gtk/SubpatchModule.h69
-rw-r--r--src/progs/gtk/cmdline.c149
-rw-r--r--src/progs/gtk/cmdline.ggo7
-rw-r--r--src/progs/gtk/cmdline.h45
-rw-r--r--src/progs/gtk/main.cpp103
-rw-r--r--src/progs/gtk/om-icon.pngbin0 -> 1189 bytes
-rw-r--r--src/progs/gtk/om_gtk.glade3555
-rw-r--r--src/progs/gtk/om_gtk.glade.bak3555
-rw-r--r--src/progs/gtk/om_gtk.gladep9
-rw-r--r--src/progs/gtk/om_gtk.gladep.bak9
-rw-r--r--src/progs/gtk/singletons.cpp29
-rw-r--r--src/progs/om/Makefile.am254
-rw-r--r--src/progs/om/cmdline.c150
-rw-r--r--src/progs/om/cmdline.ggo7
-rw-r--r--src/progs/om/cmdline.h45
-rw-r--r--src/progs/om/main.cpp153
-rw-r--r--src/progs/patch_loader/Makefile.am13
-rw-r--r--src/progs/patch_loader/README5
-rw-r--r--src/progs/patch_loader/cmdline.c163
-rw-r--r--src/progs/patch_loader/cmdline.ggo7
-rw-r--r--src/progs/patch_loader/cmdline.h47
-rw-r--r--src/progs/patch_loader/new_patch_loader.cpp76
-rw-r--r--src/progs/patch_loader/patch_loader.cpp82
-rw-r--r--src/progs/python/Makefile.am4
-rwxr-xr-xsrc/progs/python/OSC.py374
-rw-r--r--src/progs/python/omecho.py40
-rw-r--r--src/progs/python/omsynth.py635
-rw-r--r--src/progs/python/scripts/Makefile.am2
-rwxr-xr-xsrc/progs/python/scripts/flatten.py232
-rw-r--r--src/progs/python/scripts/sillysinepatch.py41
-rw-r--r--src/progs/supercollider/Makefile.am2
-rw-r--r--src/progs/supercollider/Om.sc746
-rw-r--r--src/progs/supercollider/README11
-rw-r--r--src/progs/supercollider/example.sc27
128 files changed, 21906 insertions, 235 deletions
diff --git a/src/progs/console/ConsoleClientHooks.cpp b/src/progs/console/ConsoleClientHooks.cpp
new file mode 100644
index 00000000..1a911c6a
--- /dev/null
+++ b/src/progs/console/ConsoleClientHooks.cpp
@@ -0,0 +1,33 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "ConsoleClientHooks.h"
+
+#include <iostream>
+
+
+void
+ConsoleClientHooks::new_node(NodeModel* nm)
+{
+ std::cerr << "[ConsoleClient] New node notification" << std::endl;
+}
+
+void
+ConsoleClientHooks::error(std::string msg)
+{
+ std::cerr << "Server returned an error: " << msg << std::endl;
+}
diff --git a/src/progs/console/ConsoleClientHooks.h b/src/progs/console/ConsoleClientHooks.h
new file mode 100644
index 00000000..ceb6cd5a
--- /dev/null
+++ b/src/progs/console/ConsoleClientHooks.h
@@ -0,0 +1,66 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONSOLECLIENTHOOKS_H
+#define CONSOLECLIENTHOOKS_H
+
+#include "ClientHooks.h"
+
+#include <string>
+using std::string;
+
+namespace LibOmClient {
+
+class PatchModel;
+class NodeModel;
+class ConnectionModel;
+class PortModel;
+class MetadataModel;
+class PluginModel;
+
+class ConsoleClientHooks : public ClientHooks
+{
+public:
+ ConsoleClientHooks()
+ ~ConsoleClientHooks();
+
+ void error(const string& msg);
+ void new_node(NodeModel* nm);
+
+ void engine_enabled() {}
+ void engine_disabled() {}
+ void new_patch(PatchModel* pm) {}
+ void new_port(PortModel* port_info) {}
+ void port_removal(const string& path) {}
+ void patch_destruction(const string& path) {}
+ void patch_enabled(const string& path) {}
+ void patch_disabled(const string& path) {}
+ void node_removal(const string& path) {}
+ void object_renamed(const string& old_path, const string& new_path) {}
+ void connection(ConnectionModel* cm) {}
+ void disconnection(const string& src_port_path, const string& dst_port_path) {}
+ void metadata_update(MetadataModel* mm) {}
+ void control_change(const string& port_path, float value) {}
+ void new_plugin(PluginModel* pi) {}
+ void program_add(const string& path, int bank, int program, const string& name) {}
+ void program_remove(const string& path, int bank, int program) {}
+};
+
+
+} // namespace LibOmClient
+
+
+#endif // CONSOLECLIENTHOOKS_H
diff --git a/src/progs/console/Makefile.am b/src/progs/console/Makefile.am
new file mode 100644
index 00000000..f46bdbad
--- /dev/null
+++ b/src/progs/console/Makefile.am
@@ -0,0 +1,12 @@
+om_console_client_CXXFLAGS = -I$(top_srcdir)/src/clients -I$(top_srcdir)/src/common -DPKGDATADIR=\"$(pkgdatadir)\" $(LXML2_CFLAGS) $(LOSC_CFLAGS)
+om_console_client_LDADD = ../libomclient.a $(LOSC_LIBS) $(LXML2_LIBS)
+
+bin_PROGRAMS = console_client
+EXTRA_DIST = README
+
+console_client_CXXFLAGS = "-I../../common"
+console_client_LDADD = @LOSC_LIBS@ ../Comm.o
+console_client_SOURCES = \
+ console_client.cpp \
+ ConsoleClientHooks.h \
+ ConsoleClientHooks.cpp
diff --git a/src/progs/console/README b/src/progs/console/README
new file mode 100644
index 00000000..79c629a5
--- /dev/null
+++ b/src/progs/console/README
@@ -0,0 +1,4 @@
+Everything in and under this directory is very, very, very old. It remains
+on the off chance that I actually make it work again - and for history's
+sake :)
+
diff --git a/src/progs/console/console_client.cpp b/src/progs/console/console_client.cpp
new file mode 100644
index 00000000..4e5586ce
--- /dev/null
+++ b/src/progs/console/console_client.cpp
@@ -0,0 +1,110 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <lo/lo.h>
+#include "OSCModelEngineInterface.h"
+#include "ConsoleClientHooks.h"
+
+
+OSCModelEngineInterface* comm;
+ConsoleClientHooks* client_hooks;
+
+int
+main()
+{
+ std::string input;
+ std::string s;
+ std::string path;
+ std::string types;
+ lo_message m;
+
+ client_hooks = new ConsoleClientHooks();
+ comm = new OSCModelEngineInterface(client_hooks);
+ comm->attach("1234");
+
+ while (input != "q") {
+ std::cout << "> ";
+ std::getline(std::cin, input);
+ std::istringstream stream(input);
+
+ if (std::cin.eof()) break;
+
+ if (input == "") continue;
+
+ //std::cout << "INPUT = " << input << std::endl;
+
+ m = lo_message_new();
+ stream >> path;
+
+ char cs[256];
+
+ stream >> types;
+
+ for (uint i=0; stream >> s && i < types.length(); ++i) {
+ // Take care of quoted strings
+ if (s[0] == '\"') {
+ s = s.substr(1); // hack off begin quote
+
+ if (s[s.length()-1] == '\"') { // single quoted word
+ s = s.substr(0, s.length()-1);
+ } else {
+ std::string temp;
+ do {
+ stream >> temp;
+ if (temp.find('\n') != std::string::npos) {
+ std::cerr << "Mismatched quotes.\n" << std::endl;
+ break;
+ }
+ s += ' ';
+ s += temp;
+ } while (temp[temp.length()-1] != '\"');
+ s = s.substr(0, s.length() - 1); // hack off end quote
+ }
+ }
+
+ switch (types[i]) {
+ case 'f':
+ //std::cerr << "float" << std::endl;
+ lo_message_add_float(m, atof(s.c_str()));
+ break;
+ case 's':
+ strncpy(cs, s.c_str(), 256);
+ //std::cerr << "string" << std::endl;
+ lo_message_add_string(m, cs);
+ break;
+ case 'i':
+ //std::cerr << "integer" << std::endl;
+ lo_message_add_int32(m, atoi(s.c_str()));
+ break;
+ default:
+ std::cerr << "Unknown type \'" << types[i] << "\'" << std::endl;
+ exit(1);
+ }
+ }
+
+ //std::cout << "Sending message..." << std::endl;
+ //lo_message_pp(m);
+ //usleep(500);
+ lo_send_message(comm->addr(), path.c_str(), m);
+ lo_message_free(m);
+ //std::cout << "Input = " << input << std::endl;
+ input = "";
+ }
+ return 0;
+}
diff --git a/src/progs/console/patches/COPYING b/src/progs/console/patches/COPYING
new file mode 100644
index 00000000..e265c4a3
--- /dev/null
+++ b/src/progs/console/patches/COPYING
@@ -0,0 +1,2 @@
+The files in this directory are temporary and not considered code; thus are
+released into the public domain. You may do with them as you please.
diff --git a/src/progs/console/patches/dssi_test.omp b/src/progs/console/patches/dssi_test.omp
new file mode 100644
index 00000000..0613a57b
--- /dev/null
+++ b/src/progs/console/patches/dssi_test.omp
@@ -0,0 +1,19 @@
+/create_patch si patch1 1
+/patches/patch1/add_output s out_1
+/patches/patch1/add_output s out_2
+
+/patches/patch1/add_midi_input
+
+/patches/patch1/load_dssi_plugin sss trivial_synth.so trivial_synth synth
+
+/patches/patch1/connect ssss midi_in Frequency synth Frequency
+/patches/patch1/connect ssss synth Output output out_1
+/patches/patch1/connect ssss synth Output output out_2
+
+/patches/patch1/set_control ssf midi_in Frequency 440
+
+/patches/patch1/set_control ssf osc Slope 0.5
+
+/patches/patch1/activate
+/engine/set_master_patch s patch1
+/engine/start
diff --git a/src/progs/console/patches/filter_patch.omp b/src/progs/console/patches/filter_patch.omp
new file mode 100644
index 00000000..acaad0c8
--- /dev/null
+++ b/src/progs/console/patches/filter_patch.omp
@@ -0,0 +1,54 @@
+/create_patch si patch1 1
+/patches/patch1/add_output s out_1
+/patches/patch1/add_output s out_2
+
+/patches/patch1/add_midi_input
+
+/patches/patch1/load_ladspa_plugin isss 1 triangle_1649.so triangle_fcsc_oa osc
+/patches/patch1/load_ladspa_plugin isss 1 dahdsr_2021.so dahdsr_cg+t_control aenv
+/patches/patch1/load_ladspa_plugin isss 1 amp_1654.so amp_gaia_oa amp
+/patches/patch1/load_ladspa_plugin isss 1 lp4pole_1671.so lp4pole_fcrcia_oa filt
+/patches/patch1/load_ladspa_plugin isss 1 dahdsr_2021.so dahdsr_cg+t_control fenv
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaic_oa fscale
+/patches/patch1/load_ladspa_plugin isss 1 sum_1665.so sum_iaic_oa sum
+
+/patches/patch1/connect ssss midi_in Frequency osc Frequency
+/patches/patch1/connect ssss midi_in Gate aenv "Gate"
+/patches/patch1/connect ssss midi_in Gate fenv "Gate"
+/patches/patch1/connect ssss osc Output filt Input
+/patches/patch1/connect ssss fenv "Envelope Out" fscale "First Input"
+/patches/patch1/connect ssss fscale "Product Output" sum "First Input"
+/patches/patch1/connect ssss midi_in Frequency sum "Second Input"
+/patches/patch1/connect ssss sum "Summed Output" filt "Cutoff Frequency"
+
+/patches/patch1/connect ssss filt Output amp "Input"
+/patches/patch1/connect ssss aenv "Envelope Out" amp "Gain (dB)"
+/patches/patch1/connect ssss amp "Output" output out_1
+/patches/patch1/connect ssss amp "Output" output out_2
+
+
+/patches/patch1/set_control ssf midi_in Frequency 200.0
+/patches/patch1/set_control ssf midi_in Gate 0.0
+/patches/patch1/set_control ssf filt Resonance 3.0
+/patches/patch1/set_control ssf osc Slope 0.9
+/patches/patch1/set_control ssf fscale "Second Input" 800
+
+/patches/patch1/set_control ssf aenv "Trigger" 0
+/patches/patch1/set_control ssf aenv "Delay Time (s)" 0.0
+/patches/patch1/set_control ssf aenv "Attack Time (s)" 0.01
+/patches/patch1/set_control ssf aenv "Hold Time (s)" 0.0
+/patches/patch1/set_control ssf aenv "Decay Time (s)" 0.3
+/patches/patch1/set_control ssf aenv "Sustain Level" 0.7
+/patches/patch1/set_control ssf aenv "Release Time (s)" 1.5
+
+/patches/patch1/set_control ssf fenv "Trigger" 0
+/patches/patch1/set_control ssf fenv "Delay Time (s)" 0.0
+/patches/patch1/set_control ssf fenv "Attack Time (s)" 0.0
+/patches/patch1/set_control ssf fenv "Hold Time (s)" 0.0
+/patches/patch1/set_control ssf fenv "Decay Time (s)" 0.1
+/patches/patch1/set_control ssf fenv "Sustain Level" 0.0
+/patches/patch1/set_control ssf fenv "Release Time (s)" 0.0
+
+/patches/patch1/activate
+/engine/set_master_patch s patch1
+/engine/start
diff --git a/src/progs/console/patches/old_super_simple_patch.omp b/src/progs/console/patches/old_super_simple_patch.omp
new file mode 100644
index 00000000..1abd3c23
--- /dev/null
+++ b/src/progs/console/patches/old_super_simple_patch.omp
@@ -0,0 +1,36 @@
+/create_patch si patch1 4
+/patches/patch1/add_output s out_1
+/patches/patch1/add_output s out_2
+
+/patches/patch1/add_midi_input
+
+/patches/patch1/load_ladspa_plugin isss 1 triangle_1649.so triangle_fcsc_oa osc
+/patches/patch1/load_ladspa_plugin isss 1 dahdsr_2021.so dahdsr_cg+t_control aenv
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaia_oa amp
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaic_oa endamp
+
+/patches/patch1/connect ssss midi_in Frequency osc Frequency
+/patches/patch1/connect ssss midi_in Gate aenv "Gate"
+/patches/patch1/connect ssss osc Output amp "First Input"
+/patches/patch1/connect ssss aenv "Envelope Out" amp "Second Input"
+/patches/patch1/connect ssss amp "Product Output" endamp "First Input"
+/patches/patch1/connect ssss endamp "Product Output" output out_1
+/patches/patch1/connect ssss endamp "Product Output" output out_2
+
+
+/patches/patch1/set_control ssf midi_in Frequency 200
+/patches/patch1/set_control ssf midi_in Gate 0
+/patches/patch1/set_control ssf osc Slope 0.5
+/patches/patch1/set_control ssf endamp "Second Input" 0.5
+
+/patches/patch1/set_control ssf aenv "Trigger" 0
+/patches/patch1/set_control ssf aenv "Delay Time (s)" 0.0
+/patches/patch1/set_control ssf aenv "Attack Time (s)" 0.001
+/patches/patch1/set_control ssf aenv "Hold Time (s)" 0.0
+/patches/patch1/set_control ssf aenv "Decay Time (s)" 0.3
+/patches/patch1/set_control ssf aenv "Sustain Level" 0.5
+/patches/patch1/set_control ssf aenv "Release Time (s)" 0.5
+
+/patches/patch1/activate
+/engine/set_master_patch s patch1
+/engine/start
diff --git a/src/progs/console/patches/send_test.omp b/src/progs/console/patches/send_test.omp
new file mode 100644
index 00000000..579a3b80
--- /dev/null
+++ b/src/progs/console/patches/send_test.omp
@@ -0,0 +1,2 @@
+/load_plugins
+/send_plugins
diff --git a/src/progs/console/patches/simple_patch.omp b/src/progs/console/patches/simple_patch.omp
new file mode 100644
index 00000000..aaa38827
--- /dev/null
+++ b/src/progs/console/patches/simple_patch.omp
@@ -0,0 +1,54 @@
+/load_plugins
+/create_patch si patch1 1
+/patches/patch1/add_output s out_1
+/patches/patch1/add_output s out_2
+
+/patches/patch1/add_midi_input
+
+/patches/patch1/load_ladspa_plugin isss 1 triangle_1649.so triangle_fcsc_oa osc
+/patches/patch1/load_ladspa_plugin isss 1 dahdsr_2021.so dahdsr_cg+t_control aenv
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaia_oa amp
+/patches/patch1/load_ladspa_plugin isss 1 lp4pole_1671.so lp4pole_fcrcia_oa filt
+/patches/patch1/load_ladspa_plugin isss 1 dahdsr_2021.so dahdsr_cg+t_control fenv
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaic_oa fscale
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaic_oa endamp
+
+/patches/patch1/connect ssss midi_in Frequency osc Frequency
+/patches/patch1/connect ssss midi_in Gate aenv "Gate"
+/patches/patch1/connect ssss midi_in Gate fenv "Gate"
+/patches/patch1/connect ssss osc Output filt Input
+/patches/patch1/connect ssss fenv "Envelope Out" fscale "First Input"
+/patches/patch1/connect ssss fscale "Product Output" filt "Cutoff Frequency"
+/patches/patch1/connect ssss filt Output amp "First Input"
+/patches/patch1/connect ssss aenv "Envelope Out" amp "Second Input"
+/patches/patch1/connect ssss amp "Product Output" endamp "First Input"
+/patches/patch1/connect ssss endamp "Product Output" output out_1
+/patches/patch1/connect ssss endamp "Product Output" output out_2
+
+
+/patches/patch1/set_control ssf midi_in Frequency 200.0
+/patches/patch1/set_control ssf midi_in Gate 0.0
+/patches/patch1/set_control ssf filt Resonance 2.0
+/patches/patch1/set_control ssf osc Slope 1.0
+/patches/patch1/set_control ssf fscale "Second Input" 900
+/patches/patch1/set_control ssf endamp "Second Input" 0.5
+
+/patches/patch1/set_control ssf aenv "Trigger" 0
+/patches/patch1/set_control ssf aenv "Delay Time (s)" 0.0
+/patches/patch1/set_control ssf aenv "Attack Time (s)" 0.01
+/patches/patch1/set_control ssf aenv "Hold Time (s)" 0.0
+/patches/patch1/set_control ssf aenv "Decay Time (s)" 0.3
+/patches/patch1/set_control ssf aenv "Sustain Level" 0.6
+/patches/patch1/set_control ssf aenv "Release Time (s)" 0.6
+
+/patches/patch1/set_control ssf fenv "Trigger" 0
+/patches/patch1/set_control ssf fenv "Delay Time (s)" 0.0
+/patches/patch1/set_control ssf fenv "Attack Time (s)" 0.0
+/patches/patch1/set_control ssf fenv "Hold Time (s)" 0.0
+/patches/patch1/set_control ssf fenv "Decay Time (s)" 0.2
+/patches/patch1/set_control ssf fenv "Sustain Level" 0.5
+/patches/patch1/set_control ssf fenv "Release Time (s)" 0.5
+
+/patches/patch1/activate
+/engine/set_master_patch s patch1
+/engine/start
diff --git a/src/progs/console/patches/super_simple_patch.omp b/src/progs/console/patches/super_simple_patch.omp
new file mode 100644
index 00000000..02dc63a2
--- /dev/null
+++ b/src/progs/console/patches/super_simple_patch.omp
@@ -0,0 +1,36 @@
+/load_plugins
+
+/create_patch si patch1 1
+
+/patches/patch1/load_internal_plugin iss 0 midi_in midi_in
+/patches/patch1/load_internal_plugin iss 0 output output
+
+/patches/patch1/load_ladspa_plugin isss 0 triangle_1649.so triangle_fcsc_oa osc
+/patches/patch1/load_ladspa_plugin isss 0 dahdsr_2021.so dahdsr_cg+t_control aenv
+/patches/patch1/load_ladspa_plugin isss 0 product_1668.so product_iaia_oa amp
+/patches/patch1/load_ladspa_plugin isss 0 product_1668.so product_iaic_oa endamp
+
+/patches/patch1/connect sisi midi_in 0 osc 0
+/patches/patch1/connect sisi midi_in 1 aenv 0
+/patches/patch1/connect sisi osc 2 amp 0
+/patches/patch1/connect sisi aenv 8 amp 1
+/patches/patch1/connect sisi amp 2 endamp 0
+/patches/patch1/connect sisi endamp 2 output 0
+/patches/patch1/connect sisi endamp 2 output 1
+
+
+/patches/patch1/set_control sif midi_in 0 200
+/patches/patch1/set_control sif midi_in 1 1.0
+/patches/patch1/set_control sif osc 1 0.5
+/patches/patch1/set_control sif endamp 1 0.5
+
+/patches/patch1/set_control sif aenv 1 0
+/patches/patch1/set_control sif aenv 2 0.0
+/patches/patch1/set_control sif aenv 3 0.001
+/patches/patch1/set_control sif aenv 4 0.0
+/patches/patch1/set_control sif aenv 5 0.3
+/patches/patch1/set_control sif aenv 6 0.5
+/patches/patch1/set_control sif aenv 7 0.5
+
+/engine/set_master_patch i 0
+/engine/start
diff --git a/src/progs/console/patches/test_patch.omp b/src/progs/console/patches/test_patch.omp
new file mode 100644
index 00000000..04c0487b
--- /dev/null
+++ b/src/progs/console/patches/test_patch.omp
@@ -0,0 +1,48 @@
+/create_patch si patch1 1
+/patches/patch1/add_output s out_1
+/patches/patch1/add_output s out_2
+
+/patches/patch1/add_midi_input
+
+/patches/patch1/load_ladspa_plugin isss 1 triangle_1649.so triangle_fcsc_oa osc
+/patches/patch1/load_ladspa_plugin isss 1 adsr_1653.so adsr aenv
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaia_oa amp
+/patches/patch1/load_ladspa_plugin isss 1 lp4pole_1671.so lp4pole_fcrcia_oa filt
+/patches/patch1/load_ladspa_plugin isss 1 adsr_1653.so adsr fenv
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaic_oa fscale
+/patches/patch1/load_ladspa_plugin isss 1 product_1668.so product_iaic_oa endamp
+
+/patches/patch1/connect ssss midi_in Frequency osc Frequency
+/patches/patch1/connect ssss midi_in Gate aenv "Driving Signal"
+/patches/patch1/connect ssss midi_in Gate fenv "Driving Signal"
+/patches/patch1/connect ssss osc Output filt Input
+/patches/patch1/connect ssss fenv "Envelope Out" output out_1
+/patches/patch1/connect ssss fscale "Product Output" filt "Cutoff Frequency"
+/patches/patch1/connect ssss filt Output amp "First Input"
+/patches/patch1/connect ssss aenv "Envelope Out" output out_2
+/patches/patch1/connect ssss amp "Product Output" endamp "First Input"
+/patches/patch1/connect ssss endamp "Product Output" output out_1
+/patches/patch1/connect ssss endamp "Product Output" output out_2
+
+
+/patches/patch1/set_control ssf midi_in Frequency 200
+/patches/patch1/set_control ssf filt Resonance 3.0
+/patches/patch1/set_control ssf osc Slope 1.0
+/patches/patch1/set_control ssf fscale "Second Input" 900
+/patches/patch1/set_control ssf endamp "Second Input" 0.5
+
+/patches/patch1/set_control ssf aenv "Trigger Threshold" 0
+/patches/patch1/set_control ssf aenv "Attack Time (s)" 0.1
+/patches/patch1/set_control ssf aenv "Decay Time (s)" 0.3
+/patches/patch1/set_control ssf aenv "Sustain Level" 0.5
+/patches/patch1/set_control ssf aenv "Release Time (s)" 0.5
+
+/patches/patch1/set_control ssf fenv "Trigger Threshold" 0
+/patches/patch1/set_control ssf fenv "Attack Time (s)" 0.1
+/patches/patch1/set_control ssf fenv "Decay Time (s)" 0.2
+/patches/patch1/set_control ssf fenv "Sustain Level" 0.6
+/patches/patch1/set_control ssf fenv "Release Time (s)" 0.4
+
+/patches/patch1/activate
+/engine/set_master_patch s patch1
+/engine/start
diff --git a/src/progs/demolition/DemolitionClientInterface.cpp b/src/progs/demolition/DemolitionClientInterface.cpp
new file mode 100644
index 00000000..e58371d4
--- /dev/null
+++ b/src/progs/demolition/DemolitionClientInterface.cpp
@@ -0,0 +1,109 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DemolitionClientInterface.h"
+#include "DemolitionModel.h"
+
+
+DemolitionClientInterface::DemolitionClientInterface(DemolitionModel* model)
+: m_model(model)
+{
+}
+
+
+DemolitionClientInterface::~DemolitionClientInterface()
+{
+}
+
+
+void
+DemolitionClientInterface::error(const string& msg)
+{
+}
+
+
+void
+DemolitionClientInterface::new_patch_model(PatchModel* pm)
+{
+ m_model->add_patch(pm);
+}
+
+
+void
+DemolitionClientInterface::new_port_model(PortModel* port_model)
+{
+ m_model->add_port(port_model);
+}
+
+
+void
+DemolitionClientInterface::object_destroyed(const string& path)
+{
+ m_model->remove_object(path);
+}
+
+void
+DemolitionClientInterface::patch_enabled(const string& path)
+{
+}
+
+
+void
+DemolitionClientInterface::patch_disabled(const string& path)
+{
+}
+
+
+void
+DemolitionClientInterface::new_node_model(NodeModel* nm)
+{
+ m_model->add_node(nm);
+}
+
+
+void
+DemolitionClientInterface::object_renamed(const string& old_path, const string& new_path)
+{
+ m_model->object_renamed(old_path, new_path);
+}
+
+
+void
+DemolitionClientInterface::connection_model(ConnectionModel* cm)
+{
+ m_model->add_connection(cm);
+}
+
+
+void
+DemolitionClientInterface::disconnection(const string& src_port_path, const string& dst_port_path)
+{
+ m_model->remove_connection(src_port_path, dst_port_path);
+}
+
+
+void
+DemolitionClientInterface::control_change(const string& port_path, float value)
+{
+}
+
+
+void
+DemolitionClientInterface::new_plugin_model(PluginModel* pi)
+{
+ m_model->add_plugin(pi);
+}
+
diff --git a/src/progs/demolition/DemolitionClientInterface.h b/src/progs/demolition/DemolitionClientInterface.h
new file mode 100644
index 00000000..374008b5
--- /dev/null
+++ b/src/progs/demolition/DemolitionClientInterface.h
@@ -0,0 +1,75 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef DEMOLITIONCLIENTHOOKS_H
+#define DEMOLITIONCLIENTHOOKS_H
+
+#include <cstdlib>
+#include <string>
+#include "ModelClientInterface.h"
+#include "DemolitionModel.h"
+#include "PluginModel.h"
+using std::string;
+
+using namespace LibOmClient;
+
+
+/** ModelClientInterface implementation for the Gtk client.
+ *
+ * These are the hooks into the GUI for the Comm class.
+ *
+ * \ingroup OmGtk
+ */
+class DemolitionClientInterface : public ModelClientInterface
+{
+public:
+ DemolitionClientInterface(DemolitionModel* model);
+ virtual ~DemolitionClientInterface();
+
+ void bundle_begin() {}
+ void bundle_end() {}
+
+ void num_plugins(size_t num) {}
+
+ // OSC thread functions
+ void error(const string& msg);
+
+ void new_plugin(const string& type,
+ const string& uri,
+ const string& name) {}
+ void new_patch_model(PatchModel* const pm);
+ void new_port_model(PortModel* const port_model);
+ void object_destroyed(const string& path);
+ void patch_enabled(const string& path);
+ void patch_disabled(const string& path);
+ void patch_cleared(const string& path) { throw; }
+ void new_node_model(NodeModel* const nm);
+ void object_renamed(const string& old_path, const string& new_path);
+ void connection_model(ConnectionModel* const cm);
+ void disconnection(const string& src_port_path, const string& dst_port_path);
+ void metadata_update(const string& path, const string& key, const string& value) {}
+ void control_change(const string& port_path, float value);
+ void new_plugin_model(PluginModel* const pi);
+ void program_add(const string& path, uint32_t bank, uint32_t program, const string& name) {};
+ void program_remove(const string& path, uint32_t bank, uint32_t program) {};
+
+private:
+ DemolitionModel* m_model;
+};
+
+
+#endif // DEMOLITIONCLIENTHOOKS_H
diff --git a/src/progs/demolition/DemolitionModel.cpp b/src/progs/demolition/DemolitionModel.cpp
new file mode 100644
index 00000000..786c08cd
--- /dev/null
+++ b/src/progs/demolition/DemolitionModel.cpp
@@ -0,0 +1,246 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DemolitionModel.h"
+#include "util/Path.h"
+#include "PluginModel.h"
+#include <stdlib.h>
+#include <iostream>
+using std::cout; using std::cerr; using std::endl;
+
+
+DemolitionModel::~DemolitionModel()
+{
+ for (vector<PluginModel*>::iterator i = m_plugins.begin(); i != m_plugins.end(); ++i)
+ delete *i;
+
+ for (vector<PatchModel*>::iterator i = m_patches.begin(); i != m_patches.end(); ++i)
+ delete *i;
+}
+
+
+PatchModel*
+DemolitionModel::random_patch()
+{
+ if (m_patches.size() == 0)
+ return NULL;
+ else
+ return m_patches.at(rand() % m_patches.size());
+}
+
+
+NodeModel*
+DemolitionModel::random_node()
+{
+ int max_attempts = m_patches.size()*4;
+ int attempts = 0;
+
+ while (attempts++ < max_attempts) {
+ PatchModel* pm = random_patch();
+
+ if (pm == NULL) {
+ return NULL;
+ } else {
+ int size = pm->nodes().size();
+ if (size == 0)
+ continue;
+ int index = rand() % size;
+ NodeModelMap::const_iterator i = pm->nodes().begin();
+ for (int j=0; j < index; ++j) {
+ ++i;
+ if (i == pm->nodes().end())
+ return NULL;
+ }
+ return (*i).second;
+ }
+ }
+ //cout << "***************************** Not returning node *********" << endl;
+ return NULL;
+}
+
+
+NodeModel*
+DemolitionModel::random_node_in_patch(PatchModel* pm)
+{
+ if (pm == NULL)
+ return NULL;
+
+ int size = pm->nodes().size();
+ if (size == 0)
+ return NULL;
+
+ int index = rand() % size;
+ NodeModelMap::const_iterator i = pm->nodes().begin();
+ for (int j=0; j < index; ++j) {
+ ++i;
+ if (i == pm->nodes().end())
+ return NULL;
+ }
+ return (*i).second;
+}
+
+
+PortModel*
+DemolitionModel::random_port()
+{
+ NodeModel* node = random_node();
+
+ if (node == NULL) return NULL;
+
+ PortModelList ports = node->ports();
+
+ int num_ports = ports.size();
+ int index = rand() % num_ports;
+ int i = 0;
+
+ for (PortModelList::iterator p = ports.begin(); p != ports.end(); ++p, ++i)
+ if (i == index)
+ return (*p);
+
+ return NULL; // shouldn't happen
+}
+
+
+PortModel*
+DemolitionModel::random_port_in_node(NodeModel* node)
+{
+ if (node == NULL)
+ return NULL;
+
+ PortModelList ports = node->ports();
+
+ int num_ports = ports.size();
+ int index = rand() % num_ports;
+ int i = 0;
+
+ for (PortModelList::iterator p = ports.begin(); p != ports.end(); ++p, ++i)
+ if (i == index)
+ return (*p);
+
+ return NULL; // shouldn't happen
+}
+
+
+PluginModel*
+DemolitionModel::random_plugin()
+{
+ if (m_plugins.size() == 0)
+ return NULL;
+ else
+ return m_plugins.at(rand() % m_plugins.size());
+}
+
+
+PatchModel*
+DemolitionModel::patch(const Path& path)
+{
+ for (vector<PatchModel*>::iterator i = m_patches.begin(); i != m_patches.end(); ++i) {
+ if ((*i)->path() == path)
+ return (*i);
+ }
+ return NULL;
+}
+
+
+NodeModel*
+DemolitionModel::node(const Path& path)
+{
+ NodeModel* ret = NULL;
+
+ for (vector<PatchModel*>::iterator i = m_patches.begin(); i != m_patches.end(); ++i) {
+ ret = (*i)->get_node(path.name());
+ if (ret != NULL)
+ break;
+ }
+ return ret;
+}
+
+
+void
+DemolitionModel::remove_object(const Path& path)
+{
+ // Patch
+ for (vector<PatchModel*>::iterator i = m_patches.begin(); i != m_patches.end(); ++i) {
+ if ((*i)->path() == path) {
+ delete (*i);
+ m_patches.erase(i);
+ break;
+ }
+ }
+
+ // Node
+ PatchModel* parent = patch(path.parent());
+ if (parent != NULL)
+ parent->remove_node(path.name());
+}
+
+
+void
+DemolitionModel::add_node(NodeModel* nm)
+{
+ PatchModel* parent = patch(nm->path().parent());
+ if (parent == NULL) {
+ cout << "**** ERROR: Parent for new Node does not exist." << endl;
+ } else {
+ parent->add_node(nm);
+ }
+}
+
+
+void
+DemolitionModel::add_port(PortModel* pm)
+{
+}
+
+
+void
+DemolitionModel::add_connection(ConnectionModel* cm)
+{
+}
+
+
+void
+DemolitionModel::remove_connection(const Path& src_port_path, const Path& dst_port_path)
+{
+}
+
+void
+DemolitionModel::object_renamed(const Path& old_path, const Path& new_path)
+{
+ /* FIXME: broken, does not rename children
+ assert(OmPath::parent(old_path) == OmPath::parent(new_path));
+
+ // Rename node
+ NodeModel* nm = get_node(old_path);
+ if (nm != NULL) {
+ if (nm->parent() != NULL) {
+ nm->parent()->remove_node(OmPath::name(old_path));
+ nm->path(new_path);
+ nm->parent()->add_node(OmPath::name(new_path));
+ }
+ }
+
+ // Rename patch
+ for (vector<PatchModel*>::iterator i = m_patches.begin(); i != m_patches.end(); ++i) {
+ if ((*i)->path() == path) {
+ delete (*i);
+ m_patches.erase(i);
+ return;
+ }
+ }
+ */
+}
+
diff --git a/src/progs/demolition/DemolitionModel.h b/src/progs/demolition/DemolitionModel.h
new file mode 100644
index 00000000..64e0cc61
--- /dev/null
+++ b/src/progs/demolition/DemolitionModel.h
@@ -0,0 +1,60 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DEMOLITIONMODEL_H
+#define DEMOLITIONMODEL_H
+
+#include <vector>
+#include "PatchModel.h"
+#include "util/Path.h"
+using Om::Path;
+using std::vector;
+
+using namespace LibOmClient;
+
+class DemolitionModel {
+public:
+ ~DemolitionModel();
+
+ PatchModel* random_patch();
+ NodeModel* random_node();
+ NodeModel* random_node_in_patch(PatchModel* patch);
+ PortModel* random_port();
+ PortModel* random_port_in_node(NodeModel* node);
+ PluginModel* random_plugin();
+
+ PatchModel* patch(const Path& path);
+ NodeModel* node(const Path& path);
+
+ void add_patch(PatchModel* pm) { m_patches.push_back(pm); }
+ void add_node(NodeModel* nm);
+ void add_port(PortModel* pm);
+ void remove_object(const Path& path);
+ void add_connection(ConnectionModel* cm);
+ void remove_connection(const Path& src_port_path, const Path& dst_port_path);
+ void add_plugin(PluginModel* pi) { m_plugins.push_back(pi); }
+
+ void object_renamed(const Path& old_path, const Path& new_path);
+
+ int num_patches() { return m_patches.size(); }
+
+private:
+ vector<PluginModel*> m_plugins;
+ vector<PatchModel*> m_patches;
+};
+
+
+#endif // DEMOLITIONMODEL_H
diff --git a/src/progs/demolition/Makefile.am b/src/progs/demolition/Makefile.am
new file mode 100644
index 00000000..4854f045
--- /dev/null
+++ b/src/progs/demolition/Makefile.am
@@ -0,0 +1,17 @@
+EXTRA_DIST = README
+
+om_demolition_CXXFLAGS = -I$(top_srcdir)/src/clients -I$(top_srcdir)/src/common -DPKGDATADIR=\"$(pkgdatadir)\" $(LXML2_CFLAGS) $(LOSC_CFLAGS)
+om_demolition_LDADD = ../libomclient.a $(LOSC_LIBS) $(LXML2_LIBS)
+
+bin_PROGRAMS = om_demolition
+
+om_demolition_DEPENDENCIES = ../libomclient.a
+
+om_demolition_SOURCES = \
+ DemolitionClientInterface.h \
+ DemolitionClientInterface.cpp \
+ DemolitionModel.h \
+ DemolitionModel.cpp \
+ demolition.cpp \
+ cmdline.h \
+ cmdline.c
diff --git a/src/progs/demolition/README b/src/progs/demolition/README
new file mode 100644
index 00000000..536d3481
--- /dev/null
+++ b/src/progs/demolition/README
@@ -0,0 +1,7 @@
+This is a stress tester for the Om engine.
+
+It constantly sends random events (with a bias towards sane events) to the
+engine, to test for potential segfaults (or assertion failures, etc).
+
+It's not very clever at the moment, and doesn't hit all test cases. Renaming
+is also not supported yet.
diff --git a/src/progs/demolition/cmdline.c b/src/progs/demolition/cmdline.c
new file mode 100644
index 00000000..11fcd108
--- /dev/null
+++ b/src/progs/demolition/cmdline.c
@@ -0,0 +1,149 @@
+/*
+ File autogenerated by gengetopt version 2.10
+ generated with the following command:
+ gengetopt
+
+ The developers of gengetopt consider the fixed text that goes in all
+ gengetopt output files to be in the public domain:
+ we make no copyright claims on it.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#include "cmdline.h"
+
+void
+cmdline_parser_print_version (void)
+{
+ printf ("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION);
+}
+
+void
+cmdline_parser_print_help (void)
+{
+ cmdline_parser_print_version ();
+ printf("\n"
+ "Usage: %s [OPTIONS]...\n", CMDLINE_PARSER_PACKAGE);
+ printf(" -h --help Print help and exit\n");
+ printf(" -V --version Print version and exit\n");
+ printf(" -uSTRING --engine-url=STRING Om engine URL to connect to\n");
+ printf(" -pINT --client-port=INT Client port to listen on\n");
+}
+
+
+static char *gengetopt_strdup (const char *s);
+
+/* gengetopt_strdup() */
+/* strdup.c replacement of strdup, which is not standard */
+char *
+gengetopt_strdup (const char *s)
+{
+ char *result = (char*)malloc(strlen(s) + 1);
+ if (result == (char*)0)
+ return (char*)0;
+ strcpy(result, s);
+ return result;
+}
+
+int
+cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info)
+{
+ int c; /* Character of the parsed option. */
+ int missing_required_options = 0;
+
+ args_info->help_given = 0 ;
+ args_info->version_given = 0 ;
+ args_info->engine_url_given = 0 ;
+ args_info->client_port_given = 0 ;
+#define clear_args() { \
+ args_info->engine_url_arg = NULL; \
+}
+
+ clear_args();
+
+ optarg = 0;
+ optind = 1;
+ opterr = 1;
+ optopt = '?';
+
+ while (1)
+ {
+ int option_index = 0;
+ char *stop_char;
+
+ static struct option long_options[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "engine-url", 1, NULL, 'u' },
+ { "client-port", 1, NULL, 'p' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ stop_char = 0;
+ c = getopt_long (argc, argv, "hVu:p:", long_options, &option_index);
+
+ if (c == -1) break; /* Exit from `while (1)' loop. */
+
+ switch (c)
+ {
+ case 'h': /* Print help and exit. */
+ clear_args ();
+ cmdline_parser_print_help ();
+ exit (EXIT_SUCCESS);
+
+ case 'V': /* Print version and exit. */
+ clear_args ();
+ cmdline_parser_print_version ();
+ exit (EXIT_SUCCESS);
+
+ case 'u': /* Om engine URL to connect to. */
+ if (args_info->engine_url_given)
+ {
+ fprintf (stderr, "%s: `--engine-url' (`-u') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->engine_url_given = 1;
+ args_info->engine_url_arg = gengetopt_strdup (optarg);
+ break;
+
+ case 'p': /* Client port to listen on. */
+ if (args_info->client_port_given)
+ {
+ fprintf (stderr, "%s: `--client-port' (`-p') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->client_port_given = 1;
+ args_info->client_port_arg = strtol (optarg,&stop_char,0);
+ break;
+
+
+ case 0: /* Long option with no short option */
+
+ case '?': /* Invalid option. */
+ /* `getopt_long' already printed an error message. */
+ exit (EXIT_FAILURE);
+
+ default: /* bug: option not considered. */
+ fprintf (stderr, "%s: option unknown: %c\n", CMDLINE_PARSER_PACKAGE, c);
+ abort ();
+ } /* switch */
+ } /* while */
+
+
+ if ( missing_required_options )
+ exit (EXIT_FAILURE);
+
+ return 0;
+}
diff --git a/src/progs/demolition/cmdline.ggo b/src/progs/demolition/cmdline.ggo
new file mode 100644
index 00000000..8fb68170
--- /dev/null
+++ b/src/progs/demolition/cmdline.ggo
@@ -0,0 +1,7 @@
+# Process this file with gengetopt -u to generate the necessary code (in cmdline.h, cmdline.c)
+
+package "om_demolition - A stress testing client for Om"
+
+option "engine-url" u "Om engine URL to connect to" string no
+option "client-port" p "Client port to listen on" int no
+
diff --git a/src/progs/demolition/cmdline.h b/src/progs/demolition/cmdline.h
new file mode 100644
index 00000000..be56aafe
--- /dev/null
+++ b/src/progs/demolition/cmdline.h
@@ -0,0 +1,45 @@
+/* cmdline.h */
+
+/* File autogenerated by gengetopt version 2.10 */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+#define CMDLINE_PARSER_PACKAGE "om_demolotion - A stress testing client for Om"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+struct gengetopt_args_info
+{
+ char * engine_url_arg; /* Om engine URL to connect to. */
+ int client_port_arg; /* Client port to listen on. */
+
+ int help_given ; /* Whether help was given. */
+ int version_given ; /* Whether version was given. */
+ int engine_url_given ; /* Whether engine-url was given. */
+ int client_port_given ; /* Whether client-port was given. */
+
+} ;
+
+int cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info);
+
+void cmdline_parser_print_help(void);
+void cmdline_parser_print_version(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/src/progs/demolition/demolition.cpp b/src/progs/demolition/demolition.cpp
new file mode 100644
index 00000000..84a08c84
--- /dev/null
+++ b/src/progs/demolition/demolition.cpp
@@ -0,0 +1,334 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OSCModelEngineInterface.h"
+#include "OSCModelEngineInterface.h" // FIXME: make conditional
+#include "PatchLibrarian.h"
+#include "DemolitionClientInterface.h"
+#include "interface/ClientInterface.h"
+#include "interface/ClientKey.h"
+#include "util/CountedPtr.h"
+#include <iostream>
+#include <unistd.h>
+#include <stdlib.h>
+#include "cmdline.h" // generated by gengetopt
+
+using std::cout;
+using std::endl;
+
+using namespace LibOmClient;
+
+void do_something();
+
+string random_name();
+
+void create_patch();
+void destroy();
+void add_node();
+void connect();
+void disconnect();
+void disconnect_all();
+void set_port_value();
+void set_port_value_queued();
+void rename_object();
+
+
+// Yay globals!
+DemolitionModel* model;
+OSCModelEngineInterface* engine;
+
+
+int
+main(int argc, char** argv)
+{
+ const char* engine_url = NULL;
+ int client_port = 0;
+
+ /* Parse command line options */
+ gengetopt_args_info args_info;
+ if (cmdline_parser (argc, argv, &args_info) != 0)
+ return 1;
+
+ if (args_info.engine_url_given) {
+ engine_url = args_info.engine_url_arg;
+ } else {
+ cout << "[Main] No engine URL specified. Attempting to use osc.udp://localhost:16180" << endl;
+ engine_url = "osc.udp://localhost:16180";
+ }
+
+ if (args_info.client_port_given)
+ client_port = args_info.client_port_arg;
+ else
+ client_port = 0; // will choose a free port automatically
+
+ model = new DemolitionModel();
+
+ // Create this first so engine interface (liblo) uses the port
+ CountedPtr<DemolitionClientInterface> client
+ = CountedPtr<DemolitionClientInterface>(new DemolitionClientInterface(model));
+
+ engine = new OSCModelEngineInterface(engine_url);
+ engine->activate();
+
+ /* Connect to engine */
+ //engine->attach(engine_url);
+ engine->register_client(ClientKey(), (CountedPtr<ClientInterface>)client);
+
+ engine->load_plugins();
+ engine->request_plugins();
+
+ //int id = engine->get_next_request_id();
+ engine->request_all_objects(/*id*/);
+ //engine->set_wait_response_id(id);
+ //engine->wait_for_response();
+
+ // Disable DSP for stress testing
+ engine->disable_patch("/");
+
+ while (true) {
+ do_something();
+ usleep(100000);
+ }
+
+ sleep(2);
+ engine->unregister_client(ClientKey());
+ //engine->detach();
+
+ delete engine;
+ delete model;
+
+ return 0;
+}
+
+/** Does some random action
+ */
+void
+do_something()
+{
+ int action = rand() % 10;
+
+ switch(action) {
+ case 0:
+ create_patch(); break;
+ case 1:
+ destroy(); break;
+ case 2:
+ add_node(); break;
+ case 3:
+ connect(); break;
+ case 4:
+ disconnect(); break;
+ case 5:
+ disconnect_all(); break;
+ case 6:
+ set_port_value(); break;
+ case 7:
+ set_port_value_queued(); break;
+ case 8:
+ rename_object(); break;
+ default:
+ break;
+ }
+}
+
+
+string
+random_name()
+{
+ int length = (rand()%10)+1;
+ string name(length, '-');
+
+ for (int i=0; i < length; ++i)
+ name[i] = 'a' + rand()%26;
+
+ return name;
+}
+
+
+void
+create_patch()
+{
+ // Make the probability of this happening inversely proportionate to the number
+ // of patches to keep the # in a sane range
+ //if (model->num_patches() > 0 && (rand() % model->num_patches()))
+ // return;
+
+ bool subpatch = rand()%2;
+ PatchModel* parent = NULL;
+ string name = random_name();
+ PatchModel* pm = NULL;
+
+ if (subpatch)
+ parent = model->random_patch();
+
+ if (parent != NULL)
+ pm = new PatchModel(parent->path() +"/"+ name, (rand()%8)+1);
+ else
+ pm = new PatchModel(string("/") + name, (rand()%8)+1);
+
+ cout << "Creating patch " << pm->path() << endl;
+
+ engine->create_patch_from_model(pm);
+
+ // Spread them out a bit for easier monitoring with om_gtk
+ char tmp_buf[8];
+ snprintf(tmp_buf, 8, "%d", 1600 + rand()%800 - 400);
+ engine->set_metadata(pm->path(), "module-x", string(tmp_buf));
+ snprintf(tmp_buf, 8, "%d", 1200 + rand()%700 - 350);
+ engine->set_metadata(pm->path(), "module-y", string(tmp_buf));
+
+ delete pm;
+}
+
+
+void
+destroy()
+{
+ // Make the probability of this happening proportionate to the number
+ // of patches to keep the # in a sane range
+ if (model->num_patches() == 0 || !(rand() % model->num_patches()))
+ return;
+
+ NodeModel* nm = NULL;
+
+ if (rand()%2)
+ nm = model->random_patch();
+ else
+ nm = model->random_node();
+
+ if (nm != NULL) {
+ cout << "Destroying " << nm->path() << endl;
+ engine->destroy(nm->path());
+ }
+}
+
+
+void
+add_node()
+{
+ PatchModel* parent = model->random_patch();
+ PluginModel* plugin = model->random_plugin();
+
+ if (parent != NULL && plugin != NULL) {
+ NodeModel* nm = new NodeModel(parent->path() +"/"+ random_name());
+ nm->plugin(plugin);
+ cout << "Adding node " << nm->path() << endl;
+ engine->create_node_from_model(nm);
+ // Spread them out a bit for easier monitoring with om_gtk
+ char tmp_buf[8];
+ snprintf(tmp_buf, 8, "%d", 1600 + rand()%800 - 400);
+ engine->set_metadata(nm->path(), "module-x", tmp_buf);
+ snprintf(tmp_buf, 8, "%d", 1200 + rand()%700 - 350);
+ engine->set_metadata(nm->path(), "module-y", tmp_buf);
+ }
+}
+
+
+void
+connect()
+{
+ if (!(rand() % 10)) { // Attempt a connection between two nodes in the same patch
+ PatchModel* parent = model->random_patch();
+ NodeModel* n1 = model->random_node_in_patch(parent);
+ NodeModel* n2 = model->random_node_in_patch(parent);
+ PortModel* p1 = model->random_port_in_node(n1);
+ PortModel* p2 = model->random_port_in_node(n2);
+
+ if (p1 != NULL && p2 != NULL) {
+ cout << "Connecting " << p1->path() << " -> " << p2->path() << endl;
+ engine->connect(p1->path(), p2->path());
+ }
+
+ } else { // Attempt a connection between two truly random nodes
+ PortModel* p1 = model->random_port();
+ PortModel* p2 = model->random_port();
+
+ if (p1 != NULL && p2 != NULL) {
+ cout << "Connecting " << p1->path() << " -> " << p2->path() << endl;
+ engine->connect(p1->path(), p2->path());
+ }
+ }
+}
+
+
+void
+disconnect()
+{
+ PortModel* p1 = model->random_port();
+ PortModel* p2 = model->random_port();
+
+ if (p1 != NULL && p2 != NULL) {
+ cout << "Disconnecting " << p1->path() << " -> " << p2->path() << endl;
+ engine->disconnect(p1->path(), p2->path());
+ }
+}
+
+
+void
+disconnect_all()
+{
+ PortModel* p = model->random_port();
+
+ if (p != NULL) {
+ cout << "Disconnecting all from" << p->path() << endl;
+ engine->disconnect_all(p->path());
+ }
+}
+
+
+void
+set_port_value()
+{
+ PortModel* pm = model->random_port();
+ float val = (float)rand() / (float)RAND_MAX;
+
+ if (pm != NULL) {
+ cout << "Setting control for port " << pm->path() << " to " << val << endl;
+ engine->set_port_value(pm->path(), val);
+ }
+}
+
+
+void
+set_port_value_queued()
+{
+ PortModel* pm = model->random_port();
+ float val = (float)rand() / (float)RAND_MAX;
+
+ if (pm != NULL) {
+ cout << "Setting control (slow) for port " << pm->path() << " to " << val << endl;
+ engine->set_port_value_queued(pm->path(), val);
+ }
+}
+
+
+void
+rename_object()
+{
+ // 1/6th chance of it being a patch
+ /*int type = rand()%6;
+
+ if (type == 0) {
+ NodeModel* n = model->random_node();
+ if (n != NULL)
+ engine->rename(n->path(), random_name());
+ } else {
+ PatchModel* p = model->random_patch();
+ if (p != NULL)
+ engine->rename(p->path(), random_name());
+ }*/
+}
+
diff --git a/src/progs/gtk/App.cpp b/src/progs/gtk/App.cpp
new file mode 100644
index 00000000..6b71b4ec
--- /dev/null
+++ b/src/progs/gtk/App.cpp
@@ -0,0 +1,230 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "App.h"
+#include <cassert>
+#include <string>
+#include <fstream>
+#include <libgnomecanvasmm.h>
+#include <time.h>
+#include <sys/time.h>
+#include "ControlInterface.h"
+#include "PatchView.h"
+#include "OmModule.h"
+#include "ControlPanel.h"
+#include "SubpatchModule.h"
+#include "OmFlowCanvas.h"
+#include "GtkObjectController.h"
+#include "PatchController.h"
+#include "NodeController.h"
+#include "PortController.h"
+#include "LoadPluginWindow.h"
+#include "PatchWindow.h"
+#include "MessagesWindow.h"
+#include "ConfigWindow.h"
+#include "Controller.h"
+#include "GladeFactory.h"
+#include "util/Path.h"
+#include "ObjectModel.h"
+#include "PatchModel.h"
+#include "PatchTreeWindow.h"
+#include "Configuration.h"
+#include "ConnectWindow.h"
+#ifdef HAVE_LASH
+#include "LashController.h"
+#endif
+using std::cerr; using std::cout; using std::endl;
+using std::string;
+namespace LibOmClient { class PluginModel; }
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class OmPort;
+
+App::App()
+: m_control_interface(new ControlInterface(this)),
+ m_configuration(new Configuration()),
+ m_about_dialog(NULL),
+ m_enable_signal(true)
+{
+ Glib::RefPtr<Gnome::Glade::Xml> glade_xml = GladeFactory::new_glade_reference();
+
+ glade_xml->get_widget_derived("connect_win", m_connect_window);
+ //glade_xml->get_widget_derived("new_patch_win", m_new_patch_window);
+ //glade_xml->get_widget_derived("load_patch_win", m_load_patch_window);
+ glade_xml->get_widget_derived("config_win", m_config_window);
+ glade_xml->get_widget_derived("patch_tree_win", m_patch_tree_window);
+// glade_xml->get_widget_derived("main_patches_treeview", m_objects_treeview);
+ glade_xml->get_widget("about_win", m_about_dialog);
+
+ m_config_window->configuration(m_configuration);
+
+ glade_xml->get_widget_derived("messages_win", m_messages_window);
+}
+
+
+App::~App()
+{
+ delete m_control_interface;
+}
+
+
+void
+App::error_message(const string& str)
+{
+ m_messages_window->post(str);
+ m_messages_window->show();
+ m_messages_window->raise();
+}
+
+
+/*
+bool
+App::idle_callback()
+{
+ m_client_hooks->process_events();
+
+#ifdef HAVE_LASH
+ //if (lash_controller->enabled())
+ // lash_controller->process_events();
+#endif
+
+ return true;
+}
+*/
+
+
+/******** Event Handlers ************/
+
+
+#if 0
+App::event_load_session()
+{
+ Gtk::FileChooserDialog* dialog
+ = new Gtk::FileChooserDialog(*m_main_window, "Load Session", Gtk::FILE_CHOOSER_ACTION_OPEN);
+
+ dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+ int result = dialog->run();
+ string filename = dialog->get_filename();
+ delete dialog;
+
+ cout << result << endl;
+
+ assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE);
+
+ if (result == Gtk::RESPONSE_OK)
+ //configuration->load_session(filename);
+ _controller->load_session(filename);
+}
+
+
+void
+App::event_save_session_as()
+{
+ Gtk::FileChooserDialog dialog(*m_main_window, "Save Session", Gtk::FILE_CHOOSER_ACTION_SAVE);
+
+ /*
+ Gtk::VBox* box = dialog.get_vbox();
+ Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \
+ without confirmation.");
+ box->pack_start(warning, false, false, 2);
+ Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches");
+ box->pack_start(recursive_checkbutton, false, false, 0);
+ recursive_checkbutton.show();
+ */
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+ int result = dialog.run();
+ //bool recursive = recursive_checkbutton.get_active();
+
+ assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE);
+
+ if (result == Gtk::RESPONSE_OK) {
+ string filename = dialog.get_filename();
+ if (filename.length() < 11 || filename.substr(filename.length()-10) != ".omsession")
+ filename += ".omsession";
+
+ bool confirm = false;
+ std::fstream fin;
+ fin.open(filename.c_str(), std::ios::in);
+ if (fin.is_open()) { // File exists
+ string msg = "File already exists! Are you sure you want to overwrite ";
+ msg += filename + "?";
+ Gtk::MessageDialog confirm_dialog(*m_main_window,
+ msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
+ if (confirm_dialog.run() == Gtk::RESPONSE_YES)
+ confirm = true;
+ else
+ confirm = false;
+ } else { // File doesn't exist
+ confirm = true;
+ }
+ fin.close();
+
+ if (confirm) {
+ _controller->save_session(filename);
+ }
+ }
+}
+#endif
+
+void
+App::add_patch_window(PatchWindow* pw)
+{
+ m_windows.push_back(pw);
+}
+
+
+void
+App::remove_patch_window(PatchWindow* pw)
+{
+ m_windows.erase(find(m_windows.begin(), m_windows.end(), pw));
+}
+
+
+/** Returns the number of Patch windows currently visible.
+ */
+int
+App::num_open_patch_windows()
+{
+ int ret = 0;
+ for (list<PatchWindow*>::iterator i = m_windows.begin(); i != m_windows.end(); ++i)
+ if ((*i)->is_visible())
+ ++ret;
+
+ return ret;
+}
+
+void
+App::quit()
+{
+ Gtk::Main::quit();
+}
+
+void
+App::quit_and_kill()
+{
+ Controller::instance().quit();
+ Gtk::Main::quit();
+}
+
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/App.h b/src/progs/gtk/App.h
new file mode 100644
index 00000000..1ba18962
--- /dev/null
+++ b/src/progs/gtk/App.h
@@ -0,0 +1,123 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OMGTKAPP_H
+#define OMGTKAPP_H
+
+#include <cassert>
+#include <string>
+#include <map>
+#include <list>
+#include <iostream>
+#include <libgnomecanvasmm.h>
+#include <gtkmm.h>
+#include <libglademm.h>
+using std::string; using std::map; using std::list;
+using std::cerr; using std::endl;
+
+namespace LibOmClient { class PatchModel; class PluginModel; }
+using namespace LibOmClient;
+
+/** \defgroup OmGtk GTK Client
+ */
+
+namespace OmGtk {
+
+class PatchWindow;
+class GtkObjectController;
+class PatchController;
+class NodeController;
+class PortController;
+class LoadPatchWindow;
+class MessagesWindow;
+class ConfigWindow;
+class OmGtkObject;
+class OmModule;
+class OmPort;
+class OmFlowCanvas;
+class PatchTreeView;
+class PatchTreeWindow;
+class ControlInterface;
+class ConnectWindow;
+class Configuration;
+
+
+/** Singleton master class most everything is contained within.
+ *
+ * This is a horrible god-object, but it's shrinking in size as things are
+ * moved out. Hopefully it will go away entirely some day..
+ *
+ * \ingroup OmGtk
+ */
+class App
+{
+public:
+ ~App();
+
+ void error_message(const string& msg);
+
+ void quit();
+ void quit_and_kill();
+
+ void add_patch_window(PatchWindow* pw);
+ void remove_patch_window(PatchWindow* pw);
+
+ int num_open_patch_windows();
+
+ ConnectWindow* connect_window() const { return m_connect_window; }
+ Gtk::Dialog* about_dialog() const { return m_about_dialog; }
+ ConfigWindow* configuration_dialog() const { return m_config_window; }
+ MessagesWindow* messages_dialog() const { return m_messages_window; }
+ PatchTreeWindow* patch_tree() const { return m_patch_tree_window; }
+ Configuration* configuration() const { return m_configuration; }
+
+ ControlInterface* control_interface() { return m_control_interface; }
+
+ static void instantiate() { if (!_instance) _instance = new App(); }
+ static inline App& instance() { assert(_instance); return *_instance; }
+
+protected:
+ App();
+ static App* _instance;
+
+ //bool connect_callback();
+ //bool idle_callback();
+
+ ControlInterface* m_control_interface;
+ Configuration* m_configuration;
+
+ list<PatchWindow*> m_windows;
+
+ ConnectWindow* m_connect_window;
+ MessagesWindow* m_messages_window;
+ PatchTreeWindow* m_patch_tree_window;
+ ConfigWindow* m_config_window;
+ Gtk::Dialog* m_about_dialog;
+ Gtk::Button* m_engine_error_close_button;
+
+ /** Used to avoid feedback loops with (eg) process checkbutton
+ * FIXME: Maybe this should be globally implemented at the Controller level,
+ * disable all command sending while handling events to avoid feedback
+ * issues with widget event callbacks? This same pattern is duplicated
+ * too much... */
+ bool m_enable_signal;
+};
+
+
+} // namespace OmGtk
+
+#endif // OMGTKAPP_H
+
diff --git a/src/progs/gtk/BreadCrumb.h b/src/progs/gtk/BreadCrumb.h
new file mode 100644
index 00000000..f79ad363
--- /dev/null
+++ b/src/progs/gtk/BreadCrumb.h
@@ -0,0 +1,66 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BREADCRUMB_H
+#define BREADCRUMB_H
+
+#include <gtkmm.h>
+#include "PatchWindow.h"
+#include "PatchController.h"
+#include "PatchModel.h"
+
+namespace OmGtk {
+
+
+/** Breadcrumb button in a PatchWindow.
+ *
+ * \ingroup OmGtk
+ */
+class BreadCrumb : public Gtk::ToggleButton
+{
+public:
+ BreadCrumb(PatchWindow* window, PatchController* patch)
+ : m_window(window),
+ m_patch(patch)
+ {
+ assert(patch != NULL);
+ set_border_width(0);
+ path(m_patch->path());
+ signal_clicked().connect(sigc::bind(sigc::mem_fun(
+ m_window, &PatchWindow::breadcrumb_clicked), this));
+ show_all();
+ }
+
+ PatchController* patch() { return m_patch; }
+
+ void path(const Path& path)
+ {
+ remove();
+ const string text = (path == "/") ? "/" : path.name();
+ Gtk::Label* lab = manage(new Gtk::Label(text));
+ lab->set_padding(0, 0);
+ lab->show();
+ add(*lab);
+ }
+
+private:
+ PatchWindow* m_window;
+ PatchController* m_patch;
+};
+
+} // namespace OmGtk
+
+#endif // BREADCRUMB_H
diff --git a/src/progs/gtk/ConfigWindow.cpp b/src/progs/gtk/ConfigWindow.cpp
new file mode 100644
index 00000000..db3bce18
--- /dev/null
+++ b/src/progs/gtk/ConfigWindow.cpp
@@ -0,0 +1,86 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ConfigWindow.h"
+#include <iostream>
+#include <cassert>
+#include <algorithm>
+#include <cctype>
+#include "PatchController.h"
+#include "NodeModel.h"
+#include "OmFlowCanvas.h"
+using std::cout; using std::cerr; using std::endl;
+
+
+namespace OmGtk {
+
+ConfigWindow::ConfigWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ m_configuration(NULL)
+{
+ xml->get_widget("config_path_entry", m_path_entry);
+ xml->get_widget("config_save_button", m_save_button);
+ xml->get_widget("config_cancel_button", m_cancel_button);
+ xml->get_widget("config_ok_button", m_ok_button);
+
+ m_save_button->signal_clicked().connect( sigc::mem_fun(this, &ConfigWindow::save_clicked));
+ m_cancel_button->signal_clicked().connect(sigc::mem_fun(this, &ConfigWindow::cancel_clicked));
+ m_ok_button->signal_clicked().connect( sigc::mem_fun(this, &ConfigWindow::ok_clicked));
+}
+
+
+/** Sets the state manager for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+ConfigWindow::configuration(Configuration* sm)
+{
+ m_configuration = sm;
+ m_path_entry->set_text(sm->patch_path());
+}
+
+
+
+///// Event Handlers //////
+
+
+void
+ConfigWindow::save_clicked()
+{
+ m_configuration->patch_path(m_path_entry->get_text());
+ m_configuration->apply_settings();
+ m_configuration->save_settings();
+}
+
+
+void
+ConfigWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+void
+ConfigWindow::ok_clicked()
+{
+ m_configuration->patch_path(m_path_entry->get_text());
+ m_configuration->apply_settings();
+ hide();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/ConfigWindow.h b/src/progs/gtk/ConfigWindow.h
new file mode 100644
index 00000000..caf1531f
--- /dev/null
+++ b/src/progs/gtk/ConfigWindow.h
@@ -0,0 +1,63 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef CONFIGWINDOW_H
+#define CONFIGWINDOW_H
+
+#include "PluginModel.h"
+#include "Configuration.h"
+#include <list>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <gtkmm.h>
+
+using std::list;
+using LibOmClient::PluginModel;
+
+namespace OmGtk {
+
+
+/** 'Configuration' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup OmGtk
+ */
+class ConfigWindow : public Gtk::Window
+{
+public:
+ ConfigWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void configuration(Configuration* sm);
+
+private:
+ void save_clicked();
+ void cancel_clicked();
+ void ok_clicked();
+
+ Configuration* m_configuration;
+
+ Gtk::Entry* m_path_entry;
+ Gtk::Button* m_save_button;
+ Gtk::Button* m_cancel_button;
+ Gtk::Button* m_ok_button;
+};
+
+
+} // namespace OmGtk
+
+#endif // CONFIGWINDOW_H
diff --git a/src/progs/gtk/Configuration.cpp b/src/progs/gtk/Configuration.cpp
new file mode 100644
index 00000000..e4882cc3
--- /dev/null
+++ b/src/progs/gtk/Configuration.cpp
@@ -0,0 +1,184 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Configuration.h"
+#include <cstdlib>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <map>
+#include "PortModel.h"
+#include "PluginModel.h"
+#include "PatchController.h"
+#include "PatchModel.h"
+#include "OmFlowCanvas.h"
+#include "Controller.h"
+
+using std::cerr; using std::cout; using std::endl;
+using std::map; using std::string;
+using LibOmClient::PatchModel;
+
+namespace OmGtk {
+
+using namespace LibOmClient;
+
+
+Configuration::Configuration()
+: m_patch_path("/usr/share/om/patches:/usr/local/share/om/patches"),
+ m_audio_port_color( 0x394f66FF),
+ m_control_port_color(0x396639FF),
+ m_midi_port_color( 0x663939FF)
+{
+}
+
+
+Configuration::~Configuration()
+{
+}
+
+
+/** Loads settings from the rc file. Passing no parameter will load from
+ * the default location.
+ */
+void
+Configuration::load_settings(string filename)
+{
+ if (filename == "")
+ filename = string(getenv("HOME")).append("/.omgtkrc");
+
+ std::ifstream is;
+ is.open(filename.c_str(), std::ios::in);
+
+ if ( ! is.good()) {
+ cout << "[Configuration] Unable to open settings file " << filename << endl;
+ return;
+ } else {
+ cout << "[Configuration] Loading settings from " << filename << endl;
+ }
+
+ string s;
+
+ is >> s;
+ if (s != "file_version") {
+ cerr << "[Configuration] Corrupt settings file, load aborted." << endl;
+ is.close();
+ return;
+ }
+
+ is >> s;
+ if (s != "1") {
+ cerr << "[Configuration] Unknown settings file version number, load aborted." << endl;
+ is.close();
+ return;
+ }
+
+ is >> s;
+ if (s != "patch_path") {
+ cerr << "[Configuration] Corrupt settings file, load aborted." << endl;
+ is.close();
+ return;
+ }
+
+ is >> s;
+ m_patch_path = s;
+
+ is.close();
+}
+
+
+/** Saves settings to rc file. Passing no parameter will save to the
+ * default location.
+ */
+void
+Configuration::save_settings(string filename)
+{
+ if (filename == "")
+ filename = string(getenv("HOME")).append("/.omgtkrc");
+
+ std::ofstream os;
+ os.open(filename.c_str(), std::ios::out);
+
+ if ( ! os.good()) {
+ cout << "[Configuration] Unable to write to setting file " << filename << endl;
+ return;
+ } else {
+ cout << "[Configuration] Saving settings to " << filename << endl;
+ }
+
+ os << "file_version 1" << endl;
+ os << "patch_path " << m_patch_path << endl;
+
+ os.close();
+}
+
+
+/** Applies the current loaded settings to whichever parts of the app
+ * need updating.
+ */
+void
+Configuration::apply_settings()
+{
+ Controller::instance().set_patch_path(m_patch_path);
+}
+
+
+int
+Configuration::get_port_color(const PortModel* pi)
+{
+ assert(pi != NULL);
+
+ if (pi->is_control()) {
+ return m_control_port_color;
+ } else if (pi->is_audio()) {
+ return m_audio_port_color;
+ } else if (pi->is_midi()) {
+ return m_midi_port_color;
+ }
+
+ cerr << "[Configuration] Unknown port type! Port will be bright red, this is an error." << endl;
+ return 0xFF0000FF;
+}
+
+/*
+Coord
+Configuration::get_window_location(const string& id)
+{
+ return m_window_locations[id];
+}
+
+
+void
+Configuration::set_window_location(const string& id, Coord loc)
+{
+ m_window_locations[id] = loc;
+}
+
+
+Coord
+Configuration::get_window_size(const string& id)
+{
+ return m_window_sizes[id];
+}
+
+
+void
+Configuration::set_window_size(const string& id, Coord size)
+{
+ m_window_sizes[id] = size;
+}*/
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/Configuration.h b/src/progs/gtk/Configuration.h
new file mode 100644
index 00000000..f0f9bef9
--- /dev/null
+++ b/src/progs/gtk/Configuration.h
@@ -0,0 +1,76 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <string>
+
+namespace LibOmClient { class PortModel; }
+using LibOmClient::PortModel;
+using std::string;
+
+struct Coord { double x; double y; };
+
+namespace OmGtk {
+
+class Controller;
+
+
+/** Singleton state manager for the entire app.
+ *
+ * Stores settings like color preferences, search paths, etc.
+ * (ie any user-defined preferences to be stoed in the rc file).
+ *
+ * \ingroup OmGtk
+ */
+class Configuration
+{
+public:
+ Configuration();
+ ~Configuration();
+
+ void load_settings(string filename = "");
+ void save_settings(string filename = "");
+
+ void apply_settings();
+
+ string patch_path() { return m_patch_path; }
+ void patch_path(const string& path) { m_patch_path = path; }
+
+ const string& patch_folder() { return m_patch_folder; }
+ void set_patch_folder(const string& f) { m_patch_folder = f; }
+
+ int get_port_color(const PortModel* pi);
+
+private:
+ /** Search path for patch files. Colon delimited, as usual. */
+ string m_patch_path;
+
+ /** Most recent patch folder shown in open dialog */
+ string m_patch_folder;
+
+ int m_audio_port_color;
+ int m_control_port_color;
+ int m_midi_port_color;
+};
+
+
+} // namespace OmGtk
+
+#endif // CONFIG_H
+
+
diff --git a/src/progs/gtk/ConnectWindow.cpp b/src/progs/gtk/ConnectWindow.cpp
new file mode 100644
index 00000000..15bab62c
--- /dev/null
+++ b/src/progs/gtk/ConnectWindow.cpp
@@ -0,0 +1,170 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ConnectWindow.h"
+#include <string>
+#include <time.h>
+#include <sys/time.h>
+#include "interface/ClientKey.h"
+#include "interface/ClientInterface.h"
+#include "ThreadedSigClientInterface.h"
+#include "Controller.h"
+#include "OSCListener.h"
+#include "Store.h"
+
+namespace OmGtk {
+
+
+ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Dialog(cobject)
+, _client(NULL)
+{
+ xml->get_widget("connect_progress_bar", _progress_bar);
+ xml->get_widget("connect_label", _label);
+ xml->get_widget("connect_launch_button", _launch_button);
+ xml->get_widget("connect_cancel_button", _cancel_button);
+
+ assert(_progress_bar);
+ assert(_label);
+ assert(_launch_button);
+ assert(_cancel_button);
+
+ _launch_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::launch_engine));
+ _cancel_button->signal_clicked().connect(sigc::ptr_fun(&Gtk::Main::quit));
+}
+
+
+void
+ConnectWindow::start(CountedPtr<Om::Shared::ClientInterface> client)
+{
+ _client = client;
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &ConnectWindow::gtk_callback), 100);
+
+ resize(100, 100);
+}
+
+
+void
+ConnectWindow::launch_engine()
+{
+ if (fork() == 0) {
+ //cerr << "Launching engine..";
+ execlp("om", NULL);
+ }
+}
+
+
+bool
+ConnectWindow::gtk_callback()
+{
+ /* This isn't very nice (isn't threaded), but better than no dialog at
+ * all like before :)
+ */
+
+ // Timing stuff for repeated attach attempts
+ timeval now;
+ gettimeofday(&now, NULL);
+ static timeval last = now;
+
+ static int stage = 0;
+
+ /* Connecting to engine */
+ if (stage == 0) {
+ // FIXME
+ //assert(!Controller::instance().is_attached());
+ _label->set_text(string("Connecting to engine at ").append(
+ Controller::instance().engine_url()).append("..."));
+ present();
+ Controller::instance().attach();
+ ++stage;
+ } else if (stage == 1) {
+ if (Controller::instance().is_attached()) {
+ Controller::instance().activate();
+ ++stage;
+ } else {
+ const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f +
+ (now.tv_usec - last.tv_usec) * 0.001f;
+ if (ms_since_last > 1000) {
+ Controller::instance().attach();
+ last = now;
+ }
+ }
+ } else if (stage == 2) {
+ _label->set_text(string("Registering as client..."));
+ //Controller::instance().register_client(Controller::instance().client_hooks());
+ // FIXME
+ //auto_ptr<ClientInterface> client(new ThreadedSigClientInterface();
+ Controller::instance().register_client(ClientKey(), _client);
+ Controller::instance().load_plugins();
+ ++stage;
+ } else if (stage == 3) {
+ // Register idle callback to process events and whatnot
+ // (Gtk refreshes at priority G_PRIORITY_HIGH_IDLE+20)
+ /*Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &App::idle_callback), 100, G_PRIORITY_HIGH_IDLE);*/
+ //Glib::signal_idle().connect(sigc::mem_fun(this, &App::idle_callback));
+ /* Glib::signal_timeout().connect(
+ sigc::mem_fun((ThreadedSigClientInterface*)_client, &ThreadedSigClientInterface::emit_signals),
+ 5, G_PRIORITY_DEFAULT_IDLE);*/
+
+ _label->set_text(string("Requesting plugins..."));
+ Controller::instance().request_plugins();
+ ++stage;
+ } else if (stage == 4) {
+ // Wait for first plugins message
+ if (Store::instance().plugins().size() > 0) {
+ _label->set_text(string("Receiving plugins..."));
+ ++stage;
+ }
+ } else if (stage == 5) {
+ // FIXME
+ /*if (Store::instance().plugins().size() < _client->num_plugins()) {
+ static char buf[32];
+ snprintf(buf, 32, "%zu/%zu", Store::instance().plugins().size(),
+ ThreadedSigClientInterface::instance()->num_plugins());
+ _progress_bar->set_text(Glib::ustring(buf));
+ _progress_bar->set_fraction(
+ Store::instance().plugins().size() / (double)_client->num_plugins());
+ } else {*/
+ _progress_bar->set_text("");
+ ++stage;
+ //}
+ } else if (stage == 6) {
+ _label->set_text(string("Waiting for root patch..."));
+ Controller::instance().request_all_objects();
+ ++stage;
+ } else if (stage == 7) {
+ if (Store::instance().num_objects() > 0)
+ ++stage;
+ } else if (stage == 8) {
+ stage = -1;
+ hide(); // FIXME: actually destroy window to save mem?
+ }
+
+ if (stage != 5) // yeah, ew
+ _progress_bar->pulse();
+
+ if (stage == -1) { // finished connecting
+ return false; // deregister this callback
+ } else {
+ return true;
+ }
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/ConnectWindow.h b/src/progs/gtk/ConnectWindow.h
new file mode 100644
index 00000000..8c0c7390
--- /dev/null
+++ b/src/progs/gtk/ConnectWindow.h
@@ -0,0 +1,61 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONNECT_WINDOW_H
+#define CONNECT_WINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include "util/CountedPtr.h"
+#include "interface/ClientInterface.h"
+
+namespace OmGtk {
+
+class App;
+class Controller;
+
+
+/** The initially visible "Connect to engine" window.
+ *
+ * This handles actually connecting to the engine and making sure everything
+ * is ready before really launching the app (eg wait for the root patch).
+ *
+ * \ingroup OmGtk
+ */
+class ConnectWindow : public Gtk::Dialog
+{
+public:
+ ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void start(CountedPtr<Om::Shared::ClientInterface> client);
+
+private:
+ void launch_engine();
+
+ bool gtk_callback();
+
+ CountedPtr<Om::Shared::ClientInterface> _client;
+ Gtk::ProgressBar* _progress_bar;
+ Gtk::Label* _label;
+ Gtk::Button* _launch_button;
+ Gtk::Button* _cancel_button;
+};
+
+
+} // namespace OmGtk
+
+#endif // CONNECT_WINDOW_H
diff --git a/src/progs/gtk/ControlGroups.cpp b/src/progs/gtk/ControlGroups.cpp
new file mode 100644
index 00000000..de87be05
--- /dev/null
+++ b/src/progs/gtk/ControlGroups.cpp
@@ -0,0 +1,419 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ControlGroups.h"
+#include "ControlPanel.h"
+#include "PortModel.h"
+#include "Controller.h"
+//#include "GtkClientInterface.h"
+#include <cmath>
+#include <iostream>
+using std::cerr; using std::cout; using std::endl;
+
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+//////////////////// SliderControlGroup ////////////////////////
+
+
+SliderControlGroup::SliderControlGroup(ControlPanel* panel, PortModel* pm, bool separator)
+: ControlGroup(panel, pm, separator),
+ m_enabled(true),
+ m_enable_signal(false),
+ m_name_label(pm->name(), 0.0, 0.0),
+ m_range_box(false, 0),
+ m_range_label("<small>Range: </small>"),
+ m_min_spinner(1.0, (pm->is_integer() ? 0 : 4)), // climb rate, digits
+ m_hyphen_label(" - "),
+ m_max_spinner(1.0, (pm->is_integer() ? 0 : 4)),
+ m_slider_box(false, 0),
+ m_value_spinner(1.0, (pm->is_integer() ? 0 : 4)),
+ m_slider(pm->user_min(), pm->user_max(), (pm->is_integer() ? 1.0 : 0.0001))
+{
+ m_slider.set_increments(1.0, 10.0);
+
+ // Compensate for crazy plugins
+ if (m_port_model->user_max() <= m_port_model->user_min()) {
+ m_port_model->user_max(m_port_model->user_min() + 1.0);
+ m_slider.set_range(m_port_model->user_min(), m_port_model->user_max());
+ }
+ m_slider.property_draw_value() = false;
+
+ set_name(pm->name());
+
+ m_name_label.property_use_markup() = true;
+ m_range_label.property_use_markup() = true;
+
+ m_value_spinner.set_range(-99999, 99999);
+ m_value_spinner.set_update_policy(Gtk::UPDATE_ALWAYS); // allow entered values outside range
+ m_value_spinner.set_value(m_port_model->value());
+ m_value_spinner.set_increments(1.0, 10.0);
+ m_value_spinner.set_digits(5);
+ m_value_spinner.signal_value_changed().connect(
+ sigc::mem_fun(*this, &SliderControlGroup::update_value_from_spinner));
+ m_min_spinner.set_range(-99999, 99999);
+ m_min_spinner.set_value(m_port_model->user_min());
+ m_min_spinner.set_increments(1.0, 10.0);
+ m_min_spinner.set_digits(5);
+ m_min_spinner.signal_value_changed().connect(sigc::mem_fun(*this, &SliderControlGroup::min_changed));
+ m_max_spinner.set_range(-99999, 99999);
+ m_max_spinner.set_value(m_port_model->user_max());
+ m_max_spinner.set_increments(1.0, 10.0);
+ m_max_spinner.set_digits(5);
+ m_max_spinner.signal_value_changed().connect(sigc::mem_fun(*this, &SliderControlGroup::max_changed));
+
+ m_slider.set_value(m_port_model->value());
+
+ m_slider.signal_event().connect(
+ sigc::mem_fun(*this, &SliderControlGroup::slider_pressed));
+
+ m_slider.signal_value_changed().connect(
+ sigc::mem_fun(*this, &SliderControlGroup::update_value_from_slider));
+
+ m_range_box.pack_start(m_range_label, false, false, 2);
+ m_range_box.pack_start(m_min_spinner, false, false, 1);
+ m_range_box.pack_start(m_hyphen_label, false, false, 1);
+ m_range_box.pack_start(m_max_spinner, false, false, 1);
+
+ m_header_box.pack_start(m_name_label, true, true, 0);
+ m_header_box.pack_start(m_range_box, true, true, 2);
+
+ m_slider_box.pack_start(m_value_spinner, false, false, 1);
+ m_slider_box.pack_start(m_slider, true, true, 5);
+
+ pack_start(m_header_box, false, false, 0);
+ pack_start(m_slider_box, false, false, 0);
+
+ update_range();
+
+ m_enable_signal = true;
+
+ show_all();
+}
+
+
+void
+SliderControlGroup::set_name(const string& name)
+{
+ string name_label = "<small><span weight=\"bold\">";
+ name_label += name + "</span></small>";
+ m_name_label.set_markup(name_label);
+}
+
+
+void
+SliderControlGroup::set_min(float val)
+{
+ m_enable_signal = false;
+ m_min_spinner.set_value(val);
+ m_enable_signal = true;
+}
+
+
+void
+SliderControlGroup::set_max(float val)
+{
+ m_enable_signal = false;
+ m_max_spinner.set_value(val);
+ m_enable_signal = true;
+}
+
+
+void
+SliderControlGroup::enable()
+{
+ m_slider.property_sensitive() = true;
+ m_min_spinner.property_sensitive() = true;
+ m_max_spinner.property_sensitive() = true;
+ m_value_spinner.property_sensitive() = true;
+ m_name_label.property_sensitive() = true;
+ m_range_label.property_sensitive() = true;
+}
+
+
+void
+SliderControlGroup::disable()
+{
+ m_slider.property_sensitive() = false;
+ m_min_spinner.property_sensitive() = false;
+ m_max_spinner.property_sensitive() = false;
+ m_value_spinner.property_sensitive() = false;
+ m_name_label.property_sensitive() = false;
+ m_range_label.property_sensitive() = false;
+}
+
+
+void
+SliderControlGroup::min_changed()
+{
+ double min = m_min_spinner.get_value();
+ const double max = m_max_spinner.get_value();
+
+ if (min >= max) {
+ min = max - 1.0;
+ m_min_spinner.set_value(min);
+ }
+
+ update_range();
+}
+
+
+void
+SliderControlGroup::max_changed()
+{
+ const double min = m_min_spinner.get_value();
+ double max = m_max_spinner.get_value();
+
+ if (max <= min) {
+ max = min + 1.0;
+ m_max_spinner.set_value(max);
+ }
+
+ update_range();
+}
+
+
+void
+SliderControlGroup::update_range()
+{
+ const double min = m_min_spinner.get_value();
+ const double max = m_max_spinner.get_value();
+
+ assert(min < max);
+ m_slider.set_range(min, max);
+
+ m_port_model->user_min(min);
+ m_port_model->user_max(max);
+
+ if (m_enable_signal) {
+ char temp_buf[16];
+ snprintf(temp_buf, 16, "%f", m_port_model->user_min());
+ Controller::instance().set_metadata(m_port_model->path(), "user-min", temp_buf);
+ snprintf(temp_buf, 16, "%f", m_port_model->user_max());
+ Controller::instance().set_metadata(m_port_model->path(), "user-max", temp_buf);
+ }
+}
+
+
+void
+SliderControlGroup::update_value_from_slider()
+{
+ if (m_enable_signal) {
+ const float value = m_slider.get_value();
+ // Prevent spinner signal from doing all this over again (slow)
+ m_enable_signal = false;
+ m_value_spinner.set_value(value);
+ m_control_panel->value_changed(m_port_model->path(), value);
+ m_port_model->value(value);
+ m_enable_signal = true;
+ }
+}
+
+
+void
+SliderControlGroup::update_value_from_spinner()
+{
+ if (m_enable_signal) {
+ m_enable_signal = false;
+ const float value = m_value_spinner.get_value();
+ if (value < m_min_spinner.get_value())
+ m_min_spinner.set_value(value);
+ if (value > m_max_spinner.get_value())
+ m_max_spinner.set_value(value);
+
+ m_enable_signal = false;
+ update_range();
+ m_slider.set_value(m_value_spinner.get_value());
+ m_enable_signal = true;
+
+ m_control_panel->value_changed(m_port_model->path(), value);
+
+ m_port_model->value(value);
+ m_enable_signal = true;
+ }
+}
+
+
+/** Callback for when slider is grabbed so we can ignore set_control
+ * events for this port (and avoid nasty feedback issues).
+ */
+bool
+SliderControlGroup::slider_pressed(GdkEvent* ev)
+{
+ //cerr << "Pressed: " << ev->type << endl;
+ if (ev->type == GDK_BUTTON_PRESS) {
+ m_enabled = false;
+ cerr << "SLIDER FIXME\n";
+ //GtkClientInterface::instance()->set_ignore_port(m_port_model->path());
+ } else if (ev->type == GDK_BUTTON_RELEASE) {
+ m_enabled = true;
+ cerr << "SLIDER FIXME\n";
+ //GtkClientInterface::instance()->clear_ignore_port();
+ }
+
+ return false;
+}
+
+
+/////////////// IntegerControlGroup ////////////////
+
+
+IntegerControlGroup::IntegerControlGroup(ControlPanel* panel, PortModel* pm, bool separator)
+: ControlGroup(panel, pm, separator),
+ m_enable_signal(false),
+ m_alignment(0.5, 0.5, 0.0, 0.0),
+ m_name_label(pm->name()),
+ m_spinner(1.0, 0)
+{
+ set_name(pm->name());
+
+ m_spinner.set_range(-99999, 99999);
+ m_spinner.set_value(m_port_model->value());
+ m_spinner.signal_value_changed().connect(
+ sigc::mem_fun(*this, &IntegerControlGroup::update_value));
+ m_spinner.set_increments(1, 10);
+
+ m_alignment.add(m_spinner);
+ pack_start(m_name_label);
+ pack_start(m_alignment);
+
+ m_enable_signal = true;
+
+ show_all();
+}
+
+
+void
+IntegerControlGroup::set_name(const string& name)
+{
+ string name_label = "<span weight=\"bold\">";
+ name_label += name + "</span>";
+ m_name_label.set_markup(name_label);
+}
+
+
+void
+IntegerControlGroup::set_value(float val)
+{
+ //cerr << "[IntegerControlGroup] Setting value to " << val << endl;
+ m_enable_signal = false;
+ m_spinner.set_value(val);
+ m_port_model->value(val);
+ m_enable_signal = true;
+}
+
+
+void
+IntegerControlGroup::enable()
+{
+ m_spinner.property_sensitive() = true;
+ m_name_label.property_sensitive() = true;
+}
+
+
+void
+IntegerControlGroup::disable()
+{
+ m_spinner.property_sensitive() = false;
+ m_name_label.property_sensitive() = false;
+}
+
+
+void
+IntegerControlGroup::update_value()
+{
+ if (m_enable_signal) {
+ float value = m_spinner.get_value();
+ m_control_panel->value_changed(m_port_model->path(), value);
+ m_port_model->value(value);
+ }
+}
+
+
+/////////////// ToggleControlGroup ////////////////
+
+
+ToggleControlGroup::ToggleControlGroup(ControlPanel* panel, PortModel* pm, bool separator)
+: ControlGroup(panel, pm, separator),
+ m_enable_signal(false),
+ m_alignment(0.5, 0.5, 0.0, 0.0),
+ m_name_label(pm->name())
+{
+ set_name(pm->name());
+
+ set_value(m_port_model->value());
+ m_checkbutton.signal_toggled().connect(
+ sigc::mem_fun(*this, &ToggleControlGroup::update_value));
+
+ m_alignment.add(m_checkbutton);
+ pack_start(m_name_label);
+ pack_start(m_alignment);
+
+ m_enable_signal = true;
+
+ show_all();
+}
+
+
+void
+ToggleControlGroup::set_name(const string& name)
+{
+ string name_label = "<span weight=\"bold\">";
+ name_label += name + "</span>";
+ m_name_label.set_markup(name_label);
+}
+
+
+void
+ToggleControlGroup::set_value(float val)
+{
+ //cerr << "[ToggleControlGroup] Setting value to " << val << endl;
+ m_enable_signal = false;
+ m_checkbutton.set_active( (val > 0.0f) );
+ m_port_model->value(val);
+ m_enable_signal = true;
+}
+
+
+void
+ToggleControlGroup::enable()
+{
+ m_checkbutton.property_sensitive() = true;
+ m_name_label.property_sensitive() = true;
+}
+
+
+void
+ToggleControlGroup::disable()
+{
+ m_checkbutton.property_sensitive() = false;
+ m_name_label.property_sensitive() = false;
+}
+
+
+void
+ToggleControlGroup::update_value()
+{
+ if (m_enable_signal) {
+ float value = m_checkbutton.get_active() ? 1.0f : 0.0f;
+ m_control_panel->value_changed(m_port_model->path(), value);
+ m_port_model->value(value);
+ }
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/ControlGroups.h b/src/progs/gtk/ControlGroups.h
new file mode 100644
index 00000000..ab1e3413
--- /dev/null
+++ b/src/progs/gtk/ControlGroups.h
@@ -0,0 +1,196 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONTROLGROUPS_H
+#define CONTROLGROUPS_H
+
+#include <cassert>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include "PortModel.h"
+
+
+namespace LibOmClient { class PortModel; }
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class ControlPanel;
+
+
+/** A group of controls in a NodeControlWindow.
+ *
+ * \ingroup OmGtk
+ */
+class ControlGroup : public Gtk::VBox
+{
+public:
+ ControlGroup(ControlPanel* panel, PortModel* pm, bool separator)
+ : Gtk::VBox(false, 0),
+ m_control_panel(panel),
+ m_port_model(pm),
+ m_has_separator(separator)
+ {
+ assert(m_port_model != NULL);
+ assert(panel != NULL);
+
+ if (separator) {
+ m_separator = new Gtk::HSeparator();
+ pack_start(*m_separator, false, false, 4);
+ } else {
+ m_separator = NULL;
+ }
+ }
+
+ ~ControlGroup() { delete m_separator; }
+
+ virtual void set_name(const string& name) {}
+
+ virtual void set_value(float val) = 0;
+
+ inline const PortModel* const port_model() { return m_port_model; }
+
+ virtual void set_min(float val) {}
+ virtual void set_max(float val) {}
+
+ void remove_separator() { assert(m_has_separator);
+ remove(*m_separator); delete m_separator; }
+
+ virtual void enable() {}
+ virtual void disable() {}
+
+protected:
+ ControlPanel* m_control_panel;
+ PortModel* m_port_model;
+ bool m_has_separator;
+ Gtk::HSeparator* m_separator;
+};
+
+
+/** A slider for a continuous control.
+ *
+ * \ingroup OmGtk
+ */
+class SliderControlGroup : public ControlGroup
+{
+public:
+ SliderControlGroup(ControlPanel* panel, PortModel* pm, bool separator);
+
+ void set_name(const string& name);
+
+ inline void set_value(const float val);
+ void set_min(float val);
+ void set_max(float val);
+
+ void enable();
+ void disable();
+
+private:
+ void min_changed();
+ void max_changed();
+ void update_range();
+ void update_value_from_slider();
+ void update_value_from_spinner();
+
+ //void slider_grabbed(bool b);
+
+ bool slider_pressed(GdkEvent* ev);
+
+ bool m_enabled;
+ bool m_enable_signal;
+
+ Gtk::HBox m_header_box;
+ Gtk::Label m_name_label;
+ Gtk::HBox m_range_box;
+ Gtk::Label m_range_label;
+ Gtk::SpinButton m_min_spinner;
+ Gtk::Label m_hyphen_label;
+ Gtk::SpinButton m_max_spinner;
+ Gtk::HBox m_slider_box;
+ Gtk::SpinButton m_value_spinner;
+ Gtk::HScale m_slider;
+};
+
+
+inline void
+SliderControlGroup::set_value(const float val)
+{
+ m_enable_signal = false;
+ if (m_enabled) {
+ m_slider.set_value(val);
+ m_value_spinner.set_value(val);
+ }
+ m_port_model->value(val);
+ m_enable_signal = true;
+}
+
+
+
+
+/** A spinbutton for integer controls.
+ *
+ * \ingroup OmGtk
+ */
+class IntegerControlGroup : public ControlGroup
+{
+public:
+ IntegerControlGroup(ControlPanel* panel, PortModel* pm, bool separator);
+
+ void set_name(const string& name);
+ void set_value(float val);
+
+ void enable();
+ void disable();
+
+private:
+ void update_value();
+
+ bool m_enable_signal;
+ Gtk::Alignment m_alignment;
+ Gtk::Label m_name_label;
+ Gtk::SpinButton m_spinner;
+};
+
+
+/** A radio button for toggle controls.
+ *
+ * \ingroup OmGtk
+ */
+class ToggleControlGroup : public ControlGroup
+{
+public:
+ ToggleControlGroup(ControlPanel* panel, PortModel* pm, bool separator);
+
+ void set_name(const string& name);
+ void set_value(float val);
+
+ void enable();
+ void disable();
+
+private:
+ void update_value();
+
+ bool m_enable_signal;
+ Gtk::Alignment m_alignment;
+ Gtk::Label m_name_label;
+ Gtk::CheckButton m_checkbutton;
+};
+
+
+} // namespace OmGtk
+
+#endif // CONTROLGROUPS_H
diff --git a/src/progs/gtk/ControlInterface.cpp b/src/progs/gtk/ControlInterface.cpp
new file mode 100644
index 00000000..9ac6e07e
--- /dev/null
+++ b/src/progs/gtk/ControlInterface.cpp
@@ -0,0 +1,302 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <cassert>
+
+#include "ControlInterface.h"
+#include "App.h"
+
+#include "PluginModel.h"
+#include "PatchModel.h"
+#include "NodeModel.h"
+#include "PortModel.h"
+#include "ConnectionModel.h"
+
+#include "PatchController.h"
+#include "PortController.h"
+#include "PatchTreeWindow.h"
+#include "Store.h"
+
+namespace OmGtk
+{
+
+
+void
+ControlInterface::error(const string& msg)
+{
+ _app->error_message(msg);
+}
+
+
+void
+ControlInterface::new_plugin_model(PluginModel* pm)
+{
+ cerr << "NEW PLUGIN\n";
+ Store::instance().add_plugin(pm);
+}
+
+
+void
+ControlInterface::new_patch_model(PatchModel* const pm)
+{
+ assert(pm);
+
+ //cout << "[ControlInterface] New patch." << endl;
+
+ if (Store::instance().patch(pm->path()) != NULL) {
+ delete pm;
+ } else {
+
+ // See if we cached this patch model to store its location (to avoid the
+ // module "jumping") and filename (which isn't sent to engine)
+ /*PatchModel* pm = _controller->yank_added_patch(pm->path());
+ if (pm != NULL) {
+ assert(pm->path() == pm->path());
+ // FIXME: ew
+ if (pm->parent() != NULL)
+ pm->set_parent(NULL);
+ const PluginModel* plugin = pm->plugin();
+ *pm = *pm;
+ pm->plugin(plugin);
+ }*/
+
+ assert(pm->parent() == NULL);
+ PatchController* patch = new PatchController(pm);
+ Store::instance().add_object(patch);
+ _app->patch_tree()->add_patch(patch);
+
+ if (pm->path() == "/")
+ patch->show_patch_window();
+
+ //_app->add_patch(pm, show);
+ }
+}
+
+
+void
+ControlInterface::new_node_model(NodeModel* const nm)
+{
+ assert(nm);
+
+ //cerr << "[ControlInterface] New node: " << nm->name() << endl;
+
+ PatchController* const pc = Store::instance().patch(nm->path().parent());
+
+ if (pc != NULL) {
+ /* FIXME: this is crazy slow, and slows down patch loading too much. Define some
+ * kind of unified string to describe all plugins (ie ladspa:amp.so:amp_gaia) and
+ * just store that in node models, and look it up when needed */
+
+ /* Bit of a hack, throw away the placeholder PluginModel in the NodeModel
+ * and set it to the nice complete one we have stored */
+
+ for (map<string, const PluginModel*>::const_iterator i = Store::instance().plugins().begin();
+ i != Store::instance().plugins().end(); ++i) {
+ if ((*i).second->uri() == nm->plugin()->uri()) {
+ // FIXME: EVIL
+ delete (PluginModel*)(nm->plugin());
+ nm->plugin((*i).second);
+ break;
+ }
+ }
+
+ pc->add_node(nm);
+
+ } else {
+ cerr << "[NewNode] Can not find parent of " << nm->path()
+ << ". Module will not appear." << endl;
+ }
+}
+
+
+void
+ControlInterface::new_port_model(PortModel* const pm)
+{
+ assert(pm);
+
+ //cout << "[ControlInterface] New port." << endl;
+
+ NodeController* node = Store::instance().node(pm->path().parent());
+ if (node != NULL)
+ node->add_port(pm);
+ else
+ cerr << "[NewPort] Could not find parent for "
+ << pm->path() << endl;
+}
+
+
+void
+ControlInterface::patch_enabled(const string& path)
+{
+ //cout << "[ControlInterface] Patch enabled." << endl;
+
+ PatchController* patch = Store::instance().patch(path);
+ if (patch != NULL)
+ patch->enable();
+ else
+ cerr << "[PatchEnabled] Cannot find patch " << path << endl;
+}
+
+
+void
+ControlInterface::patch_disabled(const string& path)
+{
+ //cout << "[ControlInterface] Patch disabled." << endl;
+
+ PatchController* patch = Store::instance().patch(path);
+ if (patch != NULL)
+ patch->disable();
+ else
+ cerr << "[PatchDisabled] Cannot find patch " << path << endl;
+}
+
+
+void
+ControlInterface::patch_cleared(const string& path)
+{
+ //cout << "[ControlInterface] Patch cleared." << endl;
+
+ PatchController* patch = Store::instance().patch(path);
+ if (patch != NULL)
+ patch->clear();
+ else
+ cerr << "[PatchCleared] Cannot find patch " << path << endl;
+}
+
+
+void
+ControlInterface::object_destroyed(const string& path)
+{
+ //cout << "[ControlInterface] Destroying." << endl;
+
+ GtkObjectController* object = Store::instance().object(path);
+ if (object != NULL) {
+ object->destroy();
+ delete object;
+ } else {
+ cerr << "[Destroy] Cannot find object " << path << endl;
+ }
+}
+
+
+void
+ControlInterface::object_renamed(const string& old_path, const string& new_path)
+{
+ //cout << "[ControlInterface] Renaming." << endl;
+
+ GtkObjectController* object = Store::instance().object(old_path);
+ if (object != NULL)
+ object->set_path(new_path);
+ else
+ cerr << "[ObjectRenamed] Can not find object " << old_path
+ << " to rename." << endl;
+}
+
+
+void
+ControlInterface::connection_model(ConnectionModel* connection)
+{
+ assert(connection);
+
+ //cout << "[ControlInterface] Connection" << endl;
+
+ assert(connection->src_port_path().parent().parent()
+ == connection->dst_port_path().parent().parent());
+
+ PatchController* pc = Store::instance().patch(connection->patch_path());
+
+ if (pc != NULL)
+ pc->connection(connection);
+ else
+ cerr << "[Connection] Can not find patch " << connection->patch_path()
+ << ". Connection will not be made." << endl;
+}
+
+
+void
+ControlInterface::disconnection(const string& src_port_path, const string& dst_port_path)
+{
+ //cerr << "[ControlInterface] Disconnection." << endl;
+ string patch_path = src_port_path;
+ patch_path = patch_path.substr(0, patch_path.find_last_of("/"));
+ patch_path = patch_path.substr(0, patch_path.find_last_of("/"));
+
+ if (patch_path == "")
+ patch_path = "/";
+
+ PatchController* pc = Store::instance().patch(patch_path);
+
+ if (pc != NULL)
+ pc->disconnection(src_port_path, dst_port_path);
+ else
+ cerr << "[Disconnection] Can not find window for patch " << patch_path
+ << ". Connection will not be removed." << endl;
+}
+
+
+void
+ControlInterface::metadata_update(const string& path, const string& key, const string& value)
+{
+ //cerr << "[ControlInterface] Metadata." << endl;
+
+ GtkObjectController* object = Store::instance().object(path);
+ if (object != NULL)
+ object->metadata_update(key, value);
+ else
+ cerr << "[MetadataUpdate] Could not find object " << path << endl;
+}
+
+
+void
+ControlInterface::control_change(const string& port_path, float value)
+{
+ //cerr << "[ControlInterface] Control change." << endl;
+
+ PortController* port = Store::instance().port(port_path);
+ if (port != NULL)
+ port->control_change(value);
+ else
+ cerr << "[ControlChange] Can not find port " << port_path << endl;
+}
+
+
+void
+ControlInterface::program_add(const string& path, uint32_t bank, uint32_t program, const string& name)
+{
+ NodeController* node = Store::instance().node(path);
+ if (node != NULL) {
+ node->program_add(bank, program, name);
+ return;
+ } else {
+ cerr << "[ProgramAdd] Can not find node " << path << endl;
+ }
+}
+
+
+void
+ControlInterface::program_remove(const string& path, uint32_t bank, uint32_t program)
+{
+ NodeController* node = Store::instance().node(path);
+ if (node != NULL) {
+ node->program_remove(bank, program);
+ return;
+ } else {
+ cerr << "[ProgramRemove] Can not find node " << path << endl;
+ }
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/ControlInterface.h b/src/progs/gtk/ControlInterface.h
new file mode 100644
index 00000000..8cba6a79
--- /dev/null
+++ b/src/progs/gtk/ControlInterface.h
@@ -0,0 +1,129 @@
+/* This file is part of Om. Copyright(C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONTROLINTERFACE_H
+#define CONTROLINTERFACE_H
+
+#include <cassert>
+#include "ModelClientInterface.h"
+#include <sigc++/sigc++.h>
+#include <string>
+using std::string;
+
+namespace LibOmClient
+{
+class PluginModel;
+class PatchModel;
+class NodeModel;
+class PortModel;
+class ConnectionModel;
+}
+using namespace LibOmClient;
+
+namespace OmGtk
+{
+
+class App;
+
+
+/** Provides the public interface for controlling OmGtk(via engine events).
+ *
+ * All control of OmGtk from the engine happens through this interface.
+ *
+ * All of these functions must be called in the GTK thread. This is a unified
+ * interface for controlling OmGtk(eg from the engine) but it doesn't take
+ * care of any threading issues.
+ *
+ * \ingroup OmGtk
+ */
+class ControlInterface : public sigc::trackable//, public ModelClientInterface
+{
+public:
+
+ ControlInterface(App* app)
+ : error_slot(sigc::mem_fun(this, &ControlInterface::error))
+ , new_plugin_slot(sigc::mem_fun(this, &ControlInterface::new_plugin_model))
+ , new_patch_slot(sigc::mem_fun(this, &ControlInterface::new_patch_model))
+ , new_node_slot(sigc::mem_fun(this, &ControlInterface::new_node_model))
+ , new_port_slot(sigc::mem_fun(this, &ControlInterface::new_port_model))
+ , patch_enabled_slot(sigc::mem_fun(this, &ControlInterface::patch_enabled))
+ , patch_disabled_slot(sigc::mem_fun(this, &ControlInterface::patch_disabled))
+ , patch_cleared_slot(sigc::mem_fun(this, &ControlInterface::patch_cleared))
+ , object_destroyed_slot(sigc::mem_fun(this, &ControlInterface::object_destroyed))
+ , object_renamed_slot(sigc::mem_fun(this, &ControlInterface::object_renamed))
+ , connection_slot(sigc::mem_fun(this, &ControlInterface::connection_model))
+ , disconnection_slot(sigc::mem_fun(this, &ControlInterface::disconnection))
+ , metadata_update_slot(sigc::mem_fun(this, &ControlInterface::metadata_update))
+ , control_change_slot(sigc::mem_fun(this, &ControlInterface::control_change))
+ , program_add_slot(sigc::mem_fun(this, &ControlInterface::program_add))
+ , program_remove_slot(sigc::mem_fun(this, &ControlInterface::program_remove))
+ , _app(app)
+ {
+ assert(_app);
+ }
+
+ virtual ~ControlInterface() {}
+
+ sigc::slot<void> bundle_begin_slot;
+ sigc::slot<void> bundle_end_slot;
+ sigc::slot<void, uint32_t> num_plugins_slot;
+ sigc::slot<void, string> error_slot;
+ sigc::slot<void, PluginModel*> new_plugin_slot;
+ sigc::slot<void, PatchModel*> new_patch_slot;
+ sigc::slot<void, NodeModel*> new_node_slot;
+ sigc::slot<void, PortModel*> new_port_slot;
+ sigc::slot<void, string> patch_enabled_slot;
+ sigc::slot<void, string> patch_disabled_slot;
+ sigc::slot<void, string> patch_cleared_slot;
+ sigc::slot<void, string> object_destroyed_slot;
+ sigc::slot<void, string, string> object_renamed_slot;
+ sigc::slot<void, ConnectionModel*> connection_slot;
+ sigc::slot<void, string, string> disconnection_slot;
+ sigc::slot<void, string, string, string> metadata_update_slot;
+ sigc::slot<void, string, float> control_change_slot;
+ sigc::slot<void, string, uint32_t, uint32_t, const string&> program_add_slot;
+ sigc::slot<void, string, uint32_t, uint32_t> program_remove_slot;
+
+private:
+
+ void bundle_begin();
+ void bundle_end();
+ void num_plugins(uint32_t);
+ void error(const string&);
+ void new_plugin_model(PluginModel*);
+ void new_patch_model(PatchModel*);
+ void new_node_model(NodeModel*);
+ void new_port_model(PortModel*);
+ void patch_enabled(const string&);
+ void patch_disabled(const string&);
+ void patch_cleared(const string&);
+ void object_destroyed(const string&);
+ void object_renamed(const string&, const string&);
+ void connection_model(ConnectionModel*);
+ void disconnection(const string&, const string&);
+ void metadata_update(const string&, const string&, const string&);
+ void control_change(const string&, float);
+ void program_add(const string&, uint32_t, uint32_t, const string&);
+ void program_remove(const string&, uint32_t, uint32_t);
+
+ App* _app;
+};
+
+
+} // namespace OmGtk
+
+#endif // CONTROLINTERFACE_H
+
diff --git a/src/progs/gtk/ControlPanel.cpp b/src/progs/gtk/ControlPanel.cpp
new file mode 100644
index 00000000..f344e2c3
--- /dev/null
+++ b/src/progs/gtk/ControlPanel.cpp
@@ -0,0 +1,279 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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 alongCont
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ControlPanel.h"
+#include "PatchModel.h"
+#include "NodeModel.h"
+#include "PortModel.h"
+#include "ControlGroups.h"
+#include "PatchController.h"
+#include "Controller.h"
+
+namespace OmGtk {
+
+
+ControlPanel::ControlPanel(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::VBox(cobject),
+ m_callback_enabled(true)
+{
+ xml->get_widget("control_panel_controls_box", m_control_box);
+ xml->get_widget("control_panel_voice_controls_box", m_voice_control_box);
+ xml->get_widget("control_panel_all_voices_radio", m_all_voices_radio);
+ xml->get_widget("control_panel_specific_voice_radio", m_specific_voice_radio);
+ xml->get_widget("control_panel_voice_spinbutton", m_voice_spinbutton);
+
+ m_all_voices_radio->signal_toggled().connect(sigc::mem_fun(this, &ControlPanel::all_voices_selected));
+ m_specific_voice_radio->signal_toggled().connect(sigc::mem_fun(this, &ControlPanel::specific_voice_selected));
+ m_voice_spinbutton->signal_value_changed().connect(sigc::mem_fun(this, &ControlPanel::voice_selected));
+
+ show_all();
+}
+
+
+ControlPanel::~ControlPanel()
+{
+ for (vector<ControlGroup*>::iterator i = m_controls.begin(); i != m_controls.end(); ++i)
+ delete (*i);
+}
+
+
+void
+ControlPanel::init(NodeController* node, size_t poly)
+{
+ assert(node != NULL);
+ assert(poly > 0);
+
+ NodeModel* const node_model = node->node_model();
+
+ if (poly > 1) {
+ m_voice_spinbutton->set_range(0, poly - 1);
+ } else {
+ remove(*m_voice_control_box);
+ }
+
+ for (PortModelList::const_iterator i = node_model->ports().begin();
+ i != node_model->ports().end(); ++i) {
+ PortController* pc = (PortController*)(*i)->controller();
+ assert(pc != NULL);
+ add_port(pc);
+ }
+
+ m_callback_enabled = true;
+}
+
+
+ControlGroup*
+ControlPanel::find_port(const Path& path) const
+{
+ for (vector<ControlGroup*>::const_iterator cg = m_controls.begin(); cg != m_controls.end(); ++cg)
+ if ((*cg)->port_model()->path() == path)
+ return (*cg);
+
+ return NULL;
+}
+
+
+/** Add a control to the panel for the given port.
+ */
+void
+ControlPanel::add_port(PortController* port)
+{
+ assert(port != NULL);
+ assert(port->model() != NULL);
+ assert(port->control_panel() == NULL);
+
+ PortModel* const pm = port->port_model();
+
+ // Already have port, don't add another
+ if (find_port(pm->path()) != NULL)
+ return;
+
+ // Add port
+ if (pm->is_control() && pm->is_input()) {
+ bool separator = (m_controls.size() > 0);
+ ControlGroup* cg = NULL;
+ if (pm->is_integer())
+ cg = new IntegerControlGroup(this, pm, separator);
+ else if (pm->is_toggle())
+ cg = new ToggleControlGroup(this, pm, separator);
+ else
+ cg = new SliderControlGroup(this, pm, separator);
+
+ m_controls.push_back(cg);
+ m_control_box->pack_start(*cg, false, false, 0);
+
+ if (pm->connected())
+ cg->disable();
+ else
+ cg->enable();
+ }
+
+ port->set_control_panel(this);
+
+ Gtk::Requisition controls_size;
+ m_control_box->size_request(controls_size);
+ m_ideal_size.first = controls_size.width;
+ m_ideal_size.second = controls_size.height;
+
+ Gtk::Requisition voice_size;
+ m_voice_control_box->size_request(voice_size);
+ m_ideal_size.first += voice_size.width;
+ m_ideal_size.second += voice_size.height;
+
+ //cerr << "Setting ideal size to " << m_ideal_size.first
+ // << " x " << m_ideal_size.second << endl;
+}
+
+
+/** Remove the control for the given port.
+ */
+void
+ControlPanel::remove_port(const Path& path)
+{
+ bool was_first = false;
+ for (vector<ControlGroup*>::iterator cg = m_controls.begin(); cg != m_controls.end(); ++cg) {
+ if ((*cg)->port_model()->path() == path) {
+ m_control_box->remove(**cg);
+ if (cg == m_controls.begin())
+ was_first = true;
+ m_controls.erase(cg);
+ break;
+ }
+ }
+
+ if (was_first && m_controls.size() > 0)
+ (*m_controls.begin())->remove_separator();
+}
+
+
+/** Rename the control for the given port.
+ */
+void
+ControlPanel::rename_port(const Path& old_path, const Path& new_path)
+{
+ for (vector<ControlGroup*>::iterator cg = m_controls.begin(); cg != m_controls.end(); ++cg) {
+ if ((*cg)->port_model()->path() == old_path) {
+ (*cg)->set_name(new_path.name());
+ return;
+ }
+ }
+}
+
+
+/** Enable the control for the given port.
+ *
+ * Used when all connections to port are un-made.
+ */
+void
+ControlPanel::enable_port(const Path& path)
+{
+ for (vector<ControlGroup*>::iterator i = m_controls.begin(); i != m_controls.end(); ++i) {
+ if ((*i)->port_model()->path() == path) {
+ (*i)->enable();
+ return;
+ }
+ }
+}
+
+
+/** Disable the control for the given port.
+ *
+ * Used when port is connected.
+ */
+void
+ControlPanel::disable_port(const Path& path)
+{
+ for (vector<ControlGroup*>::iterator i = m_controls.begin(); i != m_controls.end(); ++i) {
+ if ((*i)->port_model()->path() == path) {
+ (*i)->disable();
+ return;
+ }
+ }
+}
+
+
+/** Callback for ControlGroups to notify this of a change.
+ */
+void
+ControlPanel::value_changed(const Path& port_path, float val)
+{
+ if (m_callback_enabled) {
+ // Update patch control slider, if this is a control panel for a patch
+ // (or vice versa)
+ //if (m_mirror != NULL)
+ // m_mirror->set_port_value(port_path, val);
+
+ if (m_all_voices_radio->get_active()) {
+ Controller::instance().set_port_value(port_path, val);
+ } else {
+ int voice = m_voice_spinbutton->get_value_as_int();
+ Controller::instance().set_port_value(port_path, voice, val);
+ }
+ }
+}
+
+
+void
+ControlPanel::set_range_min(const Path& port_path, float val)
+{
+ bool found = false;
+ for (vector<ControlGroup*>::iterator i = m_controls.begin(); i != m_controls.end(); ++i) {
+ if ((*i)->port_model()->path() == port_path) {
+ found = true;
+ (*i)->set_min(val);
+ }
+ }
+ if (found == false)
+ cerr << "[ControlPanel::set_range_min] Unable to find control " << port_path << endl;
+}
+
+
+void
+ControlPanel::set_range_max(const Path& port_path, float val)
+{
+ bool found = false;
+ for (vector<ControlGroup*>::iterator i = m_controls.begin(); i != m_controls.end(); ++i) {
+ if ((*i)->port_model()->path() == port_path) {
+ found = true;
+ (*i)->set_max(val);
+ }
+ }
+ if (found == false)
+ cerr << "[ControlPanel::set_range_max] Unable to find control " << port_path << endl;
+}
+
+
+void
+ControlPanel::all_voices_selected()
+{
+ m_voice_spinbutton->property_sensitive() = false;
+}
+
+
+void
+ControlPanel::specific_voice_selected()
+{
+ m_voice_spinbutton->property_sensitive() = true;
+}
+
+
+void
+ControlPanel::voice_selected()
+{
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/ControlPanel.h b/src/progs/gtk/ControlPanel.h
new file mode 100644
index 00000000..1f24344f
--- /dev/null
+++ b/src/progs/gtk/ControlPanel.h
@@ -0,0 +1,129 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONTROLPANEL_H
+#define CONTROLPANEL_H
+
+#include <gtkmm.h>
+#include <sigc++/sigc++.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <utility> // for pair<>
+#include "ControlGroups.h"
+#include "util/Path.h"
+#include "PortController.h"
+
+using std::vector; using std::string; using std::pair;
+using std::cerr; using std::cout; using std::endl;
+
+namespace LibOmClient {
+class PortModel;
+class NodeModel;
+}
+using namespace LibOmClient;
+using Om::Path;
+
+namespace OmGtk {
+
+class NodeController;
+class PortController;
+
+
+/** A group of controls for a node (or patch).
+ *
+ * Used by both NodeControlWindow and the main window (for patch controls).
+ *
+ * \ingroup OmGtk
+ */
+class ControlPanel : public Gtk::VBox {
+public:
+ ControlPanel(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+ virtual ~ControlPanel();
+
+ void init(NodeController* node, size_t poly);
+
+ ControlGroup* find_port(const Path& path) const;
+
+ void add_port(PortController* port);
+ void remove_port(const Path& path);
+
+ void rename_port(const Path& old_path, const Path& new_path);
+
+ void enable_port(const Path& path);
+ void disable_port(const Path& path);
+
+ size_t num_controls() const { return m_controls.size(); }
+ pair<int,int> ideal_size() const { return m_ideal_size; }
+
+ // Callback for ControlGroup
+ void value_changed(const Path& port_path, float val);
+
+ inline void set_control(const Path& port_path, float value);
+ void set_range_min(const Path& port_path, float value);
+ void set_range_max(const Path& port_path, float value);
+
+private:
+ void all_voices_selected();
+ void specific_voice_selected();
+ void voice_selected();
+
+ bool m_callback_enabled;
+
+ pair<int,int> m_ideal_size;
+
+ vector<ControlGroup*> m_controls;
+ Gtk::VBox* m_control_box;
+ Gtk::Box* m_voice_control_box;
+ Gtk::RadioButton* m_all_voices_radio;
+ Gtk::RadioButton* m_specific_voice_radio;
+ Gtk::SpinButton* m_voice_spinbutton;
+};
+
+
+/** Set a port on this panel to a certain value.
+ *
+ * Profiling has shown this is performance critical. Needs to be made
+ * faster.
+ */
+inline void
+ControlPanel::set_control(const Path& port_path, const float val)
+{
+ // FIXME: double lookup, ports should just have a pointer directly to
+ // their control group
+
+ m_callback_enabled = false;
+ ControlGroup* cg = NULL;
+
+ for (vector<ControlGroup*>::iterator i = m_controls.begin(); i != m_controls.end(); ++i) {
+ cg = (*i);
+ if (cg->port_model()->path() == port_path) {
+ cg->set_value(val);
+ m_callback_enabled = true;
+ return;
+ }
+ }
+
+ cerr << "[ControlPanel::set_control] Unable to find control " << port_path << endl;
+ m_callback_enabled = true;
+}
+
+
+} // namespace OmGtk
+
+#endif // CONTROLPANEL_H
diff --git a/src/progs/gtk/Controller.cpp b/src/progs/gtk/Controller.cpp
new file mode 100644
index 00000000..05ebe84a
--- /dev/null
+++ b/src/progs/gtk/Controller.cpp
@@ -0,0 +1,173 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PatchModel.h"
+#include "PatchController.h"
+#include "ControlInterface.h"
+#include "OSCModelEngineInterface.h"
+#include "OSCListener.h"
+//#include "GtkClientInterface.h"
+#include "PatchLibrarian.h"
+#include "Controller.h"
+#include "Loader.h"
+#include "interface/ClientKey.h"
+
+namespace OmGtk {
+
+
+Controller::Controller(const string& engine_url)
+: OSCModelEngineInterface(engine_url),
+ m_patch_librarian(new PatchLibrarian(this)),
+ m_loader(new Loader(m_patch_librarian))
+{
+ m_loader->launch();
+}
+
+
+Controller::~Controller()
+{
+ if (is_attached()) {
+ unregister_client(ClientKey()); // FIXME
+ detach();
+ }
+
+ delete m_loader;
+ delete m_patch_librarian;
+}
+
+
+/** "Attach" to the Om engine.
+ * See documentation OSCModelEngineInterface::attach.
+ */
+void
+Controller::attach()
+{
+ OSCModelEngineInterface::attach(false);
+}
+
+/*
+void
+Controller::register_client_and_wait()
+{
+// int id = get_next_request_id();
+// set_wait_response_id(id);
+ OSCModelEngineInterface::register_client();
+// wait_for_response();
+ cout << "[Controller] Registered with engine. maybe. fixme." << endl;
+}
+*/
+void Controller::set_engine_url(const string& url) { _engine_url = url; }
+
+void
+Controller::create_node_from_model(const NodeModel* nm)
+{
+ //push_added_node(nm);
+ OSCModelEngineInterface::create_node_from_model(nm);
+ char temp_buf[16];
+ snprintf(temp_buf, 16, "%f", nm->x());
+ set_metadata(nm->path(), "module-x", temp_buf);
+ snprintf(temp_buf, 16, "%f", nm->y());
+ set_metadata(nm->path(), "module-y", temp_buf);
+}
+
+void
+Controller::create_patch_from_model(const PatchModel* pm)
+{
+ //push_added_patch(pm);
+
+ //int id = get_next_request_id();
+ //set_wait_response_id(id);
+ create_patch_from_model(pm);
+ if (pm->parent() != NULL) {
+ // wait_for_response();
+ char temp_buf[16];
+ snprintf(temp_buf, 16, "%f", pm->x());
+ set_metadata(pm->path(), "module-x", temp_buf);
+ snprintf(temp_buf, 16, "%f", pm->y());
+ set_metadata(pm->path(), "module-y", temp_buf);
+ }
+ enable_patch(pm->path());
+}
+
+
+void
+Controller::set_patch_path(const string& path)
+{
+ m_patch_librarian->path(path);
+}
+
+
+/** Load patch in a seperate thread.
+ * This will return immediately and the patch will be loaded in the background.
+ * FIXME: merge parameter makes no sense, always true */
+void
+Controller::load_patch(PatchModel* model, bool wait, bool merge)
+{
+ //push_added_patch(model);
+ m_loader->load_patch(model, wait, merge);
+}
+
+
+/** Load patch in a seperate thread.
+ * This will return immediately and the patch will be saved in the background. */
+void
+Controller::save_patch(PatchModel* model, const string& filename, bool recursive)
+{
+ m_loader->save_patch(model, filename, recursive);
+}
+
+
+#if 0
+/** Returns the added node with the given path and removes it from the cache.
+ */
+NodeModel*
+Controller::yank_added_node(const string& path)
+{
+ NodeModel* nm = NULL;
+
+ for (list<NodeModel*>::iterator i = m_added_nodes.begin(); i != m_added_nodes.end(); ++i) {
+ if ((*i)->path() == path) {
+ nm = *i;
+ m_added_nodes.erase(i);
+ break;
+ }
+ }
+
+ return nm;
+}
+
+
+/** Returns the added patch with the given path and removes it from the cache.
+ */
+PatchModel*
+Controller::yank_added_patch(const string& path)
+{
+ PatchModel* pm = NULL;
+
+ for (list<PatchModel*>::iterator i = m_added_patches.begin(); i != m_added_patches.end(); ++i) {
+ if ((*i)->path() == path) {
+ pm = *i;
+ m_added_patches.erase(i);
+ break;
+ }
+ }
+
+ return pm;
+}
+#endif
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/Controller.h b/src/progs/gtk/Controller.h
new file mode 100644
index 00000000..fd516992
--- /dev/null
+++ b/src/progs/gtk/Controller.h
@@ -0,0 +1,112 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONTROLLER_H
+#define CONTROLLER_H
+
+#include <cassert>
+#include <string>
+#include <list>
+#include <glibmm.h>
+#include "PluginModel.h"
+#include "OSCModelEngineInterface.h"
+
+namespace LibOmClient {
+class PatchModel;
+class NodeModel;
+class PatchLibrarian;
+}
+
+using std::string; using std::list;
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class PatchController;
+//class GtkClientInterface;
+class Loader;
+
+
+/** Singleton engine controller for the entire app.
+ *
+ * This is hardly more than a trivial wrapper for OSCModelEngineInterface, suggesting something
+ * needs a rethink around here..
+ *
+ * This needs to be either eliminated or the name changed to OmController. Probably
+ * best to keep around in case multiple engine control support comes around?
+ *
+ * \ingroup OmGtk
+ */
+class Controller : public OSCModelEngineInterface
+{
+public:
+ ~Controller();
+
+ void attach();
+
+ //void register_client_and_wait();
+
+ void set_engine_url(const string& url);
+
+ void create_node_from_model(const NodeModel* nm);
+
+ void load_patch(PatchModel* model, bool wait = true, bool merge = false);
+ void save_patch(PatchModel* model, const string& filename, bool recursive);
+
+ void create_patch_from_model(const PatchModel* pm);
+
+ //void lash_restore_finished();
+
+ void set_patch_path(const string& path);
+
+ /*
+ void push_added_node(NodeModel* nm) { m_added_nodes.push_back(nm); }
+ NodeModel* yank_added_node(const string& path);
+
+ void push_added_patch(PatchModel* pm) { m_added_patches.push_back(pm); }
+ PatchModel* yank_added_patch(const string& path);
+ */
+ //GtkClientInterface* client_hooks() { return (GtkClientInterface*)m_client_hooks; }
+
+ static void instantiate(const string& engine_url)
+ { if (!_instance) _instance = new Controller(engine_url); }
+
+ inline static Controller& instance() { assert(_instance); return *_instance; }
+
+private:
+ Controller(const string& engine_url);
+ static Controller* _instance;
+
+ PatchLibrarian* m_patch_librarian;
+ Loader* m_loader;
+
+ /** Used to cache added nodes so client can remember some values when the
+ * new node notification comes (location, etc). Used to prevent the node
+ * jumping locations during the delay between new node and the module-x/y
+ * metadata notifications */
+ //list<NodeModel*> m_added_nodes;
+
+ /** Used to cache added patches in the same was as m_added_nodes. Used to
+ * rember filename so File->Save can work without prompting (filename can't
+ * be sent to the server as metadata, because a client on another machine
+ * doesn't want that information) */
+ //list<PatchModel*> m_added_patches;
+};
+
+
+} // namespace OmGtk
+
+#endif // CONTROLLER_H
diff --git a/src/progs/gtk/DSSIController.cpp b/src/progs/gtk/DSSIController.cpp
new file mode 100644
index 00000000..570211c7
--- /dev/null
+++ b/src/progs/gtk/DSSIController.cpp
@@ -0,0 +1,280 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIController.h"
+#include <iomanip>
+#include <sstream>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "NodeModel.h"
+#include "DSSIModule.h"
+#include "Controller.h"
+
+namespace OmGtk {
+
+
+DSSIController::DSSIController(NodeModel* model)
+: NodeController(model),
+ m_banks_dirty(true)
+{
+ assert(model->plugin()->type() == PluginModel::DSSI);
+ Gtk::Menu::MenuList& items = m_menu.items();
+ items[0].property_sensitive() = true; // "Show Control Window" item
+
+ Gtk::Menu_Helpers::MenuElem program_elem("Select Program", m_program_menu);
+ items.push_front(program_elem);
+ m_program_menu_item = program_elem.get_child();
+ m_program_menu_item->set_sensitive(false);
+
+ items.push_front(Gtk::Menu_Helpers::MenuElem("Show Plugin GUI",
+ sigc::mem_fun(this, &DSSIController::show_gui)));
+}
+
+void
+DSSIController::program_add(int bank, int program, const string& name)
+{
+ node_model()->add_program(bank, program, name);
+ m_banks_dirty = true;
+}
+
+
+void
+DSSIController::program_remove(int bank, int program)
+{
+ node_model()->remove_program(bank, program);
+ m_banks_dirty = true;
+}
+
+/** Trivial wrapper of attempt_to_show_gui for libsigc
+ */
+void
+DSSIController::show_gui()
+{
+ attempt_to_show_gui();
+}
+
+
+void
+DSSIController::update_program_menu()
+{
+ m_program_menu.items().clear();
+
+ const map<int, map<int, string> >& banks = node_model()->get_programs();
+ std::ostringstream oss;
+
+ map<int, map<int, string> >::const_iterator i;
+ for (i = banks.begin(); i != banks.end(); ++i) {
+ Gtk::Menu* bank_menu;
+ if (banks.size() > 1)
+ bank_menu = manage(new Gtk::Menu());
+ else
+ bank_menu = &m_program_menu;
+ map<int, string>::const_iterator j;
+ for (j = i->second.begin(); j != i->second.end(); ++j) {
+ oss.str("");
+ oss << std::setw(3) << std::setfill('0')
+ << j->first << ' ' << j->second;
+ sigc::slot<void> slt = bind(
+ bind(sigc::mem_fun(*this, &DSSIController::send_program_change),
+ j->first), i->first);
+ bank_menu->items().push_back(
+ Gtk::Menu_Helpers::MenuElem(oss.str(), slt));
+ }
+ if (banks.size() > 1) {
+ oss.str("");
+ oss << "Bank " << i->first;
+ m_program_menu.items().push_back(
+ Gtk::Menu_Helpers::MenuElem(oss.str(), *bank_menu));
+ }
+ }
+
+ // Disable the program menu if there are no programs
+ if (banks.size() == 0)
+ m_program_menu_item->set_sensitive(false);
+ else
+ m_program_menu_item->set_sensitive(true);
+
+ m_banks_dirty = false;
+}
+
+
+void
+DSSIController::send_program_change(int bank, int program)
+{
+ Controller::instance().set_program(node_model()->path(), bank, program);
+}
+
+
+void
+DSSIController::create_module(OmFlowCanvas* canvas)
+{
+ //cerr << "Creating DSSI module " << m_model->path() << endl;
+
+ assert(canvas != NULL);
+ assert(m_module == NULL);
+
+ m_module = new DSSIModule(canvas, this);
+ create_all_ports();
+
+ m_module->move_to(node_model()->x(), node_model()->y());
+}
+
+
+
+/** Attempt to show the DSSI GUI for this plugin.
+ *
+ * Returns whether or not GUI was successfully loaded/shown.
+ */
+bool
+DSSIController::attempt_to_show_gui()
+{
+ // Shamelessley "inspired by" jack-dssi-host
+ // Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton.
+
+ const bool verbose = false;
+
+ string engine_url = Controller::instance().engine_url();
+ // Hack off last character if it's a /
+ if (engine_url[engine_url.length()-1] == '/')
+ engine_url = engine_url.substr(0, engine_url.length()-1);
+
+ const char* dllName = node_model()->plugin()->lib_name().c_str();
+ const char* label = node_model()->plugin()->plug_label().c_str();
+ const char* myName = "om_gtk";
+ const string& oscUrl = engine_url + "/dssi" + node_model()->path();
+
+ struct dirent* entry = NULL;
+ char* dllBase = strdup(dllName);
+ char* subpath = NULL;
+ DIR* subdir = NULL;
+ char* filename = NULL;
+ struct stat buf;
+ int fuzzy = 0;
+
+ char* env_dssi_path = getenv("DSSI_PATH");
+ string dssi_path;
+ if (!env_dssi_path) {
+ cerr << "DSSI_PATH is empty. Assuming /usr/lib/dssi:/usr/local/lib/dssi." << endl;
+ dssi_path = "/usr/lib/dssi:/usr/local/lib/dssi";
+ } else {
+ dssi_path = env_dssi_path;
+ }
+
+ if (strlen(dllBase) > 3 && !strcasecmp(dllBase + strlen(dllBase) - 3, ".so")) {
+ dllBase[strlen(dllBase) - 3] = '\0';
+ }
+
+
+ // This is pretty nasty, it loops through the path, even if the dllBase is absolute
+ while (dssi_path != "") {
+ string directory = dssi_path.substr(0, dssi_path.find(':'));
+ if (dssi_path.find(':') != string::npos)
+ dssi_path = dssi_path.substr(dssi_path.find(':')+1);
+ else
+ dssi_path = "";
+
+ if (*dllBase == '/') {
+ subpath = strdup(dllBase);
+ } else {
+ subpath = (char*)malloc(strlen(directory.c_str()) + strlen(dllBase) + 2);
+ sprintf(subpath, "%s/%s", directory.c_str(), dllBase);
+ }
+
+ for (fuzzy = 0; fuzzy <= 1; ++fuzzy) {
+
+ if (!(subdir = opendir(subpath))) {
+ if (verbose) {
+ fprintf(stderr, "%s: can't open plugin GUI directory \"%s\"\n", myName, subpath);
+ }
+ break;
+ }
+
+ while ((entry = readdir(subdir))) {
+
+ if (entry->d_name[0] == '.')
+ continue;
+ if (!strchr(entry->d_name, '_'))
+ continue;
+
+ if (fuzzy) {
+ if (verbose) {
+ fprintf(stderr, "checking %s against %s\n", entry->d_name, dllBase);
+ }
+ if (strncmp(entry->d_name, dllBase, strlen(dllBase)))
+ continue;
+ } else {
+ if (verbose) {
+ fprintf(stderr, "checking %s against %s\n", entry->d_name, label);
+ }
+ if (strncmp(entry->d_name, label, strlen(label)))
+ continue;
+ }
+
+ filename = (char*)malloc(strlen(subpath) + strlen(entry->d_name) + 2);
+ sprintf(filename, "%s/%s", subpath, entry->d_name);
+
+ if (stat(filename, &buf)) {
+ perror("stat failed");
+ free(filename);
+ continue;
+ }
+
+ if ((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) &&
+ (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+
+ if (verbose) {
+ fprintf(stderr, "%s: trying to execute GUI at \"%s\"\n",
+ myName, filename);
+ }
+
+ if (fork() == 0) {
+ execlp(filename, filename, oscUrl.c_str(), dllName, label,
+ node_model()->name().c_str(), 0);
+ perror("exec failed");
+ exit(1);
+ }
+
+ free(filename);
+ free(subpath);
+ free(dllBase);
+ return true;
+ }
+
+ free(filename);
+ }
+ }
+ }
+
+ cerr << "Unable to launch DSSI GUI for " << node_model()->path() << endl;
+
+ free(subpath);
+ free(dllBase);
+ return false;
+}
+
+
+void
+DSSIController::show_menu(GdkEventButton* event)
+{
+ if (m_banks_dirty)
+ update_program_menu();
+ NodeController::show_menu(event);
+}
+
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/DSSIController.h b/src/progs/gtk/DSSIController.h
new file mode 100644
index 00000000..53f6fc8b
--- /dev/null
+++ b/src/progs/gtk/DSSIController.h
@@ -0,0 +1,79 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSICONTROLLER_H
+#define DSSICONTROLLER_H
+
+#include <string>
+#include <gtkmm.h>
+#include "util/Path.h"
+#include "NodeController.h"
+
+using std::string;
+using Om::Path;
+using namespace LibOmClient;
+
+namespace LibOmClient {
+ class MetadataModel;
+ class NodeModel;
+ class PortModel;
+}
+
+namespace OmGtk {
+
+class Controller;
+class OmModule;
+class NodeControlWindow;
+class NodePropertiesWindow;
+class PortController;
+class OmFlowCanvas;
+class DSSIModule;
+
+/* Controller for a DSSI node.
+ *
+ * \ingroup OmGtk
+ */
+class DSSIController : public NodeController
+{
+public:
+ DSSIController(NodeModel* model);
+
+ virtual ~DSSIController() {}
+
+ void show_gui();
+ bool attempt_to_show_gui();
+ void program_add(int bank, int program, const string& name);
+ void program_remove(int bank, int program);
+
+ void send_program_change(int bank, int program);
+
+ void create_module(OmFlowCanvas* canvas);
+
+ void show_menu(GdkEventButton* event);
+
+private:
+ void update_program_menu();
+
+ Gtk::Menu m_program_menu;
+ Glib::RefPtr<Gtk::MenuItem> m_program_menu_item;
+
+ bool m_banks_dirty;
+};
+
+
+} // namespace OmGtk
+
+#endif // DSSICONTROLLER_H
diff --git a/src/progs/gtk/DSSIModule.cpp b/src/progs/gtk/DSSIModule.cpp
new file mode 100644
index 00000000..7dae48ac
--- /dev/null
+++ b/src/progs/gtk/DSSIModule.cpp
@@ -0,0 +1,38 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIModule.h"
+#include "DSSIController.h"
+
+namespace OmGtk {
+
+
+DSSIModule::DSSIModule(OmFlowCanvas* canvas, DSSIController* node)
+: OmModule(canvas, static_cast<NodeController*>(node))
+{
+}
+
+
+void
+DSSIModule::on_double_click(GdkEventButton* ev)
+{
+ DSSIController* const dc = static_cast<DSSIController*>(m_node);
+ if (!dc->attempt_to_show_gui())
+ show_control_window();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/DSSIModule.h b/src/progs/gtk/DSSIModule.h
new file mode 100644
index 00000000..2ad10c04
--- /dev/null
+++ b/src/progs/gtk/DSSIModule.h
@@ -0,0 +1,43 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSIMODULE_H
+#define DSSIMODULE_H
+
+#include "OmModule.h"
+
+namespace OmGtk {
+
+class DSSIController;
+
+/* Module for a DSSI node.
+ *
+ * \ingroup OmGtk
+ */
+class DSSIModule : public OmModule
+{
+public:
+ DSSIModule(OmFlowCanvas* canvas, DSSIController* node);
+ virtual ~DSSIModule() {}
+
+ void on_double_click(GdkEventButton* ev);
+};
+
+
+} // namespace OmGtk
+
+#endif // DSSIMODULE_H
+
diff --git a/src/progs/gtk/GladeFactory.cpp b/src/progs/gtk/GladeFactory.cpp
new file mode 100644
index 00000000..e930c4c4
--- /dev/null
+++ b/src/progs/gtk/GladeFactory.cpp
@@ -0,0 +1,69 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "GladeFactory.h"
+#include <iostream>
+#include <fstream>
+using std::cout; using std::cerr; using std::endl;
+using std::ifstream;
+
+namespace OmGtk {
+
+
+Glib::ustring GladeFactory::glade_filename = "";
+
+
+void
+GladeFactory::find_glade_file()
+{
+ // Check for the .glade file in current directory
+ glade_filename = "./om_gtk.glade";
+ ifstream fs(glade_filename.c_str());
+ if (fs.fail()) { // didn't find it, check PKGDATADIR
+ fs.clear();
+ glade_filename = PKGDATADIR;
+ glade_filename += "/om_gtk.glade";
+
+ fs.open(glade_filename.c_str());
+ if (fs.fail()) {
+ cerr << "[GladeFactory] Unable to find om_gtk.glade in current directory or " << PKGDATADIR << "." << endl;
+ throw;
+ }
+ fs.close();
+ }
+ cerr << "[GladeFactory] Loading widgets from " << glade_filename.c_str() << endl;
+}
+
+
+Glib::RefPtr<Gnome::Glade::Xml>
+GladeFactory::new_glade_reference(const string& toplevel_widget)
+{
+ if (glade_filename == "")
+ find_glade_file();
+
+ try {
+ if (toplevel_widget == "")
+ return Gnome::Glade::Xml::create(glade_filename);
+ else
+ return Gnome::Glade::Xml::create(glade_filename, toplevel_widget.c_str());
+ } catch (const Gnome::Glade::XmlError& ex) {
+ cerr << ex.what() << endl;
+ throw ex;
+ }
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/GladeFactory.h b/src/progs/gtk/GladeFactory.h
new file mode 100644
index 00000000..a3ee1022
--- /dev/null
+++ b/src/progs/gtk/GladeFactory.h
@@ -0,0 +1,48 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef GLADEFACTORY_H
+#define GLADEFACTORY_H
+
+#include <string>
+#include <libglademm/xml.h>
+
+using std::string;
+
+namespace OmGtk {
+
+
+/** Creates glade references, so various objects can create widgets.
+ * Purely static.
+ *
+ * \ingroup OmGtk
+ */
+class GladeFactory {
+public:
+ static Glib::RefPtr<Gnome::Glade::Xml>
+ new_glade_reference(const string& toplevel_widget = "");
+
+private:
+ GladeFactory() {}
+
+ static void find_glade_file();
+ static Glib::ustring glade_filename;
+};
+
+
+} // namespace OmGtk
+
+#endif // GLADEFACTORY_H
diff --git a/src/progs/gtk/GtkClientInterface.cpp b/src/progs/gtk/GtkClientInterface.cpp
new file mode 100644
index 00000000..7bda1e9e
--- /dev/null
+++ b/src/progs/gtk/GtkClientInterface.cpp
@@ -0,0 +1,81 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "GtkClientInterface.h"
+#include <cstdio>
+#include <string>
+#include <sys/time.h>
+#include <time.h>
+#include <gtkmm.h>
+#include "ControlInterface.h"
+
+namespace OmGtk {
+
+
+GtkClientInterface::GtkClientInterface(ControlInterface* interface, int client_port)
+: OSCListener(client_port)
+, ModelClientInterface()
+, _interface(interface)
+, _num_plugins(0)
+, _events(4096)
+{
+}
+
+
+/** Push an event (from the engine, ie 'new patch') on to the queue.
+ */
+void
+GtkClientInterface::push_event(Closure ev)
+{
+ bool success = false;
+ bool first = true;
+
+ // (Very) slow busy-wait if the queue is full
+ // FIXME: Make this wait on a signal from process_events iff this happens
+ while (!success) {
+ success = _events.push(ev);
+ if (!success) {
+ if (first) {
+ cerr << "[GtkClientInterface] WARNING: (Client) event queue full. Waiting to try again..." << endl;
+ first = false;
+ }
+ usleep(200000); // 100 milliseconds (2* rate process_events is called)
+ }
+ }
+}
+
+
+/** Process all queued events that came from the OSC thread.
+ *
+ * This function is to be called from the Gtk thread only.
+ */
+bool
+GtkClientInterface::process_events()
+{
+ // Process a maximum of queue-size events, to prevent locking the GTK
+ // thread indefinitely while processing continually arriving events
+ size_t num_processed = 0;
+ while (!_events.is_empty() && num_processed++ < _events.capacity()/2) {
+ Closure& ev = _events.pop();
+ ev();
+ ev.disconnect();
+ }
+
+ return true;
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/GtkClientInterface.h b/src/progs/gtk/GtkClientInterface.h
new file mode 100644
index 00000000..7214ef19
--- /dev/null
+++ b/src/progs/gtk/GtkClientInterface.h
@@ -0,0 +1,156 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef GTKCLIENTHOOKS_H
+#define GTKCLIENTHOOKS_H
+
+#include <cassert>
+#include <string>
+#include <queue>
+#include <iostream>
+#include <sigc++/sigc++.h>
+#include "ControlInterface.h"
+#include "OSCListener.h"
+#include "util/Queue.h"
+#include "util/CountedPtr.h"
+#include "ModelClientInterface.h"
+#include "SigClientInterface.h"
+using std::string;
+using std::cerr; using std::endl;
+
+namespace LibOmClient { class PluginModel; }
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+/** Returns nothing and takes no parameters (because they have been bound) */
+typedef sigc::slot<void> Closure;
+
+#if 0
+/** ModelClientInterface implementation for the Gtk client.
+ *
+ * This is a threadsafe interface to OmGtk. It provides the same interface as
+ * @ref ControlInterface, except all public functions may be called in a thread
+ * other than the GTK thread (a closure will be created and pushed through a
+ * queue for the GTK thread to execute).
+ *
+ * It is also a ModelClientInterface, which is how it is driven by the engine.
+ * This is redundant, "ControlInterface" needs to go away. A model database
+ * that wraps a ClientInterface and emits sigc signals when things change would
+ * be a much better way of doing this.
+ *
+ * \ingroup OmGtk
+ */
+class GtkClientInterface : public OSCListener, public ModelClientInterface
+{
+public:
+ GtkClientInterface(ControlInterface* interface, int client_port);
+
+ void set_ignore_port(const string& path) { _ignore_port_path = path; }
+ void clear_ignore_port() { _ignore_port_path = ""; }
+
+ // FIXME: ugly accessor
+ size_t num_plugins() const { return _num_plugins; }
+
+ // OSC thread functions (deferred calls)
+
+ void bundle_begin() {}
+ void bundle_end() {}
+
+ void num_plugins(size_t num) { _num_plugins = num; }
+
+ void error(const string& msg)
+ { push_event(sigc::bind(_interface->error_slot, msg)); }
+
+ void new_plugin_model(PluginModel* const pm)
+ { push_event(sigc::bind(_interface->new_plugin_slot, pm)); }
+
+ void new_patch_model(PatchModel* const pm)
+ { push_event(sigc::bind(_interface->new_patch_slot, pm)); }
+
+ void new_node_model(NodeModel* const nm)
+ { assert(nm); push_event(sigc::bind(_interface->new_node_slot, nm)); }
+
+ void new_port_model(PortModel* const pm)
+ { push_event(sigc::bind(_interface->new_port_slot, pm)); }
+
+ void connection_model(ConnectionModel* const cm)
+ { push_event(sigc::bind(_interface->connection_slot, cm)); }
+
+ void object_destroyed(const string& path)
+ { push_event(sigc::bind(_interface->object_destroyed_slot, path)); }
+
+ void patch_enabled(const string& path)
+ { push_event(sigc::bind(_interface->patch_enabled_slot, path)); }
+
+ void patch_disabled(const string& path)
+ { push_event(sigc::bind(_interface->patch_disabled_slot, path)); }
+
+ void patch_cleared(const string& path)
+ { push_event(sigc::bind(_interface->patch_cleared_slot, path)); }
+
+ void object_renamed(const string& old_path, const string& new_path)
+ { push_event(sigc::bind(_interface->object_renamed_slot, old_path, new_path)); }
+
+ void disconnection(const string& src_port_path, const string& dst_port_path)
+ { push_event(sigc::bind(_interface->disconnection_slot, src_port_path, dst_port_path)); }
+
+ void metadata_update(const string& path, const string& key, const string& value)
+ { push_event(sigc::bind(_interface->metadata_update_slot, path, key, value)); }
+
+ void control_change(const string& port_path, float value)
+ { push_event(sigc::bind(_interface->control_change_slot, port_path, value)); }
+
+ void program_add(const string& path, uint32_t bank, uint32_t program, const string& name)
+ { push_event(sigc::bind(_interface->program_add_slot, path, bank, program, name)); }
+
+ void program_remove(const string& path, uint32_t bank, uint32_t program)
+ { push_event(sigc::bind(_interface->program_remove_slot, path, bank, program)); }
+
+ /** Process all queued events - MUST be called from Gtk thread.
+ * Registered as a GTK idle handler by App. */
+ bool process_events();
+
+ static void instantiate(ControlInterface* interface, int client_port)
+ { if (!_instance) _instance = new GtkClientInterface(interface, client_port); }
+
+ inline static CountedPtr<GtkClientInterface> instance()
+ { assert(_instance); return _instance; }
+
+private:
+
+ static CountedPtr<GtkClientInterface> _instance;
+
+ /** Provides the functions/slots the closures will actually call in the GTK thread */
+ ControlInterface* _interface;
+
+ size_t _num_plugins;
+
+ void push_event(Closure ev);
+
+ /** Set if a port slider is grabbed and is being dragged.
+ * If a control event comes in for a port with this path, we'll just
+ * ignore it outright. (Just an optimization over doing all the searching
+ * for the port slider just to ignore the event) */
+ string _ignore_port_path;
+
+ Queue<Closure> _events;
+};
+#endif
+
+} // namespace OmGtk
+
+#endif // GTKCLIENTHOOKS_H
diff --git a/src/progs/gtk/GtkObjectController.cpp b/src/progs/gtk/GtkObjectController.cpp
new file mode 100644
index 00000000..7277d346
--- /dev/null
+++ b/src/progs/gtk/GtkObjectController.cpp
@@ -0,0 +1,40 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "GtkObjectController.h"
+
+namespace OmGtk {
+
+
+GtkObjectController::GtkObjectController(ObjectModel* model)
+: m_model(model)
+{
+ assert(m_model != NULL);
+}
+
+
+/** Derived classes should override this to handle special metadata
+ * keys, then call this to set the model's metadata key.
+ */
+void
+GtkObjectController::metadata_update(const string& key, const string& value)
+{
+ m_model->set_metadata(key, value);
+}
+
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/GtkObjectController.h b/src/progs/gtk/GtkObjectController.h
new file mode 100644
index 00000000..4a09b9c7
--- /dev/null
+++ b/src/progs/gtk/GtkObjectController.h
@@ -0,0 +1,77 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef GTKOBJECTCONTROLLER_H
+#define GTKOBJECTCONTROLLER_H
+
+#include <string>
+#include <gtkmm.h>
+
+#include "ObjectModel.h"
+#include "ObjectController.h"
+
+
+using std::string;
+
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class Controller;
+
+
+/** Controller for an Om object.
+ *
+ * Management of the model and view are this object's responsibility.
+ * The view may not be created (ie in the case of patches which have never
+ * been shown and all their children).
+ *
+ * \ingroup OmGtk
+ */
+class GtkObjectController : public LibOmClient::ObjectController
+{
+public:
+ GtkObjectController(ObjectModel* model);
+ virtual ~GtkObjectController() {}
+
+ /** Destroy object.
+ *
+ * Object must be safe to delete immediately following the return of
+ * this call.
+ */
+ virtual void destroy() = 0;
+
+ virtual void add_to_store() = 0;
+ virtual void remove_from_store() = 0;
+
+ virtual void metadata_update(const string& key, const string& value);
+
+ /** Rename object */
+ virtual void set_path(const Path& new_path)
+ { m_model->set_path(new_path); }
+
+ const Path& path() const { return m_model->path(); }
+
+ ObjectModel* model() const { return m_model; }
+
+protected:
+ ObjectModel* m_model; ///< Model for this object
+};
+
+
+} // namespace OmGtk
+
+#endif // GTKOBJECTCONTROLLER_H
diff --git a/src/progs/gtk/LashController.cpp b/src/progs/gtk/LashController.cpp
new file mode 100644
index 00000000..d95d8515
--- /dev/null
+++ b/src/progs/gtk/LashController.cpp
@@ -0,0 +1,168 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "LashController.h"
+#include "config.h"
+#include <iostream>
+#include <string>
+#include <cassert>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "App.h"
+#include "PatchController.h"
+#include "PatchModel.h"
+
+using std::cerr; using std::cout; using std::endl;
+using std::string;
+
+namespace OmGtk {
+
+
+LashController::LashController(lash_args_t* args)
+: m_client(NULL)
+{
+ m_client = lash_init(args, PACKAGE_NAME,
+ /*LASH_Config_Data_Set|*/LASH_Config_File, LASH_PROTOCOL(2, 0));
+ if (m_client == NULL) {
+ cerr << "[LashController] Failed to connect to LASH. Session management will not function." << endl;
+ } else {
+ cout << "[LashController] Lash initialised" << endl;
+ }
+
+ lash_event_t* event = lash_event_new_with_type(LASH_Client_Name);
+ lash_event_set_string(event, "OmGtk");
+ lash_send_event(m_client, event);
+}
+
+
+LashController::~LashController()
+{
+ if (m_client != NULL) {
+ lash_event_t* quit_event = lash_event_new_with_type(LASH_Quit);
+ lash_send_event(m_client, quit_event);
+ }
+}
+
+
+void
+LashController::process_events()
+{
+ assert(m_client != NULL);
+
+ lash_event_t* ev = NULL;
+ lash_config_t* conf = NULL;
+
+ // Process events
+ while ((ev = lash_get_event(m_client)) != NULL) {
+ handle_event(ev);
+ lash_event_destroy(ev);
+ }
+
+ // Process configs
+ while ((conf = lash_get_config(m_client)) != NULL) {
+ handle_config(conf);
+ lash_config_destroy(conf);
+ }
+}
+
+
+void
+LashController::handle_event(lash_event_t* ev)
+{
+ assert(ev != NULL);
+
+ LASH_Event_Type type = lash_event_get_type(ev);
+ const char* c_str = lash_event_get_string(ev);
+ string str = (c_str == NULL) ? "" : c_str;
+
+ if (type == LASH_Save_File) {
+ cout << "[LashController] LASH Save File - " << str << endl;
+ save(str);
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Save_File));
+ } else if (type == LASH_Restore_File) {
+ cout << "[LashController] LASH Restore File - " << str << endl;
+ cerr << "LASH RESTORE NOT YET (RE)IMPLEMENTED." << endl;
+ /*_controller->load_session_blocking(str + "/session");
+ _controller->lash_restore_finished();
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Restore_File));
+ */
+ /*} else if (type == LASH_Save_Data_Set) {
+ //cout << "[LashController] LASH Save Data Set - " << endl;
+
+ // Tell LASH we're done
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Save_Data_Set));
+ */
+ } else if (type == LASH_Quit) {
+ cout << "[LashController] LASH Quit" << endl;
+ m_client = NULL;
+ App::instance().quit();
+ } else {
+ cerr << "[LashController] Unhandled LASH event, type: " << static_cast<int>(type) << endl;
+ }
+}
+
+
+void
+LashController::handle_config(lash_config_t* conf)
+{
+ assert(conf != NULL);
+
+ const char* key = NULL;
+ const void* val = NULL;
+ size_t val_size = 0;
+
+ cout << "[LashController] LASH Config. Key = " << key << endl;
+
+ key = lash_config_get_key(conf);
+ val = lash_config_get_value(conf);
+ val_size = lash_config_get_value_size(conf);
+}
+
+void
+LashController::save(const string& dir)
+{
+ cerr << "LASH SAVING NOT YET (RE)IMPLEMENTED\n";
+ /*
+ PatchController* pc = NULL;
+
+ // Save every patch under dir with it's path as a filename
+ // (so the dir structure will resemble the patch heirarchy)
+ for (map<string,PatchController*>::iterator i = m_app->patches().begin();
+ i != m_app->patches().end(); ++i) {
+ pc = (*i).second;
+ pc->model()->filename(dir + pc->model()->path() + ".om");
+ }
+
+ // Create directories
+ for (map<string,PatchController*>::iterator i = m_app->patches().begin();
+ i != m_app->patches().end(); ++i) {
+ pc = (*i).second;
+ if (pc->model()->parent() != NULL) {
+ string path = OmPath::parent(pc->model()->path()).substr(1) + "/";
+ while (path.find("/") != string::npos) {
+ mkdir(string(dir +"/"+ path.substr(0, path.find("/"))).c_str(), 0744);
+ path = path.substr(path.find("/")+1);
+ }
+ }
+ _controller->save_patch_blocking(pc->model(), pc->model()->filename(), false);
+ }
+
+ //m_app->state_manager()->save(str + "/omgtkrc");
+ _controller->save_session_blocking(dir + "/session");
+ */
+}
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/LashController.h b/src/progs/gtk/LashController.h
new file mode 100644
index 00000000..f61c9f7f
--- /dev/null
+++ b/src/progs/gtk/LashController.h
@@ -0,0 +1,53 @@
+/* This file is part of OmGtk. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LASHCONTROLLER_H
+#define LASHCONTROLLER_H
+
+#include <string>
+#include <lash/lash.h>
+using std::string;
+
+namespace OmGtk {
+
+class App;
+
+/* Old and unused LASH controller.
+ *
+ * \ingroup OmGtk
+ */
+class LashController
+{
+public:
+ LashController(lash_args_t* args);
+ ~LashController();
+
+ bool enabled() { return lash_enabled(m_client); }
+ void process_events();
+
+private:
+ void save(const string& dir);
+
+ lash_client_t* m_client;
+
+ void handle_event(lash_event_t* conf);
+ void handle_config(lash_config_t* conf);
+};
+
+
+} // namespace OmGtk
+
+#endif // LASHCONTROLLER_H
diff --git a/src/progs/gtk/LoadPatchWindow.cpp b/src/progs/gtk/LoadPatchWindow.cpp
new file mode 100644
index 00000000..60f6e15f
--- /dev/null
+++ b/src/progs/gtk/LoadPatchWindow.cpp
@@ -0,0 +1,131 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "LoadPatchWindow.h"
+#include <sys/types.h>
+#include <dirent.h>
+#include "App.h"
+#include "Configuration.h"
+#include "PatchController.h"
+#include "PatchModel.h"
+#include "Controller.h"
+
+namespace OmGtk {
+
+
+LoadPatchWindow::LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::FileChooserDialog(cobject),
+ m_patch_controller(NULL),
+ m_replace(true)
+{
+ xml->get_widget("load_patch_poly_from_current_radio", m_poly_from_current_radio);
+ xml->get_widget("load_patch_poly_from_file_radio", m_poly_from_file_radio);
+ xml->get_widget("load_patch_poly_from_user_radio", m_poly_from_user_radio);
+ xml->get_widget("load_patch_poly_spinbutton", m_poly_spinbutton);
+ xml->get_widget("load_patch_ok_button", m_ok_button);
+ xml->get_widget("load_patch_cancel_button", m_cancel_button);
+
+ m_poly_from_current_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected));
+ m_poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected));
+ m_poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_user_selected));
+ m_ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::ok_clicked));
+ m_cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::cancel_clicked));
+
+ m_poly_from_current_radio->set_active(true);
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.om");
+ filt.set_name("Om patch files (*.om)");
+ set_filter(filt);
+
+ // Add global examples directory to "shortcut folders" (bookmarks)
+ string examples_dir = PKGDATADIR;
+ examples_dir.append("/patches");
+ DIR* d = opendir(examples_dir.c_str());
+ if (d != NULL)
+ add_shortcut_folder(examples_dir);
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+LoadPatchWindow::patch_controller(PatchController* pc)
+{
+ m_patch_controller = pc;
+}
+
+
+void
+LoadPatchWindow::on_show()
+{
+ if (App::instance().configuration()->patch_folder().length() > 0)
+ set_current_folder(App::instance().configuration()->patch_folder());
+ Gtk::FileChooserDialog::on_show();
+}
+
+
+///// Event Handlers //////
+
+
+void
+LoadPatchWindow::poly_from_file_selected()
+{
+ m_poly_spinbutton->property_sensitive() = false;
+}
+
+
+void
+LoadPatchWindow::poly_from_user_selected()
+{
+ m_poly_spinbutton->property_sensitive() = true;
+}
+
+
+void
+LoadPatchWindow::ok_clicked()
+{
+ // These values are interpreted by load_patch() as "not defined", ie load from file
+ string name = "";
+ int poly = 0;
+
+ if (m_poly_from_user_radio->get_active())
+ poly = m_poly_spinbutton->get_value_as_int();
+
+ if (m_replace)
+ Controller::instance().clear_patch(m_patch_controller->model()->path());
+
+ PatchModel* pm = new PatchModel(m_patch_controller->model()->path(), poly);
+ pm->filename(get_filename());
+ pm->set_metadata("filename", get_filename());
+ pm->set_parent(m_patch_controller->patch_model()->parent());
+ //Controller::instance().push_added_patch(pm);
+ Controller::instance().load_patch(pm, true, true);
+
+ hide();
+}
+
+
+void
+LoadPatchWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/LoadPatchWindow.h b/src/progs/gtk/LoadPatchWindow.h
new file mode 100644
index 00000000..7d7093bd
--- /dev/null
+++ b/src/progs/gtk/LoadPatchWindow.h
@@ -0,0 +1,73 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LOADPATCHWINDOW_H
+#define LOADPATCHWINDOW_H
+
+#include "PluginModel.h"
+
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+
+namespace OmGtk {
+
+class PatchController;
+
+
+/** 'Load Patch' window.
+ *
+ * Loaded by glade as a derived object. Used for both "Load" and "Load Into"
+ * operations (the radio button state should be changed with the provided
+ * methods prior to presenting this window).
+ *
+ * This is not for loading subpatches. See @a LoadSubpatchWindow for that.
+ *
+ * \ingroup OmGtk
+ */
+class LoadPatchWindow : public Gtk::FileChooserDialog
+{
+public:
+ LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void patch_controller(PatchController* pc);
+
+ void set_replace() { m_replace = true; }
+ void set_merge() { m_replace = false; }
+
+protected:
+ void on_show();
+
+private:
+ void poly_from_file_selected();
+ void poly_from_user_selected();
+ void ok_clicked();
+ void cancel_clicked();
+
+ PatchController* m_patch_controller;
+ bool m_replace;
+
+ Gtk::RadioButton* m_poly_from_current_radio;
+ Gtk::RadioButton* m_poly_from_file_radio;
+ Gtk::RadioButton* m_poly_from_user_radio;
+ Gtk::SpinButton* m_poly_spinbutton;
+ Gtk::Button* m_ok_button;
+ Gtk::Button* m_cancel_button;
+};
+
+
+} // namespace OmGtk
+
+#endif // LOADPATCHWINDOW_H
diff --git a/src/progs/gtk/LoadPluginWindow.cpp b/src/progs/gtk/LoadPluginWindow.cpp
new file mode 100644
index 00000000..b84ae39e
--- /dev/null
+++ b/src/progs/gtk/LoadPluginWindow.cpp
@@ -0,0 +1,407 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "LoadPluginWindow.h"
+#include <iostream>
+#include <cassert>
+#include <algorithm>
+#include <cctype>
+#include "PatchController.h"
+#include "NodeModel.h"
+#include "Controller.h"
+#include "App.h"
+#include "PatchWindow.h"
+#include "OmFlowCanvas.h"
+#include "PatchModel.h"
+#include "Store.h"
+using std::cout; using std::cerr; using std::endl;
+
+
+namespace OmGtk {
+
+LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ m_patch_controller(NULL),
+ m_has_shown(false),
+ m_plugin_name_offset(0),
+ m_new_module_x(0),
+ m_new_module_y(0)
+{
+ xml->get_widget("load_plugin_plugins_treeview", m_plugins_treeview);
+ xml->get_widget("load_plugin_polyphonic_checkbutton", m_polyphonic_checkbutton);
+ xml->get_widget("load_plugin_name_entry", m_node_name_entry);
+ xml->get_widget("load_plugin_clear_button", m_clear_button);
+ xml->get_widget("load_plugin_add_button", m_add_button);
+ xml->get_widget("load_plugin_close_button", m_close_button);
+ //xml->get_widget("load_plugin_ok_button", m_ok_button);
+
+ xml->get_widget("load_plugin_filter_combo", m_filter_combo);
+ xml->get_widget("load_plugin_search_entry", m_search_entry);
+
+ // Set up the plugins list
+ m_plugins_liststore = Gtk::ListStore::create(m_plugins_columns);
+ m_plugins_treeview->set_model(m_plugins_liststore);
+ m_plugins_treeview->append_column("Name", m_plugins_columns.m_col_name);
+ m_plugins_treeview->append_column("Type", m_plugins_columns.m_col_type);
+ m_plugins_treeview->append_column("URI", m_plugins_columns.m_col_uri);
+ //m_plugins_treeview->append_column("Library", m_plugins_columns.m_col_library);
+ //m_plugins_treeview->append_column("Label", m_plugins_columns.m_col_label);
+
+ // This could be nicer.. store the TreeViewColumns locally maybe?
+ m_plugins_treeview->get_column(0)->set_sort_column(m_plugins_columns.m_col_name);
+ m_plugins_treeview->get_column(1)->set_sort_column(m_plugins_columns.m_col_type);
+ m_plugins_treeview->get_column(2)->set_sort_column(m_plugins_columns.m_col_uri);
+ //m_plugins_treeview->get_column(3)->set_sort_column(m_plugins_columns.m_col_library);
+ //m_plugins_treeview->get_column(4)->set_sort_column(m_plugins_columns.m_col_label);
+ for (int i=0; i < 3; ++i)
+ m_plugins_treeview->get_column(i)->set_resizable(true);
+
+ // Set up the search criteria combobox
+ m_criteria_liststore = Gtk::ListStore::create(m_criteria_columns);
+ m_filter_combo->set_model(m_criteria_liststore);
+ Gtk::TreeModel::iterator iter = m_criteria_liststore->append();
+ Gtk::TreeModel::Row row = *iter;
+ row[m_criteria_columns.m_col_label] = "Name contains: ";
+ row[m_criteria_columns.m_col_criteria] = CriteriaColumns::NAME;
+ m_filter_combo->set_active(iter);
+ iter = m_criteria_liststore->append(); row = *iter;
+ row[m_criteria_columns.m_col_label] = "Type contains: ";
+ row[m_criteria_columns.m_col_criteria] = CriteriaColumns::TYPE;
+ iter = m_criteria_liststore->append(); row = *iter;
+ row[m_criteria_columns.m_col_label] = "URI contains: ";
+ row[m_criteria_columns.m_col_criteria] = CriteriaColumns::URI;
+ /*iter = m_criteria_liststore->append(); row = *iter;
+ row[m_criteria_columns.m_col_label] = "Library contains: ";
+ row[m_criteria_columns.m_col_criteria] = CriteriaColumns::LIBRARY;
+ iter = m_criteria_liststore->append(); row = *iter;
+ row[m_criteria_columns.m_col_label] = "Label contains: ";
+ row[m_criteria_columns.m_col_criteria] = CriteriaColumns::LABEL;*/
+
+ m_clear_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::clear_clicked));
+ m_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_ok_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::ok_clicked));
+ m_plugins_treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_activated));
+ m_search_entry->signal_activate().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked));
+ m_search_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::filter_changed));
+
+ m_selection = m_plugins_treeview->get_selection();
+ m_selection->signal_changed().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_selection_changed));
+
+ //m_add_button->grab_default();
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+LoadPluginWindow::patch_controller(PatchController* pc)
+{
+ m_patch_controller = pc;
+
+ if (pc->patch_model()->poly() <= 1)
+ m_polyphonic_checkbutton->property_sensitive() = false;
+ else
+ m_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 (!m_has_shown) {
+ set_plugin_model(Store::instance().plugins());
+
+ // Center on patch window
+ int m_w, m_h;
+ get_size(m_w, m_h);
+
+ int parent_x, parent_y, parent_w, parent_h;
+ m_patch_controller->window()->get_position(parent_x, parent_y);
+ m_patch_controller->window()->get_size(parent_w, parent_h);
+
+ move(parent_x + parent_w/2 - m_w/2, parent_y + parent_h/2 - m_h/2);
+
+ m_has_shown = true;
+ }
+ Gtk::Window::on_show();
+}
+
+
+void
+LoadPluginWindow::on_hide()
+{
+ m_new_module_x = 0;
+ m_new_module_y = 0;
+ Gtk::Window::on_hide();
+}
+
+
+void
+LoadPluginWindow::set_plugin_model(const std::map<string, const PluginModel*>& m)
+{
+ m_plugins_liststore->clear();
+
+ const PluginModel* plugin = NULL;
+ for (std::map<string, const PluginModel*>::const_iterator i = m.begin(); i != m.end(); ++i) {
+ plugin = (*i).second;
+
+ Gtk::TreeModel::iterator iter = m_plugins_liststore->append();
+ Gtk::TreeModel::Row row = *iter;
+
+ row[m_plugins_columns.m_col_name] = plugin->name();
+ //row[m_plugins_columns.m_col_label] = plugin->plug_label();
+ row[m_plugins_columns.m_col_type] = plugin->type_string();
+ row[m_plugins_columns.m_col_uri] = plugin->uri();
+ row[m_plugins_columns.m_col_label] = plugin->name();
+ //row[m_plugins_columns.m_col_library] = plugin->lib_name();
+ row[m_plugins_columns.m_col_plugin_model] = plugin;
+ }
+
+ m_plugins_treeview->columns_autosize();
+}
+
+
+void
+LoadPluginWindow::add_plugin(const PluginModel* const plugin)
+{
+ Gtk::TreeModel::iterator iter = m_plugins_liststore->append();
+ Gtk::TreeModel::Row row = *iter;
+
+ row[m_plugins_columns.m_col_name] = plugin->name();
+ //row[m_plugins_columns.m_col_label] = plugin->plug_label();
+ row[m_plugins_columns.m_col_type] = plugin->type_string();
+ row[m_plugins_columns.m_col_uri] = plugin->uri();
+ row[m_plugins_columns.m_col_label] = plugin->name();
+ //row[m_plugins_columns.m_col_library] = plugin->lib_name();
+ row[m_plugins_columns.m_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()
+{
+ m_plugin_name_offset = 0;
+
+ m_node_name_entry->set_text(generate_module_name());
+
+ //Gtk::TreeModel::iterator iter = m_selection->get_selected();
+ //Gtk::TreeModel::Row row = *iter;
+ //const PluginModel* plugin = row.get_value(m_plugins_columns.m_col_plugin_model);
+}
+
+
+/** 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 = m_selection->get_selected();
+
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ const PluginModel* const plugin = row.get_value(m_plugins_columns.m_col_plugin_model);
+ char num_buf[3];
+ for (uint i=0; i < 99; ++i) {
+ name = plugin->plug_label();
+ if (name == "")
+ name = plugin->name().substr(0, plugin->name().find(' '));
+ if (i+offset != 0) {
+ snprintf(num_buf, 3, "%d", i+offset+1);
+ name += "_";
+ name += num_buf;
+ }
+ if (m_patch_controller->patch_model()->get_node(name) == NULL)
+ break;
+ else
+ name = "";
+ }
+ }
+
+ return name;
+}
+
+
+void
+LoadPluginWindow::add_clicked()
+{
+ Gtk::TreeModel::iterator iter = m_selection->get_selected();
+ bool polyphonic = m_polyphonic_checkbutton->get_active();
+
+ if (iter) { // If anything is selected
+ Gtk::TreeModel::Row row = *iter;
+ const PluginModel* const plugin = row.get_value(m_plugins_columns.m_col_plugin_model);
+ string name = m_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 {
+ const string path = m_patch_controller->model()->base_path() + name;
+ NodeModel* nm = new NodeModel(path);
+ nm->plugin(plugin);
+ nm->polyphonic(polyphonic);
+
+ if (m_new_module_x == 0 && m_new_module_y == 0) {
+ m_patch_controller->get_new_module_location(
+ m_new_module_x, m_new_module_y);
+ }
+ nm->x(m_new_module_x);
+ nm->y(m_new_module_y);
+
+ Controller::instance().create_node_from_model(nm);
+ ++m_plugin_name_offset;
+ m_node_name_entry->set_text(generate_module_name(m_plugin_name_offset));
+
+ // Set the next module location 20 over, for a cascade effect
+ m_new_module_x += 20;
+ m_new_module_y += 20;
+ }
+ }
+}
+
+
+void
+LoadPluginWindow::close_clicked()
+{
+ hide();
+}
+
+
+/*
+void
+LoadPluginWindow::ok_clicked()
+{
+ add_clicked();
+ close_clicked();
+}
+*/
+
+void
+LoadPluginWindow::filter_changed()
+{
+ m_plugins_liststore->clear();
+
+ string search = m_search_entry->get_text();
+ transform(search.begin(), search.end(), search.begin(), toupper);
+
+ // Get selected criteria
+ const Gtk::TreeModel::Row row = *(m_filter_combo->get_active());
+ CriteriaColumns::Criteria criteria = row[m_criteria_columns.m_col_criteria];
+
+ string field;
+
+ Gtk::TreeModel::Row model_row;
+ Gtk::TreeModel::iterator model_iter;
+ size_t num_visible = 0;
+
+ const PluginModel* plugin = NULL;
+ for (std::map<string, const PluginModel*>::const_iterator i = Store::instance().plugins().begin();
+ i != Store::instance().plugins().end(); ++i) {
+ plugin = (*i).second;
+
+ switch (criteria) {
+ case CriteriaColumns::NAME:
+ field = plugin->name(); break;
+ case CriteriaColumns::TYPE:
+ field = plugin->type_string(); 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 = m_plugins_liststore->append();
+ model_row = *model_iter;
+
+ model_row[m_plugins_columns.m_col_name] = plugin->name();
+ //model_row[m_plugins_columns.m_col_label] = plugin->plug_label();
+ model_row[m_plugins_columns.m_col_type] = plugin->type_string();
+ model_row[m_plugins_columns.m_col_uri] = plugin->uri();
+ model_row[m_plugins_columns.m_col_label] = plugin->plug_label();
+ //model_row[m_plugins_columns.m_col_library] = plugin->lib_name();
+ model_row[m_plugins_columns.m_col_plugin_model] = plugin;
+
+ ++num_visible;
+ }
+ }
+
+ if (num_visible == 1) {
+ m_selection->unselect_all();
+ m_selection->select(model_iter);
+ }
+}
+
+
+void
+LoadPluginWindow::clear_clicked()
+{
+ m_search_entry->set_text("");
+ set_plugin_model(Store::instance().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 OmGtk
diff --git a/src/progs/gtk/LoadPluginWindow.h b/src/progs/gtk/LoadPluginWindow.h
new file mode 100644
index 00000000..adf11ab4
--- /dev/null
+++ b/src/progs/gtk/LoadPluginWindow.h
@@ -0,0 +1,145 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef LOADPLUGINWINDOW_H
+#define LOADPLUGINWINDOW_H
+
+#include "PluginModel.h"
+#include <map>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+#include <gtkmm.h>
+
+
+using LibOmClient::PluginModel;
+
+namespace OmGtk {
+
+class PatchController;
+
+// Gtkmm _really_ needs to add some helper to abstract away this stupid nonsense
+
+/** Columns for the plugin list in the load plugin window.
+ *
+ * \ingroup OmGtk
+ */
+class ModelColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+ ModelColumns() {
+ add(m_col_name);
+ add(m_col_type);
+ add(m_col_uri);
+ add(m_col_label);
+ //add(m_col_library);
+ //add(m_col_label);
+ add(m_col_plugin_model);
+ }
+
+ Gtk::TreeModelColumn<Glib::ustring> m_col_name;
+ Gtk::TreeModelColumn<Glib::ustring> m_col_type;
+ Gtk::TreeModelColumn<Glib::ustring> m_col_uri;
+
+ // Not displayed:
+ Gtk::TreeModelColumn<Glib::ustring> m_col_label;
+ Gtk::TreeModelColumn<const PluginModel*> m_col_plugin_model;
+};
+
+
+/** Column for the criteria combo box in the load plugin window.
+ *
+ * \ingroup OmGtk
+ */
+class CriteriaColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+ enum Criteria { NAME, TYPE, URI, };
+
+ CriteriaColumns() { add(m_col_label); add(m_col_criteria); }
+
+ Gtk::TreeModelColumn<Glib::ustring> m_col_label;
+ Gtk::TreeModelColumn<Criteria> m_col_criteria;
+};
+
+
+/** 'Load Plugin' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup OmGtk
+ */
+class LoadPluginWindow : public Gtk::Window
+{
+public:
+ LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void patch_controller(PatchController* pc);
+ void set_plugin_model(const std::map<string, const PluginModel*>& m);
+
+ void set_next_module_location(int x, int y)
+ { m_new_module_x = x; m_new_module_y = y; }
+
+ void add_plugin(const PluginModel* info);
+ bool has_shown() const { return m_has_shown; }
+
+protected:
+ void on_show();
+ void on_hide();
+ bool on_key_press_event(GdkEventKey* event);
+
+private:
+ void add_clicked();
+ void close_clicked();
+ //void ok_clicked();
+ void filter_changed();
+ void clear_clicked();
+
+ void plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col);
+ void plugin_selection_changed();
+ string generate_module_name(int offset = 0);
+
+ PatchController* m_patch_controller;
+ bool m_has_shown; // plugin list only populated on show to speed patch window creation
+
+ Glib::RefPtr<Gtk::ListStore> m_plugins_liststore;
+ ModelColumns m_plugins_columns;
+
+ Glib::RefPtr<Gtk::ListStore> m_criteria_liststore;
+ CriteriaColumns m_criteria_columns;
+
+ Glib::RefPtr<Gtk::TreeSelection> m_selection;
+
+ int m_plugin_name_offset; // see comments for generate_plugin_name
+
+ int m_new_module_x;
+ int m_new_module_y;
+
+ Gtk::TreeView* m_plugins_treeview;
+ Gtk::CheckButton* m_polyphonic_checkbutton;
+ Gtk::Entry* m_node_name_entry;
+ Gtk::Button* m_clear_button;
+ Gtk::Button* m_add_button;
+ Gtk::Button* m_close_button;
+ //Gtk::Button* m_ok_button;
+ Gtk::ComboBox* m_filter_combo;
+ Gtk::Entry* m_search_entry;
+};
+
+
+} // namespace OmGtk
+
+#endif // LOADPLUGINWINDOW_H
diff --git a/src/progs/gtk/LoadSubpatchWindow.cpp b/src/progs/gtk/LoadSubpatchWindow.cpp
new file mode 100644
index 00000000..fe02d772
--- /dev/null
+++ b/src/progs/gtk/LoadSubpatchWindow.cpp
@@ -0,0 +1,177 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "LoadSubpatchWindow.h"
+#include <sys/types.h>
+#include <dirent.h>
+#include <cassert>
+#include "App.h"
+#include "PatchController.h"
+#include "NodeModel.h"
+#include "Controller.h"
+#include "PatchModel.h"
+#include "Configuration.h"
+
+namespace OmGtk {
+
+
+LoadSubpatchWindow::LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::FileChooserDialog(cobject),
+ m_patch_controller(NULL),
+ m_new_module_x(0),
+ m_new_module_y(0)
+{
+ xml->get_widget("load_subpatch_name_from_file_radio", m_name_from_file_radio);
+ xml->get_widget("load_subpatch_name_from_user_radio", m_name_from_user_radio);
+ xml->get_widget("load_subpatch_name_entry", m_name_entry);
+ xml->get_widget("load_subpatch_poly_from_file_radio", m_poly_from_file_radio);
+ xml->get_widget("load_subpatch_poly_from_parent_radio", m_poly_from_parent_radio);
+ xml->get_widget("load_subpatch_poly_from_user_radio", m_poly_from_user_radio);
+ xml->get_widget("load_subpatch_poly_spinbutton", m_poly_spinbutton);
+ xml->get_widget("load_subpatch_ok_button", m_ok_button);
+ xml->get_widget("load_subpatch_cancel_button", m_cancel_button);
+
+ m_name_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_name_entry));
+ m_name_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_name_entry));
+ m_poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner));
+ m_poly_from_parent_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner));
+ m_poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_poly_spinner));
+ m_ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::ok_clicked));
+ m_cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::cancel_clicked));
+
+ Gtk::FileFilter filt;
+ filt.add_pattern("*.om");
+ filt.set_name("Om patch files (*.om)");
+ set_filter(filt);
+
+ // Add global examples directory to "shortcut folders" (bookmarks)
+ string examples_dir = PKGDATADIR;
+ examples_dir.append("/patches");
+ DIR* d = opendir(examples_dir.c_str());
+ if (d != NULL)
+ add_shortcut_folder(examples_dir);
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+LoadSubpatchWindow::patch_controller(PatchController* pc)
+{
+ m_patch_controller = pc;
+
+ char temp_buf[4];
+ snprintf(temp_buf, 4, "%zd", pc->patch_model()->poly());
+ Glib::ustring txt = "Same as parent (";
+ txt.append(temp_buf).append(")");
+ m_poly_from_parent_radio->set_label(txt);
+}
+
+
+void
+LoadSubpatchWindow::on_show()
+{
+ if (App::instance().configuration()->patch_folder().length() > 0)
+ set_current_folder(App::instance().configuration()->patch_folder());
+ Gtk::FileChooserDialog::on_show();
+}
+
+
+///// Event Handlers //////
+
+
+
+void
+LoadSubpatchWindow::disable_name_entry()
+{
+ m_name_entry->property_sensitive() = false;
+}
+
+
+void
+LoadSubpatchWindow::enable_name_entry()
+{
+ m_name_entry->property_sensitive() = true;
+}
+
+
+void
+LoadSubpatchWindow::disable_poly_spinner()
+{
+ m_poly_spinbutton->property_sensitive() = false;
+}
+
+
+void
+LoadSubpatchWindow::enable_poly_spinner()
+{
+ m_poly_spinbutton->property_sensitive() = true;
+}
+
+
+void
+LoadSubpatchWindow::ok_clicked()
+{
+ assert(m_patch_controller != NULL);
+ assert(m_patch_controller->model() != NULL);
+
+ // These values are interpreted by load_patch() as "not defined", ie load from file
+ string name = "";
+ int poly = 0;
+
+ if (m_name_from_user_radio->get_active())
+ name = m_name_entry->get_text();
+
+ if (m_poly_from_user_radio->get_active())
+ poly = m_poly_spinbutton->get_value_as_int();
+ else if (m_poly_from_parent_radio->get_active())
+ poly = m_patch_controller->patch_model()->poly();
+
+ if (m_new_module_x == 0 && m_new_module_y == 0) {
+ m_patch_controller->get_new_module_location(
+ m_new_module_x, m_new_module_y);
+ }
+
+ PatchModel* pm = new PatchModel(m_patch_controller->model()->base_path() + name, poly);
+ pm->filename(get_filename());
+ pm->set_parent(m_patch_controller->model());
+ pm->x(m_new_module_x);
+ pm->y(m_new_module_y);
+ if (name == "")
+ pm->set_path("");
+ char temp_buf[16];
+ snprintf(temp_buf, 16, "%d", m_new_module_x);
+ pm->set_metadata("module-x", temp_buf);
+ snprintf(temp_buf, 16, "%d", m_new_module_y);
+ pm->set_metadata("module-y", temp_buf);
+ Controller::instance().load_patch(pm);
+
+ App::instance().configuration()->set_patch_folder(pm->filename().substr(0, pm->filename().find_last_of("/")));
+
+ hide();
+}
+
+
+void
+LoadSubpatchWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/LoadSubpatchWindow.h b/src/progs/gtk/LoadSubpatchWindow.h
new file mode 100644
index 00000000..45efdd15
--- /dev/null
+++ b/src/progs/gtk/LoadSubpatchWindow.h
@@ -0,0 +1,78 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef LOADSUBPATCHWINDOW_H
+#define LOADSUBPATCHWINDOW_H
+
+#include "PluginModel.h"
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+
+
+namespace OmGtk {
+
+class PatchController;
+
+
+/** 'Add Subpatch' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup OmGtk
+ */
+class LoadSubpatchWindow : public Gtk::FileChooserDialog
+{
+public:
+ LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void patch_controller(PatchController* pc);
+
+ void set_next_module_location(int x, int y)
+ { m_new_module_x = x; m_new_module_y = y; }
+
+protected:
+ void on_show();
+
+private:
+ void disable_name_entry();
+ void enable_name_entry();
+ void disable_poly_spinner();
+ void enable_poly_spinner();
+
+ void ok_clicked();
+ void cancel_clicked();
+
+ PatchController* m_patch_controller;
+
+ int m_new_module_x;
+ int m_new_module_y;
+
+ Gtk::RadioButton* m_name_from_file_radio;
+ Gtk::RadioButton* m_name_from_user_radio;
+ Gtk::Entry* m_name_entry;
+ Gtk::RadioButton* m_poly_from_file_radio;
+ Gtk::RadioButton* m_poly_from_parent_radio;
+ Gtk::RadioButton* m_poly_from_user_radio;
+ Gtk::SpinButton* m_poly_spinbutton;
+ Gtk::Button* m_ok_button;
+ Gtk::Button* m_cancel_button;
+};
+
+
+} // namespace OmGtk
+
+#endif // LOADSUBPATCHWINDOW_H
diff --git a/src/progs/gtk/Loader.cpp b/src/progs/gtk/Loader.cpp
new file mode 100644
index 00000000..643dc3c9
--- /dev/null
+++ b/src/progs/gtk/Loader.cpp
@@ -0,0 +1,233 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Loader.h"
+#include <fstream>
+#include <cassert>
+#include <string>
+#include "PatchLibrarian.h"
+#include "PatchModel.h"
+#include "PatchController.h"
+using std::cout; using std::endl;
+
+namespace OmGtk {
+
+
+// LoadPatchEvent //
+
+void
+LoadPatchEvent::execute()
+{
+ assert(m_patch_librarian != NULL);
+ m_patch_librarian->load_patch(m_patch_model, m_wait, m_merge);
+}
+
+
+
+// SavePatchEvent //
+
+void
+SavePatchEvent::execute()
+{
+ assert(m_patch_librarian != NULL);
+ m_patch_librarian->save_patch(m_patch_model, m_filename, m_recursive);
+}
+
+/*
+void
+LoadSessionEvent::execute()
+{
+ std::ifstream is;
+ is.open(m_filename.c_str(), std::ios::in);
+
+ if ( ! is.good()) {
+ cout << "[Loader] Unable to open session file " << m_filename << endl;
+ return;
+ } else {
+ cout << "[Loader] Loading session from " << m_filename << endl;
+ }
+ string s;
+
+ is >> s;
+ if (s != "version") {
+ cout << "[Loader] Corrupt session file." << endl;
+ is.close();
+ return;
+ }
+
+ is >> s;
+ if (s != "1") {
+ cout << "[Loader] Unrecognised session file version." << endl;
+ is.close();
+ return;
+ }
+
+ while (!is.eof()) {
+ is >> s;
+ if (s == "") continue;
+
+ if (s != "patch") {
+ //cerr << "[Loader] Corrupt session file, aborting session load." << endl;
+ break;
+ } else {
+ is >> s;
+ PatchModel* pm = new PatchModel("", 0);
+ if (s.substr(0, 1) != "/")
+ s = m_filename.substr(0, m_filename.find_last_of("/")+1) + s;
+ pm->filename(s);
+ pm->parent(NULL);
+ m_patch_librarian->load_patch(pm);
+ }
+ }
+
+ is.close();
+}
+
+
+void
+SaveSessionEvent::execute()
+{
+ assert(m_filename != "");
+ string dir = m_filename.substr(0, m_filename.find_last_of("/"));
+
+ string patch_filename;
+
+ std::ofstream os;
+ os.open(m_filename.c_str(), std::ios::out);
+
+ if ( ! os.good()) {
+ cout << "[Loader] Unable to write to session file " << m_filename << endl;
+ return;
+ } else {
+ cout << "[Loader] Saving session to " << m_filename << endl;
+ }
+
+ os << "version 1" << endl;
+
+ for (map<string,PatchController*>::iterator i = app->patches().begin();
+ i != app->patches().end(); ++i)
+ {
+ if ((*i).second->model()->parent() == NULL) {
+ patch_filename = (*i).second->model()->filename();
+
+ // Make path relative if possible
+ if (patch_filename.length() > dir.length() &&
+ patch_filename.substr(0, dir.length()) == dir)
+ patch_filename = patch_filename.substr(dir.length()+1);
+
+ os << "patch " << patch_filename << endl;
+ }
+ }
+
+ os.close();
+}
+*/
+
+
+//////// Loader //////////
+
+
+Loader::Loader(PatchLibrarian* const patch_librarian)
+: m_patch_librarian(patch_librarian),
+ m_event(NULL),
+ m_thread_exit_flag(false)
+{
+ assert(m_patch_librarian != NULL);
+ pthread_mutex_init(&m_mutex, NULL);
+ pthread_cond_init(&m_cond, NULL);
+}
+
+
+void
+Loader::set_event(LoaderEvent* ev)
+{
+ assert(ev != NULL);
+
+ pthread_mutex_lock(&m_mutex);
+ assert(m_event == NULL);
+ m_event = ev;
+ pthread_cond_signal(&m_cond);
+ pthread_mutex_unlock(&m_mutex);
+}
+
+
+void
+Loader::launch()
+{
+ pthread_create(&m_thread, NULL, Loader::thread_function, this);
+}
+
+
+void*
+Loader::thread_function(void* me)
+{
+ Loader* ct = static_cast<Loader*>(me);
+ return ct->m_thread_function(NULL);
+}
+
+
+void*
+Loader::m_thread_function(void *)
+{
+ while ( ! m_thread_exit_flag) {
+ pthread_mutex_lock(&m_mutex);
+ pthread_cond_wait(&m_cond, &m_mutex);
+
+ LoaderEvent* ev = m_event;
+ ev->execute();
+ delete ev;
+ m_event = NULL;
+
+ pthread_mutex_unlock(&m_mutex);
+ }
+
+ pthread_exit(NULL);
+ return NULL;
+}
+
+
+void
+Loader::load_patch(PatchModel* model, bool wait, bool merge)
+{
+ set_event(new LoadPatchEvent(m_patch_librarian, model, wait, merge));
+}
+
+
+void
+Loader::save_patch(PatchModel* model, const string& filename, bool recursive)
+{
+ cout << "[Loader] Saving patch " << filename << endl;
+ set_event(new SavePatchEvent(m_patch_librarian, model, filename, recursive));
+}
+
+
+/*
+void
+Loader::load_session(const string& filename)
+{
+ set_event(new LoadSessionEvent(m_patch_librarian, filename));
+}
+
+
+void
+Loader::save_session(const string& filename)
+{
+ cout << "Saving session..." << endl;
+ set_event(new SaveSessionEvent(m_patch_librarian, filename));
+}
+*/
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/Loader.h b/src/progs/gtk/Loader.h
new file mode 100644
index 00000000..46b5f3a8
--- /dev/null
+++ b/src/progs/gtk/Loader.h
@@ -0,0 +1,154 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LOADERTHREAD_H
+#define LOADERTHREAD_H
+
+#include <string>
+#include <cassert>
+using std::string;
+
+namespace LibOmClient {
+ class PatchLibrarian;
+ class PatchModel;
+}
+using LibOmClient::PatchLibrarian;
+using LibOmClient::PatchModel;
+
+
+namespace OmGtk {
+
+/** Event to run in the Loader thread.
+ *
+ * \ingroup OmGtk
+ */
+class LoaderEvent
+{
+public:
+ virtual void execute() = 0;
+ virtual ~LoaderEvent() {}
+protected:
+ LoaderEvent() {}
+};
+
+
+/** Loader thread patch loading event.
+ *
+ * \ingroup OmGtk
+ */
+class LoadPatchEvent : public LoaderEvent
+{
+public:
+ LoadPatchEvent(PatchLibrarian* pl, PatchModel* model, bool wait, bool merge)
+ : m_patch_librarian(pl), m_patch_model(model), m_wait(wait), m_merge(merge) {}
+ virtual ~LoadPatchEvent() {}
+ void execute();
+private:
+ PatchLibrarian* m_patch_librarian;
+ PatchModel* m_patch_model;
+ bool m_wait;
+ bool m_merge;
+};
+
+
+/** Loader thread patch loading event.
+ *
+ * \ingroup OmGtk
+ */
+class SavePatchEvent : public LoaderEvent
+{
+public:
+ SavePatchEvent(PatchLibrarian* pl, PatchModel* pm, const string& filename, bool recursive)
+ : m_patch_librarian(pl), m_patch_model(pm), m_filename(filename), m_recursive(recursive) {}
+ virtual ~SavePatchEvent() {}
+ void execute();
+private:
+ PatchLibrarian* m_patch_librarian;
+ PatchModel* m_patch_model;
+ string m_filename;
+ bool m_recursive;
+};
+
+/*
+class LoadSessionEvent : public LoaderEvent
+{
+public:
+ LoadSessionEvent(PatchLibrarian* pl, const string& filename)
+ : m_patch_librarian(pl), m_filename(filename) {}
+ virtual ~LoadSessionEvent() {}
+ void execute();
+private:
+ PatchLibrarian* m_patch_librarian;
+ string m_filename;
+};
+
+
+class SaveSessionEvent : public LoaderEvent
+{
+public:
+ SaveSessionEvent(PatchLibrarian* pl, const string& filename)
+ : m_patch_librarian(pl), m_filename(filename) {}
+ virtual ~SaveSessionEvent() {}
+ void execute();
+private:
+ PatchLibrarian* m_patch_librarian;
+ string m_filename;
+};
+*/
+
+/** Thread for loading patch files.
+ *
+ * This is a seperate thread so it can send all the loading message without
+ * blocking everything else, so the app can respond to the incoming events
+ * caused as a result of the patch loading.
+ *
+ * \ingroup OmGtk
+ */
+class Loader
+{
+public:
+ Loader(PatchLibrarian* const patch_librarian);
+ ~Loader() { m_thread_exit_flag = true; }
+
+ void launch();
+ void exit() { m_thread_exit_flag = true; }
+
+ void load_patch(PatchModel* model, bool wait, bool merge);
+ void save_patch(PatchModel* model, const string& filename, bool recursive);
+
+ //void load_session(const string& filename);
+ //void save_session(const string& filename);
+
+ static void* thread_function(void* me);
+
+private:
+ void* m_thread_function(void*);
+
+ void set_event(LoaderEvent* ev);
+
+ PatchLibrarian* const m_patch_librarian;
+ LoaderEvent* m_event;
+ bool m_thread_exit_flag;
+ pthread_t m_thread;
+ pthread_mutex_t m_mutex;
+ pthread_cond_t m_cond;
+
+};
+
+
+} // namespace OmGtk
+
+#endif // LOADERRTHREAD_H
diff --git a/src/progs/gtk/Makefile.am b/src/progs/gtk/Makefile.am
new file mode 100644
index 00000000..c92ae2b2
--- /dev/null
+++ b/src/progs/gtk/Makefile.am
@@ -0,0 +1,98 @@
+if BUILD_GTK_CLIENT
+
+EXTRA_DIST = om_gtk.gladep
+
+sharefilesdir = $(pkgdatadir)
+dist_sharefiles_DATA = om_gtk.glade om-icon.png
+
+AM_CXXFLAGS = -DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -I$(top_srcdir)/src/common -I$(top_srcdir)/src/clients -DPKGDATADIR=\"$(pkgdatadir)\" @GTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@ @GNOMECANVASMM_CFLAGS@ @LOSC_CFLAGS@ @LASH_CFLAGS@ @FLOWCANVAS_CFLAGS@
+om_gtk_LDADD = @GTKMM_LIBS@ @LIBGLADEMM_LIBS@ @GNOMECANVASMM_LIBS@ @LOSC_LIBS@ @LASH_LIBS@ @FLOWCANVAS_LIBS@ ../libomclient.a
+om_gtk_DEPENDENCIES = ../libomclient.a
+
+
+bin_PROGRAMS = om_gtk
+om_gtk_SOURCES = \
+ cmdline.h \
+ cmdline.c \
+ main.cpp \
+ singletons.cpp \
+ ConnectWindow.h \
+ ConnectWindow.cpp \
+ App.h \
+ App.cpp \
+ Store.h \
+ Store.cpp \
+ Configuration.h \
+ Configuration.cpp \
+ GladeFactory.h \
+ GladeFactory.cpp \
+ Controller.h \
+ Controller.cpp \
+ GtkObjectController.h \
+ GtkObjectController.cpp \
+ PatchController.h \
+ PatchController.cpp \
+ NodeController.h \
+ NodeController.cpp \
+ PortController.h \
+ PortController.cpp \
+ DSSIController.h \
+ DSSIController.cpp \
+ LoadPluginWindow.h \
+ LoadPluginWindow.cpp \
+ LoadPatchWindow.h \
+ LoadPatchWindow.cpp \
+ MessagesWindow.h \
+ MessagesWindow.cpp \
+ LoadSubpatchWindow.h \
+ LoadSubpatchWindow.cpp \
+ ControlInterface.h \
+ ControlInterface.cpp \
+ NodeControlWindow.h \
+ NodeControlWindow.cpp \
+ ControlPanel.h \
+ ControlPanel.cpp \
+ ControlGroups.h \
+ ControlGroups.cpp \
+ PatchView.h \
+ PatchView.cpp \
+ PatchWindow.h \
+ PatchWindow.cpp \
+ BreadCrumb.h \
+ OmFlowCanvas.h \
+ OmFlowCanvas.cpp \
+ ../../common/types.h \
+ ../../common/Path.h \
+ OmModule.h \
+ OmModule.cpp \
+ DSSIModule.h \
+ DSSIModule.cpp \
+ SubpatchModule.h \
+ SubpatchModule.cpp \
+ OmPort.h \
+ OmPort.cpp \
+ NewSubpatchWindow.h \
+ NewSubpatchWindow.cpp \
+ ConfigWindow.h \
+ ConfigWindow.cpp \
+ PatchDescriptionWindow.h \
+ PatchDescriptionWindow.cpp \
+ Loader.h \
+ Loader.cpp \
+ RenameWindow.h \
+ RenameWindow.cpp \
+ NodePropertiesWindow.h \
+ NodePropertiesWindow.cpp \
+ PatchTreeWindow.h \
+ PatchTreeWindow.cpp
+
+#GtkClientInterface.h
+#GtkClientInterface.cpp
+
+
+if WITH_LASH
+om_gtk_SOURCES += LashController.h LashController.cpp
+endif
+
+
+endif # WITH_GTK_CLIENT
diff --git a/src/progs/gtk/MessagesWindow.cpp b/src/progs/gtk/MessagesWindow.cpp
new file mode 100644
index 00000000..dfdc3750
--- /dev/null
+++ b/src/progs/gtk/MessagesWindow.cpp
@@ -0,0 +1,64 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MessagesWindow.h"
+#include <string>
+
+namespace OmGtk {
+using std::string;
+
+
+MessagesWindow::MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("messages_textview", m_textview);
+ glade_xml->get_widget("messages_clear_button", m_clear_button);
+ glade_xml->get_widget("messages_close_button", m_close_button);
+
+ m_clear_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::clear_clicked));
+ m_close_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::close_clicked));
+}
+
+
+void
+MessagesWindow::post(const string& msg)
+{
+ Glib::RefPtr<Gtk::TextBuffer> text_buf = m_textview->get_buffer();
+ text_buf->insert(text_buf->end(), msg);
+ text_buf->insert(text_buf->end(), "\n");
+
+ if (!m_clear_button->is_sensitive())
+ m_clear_button->set_sensitive(true);
+}
+
+
+void
+MessagesWindow::close_clicked()
+{
+ hide();
+}
+
+
+void
+MessagesWindow::clear_clicked()
+{
+ Glib::RefPtr<Gtk::TextBuffer> text_buf = m_textview->get_buffer();
+ text_buf->erase(text_buf->begin(), text_buf->end());
+ m_clear_button->set_sensitive(false);
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/MessagesWindow.h b/src/progs/gtk/MessagesWindow.h
new file mode 100644
index 00000000..5f5b86e6
--- /dev/null
+++ b/src/progs/gtk/MessagesWindow.h
@@ -0,0 +1,55 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MESSAGESWINDOW_H
+#define MESSAGESWINDOW_H
+
+#include <string>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+using std::string;
+
+
+namespace OmGtk {
+
+
+/** Messages Window.
+ *
+ * Loaded by libglade as a derived object.
+ * This is shown when errors occur (ie during patch loading).
+ *
+ * \ingroup OmGtk
+ */
+class MessagesWindow : public Gtk::Window
+{
+public:
+ MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void post(const string& str);
+
+private:
+ void clear_clicked();
+ void close_clicked();
+
+ Gtk::TextView* m_textview;
+ Gtk::Button* m_clear_button;
+ Gtk::Button* m_close_button;
+};
+
+
+} // namespace OmGtk
+
+#endif // MESSAGESWINDOW_H
diff --git a/src/progs/gtk/NewSubpatchWindow.cpp b/src/progs/gtk/NewSubpatchWindow.cpp
new file mode 100644
index 00000000..dcc2cd2d
--- /dev/null
+++ b/src/progs/gtk/NewSubpatchWindow.cpp
@@ -0,0 +1,110 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NewSubpatchWindow.h"
+#include "PatchController.h"
+#include "NodeModel.h"
+#include "Controller.h"
+#include "PatchModel.h"
+
+namespace OmGtk {
+
+
+NewSubpatchWindow::NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ m_new_module_x(0),
+ m_new_module_y(0)
+{
+ xml->get_widget("new_subpatch_name_entry", m_name_entry);
+ xml->get_widget("new_subpatch_message_label", m_message_label);
+ xml->get_widget("new_subpatch_polyphony_spinbutton", m_poly_spinbutton);
+ xml->get_widget("new_subpatch_ok_button", m_ok_button);
+ xml->get_widget("new_subpatch_cancel_button", m_cancel_button);
+
+ m_name_entry->signal_changed().connect(sigc::mem_fun(this, &NewSubpatchWindow::name_changed));
+ m_ok_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::ok_clicked));
+ m_cancel_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::cancel_clicked));
+
+ m_ok_button->property_sensitive() = false;
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+NewSubpatchWindow::patch_controller(PatchController* pc)
+{
+ m_patch_controller = pc;
+}
+
+
+/** Called every time the user types into the name input box.
+ * Used to display warning messages, and enable/disable the OK button.
+ */
+void
+NewSubpatchWindow::name_changed()
+{
+ string name = m_name_entry->get_text();
+ if (name.find("/") != string::npos) {
+ m_message_label->set_text("Name may not contain '/'");
+ m_ok_button->property_sensitive() = false;
+ } else if (m_patch_controller->patch_model()->get_node(name) != NULL) {
+ m_message_label->set_text("An object already exists with that name.");
+ m_ok_button->property_sensitive() = false;
+ } else if (name.length() == 0) {
+ m_message_label->set_text("");
+ m_ok_button->property_sensitive() = false;
+ } else {
+ m_message_label->set_text("");
+ m_ok_button->property_sensitive() = true;
+ }
+}
+
+
+void
+NewSubpatchWindow::ok_clicked()
+{
+ PatchModel* pm = new PatchModel(
+ m_patch_controller->model()->base_path() + m_name_entry->get_text(),
+ m_poly_spinbutton->get_value_as_int());
+
+ if (m_new_module_x == 0 && m_new_module_y == 0) {
+ m_patch_controller->get_new_module_location(
+ m_new_module_x, m_new_module_y);
+ }
+ pm->set_parent(m_patch_controller->patch_model());
+ pm->x(m_new_module_x);
+ pm->y(m_new_module_y);
+ char temp_buf[16];
+ snprintf(temp_buf, 16, "%d", m_new_module_x);
+ pm->set_metadata("module-x", temp_buf);
+ snprintf(temp_buf, 16, "%d", m_new_module_y);
+ pm->set_metadata("module-y", temp_buf);
+ Controller::instance().create_patch_from_model(pm);
+ hide();
+}
+
+
+void
+NewSubpatchWindow::cancel_clicked()
+{
+ hide();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/NewSubpatchWindow.h b/src/progs/gtk/NewSubpatchWindow.h
new file mode 100644
index 00000000..8421ea08
--- /dev/null
+++ b/src/progs/gtk/NewSubpatchWindow.h
@@ -0,0 +1,67 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef NEWSUBPATCHWINDOW_H
+#define NEWSUBPATCHWINDOW_H
+
+#include "PluginModel.h"
+#include <libglademm/xml.h>
+#include <gtkmm.h>
+
+
+namespace OmGtk {
+
+class PatchController;
+
+
+/** 'New Subpatch' window.
+ *
+ * Loaded by glade as a derived object.
+ *
+ * \ingroup OmGtk
+ */
+class NewSubpatchWindow : public Gtk::Window
+{
+public:
+ NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml);
+
+ void patch_controller(PatchController* pc);
+
+ void set_next_module_location(int x, int y)
+ { m_new_module_x = x; m_new_module_y = y; }
+
+private:
+ void name_changed();
+ void ok_clicked();
+ void cancel_clicked();
+
+ PatchController* m_patch_controller;
+
+ int m_new_module_x;
+ int m_new_module_y;
+
+ Gtk::Entry* m_name_entry;
+ Gtk::Label* m_message_label;
+ Gtk::SpinButton* m_poly_spinbutton;
+ Gtk::Button* m_ok_button;
+ Gtk::Button* m_cancel_button;
+};
+
+
+} // namespace OmGtk
+
+#endif // NEWSUBPATCHWINDOW_H
diff --git a/src/progs/gtk/NodeControlWindow.cpp b/src/progs/gtk/NodeControlWindow.cpp
new file mode 100644
index 00000000..e40af67f
--- /dev/null
+++ b/src/progs/gtk/NodeControlWindow.cpp
@@ -0,0 +1,127 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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 alongCont
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NodeControlWindow.h"
+#include "GladeFactory.h"
+#include "NodeController.h"
+#include "ControlGroups.h"
+#include "PatchWindow.h"
+#include <iostream>
+#include <cmath>
+using std::cerr; using std::cout; using std::endl;
+
+namespace OmGtk {
+
+
+/** Create a node control window and load a new ControlPanel for it.
+ */
+NodeControlWindow::NodeControlWindow(NodeController* node, size_t poly)
+: m_node(node),
+ m_position_stored(false),
+ m_x(0), m_y(0)
+{
+ assert(m_node != NULL);
+
+ property_resizable() = true;
+ set_border_width(5);
+
+ set_title(m_node->path() + " Controls");
+
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("warehouse_win");
+ xml->get_widget_derived("control_panel_vbox", m_control_panel);
+ m_control_panel->reparent(*this);
+
+ m_control_panel->init(m_node, poly);
+
+ show_all_children();
+ resize();
+
+ // FIXME: not working
+ set_icon_from_file(string(PKGDATADIR) + "/om-icon.png");
+
+ m_callback_enabled = true;
+}
+
+
+/** Create a node control window and with an existing ControlPanel.
+ */
+NodeControlWindow::NodeControlWindow(NodeController* node, ControlPanel* panel)
+: m_node(node),
+ m_control_panel(panel)
+{
+ assert(m_node != NULL);
+
+ property_resizable() = true;
+ set_border_width(5);
+
+ set_title(m_node->path() + " Controls");
+
+ m_control_panel->reparent(*this);
+
+ show_all_children();
+ resize();
+
+ m_callback_enabled = true;
+}
+
+
+NodeControlWindow::~NodeControlWindow()
+{
+ delete m_control_panel;
+}
+
+
+void
+NodeControlWindow::resize()
+{
+ pair<int,int> controls_size = m_control_panel->ideal_size();
+ /*int width = 400;
+ int height = controls_size.second
+ + ((m_node->polyphonic()) ? 4 : 40);*/
+ int width = controls_size.first;
+ int height = controls_size.second;
+
+ if (height > property_screen().get_value()->get_height() - 64)
+ height = property_screen().get_value()->get_height() - 64;
+ if (width > property_screen().get_value()->get_width() - 64)
+ width = property_screen().get_value()->get_width() - 64;
+
+ //cerr << "Resizing to: " << width << " x " << height << endl;
+
+ Gtk::Window::resize(width, height);
+}
+
+
+void
+NodeControlWindow::on_show()
+{
+ if (m_position_stored)
+ move(m_x, m_y);
+
+ Gtk::Window::on_show();
+}
+
+
+void
+NodeControlWindow::on_hide()
+{
+ m_position_stored = true;
+ get_position(m_x, m_y);
+ Gtk::Window::on_hide();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/NodeControlWindow.h b/src/progs/gtk/NodeControlWindow.h
new file mode 100644
index 00000000..a9224de9
--- /dev/null
+++ b/src/progs/gtk/NodeControlWindow.h
@@ -0,0 +1,69 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef NODECONTROLWINDOW_H
+#define NODECONTROLWINDOW_H
+
+#include <vector>
+#include <string>
+#include <gtkmm.h>
+#include <libglademm.h>
+#include <sigc++/sigc++.h>
+#include "ControlPanel.h"
+using std::string; using std::vector;
+
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class ControlGroup;
+class NodeController;
+
+
+/** Window with controls (sliders) for all control-rate ports on a Node.
+ *
+ * \ingroup OmGtk
+ */
+class NodeControlWindow : public Gtk::Window
+{
+public:
+ NodeControlWindow(NodeController* node, size_t poly);
+ NodeControlWindow(NodeController* node, ControlPanel* panel);
+ virtual ~NodeControlWindow();
+
+ ControlPanel* control_panel() const { return m_control_panel; }
+
+ void resize();
+
+protected:
+ void on_show();
+ void on_hide();
+
+private:
+ NodeController* m_node;
+ ControlPanel* m_control_panel;
+ bool m_callback_enabled;
+
+ bool m_position_stored;
+ int m_x;
+ int m_y;
+};
+
+
+} // namespace OmGtk
+
+#endif // NODECONTROLWINDOW_H
diff --git a/src/progs/gtk/NodeController.cpp b/src/progs/gtk/NodeController.cpp
new file mode 100644
index 00000000..57756877
--- /dev/null
+++ b/src/progs/gtk/NodeController.cpp
@@ -0,0 +1,408 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NodeController.h"
+#include <iostream>
+#include <gtkmm.h>
+#include "OmModule.h"
+#include "NodeModel.h"
+#include "PortModel.h"
+#include "PortController.h"
+#include "GtkObjectController.h"
+#include "NodeControlWindow.h"
+#include "OmModule.h"
+#include "PatchController.h"
+#include "OmFlowCanvas.h"
+#include "RenameWindow.h"
+#include "GladeFactory.h"
+#include "Controller.h"
+#include "PatchWindow.h"
+#include "PatchModel.h"
+#include "NodePropertiesWindow.h"
+#include "Store.h"
+using std::cerr; using std::endl;
+
+namespace OmGtk {
+
+
+NodeController::NodeController(NodeModel* model)
+: GtkObjectController(model),
+ m_controls_menuitem(NULL),
+ m_module(NULL),
+ m_control_window(NULL),
+ m_properties_window(NULL),
+ m_bridge_port(NULL)
+{
+ assert(model->controller() == NULL);
+ model->set_controller(this);
+
+ // Create port controllers
+ for (list<PortModel*>::const_iterator i = node_model()->ports().begin();
+ i != node_model()->ports().end(); ++i) {
+ assert((*i)->controller() == NULL);
+ assert((*i)->parent() == model);
+ PortController* const pc = new PortController(*i);
+ assert((*i)->controller() == pc); // PortController() does this
+ }
+
+ // Build menu
+
+ Gtk::Menu::MenuList& items = m_menu.items();
+
+ Gtk::Menu_Helpers::MenuElem controls_elem
+ = Gtk::Menu_Helpers::MenuElem("Controls",
+ sigc::mem_fun(this, &NodeController::show_control_window));
+ m_controls_menuitem = controls_elem.get_child();
+ items.push_back(controls_elem);
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Properties",
+ sigc::mem_fun(this, &NodeController::show_properties_window)));
+ items.push_back(Gtk::Menu_Helpers::SeparatorElem());
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Rename...",
+ sigc::mem_fun(this, &NodeController::show_rename_window)));
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Clone",
+ sigc::mem_fun(this, &NodeController::on_menu_clone)));
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Disconnect All",
+ sigc::mem_fun(this, &NodeController::on_menu_disconnect_all)));
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Destroy",
+ sigc::mem_fun(this, &NodeController::on_menu_destroy)));
+
+ m_controls_menuitem->property_sensitive() = false;
+
+ if (node_model()->plugin()->type() == PluginModel::Internal
+ && node_model()->plugin()->plug_label() == "midi_control_in") {
+ Gtk::Menu::MenuList& items = m_menu.items();
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Learn",
+ sigc::mem_fun(this, &NodeController::on_menu_learn)));
+ }
+}
+
+
+NodeController::~NodeController()
+{
+}
+
+
+void
+NodeController::create_module(OmFlowCanvas* canvas)
+{
+ //cerr << "Creating node module " << m_model->path() << endl;
+
+ // If this is a DSSI plugin, DSSIController should be doing this
+ assert(node_model()->plugin()->type() != PluginModel::DSSI);
+ assert(canvas != NULL);
+ assert(m_module == NULL);
+
+ m_module = new OmModule(canvas, this);
+
+ create_all_ports();
+
+ m_module->move_to(node_model()->x(), node_model()->y());
+}
+
+
+void
+NodeController::add_to_store()
+{
+ // Add self
+ Store::instance().add_object(this);
+
+ // Add ports
+ for (list<PortModel*>::const_iterator i = node_model()->ports().begin();
+ i != node_model()->ports().end(); ++i) {
+ //GtkObjectController* const pc = (GtkObjectController*)((*i)->controller());
+ assert((*i)->controller() != NULL);
+ ((GtkObjectController*)((*i)->controller()))->add_to_store();
+ }
+}
+
+
+void
+NodeController::remove_from_store()
+{
+ // Remove ports
+ for (list<PortModel*>::const_iterator i = node_model()->ports().begin();
+ i != node_model()->ports().end(); ++i) {
+ GtkObjectController* const pc = (GtkObjectController*)((*i)->controller());
+ assert(pc != NULL);
+ pc->remove_from_store();
+ }
+
+ // Remove self
+ Store::instance().remove_object(this);
+}
+
+
+void
+NodeController::set_path(const Path& new_path)
+{
+ remove_from_store();
+
+ // Rename ports
+ for (list<PortModel*>::const_iterator i = node_model()->ports().begin();
+ i != node_model()->ports().end(); ++i) {
+ GtkObjectController* const pc = (GtkObjectController*)((*i)->controller());
+ assert(pc != NULL);
+ pc->set_path(m_model->path().base_path() + pc->model()->name());
+ }
+
+ // Handle bridge port, if this node represents one
+ if (m_bridge_port != NULL)
+ m_bridge_port->set_path(new_path);
+
+ if (m_module != NULL)
+ m_module->canvas()->rename_module(node_model()->path().name(), new_path.name());
+
+ GtkObjectController::set_path(new_path);
+
+ add_to_store();
+}
+
+
+void
+NodeController::destroy()
+{
+ PatchController* pc = ((PatchController*)m_model->parent()->controller());
+ assert(pc != NULL);
+
+ remove_from_store();
+ pc->remove_node(m_model->path().name());
+
+ if (m_bridge_port != NULL)
+ m_bridge_port->destroy();
+ m_bridge_port = NULL;
+
+ //if (m_module != NULL)
+ // delete m_module;
+}
+
+
+void
+NodeController::metadata_update(const string& key, const string& value)
+{
+ //cout << "[NodeController] Metadata update: " << m_model->path() << endl;
+
+ if (m_module != NULL) {
+ if (key == "module-x") {
+ float x = atof(value.c_str());
+ //if (x > 0 && x < m_canvas->width())
+ m_module->move_to(x, m_module->property_y().get_value());
+ } else if (key == "module-y") {
+ float y = atof(value.c_str());
+ //if (y > 0 && y < m_canvas->height())
+ m_module->move_to(m_module->property_x().get_value(), y);
+ }
+ }
+
+ if (m_bridge_port != NULL)
+ m_bridge_port->metadata_update(key, value);
+ GtkObjectController::metadata_update(key, value);
+}
+
+
+void
+NodeController::add_port(PortModel* pm)
+{
+ assert(pm->parent() == NULL);
+
+ cout << "[NodeController] Adding port " << pm->path() << endl;
+
+ node_model()->add_port(pm);
+ PortController* pc = new PortController(pm);
+ assert(pm->controller() == pc);
+ pc->add_to_store();
+
+ if (m_module != NULL) {
+ pc->create_port(m_module);
+ m_module->resize();
+
+ // Enable "Controls" menu item on module
+ if (has_control_inputs())
+ enable_controls_menuitem();
+ }
+
+ if (m_control_window != NULL) {
+ assert(m_control_window->control_panel() != NULL);
+ m_control_window->control_panel()->add_port(pc);
+ m_control_window->resize();
+ }
+}
+
+
+void
+NodeController::show_control_window()
+{
+ size_t poly = 1;
+ if (node_model()->polyphonic())
+ poly = node_model()->parent_patch()->poly();
+
+ if (m_control_window == NULL)
+ m_control_window = new NodeControlWindow(this, poly);
+
+ if (m_control_window->control_panel()->num_controls() > 0)
+ m_control_window->present();
+}
+
+
+void
+NodeController::on_menu_destroy()
+{
+ Controller::instance().destroy(node_model()->path());
+}
+
+
+void
+NodeController::show_rename_window()
+{
+ assert(node_model()->parent() != NULL);
+
+ // FIXME: will this be magically cleaned up?
+ RenameWindow* win = NULL;
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("rename_win");
+ xml->get_widget_derived("rename_win", win);
+
+ PatchController* parent = ((PatchController*)node_model()->parent()->controller());
+ assert(parent != NULL);
+
+ if (parent->window() != NULL)
+ win->set_transient_for(*parent->window());
+
+ win->set_object(this);
+ win->show();
+}
+
+void
+NodeController::on_menu_clone()
+{
+ assert(node_model() != NULL);
+ //assert(m_parent != NULL);
+ //assert(m_parent->model() != NULL);
+
+ string clone_name = node_model()->name();
+ int i = 2; // postfix number (ie oldname_2)
+
+ // Check if name already has a number postfix
+ if (clone_name.find_last_of("_") != string::npos) {
+ string name_postfix = clone_name.substr(clone_name.find_last_of("_")+1);
+ clone_name = clone_name.substr(0, clone_name.find_last_of("_"));
+ if (sscanf(name_postfix.c_str(), "%d", &i))
+ ++i;
+ }
+
+ char clone_postfix[4];
+ for ( ; i < 100; ++i) {
+ snprintf(clone_postfix, 4, "_%d", i);
+ if (node_model()->parent_patch()->get_node(clone_name + clone_postfix) == NULL)
+ break;
+ }
+
+ clone_name = clone_name + clone_postfix;
+
+ const string path = node_model()->parent_patch()->base_path() + clone_name;
+ NodeModel* nm = new NodeModel(path);
+ nm->plugin(node_model()->plugin());
+ nm->polyphonic(node_model()->polyphonic());
+ nm->x(node_model()->x() + 20);
+ nm->y(node_model()->y() + 20);
+ Controller::instance().create_node_from_model(nm);
+}
+
+
+void
+NodeController::on_menu_learn()
+{
+ Controller::instance().midi_learn(node_model()->path());
+}
+
+void
+NodeController::on_menu_disconnect_all()
+{
+ Controller::instance().disconnect_all(node_model()->path());
+}
+
+
+void
+NodeController::show_properties_window()
+{
+ PatchController* parent = ((PatchController*)node_model()->parent()->controller());
+ assert(parent != NULL);
+
+ if (m_properties_window == NULL) {
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("node_properties_win");
+ xml->get_widget_derived("node_properties_win", m_properties_window);
+ }
+ assert(m_properties_window != NULL);
+ assert(parent != NULL);
+ m_properties_window->set_node(node_model());
+ if (parent->window() != NULL)
+ m_properties_window->set_transient_for(*parent->window());
+ m_properties_window->show();
+}
+
+
+/** Create all (visual) ports and add them to module (and resize it).
+ */
+void
+NodeController::create_all_ports()
+{
+ assert(m_module != NULL);
+
+ PortController* pc = NULL;
+ for (PortModelList::const_iterator i = node_model()->ports().begin();
+ i != node_model()->ports().end(); ++i) {
+ pc = dynamic_cast<PortController*>((*i)->controller());
+ assert(pc != NULL);
+ pc->create_port(m_module);
+ }
+
+ m_module->resize();
+
+ if (has_control_inputs())
+ enable_controls_menuitem();
+}
+
+
+bool
+NodeController::has_control_inputs()
+{
+ for (PortModelList::const_iterator i = node_model()->ports().begin();
+ i != node_model()->ports().end(); ++i)
+ if ((*i)->is_input() && (*i)->is_control())
+ return true;
+
+ return false;
+}
+
+
+void
+NodeController::enable_controls_menuitem()
+{
+ m_controls_menuitem->property_sensitive() = true;
+}
+
+
+void
+NodeController::disable_controls_menuitem()
+{
+ m_controls_menuitem->property_sensitive() = false;
+
+ if (m_control_window != NULL)
+ m_control_window->hide();
+}
+
+
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/NodeController.h b/src/progs/gtk/NodeController.h
new file mode 100644
index 00000000..ed2a6d59
--- /dev/null
+++ b/src/progs/gtk/NodeController.h
@@ -0,0 +1,114 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef NODECONTROLLER_H
+#define NODECONTROLLER_H
+
+#include <string>
+#include <gtkmm.h>
+#include "util/Path.h"
+#include "GtkObjectController.h"
+
+using std::string;
+using Om::Path;
+using namespace LibOmClient;
+
+namespace LibOmClient {
+ class MetadataModel;
+ class NodeModel;
+ class PortModel;
+}
+
+namespace OmGtk {
+
+class Controller;
+class OmModule;
+class NodeControlWindow;
+class NodePropertiesWindow;
+class PortController;
+class OmFlowCanvas;
+
+/** Controller for a Node.
+ *
+ * \ingroup OmGtk
+ */
+class NodeController : public GtkObjectController
+{
+public:
+ NodeController(NodeModel* model);
+ virtual ~NodeController();
+
+ virtual void destroy();
+
+ virtual void add_to_store();
+ virtual void remove_from_store();
+
+ virtual void metadata_update(const string& key, const string& value);
+
+ virtual void create_module(OmFlowCanvas* canvas);
+
+ void set_path(const Path& new_path);
+
+ virtual void add_port(PortModel* pm);
+ virtual void remove_port(const Path& path, bool resize_module) {}
+
+ virtual void program_add(int bank, int program, const string& name) {}
+ virtual void program_remove(int bank, int program) {}
+
+ OmModule* module() { return m_module; }
+
+ void bridge_port(PortController* port) { m_bridge_port = port; }
+ PortController* as_port() { return m_bridge_port; }
+
+ NodeModel* node_model() { return (NodeModel*)m_model; }
+
+ NodeControlWindow* control_window() { return m_control_window; }
+ void control_window(NodeControlWindow* cw) { m_control_window = cw; }
+
+ virtual void show_control_window();
+ void show_rename_window();
+ void show_properties_window();
+
+ bool has_control_inputs();
+
+ virtual void show_menu(GdkEventButton* event)
+ { m_menu.popup(event->button, event->time); }
+
+ virtual void enable_controls_menuitem();
+ virtual void disable_controls_menuitem();
+
+protected:
+ void create_all_ports();
+
+ void on_menu_destroy();
+ void on_menu_clone();
+ void on_menu_learn();
+ void on_menu_disconnect_all();
+
+ Gtk::Menu m_menu;
+ Glib::RefPtr<Gtk::MenuItem> m_controls_menuitem;
+
+ OmModule* m_module; ///< View (module on a patch canvas)
+
+ NodeControlWindow* m_control_window;
+ NodePropertiesWindow* m_properties_window;
+ PortController* m_bridge_port;
+};
+
+
+} // namespace OmGtk
+
+#endif // NODECONTROLLER_H
diff --git a/src/progs/gtk/NodePropertiesWindow.cpp b/src/progs/gtk/NodePropertiesWindow.cpp
new file mode 100644
index 00000000..048f2513
--- /dev/null
+++ b/src/progs/gtk/NodePropertiesWindow.cpp
@@ -0,0 +1,62 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NodePropertiesWindow.h"
+#include <cassert>
+#include <string>
+#include "NodeModel.h"
+#include "PluginModel.h"
+
+namespace OmGtk {
+using std::string;
+
+
+NodePropertiesWindow::NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("node_properties_path_label", m_node_path_label);
+ glade_xml->get_widget("node_properties_polyphonic_checkbutton", m_node_polyphonic_toggle);
+ glade_xml->get_widget("node_properties_plugin_type_label", m_plugin_type_label);
+ glade_xml->get_widget("node_properties_plugin_uri_label", m_plugin_uri_label);
+ glade_xml->get_widget("node_properties_plugin_name_label", m_plugin_name_label);
+}
+
+
+/** Set the node this window is associated with.
+ * This function MUST be called before using this object in any way.
+ */
+void
+NodePropertiesWindow::set_node(NodeModel* node_model)
+{
+ assert(node_model != NULL);
+
+ m_node_model = node_model;
+
+ set_title(node_model->path() + " Properties");
+
+ m_node_path_label->set_text(node_model->path());
+ m_node_polyphonic_toggle->set_active(node_model->polyphonic());
+
+ const PluginModel* const pm = node_model->plugin();
+
+ m_plugin_type_label->set_text(pm->type_string());
+ m_plugin_uri_label->set_text(pm->uri());
+ m_plugin_name_label->set_text(pm->name());
+}
+
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/NodePropertiesWindow.h b/src/progs/gtk/NodePropertiesWindow.h
new file mode 100644
index 00000000..efff01bb
--- /dev/null
+++ b/src/progs/gtk/NodePropertiesWindow.h
@@ -0,0 +1,55 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef NODEPROPERTIESWINDOW_H
+#define NODEPROPERTIESWINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm.h>
+
+namespace LibOmClient { class NodeModel; }
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+
+/** 'New Patch' Window.
+ *
+ * Loaded by libglade as a derived object.
+ *
+ * \ingroup OmGtk
+ */
+class NodePropertiesWindow : public Gtk::Window
+{
+public:
+ NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void set_node(NodeModel* node_model);
+
+private:
+
+ NodeModel* m_node_model;
+
+ Gtk::Label* m_node_path_label;
+ Gtk::CheckButton* m_node_polyphonic_toggle;
+ Gtk::Label* m_plugin_type_label;
+ Gtk::Label* m_plugin_uri_label;
+ Gtk::Label* m_plugin_name_label;
+};
+
+} // namespace OmGtk
+
+#endif // NODEPROPERTIESWINDOW_H
diff --git a/src/progs/gtk/OmFlowCanvas.cpp b/src/progs/gtk/OmFlowCanvas.cpp
new file mode 100644
index 00000000..a7e41ea2
--- /dev/null
+++ b/src/progs/gtk/OmFlowCanvas.cpp
@@ -0,0 +1,166 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OmFlowCanvas.h"
+#include <cassert>
+#include <flowcanvas/FlowCanvas.h>
+#include "Controller.h"
+#include "PatchController.h"
+#include "PatchModel.h"
+#include "PatchWindow.h"
+#include "LoadPluginWindow.h"
+#include "LoadSubpatchWindow.h"
+#include "NewSubpatchWindow.h"
+#include "OmPort.h"
+#include "NodeModel.h"
+#include "OmModule.h"
+
+
+namespace OmGtk {
+
+
+OmFlowCanvas::OmFlowCanvas(PatchController* controller, int width, int height)
+: FlowCanvas(width, height),
+ m_patch_controller(controller),
+ m_last_click_x(0),
+ m_last_click_y(0)
+{
+ assert(controller != NULL);
+
+ Gtk::Menu::MenuList& items = m_menu.items();
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Load Plugin...",
+ sigc::mem_fun(this, &OmFlowCanvas::menu_load_plugin)));
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Load Subpatch...",
+ sigc::mem_fun(this, &OmFlowCanvas::menu_load_subpatch)));
+ items.push_back(Gtk::Menu_Helpers::MenuElem("New Subpatch...",
+ sigc::mem_fun(this, &OmFlowCanvas::menu_create_subpatch)));
+}
+
+
+void
+OmFlowCanvas::connect(const Port* src_port, const Port* dst_port)
+{
+ assert(src_port != NULL);
+ assert(dst_port != NULL);
+
+ const OmPort* const src = static_cast<const OmPort* const>(src_port);
+ const OmPort* const dst = static_cast<const OmPort* const>(dst_port);
+
+ // Midi binding/learn shortcut
+ if (src->model()->type() == PortModel::MIDI &&
+ dst->model()->type() == PortModel::CONTROL)
+ {
+ // FIXME: leaks?
+ NodeModel* nm = new NodeModel(m_patch_controller->model()->base_path()
+ + src->name() + "-" + dst->name());
+ PluginModel* pm = new PluginModel(PluginModel::Internal, "", "midi_control_in", "");
+ nm->plugin(pm);
+ nm->x(dst->module()->property_x() - dst->module()->width() - 20);
+ nm->y(dst->module()->property_y());
+ Controller::instance().create_node_from_model(nm);
+ Controller::instance().connect(src->model()->path(), nm->path() + "/MIDI In");
+ Controller::instance().connect(nm->path() + "/Out (CR)", dst->model()->path());
+ Controller::instance().midi_learn(nm->path());
+
+ // Set control node range to port's user range
+
+ Controller::instance().set_port_value_queued(nm->path().base_path() + "Min",
+ atof(dst->model()->get_metadata("user-min").c_str()));
+ Controller::instance().set_port_value_queued(nm->path().base_path() + "Max",
+ atof(dst->model()->get_metadata("user-max").c_str()));
+ } else {
+ Controller::instance().connect(src->model()->path(),
+ dst->model()->path());
+ }
+}
+
+
+void
+OmFlowCanvas::disconnect(const Port* src_port, const Port* dst_port)
+{
+ assert(src_port != NULL);
+ assert(dst_port != NULL);
+
+ Controller::instance().disconnect(((OmPort*)src_port)->model()->path(),
+ ((OmPort*)dst_port)->model()->path());
+}
+
+
+bool
+OmFlowCanvas::canvas_event(GdkEvent* event)
+{
+ assert(event != NULL);
+
+ switch (event->type) {
+
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 3) {
+ m_last_click_x = (int)event->button.x;
+ m_last_click_y = (int)event->button.y;
+ show_menu(event);
+ }
+ break;
+
+ /*case GDK_KEY_PRESS:
+ if (event->key.keyval == GDK_Delete)
+ destroy_selected();
+ break;
+ */
+
+ default:
+ break;
+ }
+
+ return FlowCanvas::canvas_event(event);
+}
+
+
+void
+OmFlowCanvas::destroy_selected()
+{
+ for (list<Module*>::iterator m = m_selected_modules.begin(); m != m_selected_modules.end(); ++m)
+ Controller::instance().destroy(((OmModule*)(*m))->node()->path());
+}
+
+
+void
+OmFlowCanvas::menu_load_plugin()
+{
+ m_patch_controller->window()->load_plugin_window()->set_next_module_location(
+ m_last_click_x, m_last_click_y);
+ m_patch_controller->window()->load_plugin_window()->show();
+}
+
+
+void
+OmFlowCanvas::menu_load_subpatch()
+{
+ m_patch_controller->window()->load_subpatch_window()->set_next_module_location(
+ m_last_click_x, m_last_click_y);
+ m_patch_controller->window()->load_subpatch_window()->show();
+}
+
+
+void
+OmFlowCanvas::menu_create_subpatch()
+{
+ m_patch_controller->window()->new_subpatch_window()->set_next_module_location(
+ m_last_click_x, m_last_click_y);
+ m_patch_controller->window()->new_subpatch_window()->show();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/OmFlowCanvas.h b/src/progs/gtk/OmFlowCanvas.h
new file mode 100644
index 00000000..2a553f5b
--- /dev/null
+++ b/src/progs/gtk/OmFlowCanvas.h
@@ -0,0 +1,70 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OMPATCHBAYAREA_H
+#define OMPATCHBAYAREA_H
+
+#include <string>
+#include <flowcanvas/FlowCanvas.h>
+
+
+using std::string;
+using namespace LibFlowCanvas;
+
+using LibFlowCanvas::Port;
+
+namespace OmGtk {
+
+class OmModule;
+class PatchController;
+
+/** Patch canvas widget.
+ *
+ * \ingroup OmGtk
+ */
+class OmFlowCanvas : public LibFlowCanvas::FlowCanvas
+{
+public:
+ OmFlowCanvas(PatchController* controller, int width, int height);
+
+ OmModule* find_module(const string& name)
+ { return (OmModule*)FlowCanvas::find_module(name); }
+
+ void connect(const Port* src_port, const Port* dst_port);
+ void disconnect(const Port* src_port, const Port* dst_port);
+
+ bool canvas_event(GdkEvent* event);
+ void destroy_selected();
+
+ void show_menu(GdkEvent* event)
+ { m_menu.popup(event->button.button, event->button.time); }
+
+ void menu_load_plugin();
+ void menu_load_subpatch();
+ void menu_create_subpatch();
+
+private:
+ PatchController* m_patch_controller;
+ int m_last_click_x;
+ int m_last_click_y;
+
+ Gtk::Menu m_menu;
+};
+
+
+} // namespace OmGtk
+
+#endif // OMPATCHBAYAREA_H
diff --git a/src/progs/gtk/OmModule.cpp b/src/progs/gtk/OmModule.cpp
new file mode 100644
index 00000000..3a31b7f3
--- /dev/null
+++ b/src/progs/gtk/OmModule.cpp
@@ -0,0 +1,85 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OmModule.h"
+#include <cassert>
+#include "Controller.h"
+#include "OmFlowCanvas.h"
+#include "PatchModel.h"
+#include "NodeModel.h"
+#include "OmPort.h"
+#include "GladeFactory.h"
+#include "RenameWindow.h"
+#include "PatchController.h"
+#include "PatchWindow.h"
+
+namespace OmGtk {
+
+
+OmModule::OmModule(OmFlowCanvas* canvas, NodeController* node)
+: LibFlowCanvas::Module(canvas, node->node_model()->name(),
+ node->node_model()->x(), node->node_model()->y()),
+ m_node(node)
+{
+ assert(m_node != NULL);
+
+ /*if (node_model()->polyphonic() && node_model()->parent() != NULL
+ && node_model()->parent_patch()->poly() > 1) {
+ border_width(2.0);
+ }*/
+ if (node->node_model()->polyphonic()) {
+ border_width(2.0);
+ }
+}
+
+
+void
+OmModule::show_control_window()
+{
+ node()->show_control_window();
+}
+
+
+void
+OmModule::store_location()
+{
+ if (m_node->node_model()->x() == 0 || m_node->node_model()->y() == 0)
+ return;
+
+ char temp_buf[16];
+
+ m_node->node_model()->x(property_x());
+ snprintf(temp_buf, 16, "%f", m_node->node_model()->x());
+ m_node->node_model()->set_metadata("module-x", temp_buf); // just in case?
+ Controller::instance().set_metadata(m_node->node_model()->path(), "module-x", temp_buf);
+
+ m_node->node_model()->y(property_y());
+ snprintf(temp_buf, 16, "%f", m_node->node_model()->y());
+ m_node->node_model()->set_metadata("module-y", temp_buf); // just in case?
+ Controller::instance().set_metadata(m_node->node_model()->path(), "module-y", temp_buf);
+}
+
+
+void
+OmModule::move_to(double x, double y)
+{
+ Module::move_to(x, y);
+ m_node->node_model()->x(x);
+ m_node->node_model()->y(y);
+ //store_location();
+}
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/OmModule.h b/src/progs/gtk/OmModule.h
new file mode 100644
index 00000000..ebb36fe6
--- /dev/null
+++ b/src/progs/gtk/OmModule.h
@@ -0,0 +1,77 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef OMMODULE_H
+#define OMMODULE_H
+
+#include <string>
+#include <libgnomecanvasmm.h>
+#include <flowcanvas/Module.h>
+#include "NodeController.h"
+using std::string;
+
+namespace LibOmClient {
+class PortModel;
+class NodeModel;
+class ControlModel;
+}
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class PatchController;
+class OmFlowCanvas;
+class OmPort;
+
+
+/** A module in a patch.
+ *
+ * This base class is extended for various types of modules - SubpatchModule,
+ * DSSIModule, etc.
+ *
+ * \ingroup OmGtk
+ */
+class OmModule : public LibFlowCanvas::Module
+{
+public:
+ OmModule(OmFlowCanvas* canvas, NodeController* node);
+ virtual ~OmModule() {}
+
+ virtual OmPort* port(const string& port_name) {
+ return (OmPort*)Module::port(port_name);
+ }
+
+ virtual void store_location();
+ void move_to(double x, double y);
+
+ void on_right_click(GdkEventButton* event) { m_node->show_menu(event); }
+
+ void show_control_window();
+
+ NodeController* node() const { return m_node; }
+
+protected:
+ virtual void on_double_click(GdkEventButton* ev) { show_control_window(); }
+ virtual void on_middle_click(GdkEventButton* ev) { show_control_window(); }
+
+ NodeController* m_node;
+};
+
+
+} // namespace OmGtk
+
+#endif // OMMODULE_H
diff --git a/src/progs/gtk/OmPort.cpp b/src/progs/gtk/OmPort.cpp
new file mode 100644
index 00000000..b43b8294
--- /dev/null
+++ b/src/progs/gtk/OmPort.cpp
@@ -0,0 +1,57 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OmPort.h"
+#include <cassert>
+#include <iostream>
+#include "PortModel.h"
+#include "OmModule.h"
+#include "ControlModel.h"
+#include "Configuration.h"
+#include "App.h"
+using std::cerr; using std::endl;
+
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+OmPort::OmPort(OmModule* module, PortModel* pm)
+: Port(module, pm->name(), pm->is_input(), App::instance().configuration()->get_port_color(pm)),
+ m_port_model(pm)
+{
+ assert(module != NULL);
+ assert(m_port_model != NULL);
+}
+
+#if 0
+void
+OmPort::set_name(const string& n)
+{
+ cerr << "********** OmPort::set_name broken **********************" << endl;
+
+ /* FIXME: move to PortController
+ string new_path = OmPath::parent(m_port_model->path()) +"/"+ n;
+
+ for (list<ControlPanel*>::iterator i = m_control_panels.begin(); i != m_control_panels.end(); ++i)
+ (*i)->rename_port(m_port_model->path(), new_path);
+
+ Port::set_name(n);
+ m_port_model->path(new_path);
+ */
+}
+#endif
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/OmPort.h b/src/progs/gtk/OmPort.h
new file mode 100644
index 00000000..69a867d5
--- /dev/null
+++ b/src/progs/gtk/OmPort.h
@@ -0,0 +1,59 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OMPORT_H
+#define OMPORT_H
+
+#include <cassert>
+#include <string>
+#include <flowcanvas/Port.h>
+
+namespace LibOmClient { class PortModel; }
+using namespace LibOmClient;
+using namespace LibFlowCanvas;
+using std::string; using std::list;
+
+namespace OmGtk {
+
+class FlowCanvas;
+class PatchController;
+class PatchWindow;
+class OmModule;
+
+
+/** A Port on an OmModule.
+ *
+ * \ingroup OmGtk
+ */
+class OmPort : public LibFlowCanvas::Port
+{
+public:
+ OmPort(OmModule* module, PortModel* pm);
+
+ virtual ~OmPort() {}
+
+ //void set_name(const string& n);
+
+ PortModel* model() const { return m_port_model; }
+
+private:
+ PortModel* m_port_model;
+};
+
+
+} // namespace OmGtk
+
+#endif // OMPORT_H
diff --git a/src/progs/gtk/PatchController.cpp b/src/progs/gtk/PatchController.cpp
new file mode 100644
index 00000000..83ba62d9
--- /dev/null
+++ b/src/progs/gtk/PatchController.cpp
@@ -0,0 +1,685 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "PatchController.h"
+#include <cassert>
+#include <cstdlib>
+#include "GladeFactory.h"
+#include "Configuration.h"
+#include "util/Path.h"
+#include "ControlPanel.h"
+#include "ConnectionModel.h"
+#include "OmFlowCanvas.h"
+#include "PatchView.h"
+#include "flowcanvas/Module.h"
+#include "PluginModel.h"
+#include "Controller.h"
+#include "SubpatchModule.h"
+#include "DSSIModule.h"
+#include "PatchWindow.h"
+#include "NodeModel.h"
+#include "OmModule.h"
+#include "OmPort.h"
+#include "ControlModel.h"
+#include "NodeControlWindow.h"
+#include "NodeController.h"
+#include "PortController.h"
+#include "App.h"
+#include "PatchTreeWindow.h"
+#include "DSSIController.h"
+#include "PatchModel.h"
+#include "Store.h"
+
+using std::cerr; using std::cout; using std::endl;
+using Om::Path;
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+
+PatchController::PatchController(PatchModel* model)
+: NodeController(model),
+ m_window(NULL),
+ m_patch_view(NULL),
+ m_module_x(0),
+ m_module_y(0)
+{
+ assert(model->path().length() > 0);
+ assert(model->parent() == NULL);
+ assert(model->controller() == this); // NodeController() does this
+
+ if (model->path() != "/") {
+ PatchController* parent = Store::instance().patch(model->path().parent());
+ if (parent != NULL)
+ parent->add_subpatch(this);
+ else
+ cerr << "[PatchController] " << path() << " ERROR: Parent not found." << endl;
+ }
+}
+
+
+PatchController::~PatchController()
+{
+ if (m_patch_view != NULL) {
+ claim_patch_view();
+ m_patch_view->hide();
+ delete m_patch_view;
+ m_patch_view = NULL;
+ }
+
+ if (m_control_window != NULL) {
+ m_control_window->hide();
+ delete m_control_window;
+ m_control_window = NULL;
+ }
+
+ if (m_window != NULL) {
+ m_window->hide();
+ delete m_window;
+ m_window = NULL;
+ }
+}
+
+
+void
+PatchController::add_to_store()
+{
+ Store::instance().add_object(this);
+}
+
+
+void
+PatchController::remove_from_store()
+{
+ Store::instance().remove_object(this);
+}
+
+
+void
+PatchController::clear()
+{
+ // Destroy model
+ // Destroying nodes removes models from patch model, which invalidates any
+ // iterator to nodes, so avoid the iterator problem by doing it this way:
+ const NodeModelMap& nodes = patch_model()->nodes();
+ size_t remaining = nodes.size();
+
+ while (remaining > 0) {
+ NodeController* const nc = (NodeController*)(*nodes.begin()).second->controller();
+ assert(nc != NULL);
+ nc->destroy();
+ assert(nodes.size() == remaining - 1);
+ --remaining;
+ }
+ assert(nodes.empty());
+
+ patch_model()->clear();
+
+ if (m_patch_view != NULL) {
+ assert(m_patch_view->canvas() != NULL);
+ m_patch_view->canvas()->destroy();
+ }
+}
+
+
+void
+PatchController::destroy()
+{
+ // Destroying nodes removes models from patch model, which invalidates any
+ // iterator to nodes, so avoid the iterator problem by doing it this way:
+ const NodeModelMap& nodes = patch_model()->nodes();
+ size_t remaining = nodes.size();
+
+ while (remaining > 0) {
+ NodeController* const nc = (NodeController*)
+ (*nodes.begin()).second->controller();
+ assert(nc != NULL);
+ nc->destroy();
+ assert(nodes.size() == remaining - 1);
+ --remaining;
+ }
+ assert(nodes.empty());
+
+ //App::instance().remove_patch(this);
+ App::instance().patch_tree()->remove_patch(path());
+
+ // Delete all children models
+ //patch_model()->clear();
+
+ // Remove self from object store
+ Store::instance().remove_object(this);
+
+ // Delete self from parent (this will delete model)
+ if (patch_model()->parent() != NULL) {
+ PatchController* const parent = (PatchController*)patch_model()->parent()->controller();
+ assert(parent != NULL);
+ parent->remove_node(name());
+ } else {
+ delete m_model;
+ }
+}
+
+
+void
+PatchController::metadata_update(const string& key, const string& value)
+{
+ NodeController::metadata_update(key, value);
+
+ if (key == "filename")
+ patch_model()->filename(value);
+}
+
+
+void
+PatchController::set_path(const Path& new_path)
+{
+ assert(m_model != NULL);
+ Path old_path = path();
+
+ // Rename nodes
+ for (NodeModelMap::const_iterator i = patch_model()->nodes().begin();
+ i != patch_model()->nodes().end(); ++i) {
+ const NodeModel* const nm = (*i).second;
+ assert(nm != NULL);
+ NodeController* const nc = ((NodeController*)nm->controller());
+ assert(nc != NULL);
+ nc->set_path(new_path.base_path() + nc->node_model()->name());
+ }
+
+#ifdef DEBUG
+ // Be sure ports were renamed by their bridge nodes
+ for (list<PortModel*>::const_iterator i = node_model()->ports().begin();
+ i != node_model()->ports().end(); ++i) {
+ GtkObjectController* const pc = (GtkObjectController*)((*i)->controller());
+ assert(pc != NULL);
+ assert(pc->path().parent()== new_path);
+ }
+#endif
+
+ App::instance().patch_tree()->patch_renamed(old_path, new_path);
+
+ if (m_window != NULL)
+ m_window->patch_renamed(new_path);
+
+ if (m_control_window != NULL)
+ m_control_window->set_title(new_path + " Controls");
+
+ if (m_module != NULL) {
+ assert(m_module->canvas() != NULL);
+ m_module->canvas()->rename_module(old_path.name(), new_path.name());
+ assert(m_module->name() == new_path.name());
+ }
+
+ PatchController* parent = dynamic_cast<PatchController*>(
+ patch_model()->parent()->controller());
+
+ if (parent != NULL && parent->window() != NULL)
+ parent->window()->node_renamed(old_path, new_path);
+
+ remove_from_store();
+ GtkObjectController::set_path(new_path);
+ add_to_store();
+
+ if (old_path.name() != new_path.name())
+ parent->patch_model()->rename_node(old_path, new_path);
+}
+
+
+void
+PatchController::enable()
+{
+ if (m_patch_view != NULL)
+ m_patch_view->enabled(true);
+
+ patch_model()->enabled(true);
+
+ App::instance().patch_tree()->patch_enabled(m_model->path());
+}
+
+
+void
+PatchController::disable()
+{
+ if (m_patch_view != NULL)
+ m_patch_view->enabled(false);
+
+ patch_model()->enabled(false);
+
+ App::instance().patch_tree()->patch_disabled(m_model->path());
+}
+
+
+void
+PatchController::create_module(OmFlowCanvas* canvas)
+{
+ //cerr << "Creating patch module " << m_model->path() << endl;
+
+ assert(canvas != NULL);
+ assert(m_module == NULL);
+
+ m_module = new SubpatchModule(canvas, this);
+
+
+ m_menu.remove(m_menu.items()[4]);
+
+ // Add navigation menu items
+ Gtk::Menu::MenuList& items = m_menu.items();
+ items.push_front(Gtk::Menu_Helpers::SeparatorElem());
+ items.push_front(Gtk::Menu_Helpers::MenuElem("Browse to Patch",
+ sigc::mem_fun((SubpatchModule*)m_module, &SubpatchModule::browse_to_patch)));
+ items.push_front(Gtk::Menu_Helpers::MenuElem("Open Patch in New Window",
+ sigc::mem_fun(this, &PatchController::show_patch_window)));
+
+ create_all_ports();
+
+ m_module->move_to(node_model()->x(), node_model()->y());
+ m_module->store_location();
+}
+
+
+void
+PatchController::create_view()
+{
+ assert(m_patch_view == NULL);
+
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference();
+
+ xml->get_widget_derived("patch_view_vbox", m_patch_view);
+ assert(m_patch_view != NULL);
+ m_patch_view->patch_controller(this);
+ assert(m_patch_view->canvas() != NULL);
+
+ // Create modules for nodes
+ for (NodeModelMap::const_iterator i = patch_model()->nodes().begin();
+ i != patch_model()->nodes().end(); ++i) {
+
+ NodeModel* const nm = (*i).second;
+
+ string val = nm->get_metadata("module-x");
+ if (val != "")
+ nm->x(atof(val.c_str()));
+ val = nm->get_metadata("module-y");
+ if (val != "")
+ nm->y(atof(val.c_str()));
+
+ /* Set sane default coordinates if not set already yet */
+ if (nm->x() == 0.0f && nm->y() == 0.0f) {
+ int x, y;
+ get_new_module_location(x, y);
+ nm->x(x);
+ nm->y(y);
+ }
+
+ NodeController* nc = ((NodeController*)nm->controller());
+ assert(nc != NULL);
+ if (nc->module() == NULL);
+ nc->create_module(m_patch_view->canvas());
+ assert(nc->module() != NULL);
+ m_patch_view->canvas()->add_module(nc->module());
+ }
+
+ // Create connections
+ for (list<ConnectionModel*>::const_iterator i = patch_model()->connections().begin();
+ i != patch_model()->connections().end(); ++i) {
+ create_connection(*i);
+ }
+
+ // Set run checkbox
+ m_patch_view->enabled(patch_model()->enabled());
+}
+
+
+/** Create a connection in the view (canvas).
+ */
+void
+PatchController::create_connection(const ConnectionModel* cm)
+{
+ m_patch_view->canvas()->add_connection(
+ cm->src_port_path().parent().name(),
+ cm->src_port_path().name(),
+ cm->dst_port_path().parent().name(),
+ cm->dst_port_path().name());
+
+ // Disable control slider from destination node control window
+
+ PortController* p = Store::instance().port(cm->dst_port_path());
+ assert(p != NULL);
+
+ if (p->control_panel() != NULL)
+ p->control_panel()->disable_port(p->path());
+ // FIXME: don't use canvas as a model (search object store)
+ /*OmModule* m = (OmModule*)m_patch_view->canvas()->find_module(
+ cm->dst_port_path().parent().name());
+
+ if (m != NULL) {
+ OmPort* p = m->port(cm->dst_port_path().name());
+ if (p != NULL && p->connections().size() == 1) {
+ p->model()->connected(true);
+ assert(m->node_model()->controller() != NULL);
+ NodeControlWindow* cw = (((NodeController*)
+ m->node_model()->controller())->control_window());
+ if (cw != NULL)
+ cw->control_panel()->disable_port(cm->dst_port_path());
+ }
+ }*/
+}
+
+
+/** Add a subpatch to this patch.
+ */
+void
+PatchController::add_subpatch(PatchController* patch)
+{
+ assert(patch != NULL);
+ assert(patch->patch_model() != NULL);
+ assert(patch->patch_model()->parent() == NULL);
+
+ /*if (pm->x() == 0 && pm->y() == 0) {
+ int x, y;
+ parent_pc->get_new_module_location(x, y);
+ pm->x(x);
+ pm->y(y);
+ }*/
+
+ patch_model()->add_node(patch->patch_model());
+
+ if (m_patch_view != NULL) {
+ patch->create_module(m_patch_view->canvas());
+ m_patch_view->canvas()->add_module(patch->module());
+ patch->module()->resize();
+ }
+}
+
+
+void
+PatchController::add_node(NodeModel* nm)
+{
+ assert(nm != NULL);
+ assert(nm->parent() == NULL);
+ assert(nm->path().parent() == m_model->path());
+
+ if (patch_model()->get_node(nm->name()) != NULL) {
+ // Node already exists, ignore
+ delete nm;
+ } else {
+ // FIXME: Should PatchController really be responsible for creating these?
+ NodeController* nc = NULL;
+
+ if (nm->plugin()->type() == PluginModel::DSSI)
+ nc = new DSSIController(nm);
+ else
+ nc = new NodeController(nm);
+
+ assert(nc != NULL);
+ assert(nm->controller() == nc);
+
+ // Check if this is a bridge node
+ PortModel* const pm = patch_model()->get_port(nm->path().name());
+ if (pm != NULL) {
+ cerr << "Bridge node." << endl;
+ PortController* pc = ((PortController*)pm->controller());
+ assert(pc != NULL);
+ nc->bridge_port(pc);
+ }
+
+ nc->add_to_store();
+ patch_model()->add_node(nm);
+
+ if (m_patch_view != NULL) {
+
+ int x, y;
+ get_new_module_location(x, y);
+ nm->x(x);
+ nm->y(y);
+
+ // Set zoom to 1.0 so module isn't messed up (Death to GnomeCanvas)
+ float old_zoom = m_patch_view->canvas()->zoom();
+ if (old_zoom != 1.0)
+ m_patch_view->canvas()->zoom(1.0);
+
+ if (nc->module() == NULL)
+ nc->create_module(m_patch_view->canvas());
+ assert(nc->module() != NULL);
+ m_patch_view->canvas()->add_module(nc->module());
+ nc->module()->resize();
+
+ // Reset zoom
+ if (old_zoom != 1.0) {
+ m_patch_view->canvas()->zoom(old_zoom);
+ nc->module()->zoom(old_zoom);
+ }
+ }
+ }
+}
+
+
+/** Removes a node from this patch.
+ */
+void
+PatchController::remove_node(const string& name)
+{
+ assert(name.find("/") == string::npos);
+
+ // Update breadcrumbs if necessary
+ if (m_window != NULL)
+ m_window->node_removed(name);
+
+ if (m_patch_view != NULL) {
+ assert(m_patch_view->canvas() != NULL);
+ m_patch_view->canvas()->remove_module(name);
+ }
+
+ patch_model()->remove_node(name);
+}
+
+
+/** Add a port to this patch.
+ *
+ * Will add a port to the subpatch module and the control window, if they
+ * exist.
+ */
+void
+PatchController::add_port(PortModel* pm)
+{
+ assert(pm != NULL);
+ assert(pm->parent() == NULL);
+
+ //cerr << "[PatchController] Adding port " << pm->path() << endl;
+
+ if (patch_model()->get_port(pm->name()) != NULL) {
+ cerr << "[PatchController] Ignoring duplicate port "
+ << pm->path() << endl;
+ delete pm;
+ return;
+ }
+
+ node_model()->add_port(pm);
+ PortController* pc = new PortController(pm);
+
+ // Handle bridge ports/nodes (this is uglier than it should be)
+ NodeController* nc = Store::instance().node(pm->path());
+ if (nc != NULL)
+ nc->bridge_port(pc);
+
+ if (m_module != NULL) {
+ pc->create_port(m_module);
+ m_module->resize();
+ }
+
+ if (m_control_window != NULL) {
+ assert(m_control_window->control_panel() != NULL);
+ m_control_window->control_panel()->add_port(pc);
+ m_control_window->resize();
+ }
+
+ // Enable "Controls" menuitem on module and patch window, if necessary
+ if (has_control_inputs())
+ enable_controls_menuitem();
+}
+
+
+/** Removes a port from this patch
+ */
+void
+PatchController::remove_port(const Path& path, bool resize_module)
+{
+ assert(path.parent() == m_model->path());
+
+ //cerr << "[PatchController] Removing port " << path << endl;
+
+ /* FIXME
+ if (m_control_panel != NULL) {
+ m_control_panel->remove_port(path);
+ if (m_control_window != NULL) {
+ assert(m_control_window->control_panel() == m_control_panel);
+ m_control_window->resize();
+ }
+ }*/
+
+ // Remove port on module
+ if (m_module != NULL) {
+ assert(m_module->port(path.name()) != NULL);
+ m_module->remove_port(path.name(), resize_module);
+ assert(m_module->port(path.name()) == NULL);
+ }
+
+ patch_model()->remove_port(path);
+ assert(patch_model()->get_port(path.name()) == NULL);
+
+ // Disable "Controls" menuitem on module and patch window, if necessary
+ if (!has_control_inputs())
+ disable_controls_menuitem();
+}
+
+
+void
+PatchController::connection(ConnectionModel* const cm)
+{
+ assert(cm != NULL);
+
+ patch_model()->add_connection(cm);
+
+ if (m_patch_view != NULL)
+ create_connection(cm);
+}
+
+
+
+void
+PatchController::disconnection(const Path& src_port_path, const Path& dst_port_path)
+{
+ const string& src_node_name = src_port_path.parent().name();
+ const string& src_port_name = src_port_path.name();
+ const string& dst_node_name = dst_port_path.parent().name();
+ const string& dst_port_name = dst_port_path.name();
+
+ if (m_patch_view != NULL)
+ m_patch_view->canvas()->remove_connection(
+ src_node_name, src_port_name, dst_node_name, dst_port_name);
+
+ patch_model()->remove_connection(src_port_path, dst_port_path);
+
+ // Enable control slider in destination node control window
+ PortController* p = Store::instance().port(dst_port_path);
+ assert(p != NULL);
+
+ if (p->control_panel() != NULL)
+ p->control_panel()->enable_port(p->path());
+}
+
+
+/** Try to guess a suitable location for a new module.
+ */
+void
+PatchController::get_new_module_location(int& x, int& y)
+{
+ assert(m_patch_view != NULL);
+ assert(m_patch_view->canvas() != NULL);
+ m_patch_view->canvas()->get_scroll_offsets(x, y);
+ x += 20;
+ y += 20;
+}
+
+
+void
+PatchController::show_patch_window()
+{
+ if (m_window == NULL) {
+ Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference();
+
+ xml->get_widget_derived("patch_win", m_window);
+ assert(m_window != NULL);
+
+ if (m_patch_view == NULL)
+ create_view();
+
+ m_window->patch_controller(this);
+ }
+
+ assert(m_window != NULL);
+ m_window->present();
+}
+
+
+/** Become the parent of the patch view.
+ *
+ * Steals the view away from whatever window is currently showing it.
+ */
+void
+PatchController::claim_patch_view()
+{
+ assert(m_patch_view != NULL);
+
+ m_patch_view->hide();
+ m_patch_view->reparent(m_patch_view_bin);
+}
+
+
+void
+PatchController::show_control_window()
+{
+ assert(patch_model() != NULL);
+
+ if (m_control_window == NULL)
+ m_control_window = new NodeControlWindow(this, patch_model()->poly());
+
+ if (m_control_window->control_panel()->num_controls() > 0)
+ m_control_window->present();
+}
+
+
+void
+PatchController::enable_controls_menuitem()
+{
+ if (m_window != NULL)
+ m_window->menu_view_control_window()->property_sensitive() = true;
+
+ NodeController::enable_controls_menuitem();
+}
+
+
+void
+PatchController::disable_controls_menuitem()
+{
+ if (m_window != NULL)
+ m_window->menu_view_control_window()->property_sensitive() = false;
+
+ NodeController::disable_controls_menuitem();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/PatchController.h b/src/progs/gtk/PatchController.h
new file mode 100644
index 00000000..de4af5e4
--- /dev/null
+++ b/src/progs/gtk/PatchController.h
@@ -0,0 +1,127 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PATCHCONTROLLER_H
+#define PATCHCONTROLLER_H
+
+#include <string>
+#include <gtkmm.h>
+#include "NodeController.h"
+
+namespace LibOmClient {
+class PatchModel;
+class NodeModel;
+class PortModel;
+class ControlModel;
+class ConnectionModel;
+}
+
+using std::string;
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class PatchWindow;
+class SubpatchModule;
+class Controller;
+class OmFlowCanvas;
+class NodeControlWindow;
+class ControlPanel;
+class PatchView;
+class NodeController;
+
+
+/** Controller for a patch.
+ *
+ * A patch is different from a port or node because there are two
+ * representations in the Gtk client - the window and the module in the parent
+ * patch (if applicable). So, this is a master class that contains both of
+ * those representations, and acts as the recipient of all patch related
+ * events (being the controller).
+ *
+ * \ingroup OmGtk
+ */
+class PatchController : public NodeController
+{
+public:
+ PatchController(PatchModel* model);
+ virtual ~PatchController();
+
+ virtual void add_to_store();
+ virtual void remove_from_store();
+
+ virtual void destroy();
+
+ virtual void metadata_update(const string& key, const string& value);
+
+ void add_node(NodeModel* nm);
+ void remove_node(const string& name);
+
+ virtual void add_port(PortModel* pm);
+ virtual void remove_port(const Path& path, bool resize_module);
+
+ void connection(ConnectionModel* const cm);
+ void disconnection(const Path& src_port_path, const Path& dst_port_path);
+ void clear();
+
+ void add_subpatch(PatchController* patch);
+
+ void get_new_module_location(int& x, int& y);
+
+ void show_control_window();
+ void show_patch_window();
+
+ void claim_patch_view();
+
+ void create_module(OmFlowCanvas* canvas);
+ void create_view();
+
+ PatchView* view() const { return m_patch_view; }
+ PatchWindow* window() const { return m_window; }
+ void window(PatchWindow* pw) { m_window = pw; }
+
+ inline string name() const { return m_model->name(); }
+ inline const string& path() const { return m_model->path(); }
+
+ void set_path(const Path& new_path);
+
+ void enable();
+ void disable();
+
+ PatchModel* patch_model() const { return (PatchModel*)m_model; }
+
+ void enable_controls_menuitem();
+ void disable_controls_menuitem();
+
+private:
+ void create_connection(const ConnectionModel* cm);
+
+ PatchWindow* m_window; ///< Window currently showing this patch
+ PatchView* m_patch_view; ///< View (canvas) of this patch
+
+ /** Invisible bin used to store patch view when not shown by a patch window */
+ Gtk::Alignment m_patch_view_bin;
+
+ // Coordinates for next added plugin (used by canvas menu)
+ // 0 means "not set", ie guess at the best location
+ int m_module_x;
+ int m_module_y;
+};
+
+
+} // namespace OmGtk
+
+#endif // PATCHCONTROLLER_H
diff --git a/src/progs/gtk/PatchDescriptionWindow.cpp b/src/progs/gtk/PatchDescriptionWindow.cpp
new file mode 100644
index 00000000..d10a9c0e
--- /dev/null
+++ b/src/progs/gtk/PatchDescriptionWindow.cpp
@@ -0,0 +1,72 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PatchDescriptionWindow.h"
+#include <string>
+#include "PatchModel.h"
+
+namespace OmGtk {
+using std::string;
+
+
+PatchDescriptionWindow::PatchDescriptionWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("description_author_entry", m_author_entry);
+ glade_xml->get_widget("description_description_textview", m_textview);
+ glade_xml->get_widget("description_cancel_button", m_cancel_button);
+ glade_xml->get_widget("description_ok_button", m_ok_button);
+
+ m_cancel_button->signal_clicked().connect(sigc::mem_fun(this, &PatchDescriptionWindow::cancel_clicked));
+ m_ok_button->signal_clicked().connect(sigc::mem_fun(this, &PatchDescriptionWindow::ok_clicked));
+}
+
+
+/** Set the patch model this description is for.
+ *
+ * This function is a "post-constructor" - it MUST be called before using
+ * the window in any way.
+ */
+void
+PatchDescriptionWindow::patch_model(PatchModel* patch_model)
+{
+ property_title() = patch_model->path() + " Properties";
+ m_patch_model = patch_model;
+ m_author_entry->set_text(m_patch_model->get_metadata("author"));
+ m_textview->get_buffer()->set_text(m_patch_model->get_metadata("description"));
+}
+
+
+void
+PatchDescriptionWindow::cancel_clicked()
+{
+ m_author_entry->set_text(m_patch_model->get_metadata("author"));
+ m_textview->get_buffer()->set_text(m_patch_model->get_metadata("description"));
+ hide();
+}
+
+
+void
+PatchDescriptionWindow::ok_clicked()
+{
+ m_patch_model->set_metadata("author", m_author_entry->get_text());
+ m_patch_model->set_metadata("description", m_textview->get_buffer()->get_text());
+ hide();
+}
+
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/PatchDescriptionWindow.h b/src/progs/gtk/PatchDescriptionWindow.h
new file mode 100644
index 00000000..7c00faab
--- /dev/null
+++ b/src/progs/gtk/PatchDescriptionWindow.h
@@ -0,0 +1,59 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PATCHDESCRIPTIONWINDOW_H
+#define PATCHDESCRIPTIONWINDOW_H
+
+#include <string>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+using std::string;
+
+namespace LibOmClient { class PatchModel; }
+using LibOmClient::PatchModel;
+
+namespace OmGtk {
+
+
+/** Patch Description Window.
+ *
+ * Loaded by libglade as a derived object.
+ *
+ * \ingroup OmGtk
+ */
+class PatchDescriptionWindow : public Gtk::Window
+{
+public:
+ PatchDescriptionWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void patch_model(PatchModel* patch_model);
+
+ void cancel_clicked();
+ void ok_clicked();
+
+private:
+ PatchModel* m_patch_model;
+
+ Gtk::Entry* m_author_entry;
+ Gtk::TextView* m_textview;
+ Gtk::Button* m_cancel_button;
+ Gtk::Button* m_ok_button;
+};
+
+
+} // namespace OmGtk
+
+#endif // PATCHDESCRIPTIONWINDOW_H
diff --git a/src/progs/gtk/PatchTreeWindow.cpp b/src/progs/gtk/PatchTreeWindow.cpp
new file mode 100644
index 00000000..065f4d6a
--- /dev/null
+++ b/src/progs/gtk/PatchTreeWindow.cpp
@@ -0,0 +1,252 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PatchTreeWindow.h"
+#include "Controller.h"
+#include "PatchController.h"
+#include "PatchWindow.h"
+#include "SubpatchModule.h"
+#include "PatchModel.h"
+#include "util/Path.h"
+
+using Om::Path;
+
+namespace OmGtk {
+
+
+PatchTreeWindow::PatchTreeWindow(BaseObjectType* cobject,
+ const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ m_enable_signal(true)
+{
+ xml->get_widget_derived("patches_treeview", m_patches_treeview);
+
+ m_patch_treestore = Gtk::TreeStore::create(m_patch_tree_columns);
+ m_patches_treeview->set_window(this);
+ m_patches_treeview->set_model(m_patch_treestore);
+ Gtk::TreeViewColumn* name_col = Gtk::manage(new Gtk::TreeViewColumn(
+ "Patch", m_patch_tree_columns.name_col));
+ Gtk::TreeViewColumn* enabled_col = Gtk::manage(new Gtk::TreeViewColumn(
+ "Run", m_patch_tree_columns.enabled_col));
+ name_col->set_resizable(true);
+ name_col->set_expand(true);
+
+ m_patches_treeview->append_column(*name_col);
+ m_patches_treeview->append_column(*enabled_col);
+ Gtk::CellRendererToggle* enabled_renderer = dynamic_cast<Gtk::CellRendererToggle*>(
+ m_patches_treeview->get_column_cell_renderer(1));
+ enabled_renderer->property_activatable() = true;
+
+ m_patch_tree_selection = m_patches_treeview->get_selection();
+
+ //m_patch_tree_selection->signal_changed().connect(
+ // sigc::mem_fun(this, &PatchTreeWindow::event_patch_selected));
+ m_patches_treeview->signal_row_activated().connect(
+ sigc::mem_fun(this, &PatchTreeWindow::event_patch_activated));
+ enabled_renderer->signal_toggled().connect(
+ sigc::mem_fun(this, &PatchTreeWindow::event_patch_enabled_toggled));
+
+ m_patches_treeview->columns_autosize();
+}
+
+
+void
+PatchTreeWindow::add_patch(PatchController* pc)
+{
+ PatchModel* const pm = pc->patch_model();
+
+ if (pm->parent() == NULL) {
+ Gtk::TreeModel::iterator iter = m_patch_treestore->append();
+ Gtk::TreeModel::Row row = *iter;
+ if (pm->path() == "/") {
+ string root_name = Controller::instance().engine_url();
+ // Hack off trailing '/' if it's there (ugly)
+ //if (root_name.substr(root_name.length()-1,1) == "/")
+ // root_name = root_name.substr(0, root_name.length()-1);
+ //root_name.append(":/");
+ row[m_patch_tree_columns.name_col] = root_name;
+ } else {
+ row[m_patch_tree_columns.name_col] = pm->path().name();
+ }
+ row[m_patch_tree_columns.enabled_col] = false;
+ row[m_patch_tree_columns.patch_controller_col] = pc;
+ m_patches_treeview->expand_row(m_patch_treestore->get_path(iter), true);
+ } else {
+ Gtk::TreeModel::Children children = m_patch_treestore->children();
+ Gtk::TreeModel::iterator c = find_patch(children, pm->parent()->path());
+
+ if (c != children.end()) {
+ Gtk::TreeModel::iterator iter = m_patch_treestore->append(c->children());
+ Gtk::TreeModel::Row row = *iter;
+ row[m_patch_tree_columns.name_col] = pm->path().name();
+ row[m_patch_tree_columns.enabled_col] = false;
+ row[m_patch_tree_columns.patch_controller_col] = pc;
+ m_patches_treeview->expand_row(m_patch_treestore->get_path(iter), true);
+ }
+ }
+}
+
+
+void
+PatchTreeWindow::remove_patch(const Path& path)
+{
+ Gtk::TreeModel::iterator i = find_patch(m_patch_treestore->children(), path);
+ if (i != m_patch_treestore->children().end())
+ m_patch_treestore->erase(i);
+}
+
+
+Gtk::TreeModel::iterator
+PatchTreeWindow::find_patch(Gtk::TreeModel::Children root, const Path& path)
+{
+ PatchController* pc = NULL;
+
+ for (Gtk::TreeModel::iterator c = root.begin(); c != root.end(); ++c) {
+ pc = (*c)[m_patch_tree_columns.patch_controller_col];
+ if (pc->model()->path() == path) {
+ return c;
+ } else if ((*c)->children().size() > 0) {
+ Gtk::TreeModel::iterator ret = find_patch(c->children(), path);
+ if (ret != c->children().end())
+ return ret;
+ }
+ }
+ return root.end();
+}
+
+/*
+void
+PatchTreeWindow::event_patch_selected()
+{
+ Gtk::TreeModel::iterator active = m_patch_tree_selection->get_selected();
+ if (active) {
+ Gtk::TreeModel::Row row = *active;
+ PatchController* pc = row[m_patch_tree_columns.patch_controller_col];
+ }
+}
+*/
+
+
+/** Show the context menu for the selected patch in the patches treeview.
+ */
+void
+PatchTreeWindow::show_patch_menu(GdkEventButton* ev)
+{
+ Gtk::TreeModel::iterator active = m_patch_tree_selection->get_selected();
+ if (active) {
+ Gtk::TreeModel::Row row = *active;
+ PatchController* pc = row[m_patch_tree_columns.patch_controller_col];
+ if (pc != NULL)
+ pc->show_menu(ev);
+ }
+}
+
+
+void
+PatchTreeWindow::event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col)
+{
+ Gtk::TreeModel::iterator active = m_patch_treestore->get_iter(path);
+ Gtk::TreeModel::Row row = *active;
+ PatchController* pc = row[m_patch_tree_columns.patch_controller_col];
+
+ pc->show_patch_window();
+}
+
+
+void
+PatchTreeWindow::event_patch_enabled_toggled(const Glib::ustring& path_str)
+{
+ Gtk::TreeModel::Path path(path_str);
+ Gtk::TreeModel::iterator active = m_patch_treestore->get_iter(path);
+ Gtk::TreeModel::Row row = *active;
+
+ PatchController* pc = row[m_patch_tree_columns.patch_controller_col];
+ Glib::ustring patch_path = pc->model()->path();
+
+ assert(pc != NULL);
+
+ if ( ! pc->patch_model()->enabled()) {
+ if (m_enable_signal)
+ Controller::instance().enable_patch(patch_path);
+ pc->enable();
+ row[m_patch_tree_columns.enabled_col] = true;
+ } else {
+ if (m_enable_signal)
+ Controller::instance().disable_patch(patch_path);
+ pc->disable();
+ row[m_patch_tree_columns.enabled_col] = false;
+ }
+}
+
+
+void
+PatchTreeWindow::patch_enabled(const Path& path)
+{
+ m_enable_signal = false;
+
+ Gtk::TreeModel::iterator i
+ = find_patch(m_patch_treestore->children(), path);
+
+ if (i != m_patch_treestore->children().end()) {
+ Gtk::TreeModel::Row row = *i;
+ row[m_patch_tree_columns.enabled_col] = true;
+ } else {
+ cerr << "[PatchTreeWindow] Unable to find patch " << path << endl;
+ }
+
+ m_enable_signal = true;
+}
+
+
+void
+PatchTreeWindow::patch_disabled(const Path& path)
+{
+ m_enable_signal = false;
+
+ Gtk::TreeModel::iterator i
+ = find_patch(m_patch_treestore->children(), path);
+
+ if (i != m_patch_treestore->children().end()) {
+ Gtk::TreeModel::Row row = *i;
+ row[m_patch_tree_columns.enabled_col] = false;
+ } else {
+ cerr << "[PatchTreeWindow] Unable to find patch " << path << endl;
+ }
+
+ m_enable_signal = true;
+}
+
+
+void
+PatchTreeWindow::patch_renamed(const Path& old_path, const Path& new_path)
+{
+ m_enable_signal = false;
+
+ Gtk::TreeModel::iterator i
+ = find_patch(m_patch_treestore->children(), old_path);
+
+ if (i != m_patch_treestore->children().end()) {
+ Gtk::TreeModel::Row row = *i;
+ row[m_patch_tree_columns.name_col] = new_path.name();
+ } else {
+ cerr << "[PatchTreeWindow] Unable to find patch " << old_path << endl;
+ }
+
+ m_enable_signal = true;
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/PatchTreeWindow.h b/src/progs/gtk/PatchTreeWindow.h
new file mode 100644
index 00000000..3177e5e2
--- /dev/null
+++ b/src/progs/gtk/PatchTreeWindow.h
@@ -0,0 +1,105 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PATCHTREEWINDOW_H
+#define PATCHTREEWINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm.h>
+#include "util/Path.h"
+
+
+using namespace Om;
+using Om::Path;
+
+namespace OmGtk {
+
+class PatchWindow;
+class PatchController;
+class PatchTreeView;
+
+
+/** Window with a TreeView of all loaded patches.
+ *
+ * \ingroup OmGtk
+ */
+class PatchTreeWindow : public Gtk::Window
+{
+public:
+ PatchTreeWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void patch_enabled(const Path& path);
+ void patch_disabled(const Path& path);
+ void patch_renamed(const Path& old_path, const Path& new_path);
+
+ void add_patch(PatchController* pc);
+ void remove_patch(const Path& path);
+ void show_patch_menu(GdkEventButton* ev);
+
+protected:
+ //void event_patch_selected();
+ void event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col);
+ void event_patch_enabled_toggled(const Glib::ustring& path_str);
+
+ Gtk::TreeModel::iterator find_patch(Gtk::TreeModel::Children root, const Path& path);
+
+ PatchTreeView* m_patches_treeview;
+
+ struct PatchTreeModelColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ PatchTreeModelColumns()
+ { add(name_col); add(enabled_col); add(patch_controller_col); }
+
+ Gtk::TreeModelColumn<Glib::ustring> name_col;
+ Gtk::TreeModelColumn<bool> enabled_col;
+ Gtk::TreeModelColumn<PatchController*> patch_controller_col;
+ };
+
+ bool m_enable_signal;
+ PatchTreeModelColumns m_patch_tree_columns;
+ Glib::RefPtr<Gtk::TreeStore> m_patch_treestore;
+ Glib::RefPtr<Gtk::TreeSelection> m_patch_tree_selection;
+};
+
+
+/** Derived TreeView class to support context menus for patches */
+class PatchTreeView : public Gtk::TreeView
+{
+public:
+ PatchTreeView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+ : Gtk::TreeView(cobject)
+ {}
+
+ void set_window(PatchTreeWindow* win) { m_window = win; }
+
+ bool on_button_press_event(GdkEventButton* ev) {
+ bool ret = Gtk::TreeView::on_button_press_event(ev);
+
+ if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3))
+ m_window->show_patch_menu(ev);
+
+ return ret;
+ }
+
+private:
+ PatchTreeWindow* m_window;
+
+}; // struct PatchTreeView
+
+
+} // namespace OmGtk
+
+#endif // PATCHTREEWINDOW_H
diff --git a/src/progs/gtk/PatchView.cpp b/src/progs/gtk/PatchView.cpp
new file mode 100644
index 00000000..e89428e9
--- /dev/null
+++ b/src/progs/gtk/PatchView.cpp
@@ -0,0 +1,119 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PatchView.h"
+#include <iostream>
+#include <cassert>
+#include <fstream>
+#include "App.h"
+#include "OmFlowCanvas.h"
+#include "PatchController.h"
+#include "LoadPluginWindow.h"
+#include "PatchModel.h"
+#include "NewSubpatchWindow.h"
+#include "LoadSubpatchWindow.h"
+#include "NodeControlWindow.h"
+#include "PatchDescriptionWindow.h"
+#include "PatchTreeWindow.h"
+#include "Controller.h"
+
+namespace OmGtk {
+
+
+PatchView::PatchView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Box(cobject),
+ m_patch(NULL),
+ m_canvas(NULL),
+ m_enable_signal(true)
+{
+ property_visible() = false;
+
+ xml->get_widget("patch_canvas_scrolledwindow", m_canvas_scrolledwindow);
+ xml->get_widget("patch_zoom_scale", m_zoom_slider);
+ xml->get_widget("patch_polyphony_label", m_polyphony_label);
+ xml->get_widget("patch_process_checkbutton", m_process_checkbutton);
+
+ m_zoom_slider->signal_value_changed().connect( sigc::mem_fun(this, &PatchView::zoom_changed));
+ m_process_checkbutton->signal_toggled().connect(sigc::mem_fun(this, &PatchView::process_toggled));
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+PatchView::patch_controller(PatchController* pc)
+{
+ //m_patch = new PatchController(pm, controller);
+ m_patch = pc;
+
+ m_canvas = new OmFlowCanvas(pc, 1600*2, 1200*2);
+
+ m_canvas_scrolledwindow->add(*m_canvas);
+ //m_canvas->show();
+ //m_canvas_scrolledwindow->show();
+
+ char txt[4];
+ snprintf(txt, 8, "%zd", pc->patch_model()->poly());
+ m_polyphony_label->set_text(txt);
+
+ //m_description_window->patch_model(pc->model());
+}
+
+
+void
+PatchView::show_control_window()
+{
+ if (m_patch != NULL)
+ m_patch->show_control_window();
+}
+
+
+void
+PatchView::zoom_changed()
+{
+ float z = m_zoom_slider->get_value();
+ m_canvas->zoom(z);
+}
+
+
+void
+PatchView::process_toggled()
+{
+ if (!m_enable_signal)
+ return;
+
+ if (m_process_checkbutton->get_active()) {
+ Controller::instance().enable_patch(m_patch->model()->path());
+ App::instance().patch_tree()->patch_enabled(m_patch->model()->path());
+ } else {
+ Controller::instance().disable_patch(m_patch->model()->path());
+ App::instance().patch_tree()->patch_disabled(m_patch->model()->path());
+ }
+}
+
+
+void
+PatchView::enabled(bool e)
+{
+ m_enable_signal = false;
+ m_process_checkbutton->set_active(e);
+ m_enable_signal = true;
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/PatchView.h b/src/progs/gtk/PatchView.h
new file mode 100644
index 00000000..ff6aebf9
--- /dev/null
+++ b/src/progs/gtk/PatchView.h
@@ -0,0 +1,86 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PATCHVIEW_H
+#define PATCHVIEW_H
+
+#include <string>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+
+using std::string;
+
+namespace LibOmClient {
+class PatchModel;
+class NodeModel;
+class PortModel;
+class ControlModel;
+class MetadataModel;
+}
+using namespace LibOmClient;
+
+
+namespace OmGtk {
+
+class PatchController;
+class OmFlowCanvas;
+class LoadPluginWindow;
+class NewSubpatchWindow;
+class LoadSubpatchWindow;
+class NewSubpatchWindow;
+class NodeControlWindow;
+class PatchDescriptionWindow;
+class SubpatchModule;
+class OmPort;
+
+
+/** The patch specific contents of a PatchWindow (ie the canvas and whatever else).
+ *
+ * \ingroup OmGtk
+ */
+class PatchView : public Gtk::Box
+{
+public:
+ PatchView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+
+ void patch_controller(PatchController* pc);
+
+ OmFlowCanvas* canvas() const { return m_canvas; }
+ PatchController* patch_controller() const { return m_patch; }
+
+ void show_control_window();
+ void zoom_changed();
+ void process_toggled();
+
+ void enabled(bool e);
+
+private:
+ PatchController* m_patch;
+ OmFlowCanvas* m_canvas;
+
+ Gtk::ScrolledWindow* m_canvas_scrolledwindow;
+ Gtk::HScale* m_zoom_slider;
+ Gtk::Label* m_polyphony_label;
+ Gtk::CheckButton* m_process_checkbutton;
+
+ bool m_enable_signal;
+};
+
+
+} // namespace OmGtk
+
+#endif // PATCHVIEW_H
diff --git a/src/progs/gtk/PatchWindow.cpp b/src/progs/gtk/PatchWindow.cpp
new file mode 100644
index 00000000..1513d473
--- /dev/null
+++ b/src/progs/gtk/PatchWindow.cpp
@@ -0,0 +1,532 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PatchWindow.h"
+#include <iostream>
+#include <cassert>
+#include <fstream>
+#include "App.h"
+#include "PatchView.h"
+#include "OmFlowCanvas.h"
+#include "PatchController.h"
+#include "LoadPluginWindow.h"
+#include "PatchModel.h"
+#include "NewSubpatchWindow.h"
+#include "LoadPatchWindow.h"
+#include "LoadSubpatchWindow.h"
+#include "NodeControlWindow.h"
+#include "PatchDescriptionWindow.h"
+#include "ConfigWindow.h"
+#include "MessagesWindow.h"
+#include "PatchTreeWindow.h"
+#include "Controller.h"
+#include "BreadCrumb.h"
+#include "Store.h"
+
+namespace OmGtk {
+
+
+PatchWindow::PatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+: Gtk::Window(cobject),
+ m_patch(NULL),
+ m_load_plugin_window(NULL),
+ m_new_subpatch_window(NULL),
+ m_enable_signal(true),
+ m_position_stored(false),
+ m_x(0),
+ m_y(0)
+{
+ property_visible() = false;
+
+ xml->get_widget("patch_win_vbox", m_vbox);
+ xml->get_widget("patch_win_viewport", m_viewport);
+ xml->get_widget("patch_win_breadcrumb_box", m_breadcrumb_box);
+ //xml->get_widget("patch_win_status_bar", m_status_bar);
+ xml->get_widget("patch_open_menuitem", m_menu_open);
+ xml->get_widget("patch_open_into_menuitem", m_menu_open_into);
+ xml->get_widget("patch_save_menuitem", m_menu_save);
+ xml->get_widget("patch_save_as_menuitem", m_menu_save_as);
+ xml->get_widget("patch_close_menuitem", m_menu_close);
+ xml->get_widget("patch_configuration_menuitem", m_menu_configuration);
+ xml->get_widget("patch_quit_menuitem", m_menu_quit);
+ xml->get_widget("patch_quit_and_kill_menuitem", m_menu_quit_and_kill);
+ xml->get_widget("patch_view_control_window_menuitem", m_menu_view_control_window);
+ xml->get_widget("patch_description_menuitem", m_menu_view_patch_description);
+ xml->get_widget("patch_fullscreen_menuitem", m_menu_fullscreen);
+ xml->get_widget("patch_clear_menuitem", m_menu_clear);
+ xml->get_widget("patch_destroy_menuitem", m_menu_destroy_patch);
+ xml->get_widget("patch_add_plugin_menuitem", m_menu_add_plugin);
+ xml->get_widget("patch_add_new_subpatch_menuitem", m_menu_new_subpatch);
+ xml->get_widget("patch_add_subpatch_from_file_menuitem", m_menu_load_subpatch);
+ xml->get_widget("patch_view_messages_window_menuitem", m_menu_view_messages_window);
+ xml->get_widget("patch_view_patch_tree_window_menuitem", m_menu_view_patch_tree_window);
+ xml->get_widget("patch_help_about_menuitem", m_menu_help_about);
+
+ xml->get_widget_derived("load_plugin_win", m_load_plugin_window);
+ xml->get_widget_derived("new_subpatch_win", m_new_subpatch_window);
+ xml->get_widget_derived("load_patch_win", m_load_patch_window);
+ xml->get_widget_derived("load_subpatch_win", m_load_subpatch_window);
+ xml->get_widget_derived("patch_description_win", m_description_window);
+
+ //m_load_plugin_window->set_transient_for(*this);
+ m_new_subpatch_window->set_transient_for(*this);
+ m_load_patch_window->set_transient_for(*this);
+ m_load_subpatch_window->set_transient_for(*this);
+ m_description_window->set_transient_for(*this);
+
+ m_menu_view_control_window->property_sensitive() = false;
+ //m_status_bar->push(Controller::instance().engine_url());
+ //m_status_bar->pack_start(*Gtk::manage(new Gtk::Image(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_MENU)), false, false);
+
+ m_menu_open->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_open));
+ m_menu_open_into->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_open_into));
+ m_menu_save->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_save));
+ m_menu_save_as->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_save_as));
+ m_menu_close->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_close));
+ m_menu_quit->signal_activate().connect(
+ sigc::mem_fun(App::instance(), &App::quit));
+ m_menu_quit_and_kill->signal_activate().connect(
+ sigc::mem_fun(App::instance(), &App::quit_and_kill));
+ m_menu_configuration->signal_activate().connect(
+ sigc::mem_fun(App::instance().configuration_dialog(), &ConfigWindow::show));
+ m_menu_fullscreen->signal_toggled().connect(
+ sigc::mem_fun(this, &PatchWindow::event_fullscreen_toggled));
+ m_menu_view_control_window->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::show_control_window));
+ m_menu_view_patch_description->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::show_description_window));
+ m_menu_destroy_patch->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_destroy));
+ m_menu_clear->signal_activate().connect(
+ sigc::mem_fun(this, &PatchWindow::event_clear));
+ m_menu_add_plugin->signal_activate().connect(
+ sigc::mem_fun<void>(m_load_plugin_window, &LoadPluginWindow::present));
+ m_menu_new_subpatch->signal_activate().connect(
+ sigc::mem_fun<void>(m_new_subpatch_window, &NewSubpatchWindow::present));
+ m_menu_load_subpatch->signal_activate().connect(
+ sigc::mem_fun<void>(m_load_subpatch_window, &LoadSubpatchWindow::present));
+ m_menu_view_messages_window->signal_activate().connect(
+ sigc::mem_fun<void>(App::instance().messages_dialog(), &MessagesWindow::present));
+ m_menu_view_patch_tree_window->signal_activate().connect(
+ sigc::mem_fun<void>(App::instance().patch_tree(), &PatchTreeWindow::present));
+
+ // Temporary workaround for Gtkmm 2.4 (no AboutDialog)
+ if (App::instance().about_dialog() != NULL)
+ m_menu_help_about->signal_activate().connect(
+ sigc::mem_fun<void>(App::instance().about_dialog(), &Gtk::Dialog::present));
+
+ App::instance().add_patch_window(this);
+}
+
+
+PatchWindow::~PatchWindow()
+{
+ App::instance().remove_patch_window(this);
+
+ hide();
+
+ delete m_new_subpatch_window;
+ delete m_load_subpatch_window;
+}
+
+
+/** Sets the patch controller for this window and initializes everything.
+ *
+ * This function MUST be called before using the window in any way!
+ */
+void
+PatchWindow::patch_controller(PatchController* pc)
+{
+ m_enable_signal = false;
+
+ assert(pc != NULL);
+ assert(m_patch != pc);
+ assert(m_patch == NULL ||
+ pc->model()->path() != m_patch->model()->path());
+
+ PatchController* old_pc = m_patch;
+ if (old_pc != NULL) {
+ assert(old_pc->window() == NULL || old_pc->window() == this);
+ old_pc->claim_patch_view();
+ old_pc->window(NULL);
+ }
+
+ m_patch = pc;
+
+ if (pc->view() == NULL)
+ pc->create_view();
+ assert(pc->view() != NULL);
+
+ PatchView* const patch_view = pc->view();
+ assert(patch_view != NULL);
+ patch_view->reparent(*m_viewport);
+ pc->window(this);
+ show_all();
+
+ assert(m_load_plugin_window != NULL);
+ assert(m_new_subpatch_window != NULL);
+ assert(m_load_patch_window != NULL);
+ assert(m_load_subpatch_window != NULL);
+
+ m_load_patch_window->patch_controller(m_patch);
+ m_load_plugin_window->patch_controller(m_patch);
+ m_new_subpatch_window->patch_controller(m_patch);
+ m_load_subpatch_window->patch_controller(m_patch);
+
+ m_menu_view_control_window->property_sensitive() = pc->has_control_inputs();
+
+ int width, height;
+ get_size(width, height);
+ patch_view->canvas()->scroll_to(
+ ((int)patch_view->canvas()->width() - width)/2,
+ ((int)patch_view->canvas()->height() - height)/2);
+
+ set_title(m_patch->model()->path());
+
+ m_description_window->patch_model(pc->patch_model());
+
+
+ // Setup breadcrumbs box
+ // FIXME: this is filthy
+
+ // Moving to a parent patch, depress correct button
+ if (old_pc != NULL &&
+ old_pc->model()->path().substr(0, pc->model()->path().length())
+ == pc->model()->path()) {
+ for (list<BreadCrumb*>::iterator i = m_breadcrumbs.begin(); i != m_breadcrumbs.end(); ++i) {
+ if ((*i)->patch() == pc)
+ (*i)->set_active(true);
+ else if ((*i)->patch() == old_pc)
+ (*i)->set_active(false);
+ }
+
+ // Rebuild breadcrumbs from scratch (yeah, laziness..)
+ } else {
+ rebuild_breadcrumbs();
+ }
+
+ if (pc->model()->path() == "/")
+ m_menu_destroy_patch->set_sensitive(false);
+ else
+ m_menu_destroy_patch->set_sensitive(true);
+
+ assert(old_pc == NULL || old_pc->window() != this);
+ assert(m_patch == pc);
+ assert(m_patch->window() == this);
+
+ m_enable_signal = true;
+}
+
+
+/** Destroys current breadcrumbs and rebuilds from scratch.
+ *
+ * (Needs to be called when a patch is cleared to eliminate children crumbs)
+ */
+void
+PatchWindow::rebuild_breadcrumbs()
+{
+ // Empty existing breadcrumbs
+ for (list<BreadCrumb*>::iterator i = m_breadcrumbs.begin(); i != m_breadcrumbs.end(); ++i)
+ m_breadcrumb_box->remove(**i);
+ m_breadcrumbs.clear();
+
+ // Add new ones
+ string path = m_patch->path(); // To be chopped up, starting at the left
+ string but_name; // Name on breadcrumb button
+ string but_path; // Full path breadcrumb represents
+
+ // Add root
+ assert(path[0] == '/');
+ BreadCrumb* but = manage(new BreadCrumb(this, Store::instance().patch("/")));
+ m_breadcrumb_box->pack_start(*but, false, false, 1);
+ m_breadcrumbs.push_back(but);
+ path = path.substr(1); // hack off leading slash
+
+ // Add the rest
+ while (path.length() > 0) {
+ if (path.find("/") != string::npos) {
+ but_name = path.substr(0, path.find("/"));
+ but_path += string("/") + path.substr(0, path.find("/"));
+ path = path.substr(path.find("/")+1);
+ } else {
+ but_name = path;
+ but_path += string("/") + path;
+ path = "";
+ }
+ BreadCrumb* but = manage(new BreadCrumb(this, Store::instance().patch(but_path)));
+ m_breadcrumb_box->pack_start(*but, false, false, 1);
+ m_breadcrumbs.push_back(but);
+ }
+ (*m_breadcrumbs.back()).set_active(true);
+
+}
+
+
+void
+PatchWindow::breadcrumb_clicked(BreadCrumb* crumb)
+{
+ if (m_enable_signal) {
+ PatchController* const pc = crumb->patch();
+ assert(pc != NULL);
+
+ if (pc == m_patch) {
+ crumb->set_active(true);
+ } else if (pc->window() != NULL && pc->window()->is_visible()) {
+ pc->show_patch_window();
+ crumb->set_active(false);
+ } else {
+ patch_controller(pc);
+ }
+ }
+}
+
+
+void
+PatchWindow::show_control_window()
+{
+ if (m_patch != NULL)
+ m_patch->show_control_window();
+}
+
+
+void
+PatchWindow::show_description_window()
+{
+ m_description_window->show();
+}
+
+
+/** Notification a node has been removed from the PatchView this window
+ * currently contains.
+ *
+ * This is used to update the breadcrumbs in case the Node is a patch which has
+ * a button present in the breadcrumbs that needs to be removed.
+ */
+void
+PatchWindow::node_removed(const string& name)
+{
+ for (list<BreadCrumb*>::iterator i = m_breadcrumbs.begin(); i != m_breadcrumbs.end(); ++i) {
+ if ((*i)->patch()->path() == m_patch->model()->base_path() + name) {
+ for (list<BreadCrumb*>::iterator j = i; j != m_breadcrumbs.end(); ) {
+ BreadCrumb* bc = *j;
+ j = m_breadcrumbs.erase(j);
+ m_breadcrumb_box->remove(*bc);
+ }
+ break;
+ }
+ }
+}
+
+
+/** Same as @a node_removed, but for renaming.
+ */
+void
+PatchWindow::node_renamed(const string& old_path, const string& new_path)
+{
+ for (list<BreadCrumb*>::iterator i = m_breadcrumbs.begin(); i != m_breadcrumbs.end(); ++i) {
+ if ((*i)->patch()->model()->path() == old_path)
+ (*i)->path(new_path);
+ }
+}
+
+
+/** Notification the patch this window is currently showing was renamed.
+ */
+void
+PatchWindow::patch_renamed(const string& new_path)
+{
+ set_title(new_path);
+ for (list<BreadCrumb*>::iterator i = m_breadcrumbs.begin(); i != m_breadcrumbs.end(); ++i) {
+ if ((*i)->patch() == m_patch)
+ (*i)->path(new_path);
+ }
+}
+
+
+void
+PatchWindow::event_open()
+{
+ m_load_patch_window->set_replace();
+ m_load_patch_window->present();
+}
+
+void
+PatchWindow::event_open_into()
+{
+ m_load_patch_window->set_merge();
+ m_load_patch_window->present();
+}
+
+
+void
+PatchWindow::event_save()
+{
+ PatchModel* const model = m_patch->patch_model();
+
+ if (model->filename() == "")
+ event_save_as();
+ else
+ Controller::instance().save_patch(model, model->filename(), false);
+}
+
+
+void
+PatchWindow::event_save_as()
+{
+ Gtk::FileChooserDialog dialog(*this, "Save Patch", Gtk::FILE_CHOOSER_ACTION_SAVE);
+
+ Gtk::VBox* box = dialog.get_vbox();
+ Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \
+ without confirmation.");
+ box->pack_start(warning, false, false, 2);
+ Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches");
+ box->pack_start(recursive_checkbutton, false, false, 0);
+ recursive_checkbutton.show();
+
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+ // Set current folder to most sensible default
+ const string& current_filename = m_patch->patch_model()->filename();
+ if (current_filename.length() > 0)
+ dialog.set_filename(current_filename);
+ else if (App::instance().configuration()->patch_folder().length() > 0)
+ dialog.set_current_folder(App::instance().configuration()->patch_folder());
+
+ int result = dialog.run();
+ bool recursive = recursive_checkbutton.get_active();
+
+ assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE);
+
+ if (result == Gtk::RESPONSE_OK) {
+ string filename = dialog.get_filename();
+ if (filename.length() < 4 || filename.substr(filename.length()-3) != ".om")
+ filename += ".om";
+
+ bool confirm = false;
+ std::fstream fin;
+ fin.open(filename.c_str(), std::ios::in);
+ if (fin.is_open()) { // File exists
+ string msg = "File already exists! Are you sure you want to overwrite ";
+ msg += filename + "?";
+ Gtk::MessageDialog confirm_dialog(*this,
+ msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
+ if (confirm_dialog.run() == Gtk::RESPONSE_YES)
+ confirm = true;
+ else
+ confirm = false;
+ } else { // File doesn't exist
+ confirm = true;
+ }
+ fin.close();
+
+ if (confirm) {
+ Controller::instance().save_patch(m_patch->patch_model(), filename, recursive);
+ m_patch->patch_model()->filename(filename);
+ }
+ }
+ App::instance().configuration()->set_patch_folder(dialog.get_current_folder());
+}
+
+
+void
+PatchWindow::on_show()
+{
+ if (m_position_stored)
+ move(m_x, m_y);
+
+ Gtk::Window::on_show();
+}
+
+
+void
+PatchWindow::on_hide()
+{
+ m_position_stored = true;
+ get_position(m_x, m_y);
+ Gtk::Window::on_hide();
+}
+
+
+bool
+PatchWindow::on_delete_event(GdkEventAny* ev)
+{
+ event_close();
+ return true; // destroy window
+}
+
+
+bool
+PatchWindow::on_key_press_event(GdkEventKey* event)
+{
+ if (event->keyval == GDK_Delete) {
+ if (m_patch != NULL && m_patch->view() != NULL) {
+ assert(m_patch->view()->canvas() != NULL);
+ m_patch->view()->canvas()->destroy_selected();
+ }
+ return true;
+ } else {
+ return Gtk::Window::on_key_press_event(event);
+ }
+}
+
+
+void
+PatchWindow::event_close()
+{
+ if (App::instance().num_open_patch_windows() > 1) {
+ hide();
+ } else {
+ Gtk::MessageDialog d(*this, "This is the last remaining open Om patch\
+window. Closing this window will exit OmGtk (the engine will remain running).\n\n\
+Are you sure you want to quit?",
+ true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK_CANCEL, true);
+ int ret = d.run();
+ if (ret == Gtk::RESPONSE_OK)
+ App::instance().quit();
+ }
+}
+
+
+void
+PatchWindow::event_destroy()
+{
+ Controller::instance().destroy(m_patch->model()->path());
+}
+
+
+void
+PatchWindow::event_clear()
+{
+ Controller::instance().clear_patch(m_patch->model()->path());
+}
+
+void
+PatchWindow::event_fullscreen_toggled()
+{
+ if (m_menu_fullscreen->get_active())
+ fullscreen();
+ else
+ unfullscreen();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/PatchWindow.h b/src/progs/gtk/PatchWindow.h
new file mode 100644
index 00000000..54ea11ef
--- /dev/null
+++ b/src/progs/gtk/PatchWindow.h
@@ -0,0 +1,142 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PATCHWINDOW_H
+#define PATCHWINDOW_H
+
+#include <string>
+#include <list>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include <libglademm.h>
+
+using std::string; using std::list;
+
+namespace LibOmClient {
+class PatchModel;
+class NodeModel;
+class PortModel;
+class ControlModel;
+class MetadataModel;
+}
+using namespace LibOmClient;
+
+
+namespace OmGtk {
+
+class PatchController;
+class OmFlowCanvas;
+class PatchView;
+class LoadPluginWindow;
+class LoadPatchWindow;
+class NewSubpatchWindow;
+class LoadSubpatchWindow;
+class NewSubpatchWindow;
+class NodeControlWindow;
+class PatchDescriptionWindow;
+class SubpatchModule;
+class OmPort;
+class BreadCrumb;
+
+
+/** A window for a patch.
+ *
+ * \ingroup OmGtk
+ */
+class PatchWindow : public Gtk::Window
+{
+public:
+ PatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
+ ~PatchWindow();
+
+ void patch_controller(PatchController* pc);
+
+ PatchController* patch_controller() const { return m_patch; }
+ LoadPluginWindow* load_plugin_window() const { return m_load_plugin_window; }
+ LoadSubpatchWindow* load_subpatch_window() const { return m_load_subpatch_window; }
+ NewSubpatchWindow* new_subpatch_window() const { return m_new_subpatch_window; }
+
+ void show_control_window();
+ void show_description_window();
+
+ // Breadcrumb management
+ void node_removed(const string& name);
+ void node_renamed(const string& old_path, const string& new_path);
+ void patch_renamed(const string& new_path);
+ void rebuild_breadcrumbs();
+ void breadcrumb_clicked(BreadCrumb* crumb);
+
+ Gtk::MenuItem* menu_view_control_window() { return m_menu_view_control_window; }
+
+protected:
+ void on_show();
+ void on_hide();
+ bool on_delete_event(GdkEventAny* ev);
+ bool on_key_press_event(GdkEventKey* event);
+
+private:
+ void event_open();
+ void event_open_into();
+ void event_save();
+ void event_save_as();
+ void event_close();
+ void event_destroy();
+ void event_clear();
+ void event_fullscreen_toggled();
+
+ PatchController* m_patch;
+ LoadPluginWindow* m_load_plugin_window;
+ LoadPatchWindow* m_load_patch_window;
+ NewSubpatchWindow* m_new_subpatch_window;
+ LoadSubpatchWindow* m_load_subpatch_window;
+ PatchDescriptionWindow* m_description_window;
+
+ bool m_enable_signal;
+ bool m_position_stored;
+ int m_x;
+ int m_y;
+
+ Gtk::MenuItem* m_menu_open;
+ Gtk::MenuItem* m_menu_open_into;
+ Gtk::MenuItem* m_menu_save;
+ Gtk::MenuItem* m_menu_save_as;
+ Gtk::MenuItem* m_menu_configuration;
+ Gtk::MenuItem* m_menu_close;
+ Gtk::MenuItem* m_menu_quit;
+ Gtk::MenuItem* m_menu_quit_and_kill;
+ Gtk::CheckMenuItem* m_menu_fullscreen;
+ Gtk::MenuItem* m_menu_clear;
+ Gtk::MenuItem* m_menu_destroy_patch;
+ Gtk::MenuItem* m_menu_view_control_window;
+ Gtk::MenuItem* m_menu_view_patch_description;
+ Gtk::MenuItem* m_menu_add_plugin;
+ Gtk::MenuItem* m_menu_new_subpatch;
+ Gtk::MenuItem* m_menu_load_subpatch;
+ Gtk::MenuItem* m_menu_view_messages_window;
+ Gtk::MenuItem* m_menu_view_patch_tree_window;
+ Gtk::MenuItem* m_menu_help_about;
+
+ Gtk::VBox* m_vbox;
+ Gtk::Viewport* m_viewport;
+ Gtk::HBox* m_breadcrumb_box;
+ list<BreadCrumb*> m_breadcrumbs;
+ //Gtk::Statusbar* m_status_bar;
+};
+
+
+} // namespace OmGtk
+
+#endif // PATCHWINDOW_H
diff --git a/src/progs/gtk/PortController.cpp b/src/progs/gtk/PortController.cpp
new file mode 100644
index 00000000..ec05541b
--- /dev/null
+++ b/src/progs/gtk/PortController.cpp
@@ -0,0 +1,143 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PortController.h"
+#include "OmModule.h"
+#include "PortModel.h"
+#include "ControlPanel.h"
+#include "OmPort.h"
+#include "Store.h"
+
+namespace OmGtk {
+
+
+PortController::PortController(PortModel* model)
+: GtkObjectController(model),
+ m_port(NULL),
+ m_control_panel(NULL)
+{
+ assert(model != NULL);
+ assert(model->parent() != NULL);
+ assert(model->controller() == NULL);
+
+ model->set_controller(this);
+}
+
+
+void
+PortController::add_to_store()
+{
+ Store::instance().add_object(this);
+}
+
+
+void
+PortController::remove_from_store()
+{
+ Store::instance().remove_object(this);
+}
+
+
+void
+PortController::destroy()
+{
+ assert(m_model->parent() != NULL);
+ NodeController* parent = (NodeController*)m_model->parent()->controller();
+ assert(parent != NULL);
+
+ if (m_control_panel != NULL)
+ m_control_panel->remove_port(path());
+
+ parent->remove_port(path(), false);
+}
+
+
+void
+PortController::metadata_update(const string& key, const string& value)
+{
+ // FIXME: double lookups
+
+ //cerr << path() << ": " << key << " = " << value << endl;
+
+ if (key == "user-min") {
+ port_model()->user_min(atof(value.c_str()));
+ if (m_control_panel != NULL)
+ m_control_panel->set_range_min(m_model->path(), atof(value.c_str()));
+ } else if (key == "user-max") {
+ port_model()->user_max(atof(value.c_str()));
+ if (m_control_panel != NULL)
+ m_control_panel->set_range_max(m_model->path(), atof(value.c_str()));
+ }
+
+ GtkObjectController::metadata_update(key, value);
+}
+
+
+void
+PortController::control_change(float value)
+{
+ // FIXME: double lookups
+
+ port_model()->value(value);
+
+ if (m_control_panel != NULL)
+ m_control_panel->set_control(port_model()->path(), value);
+}
+
+
+/** "Register" a control panel that is monitoring this port.
+ *
+ * The OmPort will handle notifying the ControlPanel when state
+ * changes occur, etc.
+ */
+void
+PortController::set_control_panel(ControlPanel* cp)
+{
+ assert(m_control_panel == NULL);
+ m_control_panel = cp;
+}
+
+
+void
+PortController::set_path(const Path& new_path)
+{
+ // Change port name on module, if view exists
+ if (m_port != NULL)
+ m_port->set_name(new_path.name());
+
+ if (m_control_panel != NULL)
+ m_control_panel->rename_port(m_model->path(), new_path);
+
+ m_model->set_path(new_path);
+}
+
+
+/** Create the visible port on a canvas module.
+ *
+ * Does not resize the module, caller's responsibility to do so if necessary.
+ */
+void
+PortController::create_port(OmModule* module)
+{
+ assert(module != NULL);
+
+ m_port = new OmPort(module, port_model());
+ module->add_port(m_port, false);
+}
+
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/PortController.h b/src/progs/gtk/PortController.h
new file mode 100644
index 00000000..cc2f9dc3
--- /dev/null
+++ b/src/progs/gtk/PortController.h
@@ -0,0 +1,75 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PORTCONTROLLER_H
+#define PORTCONTROLLER_H
+
+#include <string>
+#include <gtkmm.h>
+#include "GtkObjectController.h"
+
+using std::string;
+using namespace LibOmClient;
+
+namespace LibOmClient {
+ class MetadataModel;
+ class PortModel;
+}
+
+namespace OmGtk {
+
+class Controller;
+class OmPort;
+class ControlPanel;
+class OmModule;
+
+
+/** Controller for a port on a (non-patch) node.
+ *
+ * \ingroup OmGtk
+ */
+class PortController : public GtkObjectController
+{
+public:
+ PortController(PortModel* model);
+ virtual ~PortController() {}
+
+ virtual void destroy();
+
+ virtual void add_to_store();
+ virtual void remove_from_store();
+
+ virtual void metadata_update(const string& key, const string& value);
+
+ void create_port(OmModule* module);
+ void set_path(const Path& new_path);
+
+ void control_change(float value);
+
+ ControlPanel* control_panel() const { return m_control_panel; }
+ void set_control_panel(ControlPanel* cp);
+
+ PortModel* port_model() const { return (PortModel*)m_model; }
+
+private:
+ OmPort* m_port; ///< Canvas module port
+ ControlPanel* m_control_panel; ///< Control panel that contains this port
+};
+
+
+} // namespace OmGtk
+
+#endif // PORTCONTROLLER_H
diff --git a/src/progs/gtk/RenameWindow.cpp b/src/progs/gtk/RenameWindow.cpp
new file mode 100644
index 00000000..bde9a2c1
--- /dev/null
+++ b/src/progs/gtk/RenameWindow.cpp
@@ -0,0 +1,113 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "RenameWindow.h"
+#include <cassert>
+#include <string>
+#include "Controller.h"
+#include "ObjectModel.h"
+#include "GtkObjectController.h"
+#include "Store.h"
+using std::string;
+
+namespace OmGtk {
+
+
+RenameWindow::RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
+: Gtk::Window(cobject)
+{
+ glade_xml->get_widget("rename_name_entry", m_name_entry);
+ glade_xml->get_widget("rename_message_label", m_message_label);
+ glade_xml->get_widget("rename_cancel_button", m_cancel_button);
+ glade_xml->get_widget("rename_ok_button", m_ok_button);
+
+ m_name_entry->signal_changed().connect(sigc::mem_fun(this, &RenameWindow::name_changed));
+ m_cancel_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::cancel_clicked));
+ m_ok_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::ok_clicked));
+
+ m_ok_button->property_sensitive() = false;
+}
+
+
+/** Set the object this window is renaming.
+ * This function MUST be called before using this object in any way.
+ */
+void
+RenameWindow::set_object(GtkObjectController* object)
+{
+ m_object = object;
+ m_name_entry->set_text(object->path().name());
+}
+
+
+/** Called every time the user types into the name input box.
+ * Used to display warning messages, and enable/disable the rename button.
+ */
+void
+RenameWindow::name_changed()
+{
+ assert(m_name_entry != NULL);
+ assert(m_message_label != NULL);
+ assert(m_object->model() != NULL);
+ assert(m_object->model()->parent() != NULL);
+
+ string name = m_name_entry->get_text();
+ if (name.find("/") != string::npos) {
+ m_message_label->set_text("Name may not contain '/'");
+ m_ok_button->property_sensitive() = false;
+ //} else if (m_object->parent()->patch_model()->get_node(name) != NULL) {
+ } else if (Store::instance().object(m_object->model()->parent()->base_path() + name) != NULL) {
+ m_message_label->set_text("An object already exists with that name.");
+ m_ok_button->property_sensitive() = false;
+ } else if (name.length() == 0) {
+ m_message_label->set_text("");
+ m_ok_button->property_sensitive() = false;
+ } else {
+ m_message_label->set_text("");
+ m_ok_button->property_sensitive() = true;
+ }
+}
+
+
+void
+RenameWindow::cancel_clicked()
+{
+ cout << "cancel\n";
+ m_name_entry->set_text("");
+ hide();
+}
+
+
+/** Rename the object.
+ *
+ * It shouldn't be possible for this to be called with an invalid name set
+ * (since the Rename button should be deactivated). This is just shinification
+ * though - the engine will handle invalid names gracefully.
+ */
+void
+RenameWindow::ok_clicked()
+{
+ string name = m_name_entry->get_text();
+ assert(name.length() > 0);
+ assert(name.find("/") == string::npos);
+
+ Controller::instance().rename(m_object->model()->path(), name);
+
+ hide();
+}
+
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/RenameWindow.h b/src/progs/gtk/RenameWindow.h
new file mode 100644
index 00000000..81dc96e3
--- /dev/null
+++ b/src/progs/gtk/RenameWindow.h
@@ -0,0 +1,57 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef RENAMEWINDOW_H
+#define RENAMEWINDOW_H
+
+#include <gtkmm.h>
+#include <libglademm.h>
+
+
+namespace OmGtk {
+
+class GtkObjectController;
+
+
+/** 'New Patch' Window.
+ *
+ * Loaded by libglade as a derived object.
+ *
+ * \ingroup OmGtk
+ */
+class RenameWindow : public Gtk::Window
+{
+public:
+ RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+
+ void set_object(GtkObjectController* object);
+
+private:
+ void name_changed();
+ void cancel_clicked();
+ void ok_clicked();
+
+ GtkObjectController* m_object;
+
+ Gtk::Entry* m_name_entry;
+ Gtk::Label* m_message_label;
+ Gtk::Button* m_cancel_button;
+ Gtk::Button* m_ok_button;
+};
+
+} // namespace OmGtk
+
+#endif // RENAMEWINDOW_H
diff --git a/src/progs/gtk/Store.cpp b/src/progs/gtk/Store.cpp
new file mode 100644
index 00000000..97e556c9
--- /dev/null
+++ b/src/progs/gtk/Store.cpp
@@ -0,0 +1,155 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Store.h"
+#include "GtkObjectController.h"
+#include "PatchController.h"
+#include "NodeController.h"
+#include "PortController.h"
+#include "PluginModel.h"
+#include "SigClientInterface.h"
+
+namespace OmGtk {
+
+Store::Store(SigClientInterface& emitter)
+{
+ //emitter.new_plugin_sig.connect(sigc::mem_fun(this, &Store::add_plugin));
+ emitter.object_destroyed_sig.connect(sigc::mem_fun(this, &Store::destruction_event));
+ emitter.new_plugin_sig.connect(sigc::mem_fun(this, &Store::new_plugin_event));
+}
+
+void
+Store::add_object(GtkObjectController* object)
+{
+ assert(object->path() != "");
+ assert(m_objects.find(object->path()) == m_objects.end());
+
+ m_objects[object->path()] = object;
+
+ cout << "[Store] Added " << object->path() << endl;
+}
+
+
+void
+Store::remove_object(GtkObjectController* object)
+{
+ if (!object)
+ return;
+
+ map<string, GtkObjectController*>::iterator i
+ = m_objects.find(object->model()->path());
+
+ if (i != m_objects.end()) {
+ assert((*i).second == object);
+ m_objects.erase(i);
+ } else {
+ cerr << "[App] Unable to find object " << object->model()->path()
+ << " to remove." << endl;
+ }
+
+ cout << "[Store] Removed " << object->path() << endl;
+}
+
+
+GtkObjectController*
+Store::object(const string& path) const
+{
+ assert(path.length() > 0);
+ map<string, GtkObjectController*>::const_iterator i = m_objects.find(path);
+ if (i == m_objects.end())
+ return NULL;
+ else
+ return (*i).second;
+}
+
+
+PatchController*
+Store::patch(const string& path) const
+{
+ assert(path.length() > 0);
+ map<string, GtkObjectController*>::const_iterator i = m_objects.find(path);
+ if (i == m_objects.end())
+ return NULL;
+ else
+ return dynamic_cast<PatchController*>((*i).second);
+}
+
+
+NodeController*
+Store::node(const string& path) const
+{
+ assert(path.length() > 0);
+ map<string, GtkObjectController*>::const_iterator i = m_objects.find(path);
+ if (i == m_objects.end())
+ return NULL;
+ else
+ return dynamic_cast<NodeController*>((*i).second);
+}
+
+
+PortController*
+Store::port(const string& path) const
+{
+ assert(path.length() > 0);
+ map<string, GtkObjectController*>::const_iterator i = m_objects.find(path);
+ if (i == m_objects.end()) {
+ return NULL;
+ } else {
+ // Normal port
+ PortController* const pc = dynamic_cast<PortController*>((*i).second);
+ if (pc != NULL)
+ return pc;
+
+ // Patch port (corresponding Node is in store)
+ NodeController* const nc = dynamic_cast<NodeController*>((*i).second);
+ if (nc != NULL)
+ return nc->as_port(); // Patch port (maybe)
+ }
+
+ return NULL;
+}
+
+
+void
+Store::add_plugin(const PluginModel* pm)
+{
+ if (m_plugins.find(pm->uri()) != m_plugins.end()) {
+ cerr << "DUPE! " << pm->uri() << endl;
+ delete m_plugins[pm->uri()];
+ }
+
+ m_plugins[pm->uri()] = pm;
+}
+
+
+/* ****** Slots ******** */
+
+void
+Store::destruction_event(const string& path)
+{
+ remove_object(object(path));
+}
+
+void
+Store::new_plugin_event(const string& type, const string& uri, const string& name)
+{
+ PluginModel* const p = new PluginModel(type, uri);
+ p->name(name);
+ add_plugin(p);
+}
+
+} // namespace OmGtk
+
diff --git a/src/progs/gtk/Store.h b/src/progs/gtk/Store.h
new file mode 100644
index 00000000..9fb4b270
--- /dev/null
+++ b/src/progs/gtk/Store.h
@@ -0,0 +1,75 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef STORE_H
+#define STORE_H
+
+#include <cassert>
+#include <string>
+#include <map>
+using std::string; using std::map;
+
+namespace LibOmClient { class PatchModel; class PluginModel; class SigClientInterface; }
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class GtkObjectController;
+class PatchController;
+class NodeController;
+class PortController;
+
+/** Singeton which holds all "Om Objects" for easy/fast lookup
+ *
+ * \ingroup OmGtk
+ */
+class Store {
+public:
+ GtkObjectController* object(const string& path) const;
+ PatchController* patch(const string& path) const;
+ NodeController* node(const string& path) const;
+ PortController* port(const string& path) const;
+
+ void add_object(GtkObjectController* object);
+ void remove_object(GtkObjectController* object);
+
+ size_t num_objects() { return m_objects.size(); }
+
+ void add_plugin(const PluginModel* pm);
+ const map<string, const PluginModel*>& plugins() const { return m_plugins; }
+
+ static void instantiate(SigClientInterface& emitter)
+ { if (!_instance) _instance = new Store(emitter); }
+
+ inline static Store& instance() { assert(_instance); return *_instance; }
+
+private:
+ Store(SigClientInterface& emitter);
+
+ static Store* _instance;
+
+ // Slots for SigClientInterface signals
+ void destruction_event(const string& path);
+ void new_plugin_event(const string& type, const string& uri, const string& name);
+
+ map<string, GtkObjectController*> m_objects; ///< Keyed by Om path
+ map<string, const PluginModel*> m_plugins; ///< Keyed by URI
+};
+
+
+} // namespace OmGtk
+
+#endif // STORE_H
diff --git a/src/progs/gtk/SubpatchModule.cpp b/src/progs/gtk/SubpatchModule.cpp
new file mode 100644
index 00000000..9c0e78e4
--- /dev/null
+++ b/src/progs/gtk/SubpatchModule.cpp
@@ -0,0 +1,102 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "SubpatchModule.h"
+#include <cassert>
+#include <iostream>
+#include "OmModule.h"
+#include "NodeControlWindow.h"
+#include "PatchModel.h"
+#include "PatchWindow.h"
+#include "OmFlowCanvas.h"
+#include "PatchController.h"
+#include "OmPort.h"
+#include "Controller.h"
+using std::cerr; using std::cout; using std::endl;
+
+namespace OmGtk {
+
+
+SubpatchModule::SubpatchModule(OmFlowCanvas* canvas, PatchController* patch)
+: OmModule(canvas, patch),
+ m_patch(patch)
+{
+ assert(canvas != NULL);
+ assert(patch != NULL);
+}
+
+
+void
+SubpatchModule::add_om_port(PortModel* pm, bool resize_to_fit)
+{
+ OmPort* port = new OmPort(this, pm);
+
+ port->signal_event().connect(
+ sigc::bind<Port*>(sigc::mem_fun(m_canvas, &OmFlowCanvas::port_event), port));
+
+ Module::add_port(port, resize_to_fit);
+}
+
+
+void
+SubpatchModule::on_double_click(GdkEventButton* event)
+{
+ assert(m_patch != NULL);
+
+ // If window is visible
+ if (m_patch->window() != NULL
+ && m_patch->window()->is_visible()) {
+ m_patch->show_patch_window(); // just raise it
+ // No window visible
+ } else {
+ if (event->state & GDK_SHIFT_MASK)
+ m_patch->show_patch_window(); // open a new window
+ else
+ browse_to_patch();
+ }
+}
+
+
+
+/** Browse to this patch in current (parent's) window. */
+void
+SubpatchModule::browse_to_patch()
+{
+ assert(m_patch->model()->parent() != NULL);
+ PatchController* pc = (PatchController*)m_patch->model()->parent()->controller();
+ assert(pc != NULL);
+ assert(pc->window() != NULL);
+
+ assert(pc->window() != NULL);
+ pc->window()->patch_controller(m_patch);
+}
+
+
+
+void
+SubpatchModule::show_dialog()
+{
+ m_patch->show_control_window();
+}
+
+
+void
+SubpatchModule::menu_remove()
+{
+ Controller::instance().destroy(m_patch->model()->path());
+}
+
+} // namespace OmGtk
diff --git a/src/progs/gtk/SubpatchModule.h b/src/progs/gtk/SubpatchModule.h
new file mode 100644
index 00000000..162779d7
--- /dev/null
+++ b/src/progs/gtk/SubpatchModule.h
@@ -0,0 +1,69 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef SUBPATCHMODULE_H
+#define SUBPATCHMODULE_H
+
+#include <string>
+#include <libgnomecanvasmm.h>
+#include "OmModule.h"
+#include "PatchController.h"
+using std::string; using std::list;
+
+namespace LibOmClient {
+class PatchModel;
+class NodeModel;
+class PortModel;
+class PatchWindow;
+}
+using namespace LibOmClient;
+
+namespace OmGtk {
+
+class OmFlowCanvas;
+class NodeControlWindow;
+class PatchController;
+
+
+/** A module to represent a subpatch
+ *
+ * \ingroup OmGtk
+ */
+class SubpatchModule : public OmModule
+{
+public:
+ SubpatchModule(OmFlowCanvas* canvas, PatchController* controller);
+ virtual ~SubpatchModule() {}
+
+ void add_om_port(PortModel* pm, bool resize=true);
+
+ void on_double_click(GdkEventButton* ev);
+
+ void show_dialog();
+ void browse_to_patch();
+ void menu_remove();
+
+ PatchController* patch() { return m_patch; }
+
+protected:
+ PatchController* m_patch;
+};
+
+
+} // namespace OmGtk
+
+#endif // SUBPATCHMODULE_H
diff --git a/src/progs/gtk/cmdline.c b/src/progs/gtk/cmdline.c
new file mode 100644
index 00000000..11fcd108
--- /dev/null
+++ b/src/progs/gtk/cmdline.c
@@ -0,0 +1,149 @@
+/*
+ File autogenerated by gengetopt version 2.10
+ generated with the following command:
+ gengetopt
+
+ The developers of gengetopt consider the fixed text that goes in all
+ gengetopt output files to be in the public domain:
+ we make no copyright claims on it.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#include "cmdline.h"
+
+void
+cmdline_parser_print_version (void)
+{
+ printf ("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION);
+}
+
+void
+cmdline_parser_print_help (void)
+{
+ cmdline_parser_print_version ();
+ printf("\n"
+ "Usage: %s [OPTIONS]...\n", CMDLINE_PARSER_PACKAGE);
+ printf(" -h --help Print help and exit\n");
+ printf(" -V --version Print version and exit\n");
+ printf(" -uSTRING --engine-url=STRING Om engine URL to connect to\n");
+ printf(" -pINT --client-port=INT Client port to listen on\n");
+}
+
+
+static char *gengetopt_strdup (const char *s);
+
+/* gengetopt_strdup() */
+/* strdup.c replacement of strdup, which is not standard */
+char *
+gengetopt_strdup (const char *s)
+{
+ char *result = (char*)malloc(strlen(s) + 1);
+ if (result == (char*)0)
+ return (char*)0;
+ strcpy(result, s);
+ return result;
+}
+
+int
+cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info)
+{
+ int c; /* Character of the parsed option. */
+ int missing_required_options = 0;
+
+ args_info->help_given = 0 ;
+ args_info->version_given = 0 ;
+ args_info->engine_url_given = 0 ;
+ args_info->client_port_given = 0 ;
+#define clear_args() { \
+ args_info->engine_url_arg = NULL; \
+}
+
+ clear_args();
+
+ optarg = 0;
+ optind = 1;
+ opterr = 1;
+ optopt = '?';
+
+ while (1)
+ {
+ int option_index = 0;
+ char *stop_char;
+
+ static struct option long_options[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "engine-url", 1, NULL, 'u' },
+ { "client-port", 1, NULL, 'p' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ stop_char = 0;
+ c = getopt_long (argc, argv, "hVu:p:", long_options, &option_index);
+
+ if (c == -1) break; /* Exit from `while (1)' loop. */
+
+ switch (c)
+ {
+ case 'h': /* Print help and exit. */
+ clear_args ();
+ cmdline_parser_print_help ();
+ exit (EXIT_SUCCESS);
+
+ case 'V': /* Print version and exit. */
+ clear_args ();
+ cmdline_parser_print_version ();
+ exit (EXIT_SUCCESS);
+
+ case 'u': /* Om engine URL to connect to. */
+ if (args_info->engine_url_given)
+ {
+ fprintf (stderr, "%s: `--engine-url' (`-u') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->engine_url_given = 1;
+ args_info->engine_url_arg = gengetopt_strdup (optarg);
+ break;
+
+ case 'p': /* Client port to listen on. */
+ if (args_info->client_port_given)
+ {
+ fprintf (stderr, "%s: `--client-port' (`-p') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->client_port_given = 1;
+ args_info->client_port_arg = strtol (optarg,&stop_char,0);
+ break;
+
+
+ case 0: /* Long option with no short option */
+
+ case '?': /* Invalid option. */
+ /* `getopt_long' already printed an error message. */
+ exit (EXIT_FAILURE);
+
+ default: /* bug: option not considered. */
+ fprintf (stderr, "%s: option unknown: %c\n", CMDLINE_PARSER_PACKAGE, c);
+ abort ();
+ } /* switch */
+ } /* while */
+
+
+ if ( missing_required_options )
+ exit (EXIT_FAILURE);
+
+ return 0;
+}
diff --git a/src/progs/gtk/cmdline.ggo b/src/progs/gtk/cmdline.ggo
new file mode 100644
index 00000000..d62cf521
--- /dev/null
+++ b/src/progs/gtk/cmdline.ggo
@@ -0,0 +1,7 @@
+# Process this file with gengetopt -u to generate the necessary code (in cmdline.h, cmdline.c)
+
+package "om_gtk - A GUI client for the Om realtime modular synthesizer"
+
+option "engine-url" u "Om engine URL to connect to" string no
+option "client-port" p "Client port to listen on" int no
+
diff --git a/src/progs/gtk/cmdline.h b/src/progs/gtk/cmdline.h
new file mode 100644
index 00000000..e404ac2e
--- /dev/null
+++ b/src/progs/gtk/cmdline.h
@@ -0,0 +1,45 @@
+/* cmdline.h */
+
+/* File autogenerated by gengetopt version 2.10 */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+#define CMDLINE_PARSER_PACKAGE "om_gtk - A GUI client for the Om realtime modular synthesizer"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+struct gengetopt_args_info
+{
+ char * engine_url_arg; /* Om engine URL to connect to. */
+ int client_port_arg; /* Client port to listen on. */
+
+ int help_given ; /* Whether help was given. */
+ int version_given ; /* Whether version was given. */
+ int engine_url_given ; /* Whether engine-url was given. */
+ int client_port_given ; /* Whether client-port was given. */
+
+} ;
+
+int cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info);
+
+void cmdline_parser_print_help(void);
+void cmdline_parser_print_version(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/src/progs/gtk/main.cpp b/src/progs/gtk/main.cpp
new file mode 100644
index 00000000..702d52f9
--- /dev/null
+++ b/src/progs/gtk/main.cpp
@@ -0,0 +1,103 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "cmdline.h"
+#include "ConnectWindow.h"
+#include "App.h"
+#include "Store.h"
+#include "Controller.h"
+#include "Configuration.h"
+#include "GtkClientInterface.h"
+#ifdef HAVE_LASH
+ #include "LashController.h"
+#endif
+#include "ThreadedSigClientInterface.h"
+using Om::Shared::ClientInterface;
+
+using namespace OmGtk;
+
+
+class OSCSigEmitter : public OSCListener, public ThreadedSigClientInterface {
+public:
+ OSCSigEmitter(size_t queue_size, int listen_port)
+ : Om::Shared::ClientInterface()
+ , OSCListener(listen_port)
+ , ThreadedSigClientInterface(queue_size)
+ {
+ Glib::signal_timeout().connect(
+ sigc::mem_fun((ThreadedSigClientInterface*)this,
+ &ThreadedSigClientInterface::emit_signals),
+ 5, G_PRIORITY_DEFAULT_IDLE);
+ }
+};
+
+
+int
+main(int argc, char** argv)
+{
+ string engine_url = "osc.udp://localhost:16180";
+ int client_port = 0;
+
+ /* **** Parse command line options **** */
+ gengetopt_args_info args_info;
+ if (cmdline_parser (argc, argv, &args_info) != 0)
+ return 1;
+
+ if (args_info.engine_url_given)
+ engine_url = args_info.engine_url_arg;
+ if (args_info.client_port_given)
+ client_port = args_info.client_port_arg;
+
+ // FIXME:
+ client_port = 16181;
+
+ Gnome::Canvas::init();
+ Gtk::Main gtk_main(argc, argv);
+
+ OSCSigEmitter* emitter = new OSCSigEmitter(1024, 16181);
+
+ //CountedPtr<Om::Shared::ClientInterface> emitter(new OSCSigEmitter(1024, 16181));
+
+ /* Instantiate all singletons */
+ App::instantiate();
+ Store::instantiate(*(SigClientInterface*)emitter);
+ ControlInterface* gtk_interface = App::instance().control_interface();
+ assert(gtk_interface);
+
+ //GtkClientInterface::instantiate(App::instance().control_interface(), client_port);
+ Controller::instantiate(engine_url);
+
+ /* Load settings */
+ App::instance().configuration()->load_settings();
+ App::instance().configuration()->apply_settings();
+
+ #ifdef HAVE_LASH
+ lash_args_t* lash_args = lash_extract_args(&argc, &argv);
+ #endif
+
+ //gtk_main.signal_quit().connect(sigc::ptr_fun(cleanup));
+
+ #ifdef HAVE_LASH
+ LashController* lash_controller = new LashController(lash_args);
+ #endif
+
+ App::instance().connect_window()->start(CountedPtr<ClientInterface>(emitter));
+ gtk_main.run();
+
+ return 0;
+}
+
diff --git a/src/progs/gtk/om-icon.png b/src/progs/gtk/om-icon.png
new file mode 100644
index 00000000..b26dd942
--- /dev/null
+++ b/src/progs/gtk/om-icon.png
Binary files differ
diff --git a/src/progs/gtk/om_gtk.glade b/src/progs/gtk/om_gtk.glade
new file mode 100644
index 00000000..abe5f254
--- /dev/null
+++ b/src/progs/gtk/om_gtk.glade
@@ -0,0 +1,3555 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="patch_win">
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">Om</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="patch_win_vbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
+ <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="patch_file_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="patch_file_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_open_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Open (Replace)...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_open_(replace)1_activate" last_modification_time="Mon, 06 Feb 2006 06:20:27 GMT"/>
+ <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1609">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_open_into_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Open _Into (Import)...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_open_into_(merge)1_activate" last_modification_time="Mon, 06 Feb 2006 06:20:27 GMT"/>
+ <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1610">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator9">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_save_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save this patch</property>
+ <property name="label" translatable="yes">_Save</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_file_save_patch_menuitem_activate" last_modification_time="Thu, 20 Jan 2005 08:35:00 GMT"/>
+ <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1611">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_save_as_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save this patch to a specific filename</property>
+ <property name="label" translatable="yes">Save _As...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_file_save_patch_menuitem_activate" last_modification_time="Sat, 23 Oct 2004 02:16:12 GMT"/>
+ <accelerator key="S" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1612">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save-as</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator10">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_configuration_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Configure OmGtk</property>
+ <property name="label" translatable="yes">Confi_guration</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_configuration_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:17:26 GMT"/>
+ <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1613">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator11">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_close_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Close this window (patch will not be destroyed)</property>
+ <property name="label" translatable="yes">Close _Window</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_file_close_menuitem_activate" last_modification_time="Sat, 11 Sep 2004 20:05:11 GMT"/>
+ <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1614">
+ <property name="visible">True</property>
+ <property name="stock">gtk-close</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator6">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_quit_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Quit OmGtk (Om engine will continue running)</property>
+ <property name="label" translatable="yes">_Quit</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_file_quit_nokill_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:18:07 GMT"/>
+ <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1615">
+ <property name="visible">True</property>
+ <property name="stock">gtk-quit</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_quit_and_kill_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Quit OmGtk and kill the Om engine</property>
+ <property name="label" translatable="yes">Quit and _Kill Engine</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_file_quit_and_kill_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:18:19 GMT"/>
+ <accelerator key="Q" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1616">
+ <property name="visible">True</property>
+ <property name="stock">gtk-stop</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="patch_patch_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Patch</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="patch_patch_menu_menu">
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="patch_fullscreen_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Fullscreen</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="patch_fullscreen_menuitem" last_modification_time="Sat, 11 Mar 2006 05:24:13 GMT"/>
+ <accelerator key="F11" modifiers="0" signal="activate"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator12">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_control_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View/Edit controls for this patch</property>
+ <property name="label" translatable="yes">_Controls</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_control_window_menuitem_activate" last_modification_time="Wed, 20 Apr 2005 16:05:32 GMT"/>
+ <accelerator key="C" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1617">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_description_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View/Edit description for this patch</property>
+ <property name="label" translatable="yes">Descr_iption</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_description_menuitem_activate" last_modification_time="Mon, 08 Aug 2005 07:18:52 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1618">
+ <property name="visible">True</property>
+ <property name="stock">gtk-edit</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_clear_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Remove all objects from patch</property>
+ <property name="label" translatable="yes">Clear</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_clear_menuitem_activate" last_modification_time="Wed, 22 Feb 2006 21:29:43 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1619">
+ <property name="visible">True</property>
+ <property name="stock">gtk-clear</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_destroy_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Destoy this patch (remove it from the engine)</property>
+ <property name="label" translatable="yes">_Destroy</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_destroy_menuitem_activate" last_modification_time="Wed, 25 May 2005 00:22:00 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1620">
+ <property name="visible">True</property>
+ <property name="stock">gtk-delete</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="add_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Insert</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_add_menu_activate" last_modification_time="Sun, 26 Dec 2004 04:33:10 GMT"/>
+
+ <child>
+ <widget class="GtkMenu" id="add_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_add_plugin_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Insert a plugin into this patch</property>
+ <property name="label" translatable="yes">_Plugin...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1621">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_add_new_subpatch_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Insert a new, empty subpatch into this patchch</property>
+ <property name="label" translatable="yes">_New Patch...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_add_new_subpatch_menuitem_activate" last_modification_time="Sun, 26 Dec 2004 04:33:10 GMT"/>
+ <accelerator key="N" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1622">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_add_subpatch_from_file_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Load a subpatch from a file and insert it into this patch</property>
+ <property name="label" translatable="yes">_Load Patch...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_subpatch_from_file1_activate" last_modification_time="Sun, 27 Feb 2005 00:37:03 GMT"/>
+ <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1623">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator7">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="right_click_on_canvas_to_place_a_new_object1">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Using this menu will place items randomly - right click!</property>
+ <property name="label" translatable="yes">Right click on canvas to manually place an object</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_right_click_on_canvas_to_place_a_new_object1_activate" last_modification_time="Sun, 29 Jan 2006 21:22:13 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1624">
+ <property name="visible">True</property>
+ <property name="stock">gtk-info</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="view1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Windows</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_view1_activate" last_modification_time="Sun, 29 Jan 2006 21:29:58 GMT"/>
+
+ <child>
+ <widget class="GtkMenu" id="view1_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_patch_tree_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View all patches in the engine as a heirarchial list</property>
+ <property name="label" translatable="yes">_Patch Tree</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_tree_window_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:29:58 GMT"/>
+ <accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1625">
+ <property name="visible">True</property>
+ <property name="stock">gtk-index</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_messages_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View error messages from the engine</property>
+ <property name="label" translatable="yes">_Messages</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_messages_window_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:29:58 GMT"/>
+ <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1626">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-error</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="help_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_help_menu_activate" last_modification_time="Sun, 29 Jan 2006 02:34:41 GMT"/>
+
+ <child>
+ <widget class="GtkMenu" id="help_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_help_about_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-about</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_about1_activate" last_modification_time="Sun, 29 Jan 2006 02:35:55 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="patch_win_breadcrumb_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="patch_win_scrolledwin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkViewport" id="patch_win_viewport">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="load_plugin_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Load Plugin</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">1</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="load_plugin_plugins_treeview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">All plugins available for loading</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">True</property>
+ <property name="reorderable">True</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table16">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="load_plugin_clear_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Clear filter text (show all plugins)</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-clear</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label66">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Module Name:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_plugin_close_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Close dialog</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator3">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox63">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkEntry" id="load_plugin_name_entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Name of new Module</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="load_plugin_polyphonic_checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Polyphonic</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">8</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_plugin_add_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add selected plugin to patch</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_padding">6</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="load_plugin_search_entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Search string to filter plugin list</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_padding">6</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="load_plugin_filter_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Name contains: </property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="new_subpatch_win">
+ <property name="border_width">8</property>
+ <property name="width_request">320</property>
+ <property name="title" translatable="yes">Create Subpatch</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Name: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_padding">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Polyphony: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_padding">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="new_subpatch_polyphony_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_padding">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="new_subpatch_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_padding">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="new_subpatch_message_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox5">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkButton" id="new_subpatch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="new_subpatch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox54">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image259">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label96">
+ <property name="visible">True</property>
+ <property name="label">Create</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkFileChooserDialog" id="load_subpatch_win">
+ <property name="action">GTK_FILE_CHOOSER_ACTION_OPEN</property>
+ <property name="local_only">True</property>
+ <property name="select_multiple">False</property>
+ <property name="show_hidden">False</property>
+ <property name="do_overwrite_confirmation">False</property>
+ <property name="title" translatable="yes">Load Subpatch</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="load_subpatch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_subpatch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table6">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label79">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Name: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label80">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Polyphony: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_name_from_file_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the name stored in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_file_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox46">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_user_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specify: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_poly_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="load_subpatch_poly_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 1000 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_parent_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Set polyphony to the same value as the parent (containing) patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Same as parent (?)</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_poly_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label104">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox45">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_name_from_user_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Specify the name for the new patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specify: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_name_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="load_subpatch_name_entry">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Specify the name for the new patch</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkFileChooserDialog" id="load_patch_win">
+ <property name="action">GTK_FILE_CHOOSER_ACTION_OPEN</property>
+ <property name="local_only">True</property>
+ <property name="select_multiple">False</property>
+ <property name="show_hidden">False</property>
+ <property name="do_overwrite_confirmation">False</property>
+ <property name="title" translatable="yes">Load Patch</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="vbox11">
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="load_patch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_patch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table14">
+ <property name="visible">True</property>
+ <property name="n_rows">1</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label123">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Polyphony: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_current_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the same polyphony as the current patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Keep current</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_file_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_patch_poly_from_current_radio</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox58">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_user_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specify:</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_patch_poly_from_current_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="load_patch_poly_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="warehouse_win">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkTable" id="table8">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="control_panel_vbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwin1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkVBox" id="control_panel_controls_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="control_panel_voice_controls_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="control_panel_all_voices_radio">
+ <property name="tooltip" translatable="yes">Apply changed controls to all voices</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">All Voices</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox32">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="control_panel_specific_voice_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Apply changed controls to one voice only</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specific Voice:</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">control_panel_all_voices_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="control_panel_voice_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Voice control changes are applied to</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 1 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="patch_view_vbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="patch_canvas_scrolledwindow">
+ <property name="border_width">1</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="patch_process_checkbutton">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Enable audio processing for this patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Run</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem2">
+ <property name="visible">True</property>
+ <property name="draw">True</property>
+ <property name="visible_horizontal">False</property>
+ <property name="visible_vertical">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox26">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Polyphony: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="patch_polyphony_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">?</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem3">
+ <property name="visible">True</property>
+ <property name="draw">True</property>
+ <property name="visible_horizontal">False</property>
+ <property name="visible_vertical">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label77">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Zoom: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.469999998808</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">2</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHScale" id="patch_zoom_scale">
+ <property name="width_request">90</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <property name="digits">1</property>
+ <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+ <property name="inverted">False</property>
+ <property name="adjustment">1.10638296604 0.25 2 0.25 0 0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="messages_win">
+ <property name="border_width">8</property>
+ <property name="width_request">400</property>
+ <property name="height_request">180</property>
+ <property name="title" translatable="yes">Error Messages</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="messages_textview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Error messages from the engine since the last time &quot;Clear&quot; was pressed</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">False</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">False</property>
+ <property name="pixels_above_lines">5</property>
+ <property name="pixels_below_lines">5</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">5</property>
+ <property name="right_margin">5</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox8">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="messages_clear_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-clear</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="messages_close_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="config_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Configuration</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkTable" id="table9">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label90">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Patch Search Path: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="config_path_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label91">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;i&gt;Example: /foo/bar:/home/john/patches:/usr/share/om/patches&lt;/i&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label103">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="config_save_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save these settings for future sessions</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="config_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="config_ok_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Apply these settings to this session only</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="patch_description_win">
+ <property name="border_width">8</property>
+ <property name="width_request">400</property>
+ <property name="height_request">200</property>
+ <property name="title" translatable="yes">Patch Description</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox51">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkLabel" id="label93">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="description_author_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow9">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="description_description_textview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">A short description of the patch to be included in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkButton" id="description_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="description_ok_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Apply these changes to be saved the next time the patch is saved</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="rename_win">
+ <property name="width_request">250</property>
+ <property name="title" translatable="yes">Rename</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox15">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox53">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label95">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New name: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="rename_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="rename_message_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox4">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkButton" id="rename_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="rename_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox52">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image258">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label94">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Rename</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="node_properties_win">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox17">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label105">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Node&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox18">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox56">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label121">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Path: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_path_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="node_properties_polyphonic_checkbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Polyphonic</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label106">
+ <property name="width_request">240</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Plugin&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table13">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">10</property>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label114">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Type: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label120">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">URI: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_uri_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label116">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Name: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_name_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkAboutDialog" id="about_win">
+ <property name="destroy_with_parent">True</property>
+ <property name="name" translatable="yes">OmGtk</property>
+ <property name="copyright" translatable="yes">Copyright (C) 2005-2006 Dave Robillard</property>
+ <property name="comments" translatable="yes">A client for the Om modular synthesizer</property>
+ <property name="license" translatable="yes">Licensed under the GNU GPL, Version 2.
+
+See COPYING file included with Om, or http://www.gnu.org/licenses/gpl.txt for more information</property>
+ <property name="wrap_license">False</property>
+ <property name="website">http://om-synth.nongnu.org</property>
+ <property name="website_label" translatable="yes"></property>
+ <property name="authors">Author:
+ Dave Robillard &lt;drobilla@connect.carelton.ca&gt;
+
+Contributors:
+ Lars Luthman - DSSI enhancements, bugfixes
+ Mario Lang - SuperCollider bindings, bugfixes
+ Leonard Ritter - Python bindings
+</property>
+ <property name="artists">Usability / UI Design:
+ Thorsten Wilms</property>
+ <property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property>
+ <property name="logo">om-icon.png</property>
+</widget>
+
+<widget class="GtkWindow" id="patch_tree_win">
+ <property name="border_width">8</property>
+ <property name="width_request">320</property>
+ <property name="height_request">340</property>
+ <property name="title" translatable="yes">Patches</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow8">
+ <property name="border_width">3</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="patches_treeview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">All patches loaded in the engine</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">True</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="connect_win">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">Connecting to Engine</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
+
+ <child>
+ <widget class="GtkButton" id="connect_launch_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">0</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox62">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image946">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label126">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Launch Engine</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="connect_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox19">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox61">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkImage" id="image846">
+ <property name="visible">True</property>
+ <property name="pixbuf">om-icon.png</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">6</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox20">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label125">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkProgressBar" id="connect_progress_bar">
+ <property name="visible">True</property>
+ <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+ <property name="fraction">0</property>
+ <property name="pulse_step">0.10000000149</property>
+ <property name="text" translatable="yes"></property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label124">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="connect_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Connecting to engine</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/src/progs/gtk/om_gtk.glade.bak b/src/progs/gtk/om_gtk.glade.bak
new file mode 100644
index 00000000..7714d1f3
--- /dev/null
+++ b/src/progs/gtk/om_gtk.glade.bak
@@ -0,0 +1,3555 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="patch_win">
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">Om</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="patch_win_vbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
+ <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="patch_file_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="patch_file_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_open_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Open (Replace)...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_open_(replace)1_activate" last_modification_time="Mon, 06 Feb 2006 06:20:27 GMT"/>
+ <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1609">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_open_into_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Open _Into (Import)...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_open_into_(merge)1_activate" last_modification_time="Mon, 06 Feb 2006 06:20:27 GMT"/>
+ <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1610">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator9">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_save_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save this patch</property>
+ <property name="label" translatable="yes">_Save</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_file_save_patch_menuitem_activate" last_modification_time="Thu, 20 Jan 2005 08:35:00 GMT"/>
+ <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1611">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_save_as_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save this patch to a specific filename</property>
+ <property name="label" translatable="yes">Save _As...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_file_save_patch_menuitem_activate" last_modification_time="Sat, 23 Oct 2004 02:16:12 GMT"/>
+ <accelerator key="S" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1612">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save-as</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator10">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_configuration_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Configure OmGtk</property>
+ <property name="label" translatable="yes">Confi_guration</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_configuration_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:17:26 GMT"/>
+ <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1613">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator11">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_close_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Close this window (patch will not be destroyed)</property>
+ <property name="label" translatable="yes">Close _Window</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_file_close_menuitem_activate" last_modification_time="Sat, 11 Sep 2004 20:05:11 GMT"/>
+ <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1614">
+ <property name="visible">True</property>
+ <property name="stock">gtk-close</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator6">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_quit_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Quit OmGtk (Om engine will continue running)</property>
+ <property name="label" translatable="yes">_Quit</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_file_quit_nokill_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:18:07 GMT"/>
+ <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1615">
+ <property name="visible">True</property>
+ <property name="stock">gtk-quit</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_quit_and_kill_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Quit OmGtk and kill the Om engine</property>
+ <property name="label" translatable="yes">Quit and _Kill Engine</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_file_quit_and_kill_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:18:19 GMT"/>
+ <accelerator key="Q" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1616">
+ <property name="visible">True</property>
+ <property name="stock">gtk-stop</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="patch_patch_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Patch</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="patch_patch_menu_menu">
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="patch_fullscreen_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Fullscreen</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="patch_fullscreen_menuitem" last_modification_time="Sat, 11 Mar 2006 05:24:13 GMT"/>
+ <accelerator key="F11" modifiers="0" signal="activate"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator12">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_control_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View/Edit controls for this patch</property>
+ <property name="label" translatable="yes">_Controls</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_control_window_menuitem_activate" last_modification_time="Wed, 20 Apr 2005 16:05:32 GMT"/>
+ <accelerator key="C" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1617">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_description_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View/Edit description for this patch</property>
+ <property name="label" translatable="yes">Descr_iption</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_description_menuitem_activate" last_modification_time="Mon, 08 Aug 2005 07:18:52 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1618">
+ <property name="visible">True</property>
+ <property name="stock">gtk-edit</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_clear_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Remove all objects from patch</property>
+ <property name="label" translatable="yes">Clear</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_clear_menuitem_activate" last_modification_time="Wed, 22 Feb 2006 21:29:43 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1619">
+ <property name="visible">True</property>
+ <property name="stock">gtk-clear</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_destroy_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Destoy this patch (remove it from the engine)</property>
+ <property name="label" translatable="yes">_Destroy</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_destroy_menuitem_activate" last_modification_time="Wed, 25 May 2005 00:22:00 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1620">
+ <property name="visible">True</property>
+ <property name="stock">gtk-delete</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="add_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Insert</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_add_menu_activate" last_modification_time="Sun, 26 Dec 2004 04:33:10 GMT"/>
+
+ <child>
+ <widget class="GtkMenu" id="add_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_add_plugin_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Insert a plugin into this patch</property>
+ <property name="label" translatable="yes">_Plugin...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1621">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_add_new_subpatch_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Insert a new, empty subpatch into this patchch</property>
+ <property name="label" translatable="yes">_New Patch...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_add_new_subpatch_menuitem_activate" last_modification_time="Sun, 26 Dec 2004 04:33:10 GMT"/>
+ <accelerator key="N" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1622">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_add_subpatch_from_file_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Load a subpatch from a file and insert it into this patch</property>
+ <property name="label" translatable="yes">_Load Patch...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_subpatch_from_file1_activate" last_modification_time="Sun, 27 Feb 2005 00:37:03 GMT"/>
+ <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1623">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator7">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="right_click_on_canvas_to_place_a_new_object1">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Using this menu will place items randomly - right click!</property>
+ <property name="label" translatable="yes">Right click on canvas to manually place an object</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_right_click_on_canvas_to_place_a_new_object1_activate" last_modification_time="Sun, 29 Jan 2006 21:22:13 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1624">
+ <property name="visible">True</property>
+ <property name="stock">gtk-info</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="view1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Windows</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_view1_activate" last_modification_time="Sun, 29 Jan 2006 21:29:58 GMT"/>
+
+ <child>
+ <widget class="GtkMenu" id="view1_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_patch_tree_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View all patches in the engine as a heirarchial list</property>
+ <property name="label" translatable="yes">_Patch Tree</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_tree_window_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:29:58 GMT"/>
+ <accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1625">
+ <property name="visible">True</property>
+ <property name="stock">gtk-index</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_view_messages_window_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View error messages from the engine</property>
+ <property name="label" translatable="yes">_Messages</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_patch_view_messages_window_menuitem_activate" last_modification_time="Sun, 29 Jan 2006 21:29:58 GMT"/>
+ <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1626">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-error</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="help_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_help_menu_activate" last_modification_time="Sun, 29 Jan 2006 02:34:41 GMT"/>
+
+ <child>
+ <widget class="GtkMenu" id="help_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="patch_help_about_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-about</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_about1_activate" last_modification_time="Sun, 29 Jan 2006 02:35:55 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="patch_win_breadcrumb_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="patch_win_scrolledwin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkViewport" id="patch_win_viewport">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="load_plugin_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Load Plugin</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">1</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="load_plugin_plugins_treeview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">All plugins available for loading</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">True</property>
+ <property name="reorderable">True</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table16">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="load_plugin_clear_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Clear filter text (show all plugins)</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-clear</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label66">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Module Name:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_plugin_close_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Close dialog</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator3">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox63">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkEntry" id="load_plugin_name_entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Name of new Module</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="load_plugin_polyphonic_checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Polyphonic</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">8</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_plugin_add_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add selected plugin to patch</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_padding">6</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="load_plugin_search_entry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Search string to filter plugin list</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_padding">6</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="load_plugin_filter_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Name contains: </property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="new_subpatch_win">
+ <property name="border_width">8</property>
+ <property name="width_request">320</property>
+ <property name="title" translatable="yes">Create Subpatch</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Name: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_padding">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Polyphony: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_padding">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">expand</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="new_subpatch_polyphony_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_padding">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="new_subpatch_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_padding">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="new_subpatch_message_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox5">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkButton" id="new_subpatch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="new_subpatch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox54">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image259">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label96">
+ <property name="visible">True</property>
+ <property name="label">Create</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkFileChooserDialog" id="load_subpatch_win">
+ <property name="action">GTK_FILE_CHOOSER_ACTION_OPEN</property>
+ <property name="local_only">True</property>
+ <property name="select_multiple">False</property>
+ <property name="show_hidden">False</property>
+ <property name="do_overwrite_confirmation">False</property>
+ <property name="title" translatable="yes">Load Subpatch</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="load_subpatch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_subpatch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table6">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label79">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Name: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label80">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Polyphony: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_name_from_file_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the name stored in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_file_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox46">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_user_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specify: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_poly_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="load_subpatch_poly_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 1000 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_poly_from_parent_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Set polyphony to the same value as the parent (containing) patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Same as parent (?)</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_poly_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label104">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox45">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_subpatch_name_from_user_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Specify the name for the new patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specify: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_subpatch_name_from_file_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="load_subpatch_name_entry">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Specify the name for the new patch</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkFileChooserDialog" id="load_patch_win">
+ <property name="action">GTK_FILE_CHOOSER_ACTION_OPEN</property>
+ <property name="local_only">True</property>
+ <property name="select_multiple">False</property>
+ <property name="show_hidden">False</property>
+ <property name="do_overwrite_confirmation">False</property>
+ <property name="title" translatable="yes">Load Patch</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="vbox11">
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="load_patch_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="load_patch_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table14">
+ <property name="visible">True</property>
+ <property name="n_rows">1</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label123">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Polyphony: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_current_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the same polyphony as the current patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Keep current</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_file_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Load from file</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_patch_poly_from_current_radio</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox58">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="load_patch_poly_from_user_radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specify:</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">load_patch_poly_from_current_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="load_patch_poly_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="warehouse_win">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkTable" id="table8">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="control_panel_vbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwin1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkVBox" id="control_panel_controls_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="control_panel_voice_controls_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="control_panel_all_voices_radio">
+ <property name="tooltip" translatable="yes">Apply changed controls to all voices</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">All Voices</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox32">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="control_panel_specific_voice_radio">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Apply changed controls to one voice only</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Specific Voice:</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">control_panel_all_voices_radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="control_panel_voice_spinbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Voice control changes are applied to</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 1 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="patch_view_vbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="patch_canvas_scrolledwindow">
+ <property name="border_width">1</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="patch_process_checkbutton">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Enable audio processing for this patch</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Run</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem2">
+ <property name="visible">True</property>
+ <property name="draw">True</property>
+ <property name="visible_horizontal">False</property>
+ <property name="visible_vertical">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox26">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Polyphony: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="patch_polyphony_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">?</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem3">
+ <property name="visible">True</property>
+ <property name="draw">True</property>
+ <property name="visible_horizontal">False</property>
+ <property name="visible_vertical">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label77">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Zoom: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.469999998808</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">2</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHScale" id="patch_zoom_scale">
+ <property name="width_request">90</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <property name="digits">1</property>
+ <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+ <property name="inverted">False</property>
+ <property name="adjustment">1.10638296604 0.25 2 0.25 0 0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="messages_win">
+ <property name="border_width">8</property>
+ <property name="width_request">400</property>
+ <property name="height_request">180</property>
+ <property name="title" translatable="yes">Error Messages</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="messages_textview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Error messages from the engine since the last time &quot;Clear&quot; was pressed</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">False</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">False</property>
+ <property name="pixels_above_lines">5</property>
+ <property name="pixels_below_lines">5</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">5</property>
+ <property name="right_margin">5</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox8">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="messages_clear_button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-clear</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="messages_close_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="config_win">
+ <property name="border_width">8</property>
+ <property name="title" translatable="yes">Configuration</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkTable" id="table9">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label90">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Patch Search Path: &lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="config_path_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label91">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;i&gt;Example: /foo/bar:/home/john/patches:/usr/share/om/patches&lt;/i&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label103">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="config_save_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save these settings for future sessions</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="config_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="config_ok_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Apply these settings to this session only</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="patch_description_win">
+ <property name="border_width">8</property>
+ <property name="width_request">400</property>
+ <property name="height_request">200</property>
+ <property name="title" translatable="yes">Patch Description</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox51">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkLabel" id="label93">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Author:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="description_author_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow9">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="description_description_textview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">A short description of the patch to be included in the patch file</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkButton" id="description_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="description_ok_button">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Apply these changes to be saved the next time the patch is saved</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="rename_win">
+ <property name="width_request">250</property>
+ <property name="title" translatable="yes">Rename</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox15">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox53">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label95">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New name: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="rename_name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="rename_message_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox4">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkButton" id="rename_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="rename_ok_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox52">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image258">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label94">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Rename</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="node_properties_win">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox17">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label105">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Node&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox18">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox56">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label121">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Path: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_path_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="node_properties_polyphonic_checkbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Polyphonic</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label106">
+ <property name="width_request">240</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Plugin&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table13">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">10</property>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label114">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Type: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label120">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">URI: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_uri_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label116">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Name: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="node_properties_plugin_name_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">-</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkAboutDialog" id="about_win">
+ <property name="destroy_with_parent">True</property>
+ <property name="name" translatable="yes">OmGtk</property>
+ <property name="copyright" translatable="yes">Copyright (C) 2005-2006 Dave Robillard</property>
+ <property name="comments" translatable="yes">A client for the Om modular synthesizer</property>
+ <property name="license" translatable="yes">Licensed under the GNU GPL, Version 2.
+
+See COPYING file included with Om, or http://www.gnu.org/licenses/gpl.txt for more information</property>
+ <property name="wrap_license">False</property>
+ <property name="website">http://om-synth.nongnu.org</property>
+ <property name="website_label" translatable="yes"></property>
+ <property name="authors">Author:
+ Dave Robillard &lt;drobilla@connect.carelton.ca&gt;
+
+Contributors:
+ Lars Luthman - DSSI enhancements, bugfixes
+ Mario Lang - SuperCollider bindings, bugfixes
+ Leonard Ritter - Python bindings
+</property>
+ <property name="artists">Usability / UI Design:
+ Thorsten Wilms</property>
+ <property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property>
+ <property name="logo">om-icon.png</property>
+</widget>
+
+<widget class="GtkWindow" id="patch_tree_win">
+ <property name="border_width">8</property>
+ <property name="width_request">320</property>
+ <property name="height_request">340</property>
+ <property name="title" translatable="yes">Patches</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow8">
+ <property name="border_width">3</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="patches_treeview">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">All patches loaded in the engine</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">True</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="connect_win">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">Connecting to Engine</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">om-icon.png</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
+
+ <child>
+ <widget class="GtkButton" id="connect_launch_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">0</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox62">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image946">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label126">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Launch Engine</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="connect_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox19">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox61">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkImage" id="image846">
+ <property name="visible">True</property>
+ <property name="pixbuf">om-icon.png</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">6</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox20">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label125">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkProgressBar" id="connect_progress_bar">
+ <property name="visible">True</property>
+ <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+ <property name="fraction">0</property>
+ <property name="pulse_step">0.10000000149</property>
+ <property name="text" translatable="yes"></property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label124">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="connect_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Connecting to engine</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/src/progs/gtk/om_gtk.gladep b/src/progs/gtk/om_gtk.gladep
new file mode 100644
index 00000000..a8bd18bd
--- /dev/null
+++ b/src/progs/gtk/om_gtk.gladep
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>OmGtk</name>
+ <program_name>om_gtk</program_name>
+ <language>C++</language>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>
diff --git a/src/progs/gtk/om_gtk.gladep.bak b/src/progs/gtk/om_gtk.gladep.bak
new file mode 100644
index 00000000..a8bd18bd
--- /dev/null
+++ b/src/progs/gtk/om_gtk.gladep.bak
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>OmGtk</name>
+ <program_name>om_gtk</program_name>
+ <language>C++</language>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>
diff --git a/src/progs/gtk/singletons.cpp b/src/progs/gtk/singletons.cpp
new file mode 100644
index 00000000..75b441e9
--- /dev/null
+++ b/src/progs/gtk/singletons.cpp
@@ -0,0 +1,29 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "App.h"
+#include "Store.h"
+#include "Controller.h"
+//#include "GtkClientInterface.h"
+
+namespace OmGtk
+{
+ App* App::_instance = 0;
+ Store* Store::_instance = 0;
+ Controller* Controller::_instance = 0;
+ //CountedPtr<GtkClientInterface> GtkClientInterface::_instance = 0;
+}
+
diff --git a/src/progs/om/Makefile.am b/src/progs/om/Makefile.am
index ae0b3e53..2f503130 100644
--- a/src/progs/om/Makefile.am
+++ b/src/progs/om/Makefile.am
@@ -1,232 +1,15 @@
-SUBDIRS = tests
-DIST_SUBDIRS = events
-
-AM_CXXFLAGS = @JACK_CFLAGS@ @LOSC_CFLAGS@ @ALSA_CFLAGS@ @LASH_CFLAGS@ @SLV2_CFLAGS@ -I$(top_srcdir)/src/common -I$(top_srcdir)/src/engine/events -fno-exceptions -fno-rtti
+AM_CXXFLAGS = @JACK_CFLAGS@ @LOSC_CFLAGS@ @ALSA_CFLAGS@ @LASH_CFLAGS@ @SLV2_CFLAGS@ -I$(top_srcdir)/src/common -I$(top_srcdir)/src/engine -I$(top_srcdir)/src/engine/events -fno-exceptions -fno-rtti
MAINTAINERCLEANFILES = Makefile.in
-common_SOURCES = \
- util.h \
- tuning.h \
- Node.h \
- NodeBase.h \
- NodeBase.cpp \
- InternalNode.h \
- Patch.h \
- Patch.cpp \
- NodeFactory.h \
- NodeFactory.cpp \
- Om.h \
- Om.cpp \
- OmApp.h \
- OmApp.cpp \
- JackAudioDriver.h \
- JackAudioDriver.cpp \
- OSCReceiver.h \
- OSCReceiver.cpp \
- Responder.h \
- OSCResponder.h \
- OSCResponder.cpp \
- ClientKey.h \
- ClientBroadcaster.h \
- ClientBroadcaster.cpp \
- ObjectSender.h \
- ObjectSender.cpp \
- OSCClient.h \
- OSCClient.cpp \
- Buffer.h \
- Buffer.cpp \
- Port.h \
- Port.cpp \
- PortBase.h \
- PortBase.cpp \
- InputPort.h \
- InputPort.cpp \
- OutputPort.h \
- OutputPort.cpp \
- MidiMessage.h \
- MidiNoteNode.h \
- MidiNoteNode.cpp \
- MidiTriggerNode.h \
- MidiTriggerNode.cpp \
- MidiControlNode.h \
- MidiControlNode.cpp \
- BridgeNode.h \
- BridgeNode.cpp \
- ControlInputNode.h \
- ControlInputNode.cpp \
- ControlOutputNode.h \
- ControlOutputNode.cpp \
- AudioInputNode.h \
- AudioInputNode.cpp \
- AudioOutputNode.h \
- AudioOutputNode.cpp \
- MidiInputNode.h \
- MidiInputNode.cpp \
- MidiOutputNode.h \
- MidiOutputNode.cpp \
- Event.h \
- Event.cpp \
- QueuedEvent.h \
- EventSource.h \
- QueuedEventSource.h \
- QueuedEventSource.cpp \
- QueuedEngineInterface.h \
- QueuedEngineInterface.cpp \
- OmObject.h \
- Maid.h \
- Maid.cpp \
- MaidObject.h \
- Tree.h \
- ClientRecord.h \
- PortInfo.h \
- PluginLibrary.h \
- Plugin.h \
- Array.h \
- List.h \
- PostProcessor.h \
- PostProcessor.cpp \
- Connection.h \
- Connection.cpp \
- ConnectionBase.h \
- ConnectionBase.cpp \
- ObjectStore.h \
- ObjectStore.cpp \
- TransportNode.h \
- TransportNode.cpp \
- Driver.h \
- AudioDriver.h \
- MidiDriver.h \
- midi.h \
- ../common/util/Semaphore.h \
- ../common/util/types.h \
- ../common/util/Path.h \
- ../common/util/Queue.h \
- ../common/interface/ClientInterface.h \
- ../common/interface/EngineInterface.h \
- instantiations.cpp
-
-# Events
-common_SOURCES += \
- events/RegisterClientEvent.h \
- events/RegisterClientEvent.cpp \
- events/UnregisterClientEvent.h \
- events/UnregisterClientEvent.cpp \
- events/PingQueuedEvent.h \
- events/ActivateEvent.h \
- events/ActivateEvent.cpp \
- events/DeactivateEvent.h \
- events/DeactivateEvent.cpp \
- events/SetPortValueEvent.h \
- events/SetPortValueEvent.cpp \
- events/SetPortValueQueuedEvent.h \
- events/SetPortValueQueuedEvent.cpp \
- events/NoteOnEvent.h \
- events/NoteOnEvent.cpp \
- events/NoteOffEvent.h \
- events/NoteOffEvent.cpp \
- events/AllNotesOffEvent.h \
- events/AllNotesOffEvent.cpp \
- events/ConnectionEvent.h \
- events/ConnectionEvent.cpp \
- events/DisconnectionEvent.h \
- events/DisconnectionEvent.cpp \
- events/DisconnectNodeEvent.h \
- events/DisconnectNodeEvent.cpp \
- events/DisconnectPortEvent.h \
- events/DisconnectPortEvent.cpp \
- events/DestroyEvent.h \
- events/DestroyEvent.cpp \
- events/AddNodeEvent.h \
- events/AddNodeEvent.cpp \
- events/SetMetadataEvent.h \
- events/SetMetadataEvent.cpp \
- events/RequestMetadataEvent.h \
- events/RequestMetadataEvent.cpp \
- events/RequestPortValueEvent.h \
- events/RequestPortValueEvent.cpp \
- events/RequestAllObjectsEvent.h \
- events/RequestAllObjectsEvent.cpp \
- events/RequestPluginsEvent.h \
- events/RequestPluginsEvent.cpp \
- events/CreatePatchEvent.h \
- events/CreatePatchEvent.cpp \
- events/LoadPluginsEvent.h \
- events/LoadPluginsEvent.cpp \
- events/EnablePatchEvent.h \
- events/EnablePatchEvent.cpp \
- events/DisablePatchEvent.h \
- events/DisablePatchEvent.cpp \
- events/ClearPatchEvent.h \
- events/ClearPatchEvent.cpp \
- events/RenameEvent.h \
- events/RenameEvent.cpp \
- events/MidiLearnEvent.h \
- events/MidiLearnEvent.cpp
-
-if WITH_JACK_MIDI
-common_SOURCES += \
- JackMidiDriver.h \
- JackMidiDriver.cpp
-endif
-
-if WITH_ALSA_MIDI
-common_SOURCES += \
- AlsaMidiDriver.h \
- AlsaMidiDriver.cpp
-endif
-
-if WITH_LADSPA
-common_SOURCES += \
- LADSPAPlugin.h \
- LADSPAPlugin.cpp
-endif
-
-if WITH_DSSI
-common_SOURCES += \
- DSSIPlugin.h \
- DSSIPlugin.cpp \
- events/DSSIConfigureEvent.cpp \
- events/DSSIConfigureEvent.h \
- events/DSSIControlEvent.cpp \
- events/DSSIControlEvent.h \
- events/DSSIProgramEvent.cpp \
- events/DSSIProgramEvent.h \
- events/DSSIUpdateEvent.cpp \
- events/DSSIUpdateEvent.h
-endif
-
-if WITH_LV2
-common_SOURCES += \
- LV2Plugin.h \
- LV2Plugin.cpp
-endif
-
-if WITH_LASH
-common_SOURCES += \
- LashDriver.h \
- LashDriver.cpp \
- LashRestoreDoneEvent.h
-endif
-
-
-#
-# Non-installed library to link stand-alone and in-process build against
-#
-
-noinst_LTLIBRARIES = libom.la
-libom_la_SOURCES = $(common_SOURCES)
-
-
-
#
# Stand-alone engine
#
if BUILD_ENGINE
bin_PROGRAMS = om
-om_DEPENDENCIES = libom.la
-om_LDADD = @JACK_LIBS@ @LOSC_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @SLV2_LIBS@ -lrt libom.la
+om_DEPENDENCIES = ../../libs/engine/libom.la
+om_LDADD = @JACK_LIBS@ @LOSC_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @SLV2_LIBS@ -lrt ../../libs/engine/libom.la
om_SOURCES = \
main.cpp \
@@ -236,24 +19,25 @@ om_SOURCES = \
endif # BUILD_ENGINE
-
+##
+## Jack internal client
+##
+#if BUILD_IN_PROCESS_ENGINE
#
-# Jack internal client
#
-if BUILD_IN_PROCESS_ENGINE
-
-
-# FIXME: broken
-
-
-# FIXME: Figure out how to get this properly
-omdir = $(prefix)/lib/jack
+## FIXME: broken
+#
+#
+## FIXME: Figure out how to get this properly
+#omdir = $(prefix)/lib/jack
+#
+#om_la_CFLAGS = -fPIC
+#om_LTLIBRARIES = om.la
+#om_la_LDFLAGS = -module -avoid-version @JACK_LIBS@ @LOSC_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @SLV2_LIBS@
+#om_la_SOURCES = OmInProcess.cpp
+#
+#endif # BUILD_IN_PROCESS_ENGINE
-om_la_CFLAGS = -fPIC
-om_LTLIBRARIES = om.la
-om_la_LDFLAGS = -module -avoid-version @JACK_LIBS@ @LOSC_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @SLV2_LIBS@
-om_la_SOURCES = OmInProcess.cpp
-endif # BUILD_IN_PROCESS_ENGINE
diff --git a/src/progs/om/cmdline.c b/src/progs/om/cmdline.c
new file mode 100644
index 00000000..6b7ddf6a
--- /dev/null
+++ b/src/progs/om/cmdline.c
@@ -0,0 +1,150 @@
+/*
+ File autogenerated by gengetopt version 2.10
+ generated with the following command:
+ gengetopt
+
+ The developers of gengetopt consider the fixed text that goes in all
+ gengetopt output files to be in the public domain:
+ we make no copyright claims on it.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#include "cmdline.h"
+
+void
+cmdline_parser_print_version (void)
+{
+ printf ("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION);
+}
+
+void
+cmdline_parser_print_help (void)
+{
+ cmdline_parser_print_version ();
+ printf("\n"
+ "Usage: %s [OPTIONS]...\n", CMDLINE_PARSER_PACKAGE);
+ printf(" -h --help Print help and exit\n");
+ printf(" -V --version Print version and exit\n");
+ printf(" -pSTRING --port=STRING OSC port to listen on (default='16180')\n");
+ printf(" -i --in-jackd Run engine as in-process JACK client (default=off)\n");
+}
+
+
+static char *gengetopt_strdup (const char *s);
+
+/* gengetopt_strdup() */
+/* strdup.c replacement of strdup, which is not standard */
+char *
+gengetopt_strdup (const char *s)
+{
+ char *result = (char*)malloc(strlen(s) + 1);
+ if (result == (char*)0)
+ return (char*)0;
+ strcpy(result, s);
+ return result;
+}
+
+int
+cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info)
+{
+ int c; /* Character of the parsed option. */
+ int missing_required_options = 0;
+
+ args_info->help_given = 0 ;
+ args_info->version_given = 0 ;
+ args_info->port_given = 0 ;
+ args_info->in_jackd_given = 0 ;
+#define clear_args() { \
+ args_info->port_arg = gengetopt_strdup("16180") ;\
+ args_info->in_jackd_flag = 0;\
+}
+
+ clear_args();
+
+ optarg = 0;
+ optind = 1;
+ opterr = 1;
+ optopt = '?';
+
+ while (1)
+ {
+ int option_index = 0;
+ char *stop_char;
+
+ static struct option long_options[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "port", 1, NULL, 'p' },
+ { "in-jackd", 0, NULL, 'i' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ stop_char = 0;
+ c = getopt_long (argc, argv, "hVp:i", long_options, &option_index);
+
+ if (c == -1) break; /* Exit from `while (1)' loop. */
+
+ switch (c)
+ {
+ case 'h': /* Print help and exit. */
+ clear_args ();
+ cmdline_parser_print_help ();
+ exit (EXIT_SUCCESS);
+
+ case 'V': /* Print version and exit. */
+ clear_args ();
+ cmdline_parser_print_version ();
+ exit (EXIT_SUCCESS);
+
+ case 'p': /* OSC port to listen on. */
+ if (args_info->port_given)
+ {
+ fprintf (stderr, "%s: `--port' (`-p') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->port_given = 1;
+ args_info->port_arg = gengetopt_strdup (optarg);
+ break;
+
+ case 'i': /* Run engine as in-process JACK client. */
+ if (args_info->in_jackd_given)
+ {
+ fprintf (stderr, "%s: `--in-jackd' (`-i') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->in_jackd_given = 1;
+ args_info->in_jackd_flag = !(args_info->in_jackd_flag);
+ break;
+
+
+ case 0: /* Long option with no short option */
+
+ case '?': /* Invalid option. */
+ /* `getopt_long' already printed an error message. */
+ exit (EXIT_FAILURE);
+
+ default: /* bug: option not considered. */
+ fprintf (stderr, "%s: option unknown: %c\n", CMDLINE_PARSER_PACKAGE, c);
+ abort ();
+ } /* switch */
+ } /* while */
+
+
+ if ( missing_required_options )
+ exit (EXIT_FAILURE);
+
+ return 0;
+}
diff --git a/src/progs/om/cmdline.ggo b/src/progs/om/cmdline.ggo
new file mode 100644
index 00000000..8c006ca7
--- /dev/null
+++ b/src/progs/om/cmdline.ggo
@@ -0,0 +1,7 @@
+# Process this file with gengetopt -u to generate the necessary code (in cmdline.h, cmdline.c)
+
+package "Om - An OSC controlled realtime modular synthesizer"
+
+option "port" p "OSC port to listen on" string default="16180" no
+option "in-jackd" i "Run engine as in-process JACK client" flag off
+
diff --git a/src/progs/om/cmdline.h b/src/progs/om/cmdline.h
new file mode 100644
index 00000000..fe36a969
--- /dev/null
+++ b/src/progs/om/cmdline.h
@@ -0,0 +1,45 @@
+/* cmdline.h */
+
+/* File autogenerated by gengetopt version 2.10 */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+#define CMDLINE_PARSER_PACKAGE "Om - An OSC controlled realtime modular synthesizer"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+struct gengetopt_args_info
+{
+ char * port_arg; /* OSC port to listen on (default='16180'). */
+ int in_jackd_flag; /* Run engine as in-process JACK client (default=off). */
+
+ int help_given ; /* Whether help was given. */
+ int version_given ; /* Whether version was given. */
+ int port_given ; /* Whether port was given. */
+ int in_jackd_given ; /* Whether in-jackd was given. */
+
+} ;
+
+int cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info);
+
+void cmdline_parser_print_help(void);
+void cmdline_parser_print_version(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/src/progs/om/main.cpp b/src/progs/om/main.cpp
new file mode 100644
index 00000000..a69c7e76
--- /dev/null
+++ b/src/progs/om/main.cpp
@@ -0,0 +1,153 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <cstddef>
+#include <signal.h>
+#include "config.h"
+#include "util.h"
+#include "cmdline.h"
+#include "Om.h"
+#include "OmApp.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+#ifdef BUILD_IN_PROCESS_ENGINE
+#include <jack/jack.h>
+#include <jack/intclient.h>
+#endif
+
+using std::cout; using std::endl; using std::cerr;
+
+
+void
+catch_int(int)
+{
+ signal(SIGINT, catch_int);
+ signal(SIGTERM, catch_int);
+
+ std::cout << "[Main] Om interrupted." << std::endl;
+ Om::om->quit();
+}
+
+
+#ifdef BUILD_IN_PROCESS_ENGINE
+
+jack_client_t* jack_client;
+jack_intclient_t jack_intclient;
+
+
+void
+unload_in_process_engine(int)
+{
+ jack_status_t status;
+ int ret = EXIT_SUCCESS;
+
+ cout << "Unloading...";
+ status = jack_internal_client_unload(jack_client, jack_intclient);
+ if (status & JackFailure) {
+ cout << "failed" << endl;
+ ret = EXIT_FAILURE;
+ } else {
+ cout << "done" << endl;
+ }
+ jack_client_close(jack_client);
+ exit(ret);
+}
+
+
+int
+load_in_process_engine(const char* port)
+{
+ int ret = EXIT_SUCCESS;
+
+ jack_status_t status;
+
+ if ((jack_client = jack_client_open("om_load", JackNoStartServer,
+ &status)) != NULL) {
+ jack_intclient =
+ jack_internal_client_load(jack_client, "Om",
+ (jack_options_t)(JackLoadName|JackLoadInit),
+ &status, "om", port);
+ if (status == 0) {
+ cout << "Engine loaded" << endl;
+ signal(SIGINT, unload_in_process_engine);
+ signal(SIGTERM, unload_in_process_engine);
+
+ while (1) {
+ sleep(1);
+ }
+ } else if (status & JackFailure) {
+ cerr << "Could not load om.so" << endl;
+ ret = EXIT_FAILURE;
+ }
+
+ jack_client_close(jack_client);
+ } else {
+ cerr << "jack_client_open failed" << endl;
+ ret = EXIT_FAILURE;
+ }
+}
+
+#endif // BUILD_IN_PROCESS_ENGINE
+
+
+int
+main(int argc, char** argv)
+{
+#ifdef HAVE_LASH
+ lash_args_t* lash_args = lash_extract_args(&argc, &argv);
+#endif
+
+ int ret = EXIT_SUCCESS;
+
+ /* Parse command line options */
+ gengetopt_args_info args_info;
+ if (cmdline_parser (argc, argv, &args_info) != 0)
+ return EXIT_FAILURE;
+
+
+ if (args_info.in_jackd_flag) {
+#ifdef BUILD_IN_PROCESS_ENGINE
+ ret = load_in_process_engine(args_info.port_arg);
+#else
+ cerr << "In-process Jack client support not enabled in this build." << endl;
+ ret = EXIT_FAILURE;
+#endif // JACK_IN_PROCESS_ENGINE
+ } else {
+ signal(SIGINT, catch_int);
+ signal(SIGTERM, catch_int);
+
+ Om::set_denormal_flags();
+
+ Om::om = new Om::OmApp(args_info.port_arg);
+
+#ifdef HAVE_LASH
+ Om::lash_driver = new Om::LashDriver(Om::om, lash_args);
+#endif
+
+ Om::om->main();
+
+#ifdef HAVE_LASH
+ delete Om::lash_driver;
+#endif
+
+ delete Om::om;
+ }
+
+ return ret;
+}
+
diff --git a/src/progs/patch_loader/Makefile.am b/src/progs/patch_loader/Makefile.am
new file mode 100644
index 00000000..15e0c503
--- /dev/null
+++ b/src/progs/patch_loader/Makefile.am
@@ -0,0 +1,13 @@
+EXTRA_DIST = README
+
+om_patch_loader_CXXFLAGS = -I$(top_srcdir)/src/clients -I$(top_srcdir)/src/common -DPKGDATADIR=\"$(pkgdatadir)\" $(LXML2_CFLAGS) $(LOSC_CFLAGS)
+om_patch_loader_LDADD = ../libomclient.a $(LOSC_LIBS) $(LXML2_LIBS)
+
+bin_PROGRAMS = om_patch_loader
+
+om_patch_loader_DEPENDENCIES = ../libomclient.a
+
+om_patch_loader_SOURCES = \
+ patch_loader.cpp \
+ cmdline.h \
+ cmdline.c
diff --git a/src/progs/patch_loader/README b/src/progs/patch_loader/README
new file mode 100644
index 00000000..9f3b6f4b
--- /dev/null
+++ b/src/progs/patch_loader/README
@@ -0,0 +1,5 @@
+This is a stand-alone patch loader for Om. It has no user interface, it
+just launches, loads the patch(es) passed as a parameter, and exits.
+
+Useful for loading patches from scripts, other apps, etc. or just using
+Om patches without loading the GUI.
diff --git a/src/progs/patch_loader/cmdline.c b/src/progs/patch_loader/cmdline.c
new file mode 100644
index 00000000..0606cb7b
--- /dev/null
+++ b/src/progs/patch_loader/cmdline.c
@@ -0,0 +1,163 @@
+/*
+ File autogenerated by gengetopt version 2.10
+ generated with the following command:
+ gengetopt -u
+
+ The developers of gengetopt consider the fixed text that goes in all
+ gengetopt output files to be in the public domain:
+ we make no copyright claims on it.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#include "cmdline.h"
+
+void
+cmdline_parser_print_version (void)
+{
+ printf ("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION);
+}
+
+void
+cmdline_parser_print_help (void)
+{
+ cmdline_parser_print_version ();
+ printf("\n"
+ "Usage: %s [OPTIONS]... [FILES]...\n", CMDLINE_PARSER_PACKAGE);
+ printf(" -h --help Print help and exit\n");
+ printf(" -V --version Print version and exit\n");
+ printf(" -uSTRING --engine-url=STRING Om engine URL to connect to\n");
+ printf(" -pINT --client-port=INT Client port to listen on\n");
+}
+
+
+static char *gengetopt_strdup (const char *s);
+
+/* gengetopt_strdup() */
+/* strdup.c replacement of strdup, which is not standard */
+char *
+gengetopt_strdup (const char *s)
+{
+ char *result = (char*)malloc(strlen(s) + 1);
+ if (result == (char*)0)
+ return (char*)0;
+ strcpy(result, s);
+ return result;
+}
+
+int
+cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info)
+{
+ int c; /* Character of the parsed option. */
+ int missing_required_options = 0;
+
+ args_info->help_given = 0 ;
+ args_info->version_given = 0 ;
+ args_info->engine_url_given = 0 ;
+ args_info->client_port_given = 0 ;
+#define clear_args() { \
+ args_info->engine_url_arg = NULL; \
+}
+
+ clear_args();
+
+ args_info->inputs = NULL;
+ args_info->inputs_num = 0;
+
+ optarg = 0;
+ optind = 1;
+ opterr = 1;
+ optopt = '?';
+
+ while (1)
+ {
+ int option_index = 0;
+ char *stop_char;
+
+ static struct option long_options[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "engine-url", 1, NULL, 'u' },
+ { "client-port", 1, NULL, 'p' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ stop_char = 0;
+ c = getopt_long (argc, argv, "hVu:p:", long_options, &option_index);
+
+ if (c == -1) break; /* Exit from `while (1)' loop. */
+
+ switch (c)
+ {
+ case 'h': /* Print help and exit. */
+ clear_args ();
+ cmdline_parser_print_help ();
+ exit (EXIT_SUCCESS);
+
+ case 'V': /* Print version and exit. */
+ clear_args ();
+ cmdline_parser_print_version ();
+ exit (EXIT_SUCCESS);
+
+ case 'u': /* Om engine URL to connect to. */
+ if (args_info->engine_url_given)
+ {
+ fprintf (stderr, "%s: `--engine-url' (`-u') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->engine_url_given = 1;
+ args_info->engine_url_arg = gengetopt_strdup (optarg);
+ break;
+
+ case 'p': /* Client port to listen on. */
+ if (args_info->client_port_given)
+ {
+ fprintf (stderr, "%s: `--client-port' (`-p') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->client_port_given = 1;
+ args_info->client_port_arg = strtol (optarg,&stop_char,0);
+ break;
+
+
+ case 0: /* Long option with no short option */
+
+ case '?': /* Invalid option. */
+ /* `getopt_long' already printed an error message. */
+ exit (EXIT_FAILURE);
+
+ default: /* bug: option not considered. */
+ fprintf (stderr, "%s: option unknown: %c\n", CMDLINE_PARSER_PACKAGE, c);
+ abort ();
+ } /* switch */
+ } /* while */
+
+
+ if ( missing_required_options )
+ exit (EXIT_FAILURE);
+
+ if (optind < argc)
+ {
+ int i = 0 ;
+
+ args_info->inputs_num = argc - optind ;
+ args_info->inputs =
+ (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ;
+ while (optind < argc)
+ args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind++]) ;
+ }
+
+ return 0;
+}
diff --git a/src/progs/patch_loader/cmdline.ggo b/src/progs/patch_loader/cmdline.ggo
new file mode 100644
index 00000000..5acd6737
--- /dev/null
+++ b/src/progs/patch_loader/cmdline.ggo
@@ -0,0 +1,7 @@
+# Process this file with gengetopt -u to generate the necessary code (in cmdline.h, cmdline.c)
+
+package "om_patch_loader - A command line patch loading client for Om"
+
+option "engine-url" u "Om engine URL to connect to" string no
+option "client-port" p "Client port to listen on" int no
+
diff --git a/src/progs/patch_loader/cmdline.h b/src/progs/patch_loader/cmdline.h
new file mode 100644
index 00000000..b417c664
--- /dev/null
+++ b/src/progs/patch_loader/cmdline.h
@@ -0,0 +1,47 @@
+/* cmdline.h */
+
+/* File autogenerated by gengetopt version 2.10 */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+#define CMDLINE_PARSER_PACKAGE "om_patch_loader - A command line patch loading client for the Om realtime modular synthesizer"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+struct gengetopt_args_info
+{
+ char * engine_url_arg; /* Om engine URL to connect to. */
+ int client_port_arg; /* Client port to listen on. */
+
+ int help_given ; /* Whether help was given. */
+ int version_given ; /* Whether version was given. */
+ int engine_url_given ; /* Whether engine-url was given. */
+ int client_port_given ; /* Whether client-port was given. */
+
+ char **inputs ; /* unamed options */
+ unsigned inputs_num ; /* unamed options number */
+} ;
+
+int cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info);
+
+void cmdline_parser_print_help(void);
+void cmdline_parser_print_version(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/src/progs/patch_loader/new_patch_loader.cpp b/src/progs/patch_loader/new_patch_loader.cpp
new file mode 100644
index 00000000..8884e85b
--- /dev/null
+++ b/src/progs/patch_loader/new_patch_loader.cpp
@@ -0,0 +1,76 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <unistd.h>
+#include "cmdline.h" // generated by gengetopt
+#include "util/Path.h"
+#include "OSCEngineInterface.h"
+#include "PatchLibrarian.h"
+
+using std::cout; using std::cerr; using std::endl;
+using Om::Path;
+
+using namespace LibOmClient;
+
+
+int
+main(int argc, char** argv)
+{
+ const char* engine_url = NULL;
+ int client_port = 0;
+
+ /* Parse command line options */
+ gengetopt_args_info args_info;
+ if (cmdline_parser (argc, argv, &args_info) != 0)
+ return 1;
+
+ if (args_info.engine_url_given) {
+ engine_url = args_info.engine_url_arg;
+ } else {
+ cout << "[Main] No engine URL specified. Attempting to use osc.udp://localhost:16180" << endl;
+ engine_url = "osc.udp://localhost:16180";
+ }
+
+ if (args_info.client_port_given)
+ client_port = args_info.client_port_arg;
+ else
+ client_port = 0; // will choose a free port automatically
+
+ OSCEngineInterface engine(engine_url);
+
+ /* Connect to engine */
+ if (!engine.attach()) {
+ cerr << "Unable to connect to engine" << endl;
+ return 1;
+ }
+
+ engine.activate();
+ engine.register_client();
+
+ /*int id = engine.get_next_request_id();
+ engine.set_wait_response_id(id);
+ engine.load_plugins(id);
+ engine.wait_for_response();
+ */
+ engine.load_plugins();
+
+ // Load patches
+ for (uint i=0; i < args_info.inputs_num; ++i)
+ PatchLibrarian::load_patch(&engine, args_info.inputs[i]);
+
+ return 0;
+}
diff --git a/src/progs/patch_loader/patch_loader.cpp b/src/progs/patch_loader/patch_loader.cpp
new file mode 100644
index 00000000..e8579ed7
--- /dev/null
+++ b/src/progs/patch_loader/patch_loader.cpp
@@ -0,0 +1,82 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om 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.
+ *
+ * Om 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "OSCModelEngineInterface.h"
+#include "PatchLibrarian.h"
+#include "PatchModel.h"
+#include "util/Path.h"
+#include <iostream>
+#include <unistd.h>
+#include "cmdline.h" // generated by gengetopt
+
+using std::cout; using std::endl;
+using Om::Path;
+
+using namespace LibOmClient;
+
+int main(int argc, char** argv)
+{
+ const char* engine_url = NULL;
+ int client_port = 0;
+
+ /* **** Parse command line options **** */
+
+ gengetopt_args_info args_info;
+ if (cmdline_parser (argc, argv, &args_info) != 0)
+ return 1;
+
+ if (args_info.engine_url_given) {
+ engine_url = args_info.engine_url_arg;
+ } else {
+ cout << "[Main] No engine URL specified. Attempting to use osc.udp://localhost:16180" << endl;
+ engine_url = "osc.udp://localhost:16180";
+ }
+
+ if (args_info.client_port_given)
+ client_port = args_info.client_port_arg;
+
+
+ /* **** Mr. Spock.. Engage **** */
+
+
+ OSCModelEngineInterface osc_controller(engine_url);
+ PatchLibrarian librarian(&osc_controller);
+
+ /* Connect to engine */
+ osc_controller.attach(client_port);
+ osc_controller.activate();
+ //osc_controller.register_client(NULL); // FIXME
+
+ //int id = osc_controller.get_next_request_id();
+ //osc_controller.set_wait_response_id(id);
+ //osc_controller.load_plugins(id);
+ //osc_controller.wait_for_response();
+ /* FIXME: Make this work like this:
+ * osc_controller.load_plugins();
+ * osc_controller.wait_for_response();
+ */
+
+ // Load patches
+ for (uint i=0; i < args_info.inputs_num; ++i) {
+ PatchModel* pm = new PatchModel("", 0);
+ pm->filename(args_info.inputs[i]);
+ librarian.load_patch(pm, true);
+ delete pm;
+ }
+
+ return 0;
+}
diff --git a/src/progs/python/Makefile.am b/src/progs/python/Makefile.am
new file mode 100644
index 00000000..017b1f4b
--- /dev/null
+++ b/src/progs/python/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = scripts
+
+EXTRA_DIST = omecho.py omsynth.py OSC.py
+
diff --git a/src/progs/python/OSC.py b/src/progs/python/OSC.py
new file mode 100755
index 00000000..74eb5880
--- /dev/null
+++ b/src/progs/python/OSC.py
@@ -0,0 +1,374 @@
+#!/usr/bin/python
+#
+# Open SoundControl for Python
+# Copyright (C) 2002 Daniel Holth, Clinton McChesney
+# Modified by Leonard Ritter
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# For questions regarding this module contact
+# Daniel Holth <dholth@stetson.edu> or visit
+# http://www.stetson.edu/~ProctoLogic/
+
+import socket
+import struct
+import math
+import sys
+import string
+
+
+def hexDump(bytes):
+ """Useful utility; prints the string in hexadecimal"""
+ for i in range(len(bytes)):
+ sys.stdout.write("%2x " % (ord(bytes[i])))
+ if (i+1) % 8 == 0:
+ print repr(bytes[i-7:i+1])
+
+ if(len(bytes) % 8 != 0):
+ print string.rjust("", 11), repr(bytes[i-7:i+1])
+
+
+class OSCMessage:
+ """Builds typetagged OSC messages."""
+ def __init__(self):
+ self.address = ""
+ self.typetags = ","
+ self.message = ""
+
+ def setAddress(self, address):
+ self.address = address
+
+ def setMessage(self, message):
+ self.message = message
+
+ def setTypetags(self, typetags):
+ self.typetags = typetags
+
+ def clear(self):
+ self.address = ""
+ self.clearData()
+
+ def clearData(self):
+ self.typetags = ","
+ self.message = ""
+
+ def append(self, argument, typehint = None):
+ """Appends data to the message,
+ updating the typetags based on
+ the argument's type.
+ If the argument is a blob (counted string)
+ pass in 'b' as typehint."""
+
+ if typehint == 'b':
+ binary = OSCBlob(argument)
+ else:
+ binary = OSCArgument(argument)
+
+ self.typetags = self.typetags + binary[0]
+ self.rawAppend(binary[1])
+
+ def rawAppend(self, data):
+ """Appends raw data to the message. Use append()."""
+ self.message = self.message + data
+
+ def getBinary(self):
+ """Returns the binary message (so far) with typetags."""
+ address = OSCArgument(self.address)[1]
+ typetags = OSCArgument(self.typetags)[1]
+ return address + typetags + self.message
+
+ def __repr__(self):
+ return self.getBinary()
+
+def readString(data):
+ length = string.find(data,"\0")
+ nextData = int(math.ceil((length+1) / 4.0) * 4)
+ return (data[0:length], data[nextData:])
+
+
+def readBlob(data):
+ length = struct.unpack(">i", data[0:4])[0]
+ nextData = int(math.ceil((length) / 4.0) * 4) + 4
+ return (data[4:length+4], data[nextData:])
+
+
+def readInt(data):
+ if(len(data)<4):
+ print "Error: too few bytes for int", data, len(data)
+ rest = data
+ integer = 0
+ else:
+ integer = struct.unpack(">i", data[0:4])[0]
+ rest = data[4:]
+
+ return (integer, rest)
+
+
+
+def readLong(data):
+ """Tries to interpret the next 8 bytes of the data
+ as a 64-bit signed integer."""
+ high, low = struct.unpack(">ll", data[0:8])
+ big = (long(high) << 32) + low
+ rest = data[8:]
+ return (big, rest)
+
+
+
+def readFloat(data):
+ if(len(data)<4):
+ print "Error: too few bytes for float", data, len(data)
+ rest = data
+ float = 0
+ else:
+ float = struct.unpack(">f", data[0:4])[0]
+ rest = data[4:]
+
+ return (float, rest)
+
+
+def OSCBlob(next):
+ """Convert a string into an OSC Blob,
+ returning a (typetag, data) tuple."""
+
+ if type(next) == type(""):
+ length = len(next)
+ padded = math.ceil((len(next)) / 4.0) * 4
+ binary = struct.pack(">i%ds" % (padded), length, next)
+ tag = 'b'
+ else:
+ tag = ''
+ binary = ''
+
+ return (tag, binary)
+
+
+def OSCArgument(next):
+ """Convert some Python types to their
+ OSC binary representations, returning a
+ (typetag, data) tuple."""
+
+ if type(next) == type(""):
+ OSCstringLength = math.ceil((len(next)+1) / 4.0) * 4
+ binary = struct.pack(">%ds" % (OSCstringLength), next)
+ tag = "s"
+ elif type(next) == type(42.5):
+ binary = struct.pack(">f", next)
+ tag = "f"
+ elif type(next) == type(13):
+ binary = struct.pack(">i", next)
+ tag = "i"
+ else:
+ binary = ""
+ tag = ""
+
+ return (tag, binary)
+
+
+def parseArgs(args):
+ """Given a list of strings, produces a list
+ where those strings have been parsed (where
+ possible) as floats or integers."""
+ parsed = []
+ for arg in args:
+ print arg
+ arg = arg.strip()
+ interpretation = None
+ try:
+ interpretation = float(arg)
+ if string.find(arg, ".") == -1:
+ interpretation = int(interpretation)
+ except:
+ # Oh - it was a string.
+ interpretation = arg
+ pass
+ parsed.append(interpretation)
+ return parsed
+
+
+
+def decodeOSC(data):
+ """Converts a typetagged OSC message to a Python list."""
+ table = {"i":readInt, "f":readFloat, "s":readString, "b":readBlob}
+ decoded = []
+ address, rest = readString(data)
+ typetags = ""
+
+ if address == "#bundle":
+ time, rest = readLong(rest)
+ decoded.append(address)
+ decoded.append(time)
+ while len(rest)>0:
+ length, rest = readInt(rest)
+ decoded.append(decodeOSC(rest[:length]))
+ rest = rest[length:]
+
+ elif len(rest)>0:
+ typetags, rest = readString(rest)
+ decoded.append(address)
+ decoded.append(typetags)
+ if(typetags[0] == ","):
+ for tag in typetags[1:]:
+ value, rest = table[tag](rest)
+ decoded.append(value)
+ else:
+ print "Oops, typetag lacks the magic ,"
+
+ # return only the data
+ return address,decoded[2:]
+
+
+class CallbackManager:
+ """This utility class maps OSC addresses to callables.
+
+ The CallbackManager calls its callbacks with a list
+ of decoded OSC arguments, including the address and
+ the typetags as the first two arguments."""
+
+ def __init__(self):
+ self.callbacks = {}
+ self.addCallback(self.unbundler, "#bundle")
+
+ def handle(self, data, source = None):
+ """Given OSC data, tries to call the callback with the
+ right address."""
+ decoded = decodeOSC(data)
+ self.dispatch(decoded)
+
+ def dispatch(self, message):
+ """Sends decoded OSC data to an appropriate calback"""
+ try:
+ address = message[0]
+ self.callbacks[address](message)
+ except KeyError, e:
+ # address not found
+ print "no handler for '%s'" % address
+ pass
+ except None, e:
+ print "Exception in", address, "callback :", e
+
+ return
+
+ def addCallback(self, callback, name):
+ """Adds a callback to our set of callbacks,
+ or removes the callback with name if callback
+ is None."""
+ if callback == None:
+ del self.callbacks[name]
+ else:
+ self.callbacks[name] = callback
+
+ def unbundler(self, messages):
+ """Dispatch the messages in a decoded bundle."""
+ # first two elements are #bundle and the time tag, rest are messages.
+ for message in messages[2:]:
+ self.dispatch(message)
+
+
+if __name__ == "__main__":
+ hexDump("Welcome to the OSC testing program.")
+ print
+ message = OSCMessage()
+ message.setAddress("/foo/play")
+ message.append(44)
+ message.append(11)
+ message.append(4.5)
+ message.append("the white cliffs of dover")
+ hexDump(message.getBinary())
+
+ print "Making and unmaking a message.."
+
+ strings = OSCMessage()
+ strings.append("Mary had a little lamb")
+ strings.append("its fleece was white as snow")
+ strings.append("and everywhere that Mary went,")
+ strings.append("the lamb was sure to go.")
+ strings.append(14.5)
+ strings.append(14.5)
+ strings.append(-400)
+
+ raw = strings.getBinary()
+
+ hexDump(raw)
+
+ print "Retrieving arguments..."
+ data = raw
+ for i in range(6):
+ text, data = readString(data)
+ print text
+
+ number, data = readFloat(data)
+ print number
+
+ number, data = readFloat(data)
+ print number
+
+ number, data = readInt(data)
+ print number
+
+ hexDump(raw)
+ print decodeOSC(raw)
+ print decodeOSC(message.getBinary())
+
+ print "Testing Blob types."
+
+ blob = OSCMessage()
+ blob.append("","b")
+ blob.append("b","b")
+ blob.append("bl","b")
+ blob.append("blo","b")
+ blob.append("blob","b")
+ blob.append("blobs","b")
+ blob.append(42)
+
+ hexDump(blob.getBinary())
+
+ print decodeOSC(blob.getBinary())
+
+ def printingCallback(stuff):
+ sys.stdout.write("Got: ")
+ for i in stuff:
+ sys.stdout.write(str(i) + " ")
+ sys.stdout.write("\n")
+
+ print "Testing the callback manager."
+
+ c = CallbackManager()
+ c.add(printingCallback, "/print")
+
+ c.handle(message.getBinary())
+ message.setAddress("/print")
+ c.handle(message.getBinary())
+
+ print1 = OSCMessage()
+ print1.setAddress("/print")
+ print1.append("Hey man, that's cool.")
+ print1.append(42)
+ print1.append(3.1415926)
+
+ c.handle(print1.getBinary())
+
+ bundle = OSCMessage()
+ bundle.setAddress("")
+ bundle.append("#bundle")
+ bundle.append(0)
+ bundle.append(0)
+ bundle.append(print1.getBinary(), 'b')
+ bundle.append(print1.getBinary(), 'b')
+
+ bundlebinary = bundle.message
+
+ print "sending a bundle to the callback manager"
+ c.handle(bundlebinary)
diff --git a/src/progs/python/omecho.py b/src/progs/python/omecho.py
new file mode 100644
index 00000000..c0d2d3b1
--- /dev/null
+++ b/src/progs/python/omecho.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+#
+# Python bindings for Om
+# Copyright (C) 2005 Leonard Ritter
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import omsynth
+import os,time,sys
+
+def main(om):
+ om.setEnvironment(omsynth.Environment())
+ om.engine.activate()
+ om.engine.register_client(om.getAddressAsString())
+ om.request.all_objects(om.getAddressAsString())
+
+ om.request.all_objects()
+ time.sleep(3)
+ om.getEnvironment().printPatch()
+ om.getEnvironment().printConnections()
+ print "omecho will now monitor and mirror changes in the structure"
+ print "hit return to exit when youre done"
+ sys.stdin.readline()
+ om.engine.unregister_client(om.getAddressAsString())
+ os._exit(0)
+
+if __name__ == "__main__":
+ omsynth.startClient(main)
diff --git a/src/progs/python/omsynth.py b/src/progs/python/omsynth.py
new file mode 100644
index 00000000..d7cfa5ab
--- /dev/null
+++ b/src/progs/python/omsynth.py
@@ -0,0 +1,635 @@
+#!/usr/bin/python
+#
+# Python bindings for Om
+# Copyright (C) 2005 Leonard Ritter
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import os,sys,thread,time
+
+from OSC import OSCMessage, decodeOSC
+
+from twisted.internet.protocol import DatagramProtocol
+from twisted.internet import reactor
+
+OM_CALL_TIMEOUT = 5
+OM_CALL_POLLTIME = 0.1
+
+class TreeElement:
+ def __init__(self,environment,parent,name):
+ self.environment = environment
+ self.parent = parent
+ self.name = name
+ self.metadata = {}
+
+ def __del__(self):
+ print "'%s': gone" % self.name
+
+ def removeChild(self, child):
+ pass
+
+ def remove(self):
+ self.parent.removeChild(self.name)
+ self.parent = None
+ del self
+
+ def getParent(self):
+ return self.parent
+
+ def getName(self):
+ return self.name
+
+ def getPath(self):
+ if self.parent:
+ return self.parent.getPath() + "/" + self.name
+ else:
+ return self.name
+
+ def getDepth(self):
+ if self.parent:
+ return self.parent.getDepth() + 1
+ else:
+ return 0
+
+ def setMetaData(self,key,value):
+ if (not self.metadata.has_key(value)) or (self.metadata[key] != value):
+ print "||| '%s': '%s' = '%s'" % (self.getPath(), key, value)
+ self.metadata[key] = value
+
+class Port(TreeElement):
+ def __init__(self,environment,parent,name):
+ TreeElement.__init__(self,environment,parent,name)
+ self.porttype = ""
+ self.direction = ""
+ self.hint = ""
+ self.defaultvalue = 0.0
+ self.minvalue = 0.0
+ self.maxvalue = 0.0
+ self.value = 0.0
+ self.connections = {}
+ print "*** '%s': new port" % self.getPath()
+
+ def remove(self):
+ for connection in self.getConnections():
+ connection.remove()
+ TreeElement.remove(self)
+
+ def getConnections(self):
+ return self.connections
+
+ def addConnection(self,target,connection):
+ self.connections[target] = connection
+
+ def removeConnection(self,target):
+ del self.connections[target]
+
+ def setPortType(self,porttype):
+ if self.porttype != porttype:
+ print "*** '%s': changing porttype from '%s' to '%s'" % (self.getPath(), self.porttype, porttype)
+ self.porttype = porttype
+
+ def setDirection(self,direction):
+ if self.direction != direction:
+ print "*** '%s': changing direction from '%s' to '%s'" % (self.getPath(), self.direction, direction)
+ self.direction = direction
+
+ def setHint(self,hint):
+ if self.hint != hint:
+ print "*** '%s': changing hint from '%s' to '%s'" % (self.getPath(), self.hint, hint)
+ self.hint = hint
+
+ def setDefaultValue(self,defaultvalue):
+ if self.defaultvalue != defaultvalue:
+ print "*** '%s': changing defaultvalue from '%.3f' to '%.3f'" % (self.getPath(), self.defaultvalue, defaultvalue)
+ self.defaultvalue = defaultvalue
+
+ def setMinValue(self,minvalue):
+ if self.minvalue != minvalue:
+ print "*** '%s': changing minvalue from '%.3f' to '%.3f'" % (self.getPath(), self.minvalue, minvalue)
+ self.minvalue = minvalue
+
+ def setMaxValue(self,maxvalue):
+ if self.maxvalue != maxvalue:
+ print "*** '%s': changing maxvalue from '%.3f' to '%.3f'" % (self.getPath(), self.maxvalue, maxvalue)
+ self.maxvalue = maxvalue
+
+ def setValue(self,value):
+ if self.value != value:
+ print "*** '%s': changing value from '%.3f' to '%.3f'" % (self.getPath(), self.value, value)
+ self.value = value
+
+class Node(TreeElement):
+ def __init__(self,environment,parent,name):
+ TreeElement.__init__(self,environment,parent,name)
+ self.ports = {}
+ self.polyphonic = 0
+ self.plugintype = ""
+ self.libname = ""
+ self.pluginlabel = ""
+ print "+++ '%s': new node" % self.getPath()
+
+ def remove(self):
+ for port in self.getPorts():
+ port.remove()
+ TreeElement.remove(self)
+
+ def removeChild(self, child):
+ del self.ports[child]
+
+ def getPorts(self):
+ return self.ports.values()
+
+ def setPluginLabel(self,pluginlabel):
+ if pluginlabel != self.pluginlabel:
+ print "+++ '%s': changing pluginlabel from '%s' to '%s'" % (self.getPath(), self.pluginlabel, pluginlabel)
+ self.pluginlabel = pluginlabel
+
+ def setLibName(self,libname):
+ if libname != self.libname:
+ print "+++ '%s': changing libname from '%s' to '%s'" % (self.getPath(), self.libname, libname)
+ self.libname = libname
+
+ def setPluginType(self,plugintype):
+ if plugintype != self.plugintype:
+ print "+++ '%s': changing plugintype from '%s' to '%s'" % (self.getPath(), self.plugintype, plugintype)
+ self.plugintype = plugintype
+
+ def setPolyphonic(self,polyphonic):
+ if polyphonic != self.polyphonic:
+ print "+++ '%s': changing polyphony from %i to %i" % (self.getPath(), self.polyphonic, polyphonic)
+ self.polyphonic = polyphonic
+
+ def hasPort(self,name):
+ return self.ports.has_key(name)
+
+ def getPort(self,name,mustexist=False):
+ if not self.hasPort(name):
+ if mustexist:
+ return None
+ self.ports[name] = self.environment.getPortClass()(self.environment,self,name)
+ return self.ports[name]
+
+class Patch(Node):
+ def __init__(self,environment,parent,name):
+ Node.__init__(self,environment,parent,name)
+ self.nodes = {}
+ self.patches = {}
+ self.poly = 0
+ self.enabled = False
+ print "### '%s': new patch" % self.getPath()
+
+ def remove(self):
+ for patch in self.getPatches():
+ patch.remove()
+ for node in self.getNodes():
+ node.remove()
+ Node.remove(self)
+
+ def removeChild(self, child):
+ if self.hasNode(child):
+ del self.nodes[child]
+ elif self.hasPatch(child):
+ del self.patches[child]
+ else:
+ Node.removeChild(self,child)
+
+ def getPatches(self):
+ return self.patches.values()
+
+ def getNodes(self):
+ return self.nodes.values()
+
+ def getEnabled(self):
+ return self.enabled
+
+ def setEnabled(self,enabled):
+ if enabled != self.enabled:
+ print "### '%s': changing enabled from %s to %s" % (self.getPath(), str(self.enabled), str(enabled))
+ enabled = self.enabled
+
+ def getPoly(self):
+ return self.poly
+
+ def setPoly(self,poly):
+ if poly != self.poly:
+ print "### '%s': changing polyphony from %i to %i" % (self.getPath(), self.poly, poly)
+ self.poly = poly
+
+ def hasNode(self,name):
+ return self.nodes.has_key(name)
+
+ def getNode(self,name,mustexist=False):
+ if not self.hasNode(name):
+ if mustexist:
+ return None
+ self.nodes[name] = self.environment.getNodeClass()(self.environment,self,name)
+ return self.nodes[name]
+
+ def hasPatch(self,name):
+ return self.patches.has_key(name)
+
+ def getPatch(self,name,mustexist=False):
+ if not self.hasPatch(name):
+ if mustexist:
+ return None
+ self.patches[name] = self.environment.getPatchClass()(self.environment,self,name)
+ return self.patches[name]
+
+class Connection:
+ def __init__(self,environment,srcport,dstport):
+ self.environment = environment
+ self.srcport = srcport
+ self.dstport = dstport
+ self.srcport.addConnection(self.dstport,self)
+ self.dstport.addConnection(self.srcport,self)
+ print ">>> '%s'->'%s': new connection" % (self.srcport.getPath(),self.dstport.getPath())
+
+ def __del__(self):
+ print "connection gone"
+
+ def remove(self):
+ self.srcport.removeConnection(self.dstport)
+ self.dstport.removeConnection(self.srcport)
+ del self
+
+ def getSrcPort(self):
+ return self.srcport
+
+ def getDstPort(self):
+ return self.dstport
+
+ def getPortPair(self):
+ return self.srcport, self.dstport
+
+class Environment:
+ def __init__(self):
+ self.omPatch = self.getPatchClass()(self,None,"")
+ self.enabled = False
+ self.connections = {}
+
+ def getConnectionClass(self):
+ return Connection
+
+ def getPatchClass(self):
+ return Patch
+
+ def getNodeClass(self):
+ return Node
+
+ def getPortClass(self):
+ return Port
+
+ def getConnection(self,srcportpath,dstportpath,mustexist=False):
+ srcport = self.getPort(srcportpath,True)
+ if not srcport:
+ return None
+ dstport = self.getPort(dstportpath,True)
+ if not dstport:
+ return None
+ if not self.connections.has_key((srcport,dstport)):
+ if mustexist:
+ return None
+ self.connections[(srcport,dstport)] = self.getConnectionClass()(self,srcport,dstport)
+ return self.connections[(srcport,dstport)]
+
+ def getConnections(self):
+ return self.connections.values()
+
+ def getPatch(self,path,mustexist=False):
+ elements = path.split("/")
+ currentPatch = None
+ for element in elements:
+ if element == "":
+ currentPatch = self.omPatch
+ else:
+ currentPatch = currentPatch.getPatch(element,mustexist)
+ if not currentPatch:
+ break
+ return currentPatch
+
+ def getNode(self,path,mustexist=False):
+ elements = path.split("/")
+ basepath = "/".join(elements[:-1])
+ nodename = elements[-1]
+ patch = self.getPatch(basepath,True)
+ if patch:
+ return patch.getNode(nodename,mustexist)
+ return None
+
+ def getPort(self,path,mustexist=False):
+ elements = path.split("/")
+ basepath = "/".join(elements[:-1])
+ portname = elements[-1]
+ node = self.getNode(basepath,True)
+ if node:
+ return node.getPort(portname,mustexist)
+ patch = self.getPatch(basepath,True)
+ if patch:
+ return patch.getPort(portname,mustexist)
+ return None
+
+ def getObject(self,path):
+ patch = self.getPatch(path,True)
+ if patch:
+ return patch
+ node = self.getNode(path,True)
+ if node:
+ return node
+ return self.getPort(path,True)
+
+ def printPatch(self,patch=None):
+ if not patch:
+ patch = self.omPatch
+ print patch.getDepth()*' ' + "### " + patch.getPath()
+ for node in patch.getNodes():
+ print node.getDepth()*' ' + "+++ " + node.getPath()
+ for port in node.getPorts():
+ print port.getDepth()*' ' + "*** " + port.getPath()
+ for port in patch.getPorts():
+ print port.getDepth()*' ' + "*** " + port.getPath()
+ for subpatch in patch.getPatches():
+ self.printPatch(subpatch)
+
+ def printConnections(self):
+ for connection in self.getConnections():
+ print ">>> %s -> %s" % (connection.getSrcPort().getPath(), connection.getDstPort().getPath())
+
+ #~ /om/engine_enabled - Notification engine's DSP has been enabled.
+ def __om__engine_enabled(self):
+ self.enabled = True
+
+ #~ /om/engine_disabled - Notification engine's DSP has been disabled.
+ def __om__engine_disabled(self):
+ self.enabled = False
+
+ #~ /om/new_node - Notification of a new node's creation.
+ #~ * path (string) - Path of the new node
+ #~ * polyphonic (integer-boolean) - Node is polyphonic (1 for yes, 0 for no)
+ #~ * type (string) - Type of plugin (LADSPA, DSSI, Internal, Patch)
+ #~ * lib-name (string) - Name of library if a plugin (ie cmt.so)
+ #~ * plug-label (string) - Label of plugin in library (ie adsr_env)
+
+ #~ * New nodes are sent as a blob. The first message in the blob will be this one (/om/new_node), followed by a series of /om/new_port commands, followed by /om/new_node_end.
+ def __om__new_node(self,path,polyphonic,plugintype,libname,pluginlabel):
+ node = self.getNode(path)
+ node.setPolyphonic(polyphonic)
+ node.setPluginType(plugintype)
+ node.setLibName(libname)
+ node.setPluginLabel(pluginlabel)
+
+ def __om__new_node_end(self):
+ pass
+
+ #~ /om/node_removal - Notification of a node's destruction.
+ #~ * path (string) - Path of node (which no longer exists)
+ def __om__node_removal(self,path):
+ node = self.getNode(path)
+ node.remove()
+
+ #~ /om/new_port - Notification of a node's destruction.
+
+ #~ * path (string) - Path of new port
+ #~ * type (string) - Type of port (CONTROL or AUDIO)
+ #~ * direction (string) - Direction of data flow (INPUT or OUTPUT)
+ #~ * hint (string) - Hint (INTEGER, LOGARITHMIC, TOGGLE, or NONE)
+ #~ * default-value (float) - Default (initial) value
+ #~ * min-value (float) - Suggested minimum value
+ #~ * min-value (float) - Suggested maximum value
+
+ #~ * Note that in the event of loading a patch, this message could be followed immediately by a control change, meaning the default-value is not actually the current value of the port (ahem, Lachlan).
+ #~ * The minimum and maximum values are suggestions only, they are not enforced in any way, and going outside them is perfectly fine. Also note that the port ranges in om_gtk are not these ones! Those ranges are set as metadata.
+ def __om__new_port(self,path,porttype,direction,hint,defaultvalue,minvalue,maxvalue):
+ port = self.getPort(path)
+ port.setPortType(porttype)
+ port.setDirection(direction)
+ port.setHint(hint)
+ port.setDefaultValue(defaultvalue)
+ port.setMinValue(minvalue)
+ port.setMaxValue(maxvalue)
+
+ #~ /om/port_removal - Notification of a port's destruction.
+ #~ * path (string) - Path of port (which no longer exists)
+ def __om__port_removal(self,path):
+ port = self.getPort(path)
+ port.remove()
+
+ #~ /om/patch_destruction - Notification of a patch's destruction.
+ #~ * path (string) - Path of patch (which no longer exists)
+ def __om__patch_destruction(self,path):
+ patch = self.getPatch(path)
+ patch.remove()
+
+ #~ /om/patch_enabled - Notification a patch's DSP processing has been enabled.
+ #~ * path (string) - Path of enabled patch
+ def __om__patch_enabled(self,path):
+ patch = self.getPatch(path)
+ patch.setEnabled(True)
+
+ #~ /om/patch_disabled - Notification a patch's DSP processing has been disabled.
+ #~ * path (string) - Path of disabled patch
+ def __om__patch_disabled(self,path):
+ patch = self.getPatch(path)
+ patch.setEnabled(False)
+
+ #~ /om/new_connection - Notification a new connection has been made.
+ #~ * src-path (string) - Path of the source port
+ #~ * dst-path (string) - Path of the destination port
+ def __om__new_connection(self,srcpath,dstpath):
+ self.getConnection(srcpath,dstpath)
+
+ #~ /om/disconnection - Notification a connection has been unmade.
+ #~ * src-path (string) - Path of the source port
+ #~ * dst-path (string) - Path of the destination port
+ def __om__disconnection(self,srcpath,dstpath):
+ connection = self.getConnection(srcpath,dstpath)
+ portpair = connection.getPortPair()
+ connection.remove()
+ del self.connections[portpair]
+
+ #~ /om/metadata/update - Notification of a piece of metadata.
+ #~ * path (string) - Path of the object associated with metadata (can be a node, patch, or port)
+ #~ * key (string)
+ #~ * value (string)
+ def __om__metadata__update(self,path,key,value):
+ object = self.getObject(path)
+ object.setMetaData(key,value)
+
+ #~ /om/control_change - Notification the value of a port has changed
+ #~ * path (string) - Path of port
+ #~ * value (float) - New value of port
+ #~ * This will only send updates for values set by clients of course - not values changing because of connections to other ports!
+ def __om__control_change(self,path,value):
+ port = self.getPort(path)
+ port.setValue(value)
+
+ #~ /om/new_patch - Notification of a new patch
+ #~ * path (string) - Path of new patch
+ #~ * poly (int) - Polyphony of new patch (not a boolean like new_node)
+ def __om__new_patch(self,path,poly):
+ patch = self.getPatch(path)
+ patch.setPoly(poly)
+
+class SocketError:
+ pass
+
+class Call:
+ pass
+
+class ClientProxy:
+ def __init__(self, om, name, is_async = False):
+ self.name = name
+ self.om = om
+ self.is_async = is_async
+
+ def __call__(self, *args):
+ if (self.is_async):
+ self.om.sendMsg(self.name, *args)
+ return True
+
+ result = self.om.sendMsgBlocking(self.name, *args)
+ if not result:
+ return None
+ if result[0] == "/om/response/ok":
+ return True
+ print "ERROR: %s" % result[1][1]
+ return False
+
+ def __getattr__(self, name):
+ if (name[:2] == "__") and (name[-2:] == "__"): # special function
+ raise AttributeError, name
+ if name in self.__dict__:
+ raise AttributeError, name
+ if name == 'async':
+ return ClientProxy(self.om, self.name, True)
+ else:
+ return ClientProxy(self.om, self.name + '/' + name)
+
+class Client(DatagramProtocol, ClientProxy):
+ def __init__(self):
+ ClientProxy.__init__(self, self, "/om")
+
+ def startProtocol(self):
+ self.transport.connect("127.0.0.1", 16180)
+ host = self.transport.getHost()
+ self.host = host.host
+ self.port = host.port
+ self.environment = None
+ self.handlers = {}
+ self.calls = {}
+ #print "opened port at %s" % (str((self.host,self.port)))
+ self.nextPacketNumber = 1
+
+ def setEnvironment(self, environment):
+ self.handlers = {}
+ self.environment = environment
+ for name in dir(self.environment):
+ element = getattr(self.environment,name)
+ if (type(element).__name__ == 'instancemethod') and (element.__name__[:6] == "__om__"):
+ handlername = element.__name__.replace("__","/")
+ print "registering handler for '%s'" % handlername
+ self.handlers[handlername] = element
+
+ def getEnvironment(self):
+ return self.environment
+
+ def connectionRefused(self):
+ print "Noone listening, aborting."
+ os._exit(-1)
+
+ def messageReceived(self, (msg, args)):
+ if msg == "/om/error":
+ print "ERROR: %r" % args
+ return
+ if msg == "/om/response/ok":
+ omcall = self.calls[args[0]]
+ omcall.result = (msg,args)
+ omcall.done = True
+ return
+ if msg == "/om/response/error":
+ omcall = self.calls[args[0]]
+ omcall.result = (msg,args)
+ omcall.done = True
+ return
+ if msg == "#bundle":
+ for arg in args:
+ self.messageReceived(arg)
+ return
+ if self.handlers.has_key(msg):
+ try:
+ self.handlers[msg](*args)
+ except:
+ a,b,c = sys.exc_info()
+ sys.excepthook(a,b,c)
+ print "with '%s'" % repr((msg,args))
+ return
+ print "no handler for '%s'" % repr((msg,args))
+
+ def datagramReceived(self, data, (host, port)):
+ self.messageReceived(decodeOSC(data))
+
+ def getPacketNumber(self):
+ packetNumber = self.nextPacketNumber
+ self.nextPacketNumber = (self.nextPacketNumber + 1)
+ return packetNumber
+
+ def sendMsg(self,msg,*args):
+ packetNumber = self.getPacketNumber()
+ print "Sending %r (#%i)..." % (msg,packetNumber)
+ omcall = Call()
+ omcall.result = None
+ omcall.done = False
+ self.calls[packetNumber] = omcall
+ message = OSCMessage()
+ message.setAddress(msg)
+ message.append(packetNumber)
+ for arg in args:
+ message.append(arg)
+ self.transport.write(message.getBinary())
+ time.sleep(0.01)
+ return True
+
+ def sendMsgBlocking(self,msg,*args):
+ packetNumber = self.getPacketNumber()
+ print "Sending %r (#%i)..." % (msg,packetNumber)
+ omcall = Call()
+ omcall.result = None
+ omcall.done = False
+ self.calls[packetNumber] = omcall
+ message = OSCMessage()
+ message.setAddress(msg)
+ message.append(packetNumber)
+ for arg in args:
+ message.append(arg)
+ self.transport.write(message.getBinary())
+ now = time.time()
+ while not omcall.done:
+ time.sleep(OM_CALL_POLLTIME)
+ distance = time.time() - now
+ if distance > OM_CALL_TIMEOUT:
+ print "timeout"
+ break
+ del self.calls[packetNumber]
+ return omcall.result
+
+ def getAddressAsString(self):
+ return "osc.udp://%s:%d" % (self.host, self.port)
+
+
+
+def startClient(func):
+ om = Client()
+ reactor.listenUDP(0, om)
+ thread.start_new_thread(func,(om,))
+ reactor.run()
diff --git a/src/progs/python/scripts/Makefile.am b/src/progs/python/scripts/Makefile.am
new file mode 100644
index 00000000..3e070601
--- /dev/null
+++ b/src/progs/python/scripts/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = sillysinepatch.py
+
diff --git a/src/progs/python/scripts/flatten.py b/src/progs/python/scripts/flatten.py
new file mode 100755
index 00000000..eaf8084d
--- /dev/null
+++ b/src/progs/python/scripts/flatten.py
@@ -0,0 +1,232 @@
+#!/usr/bin/python
+
+###############################################################################
+#
+# flatten.py - a python script that merges all subpatches in an Om patch
+# into the parent patch
+#
+# Copyright (C) 2005 Lars Luthman
+#
+# This program 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.
+#
+# This program 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 more 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+###############################################################################
+
+import omsynth
+import os,time,sys
+
+
+def getPatchBounds(patch):
+ """Returns the smallest rectangle that contains all modules in the patch."""
+ min_x = None
+ min_y = None
+ max_x = None
+ max_y = None
+ for node in patch.getNodes():
+ x = node.metadata['module-x']
+ y = node.metadata['module-y']
+ if (x != None):
+ if (min_x == None or float(x) < min_x):
+ min_x = float(x)
+ if (max_x == None or float(x) > max_x):
+ max_x = float(x)
+ if (y != None):
+ if (min_y == None or float(y) < min_y):
+ min_y = float(y)
+ if (max_y == None or float(y) > max_y):
+ max_y = float(y)
+ if min_x == None:
+ min_x = 0
+ max_x = 0
+ if min_y == None:
+ min_y = 0
+ max_y = 0
+
+ return (min_x, min_y, max_x, max_y)
+
+
+def cloneNode(om, node, patch):
+ """Copy a node into a patch, return the new node's name."""
+
+ # create a node with a free name in the parent
+ names = []
+ for node2 in patch.getNodes():
+ names.append(node2.getName())
+ for patch2 in patch.getPatches():
+ names.append(patch2.getName())
+ names.sort()
+ name = node.getName()
+ for name2 in names:
+ if name2 == name:
+ name = name + '_'
+ om.synth.create_node.async(patch.getPath() + '/' + name,
+ node.plugintype, node.libname,
+ node.pluginlabel, node.polyphonic)
+
+ # copy port values
+ for port in node.getPorts():
+ path = '%s/%s/%s' % (patch.getPath(), name, port.getName())
+ om.synth.set_port_value_slow.async(path, port.value)
+ om.metadata.set.async(path, 'user-min', '%f' % port.minvalue)
+ om.metadata.set.async(path, 'user-max', '%f' % port.maxvalue)
+ return name
+
+
+def flatten(om, patch):
+ """Merge all subpatches into the parent patch."""
+
+ # something is wrong, we don't have a patch
+ if patch == None:
+ return
+
+ # iterate over all subpatches
+ for subpatch in patch.getPatches():
+ flatten(om, subpatch)
+ lookup = {}
+
+ # copy all nodes from the subpatch to the parent patch
+ for node in subpatch.getNodes():
+ lookup[node.getName()] = cloneNode(om, node, patch)
+
+ # copy all connections
+ for node in subpatch.getNodes():
+ for port in node.getPorts():
+ if port.direction == 'OUTPUT':
+ for target in port.getConnections().keys():
+ targetname = '%s/%s' % (lookup[target.getParent().getName()], target.getName())
+ om.synth.connect.async(patch.getPath() + '/' +
+ lookup[node.getName()] + '/' +
+ port.getName(),
+ patch.getPath() + '/' +
+ targetname)
+
+ # make external connections
+ for node in subpatch.getNodes():
+ if node.libname == '':
+ lbl = node.pluginlabel
+
+ if lbl == 'audio_input' or lbl == 'control_input':
+ port1 = node.getPort('in')
+ for port2 in port1.getConnections().keys():
+ dst = '%s/%s/%s' % (patch.getPath(), lookup[port2.getParent().getName()], port2.getName())
+ port4 = subpatch.getPort(node.getName())
+ conns = port4.getConnections().keys()
+ if len(conns) == 0:
+ portValue = port4.value
+ om.synth.set_port_value_slow.async(dst, portValue)
+ else:
+ for port3 in port4.getConnections().keys():
+ src = port3.getPath()
+ om.synth.connect.async(src, dst)
+
+ if lbl == 'audio_output' or lbl == 'control_output':
+ port2 = node.getPort('out', True)
+ for port1 in port2.getConnections().keys():
+ src = '%s/%s/%s' % (patch.getPath(), lookup[port1.getParent().getName()], port1.getName())
+ port3 = subpatch.getPort(node.getName())
+ for port4 in port3.getConnections().keys():
+ dst = port4.getPath()
+ om.synth.connect.async(src, dst)
+
+ # destroy all input and output nodes from the subpatch
+ for node in subpatch.getNodes():
+ if node.libname == '':
+ lbl = node.pluginlabel
+ if (lbl == 'audio_input' or lbl == 'control_input' or
+ lbl == 'audio_output' or lbl == 'control_output'):
+ om.synth.destroy_node.async('%s/%s' % (patch.getPath(),
+ lookup[node.getName()]))
+
+ # calculate where to move all the new nodes
+ (min_x, min_y, max_x, max_y) = getPatchBounds(subpatch)
+ sub_x = subpatch.metadata['module-x']
+ if sub_x == None:
+ sub_x = 0
+ sub_y = subpatch.metadata['module-y']
+ if sub_y == None:
+ sub_y = 0
+ x_offset = float(sub_x)
+ if min_x != None:
+ x_offset = float(sub_x) - min_x
+ y_offset = float(sub_y)
+ if min_y != None:
+ y_offset = float(sub_y) - min_y
+
+ # move the new nodes
+ for node in subpatch.getNodes():
+ x = float(node.metadata['module-x'])
+ if x == None:
+ x = 0
+ om.metadata.set.async('%s/%s' % (patch.getPath(),
+ lookup[node.getName()]),
+ 'module-x', '%f' % (x + x_offset))
+ y = float(node.metadata['module-y'])
+ if y == None:
+ y = 0
+ om.metadata.set.async('%s/%s' % (patch.getPath(),
+ lookup[node.getName()]),
+ 'module-y', '%f' % (y + y_offset))
+
+ # move the old nodes in the patch
+ x_offset = 0
+ if min_x != None and max_x != None:
+ x_offset = max_x - min_x
+ y_offset = 0
+ if min_y != None and max_y != None:
+ y_offset = max_y - min_y
+ for node in patch.getNodes():
+ if node.getName() not in lookup.values():
+ x = node.metadata['module-x']
+ if x != None and float(x) > float(sub_x):
+ om.metadata.set.async(node.getPath(), 'module-x',
+ '%f' % (float(x) + x_offset))
+ y = node.metadata['module-y']
+ if y != None and float(y) > float(sub_y):
+ om.metadata.set.async(node.getPath(), 'module-y',
+ '%f' % (float(y) + y_offset))
+ # destroy the subpatch
+ om.synth.destroy_patch(subpatch.getPath())
+
+
+def main(om):
+ om.setEnvironment(omsynth.Environment())
+ om.engine.activate.async()
+ om.engine.load_plugins.async()
+ om.request.all_objects(om.getAddressAsString())
+
+ # wait for all the data to arrive (there should be a cleaner way)
+ time.sleep(3)
+
+ patch = om.getEnvironment().getPatch(sys.argv[1], True);
+ flatten(om, patch)
+
+ os._exit(0)
+
+
+if len(sys.argv) > 1:
+ if sys.argv[1] == "--name":
+ print "Flatten patch"
+ os._exit(0)
+ elif sys.argv[1] == "--shortdesc":
+ print "Merge the contents of all subpatches into the parent patch";
+ os._exit(0)
+ elif sys.argv[1] == "--signature":
+ print "%p";
+ os._exit(0)
+ else:
+ omsynth.startClient(main)
+
+else:
+ print "Which patch do you want to flatten?"
+ os._exit(0)
diff --git a/src/progs/python/scripts/sillysinepatch.py b/src/progs/python/scripts/sillysinepatch.py
new file mode 100644
index 00000000..ac51a080
--- /dev/null
+++ b/src/progs/python/scripts/sillysinepatch.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+#
+# Python bindings for Om
+# Copyright (C) 2005 Leonard Ritter
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import omsynth
+import os,time,sys
+
+def main(om):
+ om.setEnvironment(omsynth.Environment())
+ om.engine.activate()
+ om.engine.load_plugins()
+ om.engine.register_client(om.getAddressAsString())
+ om.request.all_objects(om.getAddressAsString())
+ om.synth.create_patch("/silly_sine", 1)
+ om.synth.create_node("/silly_sine/output", "Internal", "", "audio_output", 0)
+ om.synth.create_node("/silly_sine/sine", "LADSPA", "cmt.so", "sine_fcac", 0)
+ om.synth.set_port_value("/silly_sine/sine/Frequency", 440.0)
+ om.synth.set_port_value("/silly_sine/sine/Amplitude", 1.0)
+ om.synth.connect("/silly_sine/sine/Output", "/silly_sine/output/out")
+ om.synth.enable_patch("/silly_sine")
+ om.engine.enable()
+ om.engine.unregister_client(om.getAddressAsString())
+ os._exit(0)
+
+if __name__ == "__main__":
+ omsynth.startClient(main)
diff --git a/src/progs/supercollider/Makefile.am b/src/progs/supercollider/Makefile.am
new file mode 100644
index 00000000..69661c73
--- /dev/null
+++ b/src/progs/supercollider/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = Om.sc example.sc
+
diff --git a/src/progs/supercollider/Om.sc b/src/progs/supercollider/Om.sc
new file mode 100644
index 00000000..cb366d58
--- /dev/null
+++ b/src/progs/supercollider/Om.sc
@@ -0,0 +1,746 @@
+// TODO:
+// * Keep track of established connections.
+Om : Model {
+ classvar <>program = "om", <>patchLoader = "om_patch_loader";
+ classvar <>oscURL, <nodeTypeMap, <>uiClass;
+ var <addr;
+ var <>loadIntoJack = true;
+ var allocator, requestResponders, requestHandlers, notificationResponders;
+ var creatingNode, newNodeEnd;
+ var <registered = false, <booting = false;
+ var <root;
+ var <plugins, pluginParentEvent;
+ var onNewPatch;
+ *initClass {
+ Class.initClassTree(Event);
+ Class.initClassTree(NetAddr);
+ Event.parentEvents.default[\omcmd] = \noteOn;
+ Event.parentEvents.default[\omEventFunctions] = (
+ noteOn: #{ arg midinote=60, amp=0.1;
+ [midinote, asInteger((amp * 127).clip(0, 127)) ] },
+ noteOff: #{ arg midinote=60; [ midinote ] },
+ setPortValue: #{|portValue=0| [portValue] }
+ );
+ Event.parentEvents.default.eventTypes[\om]=#{|server|
+ var freqs, lag, dur, sustain, strum, target, bndl, omcmd;
+ freqs = ~freq = ~freq.value + ~detune;
+ if (freqs.isKindOf(Symbol).not) {
+ ~amp = ~amp.value;
+ strum = ~strum;
+ lag = ~lag + server.latency;
+ sustain = ~sustain = ~sustain.value;
+ omcmd = ~omcmd;
+ target = ~target;
+ ~midinote = ~midinote.value;
+ ~portValue = ~portValue.value;
+ bndl = ~omEventFunctions[omcmd].valueEnvir.asCollection;
+ bndl = bndl.flop;
+ bndl.do {|msgArgs, i|
+ var latency;
+ latency = i * strum + lag;
+ if(latency == 0.0) {
+ target.performList(omcmd, msgArgs)
+ } {
+ thisThread.clock.sched(latency, {
+ target.performList(omcmd, msgArgs);
+ })
+ };
+ if(omcmd === \noteOn) {
+ thisThread.clock.sched(sustain + latency, {
+ target.noteOff(msgArgs[0])
+ })
+ }
+ }
+ }
+ };
+ oscURL="osc.udp://"++NetAddr.localAddr.ip++$:++NetAddr.localAddr.port;
+ nodeTypeMap = IdentityDictionary[
+ \Internal -> OmInternalNode,
+ \LADSPA -> OmLADSPANode,
+ \DSSI -> OmDSSINode
+ ];
+ uiClass = OmEmacsUI
+ }
+ *new { | netaddr |
+ ^super.new.init(netaddr)
+ }
+ gui { ^uiClass.new(this) }
+ init { |netaddr|
+ addr = netaddr ? NetAddr("127.0.0.1", 16180);
+ onNewPatch = IdentityDictionary.new;
+ allocator = StackNumberAllocator(0,1024);
+ requestHandlers = IdentityDictionary.new;
+ requestResponders = [
+ "response/ok" -> {|id|
+ requestHandlers.removeAt(id).value; allocator.free(id) },
+ "response/error" -> {|id,text|
+ requestHandlers.removeAt(id);
+ allocator.free(id);
+ ("Om"+text).error }
+ ].collect({|a|
+ var func = a.value;
+ OSCresponder(addr, "/om/"++a.key, {|time,resp,msg|
+ func.value(*msg[1..])
+ })
+ });
+ notificationResponders = [
+ "new_patch" -> {|path,poly|
+ var func = onNewPatch.removeAt(path);
+ if (func.notNil) {
+ func.value(this.getPatch(path,false).prSetPoly(poly))
+ }
+ },
+ "metadata/update" -> {|path,key,value|
+ this.getObject(path).metadata.prSetMetadata(key, value) },
+ "new_node" -> {|path,poly,type,lib,label|
+ var patchPath, nodeName, patch, node;
+ var lastSlash = path.asString.inject(nil,{|last,char,i|
+ if(char==$/,i,last)
+ });
+ if (lastSlash.notNil) {
+ patchPath = path.asString.copyFromStart(lastSlash-1);
+ nodeName = path.asString.copyToEnd(lastSlash+1);
+ patch = this.getPatch(patchPath);
+ if (patch.notNil) {
+ if (patch.hasNode(nodeName).not) {
+ node = nodeTypeMap[type].new
+ (nodeName, patch, poly, label, lib);
+ creatingNode = node;
+ patch.nodes[nodeName.asSymbol] = node;
+ patch.changed(\newNode, node);
+ } {
+ if (patch.getNode(nodeName).class != nodeTypeMap[type]) {
+ ("Om sent an existng node with differing type"+path).warn
+ }
+ }
+ } {
+ ("Om tried to create node in non-existing patch"+patchPath).warn
+ }
+ } {
+ ("Invalid path in node creation"+path).warn
+ }
+ },
+ "new_node_end" -> {
+ newNodeEnd.value(creatingNode);
+ newNodeEnd = nil;
+ creatingNode = nil },
+ "new_port" -> {|path,type,dir,hint,def,min,max|
+ var basePath, portName, parent, port;
+ var lastSlash = path.asString.inject(nil,{|last,char,i|
+ if(char==$/,i,last)
+ });
+ if (lastSlash.notNil) {
+ basePath = path.asString.copyFromStart(lastSlash-1);
+ portName = path.asString.copyToEnd(lastSlash+1);
+ parent = this.getNode(basePath) ? this.getPatch(basePath);
+ if (parent.notNil) {
+ if (parent.hasPort(portName).not) {
+ port = OmPort.new(portName, parent, type, dir, hint, def, min, max);
+ parent.ports[portName.asSymbol] = port;
+ parent.changed(\newPort, port)
+ } {
+ if (parent.getPort(portName).porttype != type) {
+ ("Om tried to create an already existing port with differing type"
+ +path).warn
+ }
+ }
+ } {
+ ("Om tried to create port on non-existing object"+basePath).warn
+ }
+ } {
+ ("Invalid path in port creation"+path).warn
+ }
+ },
+ "control_change" -> {|path,value|
+ this.getPort(path).prSetValue(value) },
+ "patch_enabled" -> {|path| this.getPatch(path).prSetEnabled(true) },
+ "patch_disabled" -> {|path| this.getPatch(path).prSetEnabled(false) },
+ "plugin" -> {|lib,label,name,type|
+ plugins.add(Event.new(4,nil,pluginParentEvent).putAll(
+ (type:type, lib:lib, label:label, name:name))) },
+ "node_removal" -> {|path|
+ var node = this.getNode(path);
+ if (node.notNil) {
+ node.parent.nodes.removeAt(node.name.asSymbol).free
+ } {
+ ("Om attempting to remove non-existing node"+path).warn
+ }
+ },
+ "port_removal" -> {|path|
+ var port = this.getPort(path);
+ if (port.notNil) {
+ port.parent.ports.removeAt(port.name.asSymbol).free
+ } {
+ ("Om attempting to remove non-existing port"+path).warn
+ }
+ },
+ "patch_destruction" -> {|path|
+ var patch = this.getPatch(path);
+ if (patch.notNil) {
+ patch.parent.patches.removeAt(patch.name.asSymbol).free
+ } {
+ ("Om attempting to remove non-existing patch"+path).warn
+ }
+ },
+ "program_add" -> {|path,bank,program,name|
+ var node = this.getNode(path);
+ if (node.respondsTo(\prProgramAdd)) {
+ node.prProgramAdd(bank,program,name)
+ } {
+ ("Om tried to add program info to"+node).warn
+ }
+ }
+ ].collect({|a|
+ var func = a.value;
+ OSCresponder(addr, "/om/"++a.key, {|time,resp,msg|
+ func.value(*msg[1..])
+ })
+ });
+ pluginParentEvent = Event.new(2,nil,nil).putAll((
+ engine:this,
+ new:{|self,path,poly=1,handler|self.engine.createNode(path?("/"++self.name),self.type,self.lib,self.label,poly,created:handler)}
+ ));
+ }
+ *waitForBoot {|func| ^this.new.waitForBoot(func) }
+ waitForBoot {|func|
+ var r, id = 727;
+ requestHandlers[id] = {
+ r.stop;
+ booting=false;
+ this.changed(\running, true);
+ func.value(this)
+ };
+ if (booting.not) {this.boot};
+ r = Routine.run {
+ 50.do {
+ 0.1.wait;
+ addr.sendMsg("/om/ping", id)
+ };
+ requestHandlers.removeAt(id);
+ "Om engine boot failed".error;
+ }
+ }
+ getPatch {|path, mustExist=true|
+ var elements, currentPatch;
+ if (path.class == Array) { elements = path
+ } { elements = path.asString.split($/) };
+ elements.do{|elem|
+ if (elem=="") {
+ currentPatch = root
+ } {
+ currentPatch = currentPatch.getPatch(elem,mustExist);
+ if (currentPatch.isNil) { ^nil }
+ }
+ };
+ ^currentPatch;
+ }
+ getNode {|path|
+ var basePath, nodeName, patch;
+ if (path.class == Array) { basePath = path
+ } { basePath = path.asString.split($/) };
+ nodeName = basePath.pop;
+ patch = this.getPatch(basePath,true);
+ if (patch.notNil) {
+ ^patch.getNode(nodeName)
+ };
+ ^nil
+ }
+ getPort {|path|
+ var basePath, portName, node, patch;
+ basePath = path.asString.split($/);
+ portName = basePath.pop;
+ node = this.getNode(basePath.copy);
+ if (node.notNil) { ^node.getPort(portName) };
+ patch = this.getPatch(basePath,true);
+ if (patch.notNil) { ^patch.getPort(portName) };
+ ^nil
+ }
+ getObject {|path|
+ var patch,node,port;
+ patch = this.getPatch(path,true);
+ if (patch.notNil) { ^patch };
+ node = this.getNode(path);
+ if (node.notNil) { ^node };
+ port = this.getPort(path,true);
+ if (port.notNil) { ^port };
+ ^nil
+ }
+ at {|path|^this.getObject(path.asString)}
+ *boot {|func|
+ ^Om.new.waitForBoot {|e|
+ e.activate {
+ e.register {
+ e.loadPlugins {
+ e.requestPlugins {
+ e.requestAllObjects {
+ func.value(e)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ boot {
+ requestResponders.do({|resp| resp.add});
+ booting = true;
+ if (addr.addr == 2130706433) {
+ if (loadIntoJack) {
+ ("jack_load"+"-i"+addr.port+"Om"+"om").unixCmd
+ } {
+ (program+"-p"+addr.port).unixCmd
+ }
+ } {
+ "You have to manually boot Om now".postln
+ }
+ }
+ loadPatch {|patchPath| (patchLoader + patchPath).unixCmd }
+ activate { | handler |
+ this.sendReq("engine/activate", {
+ root = OmPatch("",nil,this);
+ this.changed(\newPatch, root);
+ handler.value
+ })
+ }
+ register { | handler |
+ this.sendReq("engine/register_client", {
+ registered=true;
+ notificationResponders.do({|resp| resp.add});
+ this.changed(\registered, registered);
+ handler.value(this)
+ })
+ }
+ unregister { | handler |
+ this.sendReq("engine/unregister_client", {
+ registered=false;
+ notificationResponders.do({|resp| resp.remove});
+ this.changed(\registered, registered);
+ handler.value(this)
+ })
+ }
+ registered_ {|flag|
+ if (flag and: registered.not) {
+ this.register
+ } {
+ if (flag.not and: registered) {
+ this.unregister
+ }
+ }
+ }
+ loadPlugins { | handler | this.sendReq("engine/load_plugins", handler) }
+ requestPlugins {|handler|
+ var startTime = Main.elapsedTime;
+ plugins = Set.new;
+ this.sendReq("request/plugins", {
+ ("Received info about"+plugins.size+"plugins in"+(Main.elapsedTime-startTime)+"seconds").postln;
+ this.changed(\plugins, plugins);
+ handler.value(this);
+ })
+ }
+ requestAllObjects { |handler|
+ this.sendReq("request/all_objects", handler)
+ }
+ createPatch { | path, poly=1, handler |
+ onNewPatch[path.asSymbol] = handler;
+ this.sendReq("synth/create_patch", nil, path.asString, poly.asInteger)
+ }
+ createNode { | path, type='LADSPA', lib, label, poly=1, created, handler |
+ newNodeEnd = created;
+ this.sendReq("synth/create_node",handler,path,type,lib,label,poly)
+ }
+ createAudioInput { | path, handler |
+ this.createNode(path,"Internal","","audio_input",0,handler)
+ }
+ createAudioOutput {|path,handler|
+ this.createNode(path,"Internal","","audio_output",0,handler)
+ }
+ createMIDIInput {|path,handler|
+ this.createNode(path,"Internal","","midi_input",1,handler)
+ }
+ createMIDIOutput {|path,handler|
+ this.createNode(path,"Internal","","midi_output",1,handler)
+ }
+ createNoteIn {|path| this.createNode(path,"Internal","","note_in") }
+ connect {|fromPath,toPath,handler|
+ this.sendReq("synth/connect",handler,fromPath.asString,toPath.asString)
+ }
+ disconnect { | fromPath, toPath, handler |
+ this.sendReq("synth/disconnect",handler,fromPath.asString,toPath.asString)
+ }
+ disconnectAll { | path, handler |
+ this.sendReq("synth/disconnect_all",handler,path);
+ }
+ sendReq { | path, handler...args |
+ var id = allocator.alloc;
+ requestHandlers[id] = handler;
+ addr.sendMsg("/om/"++path, id, *args)
+ }
+ quit {
+ if (loadIntoJack) {
+ ("jack_unload"+"Om").unixCmd;
+ booting=false;
+ requestResponders.do(_.remove);
+ notificationResponders.do(_.remove);
+ this.changed(\running, false);
+ } {
+ this.sendReq("engine/quit", {
+ booting=false;
+ requestResponders.do(_.remove);
+ notificationResponders.do(_.remove);
+ this.changed(\running, false);
+ })
+ }
+ }
+ ping {| n=1, func |
+ var id, result, start;
+ id = allocator.alloc;
+ result = 0;
+ requestHandlers[id] = {
+ var end;
+ end = Main.elapsedTime;
+ result=max((end-start).postln,result);
+ n=n-1;
+ if (n > 0) {
+ start = Main.elapsedTime;
+ addr.sendMsg("/om/ping", id)
+ } {
+ allocator.free(id);
+ func.value(result)
+ }
+ };
+ start = Main.elapsedTime;
+ addr.sendMsg("/om/ping", id)
+ }
+ setPortValue {|path, val| this.getPort(path.asString).value=val }
+ jackConnect {|path, jackPort|
+ this.getPort(path).jackConnect(jackPort)
+ }
+ noteOn {|path, note, vel|
+ var patch,node;
+ patch = this.getPatch(path,true);
+ if (patch.notNil) { patch.noteOn(note,vel) };
+ node = this.getNode(path);
+ if (node.notNil) { node.noteOn(note,vel) };
+ }
+ noteOff {|path, note|
+ var patch,node;
+ patch = this.getPatch(path,true);
+ if (patch.notNil) { patch.noteOff(note) };
+ node = this.getNode(path);
+ if (node.notNil) { node.noteOff(note) };
+ }
+ matchPlugins{ | label, lib, name, type |
+ ^plugins.select{ |p|
+ label.matchItem(p.label) and: {
+ lib.matchItem(p.lib) and: {
+ name.matchItem(p.name) and: {
+ type.matchItem(p.type)
+ }
+ }
+ }
+ }
+ }
+ dssiMsg {|path,reqType="program" ...args|
+ addr.sendMsg("/dssi"++path++$/++reqType,*args)
+ }
+}
+
+OmMetadata {
+ var object, dict;
+ *new {|obj|^super.new.metadataInit(obj)}
+ metadataInit {|obj|
+ dict=Dictionary.new;
+ object=obj
+ }
+ put {|key,val|
+ object.engine.sendReq("metadata/set", nil,
+ object.path, key.asString, val.asString)
+ }
+ at {|key|^dict.at(key.asSymbol)}
+ prSetMetadata {|key,val|
+ dict.put(key,val);
+ object.changed(\metadata, key, val)
+ }
+}
+
+OmObject : Model {
+ var <name, <parent, <metadata;
+ *new {|name, parent|
+ ^super.new.initOmObject(name,parent);
+ }
+ initOmObject {|argName, argParent|
+ name = argName;
+ parent = argParent;
+ metadata=OmMetadata(this)
+ }
+ path { ^parent.notNil.if({ parent.path ++ $/ ++ name }, name).asString }
+ depth { ^parent.notNil.if({ parent.depth + 1 }, 0) }
+ engine { ^parent.engine }
+}
+
+OmPort : OmObject {
+ var <porttype, <direction, <spec, <value, <connections;
+ *new {|name,parent,type,dir,hint,def,min,max|
+ ^super.new(name,parent).initPort(type,dir,hint,def,min,max)
+ }
+ initPort {|type,dir,hint,def,min,max|
+ porttype = type;
+ direction = dir;
+ spec = ControlSpec(min, max,
+ if (hint == 'LOGARITHMIC', 'exp', 'lin'),
+ if (hint == 'INTEGER', 1, 0),
+ def);
+ connections = IdentityDictionary.new;
+ }
+ jackConnect {|jackPort|
+ if (porttype.asSymbol != \AUDIO
+ || direction.asSymbol!=\OUTPUT) {
+ Error("Not a audio output port").throw
+ };
+ ("jack_connect" + "Om:"++(this.path) + jackPort).unixCmd
+ }
+ value_ {|val|
+ if (porttype == \CONTROL and: {direction == \INPUT}) {
+ this.engine.sendReq("synth/set_port_value", nil, this.path, val)
+ } {
+ Error("Not a input control port"+this.path).throw
+ }
+ }
+ connectTo {|destPort|
+ if (this.direction!=destPort.direction) {
+ this.engine.connect(this,destPort)
+ } {
+ Error("Unable to connect ports with same direction").throw
+ }
+ }
+
+ // Setters for OSC responders
+ prSetValue {|argValue|
+ if (value != argValue) {
+ value = argValue;
+ this.changed(\value, value)
+ }
+ }
+}
+
+OmNode : OmObject { // Abstract class
+ var <ports, <polyphonic;
+ *new {|name,parent,poly|
+ ^super.new(name,parent).initNode(poly)
+ }
+ initNode {|argPoly|
+ polyphonic = argPoly;
+ ports = IdentityDictionary.new;
+ }
+ hasPort {|name| ^ports[name.asSymbol].notNil }
+ getPort {|name| ^ports[name.asSymbol] }
+ portArray {|type=\AUDIO,dir=\OUTPUT|
+ var result = Array.new(8);
+ ports.do {|port|
+ if (port.porttype==type and: {port.direction==dir}) {
+ result=result.add(port)
+ }
+ };
+ ^result
+ }
+ audioOut {
+ ^this.portArray(\AUDIO, \OUTPUT)
+ }
+ audioIn {
+ ^this.portArray(\AUDIO, \INPUT)
+ }
+}
+
+OmInternalNode : OmNode {
+ var <pluginlabel;
+ *new {|name,parent,poly,label|
+ ^super.new(name,parent,poly).initInternalNode(label)
+ }
+ initInternalNode {|label|
+ pluginlabel = label
+ }
+ noteOn { |note,vel|
+ if (pluginlabel == \note_in or:
+ {pluginlabel == \trigger_in and: {this.value == note}}) {
+ this.engine.sendReq("synth/note_on", nil, this.path, note, vel)
+ } {
+ Error("Not a trigger_in or note_in node").throw
+ }
+ }
+ noteOff {|note|
+ if (pluginlabel == \note_in or:
+ {pluginlabel == \trigger_in and: {this.value == note}}) {
+ this.engine.sendReq("synth/note_off", nil, this.path, note)
+ } {
+ Error("Not a trigger_in or note_in node").throw
+ }
+ }
+}
+
+OmLADSPANode : OmNode {
+ var <pluginlabel, <libname;
+ *new {|name, parent, poly, label, lib|
+ ^super.new(name,parent,poly).initLADSPANode(label,lib)
+ }
+ initLADSPANode {|label,lib|
+ pluginlabel = label;
+ libname = lib
+ }
+}
+
+OmDSSINode : OmLADSPANode {
+ var programs;
+ *new {|name,parent,poly,label,lib|
+ ^super.new(name,parent,poly,label,lib).initDSSI
+ }
+ initDSSI {
+ programs = Set.new;
+ }
+ program {|bank, prog|
+ this.engine.dssiMsg(this.path,"program",bank.asInteger,prog.asInteger)
+ }
+ prProgramAdd {|bank,program,name|
+ var info = (bank:bank, program:program, name:name);
+ if (programs.includes(info).not) {
+ programs.add(info);
+ this.changed(\programAdded, info)
+ }
+ }
+}
+
+OmPatch : OmNode {
+ var <nodes, <patches, <poly, <enabled;
+ var om;
+ *new {|name,parent,engine|
+ ^super.new(name,parent).initPatch(engine);
+ }
+ initPatch {|argEngine|
+ nodes = IdentityDictionary.new;
+ patches = IdentityDictionary.new;
+ om = argEngine
+ }
+ hasNode {|name|
+ ^nodes[name.asSymbol].notNil
+ }
+ getNode {|name|
+ ^nodes[name.asSymbol]
+ }
+ hasPatch {|name|
+ ^patches[name.asSymbol].notNil
+ }
+ getPatch {|name,mustExist=false|
+ if (this.hasPatch(name).not) {
+ if (mustExist) { ^nil };
+ patches[name.asSymbol] = OmPatch(name,this);
+ };
+ ^patches[name.asSymbol]
+ }
+ engine {
+ if (om.notNil) { ^om } { ^parent.engine };
+ }
+ connect {|fromPort, toPort|
+ this.engine.connect(this.path++$/++fromPort, this.path++$/++toPort)
+ }
+ dumpX {
+ (String.fill(this.depth,$ )++"*"+this.path).postln;
+ nodes.do{|node|
+ (String.fill(node.depth,$ )++"**"+node.path).postln;
+ node.ports.do{|port|
+ (String.fill(port.depth,$ )++"***"+port.path).postln };
+ };
+ ports.do{|port| (String.fill(port.depth,$ )++"***"+port.path).postln };
+ patches.do(_.dump)
+ }
+ noteOn {|note,vel|
+ var targetNode;
+ this.nodes.do{|node|
+ if (node.class == OmInternalNode) {
+ node.pluginlabel.switch(
+ \trigger_in, {
+ if (node.ports['Note Number'].value == note) {
+ targetNode = node;
+ }
+ },
+ \note_in, {
+ targetNode = node;
+ }
+ )
+ }
+ };
+ if (targetNode.notNil) {
+ targetNode.noteOn(note, vel)
+ } {
+ Error("Unable to find trigger_in for note "++note).throw
+ }
+ }
+ noteOff {|note|
+ var targetNode;
+ this.nodes.do{|node|
+ if (node.class == OmInternalNode) {
+ node.pluginlabel.switch(
+ \trigger_in, {
+ if (node.ports['Note Number'].value == note) {
+ targetNode = node;
+ }
+ },
+ \note_in, {
+ targetNode = node;
+ }
+ )
+ }
+ };
+ if (targetNode.notNil) {
+ targetNode.noteOff(note)
+ } {
+ Error("Unable to find node for note_off "++note).throw
+ }
+ }
+ newPatch {|name, poly=1, handler|
+ this.engine.createPatch(this.path++$/++name, poly, handler)
+ }
+ newNode {|name, poly=1, type, lib, label, fullname, handler|
+ var candidates = this.engine.matchPlugins(label,lib,fullname,type);
+ if (candidates.size == 1) {
+ candidates.pop.new(this.path++"/"++name, poly, handler)
+ } {
+ if (candidates.size==0) {
+ Error("Plugin not found").throw
+ } {
+ Error("Plugin info not unique").throw
+ }
+ }
+ }
+
+ // Setters for OSC responders
+ prSetPoly {|argPoly| poly = argPoly }
+ prSetEnabled {|flag|
+ if (flag != enabled) {
+ enabled = flag;
+ this.changed(\enabled, flag)
+ }
+ }
+}
+
+
+OmEmacsUI {
+ var engine, window, bootBtn;
+ *new {|engine| ^super.newCopyArgs(engine).init }
+ init {
+ window = EmacsBuffer("*Om -"+engine.addr.ip++$:++engine.addr.port);
+ bootBtn = EmacsButton(window, ["Boot","Quit"], {|value|
+ if (value==1) {
+ engine.boot
+ } {
+ engine.quit
+ }
+ }).value=engine.registered.binaryValue;
+ engine.addDependant(this);
+ window.front;
+ }
+ update {|who, what ... args|
+ Emacs.message(who.asString+what+args);
+ if (what == \newPatch or: {what == \newNode or: {what == \newPort}}) {
+ args[0].addDependant(this)
+ };
+ }
+}
diff --git a/src/progs/supercollider/README b/src/progs/supercollider/README
new file mode 100644
index 00000000..020da8aa
--- /dev/null
+++ b/src/progs/supercollider/README
@@ -0,0 +1,11 @@
+Om bindings for SuperCollider
+-----------------------------
+
+Installation:
+Copy the Om.sc file into some directory in your SC class path and
+reboot sclang.
+
+Usage:
+See example.sc for inspiration.
+
+ -- Mario Lang <mlang@delysid.org>
diff --git a/src/progs/supercollider/example.sc b/src/progs/supercollider/example.sc
new file mode 100644
index 00000000..6186e433
--- /dev/null
+++ b/src/progs/supercollider/example.sc
@@ -0,0 +1,27 @@
+// Boot Om (inside SC) and the scsynth server
+(
+o=Om.boot {
+ o.loadPatch("/home/mlang/src/om-synth/src/clients/patches/303.om");
+ s.boot;
+}
+)
+// Connect patch output to one SC input
+o.jackConnect("/303/output", "SuperCollider:in_3");
+// Play this input on the default output bus (most simple postprocessor)
+{AudioIn.ar(3).dup}.play;
+// A simple 303 pattern
+(
+Tempo.bpm=170;
+Ppar([
+ Pbind(\type, \om, \target, o.getPatch("/303",true),
+ \dur, 1/1, \octave,2,\degree, Pseq([Pshuf([0,2,4,6],16)],2)),
+ Pbind(\type, \om, \target, o.getPatch("/303",true),
+ \dur, Prand([Pshuf([1/2,1/4,1/4],4),Pshuf([1/3,1/3,1/6,1/6],4)],inf),
+ \legato, Prand((0.5,0.55..0.9),inf),
+ \octave,3,\degree, Pseq([Pshuf([0,2,4,6],2)],40)),
+ Pbind(\type, \om, \omcmd, \setPortValue,
+ \target, o.getPort("/303/cutoff",true),
+ \dur, 1/10, \portValue, Pseq((0,0.005..1).mirror,2)),
+ Pbind(\type, \om, \omcmd, \setPortValue,
+ \target, o.getPort("/303/resonance",true), \portValue, 1)]).play(quant:4)
+)