summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-05-11 13:45:26 -0400
committerDavid Robillard <d@drobilla.net>2021-05-11 13:45:26 -0400
commitdf0fdf362347495531bc3a5d19233220721d846b (patch)
treee3631d08630d4d6651eb24aa53c9fc49157778ab
parentc6ae340c6222240dc45e9bba714c722cebece186 (diff)
downloadpatchage-df0fdf362347495531bc3a5d19233220721d846b.tar.gz
patchage-df0fdf362347495531bc3a5d19233220721d846b.tar.bz2
patchage-df0fdf362347495531bc3a5d19233220721d846b.zip
Refactor most functionality around actions and settings
This moves more code into general places, and completely eliminates dependencies on the Patchage "god object".
-rw-r--r--src/Action.hpp31
-rw-r--r--src/Canvas.cpp28
-rw-r--r--src/Canvas.hpp10
-rw-r--r--src/Configuration.cpp4
-rw-r--r--src/Configuration.hpp7
-rw-r--r--src/Metadata.cpp4
-rw-r--r--src/Metadata.hpp4
-rw-r--r--src/Patchage.cpp450
-rw-r--r--src/Patchage.hpp45
-rw-r--r--src/Reactor.cpp104
-rw-r--r--src/Reactor.hpp27
-rw-r--r--src/handle_event.cpp84
-rw-r--r--src/handle_event.hpp11
13 files changed, 490 insertions, 319 deletions
diff --git a/src/Action.hpp b/src/Action.hpp
index f843a14..0a13216 100644
--- a/src/Action.hpp
+++ b/src/Action.hpp
@@ -19,6 +19,7 @@
#include "ClientID.hpp"
#include "PortID.hpp"
+#include "Setting.hpp"
#include "SignalDirection.hpp"
#include <boost/variant/variant.hpp>
@@ -26,11 +27,17 @@
namespace patchage {
namespace action {
+struct ChangeSetting {
+ Setting setting;
+};
+
struct ConnectPorts {
PortID tail;
PortID head;
};
+struct DecreaseFontSize {};
+
struct DisconnectClient {
ClientID client;
SignalDirection direction;
@@ -45,6 +52,8 @@ struct DisconnectPorts {
PortID head;
};
+struct IncreaseFontSize {};
+
struct MoveModule {
ClientID client;
SignalDirection direction;
@@ -52,6 +61,10 @@ struct MoveModule {
double y;
};
+struct Refresh {};
+
+struct ResetFontSize {};
+
struct SplitModule {
ClientID client;
};
@@ -60,16 +73,30 @@ struct UnsplitModule {
ClientID client;
};
+struct ZoomFull {};
+struct ZoomIn {};
+struct ZoomNormal {};
+struct ZoomOut {};
+
} // namespace action
/// A high-level action from the user
-using Action = boost::variant<action::ConnectPorts,
+using Action = boost::variant<action::ChangeSetting,
+ action::ConnectPorts,
+ action::DecreaseFontSize,
action::DisconnectClient,
action::DisconnectPort,
action::DisconnectPorts,
+ action::IncreaseFontSize,
action::MoveModule,
+ action::Refresh,
+ action::ResetFontSize,
action::SplitModule,
- action::UnsplitModule>;
+ action::UnsplitModule,
+ action::ZoomFull,
+ action::ZoomIn,
+ action::ZoomNormal,
+ action::ZoomOut>;
} // namespace patchage
diff --git a/src/Canvas.cpp b/src/Canvas.cpp
index 2eaeb3d..6b10128 100644
--- a/src/Canvas.cpp
+++ b/src/Canvas.cpp
@@ -24,9 +24,9 @@
#include "Coord.hpp"
#include "ILog.hpp"
#include "Metadata.hpp"
-#include "Patchage.hpp"
#include "PortInfo.hpp"
#include "PortNames.hpp"
+#include "Setting.hpp"
#include "SignalDirection.hpp"
#include "warnings.hpp"
@@ -58,8 +58,9 @@ PATCHAGE_RESTORE_WARNINGS
namespace patchage {
-Canvas::Canvas(ActionSink& action_sink, int width, int height)
+Canvas::Canvas(ILog& log, ActionSink& action_sink, int width, int height)
: Ganv::Canvas(width, height)
+ , _log(log)
, _action_sink(action_sink)
{
signal_event.connect(sigc::mem_fun(this, &Canvas::on_event));
@@ -68,7 +69,10 @@ Canvas::Canvas(ActionSink& action_sink, int width, int height)
}
CanvasPort*
-Canvas::create_port(Patchage& patchage, const PortID& id, const PortInfo& info)
+Canvas::create_port(Configuration& conf,
+ const Metadata& metadata,
+ const PortID& id,
+ const PortInfo& info)
{
const auto client_id = id.client();
@@ -78,9 +82,9 @@ Canvas::create_port(Patchage& patchage, const PortID& id, const PortInfo& info)
// Figure out the client name, for ALSA we need the metadata cache
std::string client_name;
if (id.type() == PortID::Type::alsa) {
- const auto client_info = patchage.metadata().client(client_id);
+ const auto client_info = metadata.client(client_id);
if (!client_info) {
- patchage.log().error(fmt::format(
+ _log.error(fmt::format(
R"(Unable to add port "{}", client "{}" is unknown)", id, client_id));
return nullptr;
@@ -93,7 +97,7 @@ Canvas::create_port(Patchage& patchage, const PortID& id, const PortInfo& info)
// Determine the module type to place the port on in case of splitting
SignalDirection module_type = SignalDirection::duplex;
- if (patchage.conf().get_module_split(client_name, info.is_terminal)) {
+ if (conf.get_module_split(client_name, info.is_terminal)) {
module_type = info.direction;
}
@@ -102,12 +106,12 @@ Canvas::create_port(Patchage& patchage, const PortID& id, const PortInfo& info)
if (!parent) {
// Determine initial position
Coord loc;
- if (!patchage.conf().get_module_location(client_name, module_type, loc)) {
+ if (!conf.get_module_location(client_name, module_type, loc)) {
// No position saved, come up with a pseudo-random one
loc.x = 20 + rand() % 640;
loc.y = 20 + rand() % 480;
- patchage.conf().set_module_location(client_name, module_type, loc);
+ conf.set_module_location(client_name, module_type, loc);
}
parent = new CanvasModule(
@@ -118,7 +122,7 @@ Canvas::create_port(Patchage& patchage, const PortID& id, const PortInfo& info)
if (parent->get_port(id)) {
// TODO: Update existing port?
- patchage.log().error(fmt::format(
+ _log.error(fmt::format(
R"(Module "{}" already has port "{}")", client_name, port_name));
return nullptr;
}
@@ -129,8 +133,8 @@ Canvas::create_port(Patchage& patchage, const PortID& id, const PortInfo& info)
port_name,
info.label,
info.direction == SignalDirection::input,
- patchage.conf().get_port_color(info.type),
- patchage.show_human_names(),
+ conf.get_port_color(info.type),
+ conf.get<setting::HumanNames>(),
info.order);
_port_index.insert(std::make_pair(id, port));
@@ -154,7 +158,7 @@ Canvas::find_module(const ClientID& id, const SignalDirection type)
}
}
- // Return InputOutput module for Input or Output (or nullptr if not found)
+ // Return duplex module for input or output (or nullptr if not found)
return io_module;
}
diff --git a/src/Canvas.hpp b/src/Canvas.hpp
index 3a15ca6..943b399 100644
--- a/src/Canvas.hpp
+++ b/src/Canvas.hpp
@@ -42,14 +42,17 @@ struct PortInfo;
class CanvasModule;
class CanvasPort;
-class Patchage;
+class ILog;
+class Metadata;
+class Configuration;
class Canvas : public Ganv::Canvas
{
public:
- Canvas(ActionSink& action_sink, int width, int height);
+ Canvas(ILog& log, ActionSink& action_sink, int width, int height);
- CanvasPort* create_port(Patchage& patchage,
+ CanvasPort* create_port(Configuration& conf,
+ const Metadata& metadata,
const PortID& id,
const PortInfo& info);
@@ -79,6 +82,7 @@ private:
void on_connect(Ganv::Node* port1, Ganv::Node* port2);
void on_disconnect(Ganv::Node* port1, Ganv::Node* port2);
+ ILog& _log;
ActionSink& _action_sink;
PortIndex _port_index;
ModuleIndex _module_index;
diff --git a/src/Configuration.cpp b/src/Configuration.cpp
index 665e66f..8b5a47b 100644
--- a/src/Configuration.cpp
+++ b/src/Configuration.cpp
@@ -27,6 +27,7 @@
#include <ios>
#include <iostream>
#include <limits>
+#include <utility>
#include <vector>
namespace patchage {
@@ -37,7 +38,8 @@ static const char* const port_type_names[N_PORT_TYPES] = {"JACK_AUDIO",
"JACK_OSC",
"JACK_CV"};
-Configuration::Configuration()
+Configuration::Configuration(std::function<void(const Setting&)> on_change)
+ : _on_change(std::move(on_change))
{
std::get<setting::WindowLocation>(_settings).value = Coord{0.0, 0.0};
std::get<setting::WindowSize>(_settings).value = Coord{960.0, 540.0};
diff --git a/src/Configuration.hpp b/src/Configuration.hpp
index 110a4fb..fd6b71d 100644
--- a/src/Configuration.hpp
+++ b/src/Configuration.hpp
@@ -25,6 +25,7 @@
#include <boost/optional/optional.hpp>
#include <cstdint>
+#include <functional>
#include <map>
#include <string>
#include <tuple>
@@ -36,7 +37,7 @@ namespace patchage {
class Configuration
{
public:
- explicit Configuration();
+ explicit Configuration(std::function<void(const Setting&)> on_change);
void load();
void save();
@@ -60,6 +61,7 @@ public:
void set_port_color(PortType type, uint32_t rgba)
{
_port_colors[static_cast<unsigned>(type)] = rgba;
+ _on_change(setting::PortColor{type, rgba});
}
// Set a global configuration setting
@@ -70,6 +72,7 @@ public:
if (setting.value != value) {
setting.value = std::move(value);
+ _on_change(setting);
}
}
@@ -131,6 +134,8 @@ private:
setting::Zoom>;
Settings _settings;
+
+ std::function<void(const Setting&)> _on_change;
};
} // namespace patchage
diff --git a/src/Metadata.cpp b/src/Metadata.cpp
index b1c2ffd..e75cf19 100644
--- a/src/Metadata.cpp
+++ b/src/Metadata.cpp
@@ -28,7 +28,7 @@
namespace patchage {
boost::optional<ClientInfo>
-Metadata::client(const ClientID& id)
+Metadata::client(const ClientID& id) const
{
const auto i = _client_data.find(id);
if (i == _client_data.end()) {
@@ -39,7 +39,7 @@ Metadata::client(const ClientID& id)
}
boost::optional<PortInfo>
-Metadata::port(const PortID& id)
+Metadata::port(const PortID& id) const
{
const auto i = _port_data.find(id);
if (i == _port_data.end()) {
diff --git a/src/Metadata.hpp b/src/Metadata.hpp
index 7da86d8..489c0b2 100644
--- a/src/Metadata.hpp
+++ b/src/Metadata.hpp
@@ -34,8 +34,8 @@ class Metadata
public:
Metadata() = default;
- boost::optional<ClientInfo> client(const ClientID& id);
- boost::optional<PortInfo> port(const PortID& id);
+ boost::optional<ClientInfo> client(const ClientID& id) const;
+ boost::optional<PortInfo> port(const PortID& id) const;
void set_client(const ClientID& id, const ClientInfo& info);
void set_port(const PortID& id, const PortInfo& info);
diff --git a/src/Patchage.cpp b/src/Patchage.cpp
index 9c346f2..5adb524 100644
--- a/src/Patchage.cpp
+++ b/src/Patchage.cpp
@@ -47,6 +47,8 @@ PATCHAGE_DISABLE_FMT_WARNINGS
PATCHAGE_RESTORE_WARNINGS
#include <boost/optional/optional.hpp>
+#include <boost/variant/apply_visitor.hpp>
+#include <boost/variant/variant.hpp>
#include <glib-object.h>
#include <glib.h>
#include <glibmm/fileutils.h>
@@ -96,6 +98,7 @@ PATCHAGE_RESTORE_WARNINGS
#include <cmath>
#include <cstdint>
#include <cstdlib>
+#include <functional>
#include <map>
#include <utility>
@@ -164,6 +167,7 @@ port_order(const GanvPort* a, const GanvPort* b, void*)
Patchage::Patchage(Options options)
: _xml(UIFile::open("patchage"))
+ , _conf([this](const Setting& setting) { on_conf_change(setting); })
, INIT_WIDGET(_about_win)
, INIT_WIDGET(_main_scrolledwin)
, INIT_WIDGET(_main_win)
@@ -201,16 +205,13 @@ Patchage::Patchage(Options options)
, INIT_WIDGET(_status_text)
, _legend(nullptr)
, _log(_status_text)
- , _reactor(*this)
, _drivers(_log, [this](const Event& event) { on_driver_event(event); })
+ , _canvas(new Canvas{_log, _action_sink, 1600 * 2, 1200 * 2})
+ , _reactor(_conf, _drivers, *_canvas, _log)
, _action_sink([this](const Action& action) { _reactor(action); })
, _options{options}
- , _pane_initialized(false)
, _attach(true)
{
- _canvas =
- std::unique_ptr<Canvas>{new Canvas(_action_sink, 1600 * 2, 1200 * 2)};
-
Glib::set_application_name("Patchage");
_about_win->property_program_name() = "Patchage";
_about_win->property_logo_icon_name() = "patchage";
@@ -245,8 +246,8 @@ Patchage::Patchage(Options options)
sigc::mem_fun(this, &Patchage::on_quit));
_menu_export_image->signal_activate().connect(
sigc::mem_fun(this, &Patchage::on_export_image));
- _menu_view_refresh->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::refresh));
+ _menu_view_refresh->signal_activate().connect(sigc::bind(
+ sigc::mem_fun(this, &Patchage::on_menu_action), Action{action::Refresh{}}));
_menu_view_human_names->signal_activate().connect(
sigc::mem_fun(this, &Patchage::on_view_human_names));
_menu_view_sort_ports->signal_activate().connect(
@@ -261,20 +262,26 @@ Patchage::Patchage(Options options)
sigc::mem_fun(this, &Patchage::on_view_toolbar));
_menu_help_about->signal_activate().connect(
sigc::mem_fun(this, &Patchage::on_help_about));
- _menu_zoom_in->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::on_zoom_in));
- _menu_zoom_out->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::on_zoom_out));
+
+ _menu_zoom_in->signal_activate().connect(sigc::bind(
+ sigc::mem_fun(this, &Patchage::on_menu_action), Action{action::ZoomIn{}}));
+ _menu_zoom_out->signal_activate().connect(sigc::bind(
+ sigc::mem_fun(this, &Patchage::on_menu_action), Action{action::ZoomOut{}}));
_menu_zoom_normal->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::on_zoom_normal));
+ sigc::bind(sigc::mem_fun(this, &Patchage::on_menu_action),
+ Action{action::ZoomNormal{}}));
_menu_zoom_full->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::on_zoom_full));
+ sigc::bind(sigc::mem_fun(this, &Patchage::on_menu_action),
+ Action{action::ZoomFull{}}));
_menu_increase_font_size->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::on_increase_font_size));
+ sigc::bind(sigc::mem_fun(this, &Patchage::on_menu_action),
+ Action{action::IncreaseFontSize{}}));
_menu_decrease_font_size->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::on_decrease_font_size));
+ sigc::bind(sigc::mem_fun(this, &Patchage::on_menu_action),
+ Action{action::DecreaseFontSize{}}));
_menu_normal_font_size->signal_activate().connect(
- sigc::mem_fun(this, &Patchage::on_normal_font_size));
+ sigc::bind(sigc::mem_fun(this, &Patchage::on_menu_action),
+ Action{action::ResetFontSize{}}));
if (_canvas->supports_sprung_layout()) {
_menu_view_sprung_layout->set_active(true);
@@ -283,22 +290,15 @@ Patchage::Patchage(Options options)
_menu_view_sprung_layout->set_sensitive(false);
}
+ // Present window so that display attributes like font size are available
_canvas->widget().show();
_main_win->present();
+ // Set the default font size based on the current GUI environment
_conf.set<setting::FontSize>(_canvas->get_default_font_size());
- _conf.load();
- _canvas->set_zoom(_conf.get<setting::Zoom>());
- _canvas->set_font_size(_conf.get<setting::FontSize>());
- if (_conf.get<setting::SortedPorts>()) {
- _canvas->set_port_order(port_order, nullptr);
- }
- _main_win->resize(static_cast<int>(_conf.get<setting::WindowSize>().x),
- static_cast<int>(_conf.get<setting::WindowSize>().y));
-
- _main_win->move(static_cast<int>(_conf.get<setting::WindowLocation>().x),
- static_cast<int>(_conf.get<setting::WindowLocation>().y));
+ // Load configuration file (but do not apply it yet, see below)
+ _conf.load();
_legend = new Legend(_conf);
_legend->signal_color_changed.connect(
@@ -307,6 +307,7 @@ Patchage::Patchage(Options options)
_legend->show_all();
_about_win->set_transient_for(*_main_win);
+
#ifdef __APPLE__
try {
_about_win->set_logo(Gdk::Pixbuf::create_from_file(
@@ -338,10 +339,6 @@ Patchage::Patchage(Options options)
_menu_alsa_disconnect->set_sensitive(false);
}
- _menu_view_toolbar->set_active(_conf.get<setting::ToolbarVisible>());
- _menu_view_sprung_layout->set_active(_conf.get<setting::SprungLayout>());
- _menu_view_sort_ports->set_active(_conf.get<setting::SortedPorts>());
-
g_signal_connect(
_main_win->gobj(), "configure-event", G_CALLBACK(configure_cb), this);
@@ -366,6 +363,9 @@ Patchage::Patchage(Options options)
gtkosx_application_ready(osxapp);
#endif
+ // Apply all configuration settings to ensure the GUI is synced
+ _conf.each([this](const Setting& setting) { on_conf_change(setting); });
+
// Set up an idle callback to process events and update the GUI if necessary
Glib::signal_timeout().connect(sigc::mem_fun(this, &Patchage::idle_callback),
100);
@@ -389,7 +389,6 @@ Patchage::attach()
}
process_events();
- refresh();
update_toolbar();
}
@@ -466,83 +465,6 @@ Patchage::update_load()
}
void
-Patchage::refresh()
-{
- auto sink = [this](const Event& event) {
- _log.info("Refresh: " + event_to_string(event));
- handle_event(*this, event);
- };
-
- if (_canvas) {
- sink(event::Cleared{});
-
- if (_drivers.jack()) {
- _drivers.jack()->refresh(sink);
- }
-
- if (_drivers.alsa()) {
- _drivers.alsa()->refresh(sink);
- }
- }
-}
-
-void
-Patchage::driver_attached(const ClientType type)
-{
- switch (type) {
- case ClientType::jack:
- _menu_jack_connect->set_sensitive(false);
- _menu_jack_disconnect->set_sensitive(true);
-
- if (_drivers.jack()) {
- _drivers.jack()->refresh(
- [this](const Event& event) { handle_event(*this, event); });
- }
-
- break;
- case ClientType::alsa:
- _menu_alsa_connect->set_sensitive(false);
- _menu_alsa_disconnect->set_sensitive(true);
-
- if (_drivers.alsa()) {
- _drivers.alsa()->refresh(
- [this](const Event& event) { handle_event(*this, event); });
- }
-
- break;
- }
-}
-
-void
-Patchage::driver_detached(const ClientType type)
-{
- switch (type) {
- case ClientType::jack:
- _menu_jack_connect->set_sensitive(true);
- _menu_jack_disconnect->set_sensitive(false);
-
- _canvas->remove_ports([](const CanvasPort* port) {
- return (port->type() == PortType::jack_audio ||
- port->type() == PortType::jack_midi ||
- port->type() == PortType::jack_osc ||
- port->type() == PortType::jack_cv);
- });
-
- break;
-
- case ClientType::alsa:
- _menu_alsa_connect->set_sensitive(true);
- _menu_alsa_disconnect->set_sensitive(false);
-
- _canvas->remove_ports([](const CanvasPort* port) {
- return port->type() == PortType::alsa_midi;
- });
-
- break;
- }
-}
-
-void
Patchage::store_window_location()
{
int loc_x = 0;
@@ -569,47 +491,58 @@ Patchage::clear_load()
}
void
-Patchage::on_driver_event(const Event& event)
+Patchage::operator()(const setting::AlsaAttached& setting)
{
- std::lock_guard<std::mutex> lock{_events_mutex};
-
- _driver_events.emplace(event);
-}
+ if (setting.value) {
+ _menu_alsa_connect->set_sensitive(false);
+ _menu_alsa_disconnect->set_sensitive(true);
-void
-Patchage::process_events()
-{
- std::lock_guard<std::mutex> lock{_events_mutex};
+ if (_drivers.alsa()) {
+ _drivers.alsa()->refresh([this](const Event& event) {
+ handle_event(_conf, _metadata, *_canvas, _log, event);
+ });
+ }
+ } else {
+ _menu_alsa_connect->set_sensitive(true);
+ _menu_alsa_disconnect->set_sensitive(false);
- while (!_driver_events.empty()) {
- _log.info(event_to_string(_driver_events.front()));
- handle_event(*this, _driver_events.front());
- _driver_events.pop();
+ _canvas->remove_ports([](const CanvasPort* port) {
+ return port->type() == PortType::alsa_midi;
+ });
}
}
void
-Patchage::on_arrange()
+Patchage::operator()(const setting::JackAttached& setting)
{
- if (_canvas) {
- _canvas->arrange();
- }
-}
+ if (setting.value) {
+ _menu_jack_connect->set_sensitive(false);
+ _menu_jack_disconnect->set_sensitive(true);
-void
-Patchage::on_sprung_layout_toggled()
-{
- const bool sprung = _menu_view_sprung_layout->get_active();
+ if (_drivers.jack()) {
+ _drivers.jack()->refresh([this](const Event& event) {
+ handle_event(_conf, _metadata, *_canvas, _log, event);
+ });
+ }
+ } else {
+ _menu_jack_connect->set_sensitive(true);
+ _menu_jack_disconnect->set_sensitive(false);
- _canvas->set_sprung_layout(sprung);
- _conf.set<setting::SprungLayout>(sprung);
+ _canvas->remove_ports([](const CanvasPort* port) {
+ return (port->type() == PortType::jack_audio ||
+ port->type() == PortType::jack_midi ||
+ port->type() == PortType::jack_osc ||
+ port->type() == PortType::jack_cv);
+ });
+ }
}
void
-Patchage::on_help_about()
+Patchage::operator()(const setting::FontSize& setting)
{
- _about_win->run();
- _about_win->hide();
+ if (_canvas->get_font_size() != setting.value) {
+ _canvas->set_font_size(setting.value);
+ }
}
static void
@@ -631,72 +564,37 @@ update_labels(GanvNode* node, void* data)
}
void
-Patchage::on_view_human_names()
-{
- bool human_names = show_human_names();
- _canvas->for_each_node(update_labels, &human_names);
-}
-
-void
-Patchage::on_view_sort_ports()
-{
- const bool sort_ports = this->sort_ports();
- _canvas->set_port_order(sort_ports ? port_order : nullptr, nullptr);
- _conf.set<setting::SortedPorts>(sort_ports);
- refresh();
-}
-
-void
-Patchage::on_zoom_in()
-{
- const float zoom = _canvas->get_zoom() * 1.25;
- _canvas->set_zoom(zoom);
- _conf.set<setting::Zoom>(zoom);
-}
-
-void
-Patchage::on_zoom_out()
+Patchage::operator()(const setting::HumanNames& setting)
{
- const float zoom = _canvas->get_zoom() * 0.75;
- _canvas->set_zoom(zoom);
- _conf.set<setting::Zoom>(zoom);
-}
+ bool human_names = setting.value;
-void
-Patchage::on_zoom_normal()
-{
- _canvas->set_zoom(1.0);
- _conf.set<setting::Zoom>(1.0);
+ _menu_view_human_names->set_active(human_names);
+ _canvas->for_each_node(update_labels, &human_names);
}
void
-Patchage::on_zoom_full()
+Patchage::operator()(const setting::MessagesHeight& setting)
{
- _canvas->zoom_full();
- _conf.set<setting::Zoom>(_canvas->get_zoom());
-}
+ if (_log_scrolledwindow->is_visible()) {
+ const int min_height = _log.min_height();
+ const int max_pos = _main_paned->get_allocation().get_height();
+ const int conf_height = setting.value;
-void
-Patchage::on_increase_font_size()
-{
- const float points = _canvas->get_font_size() + 1.0;
- _canvas->set_font_size(points);
- _conf.set<setting::FontSize>(points);
+ _main_paned->set_position(max_pos - std::max(conf_height, min_height));
+ }
}
void
-Patchage::on_decrease_font_size()
+Patchage::operator()(const setting::MessagesVisible& setting)
{
- const float points = _canvas->get_font_size() - 1.0;
- _canvas->set_font_size(points);
- _conf.set<setting::FontSize>(points);
-}
+ if (setting.value) {
+ _log_scrolledwindow->show();
+ _status_text->scroll_to_mark(_status_text->get_buffer()->get_insert(), 0);
+ } else {
+ _log_scrolledwindow->hide();
+ }
-void
-Patchage::on_normal_font_size()
-{
- _canvas->set_font_size(_canvas->get_default_font_size());
- _conf.set<setting::FontSize>(_canvas->get_default_font_size());
+ _menu_view_messages->set_active(setting.value);
}
static inline guint
@@ -748,17 +646,154 @@ update_edge_color(GanvEdge* edge, void* data)
}
void
-Patchage::on_legend_color_change(PortType id, const std::string&, uint32_t rgba)
+Patchage::operator()(const setting::PortColor&)
{
- _conf.set_port_color(id, rgba);
_canvas->for_each_node(update_port_colors, this);
_canvas->for_each_edge(update_edge_color, this);
}
void
+Patchage::operator()(const setting::SortedPorts& setting)
+{
+ _menu_view_sort_ports->set_active(setting.value);
+ if (setting.value) {
+ _canvas->set_port_order(port_order, nullptr);
+ } else {
+ _canvas->set_port_order(nullptr, nullptr);
+ }
+}
+
+void
+Patchage::operator()(const setting::SprungLayout& setting)
+{
+ _canvas->set_sprung_layout(setting.value);
+ _menu_view_sprung_layout->set_active(setting.value);
+}
+
+void
+Patchage::operator()(const setting::ToolbarVisible& setting)
+{
+ if (setting.value) {
+ _toolbar->show();
+ _menu_view_toolbar->set_active(true);
+ } else {
+ _toolbar->hide();
+ _menu_view_toolbar->set_active(false);
+ }
+}
+
+void
+Patchage::operator()(const setting::WindowLocation& setting)
+{
+ const int new_x = static_cast<int>(setting.value.x);
+ const int new_y = static_cast<int>(setting.value.y);
+
+ int current_x = 0;
+ int current_y = 0;
+ _main_win->get_position(current_x, current_y);
+
+ if (new_x != current_x || new_y != current_y) {
+ _main_win->move(new_x, new_y);
+ }
+}
+
+void
+Patchage::operator()(const setting::WindowSize& setting)
+{
+ const int new_w = static_cast<int>(setting.value.x);
+ const int new_h = static_cast<int>(setting.value.y);
+
+ int current_w = 0;
+ int current_h = 0;
+ _main_win->get_size(current_w, current_h);
+
+ if (new_w != current_w || new_h != current_h) {
+ _main_win->resize(new_w, new_h);
+ }
+}
+
+void
+Patchage::operator()(const setting::Zoom& setting)
+{
+ if (_canvas->get_zoom() != setting.value) {
+ _canvas->set_zoom(setting.value);
+ }
+}
+
+void
+Patchage::on_driver_event(const Event& event)
+{
+ std::lock_guard<std::mutex> lock{_events_mutex};
+
+ _driver_events.emplace(event);
+}
+
+void
+Patchage::process_events()
+{
+ std::lock_guard<std::mutex> lock{_events_mutex};
+
+ while (!_driver_events.empty()) {
+ const Event& event = _driver_events.front();
+
+ _log.info(event_to_string(event));
+ handle_event(_conf, _metadata, *_canvas, _log, event);
+
+ _driver_events.pop();
+ }
+}
+
+void
+Patchage::on_conf_change(const Setting& setting)
+{
+ boost::apply_visitor(*this, setting);
+}
+
+void
+Patchage::on_arrange()
+{
+ if (_canvas) {
+ _canvas->arrange();
+ }
+}
+
+void
+Patchage::on_sprung_layout_toggled()
+{
+ _conf.set<setting::SprungLayout>(_menu_view_sprung_layout->get_active());
+}
+
+void
+Patchage::on_help_about()
+{
+ _about_win->run();
+ _about_win->hide();
+}
+
+void
+Patchage::on_view_human_names()
+{
+ _conf.set<setting::HumanNames>(_menu_view_human_names->get_active());
+}
+
+void
+Patchage::on_view_sort_ports()
+{
+ _conf.set<setting::SortedPorts>(_menu_view_sort_ports->get_active());
+ _reactor(action::Refresh{});
+}
+
+void
+Patchage::on_legend_color_change(PortType id, const std::string&, uint32_t rgba)
+{
+ _conf.set_port_color(id, rgba);
+}
+
+void
Patchage::on_messages_resized(Gtk::Allocation&)
{
const int max_pos = _main_paned->get_allocation().get_height();
+
_conf.set<setting::MessagesHeight>(max_pos - _main_paned->get_position());
}
@@ -840,35 +875,12 @@ Patchage::on_export_image()
void
Patchage::on_view_messages()
{
- if (_menu_view_messages->get_active()) {
- Glib::RefPtr<Gtk::TextBuffer> buffer = _status_text->get_buffer();
- if (!_pane_initialized) {
- const int min_height = _log.min_height();
- const int max_pos = _main_paned->get_allocation().get_height();
- const int conf_height = _conf.get<setting::MessagesHeight>();
- _main_paned->set_position(max_pos - std::max(conf_height, min_height));
-
- _pane_initialized = true;
- }
-
- _log_scrolledwindow->show();
- _status_text->scroll_to_mark(_status_text->get_buffer()->get_insert(), 0);
- _conf.set<setting::MessagesVisible>(true);
- } else {
- _log_scrolledwindow->hide();
- _conf.set<setting::MessagesVisible>(false);
- }
+ _conf.set<setting::MessagesVisible>(_menu_view_messages->get_active());
}
void
Patchage::on_view_toolbar()
{
- if (_menu_view_toolbar->get_active()) {
- _toolbar->show();
- } else {
- _toolbar->hide();
- }
-
_conf.set<setting::ToolbarVisible>(_menu_view_toolbar->get_active());
}
@@ -879,6 +891,12 @@ Patchage::on_scroll(GdkEventScroll*)
}
void
+Patchage::on_menu_action(const Action& action)
+{
+ _reactor(action);
+}
+
+void
Patchage::buffer_size_changed()
{
if (_drivers.jack()) {
diff --git a/src/Patchage.hpp b/src/Patchage.hpp
index 4dc2ecf..6654151 100644
--- a/src/Patchage.hpp
+++ b/src/Patchage.hpp
@@ -24,8 +24,8 @@
#include <gtkmm/treemodelcolumn.h>
#include <gtkmm/widget.h>
+#include "Action.hpp"
#include "ActionSink.hpp"
-#include "ClientType.hpp"
#include "Configuration.hpp"
#include "Drivers.hpp"
#include "Event.hpp"
@@ -33,6 +33,7 @@
#include "Options.hpp"
#include "PortType.hpp"
#include "Reactor.hpp"
+#include "Setting.hpp"
#include "TextViewLog.hpp"
#include "Widget.hpp"
@@ -76,7 +77,6 @@ class Patchage
{
public:
explicit Patchage(Options options);
-
~Patchage();
Patchage(const Patchage&) = delete;
@@ -85,23 +85,27 @@ public:
Patchage(Patchage&&) = delete;
Patchage& operator=(Patchage&&) = delete;
- const std::unique_ptr<Canvas>& canvas() const { return _canvas; }
+ void operator()(const setting::AlsaAttached& setting);
+ void operator()(const setting::FontSize& setting);
+ void operator()(const setting::HumanNames& setting);
+ void operator()(const setting::JackAttached& setting);
+ void operator()(const setting::MessagesHeight& setting);
+ void operator()(const setting::MessagesVisible& setting);
+ void operator()(const setting::PortColor& setting);
+ void operator()(const setting::SortedPorts& setting);
+ void operator()(const setting::SprungLayout& setting);
+ void operator()(const setting::ToolbarVisible& setting);
+ void operator()(const setting::WindowLocation& setting);
+ void operator()(const setting::WindowSize& setting);
+ void operator()(const setting::Zoom& setting);
void attach();
- void refresh();
void save();
void quit();
- void driver_attached(ClientType type);
- void driver_detached(ClientType type);
-
void store_window_location();
- bool show_human_names() const { return _menu_view_human_names->get_active(); }
-
- bool sort_ports() const { return _menu_view_sort_ports->get_active(); }
-
- Drivers& drivers() { return _drivers; }
+ Canvas& canvas() const { return *_canvas; }
Gtk::Window* window() { return _main_win.get(); }
ILog& log() { return _log; }
Metadata& metadata() { return _metadata; }
@@ -120,6 +124,8 @@ protected:
void on_driver_event(const Event& event);
void process_events();
+ void on_conf_change(const Setting& setting);
+
void on_arrange();
void on_sprung_layout_toggled();
void on_help_about();
@@ -130,13 +136,6 @@ protected:
void on_store_positions();
void on_view_human_names();
void on_view_sort_ports();
- void on_zoom_in();
- void on_zoom_out();
- void on_zoom_normal();
- void on_zoom_full();
- void on_increase_font_size();
- void on_decrease_font_size();
- void on_normal_font_size();
void on_legend_color_change(PortType id,
const std::string& label,
@@ -146,6 +145,8 @@ protected:
bool on_scroll(GdkEventScroll* ev);
+ void on_menu_action(const Action& action);
+
bool idle_callback();
void clear_load();
bool update_load();
@@ -155,11 +156,10 @@ protected:
Glib::RefPtr<Gtk::Builder> _xml;
+ std::unique_ptr<Canvas> _canvas;
std::mutex _events_mutex;
std::queue<Event> _driver_events;
- std::unique_ptr<Canvas> _canvas;
-
- Configuration _conf;
+ Configuration _conf;
BufferSizeColumns _buf_size_columns;
@@ -209,7 +209,6 @@ protected:
Glib::RefPtr<Gtk::TextTag> _warning_tag;
Options _options;
- bool _pane_initialized;
bool _attach;
};
diff --git a/src/Reactor.cpp b/src/Reactor.cpp
index b875eb2..858a877 100644
--- a/src/Reactor.cpp
+++ b/src/Reactor.cpp
@@ -23,8 +23,8 @@
#include "Driver.hpp"
#include "Drivers.hpp"
#include "ILog.hpp"
-#include "Patchage.hpp"
#include "PortID.hpp"
+#include "Setting.hpp"
#include "warnings.hpp"
#include "ganv/Port.hpp"
@@ -35,14 +35,16 @@ PATCHAGE_RESTORE_WARNINGS
#include <boost/variant/apply_visitor.hpp>
-#include <memory>
-
namespace patchage {
-Reactor::Reactor(Patchage& patchage)
- : _patchage{patchage}
- , _log{patchage.log()}
- , _drivers{patchage.drivers()}
+Reactor::Reactor(Configuration& conf,
+ Drivers& drivers,
+ Canvas& canvas,
+ ILog& log)
+ : _conf{conf}
+ , _drivers{drivers}
+ , _canvas{canvas}
+ , _log{log}
{}
void
@@ -60,6 +62,12 @@ Reactor::operator()(const action::ConnectPorts& action)
}
void
+Reactor::operator()(const action::DecreaseFontSize&)
+{
+ _conf.set<setting::FontSize>(_conf.get<setting::FontSize>() - 1.0);
+}
+
+void
Reactor::operator()(const action::DisconnectClient& action)
{
if (CanvasModule* mod = find_module(action.client, action.direction)) {
@@ -92,31 +100,67 @@ Reactor::operator()(const action::DisconnectPorts& action)
}
void
+Reactor::operator()(const action::IncreaseFontSize&)
+{
+ _conf.set<setting::FontSize>(_conf.get<setting::FontSize>() + 1.0);
+}
+
+void
Reactor::operator()(const action::MoveModule& action)
{
- if (CanvasModule* mod = find_module(action.client, action.direction)) {
- _patchage.conf().set_module_location(
- mod->name(), action.direction, {action.x, action.y});
- }
+ _conf.set_module_location(
+ module_name(action.client), action.direction, {action.x, action.y});
+}
+
+void
+Reactor::operator()(const action::Refresh&)
+{
+ _drivers.refresh();
+}
+
+void
+Reactor::operator()(const action::ResetFontSize&)
+{
+ _conf.set<setting::FontSize>(_canvas.get_default_font_size());
}
void
Reactor::operator()(const action::SplitModule& action)
{
- if (CanvasModule* mod = find_module(action.client, SignalDirection::duplex)) {
- _patchage.conf().set_module_split(mod->name(), true);
- _patchage.refresh();
- }
+ _conf.set_module_split(module_name(action.client), true);
+ _drivers.refresh();
}
void
Reactor::operator()(const action::UnsplitModule& action)
{
- CanvasModule* mod = find_module(action.client, SignalDirection::input);
- if (mod || (mod = find_module(action.client, SignalDirection::output))) {
- _patchage.conf().set_module_split(mod->name(), false);
- _patchage.refresh();
- }
+ _conf.set_module_split(module_name(action.client), false);
+ _drivers.refresh();
+}
+
+void
+Reactor::operator()(const action::ZoomFull&)
+{
+ _canvas.zoom_full();
+ _conf.set<setting::Zoom>(_canvas.get_zoom());
+}
+
+void
+Reactor::operator()(const action::ZoomIn&)
+{
+ _conf.set<setting::Zoom>(_conf.get<setting::Zoom>() * 1.25);
+}
+
+void
+Reactor::operator()(const action::ZoomNormal&)
+{
+ _conf.set<setting::Zoom>(1.0);
+}
+
+void
+Reactor::operator()(const action::ZoomOut&)
+{
+ _conf.set<setting::Zoom>(_conf.get<setting::Zoom>() * 0.75);
}
void
@@ -125,16 +169,32 @@ Reactor::operator()(const Action& action)
boost::apply_visitor(*this, action);
}
+std::string
+Reactor::module_name(const ClientID& client)
+{
+ // Note that split modules always have the same name
+
+ if (CanvasModule* mod = find_module(client, SignalDirection::input)) {
+ return mod->name();
+ }
+
+ if (CanvasModule* mod = find_module(client, SignalDirection::output)) {
+ return mod->name();
+ }
+
+ return std::string{};
+}
+
CanvasModule*
Reactor::find_module(const ClientID& client, const SignalDirection type)
{
- return _patchage.canvas()->find_module(client, type);
+ return _canvas.find_module(client, type);
}
CanvasPort*
Reactor::find_port(const PortID& port)
{
- return _patchage.canvas()->find_port(port);
+ return _canvas.find_port(port);
}
} // namespace patchage
diff --git a/src/Reactor.hpp b/src/Reactor.hpp
index 9317b0a..17c20ce 100644
--- a/src/Reactor.hpp
+++ b/src/Reactor.hpp
@@ -20,16 +20,19 @@
#include "Action.hpp"
#include "SignalDirection.hpp"
+#include <string>
+
namespace patchage {
struct PortID;
+class Canvas;
class CanvasModule;
class CanvasPort;
class ClientID;
+class Configuration;
class Drivers;
class ILog;
-class Patchage;
/// Reacts to actions from the user
class Reactor
@@ -37,7 +40,10 @@ class Reactor
public:
using result_type = void; ///< For boost::apply_visitor
- explicit Reactor(Patchage& patchage);
+ explicit Reactor(Configuration& conf,
+ Drivers& drivers,
+ Canvas& canvas,
+ ILog& log);
Reactor(const Reactor&) = delete;
Reactor& operator=(const Reactor&) = delete;
@@ -48,22 +54,33 @@ public:
~Reactor() = default;
void operator()(const action::ConnectPorts& action);
+ void operator()(const action::DecreaseFontSize& action);
void operator()(const action::DisconnectClient& action);
void operator()(const action::DisconnectPort& action);
void operator()(const action::DisconnectPorts& action);
+ void operator()(const action::IncreaseFontSize& action);
void operator()(const action::MoveModule& action);
+ void operator()(const action::Refresh& action);
+ void operator()(const action::ResetFontSize& action);
void operator()(const action::SplitModule& action);
void operator()(const action::UnsplitModule& action);
+ void operator()(const action::ZoomFull& action);
+ void operator()(const action::ZoomIn& action);
+ void operator()(const action::ZoomNormal& action);
+ void operator()(const action::ZoomOut& action);
void operator()(const Action& action);
private:
+ std::string module_name(const ClientID& client);
+
CanvasModule* find_module(const ClientID& client, SignalDirection type);
CanvasPort* find_port(const PortID& port);
- Patchage& _patchage;
- ILog& _log;
- Drivers& _drivers;
+ Configuration& _conf;
+ Drivers& _drivers;
+ Canvas& _canvas;
+ ILog& _log;
};
} // namespace patchage
diff --git a/src/handle_event.cpp b/src/handle_event.cpp
index 65f4705..fa543e6 100644
--- a/src/handle_event.cpp
+++ b/src/handle_event.cpp
@@ -18,11 +18,13 @@
#include "Canvas.hpp"
#include "CanvasPort.hpp"
+#include "ClientType.hpp"
+#include "Configuration.hpp"
#include "Event.hpp"
#include "ILog.hpp"
#include "Metadata.hpp"
-#include "Patchage.hpp"
#include "PortID.hpp"
+#include "Setting.hpp"
#include "warnings.hpp"
PATCHAGE_DISABLE_FMT_WARNINGS
@@ -33,7 +35,6 @@ PATCHAGE_RESTORE_WARNINGS
#include <boost/variant/apply_visitor.hpp>
#include <iosfwd>
-#include <memory>
namespace patchage {
@@ -44,95 +45,122 @@ class EventHandler
public:
using result_type = void; ///< For boost::apply_visitor
- explicit EventHandler(Patchage& patchage)
- : _patchage{patchage}
+ explicit EventHandler(Configuration& conf,
+ Metadata& metadata,
+ Canvas& canvas,
+ ILog& log)
+ : _conf{conf}
+ , _metadata{metadata}
+ , _canvas{canvas}
+ , _log{log}
{}
- void operator()(const event::Cleared&) { _patchage.canvas()->clear(); }
+ void operator()(const event::Cleared&) { _canvas.clear(); }
void operator()(const event::DriverAttached& event)
{
- _patchage.driver_attached(event.type);
+ switch (event.type) {
+ case ClientType::alsa:
+ _conf.set<setting::AlsaAttached>(true);
+ break;
+ case ClientType::jack:
+ _conf.set<setting::JackAttached>(true);
+ break;
+ }
}
void operator()(const event::DriverDetached& event)
{
- _patchage.driver_detached(event.type);
+ switch (event.type) {
+ case ClientType::alsa:
+ _conf.set<setting::AlsaAttached>(false);
+ break;
+ case ClientType::jack:
+ _conf.set<setting::JackAttached>(false);
+ break;
+ }
}
void operator()(const event::ClientCreated& event)
{
// Don't create empty modules, they will be created when ports are added
- _patchage.metadata().set_client(event.id, event.info);
+ _metadata.set_client(event.id, event.info);
}
void operator()(const event::ClientDestroyed& event)
{
- _patchage.canvas()->remove_module(event.id);
- _patchage.metadata().erase_client(event.id);
+ _canvas.remove_module(event.id);
+ _metadata.erase_client(event.id);
}
void operator()(const event::PortCreated& event)
{
- _patchage.metadata().set_port(event.id, event.info);
+ _metadata.set_port(event.id, event.info);
auto* const port =
- _patchage.canvas()->create_port(_patchage, event.id, event.info);
+ _canvas.create_port(_conf, _metadata, event.id, event.info);
if (!port) {
- _patchage.log().error(
+ _log.error(
fmt::format("Unable to create view for port \"{}\"", event.id));
}
}
void operator()(const event::PortDestroyed& event)
{
- _patchage.canvas()->remove_port(event.id);
- _patchage.metadata().erase_port(event.id);
+ _canvas.remove_port(event.id);
+ _metadata.erase_port(event.id);
}
void operator()(const event::PortsConnected& event)
{
- CanvasPort* port_1 = _patchage.canvas()->find_port(event.tail);
- CanvasPort* port_2 = _patchage.canvas()->find_port(event.head);
+ CanvasPort* port_1 = _canvas.find_port(event.tail);
+ CanvasPort* port_2 = _canvas.find_port(event.head);
if (!port_1) {
- _patchage.log().error(
+ _log.error(
fmt::format("Unable to find port \"{}\" to connect", event.tail));
} else if (!port_2) {
- _patchage.log().error(
+ _log.error(
fmt::format("Unable to find port \"{}\" to connect", event.head));
} else {
- _patchage.canvas()->make_connection(port_1, port_2);
+ _canvas.make_connection(port_1, port_2);
}
}
void operator()(const event::PortsDisconnected& event)
{
- CanvasPort* port_1 = _patchage.canvas()->find_port(event.tail);
- CanvasPort* port_2 = _patchage.canvas()->find_port(event.head);
+ CanvasPort* port_1 = _canvas.find_port(event.tail);
+ CanvasPort* port_2 = _canvas.find_port(event.head);
if (!port_1) {
- _patchage.log().error(
+ _log.error(
fmt::format("Unable to find port \"{}\" to disconnect", event.tail));
} else if (!port_2) {
- _patchage.log().error(
+ _log.error(
fmt::format("Unable to find port \"{}\" to disconnect", event.head));
} else {
- _patchage.canvas()->remove_edge_between(port_1, port_2);
+ _canvas.remove_edge_between(port_1, port_2);
}
}
private:
- Patchage& _patchage;
+ Configuration& _conf;
+ Metadata& _metadata;
+ Canvas& _canvas;
+ ILog& _log;
};
} // namespace
void
-handle_event(Patchage& patchage, const Event& event)
+handle_event(Configuration& conf,
+ Metadata& metadata,
+ Canvas& canvas,
+ ILog& log,
+ const Event& event)
{
- EventHandler handler{patchage};
+ EventHandler handler{conf, metadata, canvas, log};
boost::apply_visitor(handler, event);
}
diff --git a/src/handle_event.hpp b/src/handle_event.hpp
index 6a62d61..5cf15ef 100644
--- a/src/handle_event.hpp
+++ b/src/handle_event.hpp
@@ -21,11 +21,18 @@
namespace patchage {
-class Patchage;
+class Configuration;
+class Metadata;
+class Canvas;
+class ILog;
/// Handle an event from the system by updating the GUI as necessary
void
-handle_event(Patchage& patchage, const Event& event);
+handle_event(Configuration& conf,
+ Metadata& metadata,
+ Canvas& canvas,
+ ILog& log,
+ const Event& event);
} // namespace patchage