summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2006-06-09 02:18:57 +0000
committerDavid Robillard <d@drobilla.net>2006-06-09 02:18:57 +0000
commit2f0e092964f800c0ff82cbd08bf2ee05db814e7b (patch)
treed35a09dc50535aae9292f8ca3262adc5526fe2a6 /src
downloadpatchage-2f0e092964f800c0ff82cbd08bf2ee05db814e7b.tar.gz
patchage-2f0e092964f800c0ff82cbd08bf2ee05db814e7b.tar.bz2
patchage-2f0e092964f800c0ff82cbd08bf2ee05db814e7b.zip
Added patchage
git-svn-id: http://svn.drobilla.net/lad/patchage@5 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r--src/AlsaDriver.cpp484
-rw-r--r--src/AlsaDriver.h72
-rw-r--r--src/Driver.h55
-rw-r--r--src/JackDriver.cpp258
-rw-r--r--src/JackDriver.h68
-rw-r--r--src/LashDriver.cpp146
-rw-r--r--src/LashDriver.h50
-rw-r--r--src/Makefile.am27
-rw-r--r--src/Patchage.cpp411
-rw-r--r--src/Patchage.h116
-rw-r--r--src/PatchageFlowCanvas.cpp117
-rw-r--r--src/PatchageFlowCanvas.h49
-rw-r--r--src/PatchageModule.h120
-rw-r--r--src/PatchagePort.h66
-rw-r--r--src/StateManager.cpp246
-rw-r--r--src/StateManager.h75
-rw-r--r--src/main.cpp49
-rw-r--r--src/patchage.glade607
-rw-r--r--src/patchage.gladep9
19 files changed, 3025 insertions, 0 deletions
diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp
new file mode 100644
index 0000000..2f4d52e
--- /dev/null
+++ b/src/AlsaDriver.cpp
@@ -0,0 +1,484 @@
+/* This file is part of Patchage. 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 <iostream>
+#include <cassert>
+#include <sys/poll.h>
+#include <errno.h>
+#include "PatchageFlowCanvas.h"
+#include "AlsaDriver.h"
+#include "Patchage.h"
+#include "PatchageModule.h"
+#include "PatchagePort.h"
+
+using std::cerr;
+using std::string;
+
+
+using namespace LibFlowCanvas;
+
+AlsaDriver::AlsaDriver(Patchage* app)
+: m_app(app),
+ m_canvas(app->canvas()),
+ m_seq(NULL)
+{
+}
+
+
+AlsaDriver::~AlsaDriver()
+{
+ detach();
+}
+
+
+/** Attach to ALSA.
+ * @a launch_daemon is ignored, as ALSA has no daemon to launch/connect to.
+ */
+void
+AlsaDriver::attach(bool launch_daemon)
+{
+ cout << "Connecting to Alsa... ";
+ cout.flush();
+
+ int ret = snd_seq_open(&m_seq, "default",
+ SND_SEQ_OPEN_DUPLEX,
+ SND_SEQ_NONBLOCK);
+ if (ret) {
+ cout << "Failed" << endl;
+ m_seq = NULL;
+ } else {
+ cout << "Connected" << endl;
+
+ snd_seq_set_client_name(m_seq, "Patchage");
+
+ ret = pthread_create(&m_refresh_thread, NULL, &AlsaDriver::refresh_main, this);
+ if (ret)
+ cerr << "Couldn't start refresh thread" << endl;
+ }
+}
+
+
+void
+AlsaDriver::detach()
+{
+ if (m_seq != NULL) {
+ pthread_cancel(m_refresh_thread);
+ pthread_join(m_refresh_thread, NULL);
+ snd_seq_close(m_seq);
+ m_seq = NULL;
+ cout << "Disconnected from Alsa" << endl;
+ }
+}
+
+
+/** Refresh all Alsa Midi ports and connections.
+ */
+void
+AlsaDriver::refresh()
+{
+ if (!is_attached())
+ return;
+
+ assert(m_seq);
+
+ refresh_ports();
+ refresh_connections();
+
+ undirty();
+}
+
+
+/** Refresh all Alsa Midi ports.
+ */
+void
+AlsaDriver::refresh_ports()
+{
+ assert(is_attached());
+ assert(m_seq);
+
+ snd_seq_client_info_t* cinfo;
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_client_info_set_client(cinfo, -1);
+
+ snd_seq_port_info_t * pinfo;
+ snd_seq_port_info_alloca(&pinfo);
+
+ string client_name;
+ string port_name;
+ bool is_input = false;
+ bool is_duplex = false;
+ bool is_application = true;
+
+ while (snd_seq_query_next_client (m_seq, cinfo) >= 0) {
+ snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
+ snd_seq_port_info_set_port(pinfo, -1);
+
+ client_name = snd_seq_client_info_get_name(cinfo);
+
+ while (snd_seq_query_next_port(m_seq, pinfo) >= 0) {
+ int caps = snd_seq_port_info_get_capability(pinfo);
+ int type = snd_seq_port_info_get_type(pinfo);
+
+ // Skip ports we shouldn't show
+ if (caps & SND_SEQ_PORT_CAP_NO_EXPORT)
+ continue;
+ else if ( !( (caps & SND_SEQ_PORT_CAP_READ)
+ || (caps & SND_SEQ_PORT_CAP_WRITE)
+ || (caps & SND_SEQ_PORT_CAP_DUPLEX)))
+ continue;
+ else if ((snd_seq_client_info_get_type(cinfo) != SND_SEQ_USER_CLIENT)
+ && ((type == SND_SEQ_PORT_SYSTEM_TIMER
+ || type == SND_SEQ_PORT_SYSTEM_ANNOUNCE)))
+ continue;
+
+ const snd_seq_addr_t addr = *snd_seq_port_info_get_addr(pinfo);
+
+ is_duplex = false;
+
+ // FIXME: Should be CAP_SUBS_READ etc?
+ if ((caps & SND_SEQ_PORT_CAP_READ) && (caps & SND_SEQ_PORT_CAP_WRITE))
+ is_duplex = true;
+ else if (caps & SND_SEQ_PORT_CAP_READ)
+ is_input = false;
+ else if (caps & SND_SEQ_PORT_CAP_WRITE)
+ is_input = true;
+
+ is_application = (type & SND_SEQ_PORT_TYPE_APPLICATION);
+ port_name = snd_seq_port_info_get_name(pinfo);
+ PatchageModule* m = NULL;
+
+ //cout << client_name << " : " << port_name << " is_application = " << is_application
+ // << " is_duplex = " << is_duplex << endl;
+
+ bool split = m_app->state_manager()->get_module_split(client_name, !is_application);
+
+ // Application input/output ports go on the same module
+ if (!split) {
+ m = (PatchageModule*)m_canvas->find_module(client_name, InputOutput);
+ if (m == NULL) {
+ m = new PatchageModule(m_app, client_name, InputOutput);
+ m->load_location();
+ m->store_location();
+ m_canvas->add_module(m);
+ }
+
+ if (!is_duplex) {
+ m->add_patchage_port(port_name, is_input, ALSA_MIDI, addr);
+ } else {
+ m->add_patchage_port(port_name, true, ALSA_MIDI, addr);
+ m->add_patchage_port(port_name, false, ALSA_MIDI, addr);
+ }
+ } else { // non-application input/output ports (hw interface, etc) go on separate modules
+ PatchageModule* m = NULL;
+ ModuleType type = InputOutput;
+
+ // The 'application' hint isn't always set by clients, so this bit
+ // is pretty nasty...
+
+ if (!is_duplex) { // just one port to add
+ if (is_input) type = Input;
+ else type = Output;
+
+ // See if an InputOutput module exists (maybe with Jack ports on it)
+ m = (PatchageModule*)m_canvas->find_module(client_name, InputOutput);
+
+ if (m == NULL)
+ m = (PatchageModule*)m_canvas->find_module(client_name, type);
+ if (m == NULL) {
+ m = new PatchageModule(m_app, client_name, type);
+ m->load_location();
+ m->store_location();
+ m_canvas->add_module(m);
+ }
+ m->add_patchage_port(port_name, is_input, ALSA_MIDI, addr);
+ } else { // two ports to add
+ type = Input;
+
+ // See if an InputOutput module exists (maybe with Jack ports on it)
+ m = (PatchageModule*)m_canvas->find_module(client_name, InputOutput);
+
+ if (m == NULL)
+ m = (PatchageModule*)m_canvas->find_module(client_name, type);
+ if (m == NULL) {
+ m = new PatchageModule(m_app, client_name, type);
+ m->load_location();
+ m->store_location();
+ m_canvas->add_module(m);
+ }
+ m->add_patchage_port(port_name, true, ALSA_MIDI, addr);
+
+ type = Output;
+
+ // See if an InputOutput module exists (maybe with Jack ports on it)
+ m = (PatchageModule*)m_canvas->find_module(client_name, InputOutput);
+
+ if (m == NULL)
+ m = (PatchageModule*)m_canvas->find_module(client_name, type);
+ if (m == NULL) {
+ m = new PatchageModule(m_app, client_name, type);
+ m->load_location();
+ m->store_location();
+ m_canvas->add_module(m);
+ }
+ m->add_patchage_port(port_name, false, ALSA_MIDI, addr);
+ }
+ }
+ }
+ }
+}
+
+
+/** Refresh all Alsa Midi connections.
+ */
+void
+AlsaDriver::refresh_connections()
+{
+ assert(is_attached());
+ assert(m_seq);
+
+ PatchageModule* m = NULL;
+ PatchagePort* p = NULL;
+
+ for (ModuleMap::iterator i = m_canvas->modules().begin();
+ i != m_canvas->modules().end(); ++i) {
+ m = (PatchageModule*)((*i).second);
+ for (PortList::iterator j = m->ports().begin(); j != m->ports().end(); ++j) {
+ p = (PatchagePort*)(*j);
+ if (p->type() == ALSA_MIDI)
+ add_connections(p);
+ }
+ }
+}
+
+
+/** Add all connections for the given port.
+ */
+void
+AlsaDriver::add_connections(PatchagePort* port)
+{
+ assert(is_attached());
+ assert(m_seq);
+
+ const snd_seq_addr_t* addr = port->alsa_addr();
+ PatchagePort* connected_port = NULL;
+
+ // Fix a problem with duplex->duplex connections (would show up twice)
+ // No sense doing them all twice anyway..
+ if (port->is_input())
+ return;
+
+ snd_seq_query_subscribe_t* subsinfo;
+ snd_seq_query_subscribe_alloca(&subsinfo);
+ snd_seq_query_subscribe_set_root(subsinfo, addr);
+ snd_seq_query_subscribe_set_index(subsinfo, 0);
+
+ while(!snd_seq_query_port_subscribers(m_seq, subsinfo)) {
+ const snd_seq_addr_t* connected_addr = snd_seq_query_subscribe_get_addr(subsinfo);
+
+ connected_port = m_canvas->find_port(connected_addr, !port->is_input());
+
+ if (connected_port != NULL) {
+ m_canvas->add_connection(port, connected_port);
+ }
+
+ snd_seq_query_subscribe_set_index(subsinfo, snd_seq_query_subscribe_get_index(subsinfo) + 1);
+ }
+
+}
+
+
+/** Connects two Alsa Midi ports.
+ *
+ * \return Whether connection succeeded.
+ */
+bool
+AlsaDriver::connect(const PatchagePort* const src_port, const PatchagePort* const dst_port)
+{
+ const snd_seq_addr_t* src = src_port->alsa_addr();
+ const snd_seq_addr_t* dst = dst_port->alsa_addr();
+
+ bool result = false;
+
+ if (src && dst) {
+ snd_seq_port_subscribe_t* subs;
+ snd_seq_port_subscribe_malloc(&subs);
+ snd_seq_port_subscribe_set_sender(subs, src);
+ snd_seq_port_subscribe_set_dest(subs, dst);
+ snd_seq_port_subscribe_set_exclusive(subs, 0);
+ snd_seq_port_subscribe_set_time_update(subs, 0);
+ snd_seq_port_subscribe_set_time_real(subs, 0);
+
+ // Already connected (shouldn't happen)
+ if (!snd_seq_get_port_subscription(m_seq, subs)) {
+ cerr << "Error: Attempt to subscribe Alsa ports that are already subscribed." << endl;
+ result = false;
+ }
+
+ int ret = snd_seq_subscribe_port(m_seq, subs);
+ if (ret < 0) {
+ cerr << "Alsa subscription failed: " << snd_strerror(ret) << endl;
+ result = false;
+ }
+ }
+
+ return (!result);
+}
+
+
+/** Disconnects two Alsa Midi ports.
+ *
+ * \return Whether disconnection succeeded.
+ */
+bool
+AlsaDriver::disconnect(const PatchagePort* const src_port, const PatchagePort* const dst_port)
+{
+ const snd_seq_addr_t* src = src_port->alsa_addr();
+ const snd_seq_addr_t* dst = dst_port->alsa_addr();
+
+ bool result = false;
+
+ snd_seq_port_subscribe_t* subs;
+ snd_seq_port_subscribe_malloc(&subs);
+ snd_seq_port_subscribe_set_sender(subs, src);
+ snd_seq_port_subscribe_set_dest(subs, dst);
+ snd_seq_port_subscribe_set_exclusive(subs, 0);
+ snd_seq_port_subscribe_set_time_update(subs, 0);
+ snd_seq_port_subscribe_set_time_real(subs, 0);
+
+ // Not connected (shouldn't happen)
+ if (snd_seq_get_port_subscription(m_seq, subs) != 0) {
+ cerr << "Error: Attempt to unsubscribe Alsa ports that are not subscribed." << endl;
+ result = false;
+ }
+
+ int ret = snd_seq_unsubscribe_port(m_seq, subs);
+ if (ret < 0) {
+ cerr << "Alsa unsubscription failed: " << snd_strerror(ret) << endl;
+ result = false;
+ }
+
+ return (!result);
+}
+
+
+bool
+AlsaDriver::create_refresh_port()
+{
+ // Mostly lifted from alsa-patch-bay, (C) 2002 Robert Ham, released under GPL
+
+ int ret;
+ snd_seq_port_info_t* port_info;
+
+ snd_seq_port_info_alloca(&port_info);
+ snd_seq_port_info_set_name(port_info, "System Announcement Reciever");
+ snd_seq_port_info_set_capability(port_info,
+ SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_NO_EXPORT);
+
+ snd_seq_port_info_set_type(port_info, SND_SEQ_PORT_TYPE_APPLICATION);
+
+ ret = snd_seq_create_port(m_seq, port_info);
+ if (ret) {
+ cerr << "Error creating alsa port: " << snd_strerror(ret) << endl;
+ return false;
+ }
+
+ // Subscribe the port to the system announcer
+ ret = snd_seq_connect_from(m_seq,
+ snd_seq_port_info_get_port(port_info),
+ SND_SEQ_CLIENT_SYSTEM,
+ SND_SEQ_PORT_SYSTEM_ANNOUNCE);
+
+ if (ret) {
+ cerr << "Could not connect to system announcer port: " << snd_strerror(ret) << endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+void*
+AlsaDriver::refresh_main(void* me)
+{
+ AlsaDriver* ad = (AlsaDriver*)me;
+ ad->m_refresh_main();
+ return NULL;
+}
+
+
+void
+AlsaDriver::m_refresh_main()
+{
+ // "Heavily influenced" from alsa-patch-bay
+ // (C) 2002 Robert Ham, released under GPL
+
+ int ret;
+ int nfds = snd_seq_poll_descriptors_count(m_seq, POLLIN);
+ struct pollfd* pfds = new struct pollfd[nfds];
+ unsigned short* revents = new unsigned short[nfds];
+
+ if (!create_refresh_port()) {
+ cerr << "Could not create Alsa listen port. Auto refreshing will not work." << endl;
+ return;
+ }
+
+ snd_seq_poll_descriptors(m_seq, pfds, nfds, POLLIN);
+
+ while (true) {
+ ret = poll(pfds, nfds, -1);
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+
+ cerr << "Error polling Alsa sequencer: " << strerror(errno) << endl;
+ continue;
+ }
+
+ ret = snd_seq_poll_descriptors_revents(m_seq, pfds, nfds, revents);
+ if (ret) {
+ cerr << "Error getting Alsa sequencer poll events: "
+ << snd_strerror(ret) << endl;
+ continue;
+ }
+
+ for (int i = 0; i < nfds; ++i) {
+ if (revents[i] > 0) {
+ snd_seq_event_t* ev;
+ snd_seq_event_input(m_seq, &ev);
+
+ if (ev == NULL)
+ continue;
+
+ switch (ev->type) {
+ case SND_SEQ_EVENT_RESET:
+ case SND_SEQ_EVENT_CLIENT_START:
+ case SND_SEQ_EVENT_CLIENT_EXIT:
+ case SND_SEQ_EVENT_CLIENT_CHANGE:
+ case SND_SEQ_EVENT_PORT_START:
+ case SND_SEQ_EVENT_PORT_EXIT:
+ case SND_SEQ_EVENT_PORT_CHANGE:
+ case SND_SEQ_EVENT_PORT_SUBSCRIBED:
+ case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
+ m_is_dirty = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/AlsaDriver.h b/src/AlsaDriver.h
new file mode 100644
index 0000000..d6e910f
--- /dev/null
+++ b/src/AlsaDriver.h
@@ -0,0 +1,72 @@
+/* This file is part of Patchage. 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 ALSADRIVER_H
+#define ALSADRIVER_H
+
+#include <iostream>
+#include <alsa/asoundlib.h>
+#include <pthread.h>
+#include <queue>
+#include <string>
+#include "Driver.h"
+class Patchage;
+class PatchagePort;
+class PatchageFlowCanvas;
+
+using std::queue; using std::string;
+
+
+/** Handles all externally driven functionality, registering ports etc.
+ */
+class AlsaDriver : public Driver
+{
+public:
+ AlsaDriver(Patchage* app);
+ ~AlsaDriver();
+
+ void attach(bool launch_daemon = false);
+ void detach();
+
+ bool is_attached() const { return (m_seq != NULL); }
+
+ void refresh();
+
+ bool connect(const PatchagePort* const src_port, const PatchagePort* const dst_port);
+ bool disconnect(const PatchagePort* const src_port, const PatchagePort* const dst_port);
+
+ PatchageFlowCanvas* canvas() { return m_canvas; }
+
+private:
+ void refresh_ports();
+ void refresh_connections();
+
+ void add_connections(PatchagePort* port);
+
+ bool create_refresh_port();
+ static void* refresh_main(void* me);
+ void m_refresh_main();
+
+ Patchage* m_app;
+ PatchageFlowCanvas* m_canvas;
+
+ snd_seq_t* m_seq;
+
+ pthread_t m_refresh_thread;
+};
+
+
+#endif // ALSADRIVER_H
diff --git a/src/Driver.h b/src/Driver.h
new file mode 100644
index 0000000..43b919a
--- /dev/null
+++ b/src/Driver.h
@@ -0,0 +1,55 @@
+/* This file is part of Patchage. 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 DRIVER_H
+#define DRIVER_H
+
+class PatchagePort;
+
+/** Trival driver base class */
+class Driver {
+public:
+ virtual ~Driver() {}
+
+ virtual void attach(bool launch_daemon) = 0;
+ virtual void detach() = 0;
+ virtual bool is_attached() const = 0;
+
+ virtual void refresh() = 0;
+
+ virtual bool connect(const PatchagePort* src_port,
+ const PatchagePort* dst_port)
+ { return false; }
+
+ virtual bool disconnect(const PatchagePort* src_port,
+ const PatchagePort* dst_port)
+ { return false; }
+
+ /** Returns whether or not a refresh is required. */
+ bool is_dirty() const { return m_is_dirty; }
+
+ /** Clear 'dirty' status after a refresh. */
+ void undirty() { m_is_dirty = false; }
+
+protected:
+ Driver() : m_is_dirty(false) {}
+
+ bool m_is_dirty;
+};
+
+
+#endif // DRIVER_H
+
diff --git a/src/JackDriver.cpp b/src/JackDriver.cpp
new file mode 100644
index 0000000..5bb084e
--- /dev/null
+++ b/src/JackDriver.cpp
@@ -0,0 +1,258 @@
+/* This file is part of Patchage. 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 <cassert>
+#include <cstring>
+#include <string>
+#include <iostream>
+#include <jack/jack.h>
+#include "PatchageFlowCanvas.h"
+#include "JackDriver.h"
+#include "Patchage.h"
+#include "PatchageModule.h"
+
+using std::cerr; using std::endl; using std::cout;
+using std::string;
+
+using namespace LibFlowCanvas;
+
+
+JackDriver::JackDriver(Patchage* app)
+: m_app(app),
+ m_canvas(app->canvas()),
+ m_client(NULL)
+{
+}
+
+
+JackDriver::~JackDriver()
+{
+ detach();
+}
+
+
+/** Connect to Jack.
+ */
+void
+JackDriver::attach(bool launch_daemon)
+{
+ cout << "Connecting to Jack... ";
+ cout.flush();
+
+ if (m_client != NULL) {
+ cout << "already connected. " << endl;
+ return;
+ }
+
+ jack_options_t options = (!launch_daemon) ? JackNoStartServer : JackNullOption;
+ m_client = jack_client_open("Patchage", options, NULL);
+ if (m_client == NULL) {
+ cout << "Failed" << endl;
+ } else {
+ jack_set_port_registration_callback(m_client, jack_port_registration_cb, this);
+ jack_set_graph_order_callback(m_client, jack_graph_order_cb, this);
+ jack_on_shutdown(m_client, jack_shutdown_cb, this);
+
+ jack_activate(m_client);
+
+ cout << "Connected" << endl;
+
+ m_canvas->destroy();
+
+ m_is_dirty = true;
+ }
+}
+
+
+void
+JackDriver::detach()
+{
+ if (m_client != NULL) {
+ jack_deactivate(m_client);
+ jack_client_close(m_client);
+ m_client = NULL;
+ cout << "Disconnected from Jack" << endl;
+ }
+}
+
+
+/** Refresh all Jack audio ports/connections.
+ * To be called from GTK thread only.
+ */
+void
+JackDriver::refresh()
+{
+ if (m_client == NULL)
+ return;
+
+ const char** ports;
+ jack_port_t* port;
+
+ ports = jack_get_ports(m_client, NULL, NULL, 0); // get all existing ports
+
+ string client1_name;
+ string port1_name;
+ string client2_name;
+ string port2_name;
+ PatchageModule* m = NULL;
+
+ // Add all ports
+ if (ports != NULL)
+ for (int i=0; ports[i]; ++i) {
+ port = jack_port_by_name(m_client, ports[i]);
+ client1_name = ports[i];
+ client1_name = client1_name.substr(0, client1_name.find(":"));
+
+ ModuleType type = InputOutput;
+ //if (m_app->state_manager()->get_module_split(client1_name)
+ // || jack_port_flags(port) & JackPortIsTerminal) {
+ if (m_app->state_manager()->get_module_split(client1_name,
+ (jack_port_flags(port) & JackPortIsTerminal))) {
+ if (jack_port_flags(port) & JackPortIsInput) {
+ type = Input;
+ } else {
+ type = Output;
+ }
+ }
+
+ m = (PatchageModule*)m_canvas->find_module(client1_name, type);
+
+ if (m == NULL) {
+ m = new PatchageModule(m_app, client1_name, type);
+ m->load_location();
+ m->store_location();
+ m_canvas->add_module(m);
+ }
+
+ // FIXME: leak? jack docs don't say
+ const char* const type_str = jack_port_type(port);
+ PortType port_type = JACK_AUDIO;
+ if (!strcmp(type_str, "8 bit raw midi"))
+ port_type = JACK_MIDI;
+
+ m->add_patchage_port(jack_port_short_name(port),
+ (jack_port_flags(port) & JackPortIsInput),
+ port_type);
+ }
+
+ // Add all connections
+ if (ports != NULL) {
+ for (int i=0; ports[i]; ++i) {
+ port = jack_port_by_name(m_client, ports[i]);
+ const char** connected_ports = jack_port_get_all_connections(m_client, port);
+
+ if (connected_ports != NULL)
+ for (int j=0; connected_ports[j]; ++j) {
+ client1_name = ports[i];
+ port1_name = client1_name.substr(client1_name.find(':')+1);
+ client1_name = client1_name.substr(0, client1_name.find(':'));
+
+ client2_name = connected_ports[j];
+ port2_name = client2_name.substr(client2_name.find(':')+1);
+ client2_name = client2_name.substr(0, client2_name.find(':'));
+
+ m_canvas->add_connection(client1_name, port1_name, client2_name, port2_name);
+ }
+ free(connected_ports);
+ }
+ free(ports);
+ }
+
+ undirty();
+}
+
+
+/** Connects two Jack audio ports.
+ * To be called from GTK thread only.
+ * \return Whether connection succeeded.
+ */
+bool
+JackDriver::connect(const PatchagePort* const src_port, const PatchagePort* const dst_port)
+{
+ if (m_client == NULL)
+ return false;
+
+ int result = jack_connect(m_client, src_port->full_name().c_str(), dst_port->full_name().c_str());
+
+ string msg;
+
+ if (result == 0) {
+ msg = "Successfully connected jack ports";
+ } else {
+ msg = "Unable to connect ";
+ msg += src_port->full_name() + " -> " + dst_port->full_name();
+ }
+ m_app->status_message(msg);
+
+ return (!result);
+}
+
+
+/** Disconnects two Jack audio ports.
+ * To be called from GTK thread only.
+ * \return Whether disconnection succeeded.
+ */
+bool
+JackDriver::disconnect(const PatchagePort* const src_port, const PatchagePort* const dst_port)
+{
+ if (m_client == NULL)
+ return false;
+
+ int result = jack_disconnect(m_client, src_port->full_name().c_str(), dst_port->full_name().c_str());
+
+ string msg;
+
+ if (result == 0) {
+ msg = "Successfully disconnected jack ports";
+ } else {
+ msg = "Unable to disconnect ";
+ msg += src_port->full_name() + " -> " + dst_port->full_name();
+ }
+ m_app->status_message(msg);
+
+ return (!result);
+}
+
+
+void
+JackDriver::jack_port_registration_cb(jack_port_id_t port_id, int registered, void* me)
+{
+ assert(me != NULL);
+ assert(((JackDriver*)me)->m_client != NULL);
+ ((JackDriver*)me)->m_is_dirty = true;
+}
+
+
+int
+JackDriver::jack_graph_order_cb(void* me)
+{
+ assert(me != NULL);
+ assert(((JackDriver*)me)->m_client != NULL);
+ ((JackDriver*)me)->m_is_dirty = true;
+
+ return 0;
+}
+
+
+void
+JackDriver::jack_shutdown_cb(void* me)
+{
+ assert(me != NULL);
+ ((JackDriver*)me)->m_client = NULL;
+ ((JackDriver*)me)->m_is_dirty = true;
+}
+
+
diff --git a/src/JackDriver.h b/src/JackDriver.h
new file mode 100644
index 0000000..6b69900
--- /dev/null
+++ b/src/JackDriver.h
@@ -0,0 +1,68 @@
+/* This file is part of Patchage. 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 JACKDRIVER_H
+#define JACKDRIVER_H
+
+#include <iostream>
+#include <jack/jack.h>
+#include <string>
+#include "Driver.h"
+class Patchage;
+class PatchageFlowCanvas;
+class PatchagePort;
+
+using std::string;
+
+
+/** Handles all externally driven functionality, registering ports etc.
+ *
+ * Jack callbacks and connect methods and things like that live here.
+ * Right now just for jack ports, but that will change...
+ */
+class JackDriver : public Driver
+{
+public:
+ JackDriver(Patchage* app);
+ ~JackDriver();
+
+ void attach(bool launch_daemon);
+ void detach();
+
+ bool is_attached() const { return (m_client != NULL); }
+ void refresh();
+
+ bool connect(const PatchagePort* const src_port, const PatchagePort* const dst_port);
+ bool disconnect(const PatchagePort* const src_port, const PatchagePort* const dst_port);
+ /*bool connect(const string& src_module_name, const string& src_port_name,
+ const string& dst_module_name, const string& dest_port_name);
+
+ bool disconnect(const string& src_module_name, const string& src_port_name,
+ const string& dst_module_name, const string& dest_port_name);*/
+
+private:
+ Patchage* m_app;
+ PatchageFlowCanvas* m_canvas;
+
+ jack_client_t* m_client;
+
+ static void jack_port_registration_cb(jack_port_id_t port_id, int registered, void* controller);
+ static int jack_graph_order_cb(void* controller);
+ static void jack_shutdown_cb(void* controller);
+};
+
+
+#endif // JACKDRIVER_H
diff --git a/src/LashDriver.cpp b/src/LashDriver.cpp
new file mode 100644
index 0000000..c4a89b2
--- /dev/null
+++ b/src/LashDriver.cpp
@@ -0,0 +1,146 @@
+/* This file is part of Patchage. 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 "LashDriver.h"
+#include "config.h"
+#include <iostream>
+#include <string>
+#include "Patchage.h"
+#include "StateManager.h"
+
+using std::cerr; using std::cout; using std::endl;
+using std::string;
+
+
+LashDriver::LashDriver(Patchage* app, int argc, char** argv)
+: m_app(app),
+ m_client(NULL),
+ m_args(NULL)
+{
+ m_args = lash_extract_args(&argc, &argv);
+}
+
+
+LashDriver::~LashDriver()
+{
+ if (m_args)
+ lash_args_destroy(m_args);
+}
+
+
+void
+LashDriver::attach(bool launch_daemon)
+{
+ cout << "Connecting to Lash... ";
+ cout.flush();
+
+ if (m_client != NULL) {
+ cout << "already connected." << endl;
+ return;
+ }
+
+ int lash_flags = LASH_Config_File;
+ if (launch_daemon)
+ lash_flags |= LASH_No_Start_Server;
+ m_client = lash_init(m_args, PACKAGE_NAME, lash_flags, LASH_PROTOCOL(2, 0));
+ if (m_client == NULL) {
+ cout << "Failed. Session management will not occur." << endl;
+ } else {
+ lash_event_t* event = lash_event_new_with_type(LASH_Client_Name);
+ lash_event_set_string(event, "Patchage");
+ lash_send_event(m_client, event);
+ cout << "Connected" << endl;
+ }
+}
+
+
+void
+LashDriver::detach()
+{
+ // FIXME: send some notification that we're gone??
+ m_client = NULL;
+ cout << "Disconnected from Lash" << endl;
+}
+
+
+void
+LashDriver::process_events()
+{
+ 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
+LashDriver::handle_event(lash_event_t* ev)
+{
+ 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;
+
+ //cout << "[LashDriver] LASH Event. Type = " << type << ", string = " << str << "**********" << endl;
+
+ if (type == LASH_Save_File) {
+ //cout << "[LashDriver] LASH Save File - " << str << endl;
+ m_app->store_window_location();
+ m_app->state_manager()->save(str.append("/locations"));
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Save_File));
+ } else if (type == LASH_Restore_File) {
+ //cout << "[LashDriver] LASH Restore File - " << str << endl;
+ m_app->state_manager()->load(str.append("/locations"));
+ m_app->update_state();
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Restore_File));
+ } else if (type == LASH_Save_Data_Set) {
+ //cout << "[LashDriver] 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) {
+ //stop_thread();
+ m_client = NULL;
+ m_app->quit();
+ }
+}
+
+
+void
+LashDriver::handle_config(lash_config_t* conf)
+{
+ const char* key = NULL;
+ const void* val = NULL;
+ size_t val_size = 0;
+
+ //cout << "[LashDriver] 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);
+}
+
+
+
diff --git a/src/LashDriver.h b/src/LashDriver.h
new file mode 100644
index 0000000..742c249
--- /dev/null
+++ b/src/LashDriver.h
@@ -0,0 +1,50 @@
+/* This file is part of Patchage. 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 LASHDRIVER_H
+#define LASHDRIVER_H
+
+#include <lash/lash.h>
+#include "Driver.h"
+
+class Patchage;
+
+class LashDriver : public Driver
+{
+public:
+ LashDriver(Patchage* app, int argc, char** argv);
+ ~LashDriver();
+
+ void attach(bool launch_daemon);
+ void detach();
+
+ bool is_attached() const { return lash_enabled(m_client); }
+
+ void refresh() {}
+
+ void process_events();
+
+private:
+ Patchage* m_app;
+ lash_client_t* m_client;
+ lash_args_t* m_args;
+
+ void handle_event(lash_event_t* conf);
+ void handle_config(lash_config_t* conf);
+};
+
+
+#endif // LASHDRIVER_H
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..6e6eb5f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,27 @@
+AM_CXXFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" @LIBGLADEMM_CFLAGS@ @GNOMECANVASMM_CFLAGS@ @JACK_CFLAGS@ @ALSA_CFLAGS@ @LASH_CFLAGS@ @FLOWCANVAS_CFLAGS@
+patchage_LDADD = @LIBGLADEMM_LIBS@ @GNOMECANVASMM_LIBS@ @JACK_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @FLOWCANVAS_LIBS@
+
+EXTRA_DIST = patchage.gladep
+
+sharefilesdir = $(pkgdatadir)
+dist_sharefiles_DATA = patchage.glade
+
+bin_PROGRAMS = patchage
+patchage_SOURCES = \
+ main.cpp \
+ Patchage.h \
+ Patchage.cpp \
+ StateManager.h \
+ StateManager.cpp \
+ PatchageModule.h \
+ PatchagePort.h \
+ JackDriver.h \
+ JackDriver.cpp \
+ AlsaDriver.h \
+ AlsaDriver.cpp \
+ PatchageFlowCanvas.h \
+ PatchageFlowCanvas.cpp
+
+if WITH_LASH
+patchage_SOURCES += LashDriver.h LashDriver.cpp
+endif
diff --git a/src/Patchage.cpp b/src/Patchage.cpp
new file mode 100644
index 0000000..38d17f7
--- /dev/null
+++ b/src/Patchage.cpp
@@ -0,0 +1,411 @@
+/* This file is part of Patchage. 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 "Patchage.h"
+#include "config.h"
+#include <libgnomecanvasmm.h>
+#include <libglademm/xml.h>
+#include <fstream>
+#include <pthread.h>
+#include "StateManager.h"
+#include "PatchageFlowCanvas.h"
+#include "AlsaDriver.h"
+#include "JackDriver.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+
+
+Patchage::Patchage(int argc, char** argv)
+:
+#ifdef HAVE_LASH
+ m_lash_driver(NULL),
+#endif
+#ifdef HAVE_ALSA
+ m_alsa_driver(NULL),
+#endif
+ m_canvas(NULL),
+ m_jack_driver(NULL),
+ m_state_manager(NULL),
+ m_refresh(false)
+{
+ m_settings_filename = getenv("HOME");
+ m_settings_filename += "/.patchagerc";
+
+ m_state_manager = new StateManager();
+ m_canvas = new PatchageFlowCanvas(this, 1600*2, 1200*2);
+ m_jack_driver = new JackDriver(this);
+#ifdef HAVE_ALSA
+ m_alsa_driver = new AlsaDriver(this);
+#endif
+
+ m_state_manager->load(m_settings_filename);
+
+#ifdef HAVE_LASH
+ m_lash_driver = new LashDriver(this, argc, argv);
+#endif
+
+ Glib::RefPtr<Gnome::Glade::Xml> refXml;
+
+ // Check for the .glade file in current directory
+ string glade_filename = "./patchage.glade";
+ ifstream fs(glade_filename.c_str());
+ if (fs.fail()) { // didn't find it, check PKGDATADIR
+ fs.clear();
+ glade_filename = PKGDATADIR;
+ glade_filename += "/patchage.glade";
+
+ fs.open(glade_filename.c_str());
+ if (fs.fail()) {
+ cerr << "Unable to find patchage.glade in current directory or " << PKGDATADIR << "." << endl;
+ exit(EXIT_FAILURE);
+ }
+ fs.close();
+ }
+
+ try {
+ refXml = Gnome::Glade::Xml::create(glade_filename);
+ } catch(const Gnome::Glade::XmlError& ex) {
+ std::cerr << ex.what() << std::endl;
+ throw;
+ }
+
+ refXml->get_widget("patchage_win", m_main_window);
+ refXml->get_widget("about_win", m_about_window);
+ refXml->get_widget("about_project_label", m_about_project_label);
+ refXml->get_widget("launch_jack_menuitem", m_menu_jack_launch);
+ refXml->get_widget("connect_to_jack_menuitem", m_menu_jack_connect);
+ refXml->get_widget("disconnect_from_jack_menuitem", m_menu_jack_disconnect);
+#ifdef HAVE_LASH
+ refXml->get_widget("launch_lash_menuitem", m_menu_lash_launch);
+ refXml->get_widget("connect_to_lash_menuitem", m_menu_lash_connect);
+ refXml->get_widget("disconnect_from_lash_menuitem", m_menu_lash_disconnect);
+#endif
+#ifdef HAVE_ALSA
+ refXml->get_widget("connect_to_alsa_menuitem", m_menu_alsa_connect);
+ refXml->get_widget("disconnect_from_alsa_menuitem", m_menu_alsa_disconnect);
+#endif
+ refXml->get_widget("file_save_menuitem", m_menu_file_save);
+ refXml->get_widget("file_quit_menuitem", m_menu_file_quit);
+ refXml->get_widget("view_refresh_menuitem", m_menu_view_refresh);
+ refXml->get_widget("help_about_menuitem", m_menu_help_about);
+ refXml->get_widget("canvas_scrolledwindow", m_canvas_scrolledwindow);
+ refXml->get_widget("zoom_scale", m_zoom_slider);
+ refXml->get_widget("about_close_button", m_about_close_button);
+ refXml->get_widget("status_lab", m_status_label);
+
+ m_main_window->resize(
+ static_cast<int>(m_state_manager->get_window_size().x),
+ static_cast<int>(m_state_manager->get_window_size().y));
+
+ m_main_window->move(
+ static_cast<int>(m_state_manager->get_window_location().x),
+ static_cast<int>(m_state_manager->get_window_location().y));
+
+ m_canvas_scrolledwindow->add(*m_canvas);
+ //m_canvas_scrolledwindow->signal_event().connect(sigc::mem_fun(m_canvas, &FlowCanvas::scroll_event_handler));
+ m_canvas->scroll_to(static_cast<int>(m_canvas->width()/2 - 320),
+ static_cast<int>(m_canvas->height()/2 - 240)); // FIXME: hardcoded
+ m_canvas->show();
+
+ // Idle callback, check if we need to refresh (every 250msec)
+ Glib::signal_timeout().connect(sigc::mem_fun(this, &Patchage::idle_callback), 250);
+
+ m_zoom_slider->signal_value_changed().connect( sigc::mem_fun(this, &Patchage::zoom_changed));
+ m_menu_jack_launch->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_jack_launch));
+ m_menu_jack_connect->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_jack_connect));
+ m_menu_jack_disconnect->signal_activate().connect(sigc::mem_fun(this, &Patchage::menu_jack_disconnect));
+#ifdef HAVE_LASH
+ m_menu_lash_launch->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_lash_launch));
+ m_menu_lash_connect->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_lash_connect));
+ m_menu_lash_disconnect->signal_activate().connect(sigc::mem_fun(this, &Patchage::menu_lash_disconnect));
+#endif
+ m_menu_alsa_connect->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_alsa_connect));
+ m_menu_alsa_disconnect->signal_activate().connect(sigc::mem_fun(this, &Patchage::menu_alsa_disconnect));
+ m_menu_file_save->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_file_save));
+ m_menu_file_quit->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_file_quit));
+ m_menu_view_refresh->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_view_refresh));
+ m_menu_help_about->signal_activate().connect( sigc::mem_fun(this, &Patchage::menu_help_about));
+ m_about_close_button->signal_clicked().connect( sigc::mem_fun(this, &Patchage::close_about));
+
+ //_about_project_label->use_markup(true);
+ m_about_project_label->set_markup("<span size=\"xx-large\" weight=\"bold\">Patchage " PACKAGE_VERSION "</span>");
+}
+
+
+Patchage::~Patchage()
+{
+#ifdef HAVE_LASH
+ delete m_lash_driver;
+#endif
+ delete m_jack_driver;
+ delete m_alsa_driver;
+ delete m_canvas;
+ delete m_state_manager;
+}
+
+
+void
+Patchage::attach()
+{
+ m_jack_driver->attach(false);
+
+#ifdef HAVE_LASH
+ m_lash_driver->attach(false);
+#endif
+#ifdef HAVE_ALSA
+ m_alsa_driver->attach();
+#endif
+
+ update_menu_items();
+ menu_view_refresh();
+}
+
+
+bool
+Patchage::idle_callback()
+{
+ // FIXME: no need to destroy the whole canvas every time
+ if (m_refresh || (m_jack_driver && m_jack_driver->is_dirty())
+#ifdef HAVE_ALSA
+ || (m_alsa_driver && m_alsa_driver->is_dirty())
+#endif
+ ) {
+ m_canvas->destroy();
+ m_jack_driver->refresh();
+#ifdef HAVE_ALSA
+ m_alsa_driver->refresh();
+#endif
+ update_menu_items();
+ m_refresh = false;
+ }
+
+#ifdef HAVE_LASH
+ if (m_lash_driver->is_attached())
+ m_lash_driver->process_events();
+#endif
+
+ return true;
+}
+
+
+void
+Patchage::zoom_changed()
+{
+ const float z = m_zoom_slider->get_value();
+
+ m_canvas->zoom(z);
+ m_state_manager->set_zoom(z);
+}
+
+
+void
+Patchage::update_state()
+{
+ for (ModuleMap::iterator i = m_canvas->modules().begin(); i != m_canvas->modules().end(); ++i)
+ (*i).second->load_location();
+
+ cerr << "[Patchage] Resizing window: (" << m_state_manager->get_window_size().x
+ << "," << m_state_manager->get_window_size().y << ")" << endl;
+
+ m_main_window->resize(
+ static_cast<int>(m_state_manager->get_window_size().x),
+ static_cast<int>(m_state_manager->get_window_size().y));
+
+ cerr << "[Patchage] Moving window: (" << m_state_manager->get_window_location().x
+ << "," << m_state_manager->get_window_location().y << ")" << endl;
+ m_main_window->move(
+ static_cast<int>(m_state_manager->get_window_location().x),
+ static_cast<int>(m_state_manager->get_window_location().y));
+}
+
+
+void
+Patchage::status_message(const string& msg)
+{
+ m_status_label->set_text(msg);
+}
+
+
+
+
+/** Update the sensitivity status of menus to reflect the present.
+ *
+ * (eg. disable "Connect to Jack" when Patchage is already connected to Jack)
+ */
+void
+Patchage::update_menu_items()
+{
+ // Update Jack menu items
+ const bool jack_attached = m_jack_driver->is_attached();
+ m_menu_jack_launch->set_sensitive(!jack_attached);
+ m_menu_jack_connect->set_sensitive(!jack_attached);
+ m_menu_jack_disconnect->set_sensitive(jack_attached);
+
+ // Update Lash menu items
+#ifdef HAVE_LASH
+ const bool lash_attached = m_lash_driver->is_attached();
+ m_menu_lash_launch->set_sensitive(!lash_attached);
+ m_menu_lash_connect->set_sensitive(!lash_attached);
+ m_menu_lash_disconnect->set_sensitive(lash_attached);
+#endif
+
+#ifdef HAVE_ALSA
+ // Update Alsa menu items
+ const bool alsa_attached = m_alsa_driver->is_attached();
+ m_menu_alsa_connect->set_sensitive(!alsa_attached);
+ m_menu_alsa_disconnect->set_sensitive(alsa_attached);
+#endif
+}
+
+
+void
+Patchage::menu_jack_launch()
+{
+ m_jack_driver->attach(true);
+ update_menu_items();
+}
+
+
+void
+Patchage::menu_jack_connect()
+{
+ m_jack_driver->attach(false);
+ update_menu_items();
+}
+
+
+void
+Patchage::menu_jack_disconnect()
+{
+ m_jack_driver->detach();
+ menu_view_refresh();
+ update_menu_items();
+}
+
+#ifdef HAVE_LASH
+void
+Patchage::menu_lash_launch()
+{
+ m_lash_driver->attach(true);
+ update_menu_items();
+}
+
+
+void
+Patchage::menu_lash_connect()
+{
+ m_lash_driver->attach(false);
+ update_menu_items();
+}
+
+
+void
+Patchage::menu_lash_disconnect()
+{
+ m_lash_driver->detach();
+ update_menu_items();
+}
+#endif
+
+#ifdef HAVE_ALSA
+void
+Patchage::menu_alsa_connect()
+{
+ m_alsa_driver->attach(false);
+ update_menu_items();
+}
+
+
+void
+Patchage::menu_alsa_disconnect()
+{
+ m_alsa_driver->detach();
+ menu_view_refresh();
+ update_menu_items();
+}
+#endif
+
+void
+Patchage::menu_file_save()
+{
+ store_window_location();
+ m_state_manager->save(m_settings_filename);
+}
+
+
+void
+Patchage::menu_file_quit()
+{
+#ifdef HAVE_ALSA
+ m_alsa_driver->detach();
+#endif
+ m_jack_driver->detach();
+ m_main_window->hide();
+}
+
+
+void
+Patchage::menu_view_refresh()
+{
+ assert(m_canvas);
+
+ // FIXME: rebuilding the entire canvas each time is garbage
+ m_canvas->destroy();
+
+ if (m_jack_driver)
+ m_jack_driver->refresh();
+
+#ifdef HAVE_ALSA
+ if (m_alsa_driver)
+ m_alsa_driver->refresh();
+#endif
+}
+
+
+void
+Patchage::menu_help_about()
+{
+ m_about_window->show();
+}
+
+
+void
+Patchage::close_about()
+{
+ m_about_window->hide();
+}
+
+
+/** Update the stored window location and size in the StateManager (in memory).
+ */
+void
+Patchage::store_window_location()
+{
+ int loc_x, loc_y, size_x, size_y;
+ m_main_window->get_position(loc_x, loc_y);
+ m_main_window->get_size(size_x, size_y);
+ Coord window_location;
+ window_location.x = loc_x;
+ window_location.y = loc_y;
+ Coord window_size;
+ window_size.x = size_x;
+ window_size.y = size_y;
+ m_state_manager->set_window_location(window_location);
+ m_state_manager->set_window_size(window_size);
+}
+
+
diff --git a/src/Patchage.h b/src/Patchage.h
new file mode 100644
index 0000000..f686e49
--- /dev/null
+++ b/src/Patchage.h
@@ -0,0 +1,116 @@
+/* This file is part of Patchage. 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 PATCHAGE_H
+#define PATCHAGE_H
+
+#include "config.h"
+#include <string>
+#include <libgnomecanvasmm.h>
+
+using namespace std;
+
+class PatchageFlowCanvas;
+class JackDriver;
+class AlsaDriver;
+class LashDriver;
+class StateManager;
+
+
+class Patchage
+{
+public:
+ Patchage(int argc, char** argv);
+ ~Patchage();
+
+ PatchageFlowCanvas* canvas() { return m_canvas; }
+ StateManager* state_manager() { return m_state_manager; }
+ Gtk::Window* window() { return m_main_window; }
+ JackDriver* jack_driver() { return m_jack_driver; }
+#ifdef HAVE_ALSA
+ AlsaDriver* alsa_driver() { return m_alsa_driver; }
+#endif
+#ifdef HAVE_LASH
+ LashDriver* lash_driver() { return m_lash_driver; }
+#endif
+
+ void attach();
+ void quit() { m_main_window->hide(); }
+
+ void update_state();
+ void store_window_location();
+
+ void status_message(const string& msg);
+ inline void queue_refresh() { m_refresh = true; }
+
+protected:
+ void update_menu_items();
+
+ void menu_jack_launch();
+ void menu_jack_connect();
+ void menu_jack_disconnect();
+ void menu_file_save();
+ void menu_file_quit();
+ void menu_view_refresh();
+ void menu_help_about();
+ void close_about();
+ void zoom_changed();
+ bool idle_callback();
+
+#ifdef HAVE_LASH
+ LashDriver* m_lash_driver;
+ Gtk::MenuItem* m_menu_lash_launch;
+ Gtk::MenuItem* m_menu_lash_connect;
+ Gtk::MenuItem* m_menu_lash_disconnect;
+ void menu_lash_launch();
+ void menu_lash_connect();
+ void menu_lash_disconnect();
+#endif
+
+#ifdef HAVE_ALSA
+ AlsaDriver* m_alsa_driver;
+ Gtk::MenuItem* m_menu_alsa_connect;
+ Gtk::MenuItem* m_menu_alsa_disconnect;
+ void menu_alsa_connect();
+ void menu_alsa_disconnect();
+#endif
+
+ PatchageFlowCanvas* m_canvas;
+ JackDriver* m_jack_driver;
+ StateManager* m_state_manager;
+
+ Gtk::Main* m_gtk_main;
+
+ string m_settings_filename;
+ bool m_refresh;
+
+ Gtk::Window* m_main_window;
+ Gtk::Window* m_about_window;
+ Gtk::Label* m_about_project_label;
+ Gtk::MenuItem* m_menu_jack_launch;
+ Gtk::MenuItem* m_menu_jack_connect;
+ Gtk::MenuItem* m_menu_jack_disconnect;
+ Gtk::MenuItem* m_menu_file_save;
+ Gtk::MenuItem* m_menu_file_quit;
+ Gtk::MenuItem* m_menu_view_refresh;
+ Gtk::MenuItem* m_menu_help_about;
+ Gtk::ScrolledWindow* m_canvas_scrolledwindow;
+ Gtk::HScale* m_zoom_slider;
+ Gtk::Button* m_about_close_button;
+ Gtk::Label* m_status_label;
+};
+
+#endif // PATCHAGE_H
diff --git a/src/PatchageFlowCanvas.cpp b/src/PatchageFlowCanvas.cpp
new file mode 100644
index 0000000..2b9b19d
--- /dev/null
+++ b/src/PatchageFlowCanvas.cpp
@@ -0,0 +1,117 @@
+/* This file is part of Patchage. Copyright (C) 2004 Dave Robillard.
+ *
+ * Patchage 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.
+ *
+ * Patchage 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 "PatchageFlowCanvas.h"
+#include "Patchage.h"
+#include "JackDriver.h"
+#include "AlsaDriver.h"
+#include "PatchageModule.h"
+#include "PatchagePort.h"
+
+
+PatchageFlowCanvas::PatchageFlowCanvas(Patchage* app, int width, int height)
+: FlowCanvas(width, height),
+ m_app(app)
+{
+}
+
+
+PatchageModule*
+PatchageFlowCanvas::find_module(const string& name, ModuleType type)
+{
+ PatchageModule* pm = NULL;
+
+ for (ModuleMap::iterator m = m_modules.begin(); m != m_modules.end(); ++m) {
+ pm = (PatchageModule*)(*m).second;
+ if (pm->name() == name && pm->type() == type) {
+ return pm;
+ }
+ }
+
+ return NULL;
+}
+
+
+PatchagePort*
+PatchageFlowCanvas::find_port(const snd_seq_addr_t* alsa_addr, bool is_input)
+{
+ PatchagePort* pp = NULL;
+ for (ModuleMap::iterator m = m_modules.begin(); m != m_modules.end(); ++m) {
+ for (PortList::iterator p = (*m).second->ports().begin(); p != (*m).second->ports().end(); ++p) {
+ pp = (PatchagePort*)(*p);
+ if (pp->type() == ALSA_MIDI && pp->alsa_addr()
+ && pp->alsa_addr()->client == alsa_addr->client
+ && pp->alsa_addr()->port == alsa_addr->port)
+ if (is_input == pp->is_input())
+ return pp;
+ }
+ }
+
+ return NULL;
+}
+
+
+void
+PatchageFlowCanvas::connect(const Port* port1, const Port* port2)
+{
+ PatchagePort* p1 = (PatchagePort*)port1;
+ PatchagePort* p2 = (PatchagePort*)port2;
+
+ if (p1->type() == JACK_AUDIO && p2->type() == JACK_AUDIO
+ || (p1->type() == JACK_MIDI && p2->type() == JACK_MIDI))
+ /*m_app->jack_driver()->connect(p1->module()->name(), p1->name(),
+ p2->module()->name(), p2->name());*/
+ m_app->jack_driver()->connect(p1, p2);
+ else if (p1->type() == ALSA_MIDI && p2->type() == ALSA_MIDI)
+ m_app->alsa_driver()->connect(p1, p2);
+ else
+ m_app->status_message("Cannot make connection, incompatible port types.");
+}
+
+
+void
+PatchageFlowCanvas::disconnect(const Port* port1, const Port* port2)
+{
+ PatchagePort* input = NULL;
+ PatchagePort* output = NULL;
+
+ if (port1->is_input() && !port2->is_input()) {
+ input = (PatchagePort*)port1;
+ output = (PatchagePort*)port2;
+ } else if (port2->is_input() && !port1->is_input()) {
+ input = (PatchagePort*)port2;
+ output = (PatchagePort*)port1;
+ } else {
+ m_app->status_message("Attempt to disconnect two input (or output) ports?? Please report bug.");
+ return;
+ }
+
+ if (input->type() == JACK_AUDIO && output->type() == JACK_AUDIO
+ || input->type() == JACK_MIDI && output->type() == JACK_MIDI)
+ m_app->jack_driver()->disconnect(output, input);
+ else if (input->type() == ALSA_MIDI && output->type() == ALSA_MIDI)
+ m_app->alsa_driver()->disconnect(output, input);
+ else
+ m_app->status_message("Attempt to disconnect Jack audio port from Alsa Midi port?? Please report bug.");
+}
+
+
+void
+PatchageFlowCanvas::status_message(const string& msg)
+{
+ m_app->status_message(msg);
+}
+
diff --git a/src/PatchageFlowCanvas.h b/src/PatchageFlowCanvas.h
new file mode 100644
index 0000000..2636920
--- /dev/null
+++ b/src/PatchageFlowCanvas.h
@@ -0,0 +1,49 @@
+/* This file is part of Patchage. Copyright (C) 2004 Dave Robillard.
+ *
+ * Patchage 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.
+ *
+ * Patchage 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 PATCHAGEPATCHBAYAREA_H
+#define PATCHAGEPATCHBAYAREA_H
+
+#include <string>
+#include <alsa/asoundlib.h>
+#include <flowcanvas/FlowCanvas.h>
+#include "StateManager.h"
+class Patchage;
+class PatchageModule;
+class PatchagePort;
+
+using std::string;
+using namespace LibFlowCanvas;
+
+class PatchageFlowCanvas : public FlowCanvas
+{
+public:
+ PatchageFlowCanvas(Patchage* m_app, int width, int height);
+
+ PatchageModule* find_module(const string& name, ModuleType type);
+ PatchagePort* find_port(const snd_seq_addr_t* alsa_addr, bool is_input);
+
+ void connect(const Port* port1, const Port* port2);
+ void disconnect(const Port* port1, const Port* port2);
+
+ void status_message(const string& msg);
+
+private:
+ Patchage* m_app;
+};
+
+
+#endif // PATCHAGEPATCHBAYAREA_H
diff --git a/src/PatchageModule.h b/src/PatchageModule.h
new file mode 100644
index 0000000..b511eec
--- /dev/null
+++ b/src/PatchageModule.h
@@ -0,0 +1,120 @@
+/* This file is part of Patchage. Copyright (C) 2004 Dave Robillard.
+ *
+ * Patchage 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.
+ *
+ * Patchage 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 PATCHAGEMODULE_H
+#define PATCHAGEMODULE_H
+
+#include <string>
+#include <libgnomecanvasmm.h>
+#include <alsa/asoundlib.h>
+#include <flowcanvas/FlowCanvas.h>
+#include <flowcanvas/Module.h>
+#include "PatchageFlowCanvas.h"
+#include "StateManager.h"
+#include "PatchagePort.h"
+
+using std::string; using std::list;
+
+using namespace LibFlowCanvas;
+
+class PatchageModule : public Module
+{
+public:
+ PatchageModule(Patchage* app, const string& title, ModuleType type, double x=0, double y=0)
+ : Module(app->canvas(), title, x, y),
+ m_app(app),
+ m_type(type)
+ {
+ Gtk::Menu::MenuList& items = m_menu.items();
+ if (type == InputOutput) {
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Split",
+ sigc::mem_fun(this, &PatchageModule::split)));
+ } else {
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Join",
+ sigc::mem_fun(this, &PatchageModule::join)));
+ }
+ items.push_back(Gtk::Menu_Helpers::MenuElem("Disconnect All",
+ sigc::mem_fun(this, &PatchageModule::menu_disconnect_all)));
+ }
+
+ virtual ~PatchageModule() { }
+
+ virtual void add_patchage_port(const string& port_name, bool is_input, PortType type)
+ {
+ PatchagePort* port = new PatchagePort(this, type, port_name, is_input,
+ m_app->state_manager()->get_port_color(type));
+
+ Module::add_port(port, true);
+ }
+
+ virtual void add_patchage_port(const string& port_name, bool is_input, PortType type, const snd_seq_addr_t addr)
+ {
+ PatchagePort* port = new PatchagePort(this, type, port_name, is_input,
+ m_app->state_manager()->get_port_color(type));
+
+ port->alsa_addr(addr);
+
+ Module::add_port(port, true);
+ }
+
+
+ virtual void load_location() {
+ Coord loc = m_app->state_manager()->get_module_location(m_name, m_type);
+
+ //cerr << "******" << m_name << " MOVING TO (" << loc.x << "," << loc.y << ")" << endl;
+
+ if (loc.x != -1)
+ move_to(loc.x, loc.y);
+ else
+ move_to((m_canvas->width()/2) - 100 + rand() % 400,
+ (m_canvas->height()/2) - 100 + rand() % 400);
+ }
+
+ void split() {
+ assert(m_type == InputOutput);
+ m_app->state_manager()->set_module_split(m_name, true);
+ m_app->queue_refresh();
+ }
+
+ void join() {
+ assert(m_type != InputOutput);
+ m_app->state_manager()->set_module_split(m_name, false);
+ m_app->queue_refresh();
+ }
+
+ virtual void store_location() {
+ Coord loc = { property_x().get_value(), property_y().get_value() };
+ m_app->state_manager()->set_module_location(m_name, m_type, loc);
+ }
+
+ virtual void show_dialog() {}
+ virtual void on_right_click(GdkEventButton* ev) { m_menu.popup(ev->button, ev->time); }
+ virtual void menu_disconnect_all() {
+ for (PortList::iterator p = m_ports.begin(); p != m_ports.end(); ++p)
+ (*p)->disconnect_all();
+ }
+
+ ModuleType type() { return m_type; }
+
+protected:
+ Patchage* m_app;
+ Gtk::Menu m_menu;
+ ModuleType m_type;
+};
+
+
+#endif // PATCHAGEMODULE_H
diff --git a/src/PatchagePort.h b/src/PatchagePort.h
new file mode 100644
index 0000000..601bce9
--- /dev/null
+++ b/src/PatchagePort.h
@@ -0,0 +1,66 @@
+/* This file is part of Om. Copyright (C) 2004 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 PATCHAGEPORT_H
+#define PATCHAGEPORT_H
+
+#include <string>
+#include <list>
+#include <alsa/asoundlib.h>
+#include <flowcanvas/Port.h>
+#include <flowcanvas/Module.h>
+
+using namespace LibFlowCanvas;
+using std::string; using std::list;
+
+enum PortType { JACK_AUDIO, JACK_MIDI, ALSA_MIDI };
+
+
+/** A Port on a PatchageModule
+ *
+ * \ingroup OmGtk
+ */
+class PatchagePort : public LibFlowCanvas::Port
+{
+public:
+ PatchagePort(LibFlowCanvas::Module* module, PortType type, const string& name, bool is_input, int color)
+ : Port(module, name, is_input, color),
+ m_type(type)
+ {
+ m_alsa_addr.client = '\0';
+ m_alsa_addr.port = '\0';
+ }
+
+ virtual ~PatchagePort() {}
+
+ // FIXME: This driver specific crap really needs to go
+ void alsa_addr(const snd_seq_addr_t addr) { m_alsa_addr = addr; }
+ const snd_seq_addr_t* alsa_addr() const
+ { return (m_type == ALSA_MIDI) ? &m_alsa_addr : NULL; }
+
+ /** Returns the full name of this port, as "modulename:portname" */
+ string full_name() const { return m_module->name() + ":" + m_name; }
+
+ PortType type() const { return m_type; }
+
+private:
+ snd_seq_addr_t m_alsa_addr;
+ PortType m_type;
+};
+
+
+#endif // PATCHAGEPORT_H
diff --git a/src/StateManager.cpp b/src/StateManager.cpp
new file mode 100644
index 0000000..50429e1
--- /dev/null
+++ b/src/StateManager.cpp
@@ -0,0 +1,246 @@
+/* This file is part of Patchage. 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 "StateManager.h"
+#include <stdlib.h>
+#include <iostream>
+#include <fstream>
+#include "Patchage.h"
+
+using std::map; using std::list;
+using std::cerr; using std::cout; using std::endl;
+
+
+StateManager::StateManager()
+{
+ m_window_location.x = 0;
+ m_window_location.y = 0;
+ m_window_size.x = 640;
+ m_window_size.y = 480;
+ m_zoom = 1.0;
+}
+
+
+Coord
+StateManager::get_module_location(const string& name, ModuleType type)
+{
+ for (std::list<ModuleLocation>::iterator i = m_module_locations.begin(); i != m_module_locations.end(); ++i) {
+ if ((*i).name == name && (*i).type == type)
+ return (*i).loc;
+ }
+
+ // -1 used as a flag value
+ Coord r = { -1, -1 };
+ return r;
+}
+
+
+void
+StateManager::set_module_location(const string& name, ModuleType type, Coord loc)
+{
+ for (std::list<ModuleLocation>::iterator i = m_module_locations.begin(); i != m_module_locations.end(); ++i) {
+ if ((*i).name == name && (*i).type == type) {
+ (*i).loc = loc;
+ return;
+ }
+ }
+
+ // If we get here, module isn't in list yet
+ ModuleLocation ml = { name, type, loc };
+ m_module_locations.push_back(ml);
+}
+
+
+/** Returns whether or not this module should be split.
+ *
+ * If nothing is known about the given module, @a default_val is returned (this is
+ * to allow driver's to request terminal ports get split by default).
+ */
+bool
+StateManager::get_module_split(const string& name, bool default_val) const
+{
+ map<string, bool>::const_iterator i = m_module_splits.find(name);
+ if (i == m_module_splits.end())
+ return default_val;
+ else
+ return (*i).second;
+}
+
+
+void
+StateManager::set_module_split(const string& name, bool split)
+{
+ m_module_splits[name] = split;
+}
+
+
+void
+StateManager::load(const string& filename)
+{
+ m_module_locations.clear();
+
+ cerr << "Loading configuration file " << filename << endl;
+
+ std::ifstream is;
+ is.open(filename.c_str(), std::ios::in);
+
+ if ( ! is.good()) {
+ std::cerr << "Unable to load file " << filename << "!" << endl;
+ return;
+ }
+
+ string s;
+
+ is >> s;
+ if (s != "window_location") throw "Corrupt settings file.";
+ is >> s;
+ m_window_location.x = atoi(s.c_str());
+ is >> s;
+ m_window_location.y = atoi(s.c_str());
+
+ is >> s;
+ if (s != "window_size") throw "Corrupt settings file.";
+ is >> s;
+ m_window_size.x = atoi(s.c_str());
+ is >> s;
+ m_window_size.y = atoi(s.c_str());
+
+ is >> s;
+ if (s != "zoom_level") throw "Corrupt settings file.";
+ is >> s;
+ m_zoom = atof(s.c_str());
+
+ ModuleLocation ml;
+ while (1) {
+ is >> s;
+ if (is.eof()) break;
+
+ // Old versions didn't quote, so need to support both :/
+ if (s[0] == '\"') {
+ if (s.length() > 1 && s[s.length()-1] == '\"') {
+ ml.name = s.substr(1, s.length()-2);
+ } else {
+ ml.name = s.substr(1);
+ is >> s;
+ while (s[s.length()-1] != '\"') {
+ ml.name.append(" ").append(s);
+ is >> s;
+ }
+ ml.name.append(" ").append(s.substr(0, s.length()-1));
+ }
+ } else {
+ ml.name = s;
+ }
+
+ is >> s;
+ if (s == "input") ml.type = Input;
+ else if (s == "output") ml.type = Output;
+ else if (s == "inputoutput") ml.type = InputOutput;
+ else throw "Corrupt settings file.";
+
+ is >> s;
+ ml.loc.x = atoi(s.c_str());
+ is >> s;
+ ml.loc.y = atoi(s.c_str());
+
+ m_module_locations.push_back(ml);
+ }
+
+ is.close();
+}
+
+
+void
+StateManager::save(const string& filename)
+{
+ std::ofstream os;
+ os.open(filename.c_str(), std::ios::out);
+
+ os << "window_location " << m_window_location.x << " " << m_window_location.y << std::endl;
+ os << "window_size " << m_window_size.x << " " << m_window_size.y << std::endl;
+ os << "zoom_level " << m_zoom << std::endl;
+
+ ModuleLocation ml;
+ for (std::list<ModuleLocation>::iterator i = m_module_locations.begin(); i != m_module_locations.end(); ++i) {
+ ml = *i;
+ os << "\"" << ml.name << "\"";
+
+ if (ml.type == Input) os << " input ";
+ else if (ml.type == Output) os << " output ";
+ else if (ml.type == InputOutput) os << " inputoutput ";
+ else throw;
+
+ os << ml.loc.x << " " << ml.loc.y << std::endl;
+ }
+
+ os.close();
+}
+
+
+Coord
+StateManager::get_window_location()
+{
+ return m_window_location;
+}
+
+
+void
+StateManager::set_window_location(Coord loc)
+{
+ m_window_location = loc;
+}
+
+
+Coord
+StateManager::get_window_size()
+{
+ return m_window_size;
+}
+
+
+void
+StateManager::set_window_size(Coord size)
+{
+ m_window_size = size;
+}
+
+
+float
+StateManager::get_zoom()
+{
+ return m_zoom;
+}
+
+
+void
+StateManager::set_zoom(float zoom)
+{
+ m_zoom = zoom;
+}
+
+
+int
+StateManager::get_port_color(PortType type)
+{
+ if (type == JACK_AUDIO)
+ return 0x305171FF;
+ else if (type == JACK_MIDI)
+ return 0x663939FF;
+ else if (type == ALSA_MIDI)
+ return 0x307130FF;
+ else
+ return 0xFF0000FF;
+}
diff --git a/src/StateManager.h b/src/StateManager.h
new file mode 100644
index 0000000..b584308
--- /dev/null
+++ b/src/StateManager.h
@@ -0,0 +1,75 @@
+/* This file is part of Patchage. 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 STATEMANAGER_H
+#define STATEMANAGER_H
+
+#include <string>
+#include <list>
+#include <map>
+#include <iostream>
+#include "PatchagePort.h"
+
+using std::string; using std::list; using std::map;
+
+
+enum ModuleType { Input, Output, InputOutput };
+struct Coord { double x; double y; };
+
+// This should probably be moved out in to a seperate class/file....
+typedef struct ModuleLocation
+{
+ string name;
+ ModuleType type; // for distinguishing terminal modules (input or output)
+ Coord loc;
+};
+
+
+class StateManager
+{
+public:
+ StateManager();
+
+ void load(const string& filename);
+ void save(const string& filename);
+
+ Coord get_module_location(const string& name, ModuleType type);
+ void set_module_location(const string& name, ModuleType type, Coord loc);
+
+ void set_module_split(const string& name, bool split);
+ bool get_module_split(const string& name, bool default_val) const;
+
+ Coord get_window_location();
+ void set_window_location(Coord loc);
+
+ Coord get_window_size();
+ void set_window_size(Coord loc);
+
+ float get_zoom();
+ void set_zoom(float zoom);
+
+ int get_port_color(PortType type);
+
+private:
+ list<ModuleLocation> m_module_locations;
+ map<string,bool> m_module_splits;
+ Coord m_window_location;
+ Coord m_window_size;
+ float m_zoom;
+};
+
+
+#endif // STATEMANAGER_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..5646b1c
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,49 @@
+/* This file is part of Patchage. 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 "config.h"
+
+#include <iostream>
+#include <libgnomecanvasmm.h>
+
+#include "Patchage.h"
+#include "JackDriver.h"
+
+#ifdef HAVE_LASH
+#include <lash/lash.h>
+#endif // HAVE_LASH
+
+int main(int argc, char** argv)
+{
+ try {
+
+ Gnome::Canvas::init();
+ Gtk::Main app(argc, argv);
+
+ Patchage patchage(argc, argv);
+ patchage.attach();
+
+ app.run(*patchage.window());
+
+ } catch (std::string msg) {
+ std::cerr << "Caught exception, aborting. Error message was: " << msg << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+
diff --git a/src/patchage.glade b/src/patchage.glade
new file mode 100644
index 0000000..322a714
--- /dev/null
+++ b/src/patchage.glade
@@ -0,0 +1,607 @@
+<?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="patchage_win">
+ <property name="border_width">1</property>
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Patchage</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="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="main_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="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="file_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="launch_jack_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Launch Jack</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_launch_jack_menuitem_activate" last_modification_time="Wed, 05 Apr 2006 04:52:35 GMT"/>
+ <accelerator key="J" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image143">
+ <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="connect_to_jack_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Connect to _Jack</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_connect_to_jack_menuitem_activate" last_modification_time="Wed, 05 Apr 2006 04:27:40 GMT"/>
+ <accelerator key="J" modifiers="GDK_MOD1_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image144">
+ <property name="visible">True</property>
+ <property name="stock">gtk-connect</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="disconnect_from_jack_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Disconnect from Jack</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_disconnect_from_jack1_activate" last_modification_time="Sun, 21 May 2006 23:48:26 GMT"/>
+ <accelerator key="J" modifiers="GDK_SHIFT_MASK | GDK_MOD1_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image145">
+ <property name="visible">True</property>
+ <property name="stock">gtk-disconnect</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="separator1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="launch_lash_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Launch Lash</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_launch_lash1_activate" last_modification_time="Mon, 22 May 2006 01:14:33 GMT"/>
+ <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image146">
+ <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="connect_to_lash_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Connect to _Lash</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_connect_to_lash1_activate" last_modification_time="Mon, 22 May 2006 01:14:33 GMT"/>
+ <accelerator key="L" modifiers="GDK_MOD1_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image147">
+ <property name="visible">True</property>
+ <property name="stock">gtk-connect</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="disconnect_from_lash_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Disconnect from Lash</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_disconnect_from_lash1_activate" last_modification_time="Mon, 22 May 2006 01:14:33 GMT"/>
+ <accelerator key="L" modifiers="GDK_SHIFT_MASK | GDK_MOD1_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image148">
+ <property name="visible">True</property>
+ <property name="stock">gtk-disconnect</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="separator4">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="connect_to_alsa_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Connect to _Alsa</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_connect_to_alsa_menuitem_activate" last_modification_time="Mon, 22 May 2006 00:10:31 GMT"/>
+ <accelerator key="A" modifiers="GDK_MOD1_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image149">
+ <property name="visible">True</property>
+ <property name="stock">gtk-connect</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="disconnect_from_alsa_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Disconnect from Alsa</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_disconnect_from_alsa_menuitem_activate" last_modification_time="Mon, 22 May 2006 00:10:31 GMT"/>
+ <accelerator key="A" modifiers="GDK_SHIFT_MASK | GDK_MOD1_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image150">
+ <property name="visible">True</property>
+ <property name="stock">gtk-disconnect</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="separator3">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="file_save_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Store Positions</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_save_settings1_activate" last_modification_time="Mon, 13 Sep 2004 02:42:14 GMT"/>
+ <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image151">
+ <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="GtkSeparatorMenuItem" id="separator2">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="file_quit_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-quit</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_quit1_activate" last_modification_time="Sat, 11 Sep 2004 20:05:11 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="view_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="view_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="view_refresh_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-refresh</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_item1_activate" last_modification_time="Sat, 11 Sep 2004 20:05:50 GMT"/>
+ </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>
+
+ <child>
+ <widget class="GtkMenu" id="help_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="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="Sat, 11 Sep 2004 20:05:11 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="GtkScrolledWindow" id="canvas_scrolledwindow">
+ <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="GtkLabel" id="status_lab">
+ <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="padding">2</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorToolItem" id="separatortoolitem1">
+ <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="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Zoom: </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="GtkHScale" id="zoom_scale">
+ <property name="width_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">True</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 0.1 2 0 0 0</property>
+ </widget>
+ <packing>
+ <property name="padding">5</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">2</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="about_win">
+ <property name="width_request">320</property>
+ <property name="height_request">200</property>
+ <property name="title" translatable="yes">About Patchage</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">False</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="skip_pager_hint">True</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="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="about_project_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;span size=&quot;xx-large&quot; weight=&quot;bold&quot;&gt;Patchage&lt;/span&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="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">A modular patch bay for:
+
+Jack Audio Connection Kit
+ALSA Sequencer</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</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="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Copyright © 2005 Dave Robillard</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">10</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
+ <property name="spacing">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="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="about_close_button">
+ <property name="width_request">66</property>
+ <property name="height_request">26</property>
+ <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">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/src/patchage.gladep b/src/patchage.gladep
new file mode 100644
index 0000000..8d205c3
--- /dev/null
+++ b/src/patchage.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>Patchage</name>
+ <program_name>patchage</program_name>
+ <language>C++</language>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>