From df0fdf362347495531bc3a5d19233220721d846b Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 11 May 2021 13:45:26 -0400 Subject: Refactor most functionality around actions and settings This moves more code into general places, and completely eliminates dependencies on the Patchage "god object". --- src/Action.hpp | 31 +++- src/Canvas.cpp | 28 ++-- src/Canvas.hpp | 10 +- src/Configuration.cpp | 4 +- src/Configuration.hpp | 7 +- src/Metadata.cpp | 4 +- src/Metadata.hpp | 4 +- src/Patchage.cpp | 450 ++++++++++++++++++++++++++------------------------ src/Patchage.hpp | 45 +++-- src/Reactor.cpp | 104 +++++++++--- src/Reactor.hpp | 27 ++- src/handle_event.cpp | 84 ++++++---- src/handle_event.hpp | 11 +- 13 files changed, 490 insertions(+), 319 deletions(-) (limited to 'src') 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 @@ -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::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(), 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 #include #include +#include #include 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 on_change) + : _on_change(std::move(on_change)) { std::get(_settings).value = Coord{0.0, 0.0}; std::get(_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 #include +#include #include #include #include @@ -36,7 +37,7 @@ namespace patchage { class Configuration { public: - explicit Configuration(); + explicit Configuration(std::function on_change); void load(); void save(); @@ -60,6 +61,7 @@ public: void set_port_color(PortType type, uint32_t rgba) { _port_colors[static_cast(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 _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 -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 -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 client(const ClientID& id); - boost::optional port(const PortID& id); + boost::optional client(const ClientID& id) const; + boost::optional 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 +#include +#include #include #include #include @@ -96,6 +98,7 @@ PATCHAGE_RESTORE_WARNINGS #include #include #include +#include #include #include @@ -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{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(_canvas->get_default_font_size()); - _conf.load(); - _canvas->set_zoom(_conf.get()); - _canvas->set_font_size(_conf.get()); - if (_conf.get()) { - _canvas->set_port_order(port_order, nullptr); - } - _main_win->resize(static_cast(_conf.get().x), - static_cast(_conf.get().y)); - - _main_win->move(static_cast(_conf.get().x), - static_cast(_conf.get().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()); - _menu_view_sprung_layout->set_active(_conf.get()); - _menu_view_sort_ports->set_active(_conf.get()); - 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(); } @@ -465,83 +464,6 @@ Patchage::update_load() return true; } -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() { @@ -569,47 +491,58 @@ Patchage::clear_load() } void -Patchage::on_driver_event(const Event& event) +Patchage::operator()(const setting::AlsaAttached& setting) { - std::lock_guard 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 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(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(sort_ports); - refresh(); -} - -void -Patchage::on_zoom_in() -{ - const float zoom = _canvas->get_zoom() * 1.25; - _canvas->set_zoom(zoom); - _conf.set(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(zoom); -} + bool human_names = setting.value; -void -Patchage::on_zoom_normal() -{ - _canvas->set_zoom(1.0); - _conf.set(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(_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(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(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(_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(setting.value.x); + const int new_y = static_cast(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(setting.value.x); + const int new_h = static_cast(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 lock{_events_mutex}; + + _driver_events.emplace(event); +} + +void +Patchage::process_events() +{ + std::lock_guard 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(_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(_menu_view_human_names->get_active()); +} + +void +Patchage::on_view_sort_ports() +{ + _conf.set(_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(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 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(); - _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(true); - } else { - _log_scrolledwindow->hide(); - _conf.set(false); - } + _conf.set(_menu_view_messages->get_active()); } void Patchage::on_view_toolbar() { - if (_menu_view_toolbar->get_active()) { - _toolbar->show(); - } else { - _toolbar->hide(); - } - _conf.set(_menu_view_toolbar->get_active()); } @@ -878,6 +890,12 @@ Patchage::on_scroll(GdkEventScroll*) return false; } +void +Patchage::on_menu_action(const Action& action) +{ + _reactor(action); +} + void Patchage::buffer_size_changed() { 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 #include +#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() 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 _xml; + std::unique_ptr _canvas; std::mutex _events_mutex; std::queue _driver_events; - std::unique_ptr _canvas; - - Configuration _conf; + Configuration _conf; BufferSizeColumns _buf_size_columns; @@ -209,7 +209,6 @@ protected: Glib::RefPtr _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 -#include - 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 @@ -59,6 +61,12 @@ Reactor::operator()(const action::ConnectPorts& action) } } +void +Reactor::operator()(const action::DecreaseFontSize&) +{ + _conf.set(_conf.get() - 1.0); +} + void Reactor::operator()(const action::DisconnectClient& action) { @@ -91,32 +99,68 @@ Reactor::operator()(const action::DisconnectPorts& action) } } +void +Reactor::operator()(const action::IncreaseFontSize&) +{ + _conf.set(_conf.get() + 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(_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(_canvas.get_zoom()); +} + +void +Reactor::operator()(const action::ZoomIn&) +{ + _conf.set(_conf.get() * 1.25); +} + +void +Reactor::operator()(const action::ZoomNormal&) +{ + _conf.set(1.0); +} + +void +Reactor::operator()(const action::ZoomOut&) +{ + _conf.set(_conf.get() * 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 + 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 #include -#include 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(true); + break; + case ClientType::jack: + _conf.set(true); + break; + } } void operator()(const event::DriverDetached& event) { - _patchage.driver_detached(event.type); + switch (event.type) { + case ClientType::alsa: + _conf.set(false); + break; + case ClientType::jack: + _conf.set(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 -- cgit v1.2.1