summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2008-08-22 03:26:33 +0000
committerDavid Robillard <d@drobilla.net>2008-08-22 03:26:33 +0000
commit4637ceb876904731d2628563e96a89961f9b7781 (patch)
treeb20bd41c8fcdee1b8a5e76a0aa4a3ca213f82473 /src
parent503944e72ce146e5bed2556e7b2caa5a41edd7ea (diff)
downloadpatchage-4637ceb876904731d2628563e96a89961f9b7781.tar.gz
patchage-4637ceb876904731d2628563e96a89961f9b7781.tar.bz2
patchage-4637ceb876904731d2628563e96a89961f9b7781.zip
Lash D-Bus support and projects list from LADI Patchage, with improvements/cleanup/sanification/etc.
Remove liblash stuff (meh, what the hell... here's to new beginnings). Enable/disable/hide/etc patchage widgets better based on available (compiled in) functionality. git-svn-id: http://svn.drobilla.net/lad/patchage@1462 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r--src/AlsaDriver.cpp14
-rw-r--r--src/DBus.cpp121
-rw-r--r--src/DBus.hpp61
-rw-r--r--src/JackDriver.cpp18
-rw-r--r--src/JackSettingsDialog.hpp84
-rw-r--r--src/LashClient.cpp83
-rw-r--r--src/LashClient.hpp61
-rw-r--r--src/LashDriver.cpp170
-rw-r--r--src/LashDriver.hpp82
-rw-r--r--src/LashProxy.cpp781
-rw-r--r--src/LashProxy.hpp61
-rw-r--r--src/LoadProjectDialog.cpp154
-rw-r--r--src/LoadProjectDialog.hpp53
-rw-r--r--src/Makefile.am25
-rw-r--r--src/Patchage.cpp208
-rw-r--r--src/Patchage.hpp65
-rw-r--r--src/PatchageCanvas.cpp2
-rw-r--r--src/Project.cpp175
-rw-r--r--src/Project.hpp73
-rw-r--r--src/ProjectList.cpp305
-rw-r--r--src/ProjectList.hpp38
-rw-r--r--src/ProjectPropertiesDialog.cpp83
-rw-r--r--src/ProjectPropertiesDialog.hpp38
-rw-r--r--src/Session.cpp124
-rw-r--r--src/Session.hpp54
-rw-r--r--src/Widget.hpp1
-rw-r--r--src/patchage.glade483
27 files changed, 2691 insertions, 726 deletions
diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp
index 8794386..1662471 100644
--- a/src/AlsaDriver.cpp
+++ b/src/AlsaDriver.cpp
@@ -53,10 +53,10 @@ AlsaDriver::attach(bool /*launch_daemon*/)
SND_SEQ_OPEN_DUPLEX,
SND_SEQ_NONBLOCK);
if (ret) {
- _app->status_message("[ALSA] Unable to attach");
+ _app->status_msg("[ALSA] Unable to attach");
_seq = NULL;
} else {
- _app->status_message("[ALSA] Attached");
+ _app->status_msg("[ALSA] Attached");
snd_seq_set_client_name(_seq, "Patchage");
@@ -82,7 +82,7 @@ AlsaDriver::detach()
snd_seq_close(_seq);
_seq = NULL;
signal_detached.emit();
- _app->status_message("[ALSA] Detached");
+ _app->status_msg("[ALSA] Detached");
}
}
@@ -395,10 +395,10 @@ AlsaDriver::connect(boost::shared_ptr<PatchagePort> src_port, boost::shared_ptr<
}
if (result)
- _app->status_message(string("[ALSA] Connected ")
+ _app->status_msg(string("[ALSA] Connected ")
+ src_port->full_name() + " -> " + dst_port->full_name());
else
- _app->status_message(string("[ALSA] Unable to connect ")
+ _app->status_msg(string("[ALSA] Unable to connect ")
+ src_port->full_name() + " -> " + dst_port->full_name());
return (!result);
@@ -438,10 +438,10 @@ AlsaDriver::disconnect(boost::shared_ptr<PatchagePort> src_port, boost::shared_p
}
if (result)
- _app->status_message(string("[ALSA] Disconnected ")
+ _app->status_msg(string("[ALSA] Disconnected ")
+ src_port->full_name() + " -> " + dst_port->full_name());
else
- _app->status_message(string("[ALSA] Unable to disconnect ")
+ _app->status_msg(string("[ALSA] Unable to disconnect ")
+ src_port->full_name() + " -> " + dst_port->full_name());
return (!result);
diff --git a/src/DBus.cpp b/src/DBus.cpp
new file mode 100644
index 0000000..5bd648b
--- /dev/null
+++ b/src/DBus.cpp
@@ -0,0 +1,121 @@
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <dave@drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "DBus.hpp"
+#include <stdexcept>
+#include <boost/format.hpp>
+#include "Patchage.hpp"
+
+#define DBUS_CALL_DEFAULT_TIMEOUT 1000 // in milliseconds
+
+
+DBus::DBus(Patchage* app)
+ : _app(app)
+ , _connection(NULL)
+{
+ dbus_error_init(&_error);
+
+ // Connect to the bus
+ _connection = dbus_bus_get(DBUS_BUS_SESSION, &_error);
+ if (dbus_error_is_set(&_error)) {
+ app->error_msg("dbus_bus_get() failed");
+ app->error_msg(_error.message);
+ dbus_error_free(&_error);
+ }
+
+ dbus_connection_setup_with_g_main(_connection, NULL);
+}
+
+
+bool
+DBus::call(
+ bool response_expected,
+ const char * service,
+ const char * object,
+ const char * iface,
+ const char * method,
+ DBusMessage ** reply_ptr_ptr,
+ int in_type,
+ va_list ap)
+{
+ DBusMessage* request_ptr;
+ DBusMessage* reply_ptr;
+
+ request_ptr = dbus_message_new_method_call(
+ service,
+ object,
+ iface,
+ method);
+ if (!request_ptr) {
+ throw std::runtime_error("dbus_message_new_method_call() returned 0");
+ }
+
+ dbus_message_append_args_valist(request_ptr, in_type, ap);
+
+ // send message and get a handle for a reply
+ reply_ptr = dbus_connection_send_with_reply_and_block(
+ _connection,
+ request_ptr,
+ DBUS_CALL_DEFAULT_TIMEOUT,
+ &_error);
+
+ dbus_message_unref(request_ptr);
+
+ if (!reply_ptr) {
+ if (response_expected) {
+ _app->error_msg(str(boost::format("no reply from server when calling method '%s'"
+ ", error is '%s'") % method % _error.message));
+ }
+ dbus_error_free(&_error);
+ } else {
+ *reply_ptr_ptr = reply_ptr;
+ }
+
+ return reply_ptr;
+}
+
+bool
+DBus::call(
+ bool response_expected,
+ const char * serivce,
+ const char * object,
+ const char * iface,
+ const char * method,
+ DBusMessage ** reply_ptr_ptr,
+ int in_type,
+ ...)
+{
+ bool ret;
+ va_list ap;
+
+ va_start(ap, in_type);
+
+ ret = _app->dbus()->call(
+ response_expected,
+ serivce,
+ object,
+ iface,
+ method,
+ reply_ptr_ptr,
+ in_type,
+ ap);
+
+ va_end(ap);
+
+ return (ap != NULL);
+}
diff --git a/src/DBus.hpp b/src/DBus.hpp
new file mode 100644
index 0000000..ffcabbb
--- /dev/null
+++ b/src/DBus.hpp
@@ -0,0 +1,61 @@
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGE_DBUS_HPP
+#define PATCHAGE_DBUS_HPP
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+class Patchage;
+
+class DBus {
+public:
+ DBus(Patchage* app);
+
+ bool call(
+ bool response_expected,
+ const char* service,
+ const char* object,
+ const char* iface,
+ const char* method,
+ DBusMessage** reply_ptr,
+ int in_type,
+ va_list ap);
+
+ bool call(
+ bool response_expected,
+ const char* service,
+ const char* object,
+ const char* iface,
+ const char* method,
+ DBusMessage** reply_ptr,
+ int in_type,
+ ...);
+
+ DBusConnection* connection() { return _connection; }
+ DBusError& error() { return _error; }
+
+private:
+ Patchage* _app;
+ DBusConnection* _connection;
+ DBusError _error;
+};
+
+#endif // PATCHAGE_DBUS_HPP
diff --git a/src/JackDriver.cpp b/src/JackDriver.cpp
index 2b98ecb..d31d499 100644
--- a/src/JackDriver.cpp
+++ b/src/JackDriver.cpp
@@ -68,7 +68,7 @@ JackDriver::attach(bool launch_daemon)
jack_options_t options = (!launch_daemon) ? JackNoStartServer : JackNullOption;
_client = jack_client_open("Patchage", options, NULL);
if (_client == NULL) {
- _app->status_message("[JACK] Unable to create client");
+ _app->status_msg("[JACK] Unable to create client");
_is_activated = false;
} else {
jack_client_t* const client = _client;
@@ -87,9 +87,9 @@ JackDriver::attach(bool launch_daemon)
if (!jack_activate(client)) {
_is_activated = true;
signal_attached.emit();
- _app->status_message("[JACK] Attached");
+ _app->status_msg("[JACK] Attached");
} else {
- _app->status_message("[JACK] ERROR: Failed to attach");
+ _app->status_msg("[JACK] ERROR: Failed to attach");
_is_activated = false;
}
}
@@ -108,7 +108,7 @@ JackDriver::detach()
destroy_all_ports();
_is_activated = false;
signal_detached.emit();
- _app->status_message("[JACK] Detached");
+ _app->status_msg("[JACK] Detached");
}
}
@@ -388,10 +388,10 @@ JackDriver::connect(boost::shared_ptr<PatchagePort> src_port, boost::shared_ptr<
int result = jack_connect(_client, src_port->full_name().c_str(), dst_port->full_name().c_str());
if (result == 0)
- _app->status_message(string("[JACK] Connected ")
+ _app->status_msg(string("[JACK] Connected ")
+ src_port->full_name() + " -> " + dst_port->full_name());
else
- _app->status_message(string("[JACK] Unable to connect ")
+ _app->status_msg(string("[JACK] Unable to connect ")
+ src_port->full_name() + " -> " + dst_port->full_name());
return (!result);
@@ -411,10 +411,10 @@ JackDriver::disconnect(boost::shared_ptr<PatchagePort> const src_port, boost::sh
int result = jack_disconnect(_client, src_port->full_name().c_str(), dst_port->full_name().c_str());
if (result == 0)
- _app->status_message(string("[JACK] Disconnected ")
+ _app->status_msg(string("[JACK] Disconnected ")
+ src_port->full_name() + " -> " + dst_port->full_name());
else
- _app->status_message(string("[JACK] Unable to disconnect ")
+ _app->status_msg(string("[JACK] Unable to disconnect ")
+ src_port->full_name() + " -> " + dst_port->full_name());
return (!result);
@@ -574,7 +574,7 @@ JackDriver::set_buffer_size(jack_nframes_t size)
}
if (jack_set_buffer_size(_client, size)) {
- _app->status_message("[JACK] ERROR: Unable to set buffer size");
+ _app->status_msg("[JACK] ERROR: Unable to set buffer size");
return false;
} else {
return true;
diff --git a/src/JackSettingsDialog.hpp b/src/JackSettingsDialog.hpp
deleted file mode 100644
index f26fa91..0000000
--- a/src/JackSettingsDialog.hpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/* This file is part of Patchage.
- * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
- *
- * 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.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#ifndef PATCHAGE_JACK_SETTINGS_DIALOG_HPP
-#define PATCHAGE_JACK_SETTINGS_DIALOG_HPP
-
-#include <libglademm/xml.h>
-#include <libglademm.h>
-#include <gtkmm/dialog.h>
-#include <fstream>
-#include <string>
-
-
-class JackSettingsDialog : public Gtk::Dialog {
-public:
- JackSettingsDialog(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
- : Gtk::Dialog(cobject)
- {
- xml->get_widget("jack_settings_command_entry", _command_entry);
- xml->get_widget("jack_settings_cancel_but", _cancel_but);
- xml->get_widget("jack_settings_ok_but", _ok_but);
-
- _cancel_but->signal_clicked().connect(
- sigc::mem_fun(this, &JackSettingsDialog::on_cancel));
- _ok_but->signal_clicked().connect(
- sigc::mem_fun(this, &JackSettingsDialog::on_ok));
-
- _command_entry->set_text(current_jack_command());
- }
-
-private:
- void on_cancel() { hide(); }
-
- std::string current_jack_command() {
- std::string result;
-
- const char* const home = getenv("HOME");
- if (home) {
- std::string jackdrc_path(home);
- jackdrc_path += "/.jackdrc";
-
- std::ifstream jackdrc(jackdrc_path.c_str());
- std::getline(jackdrc, result);
- jackdrc.close();
- }
-
- return result;
- }
-
- void on_ok() {
- hide();
- const char* const home = getenv("HOME");
- if (home) {
- std::string jackdrc_path(home);
- jackdrc_path += "/.jackdrc";
-
- std::ofstream jackdrc(jackdrc_path.c_str());
- jackdrc << _command_entry->get_text() << std::endl;
- jackdrc.close();
- }
- }
-
- Gtk::Entry* _command_entry;
- Gtk::Button* _cancel_but;
- Gtk::Button* _ok_but;
-};
-
-
-#endif // PATCHAGE_JACK_SETTINGS_DIALOG_HPP
-
diff --git a/src/LashClient.cpp b/src/LashClient.cpp
new file mode 100644
index 0000000..c41fef6
--- /dev/null
+++ b/src/LashClient.cpp
@@ -0,0 +1,83 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "LashClient.hpp"
+#include "Patchage.hpp"
+
+using namespace std;
+
+struct LashClientImpl {
+ LashProxy* proxy;
+ Project* project;
+ string id;
+ string name;
+};
+
+LashClient::LashClient(
+ LashProxy* proxy,
+ Project* project,
+ const string& id,
+ const string& name)
+{
+ _impl = new LashClientImpl;
+ _impl->proxy = proxy;
+ _impl->project = project;
+ _impl->id = id;
+ _impl->name = name;
+
+ //g_app->info_msg("client created");
+}
+
+LashClient::~LashClient()
+{
+ delete _impl;
+ //g_app->info_msg("client destroyed");
+}
+
+Project*
+LashClient::get_project()
+{
+ return _impl->project;
+}
+
+const string&
+LashClient::get_id() const
+{
+ return _impl->id;
+}
+
+const string&
+LashClient::get_name() const
+{
+ return _impl->name;
+}
+
+void
+LashClient::do_rename(const string& name)
+{
+ if (_impl->name != name) {
+ //_impl->proxy->client_rename(_impl->id, name);
+ }
+}
+
+void
+LashClient::on_name_changed(const string& name)
+{
+ _impl->name = name;
+ _signal_renamed.emit();
+}
diff --git a/src/LashClient.hpp b/src/LashClient.hpp
new file mode 100644
index 0000000..65f6caa
--- /dev/null
+++ b/src/LashClient.hpp
@@ -0,0 +1,61 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <dave@drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGE_LASH_CLIENT_HPP
+#define PATCHAGE_LASH_CLIENT_HPP
+
+#include <string>
+#include <sigc++/signal.h>
+
+class LashClientImpl;
+class LashProxy;
+class LashProxyImpl;
+class Project;
+
+class LashClient
+{
+public:
+ LashClient(
+ LashProxy* proxy,
+ Project* project,
+ const std::string& id,
+ const std::string& name);
+
+ ~LashClient();
+
+ Project*
+ get_project();
+
+ const std::string& get_id() const;
+ const std::string& get_name() const;
+
+ void do_rename(const std::string& name);
+
+ sigc::signal<void> _signal_renamed;
+
+private:
+ friend class LashProxyImpl;
+
+ void
+ on_name_changed(const std::string& name);
+
+ LashClientImpl* _impl;
+};
+
+#endif // PATCHAGE_LASH_CLIENT_HPP
diff --git a/src/LashDriver.cpp b/src/LashDriver.cpp
deleted file mode 100644
index 856ea2d..0000000
--- a/src/LashDriver.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/* This file is part of Patchage.
- * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
- *
- * 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.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include <iostream>
-#include <string>
-#include CONFIG_H_PATH
-#include "LashDriver.hpp"
-#include "Patchage.hpp"
-#include "StateManager.hpp"
-
-using namespace std;
-
-
-LashDriver::LashDriver(Patchage* app, int argc, char** argv)
- : Driver(2)
- , _app(app)
- , _args(NULL)
-{
- _args = lash_extract_args(&argc, &argv);
-}
-
-
-LashDriver::~LashDriver()
-{
- if (_args)
- lash_args_destroy(_args);
-}
-
-
-void
-LashDriver::attach(bool launch_daemon)
-{
- // Already connected
- if (_server_interface && _server_interface->enabled())
- return;
-
- int lash_flags = LASH_Server_Interface | LASH_Config_File;
- if (!launch_daemon)
- lash_flags |= LASH_No_Start_Server;
-
- _server_interface = Raul::LashServerInterface::create(
- _args, PACKAGE_NAME, lash_flags);
-
- if (_server_interface) {
- _server_interface->signal_project_add.connect(sigc::mem_fun(this, &LashDriver::on_project_add));
- _server_interface->signal_quit.connect(sigc::mem_fun(this, &LashDriver::on_quit));
- signal_attached.emit();
- _app->status_message("[LASH] Attached");
- } else {
- _app->status_message("[LASH] Unable to attach to server");
- }
-}
-
-
-void
-LashDriver::detach()
-{
- _server_interface.reset();
- _app->status_message("[LASH] Detached");
- signal_detached.emit();
-}
-
-
-void
-LashDriver::on_project_add(const SharedPtr<Raul::LashProject> project)
-{
- cout << "[LashDriver] Add project - " << project->name() << endl;
- _project_name = project->name();
- project->signal_save_file.connect(sigc::mem_fun(this, &LashDriver::on_save_file));
- project->signal_restore_file.connect(sigc::mem_fun(this, &LashDriver::on_restore_file));
-}
-
-
-void
-LashDriver::on_save_file(const string& directory)
-{
- cout << "[LashDriver] Save File - " << directory << endl;
- _app->store_window_location();
- _app->state_manager()->save(directory + "/locations");
-}
-
-
-void
-LashDriver::on_restore_file(const string& directory)
-{
- cout << "[LashDriver] Restore File - " << directory << endl;
- _app->state_manager()->load(directory + "/locations");
- _app->update_state();
-}
-
-
-void
-LashDriver::on_quit()
-{
- cout << "[LashDriver] Quit" << endl;
-}
-
-
-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);
-}
-
-
-void
-LashDriver::restore_project(const std::string& directory)
-{
- _project_name = "";
- _server_interface->restore_project(directory);
-}
-
-
-void
-LashDriver::set_project_directory(const std::string& directory)
-{
- SharedPtr<Raul::LashProject> project = _server_interface->project(_project_name);
-
- if (project)
- project->set_directory(directory);
- else
- cerr << "[LashDriver] No project \'" << _project_name << "\' to set directory!" << endl;
-}
-
-
-void
-LashDriver::save_project()
-{
- SharedPtr<Raul::LashProject> project = _server_interface->project(_project_name);
-
- if (project)
- project->save();
- else
- cerr << "[LashDriver] No project \'" << _project_name << "\' to save!" << endl;
-}
-
-
-void
-LashDriver::close_project()
-{
- SharedPtr<Raul::LashProject> project = _server_interface->project(_project_name);
-
- if (project)
- project->close();
- else
- cerr << "[LashDriver] No project \'" << _project_name << "\' to close!" << endl;
-}
-
diff --git a/src/LashDriver.hpp b/src/LashDriver.hpp
deleted file mode 100644
index ffe656c..0000000
--- a/src/LashDriver.hpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/* This file is part of Patchage.
- * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
- *
- * 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.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#ifndef PATCHAGE_LASHDRIVER_HPP
-#define PATCHAGE_LASHDRIVER_HPP
-
-#include <lash/lash.h>
-#include <raul/LashServerInterface.hpp>
-#include "Driver.hpp"
-
-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 _server_interface && _server_interface->enabled(); }
-
- boost::shared_ptr<PatchagePort> find_port_view(
- Patchage* patchage,
- const PatchageEvent::PortRef& ref) {
- return boost::shared_ptr<PatchagePort>();
- }
-
- boost::shared_ptr<PatchagePort> create_port_view(
- Patchage* patchage,
- const PatchageEvent::PortRef& ref) {
- return boost::shared_ptr<PatchagePort>();
- }
-
- bool connect(boost::shared_ptr<PatchagePort>, boost::shared_ptr<PatchagePort>)
- { return false; }
-
- bool disconnect(boost::shared_ptr<PatchagePort>, boost::shared_ptr<PatchagePort>)
- { return false; }
-
- void refresh() {}
-
- void process_events(Patchage* app) { _server_interface->process_events(); }
-
- void restore_project(const std::string& directory);
- void set_project_directory(const std::string& directory);
- void save_project();
- void close_project();
-
-private:
- Patchage* _app;
- std::string _project_name;
-
- lash_args_t* _args;
- SharedPtr<Raul::LashServerInterface> _server_interface;
-
- void on_project_add(const SharedPtr<Raul::LashProject> project);
- void on_save_file(const std::string& directory);
- void on_restore_file(const std::string& directory);
- void on_quit();
-
- void handle_event(lash_event_t* conf);
- void handle_config(lash_config_t* conf);
-};
-
-
-#endif // PATCHAGE_LASHDRIVER_HPP
diff --git a/src/LashProxy.cpp b/src/LashProxy.cpp
new file mode 100644
index 0000000..1d1580d
--- /dev/null
+++ b/src/LashProxy.cpp
@@ -0,0 +1,781 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <string.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/format.hpp>
+
+#include "LashProxy.hpp"
+#include "Session.hpp"
+#include "Project.hpp"
+#include "LashClient.hpp"
+#include "DBus.hpp"
+
+#define LASH_SERVICE "org.nongnu.LASH"
+#define LASH_OBJECT "/"
+#define LASH_IFACE_CONTROL "org.nongnu.LASH.Control"
+
+using namespace std;
+using boost::shared_ptr;
+
+struct LashProxyImpl {
+ void init(Patchage* app);
+
+ void fetch_loaded_projects();
+ void fetch_project_clients(shared_ptr<Project> project);
+
+ void error_msg(const std::string& msg);
+ void info_msg(const std::string& msg);
+
+ static
+ DBusHandlerResult
+ dbus_message_hook(
+ DBusConnection* connection,
+ DBusMessage* message,
+ void* proxy);
+
+ bool
+ call(
+ bool response_expected,
+ const char* iface,
+ const char* method,
+ DBusMessage** reply_ptr,
+ int in_type,
+ ...);
+
+ shared_ptr<Project>
+ on_project_added(const string& name);
+
+ shared_ptr<LashClient>
+ on_client_added(
+ shared_ptr<Project> project,
+ string id,
+ string name);
+
+ bool _server_responding;
+ Session* _session;
+ LashProxy* _interface;
+ Patchage* _app;
+};
+
+LashProxy::LashProxy(Patchage* app, Session* session)
+{
+ _impl = new LashProxyImpl;
+ _impl->_interface = this;
+ _impl->_session = session;
+ _impl->_app = app;
+ _impl->init(app);
+}
+
+LashProxy::~LashProxy()
+{
+ delete _impl;
+}
+
+void
+LashProxyImpl::init(Patchage* app)
+{
+ _server_responding = false;
+
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" DBUS_INTERFACE_DBUS "',member=NameOwnerChanged,arg0='" LASH_SERVICE "'", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ProjectAppeared", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ProjectDisappeared", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ProjectNameChanged", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ProjectModifiedStatusChanged", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ProjectDescriptionChanged", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ProjectNotesChanged", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ClientAppeared", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ClientDisappeared", NULL);
+ dbus_bus_add_match(app->dbus()->connection(), "type='signal',interface='" LASH_IFACE_CONTROL "',member=ClientNameChanged", NULL);
+
+ dbus_connection_add_filter(app->dbus()->connection(), dbus_message_hook, this, NULL);
+
+ // get initial list of projects
+ // calling any method to updates server responding status
+ // this also actiavtes lash object if it not activated already
+ fetch_loaded_projects();
+
+ app->set_lash_available(_server_responding);
+}
+
+void
+LashProxyImpl::error_msg(const std::string& msg)
+{
+ _app->error_msg((std::string)"[LASH] " + msg);
+}
+
+void
+LashProxyImpl::info_msg(const std::string& msg)
+{
+ _app->info_msg((std::string)"[LASH] " + msg);
+}
+
+DBusHandlerResult
+LashProxyImpl::dbus_message_hook(
+ DBusConnection* connection,
+ DBusMessage* message,
+ void* proxy)
+{
+ const char* project_name;
+ const char* new_project_name;
+ const char* object_name;
+ const char* old_owner;
+ const char* new_owner;
+ const char* value_string;
+ const char* client_id;
+ const char* client_name;
+ dbus_bool_t modified_status;
+ shared_ptr<Project> project;
+ shared_ptr<LashClient> client;
+
+ assert(proxy);
+ LashProxyImpl* me = reinterpret_cast<LashProxyImpl*>(proxy);
+ assert(me->_app->dbus()->connection());
+
+ Patchage* const app = me->_app;
+
+ //info_msg("dbus_message_hook() called.");
+
+ // Handle signals we have subscribed for in attach()
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &object_name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract NameOwnerChanged signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if ((string)object_name != LASH_SERVICE) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (old_owner[0] == '\0') {
+ me->info_msg("LASH activated.");
+ app->set_lash_available(true);
+ } else if (new_owner[0] == '\0') {
+ me->info_msg((string)"LASH deactivated.");
+ app->set_lash_available(false);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ProjectAppeared")) {
+ if (!dbus_message_get_args( message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ProjectAppeared signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Project '" + project_name + "' appeared.");
+ me->on_project_added(project_name);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ProjectDisappeared")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ProjectDisappeared signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Project '" + project_name + "' disappeared.");
+ me->_session->project_close(project_name);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ProjectNameChanged")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_STRING, &new_project_name,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ProjectNameChanged signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Project '" + project_name + "' renamed to '" + new_project_name + "'.");
+
+ project = me->_session->find_project_by_name(project_name);
+ if (project) {
+ project->on_name_changed(new_project_name);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ProjectModifiedStatusChanged")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_BOOLEAN, &modified_status,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ProjectModifiedStatusChanged signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Project '" + project_name + "' modified status changed to '" + (modified_status ? "true" : "false") + "'.");
+
+ project = me->_session->find_project_by_name(project_name);
+ if (project) {
+ project->on_modified_status_changed(modified_status);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ProjectDescriptionChanged")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_STRING, &value_string,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ProjectDescriptionChanged signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Project '" + project_name + "' description changed.");
+
+ project = me->_session->find_project_by_name(project_name);
+ if (project) {
+ project->on_description_changed(value_string);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ProjectNotesChanged")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_STRING, &value_string,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ProjectNotesChanged signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Project '" + project_name + "' notes changed.");
+
+ project = me->_session->find_project_by_name(project_name);
+ if (project) {
+ project->on_notes_changed(value_string);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ClientAppeared")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &client_id,
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_STRING, &client_name,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ClientAppeared signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Client '" + client_id + "':'" + client_name + "' appeared in project '" + project_name + "'.");
+
+ project = me->_session->find_project_by_name(project_name);
+ if (project) {
+ me->on_client_added(project, client_id, client_name);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ClientDisappeared")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &client_id,
+ DBUS_TYPE_STRING, &project_name,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ClientDisappeared signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Client '" + client_id + "' of project '" + project_name + "' disappeared.");
+
+ client = me->_session->find_client_by_id(client_id);
+ if (client) {
+ client->get_project()->on_client_removed(client_id);
+ me->_session->client_remove(client_id);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, LASH_IFACE_CONTROL, "ClientNameChanged")) {
+ if (!dbus_message_get_args(
+ message, &app->dbus()->error(),
+ DBUS_TYPE_STRING, &client_id,
+ DBUS_TYPE_STRING, &client_name,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract ClientNameChanged signal arguments (%s)") % app->dbus()->error().message));
+ dbus_error_free(&app->dbus()->error());
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ me->info_msg((string)"Client '" + client_id + "' name changed to '" + client_name + "'.");
+
+ client = me->_session->find_client_by_id(client_id);
+ if (client) {
+ client->on_name_changed(client_name);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+bool
+LashProxyImpl::call(
+ bool response_expected,
+ const char* iface,
+ const char* method,
+ DBusMessage** reply_ptr,
+ int in_type,
+ ...)
+{
+ va_list ap;
+ va_start(ap, in_type);
+
+ _server_responding = _app->dbus()->call(
+ response_expected,
+ LASH_SERVICE,
+ LASH_OBJECT,
+ iface,
+ method,
+ reply_ptr,
+ in_type,
+ ap);
+
+ va_end(ap);
+ return _server_responding;
+}
+
+void
+LashProxyImpl::fetch_loaded_projects()
+{
+ DBusMessage* reply_ptr;
+ const char* reply_signature;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ const char* project_name;
+ shared_ptr<Project> project;
+
+ if (!call(true, LASH_IFACE_CONTROL, "ProjectsGet", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ reply_signature = dbus_message_get_signature(reply_ptr);
+
+ if (strcmp(reply_signature, "as") != 0) {
+ error_msg((string)"ProjectsGet() reply signature mismatch. " + reply_signature);
+ goto unref;
+ }
+
+ dbus_message_iter_init(reply_ptr, &iter);
+
+ for (dbus_message_iter_recurse(&iter, &array_iter);
+ dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&array_iter)) {
+ dbus_message_iter_get_basic(&array_iter, &project_name);
+ project = on_project_added(project_name);
+ fetch_project_clients(project);
+ }
+
+unref:
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxyImpl::fetch_project_clients(shared_ptr<Project> project)
+{
+ DBusMessage* reply_ptr;
+ const char* reply_signature;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter struct_iter;
+ const char* client_id;
+ const char* client_name;
+
+ const string& project_name = project->get_name();
+ const char* const project_name_cstr = project_name.c_str();
+
+ if (!call(
+ true,
+ LASH_IFACE_CONTROL,
+ "ProjectGetClients",
+ &reply_ptr,
+ DBUS_TYPE_STRING, &project_name_cstr,
+ DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ reply_signature = dbus_message_get_signature(reply_ptr);
+
+ if (strcmp(reply_signature, "a(ss)") != 0) {
+ error_msg((string)"ProjectGetClients() reply signature mismatch. " + reply_signature);
+ goto unref;
+ }
+
+ dbus_message_iter_init(reply_ptr, &iter);
+
+ for (dbus_message_iter_recurse(&iter, &array_iter);
+ dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&array_iter)) {
+ dbus_message_iter_recurse(&array_iter, &struct_iter);
+
+ dbus_message_iter_get_basic(&struct_iter, &client_id);
+ dbus_message_iter_next(&struct_iter);
+ dbus_message_iter_get_basic(&struct_iter, &client_name);
+ dbus_message_iter_next(&struct_iter);
+
+ on_client_added(project, client_id, client_name);
+ }
+
+unref:
+ dbus_message_unref(reply_ptr);
+}
+
+shared_ptr<Project>
+LashProxyImpl::on_project_added(const string& name)
+{
+ shared_ptr<Project> project(new Project(_interface, name));
+
+ _session->project_add(project);
+
+ return project;
+}
+
+shared_ptr<LashClient>
+LashProxyImpl::on_client_added(
+ shared_ptr<Project> project,
+ string id,
+ string name)
+{
+ shared_ptr<LashClient> client(
+ new LashClient(
+ _interface,
+ project.get(),
+ id,
+ name));
+
+ project->on_client_added(client);
+ _session->client_add(client);
+
+ return client;
+}
+
+void
+LashProxy::get_available_projects(list<ProjectInfo>& projects)
+{
+ DBusMessage* reply_ptr;
+ const char* reply_signature;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter struct_iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ DBusMessageIter variant_iter;
+ const char* project_name;
+ const char* key;
+ const char* value_type;
+ dbus_uint32_t value_uint32;
+ const char* value_string;
+ ProjectInfo project_info;
+
+ if (!_impl->call(true, LASH_IFACE_CONTROL, "ProjectsGetAvailable", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ reply_signature = dbus_message_get_signature(reply_ptr);
+
+ if (strcmp(reply_signature, "a(sa{sv})") != 0) {
+ _impl->error_msg((string)"ProjectsGetAvailable() reply signature mismatch. " + reply_signature);
+ goto unref;
+ }
+
+ dbus_message_iter_init(reply_ptr, &iter);
+
+ for (dbus_message_iter_recurse(&iter, &array_iter);
+ dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&array_iter)) {
+ dbus_message_iter_recurse(&array_iter, &struct_iter);
+
+ dbus_message_iter_get_basic(&struct_iter, &project_name);
+
+ project_info.name = project_name;
+ project_info.modification_time = 0;
+ project_info.description.erase();
+
+ dbus_message_iter_next(&struct_iter);
+
+ for (dbus_message_iter_recurse(&struct_iter, &dict_iter);
+ dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&dict_iter)) {
+ dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+ dbus_message_iter_get_basic(&dict_entry_iter, &key);
+ dbus_message_iter_next(&dict_entry_iter);
+ dbus_message_iter_recurse(&dict_entry_iter, &variant_iter);
+ value_type = dbus_message_iter_get_signature(&variant_iter);
+ if (value_type[0] != 0 && value_type[1] == 0) {
+ switch (*value_type) {
+ case DBUS_TYPE_UINT32:
+ if (strcmp(key, "Modification Time") == 0) {
+ dbus_message_iter_get_basic(&variant_iter, &value_uint32);
+ project_info.modification_time = value_uint32;
+ }
+ break;
+ case DBUS_TYPE_STRING:
+ if (strcmp(key, "Description") == 0) {
+ dbus_message_iter_get_basic(&variant_iter, &value_string);
+ project_info.description = value_string;
+ }
+ break;
+ }
+ }
+ }
+
+ projects.push_back(project_info);
+ }
+
+unref:
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::load_project(const string& project_name)
+{
+ DBusMessage* reply_ptr;
+ const char* const project_name_cstr = project_name.c_str();
+
+ if (!_impl->call(true, LASH_IFACE_CONTROL, "ProjectOpen", &reply_ptr, DBUS_TYPE_STRING, &project_name_cstr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::save_all_projects()
+{
+ DBusMessage* reply_ptr;
+
+ if (!_impl->call(true, LASH_IFACE_CONTROL, "ProjectsSaveAll", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::save_project(const string& project_name)
+{
+ DBusMessage* reply_ptr;
+ const char* const project_name_cstr = project_name.c_str();
+
+ if (!_impl->call(true, LASH_IFACE_CONTROL, "ProjectSave", &reply_ptr, DBUS_TYPE_STRING, &project_name_cstr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::close_project(const string& project_name)
+{
+ DBusMessage* reply_ptr;
+ const char* const project_name_cstr = project_name.c_str();
+
+ if (!_impl->call(true, LASH_IFACE_CONTROL, "ProjectClose", &reply_ptr, DBUS_TYPE_STRING, &project_name_cstr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::close_all_projects()
+{
+ DBusMessage* reply_ptr;
+
+ if (!_impl->call(true, LASH_IFACE_CONTROL, "ProjectsCloseAll", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::project_rename(const string& old_name, const string& new_name)
+{
+ DBusMessage* reply_ptr;
+ const char* const old_name_cstr = old_name.c_str();
+ const char* const new_name_cstr = new_name.c_str();
+
+ if (!_impl->call(
+ true,
+ LASH_IFACE_CONTROL,
+ "ProjectRename",
+ &reply_ptr,
+ DBUS_TYPE_STRING, &old_name_cstr,
+ DBUS_TYPE_STRING, &new_name_cstr,
+ DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::get_loaded_project_properties(
+ const string& name,
+ LoadedProjectProperties& properties)
+{
+ DBusMessage* reply_ptr;
+ const char* reply_signature;
+ DBusMessageIter iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ DBusMessageIter variant_iter;
+ const char* key;
+ const char* value_type;
+ dbus_bool_t value_bool;
+ const char* value_string;
+
+ const char* const project_name_cstr = name.c_str();
+
+ if (!_impl->call(
+ true,
+ LASH_IFACE_CONTROL,
+ "ProjectGetProperties",
+ &reply_ptr,
+ DBUS_TYPE_STRING, &project_name_cstr,
+ DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ reply_signature = dbus_message_get_signature(reply_ptr);
+
+ if (strcmp(reply_signature, "a{sv}") != 0) {
+ _impl->error_msg((string)"ProjectGetProperties() reply signature mismatch. " + reply_signature);
+ goto unref;
+ }
+
+ dbus_message_iter_init(reply_ptr, &iter);
+
+ for (dbus_message_iter_recurse(&iter, &dict_iter);
+ dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&dict_iter)) {
+ dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+ dbus_message_iter_get_basic(&dict_entry_iter, &key);
+ dbus_message_iter_next(&dict_entry_iter);
+ dbus_message_iter_recurse(&dict_entry_iter, &variant_iter);
+ value_type = dbus_message_iter_get_signature(&variant_iter);
+ if (value_type[0] != 0 && value_type[1] == 0) {
+ switch (*value_type) {
+ case DBUS_TYPE_BOOLEAN:
+ if (strcmp(key, "Modified Status") == 0) {
+ dbus_message_iter_get_basic(&variant_iter, &value_bool);
+ properties.modified_status = value_bool;
+ }
+ break;
+ case DBUS_TYPE_STRING:
+ if (strcmp(key, "Description") == 0) {
+ dbus_message_iter_get_basic(&variant_iter, &value_string);
+ properties.description = value_string;
+ } else if (strcmp(key, "Notes") == 0) {
+ dbus_message_iter_get_basic(&variant_iter, &value_string);
+ properties.notes = value_string;
+ }
+ break;
+ }
+ }
+ }
+
+unref:
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::project_set_description(const string& project_name, const string& description)
+{
+ DBusMessage* reply_ptr;
+ const char* const project_name_cstr = project_name.c_str();
+ const char* const description_cstr = description.c_str();
+
+ if (!_impl->call(
+ true,
+ LASH_IFACE_CONTROL,
+ "ProjectSetDescription",
+ &reply_ptr,
+ DBUS_TYPE_STRING, &project_name_cstr,
+ DBUS_TYPE_STRING, &description_cstr,
+ DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+void
+LashProxy::project_set_notes(
+ const string& project_name,
+ const string& notes)
+{
+ DBusMessage* reply_ptr;
+ const char* const project_name_cstr = project_name.c_str();
+ const char* const notes_cstr = notes.c_str();
+
+ if (!_impl->call(
+ true,
+ LASH_IFACE_CONTROL,
+ "ProjectSetNotes",
+ &reply_ptr,
+ DBUS_TYPE_STRING, &project_name_cstr,
+ DBUS_TYPE_STRING, &notes_cstr,
+ DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
diff --git a/src/LashProxy.hpp b/src/LashProxy.hpp
new file mode 100644
index 0000000..fb2f3ff
--- /dev/null
+++ b/src/LashProxy.hpp
@@ -0,0 +1,61 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <dave@drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGE_LASH_PROXY_HPP
+#define PATCHAGE_LASH_PROXY_HPP
+
+#include "Patchage.hpp"
+
+struct ProjectInfo {
+ std::string name;
+ time_t modification_time;
+ std::string description;
+};
+
+struct LoadedProjectProperties {
+ bool modified_status;
+ std::string description;
+ std::string notes;
+};
+
+class Patchage;
+class Session;
+class LashProxyImpl;
+
+class LashProxy {
+public:
+ LashProxy(Patchage* app, Session* session);
+ ~LashProxy();
+
+ void get_available_projects(std::list<ProjectInfo>& projects);
+ void load_project(const std::string& project_name);
+ void save_all_projects();
+ void save_project(const std::string& project_name);
+ void close_project(const std::string& project_name);
+ void close_all_projects();
+ void project_rename(const std::string& old_name, const std::string& new_name);
+ void get_loaded_project_properties(const std::string& name, LoadedProjectProperties& properties);
+ void project_set_description(const std::string& project_name, const std::string& description);
+ void project_set_notes(const std::string& project_name, const std::string& notes);
+
+private:
+ LashProxyImpl* _impl;
+};
+
+#endif // PATCHAGE_LASH_PROXY_HPP
diff --git a/src/LoadProjectDialog.cpp b/src/LoadProjectDialog.cpp
new file mode 100644
index 0000000..0a4c92a
--- /dev/null
+++ b/src/LoadProjectDialog.cpp
@@ -0,0 +1,154 @@
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <dave@drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "LoadProjectDialog.hpp"
+#include "Patchage.hpp"
+#include "LashProxy.hpp"
+
+static void
+convert_timestamp_to_string(
+ const time_t timestamp,
+ std::string& timestamp_string)
+{
+ GDate mtime, now;
+ gint days_diff;
+ struct tm tm_mtime;
+ time_t time_now;
+ const gchar *format;
+ gchar buf[256];
+
+ if (timestamp == 0) {
+ timestamp_string = "Unknown";
+ return;
+ }
+
+ localtime_r(&timestamp, &tm_mtime);
+
+ g_date_set_time_t(&mtime, timestamp);
+ time_now = time(NULL);
+ g_date_set_time_t(&now, time_now);
+
+ days_diff = g_date_get_julian(&now) - g_date_get_julian(&mtime);
+
+ if (days_diff == 0) {
+ format = "Today at %H:%M";
+ } else if (days_diff == 1) {
+ format = "Yesterday at %H:%M";
+ } else {
+ if (days_diff > 1 && days_diff < 7) {
+ format = "%A"; /* Days from last week */
+ } else {
+ format = "%x"; /* Any other date */
+ }
+ }
+
+ if (strftime(buf, sizeof(buf), format, &tm_mtime) != 0) {
+ timestamp_string = buf;
+ } else {
+ timestamp_string = "Unknown";
+ }
+}
+
+
+LoadProjectDialog::LoadProjectDialog(Patchage* app)
+ : _app(app)
+ , _dialog(app->xml(), "load_project_dialog")
+ , _widget(app->xml(), "loadable_projects_list")
+{
+ _columns.add(_columns.name);
+ _columns.add(_columns.modified);
+ _columns.add(_columns.description);
+
+ _model = Gtk::ListStore::create(_columns);
+ _widget->set_model(_model);
+
+ _widget->remove_all_columns();
+ _widget->append_column("Project Name", _columns.name);
+ _widget->append_column("Modified", _columns.modified);
+ _widget->append_column("Description", _columns.description);
+}
+
+
+void
+LoadProjectDialog::run(std::list<ProjectInfo>& projects)
+{
+ Gtk::TreeModel::Row row;
+ int result;
+
+ for (std::list<ProjectInfo>::iterator iter = projects.begin(); iter != projects.end(); iter++) {
+ std::string str;
+ row = *(_model->append());
+ row[_columns.name] = iter->name;
+ convert_timestamp_to_string(iter->modification_time, str);
+ row[_columns.modified] = str;
+ row[_columns.description] = iter->description;
+ }
+
+ _widget->signal_button_press_event().connect(sigc::mem_fun(*this, &LoadProjectDialog::on_button_press_event), false);
+ _widget->signal_key_press_event().connect(sigc::mem_fun(*this, &LoadProjectDialog::on_key_press_event), false);
+
+loop:
+ result = _dialog->run();
+
+ if (result == 2) {
+ Glib::RefPtr<Gtk::TreeView::Selection> selection = _widget->get_selection();
+ Gtk::TreeIter iter = selection->get_selected();
+ if (!iter)
+ goto loop;
+
+ Glib::ustring project_name = (*iter)[_columns.name];
+ _app->lash_proxy()->load_project(project_name);
+ }
+
+ _dialog->hide();
+}
+
+
+void
+LoadProjectDialog::load_selected_project()
+{
+ Glib::RefPtr<Gtk::TreeView::Selection> selection = _widget->get_selection();
+ Glib::ustring name = (*selection->get_selected())[_columns.name];
+ _app->lash_proxy()->load_project(name);
+ _dialog->hide();
+}
+
+
+bool
+LoadProjectDialog::on_button_press_event(GdkEventButton * event_ptr)
+{
+ if (event_ptr->type == GDK_2BUTTON_PRESS && event_ptr->button == 1) {
+ load_selected_project();
+ return true;
+ }
+ return false;
+}
+
+
+bool
+LoadProjectDialog::on_key_press_event(GdkEventKey * event_ptr)
+{
+ if (event_ptr->type == GDK_KEY_PRESS &&
+ (event_ptr->keyval == GDK_Return ||
+ event_ptr->keyval == GDK_KP_Enter)) {
+ load_selected_project();
+ return true;
+ }
+ return false;
+}
+
diff --git a/src/LoadProjectDialog.hpp b/src/LoadProjectDialog.hpp
new file mode 100644
index 0000000..3f6c2c1
--- /dev/null
+++ b/src/LoadProjectDialog.hpp
@@ -0,0 +1,53 @@
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <dave@drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGE_LOAD_PROJECT_DIALOG_H
+#define PATCHAGE_LOAD_PROJECT_DIALOG_H
+
+#include <list>
+#include <gtkmm.h>
+#include "Widget.hpp"
+
+class Patchage;
+class ProjectInfo;
+
+class LoadProjectDialog {
+public:
+ LoadProjectDialog(Patchage* app);
+
+ void run(std::list<ProjectInfo>& projects);
+
+private:
+ struct Record : public Gtk::TreeModel::ColumnRecord {
+ Gtk::TreeModelColumn<Glib::ustring> name;
+ Gtk::TreeModelColumn<Glib::ustring> modified;
+ Gtk::TreeModelColumn<Glib::ustring> description;
+ };
+
+ void load_selected_project();
+ bool on_button_press_event(GdkEventButton* event_ptr);
+ bool on_key_press_event(GdkEventKey* event_ptr);
+
+ Patchage* _app;
+ Widget<Gtk::Dialog> _dialog;
+ Widget<Gtk::TreeView> _widget;
+ Record _columns;
+ Glib::RefPtr<Gtk::ListStore> _model;
+};
+
+#endif // PATCHAGE_LOAD_PROJECT_DIALOG_H
diff --git a/src/Makefile.am b/src/Makefile.am
index 3d8c76b..389bd15 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
-AM_CXXFLAGS = -I.. -I$(top_srcdir)/raul -I$(top_srcdir)/flowcanvas -DDATA_DIR=\"$(pkgdatadir)\" @GTHREAD_CFLAGS@ @LIBGLADEMM_CFLAGS@ @GNOMECANVASMM_CFLAGS@ @ALSA_CFLAGS@ @LASH_CFLAGS@
-patchage_LDADD = @GTHREAD_LIBS@ @LIBGLADEMM_LIBS@ @GNOMECANVASMM_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @RAUL_LIBS@ @FLOWCANVAS_LIBS@
+AM_CXXFLAGS = -I.. -I$(top_srcdir)/raul -I$(top_srcdir)/flowcanvas -DDATA_DIR=\"$(pkgdatadir)\" @GTHREAD_CFLAGS@ @LIBGLADEMM_CFLAGS@ @GNOMECANVASMM_CFLAGS@ @ALSA_CFLAGS@ @DBUS_CFLAGS@
+patchage_LDADD = @GTHREAD_LIBS@ @LIBGLADEMM_LIBS@ @GNOMECANVASMM_LIBS@ @ALSA_LIBS@ @RAUL_LIBS@ @FLOWCANVAS_LIBS@ @DBUS_LIBS@
EXTRA_DIST = patchage.gladep
@@ -10,7 +10,6 @@ bin_PROGRAMS = patchage
patchage_SOURCES = \
Driver.hpp \
GladeFile.hpp \
- JackSettingsDialog.hpp \
Patchage.cpp \
Patchage.hpp \
PatchageCanvas.cpp \
@@ -38,8 +37,24 @@ patchage_LDADD += @JACK_LIBS@
endif
endif
-if WITH_LASH
-patchage_SOURCES += LashDriver.hpp LashDriver.cpp
+if WITH_DBUS
+patchage_SOURCES += \
+ DBus.cpp \
+ DBus.hpp \
+ LashClient.cpp \
+ LashClient.hpp \
+ LashProxy.cpp \
+ LashProxy.hpp \
+ LoadProjectDialog.cpp \
+ LoadProjectDialog.hpp \
+ Project.cpp \
+ Project.hpp \
+ ProjectList.cpp \
+ ProjectList.hpp \
+ ProjectPropertiesDialog.cpp \
+ ProjectPropertiesDialog.hpp \
+ Session.cpp \
+ Session.hpp
endif
if WITH_ALSA
diff --git a/src/Patchage.cpp b/src/Patchage.cpp
index caf0c10..8b222db 100644
--- a/src/Patchage.cpp
+++ b/src/Patchage.cpp
@@ -32,7 +32,6 @@
#include "JackDriver.hpp"
#include <jack/statistics.h>
#endif
-#include "JackSettingsDialog.hpp"
#include "Patchage.hpp"
#include "PatchageCanvas.hpp"
#include "PatchageEvent.hpp"
@@ -40,10 +39,16 @@
#ifdef HAVE_ALSA
#include "AlsaDriver.hpp"
#endif
-#ifdef HAVE_LASH
-#include "LashDriver.hpp"
+#ifdef HAVE_DBUS
+#include "DBus.hpp"
+#include "LashProxy.hpp"
+#include "LoadProjectDialog.hpp"
+#include "ProjectList.hpp"
+#include "Session.hpp"
#endif
+#define LOG_TO_STATUS 1
+
using namespace std;
/* Gtk helpers (resize combo boxes) */
@@ -77,50 +82,48 @@ gtkmm_set_width_for_given_text (Gtk::Widget &w, const gchar *text,
/* end Gtk helpers */
-#define INIT_WIDGET(x) x(xml, ((const char*)#x) + 1)
+#define INIT_WIDGET(x) x(_xml, ((const char*)#x) + 1)
Patchage::Patchage(int argc, char** argv)
- : xml(GladeFile::open("patchage"))
-#ifdef HAVE_LASH
- , _lash_driver(NULL)
- , INIT_WIDGET(_menu_open_session)
- , INIT_WIDGET(_menu_save_session)
- , INIT_WIDGET(_menu_save_session_as)
- , INIT_WIDGET(_menu_close_session)
- , INIT_WIDGET(_menu_lash_connect)
- , INIT_WIDGET(_menu_lash_disconnect)
+ : _xml(GladeFile::open("patchage"))
+#ifdef HAVE_DBUS
+ , _lash_proxy(NULL)
+ , _dbus(NULL)
+ , _project_list(NULL)
+ , _session(NULL)
#endif
#ifdef HAVE_ALSA
, _alsa_driver(NULL)
, _alsa_driver_autoattach(true)
- , INIT_WIDGET(_menu_alsa_connect)
- , INIT_WIDGET(_menu_alsa_disconnect)
#endif
, _jack_driver(NULL)
, _state_manager(NULL)
, _attach(true)
, _refresh(false)
, _enable_refresh(true)
- , _jack_settings_dialog(NULL)
, INIT_WIDGET(_about_win)
, INIT_WIDGET(_buffer_size_combo)
, INIT_WIDGET(_clear_load_but)
, INIT_WIDGET(_main_scrolledwin)
, INIT_WIDGET(_main_win)
, INIT_WIDGET(_main_xrun_progress)
+ , INIT_WIDGET(_menu_alsa_connect)
+ , INIT_WIDGET(_menu_alsa_disconnect)
, INIT_WIDGET(_menu_file_quit)
, INIT_WIDGET(_menu_help_about)
, INIT_WIDGET(_menu_jack_connect)
, INIT_WIDGET(_menu_jack_disconnect)
- , INIT_WIDGET(_menu_jack_settings)
+ , INIT_WIDGET(_menu_open_session)
, INIT_WIDGET(_menu_store_positions)
, INIT_WIDGET(_menu_view_arrange)
, INIT_WIDGET(_menu_view_messages)
+ , INIT_WIDGET(_menu_view_projects)
, INIT_WIDGET(_menu_view_refresh)
, INIT_WIDGET(_menu_view_toolbar)
- , INIT_WIDGET(_messages_win)
, INIT_WIDGET(_messages_clear_but)
, INIT_WIDGET(_messages_close_but)
+ , INIT_WIDGET(_messages_win)
+ , INIT_WIDGET(_project_list_viewport)
, INIT_WIDGET(_sample_rate_label)
, INIT_WIDGET(_status_text)
, INIT_WIDGET(_toolbar)
@@ -146,8 +149,6 @@ Patchage::Patchage(int argc, char** argv)
argc--;
}
- xml->get_widget_derived("jack_settings_win", _jack_settings_dialog);
-
Glib::set_application_name("Patchage");
_about_win->property_program_name() = "Patchage";
_about_win->property_logo_icon_name() = "patchage";
@@ -173,22 +174,12 @@ Patchage::Patchage(int argc, char** argv)
sigc::mem_fun(this, &Patchage::zoom), 1.0));
_zoom_full_but->signal_clicked().connect(
sigc::mem_fun(_canvas.get(), &PatchageCanvas::zoom_full));
- _menu_jack_settings->signal_activate().connect(sigc::hide_return(
- sigc::mem_fun(_jack_settings_dialog, &JackSettingsDialog::run)));
-#ifdef HAVE_LASH
+#ifdef HAVE_DBUS
_menu_open_session->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::menu_open_session));
- _menu_save_session->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::menu_save_session));
- _menu_save_session_as->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::menu_save_session_as));
- _menu_close_session->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::menu_close_session));
- _menu_lash_connect->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::menu_lash_connect));
- _menu_lash_disconnect->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::menu_lash_disconnect));
+ sigc::mem_fun(this, &Patchage::show_load_project_dialog));
+#else
+ _menu_open_session->set_sensitive(false);
#endif
#ifdef HAVE_ALSA
@@ -196,6 +187,9 @@ Patchage::Patchage(int argc, char** argv)
sigc::mem_fun(this, &Patchage::menu_alsa_connect));
_menu_alsa_disconnect->signal_activate().connect(
sigc::mem_fun(this, &Patchage::menu_alsa_disconnect));
+#else
+ _menu_alsa_connect->set_sensitive(false);
+ _menu_alsa_disconnect->set_sensitive(false);
#endif
_menu_store_positions->signal_activate().connect(
@@ -210,6 +204,8 @@ Patchage::Patchage(int argc, char** argv)
sigc::mem_fun(this, &Patchage::on_view_toolbar));
_menu_view_messages->signal_toggled().connect(
sigc::mem_fun(this, &Patchage::on_show_messages));
+ _menu_view_projects->signal_toggled().connect(
+ sigc::mem_fun(this, &Patchage::on_show_projects));
_menu_help_about->signal_activate().connect(
sigc::mem_fun(this, &Patchage::on_help_about));
@@ -249,8 +245,13 @@ Patchage::Patchage(int argc, char** argv)
_alsa_driver = new AlsaDriver(this);
#endif
-#ifdef HAVE_LASH
- _lash_driver = new LashDriver(this, argc, argv);
+#ifdef HAVE_DBUS
+ _dbus = new DBus(this);
+ _session = new Session();
+ _project_list = new ProjectList(this, _session);
+ _lash_proxy = new LashProxy(this, _session);
+#else
+ _project_list_viewport->hide();
#endif
connect_widgets();
@@ -272,14 +273,14 @@ Patchage::~Patchage()
#ifdef HAVE_ALSA
delete _alsa_driver;
#endif
-#ifdef HAVE_LASH
- delete _lash_driver;
+#ifdef HAVE_DBUS
+ delete _lash_proxy;
#endif
delete _state_manager;
-
+
_about_win.destroy();
_messages_win.destroy();
- _main_win.destroy();
+ //_main_win.destroy();
}
@@ -292,9 +293,6 @@ Patchage::attach()
_jack_driver->attach(true);
#endif
-#ifdef HAVE_LASH
- _lash_driver->attach(true);
-#endif
#ifdef HAVE_ALSA
if (_alsa_driver_autoattach)
_alsa_driver->attach();
@@ -333,11 +331,6 @@ Patchage::idle_callback()
}
#endif
-#ifdef HAVE_LASH
- if (_lash_driver->is_attached())
- _lash_driver->process_events(this);
-#endif
-
// Do a full refresh (ie user clicked refresh)
if (_refresh) {
refresh();
@@ -451,7 +444,31 @@ Patchage::clear_load()
void
-Patchage::status_message(const string& msg)
+Patchage::error_msg(const std::string& msg)
+{
+#if defined(LOG_TO_STATUS)
+ status_msg(msg);
+#endif
+#if defined(LOG_TO_STD)
+ cerr << msg << endl;
+#endif
+}
+
+
+void
+Patchage::info_msg(const std::string& msg)
+{
+#if defined(LOG_TO_STATUS)
+ status_msg(msg);
+#endif
+#if defined(LOG_TO_STD)
+ cerr << msg << endl;
+#endif
+}
+
+
+void
+Patchage::status_msg(const string& msg)
{
if (_status_text->get_buffer()->size() > 0)
_status_text->get_buffer()->insert(_status_text->get_buffer()->end(), "\n");
@@ -479,18 +496,6 @@ Patchage::update_state()
void
Patchage::connect_widgets()
{
-#ifdef HAVE_LASH
- _lash_driver->signal_attached.connect(sigc::bind(
- sigc::mem_fun(*_menu_lash_connect, &Gtk::MenuItem::set_sensitive), false));
- _lash_driver->signal_attached.connect(sigc::bind(
- sigc::mem_fun(*_menu_lash_disconnect, &Gtk::MenuItem::set_sensitive), true));
-
- _lash_driver->signal_detached.connect(sigc::bind(
- sigc::mem_fun(*_menu_lash_connect, &Gtk::MenuItem::set_sensitive), true));
- _lash_driver->signal_detached.connect(sigc::bind(
- sigc::mem_fun(*_menu_lash_disconnect, &Gtk::MenuItem::set_sensitive), false));
-#endif
-
#if defined(HAVE_JACK) || defined(HAVE_JACK_DBUS)
_jack_driver->signal_attached.connect(
sigc::mem_fun(this, &Patchage::update_toolbar));
@@ -520,72 +525,25 @@ Patchage::connect_widgets()
#endif
}
-
-#ifdef HAVE_LASH
+#ifdef HAVE_DBUS
void
-Patchage::menu_open_session()
+Patchage::show_load_project_dialog()
{
- Gtk::FileChooserDialog dialog(*_main_win, "Open LASH Session",
- Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
+ std::list<ProjectInfo> projects;
+ _lash_proxy->get_available_projects(projects);
- dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
- dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
-
- const int result = dialog.run();
-
- if (result == Gtk::RESPONSE_OK) {
- _lash_driver->restore_project(dialog.get_filename());
- }
-}
-
-
-void
-Patchage::menu_save_session()
-{
- if (_lash_driver)
- _lash_driver->save_project();
-}
-
-
-void
-Patchage::menu_save_session_as()
-{
- if (!_lash_driver)
- return;
-
- Gtk::FileChooserDialog dialog(*_main_win, "Save LASH Session",
- Gtk::FILE_CHOOSER_ACTION_SAVE);
-
- dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
- dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
-
- const int result = dialog.run();
-
- if (result == Gtk::RESPONSE_OK) {
- _lash_driver->set_project_directory(dialog.get_filename());
- _lash_driver->save_project();
- }
-}
-
-
-void
-Patchage::menu_close_session()
-{
- _lash_driver->close_project();
-}
-
-
-void
-Patchage::menu_lash_connect()
-{
- _lash_driver->attach(true);
+ LoadProjectDialog dialog(this);
+ dialog.run(projects);
}
+#endif
-
+#ifdef HAVE_DBUS
void
-Patchage::menu_lash_disconnect()
+Patchage::set_lash_available(bool available)
{
- _lash_driver->detach();
+ _project_list->set_lash_available(available);
+ if (!available)
+ _session->clear();
}
#endif
@@ -670,6 +628,16 @@ Patchage::on_show_messages()
_messages_win->hide();
}
+
+void
+Patchage::on_show_projects()
+{
+ if (_menu_view_projects->get_active())
+ _project_list_viewport->show();
+ else
+ _project_list_viewport->hide();
+}
+
void
Patchage::on_store_positions()
diff --git a/src/Patchage.hpp b/src/Patchage.hpp
index 18f95d3..05cff7b 100644
--- a/src/Patchage.hpp
+++ b/src/Patchage.hpp
@@ -28,9 +28,12 @@
class PatchageCanvas;
class JackDriver;
class AlsaDriver;
-class LashDriver;
+class LashProxy;
class StateManager;
class JackSettingsDialog;
+class Session;
+class DBus;
+class ProjectList;
class Patchage {
public:
@@ -46,10 +49,16 @@ public:
#ifdef HAVE_ALSA
AlsaDriver* alsa_driver() const { return _alsa_driver; }
#endif
-#ifdef HAVE_LASH
- LashDriver* lash_driver() const { return _lash_driver; }
+#ifdef HAVE_DBUS
+ LashProxy* lash_proxy() const { return _lash_proxy; }
+ DBus* dbus() const { return _dbus; }
+
+ void show_load_project_dialog();
+ void set_lash_available(bool available);
#endif
+ Glib::RefPtr<Gnome::Glade::Xml> xml() { return _xml; }
+
void attach();
void quit() { _main_win->hide(); }
@@ -57,9 +66,12 @@ public:
inline void queue_refresh() { _refresh = true; }
void clear_load();
- void status_message(const std::string& msg);
+ void info_msg(const std::string& msg);
+ void error_msg(const std::string& msg);
+ void status_msg(const std::string& msg);
void update_state();
void store_window_location();
+
protected:
void connect_widgets();
@@ -71,6 +83,7 @@ protected:
bool on_messages_delete(GdkEventAny*);
void on_quit();
void on_show_messages();
+ void on_show_projects();
void on_store_positions();
void on_view_toolbar();
bool on_scroll(GdkEventScroll* ev);
@@ -82,37 +95,26 @@ protected:
void buffer_size_changed();
- Glib::RefPtr<Gnome::Glade::Xml> xml;
-
-#ifdef HAVE_LASH
- LashDriver* _lash_driver;
- Widget<Gtk::MenuItem> _menu_open_session;
- Widget<Gtk::MenuItem> _menu_save_session;
- Widget<Gtk::MenuItem> _menu_save_session_as;
- Widget<Gtk::MenuItem> _menu_close_session;
- Widget<Gtk::MenuItem> _menu_lash_connect;
- Widget<Gtk::MenuItem> _menu_lash_disconnect;
- void menu_open_session();
- void menu_save_session();
- void menu_save_session_as();
- void menu_close_session();
- void menu_lash_connect();
- void menu_lash_disconnect();
+ Glib::RefPtr<Gnome::Glade::Xml> _xml;
+
+#ifdef HAVE_DBUS
+ LashProxy* _lash_proxy;
+ DBus* _dbus;
+ ProjectList* _project_list;
+ Session* _session;
#endif
#ifdef HAVE_ALSA
- AlsaDriver* _alsa_driver;
+ AlsaDriver* _alsa_driver;
bool _alsa_driver_autoattach;
- Widget<Gtk::MenuItem> _menu_alsa_connect;
- Widget<Gtk::MenuItem> _menu_alsa_disconnect;
void menu_alsa_connect();
void menu_alsa_disconnect();
#endif
boost::shared_ptr<PatchageCanvas> _canvas;
- JackDriver* _jack_driver;
- StateManager* _state_manager;
+ JackDriver* _jack_driver;
+ StateManager* _state_manager;
Gtk::Main* _gtk_main;
@@ -120,11 +122,6 @@ protected:
bool _attach;
bool _refresh;
bool _enable_refresh;
- bool _pane_closed;
- bool _update_pane_position;
- int _user_pane_position;
-
- JackSettingsDialog* _jack_settings_dialog;
Widget<Gtk::AboutDialog> _about_win;
Widget<Gtk::ComboBox> _buffer_size_combo;
@@ -132,19 +129,23 @@ protected:
Widget<Gtk::ScrolledWindow> _main_scrolledwin;
Widget<Gtk::Window> _main_win;
Widget<Gtk::ProgressBar> _main_xrun_progress;
+ Widget<Gtk::MenuItem> _menu_alsa_connect;
+ Widget<Gtk::MenuItem> _menu_alsa_disconnect;
Widget<Gtk::MenuItem> _menu_file_quit;
Widget<Gtk::MenuItem> _menu_help_about;
Widget<Gtk::MenuItem> _menu_jack_connect;
Widget<Gtk::MenuItem> _menu_jack_disconnect;
- Widget<Gtk::MenuItem> _menu_jack_settings;
+ Widget<Gtk::MenuItem> _menu_open_session;
Widget<Gtk::MenuItem> _menu_store_positions;
Widget<Gtk::MenuItem> _menu_view_arrange;
Widget<Gtk::CheckMenuItem> _menu_view_messages;
+ Widget<Gtk::CheckMenuItem> _menu_view_projects;
Widget<Gtk::MenuItem> _menu_view_refresh;
Widget<Gtk::CheckMenuItem> _menu_view_toolbar;
- Widget<Gtk::Dialog> _messages_win;
Widget<Gtk::Button> _messages_clear_but;
Widget<Gtk::Button> _messages_close_but;
+ Widget<Gtk::Dialog> _messages_win;
+ Widget<Gtk::Viewport> _project_list_viewport;
Widget<Gtk::Label> _sample_rate_label;
Widget<Gtk::TextView> _status_text;
Widget<Gtk::Toolbar> _toolbar;
diff --git a/src/PatchageCanvas.cpp b/src/PatchageCanvas.cpp
index d88399a..e6f348b 100644
--- a/src/PatchageCanvas.cpp
+++ b/src/PatchageCanvas.cpp
@@ -199,7 +199,7 @@ PatchageCanvas::disconnect(boost::shared_ptr<Connectable> port1, boost::shared_p
void
PatchageCanvas::status_message(const string& msg)
{
- _app->status_message(string("[Canvas] ").append(msg));
+ _app->status_msg(string("[Canvas] ").append(msg));
}
diff --git a/src/Project.cpp b/src/Project.cpp
new file mode 100644
index 0000000..28d6aaa
--- /dev/null
+++ b/src/Project.cpp
@@ -0,0 +1,175 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <dave@drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "Project.hpp"
+#include "LashProxy.hpp"
+#include "LashClient.hpp"
+
+using namespace std;
+using boost::shared_ptr;
+
+struct ProjectImpl {
+ LashProxy* proxy;
+ string name;
+ string description;
+ string notes;
+ bool modified_status;
+ list< shared_ptr<LashClient> > clients;
+};
+
+Project::Project(LashProxy* proxy, const string& name)
+{
+ LoadedProjectProperties properties;
+
+ proxy->get_loaded_project_properties(name, properties);
+
+ _impl = new ProjectImpl;
+ _impl->proxy = proxy;
+ _impl->name = name;
+
+ _impl->description = properties.description;
+ _impl->notes = properties.notes;
+ _impl->modified_status = properties.modified_status;
+
+ //g_app->info_msg("project created");
+}
+
+Project::~Project()
+{
+ delete _impl;
+ //g_app->info_msg("project destroyed");
+}
+
+void
+Project::clear()
+{
+ shared_ptr<LashClient> client;
+
+ while (!_impl->clients.empty()) {
+ client = _impl->clients.front();
+ _impl->clients.pop_front();
+ _signal_client_removed.emit(client);
+ }
+}
+
+const string&
+Project::get_name() const
+{
+ return _impl->name;
+}
+
+void
+Project::on_name_changed(const string& name)
+{
+ _impl->name = name;
+ _signal_renamed.emit();
+}
+
+const string&
+Project::get_description() const
+{
+ return _impl->description;
+}
+
+const string&
+Project::get_notes() const
+{
+ return _impl->notes;
+}
+
+bool
+Project::get_modified_status() const
+{
+ return _impl->modified_status;
+}
+
+const Project::Clients&
+Project::get_clients() const
+{
+ return _impl->clients;
+}
+
+void
+Project::on_client_added(shared_ptr<LashClient> client)
+{
+ _impl->clients.push_back(client);
+ _signal_client_added.emit(client);
+}
+
+void
+Project::on_client_removed(const string& id)
+{
+ shared_ptr<LashClient> client;
+
+ for (list< shared_ptr<LashClient> >::iterator iter = _impl->clients.begin(); iter != _impl->clients.end(); iter++) {
+ client = *iter;
+
+ if (client->get_id() == id) {
+ _impl->clients.erase(iter);
+ _signal_client_removed.emit(client);
+ return;
+ }
+ }
+}
+
+void
+Project::on_modified_status_changed(bool modified_status)
+{
+ _impl->modified_status = modified_status;
+ _signal_modified_status_changed.emit();
+}
+
+void
+Project::on_description_changed(const string& description)
+{
+ _impl->description = description;
+ _signal_description_changed.emit();
+}
+
+void
+Project::on_notes_changed(const string& notes)
+{
+ _impl->notes = notes;
+ _signal_notes_changed.emit();
+}
+
+void
+Project::do_rename(const string& name)
+{
+ if (_impl->name != name) {
+ _impl->proxy->project_rename(_impl->name, name);
+ }
+}
+
+void
+Project::do_change_description(const string& description)
+{
+ if (_impl->description != description) {
+ _impl->proxy->project_set_description(_impl->name, description);
+ }
+}
+
+void
+Project::do_change_notes(const string& notes)
+{
+ if (_impl->notes != notes) {
+ _impl->proxy->project_set_notes(_impl->name, notes);
+ }
+}
+
diff --git a/src/Project.hpp b/src/Project.hpp
new file mode 100644
index 0000000..15e2b26
--- /dev/null
+++ b/src/Project.hpp
@@ -0,0 +1,73 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef LASH_PROJECT_HPP
+#define LASH_PROJECT_HPP
+
+#include <string>
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <sigc++/signal.h>
+
+struct ProjectImpl;
+class LashProxy;
+class LashProxyImpl;
+class LashClient;
+
+class Project {
+public:
+ Project(LashProxy* proxy, const std::string& name);
+
+ ~Project();
+
+ void clear();
+
+ typedef std::list< boost::shared_ptr<LashClient> > Clients;
+
+ const std::string& get_name() const;
+ const std::string& get_description() const;
+ const std::string& get_notes() const;
+ const Clients& get_clients() const;
+ bool get_modified_status() const;
+
+ void do_rename(const std::string& name);
+ void do_change_description(const std::string& description);
+ void do_change_notes(const std::string& notes);
+
+ sigc::signal<void> _signal_renamed;
+ sigc::signal<void> _signal_modified_status_changed;
+ sigc::signal<void> _signal_description_changed;
+ sigc::signal<void> _signal_notes_changed;
+
+ sigc::signal< void, boost::shared_ptr<LashClient> > _signal_client_added;
+ sigc::signal< void, boost::shared_ptr<LashClient> > _signal_client_removed;
+
+private:
+ friend class LashProxyImpl;
+
+ void on_name_changed(const std::string& name);
+ void on_modified_status_changed(bool modified_status);
+ void on_description_changed(const std::string& description);
+ void on_notes_changed(const std::string& notes);
+ void on_client_added(boost::shared_ptr<LashClient> client);
+ void on_client_removed(const std::string& id);
+
+ ProjectImpl* _impl;
+};
+
+#endif // LASH_PROJECT_HPP
diff --git a/src/ProjectList.cpp b/src/ProjectList.cpp
new file mode 100644
index 0000000..4b87e31
--- /dev/null
+++ b/src/ProjectList.cpp
@@ -0,0 +1,305 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <iostream>
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+
+#include "ProjectList.hpp"
+#include "Widget.hpp"
+#include "Session.hpp"
+#include "Project.hpp"
+#include "ProjectPropertiesDialog.hpp"
+#include "LashClient.hpp"
+#include "LashProxy.hpp"
+
+using boost::shared_ptr;
+using namespace std;
+
+struct ProjectList_column_record : public Gtk::TreeModel::ColumnRecord {
+ Gtk::TreeModelColumn<Glib::ustring> name;
+ Gtk::TreeModelColumn< shared_ptr<Project> > project;
+};
+
+struct ProjectListImpl : public sigc::trackable {
+ Patchage* _app;
+ Widget<Gtk::TreeView> _widget;
+ ProjectList_column_record _columns;
+ Glib::RefPtr<Gtk::TreeStore> _model;
+ Gtk::Menu _menu_popup;
+
+ ProjectListImpl(
+ Glib::RefPtr<Gnome::Glade::Xml> xml,
+ Patchage* app);
+
+ void project_added(shared_ptr<Project> project);
+ void project_closed(shared_ptr<Project> project);
+ void project_renamed(Gtk::TreeModel::iterator iter);
+ void client_added(shared_ptr<LashClient> client, Gtk::TreeModel::iterator iter);
+ void client_removed(shared_ptr<LashClient> client, Gtk::TreeModel::iterator iter);
+
+ bool on_button_press_event(GdkEventButton * event);
+
+ void on_menu_popup_load_project();
+ void on_menu_popup_save_all_projects();
+ void on_menu_popup_save_project(shared_ptr<Project> project);
+ void on_menu_popup_close_project(shared_ptr<Project> project);
+ void on_menu_popup_project_properties(shared_ptr<Project> project);
+ void on_menu_popup_close_all_projects();
+};
+
+ProjectList::ProjectList(
+ Patchage* app,
+ Session* session)
+{
+ _impl = new ProjectListImpl(app->xml(), app);
+ session->_signal_project_added.connect(mem_fun(_impl, &ProjectListImpl::project_added));
+ session->_signal_project_closed.connect(mem_fun(_impl, &ProjectListImpl::project_closed));
+}
+
+ProjectList::~ProjectList()
+{
+ delete _impl;
+}
+
+ProjectListImpl::ProjectListImpl(Glib::RefPtr<Gnome::Glade::Xml> xml, Patchage* app)
+ : _app(app)
+ , _widget(xml, "projects_list")
+{
+ _columns.add(_columns.name);
+ _columns.add(_columns.project);
+
+ _model = Gtk::TreeStore::create(_columns);
+ _widget->set_model(_model);
+
+ _widget->append_column("LASH projects", _columns.name);
+
+ _menu_popup.accelerate(*_widget);
+
+ _widget->signal_button_press_event().connect(sigc::mem_fun(*this, &ProjectListImpl::on_button_press_event), false);
+}
+
+bool
+ProjectListImpl::on_button_press_event(GdkEventButton* event)
+{
+ // Then do our custom stuff:
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ Glib::RefPtr<Gtk::TreeView::Selection> selection = _widget->get_selection();
+
+ Gtk::TreeModel::Path path;
+ Gtk::TreeViewColumn * column_ptr;
+ int cell_x;
+ int cell_y;
+
+ Gtk::Menu::MenuList& menulist = _menu_popup.items();
+
+ menulist.clear();
+
+ menulist.push_back(Gtk::Menu_Helpers::MenuElem("_Load project...", sigc::mem_fun(*this, &ProjectListImpl::on_menu_popup_load_project)));
+
+ menulist.push_back(Gtk::Menu_Helpers::MenuElem("Save _all projects", sigc::mem_fun(*this, &ProjectListImpl::on_menu_popup_save_all_projects)));
+
+ if (_widget->get_path_at_pos((int)event->x, (int)event->y, path, column_ptr, cell_x, cell_y)) {
+ //cout << path.to_string() << endl;
+ selection->unselect_all();
+ selection->select(path);
+
+ Gtk::TreeIter iter = selection->get_selected();
+ shared_ptr<Project> project = (*iter)[_columns.project];
+
+ if (project) {
+ const string& name = project->get_name();
+
+ menulist.push_back(Gtk::Menu_Helpers::MenuElem(
+ (string)"_Save project '" + name + "'",
+ sigc::bind(
+ sigc::mem_fun(*this,
+ &ProjectListImpl::on_menu_popup_save_project),
+ project)));
+
+ menulist.push_back(Gtk::Menu_Helpers::MenuElem(
+ (string)"_Close project '" + name + "'",
+ sigc::bind(
+ sigc::mem_fun(*this,
+ &ProjectListImpl::on_menu_popup_close_project),
+ project)));
+
+ menulist.push_back(Gtk::Menu_Helpers::MenuElem(
+ (string)"_Project '" + name + "' properties",
+ sigc::bind(
+ sigc::mem_fun(*this,
+ &ProjectListImpl::on_menu_popup_project_properties),
+ project)));
+ }
+ } else {
+ //cout << "No row" << endl;
+ selection->unselect_all();
+ }
+
+ menulist.push_back(Gtk::Menu_Helpers::MenuElem("Cl_ose all projects", sigc::mem_fun(*this, &ProjectListImpl::on_menu_popup_close_all_projects)));
+
+ _menu_popup.popup(event->button, event->time);
+
+ return true;
+ }
+
+ return false;
+}
+
+void
+ProjectListImpl::on_menu_popup_load_project()
+{
+ _app->show_load_project_dialog();
+}
+
+void
+ProjectListImpl::on_menu_popup_save_all_projects()
+{
+ _app->lash_proxy()->save_all_projects();
+}
+
+void
+ProjectListImpl::on_menu_popup_save_project(shared_ptr<Project> project)
+{
+ _app->lash_proxy()->save_project(project->get_name());
+}
+
+void
+ProjectListImpl::on_menu_popup_close_project(shared_ptr<Project> project)
+{
+ _app->lash_proxy()->close_project(project->get_name());
+}
+
+void
+ProjectListImpl::on_menu_popup_project_properties(shared_ptr<Project> project)
+{
+ ProjectPropertiesDialog dialog(_app->xml());
+ dialog.run(project);
+}
+
+void
+ProjectListImpl::on_menu_popup_close_all_projects()
+{
+ _app->lash_proxy()->close_all_projects();
+}
+
+void
+ProjectListImpl::project_added(
+ shared_ptr<Project> project)
+{
+ Gtk::TreeModel::iterator iter;
+ Gtk::TreeModel::iterator child_iter;
+ Gtk::TreeModel::Row row;
+ string project_name = project->get_name();
+
+ if (project->get_modified_status()) {
+ project_name += " *";
+ }
+
+ iter = _model->append();
+ row = *iter;
+ row[_columns.name] = project_name;
+ row[_columns.project] = project;
+
+ project->_signal_renamed.connect(bind(mem_fun(this, &ProjectListImpl::project_renamed), iter));
+ project->_signal_modified_status_changed.connect(bind(mem_fun(this, &ProjectListImpl::project_renamed), iter));
+ project->_signal_client_added.connect(bind(mem_fun(this, &ProjectListImpl::client_added), iter));
+ project->_signal_client_removed.connect(bind(mem_fun(this, &ProjectListImpl::client_removed), iter));
+}
+
+void
+ProjectListImpl::project_closed(
+ shared_ptr<Project> project)
+{
+ shared_ptr<Project> temp_project;
+ Gtk::TreeModel::Children children = _model->children();
+ Gtk::TreeModel::Children::iterator iter = children.begin();
+
+ while (iter != children.end()) {
+ Gtk::TreeModel::Row row = *iter;
+
+ temp_project = row[_columns.project];
+
+ if (temp_project == project) {
+ _model->erase(iter);
+ return;
+ }
+
+ iter++;
+ }
+}
+
+void
+ProjectListImpl::project_renamed(
+ Gtk::TreeModel::iterator iter)
+{
+ shared_ptr<Project> project;
+
+ Gtk::TreeModel::Row row = *iter;
+
+ project = row[_columns.project];
+ string project_name = project->get_name();
+
+ if (project->get_modified_status())
+ project_name += " *";
+
+ row[_columns.name] = project_name;
+}
+
+void
+ProjectListImpl::client_added(
+ shared_ptr<LashClient> client,
+ Gtk::TreeModel::iterator iter)
+{
+ Gtk::TreeModel::Path path = _model->get_path(iter);
+ Gtk::TreeModel::Row row = *iter;
+
+ iter = _model->append(row.children());
+ row = *iter;
+ row[_columns.name] = client->get_name();
+
+ _widget->expand_row(path, false);
+}
+
+void
+ProjectListImpl::client_removed(
+ shared_ptr<LashClient> client,
+ Gtk::TreeModel::iterator iter)
+{
+ Gtk::TreeModel::Path path = _model->get_path(iter);
+ Gtk::TreeModel::Row row = *iter;
+
+ Gtk::TreeNodeChildren childs = row.children();
+
+ for (Gtk::TreeModel::iterator child_iter = childs.begin(); iter != childs.end(); child_iter++) {
+ row = *child_iter;
+
+ if (row[_columns.name] == client->get_name()) {
+ _widget->expand_row(path, false);
+ _model->erase(child_iter);
+ return;
+ }
+ }
+}
+
+void
+ProjectList::set_lash_available(bool lash_active)
+{
+ _impl->_widget->set_sensitive(lash_active);
+}
+
diff --git a/src/ProjectList.hpp b/src/ProjectList.hpp
new file mode 100644
index 0000000..558fe48
--- /dev/null
+++ b/src/ProjectList.hpp
@@ -0,0 +1,38 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Dave Robillard <dave@drobilla.net>
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGE_PROJECT_LIST_HPP
+#define PATCHAGE_PROJECT_LIST_HPP
+
+struct ProjectListImpl;
+class Patchage;
+class Session;
+
+class ProjectList {
+public:
+ ProjectList(Patchage* app, Session* session);
+ ~ProjectList();
+
+ void set_lash_available(bool lash_active);
+
+private:
+ ProjectListImpl* _impl;
+};
+
+#endif // PATCHAGE_PROJECT_LIST_HPP
diff --git a/src/ProjectPropertiesDialog.cpp b/src/ProjectPropertiesDialog.cpp
new file mode 100644
index 0000000..ef52c3d
--- /dev/null
+++ b/src/ProjectPropertiesDialog.cpp
@@ -0,0 +1,83 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <gtkmm.h>
+#include <libglademm/xml.h>
+#include "Patchage.hpp"
+#include "Project.hpp"
+#include "ProjectPropertiesDialog.hpp"
+#include "Widget.hpp"
+
+using boost::shared_ptr;
+using namespace std;
+
+
+struct ProjectPropertiesDialogImpl {
+ ProjectPropertiesDialogImpl(Glib::RefPtr<Gnome::Glade::Xml> xml);
+
+ Widget<Gtk::Dialog> _dialog;
+ Widget<Gtk::Entry> _name;
+ Widget<Gtk::Entry> _description;
+ Widget<Gtk::TextView> _notes;
+};
+
+
+ProjectPropertiesDialog::ProjectPropertiesDialog(Glib::RefPtr<Gnome::Glade::Xml> xml)
+{
+ _impl = new ProjectPropertiesDialogImpl(xml);
+}
+
+
+ProjectPropertiesDialog::~ProjectPropertiesDialog()
+{
+ delete _impl;
+}
+
+
+void
+ProjectPropertiesDialog::run(shared_ptr<Project> project)
+{
+ Glib::RefPtr<Gtk::TextBuffer> buffer;
+ int result;
+
+ _impl->_name->set_text(project->get_name());
+
+ _impl->_description->set_text(project->get_description());
+
+ buffer = _impl->_notes->get_buffer();
+ buffer->set_text(project->get_notes());
+
+ result = _impl->_dialog->run();
+ if (result == 2) {
+ project->do_change_description(_impl->_description->get_text());
+ project->do_change_notes(buffer->get_text());
+ project->do_rename(_impl->_name->get_text());
+ }
+
+ _impl->_dialog->hide();
+}
+
+
+ProjectPropertiesDialogImpl::ProjectPropertiesDialogImpl(Glib::RefPtr<Gnome::Glade::Xml> xml)
+ : _dialog(xml, "project_properties_dialog")
+ , _name(xml, "project_name")
+ , _description(xml, "project_description")
+ , _notes(xml, "project_notes")
+{
+}
+
diff --git a/src/ProjectPropertiesDialog.hpp b/src/ProjectPropertiesDialog.hpp
new file mode 100644
index 0000000..e20cc20
--- /dev/null
+++ b/src/ProjectPropertiesDialog.hpp
@@ -0,0 +1,38 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGE_PROJECT_PROPERTIES_DIALOG_HPP
+#define PATCHAGE_PROJECT_PROPERTIES_DIALOG_HPP
+
+#include <boost/shared_ptr.hpp>
+
+struct ProjectPropertiesDialogImpl;
+class Project;
+
+class ProjectPropertiesDialog {
+public:
+ ProjectPropertiesDialog(Glib::RefPtr<Gnome::Glade::Xml> xml);
+ ~ProjectPropertiesDialog();
+
+ void run(boost::shared_ptr<Project> project);
+
+private:
+ ProjectPropertiesDialogImpl* _impl;
+};
+
+#endif // PATCHAGE_PROJECT_PROPERTIES_DIALOG_HPP
diff --git a/src/Session.cpp b/src/Session.cpp
new file mode 100644
index 0000000..ec508a6
--- /dev/null
+++ b/src/Session.cpp
@@ -0,0 +1,124 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "Project.hpp"
+#include "Session.hpp"
+#include "LashClient.hpp"
+
+using namespace std;
+using boost::shared_ptr;
+
+struct SessionImpl {
+ list< shared_ptr<Project> > projects;
+ list< shared_ptr<LashClient> > clients;
+};
+
+Session::Session()
+{
+ _impl = new SessionImpl;
+}
+
+Session::~Session()
+{
+ delete _impl;
+}
+
+void
+Session::clear()
+{
+ shared_ptr<Project> project;
+
+ _impl->clients.clear();
+
+ while (!_impl->projects.empty()) {
+ project = _impl->projects.front();
+ _impl->projects.pop_front();
+ project->clear();
+ _signal_project_closed.emit(project);
+ }
+}
+
+void
+Session::project_add(shared_ptr<Project> project)
+{
+ _impl->projects.push_back(project);
+
+ _signal_project_added.emit(project);
+}
+
+shared_ptr<Project>
+Session::find_project_by_name(const string& name)
+{
+ shared_ptr<Project> project;
+ for (list< shared_ptr<Project> >::iterator i = _impl->projects.begin(); i != _impl->projects.end(); i++)
+ if ((*i)->get_name() == name)
+ return (*i);
+
+ return shared_ptr<Project>();
+}
+
+void
+Session::project_close(const string& project_name)
+{
+ shared_ptr<Project> project;
+ Project::Clients clients;
+
+ for (list<shared_ptr<Project> >::iterator iter = _impl->projects.begin(); iter != _impl->projects.end(); iter++) {
+ project = *iter;
+
+ if (project->get_name() == project_name) {
+ _impl->projects.erase(iter);
+ _signal_project_closed.emit(project);
+
+ // remove clients from session, if not removed already
+ clients = project->get_clients();
+ for (Project::Clients::const_iterator i = clients.begin(); i != clients.end(); i++)
+ client_remove((*i)->get_id());
+
+ return;
+ }
+ }
+}
+
+void
+Session::client_add(shared_ptr<LashClient> client)
+{
+ _impl->clients.push_back(client);
+}
+
+void
+Session::client_remove(const string& id)
+{
+ for (list<shared_ptr<LashClient> >::iterator i = _impl->clients.begin(); i != _impl->clients.end(); i++) {
+ if ((*i)->get_id() == id) {
+ _impl->clients.erase(i);
+ return;
+ }
+ }
+}
+
+shared_ptr<LashClient>
+Session::find_client_by_id(const string& id)
+{
+ for (list<shared_ptr<LashClient> >::iterator i = _impl->clients.begin(); i != _impl->clients.end(); i++)
+ if ((*i)->get_id() == id)
+ return *i;
+
+ return shared_ptr<LashClient>();
+}
+
diff --git a/src/Session.hpp b/src/Session.hpp
new file mode 100644
index 0000000..4fc0591
--- /dev/null
+++ b/src/Session.hpp
@@ -0,0 +1,54 @@
+// -*- Mode: C++ ; indent-tabs-mode: t -*-
+/* This file is part of Patchage.
+ * Copyright (C) 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGE_SESSION_HPP
+#define PATCHAGE_SESSION_HPP
+
+#include <boost/shared_ptr.hpp>
+
+struct SessionImpl;
+class Project;
+class LashClient;
+
+class Session
+{
+public:
+ Session();
+ ~Session();
+
+ void clear();
+
+ void project_add(boost::shared_ptr<Project> project);
+ void project_close(const std::string& project_name);
+
+ boost::shared_ptr<Project> find_project_by_name(const std::string& name);
+
+ void client_add(boost::shared_ptr<LashClient> client);
+ void client_remove(const std::string& id);
+
+ boost::shared_ptr<LashClient> find_client_by_id(const std::string& id);
+
+ sigc::signal< void, boost::shared_ptr<Project> > _signal_project_added;
+ sigc::signal< void, boost::shared_ptr<Project> > _signal_project_closed;
+
+private:
+ SessionImpl* _impl;
+};
+
+#endif // PATCHAGE_SESSION_HPP
+
diff --git a/src/Widget.hpp b/src/Widget.hpp
index 7d03922..56eb6d8 100644
--- a/src/Widget.hpp
+++ b/src/Widget.hpp
@@ -20,6 +20,7 @@
#include <string>
#include <boost/utility.hpp>
+#include <libglademm/xml.h>
template <typename W>
class Widget : public boost::noncopyable {
diff --git a/src/patchage.glade b/src/patchage.glade
index 7559db8..c9325c6 100644
--- a/src/patchage.glade
+++ b/src/patchage.glade
@@ -35,58 +35,6 @@
</widget>
</child>
<child>
- <widget class="GtkImageMenuItem" id="menu_save_session">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_Save Project</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_save_session_menuitem_activate"/>
- <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image824">
- <property name="visible">True</property>
- <property name="stock">gtk-save</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="menu_save_session_as">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Save Project _As...</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_save_session_as_menuitem_activate"/>
- <accelerator key="S" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image825">
- <property name="visible">True</property>
- <property name="stock">gtk-save-as</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="menu_close_session">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_Close Project</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_close_session_menuitem_activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image826">
- <property name="visible">True</property>
- <property name="stock">gtk-close</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separator5">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
<widget class="GtkImageMenuItem" id="menu_store_positions">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Store the current module positions as the defaults</property>
@@ -128,27 +76,6 @@
<child>
<widget class="GtkMenu" id="file_system_menuitem_menu">
<child>
- <widget class="GtkImageMenuItem" id="menu_jack_settings">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Jack _Settings</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_jack_settings1_activate"/>
- <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image828">
- <property name="visible">True</property>
- <property name="stock">gtk-preferences</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separator6">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
<widget class="GtkImageMenuItem" id="menu_jack_connect">
<property name="visible">True</property>
<property name="label" translatable="yes">Connect to _JACK</property>
@@ -219,44 +146,6 @@
</child>
</widget>
</child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separator1">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="menu_lash_connect">
- <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_menu_lash_connect"/>
- <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image833">
- <property name="visible">True</property>
- <property name="stock">gtk-connect</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkImageMenuItem" id="menu_lash_disconnect">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="label" translatable="yes">Disconnect from LASH</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_menu_lash_disconnect_activate"/>
- <accelerator key="L" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK" signal="activate"/>
- <child internal-child="image">
- <widget class="GtkImage" id="image834">
- <property name="visible">True</property>
- <property name="stock">gtk-disconnect</property>
- <property name="icon_size">1</property>
- </widget>
- </child>
- </widget>
- </child>
</widget>
</child>
</widget>
@@ -279,9 +168,18 @@
</widget>
</child>
<child>
+ <widget class="GtkCheckMenuItem" id="menu_view_projects">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Show LASH projects list</property>
+ <property name="label" translatable="yes">_Projects</property>
+ <property name="use_underline">True</property>
+ <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ </widget>
+ </child>
+ <child>
<widget class="GtkCheckMenuItem" id="menu_view_messages">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">View "console" messages</property>
+ <property name="tooltip" translatable="yes">View log messages</property>
<property name="label" translatable="yes">_Messages</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_messages1_activate"/>
@@ -527,19 +425,49 @@ The bar represents the percentage of available time used for audio processing (i
</packing>
</child>
<child>
- <widget class="GtkScrolledWindow" id="main_scrolledwin">
+ <widget class="GtkHPaned" id="hpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="has_focus">True</property>
- <property name="is_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="position">204</property>
<child>
- <placeholder/>
+ <widget class="GtkViewport" id="project_list_viewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <widget class="GtkTreeView" id="projects_list">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="headers_clickable">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="main_scrolledwin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="is_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">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>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
</child>
</widget>
<packing>
@@ -552,9 +480,9 @@ The bar represents the percentage of available time used for audio processing (i
<widget class="GtkAboutDialog" id="about_win">
<property name="destroy_with_parent">True</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
- <property name="copyright" translatable="yes">© 2007 Dave Robillard</property>
- <property name="comments" translatable="yes">A modular patch bay for audio and MIDI systems
-based on JACK, LASH, and ALSA.</property>
+ <property name="copyright" translatable="yes">© 2005-2008 Dave Robillard
+© 2008 Nedko Arnaudov</property>
+ <property name="comments" translatable="yes">A LASH, JACK, and ALSA front-end.</property>
<property name="website">http://drobilla.net/software/patchage</property>
<property name="license" translatable="yes">Patchage is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -571,7 +499,7 @@ along with Patchage; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
</property>
<property name="authors">Dave Robillard &lt;dave@drobilla.net&gt;
-JACK D-Bus driver by Nedko Arnaudov &lt;nedko@arnaudov.name&gt;</property>
+Nedko Arnaudov &lt;nedko@arnaudov.name&gt;</property>
<property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property>
<property name="artists">Icon:
Lapo Calamandrei</property>
@@ -588,106 +516,147 @@ JACK D-Bus driver by Nedko Arnaudov &lt;nedko@arnaudov.name&gt;</property>
</widget>
</child>
</widget>
- <widget class="GtkDialog" id="jack_settings_win">
- <property name="width_request">400</property>
- <property name="height_request">200</property>
- <property name="border_width">8</property>
- <property name="title" translatable="yes">JACK Settings</property>
+ <widget class="GtkDialog" id="messages_win">
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Patchage - Messages</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
- <property name="default_width">400</property>
- <property name="default_height">200</property>
- <property name="destroy_with_parent">True</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
<child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox2">
+ <widget class="GtkVBox" id="dialog-vbox3">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
<child>
- <widget class="GtkVBox" id="vbox2">
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</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>
<child>
- <widget class="GtkLabel" id="label1">
+ <widget class="GtkTextView" id="status_text">
+ <property name="width_request">640</property>
+ <property name="height_request">480</property>
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="ypad">6</property>
- <property name="label" translatable="yes">&lt;b&gt;Command Line&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- <property name="wrap">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">False</property>
+ <property name="accepts_tab">False</property>
</widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
</child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
- <widget class="GtkHBox" id="hbox2">
+ <widget class="GtkButton" id="messages_clear_but">
<property name="visible">True</property>
- <child>
- <widget class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="label" translatable="yes"> </property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkEntry" id="jack_settings_command_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">*</property>
- <property name="activates_default">True</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-clear</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="messages_close_but">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
</widget>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="load_project_dialog">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Load project</property>
+ <property name="modal">True</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="transient_for">main_win</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="width_request">400</property>
+ <property name="height_request">400</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
- <widget class="GtkLabel" id="label3">
- <property name="width_request">323</property>
- <property name="height_request">200</property>
+ <widget class="GtkTreeView" id="loadable_projects_list">
<property name="visible">True</property>
- <property name="ypad">12</property>
- <property name="label" translatable="yes">Note: This will change your default setting in ~/.jackdrc</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="headers_clickable">True</property>
</widget>
- <packing>
- <property name="position">2</property>
- </packing>
</child>
</widget>
<packing>
- <property name="position">2</property>
+ <property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
- <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <widget class="GtkHButtonBox" id="dialog-action_area4">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
- <widget class="GtkButton" id="jack_settings_cancel_but">
+ <widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="label">gtk-cancel</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-cancel</property>
<property name="use_stock">True</property>
- <property name="response_id">-6</property>
+ <property name="response_id">1</property>
</widget>
</child>
<child>
- <widget class="GtkButton" id="jack_settings_ok_but">
+ <widget class="GtkButton" id="ok_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="label">gtk-ok</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-ok</property>
<property name="use_stock">True</property>
- <property name="response_id">-5</property>
+ <property name="response_id">2</property>
</widget>
<packing>
<property name="position">1</property>
@@ -702,40 +671,123 @@ JACK D-Bus driver by Nedko Arnaudov &lt;nedko@arnaudov.name&gt;</property>
</widget>
</child>
</widget>
- <widget class="GtkDialog" id="messages_win">
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">True</property>
+ <widget class="GtkDialog" id="project_properties_dialog">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
- <property name="title" translatable="yes">Patchage - Messages</property>
+ <property name="title" translatable="yes">Project properties</property>
+ <property name="modal">True</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="transient_for">main_win</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox3">
+ <widget class="GtkVBox" id="dialog-vbox5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
- <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
- <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</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="spacing">10</property>
<child>
- <widget class="GtkTextView" id="status_text">
- <property name="width_request">640</property>
- <property name="height_request">480</property>
+ <widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">False</property>
- <property name="wrap_mode">GTK_WRAP_WORD</property>
- <property name="cursor_visible">False</property>
- <property name="accepts_tab">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkEntry" id="project_name">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">&lt;b&gt;Project name&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkEntry" id="project_description">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">&lt;b&gt;Description&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkFrame" id="frame3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkTextView" id="project_notes">
+ <property name="width_request">300</property>
+ <property name="height_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">&lt;b&gt;Notes&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
</widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
</child>
</widget>
<packing>
@@ -743,31 +795,30 @@ JACK D-Bus driver by Nedko Arnaudov &lt;nedko@arnaudov.name&gt;</property>
</packing>
</child>
<child internal-child="action_area">
- <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <widget class="GtkHButtonBox" id="dialog-action_area5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
- <widget class="GtkButton" id="messages_clear_but">
+ <widget class="GtkButton" id="cancel_button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-clear</property>
+ <property name="label" translatable="yes">gtk-cancel</property>
<property name="use_stock">True</property>
- <property name="response_id">0</property>
+ <property name="response_id">1</property>
</widget>
</child>
<child>
- <widget class="GtkButton" id="messages_close_but">
+ <widget class="GtkButton" id="ok_buton">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
+ <property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-close</property>
+ <property name="label" translatable="yes">gtk-ok</property>
<property name="use_stock">True</property>
- <property name="response_id">0</property>
+ <property name="response_id">2</property>
</widget>
<packing>
<property name="position">1</property>