aboutsummaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/MachinaCanvas.cpp157
-rw-r--r--src/gui/MachinaCanvas.hpp55
-rw-r--r--src/gui/MachinaGUI.cpp370
-rw-r--r--src/gui/MachinaGUI.hpp88
-rw-r--r--src/gui/Makefile.am22
-rw-r--r--src/gui/machina-icon.svg66
-rw-r--r--src/gui/machina.glade340
-rw-r--r--src/gui/machina.gladep9
-rw-r--r--src/gui/main.cpp44
9 files changed, 1151 insertions, 0 deletions
diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp
new file mode 100644
index 0000000..2309a5c
--- /dev/null
+++ b/src/gui/MachinaCanvas.cpp
@@ -0,0 +1,157 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina 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.
+ *
+ * Machina 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 Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+//#include "config.h"
+#include <raul/SharedPtr.h>
+#include <flowcanvas/Ellipse.h>
+#include "MachinaCanvas.hpp"
+#include "MachinaGUI.hpp"
+
+MachinaCanvas::MachinaCanvas(MachinaGUI* app, int width, int height)
+: FlowCanvas(width, height),
+ _app(app)
+{
+}
+
+
+void
+MachinaCanvas::connect(boost::shared_ptr<Connectable>, //item1,
+ boost::shared_ptr<Connectable>) //item2)
+{
+#if 0
+ boost::shared_ptr<MachinaPort> p1 = boost::dynamic_pointer_cast<MachinaPort>(port1);
+ boost::shared_ptr<MachinaPort> p2 = boost::dynamic_pointer_cast<MachinaPort>(port2);
+ if (!p1 || !p2)
+ return;
+
+ if (p1->type() == JACK_AUDIO && p2->type() == JACK_AUDIO
+ || (p1->type() == JACK_MIDI && p2->type() == JACK_MIDI))
+ _app->jack_driver()->connect(p1, p2);
+#ifdef HAVE_ALSA
+ else if (p1->type() == ALSA_MIDI && p2->type() == ALSA_MIDI)
+ _app->alsa_driver()->connect(p1, p2);
+#endif
+ else
+ status_message("WARNING: Cannot make connection, incompatible port types.");
+#endif
+}
+
+
+void
+MachinaCanvas::disconnect(boost::shared_ptr<Connectable>,// item1,
+ boost::shared_ptr<Connectable>)// item2)
+{
+#if 0
+ boost::shared_ptr<MachinaPort> input
+ = boost::dynamic_pointer_cast<MachinaPort>(port1);
+ boost::shared_ptr<MachinaPort> output
+ = boost::dynamic_pointer_cast<MachinaPort>(port2);
+
+ if (!input || !output)
+ return;
+
+ if (input->is_output() && output->is_input()) {
+ // Damn, guessed wrong
+ boost::shared_ptr<MachinaPort> swap = input;
+ input = output;
+ output = swap;
+ }
+
+ if (!input || !output || input->is_output() || output->is_input()) {
+ status_message("ERROR: Attempt to disconnect mismatched/unknown ports");
+ return;
+ }
+
+ if (input->type() == JACK_AUDIO && output->type() == JACK_AUDIO
+ || input->type() == JACK_MIDI && output->type() == JACK_MIDI)
+ _app->jack_driver()->disconnect(output, input);
+#ifdef HAVE_ALSA
+ else if (input->type() == ALSA_MIDI && output->type() == ALSA_MIDI)
+ _app->alsa_driver()->disconnect(output, input);
+#endif
+ else
+ status_message("ERROR: Attempt to disconnect ports with mismatched types");
+#endif
+}
+
+
+void
+MachinaCanvas::status_message(const string& msg)
+{
+ _app->status_message(string("[Canvas] ").append(msg));
+}
+
+
+void
+MachinaCanvas::item_selected(SharedPtr<Item> i)
+{
+ SharedPtr<Connectable> item = PtrCast<Connectable>(i);
+ if (!item)
+ return;
+
+ cerr << "SELECTED: " << i->name() << endl;
+
+ SharedPtr<Connectable> last = _last_selected.lock();
+
+ if (last) {
+ boost::shared_ptr<Connection> c(new Connection(shared_from_this(),
+ last, item, 0x9999AAFF, true));
+ last->add_connection(c);
+ item->add_connection(c);
+ add_connection(c);
+ i->raise_to_top();
+ _last_selected.reset();
+ } else {
+ _last_selected = item;
+ }
+}
+
+
+bool
+MachinaCanvas::canvas_event(GdkEvent* event)
+{
+ static int last = 0;
+
+ assert(event);
+
+ if (event->type == GDK_BUTTON_PRESS) {
+
+ const double x = event->button.x;
+ const double y = event->button.y;
+
+ if (event->button.button == 1) {
+ SharedPtr<Ellipse> item(new Ellipse(shared_from_this(),
+ string("Note")+(char)(last++ +'0'), x, y, 30, 30, true));
+ item->signal_selected.connect(sigc::bind(sigc::mem_fun(this,
+ &MachinaCanvas::item_selected), item));
+ add_item(item);
+ item->raise_to_top();
+ } else if (event->button.button == 2) {
+ SharedPtr<Module> item(new Module(shared_from_this(),
+ string("Note")+(char)(last++ +'0'), x, y, true));
+ item->resize();
+ add_item(item);
+ } else if (event->button.button == 3) {
+ //_last_click_x = (int)event->button.x;
+ //_last_click_y = (int)event->button.y;
+ //show_menu(event);
+ }
+ }
+
+ return FlowCanvas::canvas_event(event);
+}
+
diff --git a/src/gui/MachinaCanvas.hpp b/src/gui/MachinaCanvas.hpp
new file mode 100644
index 0000000..85ae550
--- /dev/null
+++ b/src/gui/MachinaCanvas.hpp
@@ -0,0 +1,55 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina 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.
+ *
+ * Machina 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 Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef PATCHAGEPATCHBAYAREA_H
+#define PATCHAGEPATCHBAYAREA_H
+
+#include <string>
+#include <raul/SharedPtr.h>
+#include <raul/WeakPtr.h>
+#include <flowcanvas/FlowCanvas.h>
+
+using namespace LibFlowCanvas;
+
+class MachinaGUI;
+
+class MachinaCanvas : public FlowCanvas
+{
+public:
+ MachinaCanvas(MachinaGUI* _app, int width, int height);
+
+ void connect(boost::shared_ptr<Connectable> port1,
+ boost::shared_ptr<Connectable> port2);
+
+ void disconnect(boost::shared_ptr<Connectable> port1,
+ boost::shared_ptr<Connectable> port2);
+
+ void status_message(const string& msg);
+
+protected:
+ bool canvas_event(GdkEvent* event);
+
+ void item_selected(SharedPtr<Item> item);
+
+private:
+ MachinaGUI* _app;
+
+ WeakPtr<Connectable> _last_selected;
+};
+
+
+#endif // PATCHAGEPATCHBAYAREA_H
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
new file mode 100644
index 0000000..a7602c1
--- /dev/null
+++ b/src/gui/MachinaGUI.cpp
@@ -0,0 +1,370 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina 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.
+ *
+ * Machina 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 Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <cmath>
+#include <sstream>
+#include <libgnomecanvasmm.h>
+#include <libglademm/xml.h>
+#include <fstream>
+#include <pthread.h>
+#include "MachinaGUI.hpp"
+#include "MachinaCanvas.hpp"
+
+//#include "config.h"
+
+// FIXME: include to avoid undefined reference to boost SP debug hooks stuff
+#include <raul/SharedPtr.h>
+
+
+
+/* Gtk helpers (resize combo boxes) */
+
+#if 0
+static void
+gtkmm_get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
+ int& width,
+ int& height)
+{
+ Pango::Rectangle ink_rect = layout->get_ink_extents ();
+
+ width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
+ height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
+}
+
+
+static void
+gtkmm_set_width_for_given_text (Gtk::Widget &w, const gchar *text,
+ gint hpadding/*, gint vpadding*/)
+
+{
+ int old_width, old_height;
+ w.get_size_request(old_width, old_height);
+
+ int width, height;
+ w.ensure_style ();
+
+ gtkmm_get_ink_pixel_size (w.create_pango_layout (text), width, height);
+ w.set_size_request(width + hpadding, old_height);//height + vpadding);
+}
+#endif
+/* end Gtk helpers */
+
+
+
+MachinaGUI::MachinaGUI(/*int argc, char** argv*/)
+: _pane_closed(false),
+ _update_pane_position(true),
+ _user_pane_position(0),
+ _refresh(false)
+{
+ /*_settings_filename = getenv("HOME");
+ _settings_filename += "/.machinarc";*/
+
+ _canvas = boost::shared_ptr<MachinaCanvas>(new MachinaCanvas(this, 1600*2, 1200*2));
+
+ Glib::RefPtr<Gnome::Glade::Xml> xml;
+
+ // Check for the .glade file in current directory
+ string glade_filename = "./machina.glade";
+ ifstream fs(glade_filename.c_str());
+ if (fs.fail()) { // didn't find it, check PKGDATADIR
+ fs.clear();
+ glade_filename = PKGDATADIR;
+ glade_filename += "/machina.glade";
+
+ fs.open(glade_filename.c_str());
+ if (fs.fail()) {
+ cerr << "Unable to find machina.glade in current directory or " << PKGDATADIR << "." << endl;
+ exit(EXIT_FAILURE);
+ }
+ fs.close();
+ }
+
+ try {
+ xml = Gnome::Glade::Xml::create(glade_filename);
+ } catch(const Gnome::Glade::XmlError& ex) {
+ std::cerr << ex.what() << std::endl;
+ throw;
+ }
+
+ xml->get_widget("machina_win", _main_window);
+ xml->get_widget("about_win", _about_window);
+ xml->get_widget("file_quit_menuitem", _menu_file_quit);
+ xml->get_widget("view_refresh_menuitem", _menu_view_refresh);
+ xml->get_widget("view_messages_menuitem", _menu_view_messages);
+ xml->get_widget("help_about_menuitem", _menu_help_about);
+ xml->get_widget("canvas_scrolledwindow", _canvas_scrolledwindow);
+ xml->get_widget("status_text", _status_text);
+ xml->get_widget("main_paned", _main_paned);
+ xml->get_widget("messages_expander", _messages_expander);
+ xml->get_widget("zoom_full_but", _zoom_full_button);
+ xml->get_widget("zoom_normal_but", _zoom_normal_button);
+
+ _canvas_scrolledwindow->add(*_canvas);
+ //m_canvas_scrolledwindow->signal_event().connect(sigc::mem_fun(_canvas, &FlowCanvas::scroll_event_handler));
+ _canvas->scroll_to(static_cast<int>(_canvas->width()/2 - 320),
+ static_cast<int>(_canvas->height()/2 - 240)); // FIXME: hardcoded
+
+ //_zoom_slider->signal_value_changed().connect(sigc::mem_fun(this, &MachinaGUI::zoom_changed));
+
+ _zoom_normal_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::zoom), 1.0));
+
+ _zoom_full_button->signal_clicked().connect(sigc::mem_fun(_canvas.get(), &MachinaCanvas::zoom_full));
+
+ _menu_file_quit->signal_activate().connect( sigc::mem_fun(this, &MachinaGUI::menu_file_quit));
+ _menu_view_refresh->signal_activate().connect( sigc::mem_fun(this, &MachinaGUI::menu_view_refresh));
+ _menu_view_messages->signal_toggled().connect( sigc::mem_fun(this, &MachinaGUI::show_messages_toggled));
+ _menu_help_about->signal_activate().connect( sigc::mem_fun(this, &MachinaGUI::menu_help_about));
+
+ connect_widgets();
+
+ //update_state();
+
+ _canvas->show();
+
+ _main_window->present();
+
+ _update_pane_position = false;
+ _main_paned->set_position(max_pane_position());
+
+ _main_paned->property_position().signal_changed().connect(
+ sigc::mem_fun(*this, &MachinaGUI::on_pane_position_changed));
+
+ _messages_expander->property_expanded().signal_changed().connect(
+ sigc::mem_fun(*this, &MachinaGUI::on_messages_expander_changed));
+
+ _main_paned->set_position(max_pane_position());
+ _user_pane_position = max_pane_position() - _main_window->get_height()/8;
+ _update_pane_position = true;
+ _pane_closed = true;
+
+ // Idle callback, check if we need to refresh
+ Glib::signal_timeout().connect(sigc::mem_fun(this, &MachinaGUI::idle_callback), 100);
+
+ // Faster idle callback to update DSP load progress bar
+ //Glib::signal_timeout().connect(sigc::mem_fun(this, &MachinaGUI::update_load), 50);
+}
+
+
+MachinaGUI::~MachinaGUI()
+{
+}
+
+
+void
+MachinaGUI::attach()
+{
+#if 0
+ _jack_driver->attach(true);
+
+ menu_view_refresh();
+
+ update_toolbar();
+
+ //m_status_bar->push("Connected to JACK server");
+#endif
+}
+
+
+bool
+MachinaGUI::idle_callback()
+{
+#if 0
+ if (_jack_driver) {
+ while (!_jack_driver->events().empty()) {
+ MachinaEvent& ev = _jack_driver->events().front();
+ _jack_driver->events().pop();
+ ev.execute();
+ }
+ }
+
+
+ bool refresh = _refresh;
+
+ refresh = refresh || (_jack_driver && _jack_driver->is_dirty());
+
+ if (refresh) {
+
+ _canvas->flag_all_connections();
+
+ _jack_driver->refresh();
+ }
+
+ if (refresh) {
+ _canvas->destroy_all_flagged_connections();
+ _refresh = false;
+ }
+
+ update_load();
+#endif
+ return true;
+}
+
+
+void
+MachinaGUI::zoom(double z)
+{
+ _canvas->set_zoom(z);
+}
+
+
+void
+MachinaGUI::zoom_changed()
+{
+/*
+ static bool enable_signal = true;
+ if (enable_signal) {
+ enable_signal = false;
+ zoom(_zoom_slider->get_value());
+ enable_signal = true;
+ }
+ */
+}
+
+
+#if 0
+void
+MachinaGUI::update_state()
+{
+ for (ModuleMap::iterator i = _canvas->modules().begin(); i != _canvas->modules().end(); ++i)
+ (*i).second->load_location();
+
+ //cerr << "[Machina] Resizing window: (" << _state_manager->get_window_size().x
+ // << "," << _state_manager->get_window_size().y << ")" << endl;
+
+ _main_window->resize(
+ static_cast<int>(_state_manager->get_window_size().x),
+ static_cast<int>(_state_manager->get_window_size().y));
+
+ //cerr << "[Machina] Moving window: (" << _state_manager->get_window_location().x
+ // << "," << _state_manager->get_window_location().y << ")" << endl;
+
+ _main_window->move(
+ static_cast<int>(_state_manager->get_window_location().x),
+ static_cast<int>(_state_manager->get_window_location().y));
+
+}
+
+#endif
+
+
+void
+MachinaGUI::status_message(const string& msg)
+{
+ if (_status_text->get_buffer()->size() > 0)
+ _status_text->get_buffer()->insert(_status_text->get_buffer()->end(), "\n");
+
+ _status_text->get_buffer()->insert(_status_text->get_buffer()->end(), msg);
+ _status_text->scroll_to_mark(_status_text->get_buffer()->get_insert(), 0);
+}
+
+
+/** Update the sensitivity status of menus to reflect the present.
+ *
+ * (eg. disable "Connect to Jack" when Machina is already connected to Jack)
+ */
+void
+MachinaGUI::connect_widgets()
+{
+
+}
+
+
+void
+MachinaGUI::menu_file_quit()
+{
+ _main_window->hide();
+}
+
+
+void
+MachinaGUI::on_pane_position_changed()
+{
+ // avoid infinite recursion...
+ if (!_update_pane_position)
+ return;
+
+ _update_pane_position = false;
+
+ int new_position = _main_paned->get_position();
+
+ if (_pane_closed && new_position < max_pane_position()) {
+ // Auto open
+ _user_pane_position = new_position;
+ _messages_expander->set_expanded(true);
+ _pane_closed = false;
+ _menu_view_messages->set_active(true);
+ } else if (new_position >= max_pane_position()) {
+ // Auto close
+ _pane_closed = true;
+
+ _messages_expander->set_expanded(false);
+ if (new_position > max_pane_position())
+ _main_paned->set_position(max_pane_position()); // ... here
+ _menu_view_messages->set_active(false);
+
+ _user_pane_position = max_pane_position() - _main_window->get_height()/8;
+ }
+
+ _update_pane_position = true;
+}
+
+
+void
+MachinaGUI::on_messages_expander_changed()
+{
+ if (!_pane_closed) {
+ // Store pane position for restoring
+ _user_pane_position = _main_paned->get_position();
+ if (_update_pane_position) {
+ _update_pane_position = false;
+ _main_paned->set_position(max_pane_position());
+ _update_pane_position = true;
+ }
+ _pane_closed = true;
+ } else {
+ _main_paned->set_position(_user_pane_position);
+ _pane_closed = false;
+ }
+}
+
+
+void
+MachinaGUI::show_messages_toggled()
+{
+ if (_update_pane_position)
+ _messages_expander->set_expanded(_menu_view_messages->get_active());
+}
+
+
+void
+MachinaGUI::menu_view_refresh()
+{
+ assert(_canvas);
+
+ //_canvas->destroy();
+}
+
+
+void
+MachinaGUI::menu_help_about()
+{
+ _about_window->show();
+}
+
+
diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp
new file mode 100644
index 0000000..27b7f4f
--- /dev/null
+++ b/src/gui/MachinaGUI.hpp
@@ -0,0 +1,88 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina 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.
+ *
+ * Machina 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 Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef MACHINA_GUI_H
+#define MACHINA_GUI_H
+
+//#include "config.h"
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <libgnomecanvasmm.h>
+
+using namespace std;
+
+class MachinaCanvas;
+
+
+class MachinaGUI
+{
+public:
+ MachinaGUI(/*int argc, char** argv*/);
+ ~MachinaGUI();
+
+ boost::shared_ptr<MachinaCanvas> canvas() { return _canvas; }
+
+ Gtk::Window* window() { return _main_window; }
+
+ void attach();
+ void quit() { _main_window->hide(); }
+
+ void status_message(const string& msg);
+ inline void queue_refresh() { _refresh = true; }
+
+ int max_pane_position()
+ { return _main_paned->property_max_position() - _messages_expander->get_label_widget()->get_height() - 8; }
+
+protected:
+ void connect_widgets();
+
+ void menu_file_quit();
+ void show_messages_toggled();
+ void menu_view_refresh();
+ void menu_help_about();
+ void zoom(double z);
+ void zoom_changed();
+ bool idle_callback();
+
+ void on_pane_position_changed();
+ void on_messages_expander_changed();
+
+ bool _pane_closed;
+ bool _update_pane_position;
+ int _user_pane_position;
+
+ boost::shared_ptr<MachinaCanvas> _canvas;
+
+ Gtk::Main* _gtk_main;
+
+ bool _refresh;
+
+ Gtk::Window* _main_window;
+ Gtk::AboutDialog* _about_window;
+ Gtk::MenuItem* _menu_file_quit;
+ Gtk::CheckMenuItem* _menu_view_messages;
+ Gtk::MenuItem* _menu_view_refresh;
+ Gtk::MenuItem* _menu_help_about;
+ Gtk::ScrolledWindow* _canvas_scrolledwindow;
+ Gtk::TextView* _status_text;
+ Gtk::Paned* _main_paned;
+ Gtk::Expander* _messages_expander;
+ Gtk::ToolButton* _zoom_normal_button;
+ Gtk::ToolButton* _zoom_full_button;
+};
+
+#endif // MACHINA_GUI_H
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am
new file mode 100644
index 0000000..e7a88b7
--- /dev/null
+++ b/src/gui/Makefile.am
@@ -0,0 +1,22 @@
+AM_CXXFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" @LIBGLADEMM_CFLAGS@ @GNOMECANVASMM_CFLAGS@ @JACK_CFLAGS@ @FLOWCANVAS_CFLAGS@ @RAUL_CFLAGS@
+machina_gui_LDADD = @LIBGLADEMM_LIBS@ @GNOMECANVASMM_LIBS@ @JACK_LIBS@ @FLOWCANVAS_LIBS@ @RAUL_CFLAGS@
+
+EXTRA_DIST = machina.gladep
+
+sharefilesdir = $(pkgdatadir)
+dist_sharefiles_DATA = machina.glade
+
+#globalpixmapsdir = $(datadir)/pixmaps
+#dist_globalpixmaps_DATA = machina-icon.svg
+
+if WITH_GUI
+
+bin_PROGRAMS = machina_gui
+machina_gui_SOURCES = \
+ main.cpp \
+ MachinaGUI.hpp \
+ MachinaGUI.cpp \
+ MachinaCanvas.hpp \
+ MachinaCanvas.cpp
+
+endif
diff --git a/src/gui/machina-icon.svg b/src/gui/machina-icon.svg
new file mode 100644
index 0000000..39f7be0
--- /dev/null
+++ b/src/gui/machina-icon.svg
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45pre1"
+ width="256"
+ height="256"
+ version="1.0"
+ sodipodi:docbase="/home/dave/code/lad/machina/src/gui"
+ sodipodi:docname="machina-icon.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ sodipodi:modified="true">
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5" />
+ <sodipodi:namedview
+ inkscape:window-height="771"
+ inkscape:window-width="1183"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="1.4142136"
+ inkscape:cx="310.87907"
+ inkscape:cy="40.263308"
+ inkscape:window-x="293"
+ inkscape:window-y="52"
+ inkscape:current-layer="svg2"
+ width="256px"
+ height="256px" />
+ <path
+ style="fill:#000000"
+ id="path2189"
+ d="M 9.0278551,221.40628 C 22.572047,206.65805 31.269455,188.04472 39.259152,169.8919 C 51.644822,140.58208 56.041288,109.13092 58.849953,77.700978 C 59.962909,56.867589 61.443535,35.864275 59.495794,15.031194 C 63.699947,13.959444 67.938586,10.617016 72.108252,11.815945 C 77.429885,13.346107 71.833885,22.901101 72.179608,28.42755 C 73.082013,42.852664 75.819667,56.648771 78.944682,70.727057 C 85.857753,99.135712 98.546656,125.08352 122.5541,142.3916 C 149.02904,155.16169 159.76491,132.36389 173.41824,112.97967 C 189.89026,87.286307 201.67487,59.107084 212.79124,30.80732 C 238.81023,-0.42720544 213.47911,64.070958 211.76716,70.33904 C 202.71922,108.04829 200.75405,146.89462 201.60034,185.52025 C 202.59785,202.30805 203.03716,219.51338 208.40658,235.60545 C 209.82998,239.98783 211.97855,244.0493 214.19967,248.06447 L 201.80123,255.17231 C 199.63995,251.03059 197.23968,246.9206 196.00991,242.38503 C 191.48082,225.58243 192.01187,207.89788 191.55783,190.64995 C 191.61755,152.05096 193.33386,113.23415 201.28376,75.349455 C 205.86472,56.717664 207.3011,28.642568 227.34088,20.082503 C 229.12544,19.320222 225.62336,23.562881 224.76461,25.303071 C 211.8352,53.436232 199.91298,82.147117 183.57547,108.55509 C 167.9424,131.6754 143.08989,165.0319 111.95557,149.10157 C 88.367319,131.04024 76.047527,104.6256 68.72284,76.231983 C 66.537387,67.180737 52.831088,26.99333 59.36089,18.303914 C 62.551525,14.058035 67.78841,11.838043 72.002169,8.6051067 C 71.542368,29.947073 70.679719,51.279977 69.135855,72.573834 C 66.386515,103.98497 62.160375,135.44301 50.178851,164.87632 C 42.654028,182.74324 34.458191,200.83743 22.235303,216.04826 L 9.0278551,221.40628 z " />
+ <path
+ style="fill:#000000"
+ id="path2191"
+ d="M 177.4044,227.49356 C 186.73664,226.83386 194.85497,230.46984 202.63542,235.17331 C 208.34182,239.12994 205.55279,237.07748 211.00405,241.32859 L 199.24569,248.73751 C 193.86468,244.57093 196.63478,246.5644 190.93236,242.7613 C 183.22589,238.37114 175.24676,235.06127 166.16497,235.78874 L 177.4044,227.49356 z " />
+ <path
+ style="fill:#000000"
+ id="path2193"
+ d="M 202.904,253.28805 C 206.25526,244.218 211.55226,236.16787 216.96397,228.23361 C 217.73013,227.17184 218.49629,226.11007 219.26244,225.0483 L 232.24972,219.44712 C 231.4278,220.51566 230.60588,221.58419 229.78396,222.65273 C 224.30649,230.41756 219.07262,238.3307 215.72758,247.29568 L 202.904,253.28805 z " />
+</svg>
diff --git a/src/gui/machina.glade b/src/gui/machina.glade
new file mode 100644
index 0000000..e1f6fa0
--- /dev/null
+++ b/src/gui/machina.glade
@@ -0,0 +1,340 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="machina_win">
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">Machina</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="icon">machina-icon.svg</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="main_vbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
+ <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="file_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="file_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="open_session_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-open</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_open_session_menuitem_activate" last_modification_time="Sun, 01 Oct 2006 07:00:37 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="save_session_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-save</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_save_session_menuitem_activate" last_modification_time="Sun, 01 Oct 2006 07:01:40 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="save_session_as_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-save-as</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_save_session_as_menuitem_activate" last_modification_time="Sun, 01 Oct 2006 07:01:40 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator5">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="file_quit_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-quit</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_quit1_activate" last_modification_time="Sat, 11 Sep 2004 20:05:11 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="view_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="view_menu_menu">
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="view_messages_menuitem">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">View &quot;console&quot; messages</property>
+ <property name="label" translatable="yes">_Messages</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_messages1_activate" last_modification_time="Sat, 14 Oct 2006 15:43:59 GMT"/>
+ <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="view_refresh_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Refresh</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_refresh2_activate" last_modification_time="Sat, 11 Sep 2004 20:05:50 GMT"/>
+ <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image677">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="help_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="help_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="help_about_menuitem">
+ <property name="visible">True</property>
+ <property name="label">gtk-about</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_about1_activate" last_modification_time="Sat, 11 Sep 2004 20:05:11 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolbar" id="toolbar1">
+ <property name="visible">True</property>
+ <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property>
+ <property name="tooltips">True</property>
+ <property name="show_arrow">True</property>
+
+ <child>
+ <widget class="GtkToolButton" id="zoom_normal_but">
+ <property name="visible">True</property>
+ <property name="stock_id">gtk-zoom-100</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="zoom_full_but">
+ <property name="visible">True</property>
+ <property name="stock_id">gtk-zoom-fit</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVPaned" id="main_paned">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="canvas_scrolledwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkExpander" id="messages_expander">
+ <property name="can_focus">True</property>
+ <property name="expanded">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="status_text">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">False</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Messages</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">2</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkAboutDialog" id="about_win">
+ <property name="destroy_with_parent">True</property>
+ <property name="name" translatable="yes">Machina</property>
+ <property name="copyright" translatable="yes">© 2007 Dave Robillard &lt;http://drobilla.net&gt;</property>
+ <property name="comments" translatable="yes">It's a machine</property>
+ <property name="license" translatable="yes">Machina 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.
+
+Machina is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Machina; if not, write to the Free Software Foundation, Inc.,
+51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+</property>
+ <property name="wrap_license">False</property>
+ <property name="website">http://drobilla.net/software/machina</property>
+ <property name="website_label" translatable="yes">Website:</property>
+ <property name="authors">Dave Robillard &lt;dave@drobilla.net&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="logo">machina-icon.svg</property>
+</widget>
+
+</glade-interface>
diff --git a/src/gui/machina.gladep b/src/gui/machina.gladep
new file mode 100644
index 0000000..c27832f
--- /dev/null
+++ b/src/gui/machina.gladep
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>Machina</name>
+ <program_name>machina</program_name>
+ <language>C++</language>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>
diff --git a/src/gui/main.cpp b/src/gui/main.cpp
new file mode 100644
index 0000000..7250cb1
--- /dev/null
+++ b/src/gui/main.cpp
@@ -0,0 +1,44 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Machina 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.
+ *
+ * Machina 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 Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+//#include "../../config.h"
+
+#include <iostream>
+#include <libgnomecanvasmm.h>
+
+#include "MachinaGUI.hpp"
+
+int
+main(int argc, char** argv)
+{
+ try {
+
+ Gnome::Canvas::init();
+ Gtk::Main app(argc, argv);
+
+ MachinaGUI gui;
+ app.run(*gui.window());
+
+ } catch (std::string msg) {
+ std::cerr << "Caught exception, aborting. Error message was: " << msg << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+