From aab787f686e6287a06ba94ed8c87b524c4690f0f Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 22 Dec 2017 20:20:59 +0100 Subject: [PATCH] Separate and consolidate Transport-Control-UI code --- gtk2_ardour/ardour_ui.cc | 196 +---------------- gtk2_ardour/ardour_ui.h | 48 +--- gtk2_ardour/ardour_ui2.cc | 108 +-------- gtk2_ardour/ardour_ui_dialogs.cc | 8 +- gtk2_ardour/ardour_ui_options.cc | 13 -- gtk2_ardour/transport_control.cc | 70 ++++++ gtk2_ardour/transport_control.h | 65 ++++++ gtk2_ardour/transport_control_ui.cc | 330 ++++++++++++++++++++++++++++ gtk2_ardour/transport_control_ui.h | 72 ++++++ gtk2_ardour/wscript | 2 + 10 files changed, 552 insertions(+), 360 deletions(-) create mode 100644 gtk2_ardour/transport_control.cc create mode 100644 gtk2_ardour/transport_control.h create mode 100644 gtk2_ardour/transport_control_ui.cc create mode 100644 gtk2_ardour/transport_control_ui.h diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 251bd31431..2aba7ffb64 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -285,13 +285,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) , _initial_verbose_plugin_scan (false) , first_time_engine_run (true) , secondary_clock_spacer (0) - , roll_controllable (new TransportControllable ("transport roll", *this, TransportControllable::Roll)) - , stop_controllable (new TransportControllable ("transport stop", *this, TransportControllable::Stop)) - , goto_start_controllable (new TransportControllable ("transport goto start", *this, TransportControllable::GotoStart)) - , goto_end_controllable (new TransportControllable ("transport goto end", *this, TransportControllable::GotoEnd)) - , auto_loop_controllable (new TransportControllable ("transport auto loop", *this, TransportControllable::AutoLoop)) - , play_selection_controllable (new TransportControllable ("transport play selection", *this, TransportControllable::PlaySelection)) - , rec_controllable (new TransportControllable ("transport rec-enable", *this, TransportControllable::RecordEnable)) , auto_input_button (ArdourButton::led_default_elements) , time_info_box (0) , auto_return_button (ArdourButton::led_default_elements) @@ -380,22 +373,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) boost::function pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1)); UIConfiguration::instance().map_parameters (pc); - roll_button.set_controllable (roll_controllable); - stop_button.set_controllable (stop_controllable); - goto_start_button.set_controllable (goto_start_controllable); - goto_end_button.set_controllable (goto_end_controllable); - auto_loop_button.set_controllable (auto_loop_controllable); - play_selection_button.set_controllable (play_selection_controllable); - rec_button.set_controllable (rec_controllable); - - roll_button.set_name ("transport button"); - stop_button.set_name ("transport button"); - goto_start_button.set_name ("transport button"); - goto_end_button.set_name ("transport button"); - auto_loop_button.set_name ("transport button"); - play_selection_button.set_name ("transport button"); - rec_button.set_name ("transport recenable button"); - midi_panic_button.set_name ("transport button"); + transport_ctrl.setup (this); ARDOUR::DiskWriter::Overrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context()); ARDOUR::DiskReader::Underrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context()); @@ -680,6 +658,8 @@ ARDOUR_UI::post_engine () throw failed_constructor (); } + transport_ctrl.map_actions (); + /* Do this after setup_windows (), as that's when the _status_bar_visibility is created */ XMLNode* n = Config->extra_xml (X_("UI")); if (n) { @@ -2620,10 +2600,6 @@ void ARDOUR_UI::map_transport_state () { if (!_session) { - auto_loop_button.unset_active_state (); - play_selection_button.unset_active_state (); - roll_button.unset_active_state (); - stop_button.set_active_state (Gtkmm2ext::ExplicitActive); layered_button.set_sensitive (false); return; } @@ -2633,52 +2609,9 @@ ARDOUR_UI::map_transport_state () float sp = _session->transport_speed(); if (sp != 0.0f) { - - /* we're rolling */ - - if (_session->get_play_range()) { - - play_selection_button.set_active_state (Gtkmm2ext::ExplicitActive); - roll_button.unset_active_state (); - auto_loop_button.unset_active_state (); - - } else if (_session->get_play_loop ()) { - - auto_loop_button.set_active (true); - play_selection_button.set_active (false); - if (Config->get_loop_is_mode()) { - roll_button.set_active (true); - } else { - roll_button.set_active (false); - } - - } else { - - roll_button.set_active (true); - play_selection_button.set_active (false); - auto_loop_button.set_active (false); - } - - if (UIConfiguration::instance().get_follow_edits() && !_session->config.get_external_sync()) { - /* light up both roll and play-selection if they are joined */ - roll_button.set_active (true); - play_selection_button.set_active (true); - } layered_button.set_sensitive (!_session->actively_recording ()); - - stop_button.set_active (false); - } else { - layered_button.set_sensitive (true); - stop_button.set_active (true); - roll_button.set_active (false); - play_selection_button.set_active (false); - if (Config->get_loop_is_mode ()) { - auto_loop_button.set_active (_session->get_play_loop()); - } else { - auto_loop_button.set_active (false); - } update_disk_space (); } } @@ -2686,7 +2619,6 @@ ARDOUR_UI::map_transport_state () void ARDOUR_UI::blink_handler (bool blink_on) { - transport_rec_enable_blink (blink_on); sync_blink (blink_on); if (!UIConfiguration::instance().get_blink_alert_indicators()) { @@ -3130,34 +3062,6 @@ ARDOUR_UI::secondary_clock_value_changed () _session->request_locate (secondary_clock->current_time ()); } } - -void -ARDOUR_UI::transport_rec_enable_blink (bool onoff) -{ - if (_session == 0) { - return; - } - - if (_session->step_editing()) { - return; - } - - Session::RecordState const r = _session->record_status (); - bool const h = _session->have_rec_enabled_track (); - - if (r == Session::Enabled || (r == Session::Recording && !h)) { - if (onoff) { - rec_button.set_active_state (Gtkmm2ext::ExplicitActive); - } else { - rec_button.set_active_state (Gtkmm2ext::Off); - } - } else if (r == Session::Recording && h) { - rec_button.set_active_state (Gtkmm2ext::ExplicitActive); - } else { - rec_button.unset_active_state (); - } -} - void ARDOUR_UI::save_template_dialog_response (int response, SaveTemplateDialog* d) { @@ -5322,20 +5226,6 @@ ARDOUR_UI::update_transport_clocks (samplepos_t pos) ARDOUR_UI::instance()->video_timeline->manual_seek_video_monitor(pos); } -void -ARDOUR_UI::step_edit_status_change (bool yn) -{ - // XXX should really store pre-step edit status of things - // we make insensitive - - if (yn) { - rec_button.set_active_state (Gtkmm2ext::ImplicitActive); - rec_button.set_sensitive (false); - } else { - rec_button.unset_active_state ();; - rec_button.set_sensitive (true); - } -} void ARDOUR_UI::record_state_changed () @@ -5402,86 +5292,6 @@ ARDOUR_UI::store_clock_modes () _session->set_dirty (); } -ARDOUR_UI::TransportControllable::TransportControllable (std::string name, ARDOUR_UI& u, ToggleType tp) - : Controllable (name), ui (u), type(tp) -{ - -} - -void -ARDOUR_UI::TransportControllable::set_value (double val, PBD::Controllable::GroupControlDisposition /*group_override*/) -{ - if (val < 0.5) { - /* do nothing: these are radio-style actions */ - return; - } - - const char *action = 0; - - switch (type) { - case Roll: - action = X_("Roll"); - break; - case Stop: - action = X_("Stop"); - break; - case GotoStart: - action = X_("GotoStart"); - break; - case GotoEnd: - action = X_("GotoEnd"); - break; - case AutoLoop: - action = X_("Loop"); - break; - case PlaySelection: - action = X_("PlaySelection"); - break; - case RecordEnable: - action = X_("Record"); - break; - default: - break; - } - - if (action == 0) { - return; - } - - Glib::RefPtr act = ActionManager::get_action ("Transport", action); - - if (act) { - act->activate (); - } -} - -double -ARDOUR_UI::TransportControllable::get_value (void) const -{ - float val = 0.0; - - switch (type) { - case Roll: - break; - case Stop: - break; - case GotoStart: - break; - case GotoEnd: - break; - case AutoLoop: - break; - case PlaySelection: - break; - case RecordEnable: - break; - default: - break; - } - - return val; -} - void ARDOUR_UI::setup_profile () { diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 5413b5fde1..f714e03134 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -37,7 +37,6 @@ #include "pbd/xml++.h" -#include "pbd/controllable.h" #include #include #include @@ -79,6 +78,8 @@ #include "enums.h" #include "mini_timeline.h" #include "shuttle_control.h" +#include "transport_control.h" +#include "transport_control_ui.h" #include "visibility_group.h" #include "window_manager.h" @@ -158,7 +159,7 @@ namespace ArdourWidgets { class Tabbable; } -class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr +class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr, public TransportControlProvider { public: ARDOUR_UI (int *argcp, char **argvp[], const char* localedir); @@ -372,6 +373,7 @@ protected: void toggle_session_options_window (); private: + Gtk::Window _main_window; Gtkmm2ext::VisibilityTracker* main_window_visibility; Gtk::VBox main_vpacker; @@ -451,46 +453,13 @@ private: ArdourWidgets::ArdourVSpacer* secondary_clock_spacer; void repack_transport_hbox (); void update_clock_visibility (); - - struct TransportControllable : public PBD::Controllable { - enum ToggleType { - Roll = 0, - Stop, - RecordEnable, - GotoStart, - GotoEnd, - AutoLoop, - PlaySelection, - }; - - TransportControllable (std::string name, ARDOUR_UI&, ToggleType); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - double get_value (void) const; - - ARDOUR_UI& ui; - ToggleType type; - }; - - boost::shared_ptr roll_controllable; - boost::shared_ptr stop_controllable; - boost::shared_ptr goto_start_controllable; - boost::shared_ptr goto_end_controllable; - boost::shared_ptr auto_loop_controllable; - boost::shared_ptr play_selection_controllable; - boost::shared_ptr rec_controllable; - void toggle_follow_edits (); void set_transport_controllable_state (const XMLNode&); XMLNode& get_transport_controllable_state (); - ArdourWidgets::ArdourButton roll_button; - ArdourWidgets::ArdourButton stop_button; - ArdourWidgets::ArdourButton goto_start_button; - ArdourWidgets::ArdourButton goto_end_button; - ArdourWidgets::ArdourButton auto_loop_button; - ArdourWidgets::ArdourButton play_selection_button; - ArdourWidgets::ArdourButton rec_button; + TransportControlUI transport_ctrl; + ArdourWidgets::ArdourButton punch_in_button; ArdourWidgets::ArdourButton punch_out_button; ArdourWidgets::ArdourButton layered_button; @@ -518,7 +487,6 @@ private: ArdourWidgets::ArdourButton auto_return_button; ArdourWidgets::ArdourButton follow_edits_button; - ArdourWidgets::ArdourButton click_button; ArdourWidgets::ArdourButton sync_button; ArdourWidgets::ArdourButton auditioning_alert_button; @@ -632,7 +600,6 @@ private: void edit_metadata (); void import_metadata (); - void set_loop_sensitivity (); void set_transport_sensitivity (bool); //stuff for ProTools-style numpad @@ -818,8 +785,6 @@ private: PBD::ScopedConnection halt_connection; PBD::ScopedConnection editor_meter_connection; - void step_edit_status_change (bool); - /* these are used only in response to a platform-specific "ShouldQuit" signal */ bool idle_finish (); void queue_finish (); @@ -829,7 +794,6 @@ private: int ambiguous_file (std::string file, std::vector hits); bool click_button_clicked (GdkEventButton *); - bool click_button_scroll (GdkEventScroll *); bool sync_button_clicked (GdkEventButton *); VisibilityGroup _status_bar_visibility; diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index ea94389b28..748cd2abd0 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -74,14 +74,6 @@ ARDOUR_UI::setup_tooltips () { ArdourCanvas::Canvas::set_tooltip_timeout (Gtk::Settings::get_default()->property_gtk_tooltip_timeout ()); - set_tip (roll_button, _("Play from playhead")); - set_tip (stop_button, _("Stop playback")); - set_tip (rec_button, _("Toggle record")); - set_tip (play_selection_button, _("Play range/selection")); - set_tip (goto_start_button, _("Go to start of session")); - set_tip (goto_end_button, _("Go to end of session")); - set_tip (auto_loop_button, _("Play loop range")); - set_tip (midi_panic_button, _("MIDI Panic\nSend note off and reset controller messages on all MIDI channels")); set_tip (auto_return_button, _("Return to last playback start when stopped")); set_tip (follow_edits_button, _("Playhead follows Range tool clicks, and Range selections")); set_tip (auto_input_button, _("Track Input Monitoring automatically follows transport state")); @@ -273,29 +265,7 @@ ARDOUR_UI::setup_transport () RefPtr act; /* setup actions */ - act = ActionManager::get_action ("Transport", "ToggleClick"); - click_button.set_related_action (act); - click_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::click_button_clicked), false); - click_button.signal_scroll_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::click_button_scroll), false); - - act = ActionManager::get_action (X_("Transport"), X_("Stop")); - stop_button.set_related_action (act); - act = ActionManager::get_action (X_("Transport"), X_("Roll")); - roll_button.set_related_action (act); - act = ActionManager::get_action (X_("Transport"), X_("Record")); - rec_button.set_related_action (act); - act = ActionManager::get_action (X_("Transport"), X_("GotoStart")); - goto_start_button.set_related_action (act); - act = ActionManager::get_action (X_("Transport"), X_("GotoEnd")); - goto_end_button.set_related_action (act); - act = ActionManager::get_action (X_("Transport"), X_("Loop")); - auto_loop_button.set_related_action (act); - act = ActionManager::get_action (X_("Transport"), X_("PlaySelection")); - play_selection_button.set_related_action (act); - act = ActionManager::get_action (X_("MIDI"), X_("panic")); - midi_panic_button.set_related_action (act); act = ActionManager::get_action (X_("Transport"), X_("ToggleExternalSync")); - sync_button.set_related_action (act); sync_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::sync_button_clicked), false); @@ -381,7 +351,6 @@ ARDOUR_UI::setup_transport () monitor_disk_button.set_name ("monitor button"); auto_input_button.set_name ("transport option button"); - click_button.set_name ("transport button"); sync_button.set_name ("transport active option button"); /* and widget text */ @@ -418,29 +387,8 @@ ARDOUR_UI::setup_transport () Gtkmm2ext::UI::instance()->set_tip (monitor_in_button, _("Force all tracks to monitor Input, unless they are explicitly set to monitor Disk")); Gtkmm2ext::UI::instance()->set_tip (monitor_disk_button, _("Force all tracks to monitor Disk playback, unless they are explicitly set to Input")); - /* setup icons */ - - click_button.set_icon (ArdourIcon::TransportMetronom); - goto_start_button.set_icon (ArdourIcon::TransportStart); - goto_end_button.set_icon (ArdourIcon::TransportEnd); - roll_button.set_icon (ArdourIcon::TransportPlay); - stop_button.set_icon (ArdourIcon::TransportStop); - play_selection_button.set_icon (ArdourIcon::TransportRange); - auto_loop_button.set_icon (ArdourIcon::TransportLoop); - rec_button.set_icon (ArdourIcon::RecButton); - midi_panic_button.set_icon (ArdourIcon::TransportPanic); - /* transport control size-group */ - Glib::RefPtr transport_button_size_group = SizeGroup::create (SIZE_GROUP_BOTH); - transport_button_size_group->add_widget (goto_start_button); - transport_button_size_group->add_widget (goto_end_button); - transport_button_size_group->add_widget (auto_loop_button); - transport_button_size_group->add_widget (rec_button); - transport_button_size_group->add_widget (play_selection_button); - transport_button_size_group->add_widget (roll_button); - transport_button_size_group->add_widget (stop_button); - Glib::RefPtr punch_button_size_group = SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL); punch_button_size_group->add_widget (punch_in_button); punch_button_size_group->add_widget (punch_out_button); @@ -471,23 +419,6 @@ ARDOUR_UI::setup_transport () transport_frame.add (*ebox); ebox->add (transport_table); - /* transport controls sub-group */ - click_button.set_size_request (PX_SCALE(20), PX_SCALE(20)); - - HBox* tbox = manage (new HBox); - tbox->set_spacing (PX_SCALE(2)); - - tbox->pack_start (midi_panic_button, true, true, 0); - tbox->pack_start (click_button, true, true, 0); - tbox->pack_start (goto_start_button, true, true); - tbox->pack_start (goto_end_button, true, true); - tbox->pack_start (auto_loop_button, true, true); - tbox->pack_start (play_selection_button, true, true); - - tbox->pack_start (roll_button, true, true); - tbox->pack_start (stop_button, true, true); - tbox->pack_start (rec_button, true, true, 3); - /* alert box sub-group */ VBox* alert_box = manage (new VBox); alert_box->set_homogeneous (true); @@ -505,7 +436,7 @@ ARDOUR_UI::setup_transport () button_height_size_group->add_widget (*secondary_clock->left_btn()); button_height_size_group->add_widget (*secondary_clock->right_btn()); - button_height_size_group->add_widget (stop_button); + button_height_size_group->add_widget (transport_ctrl.size_button ()); // button_height_size_group->add_widget (sync_button); button_height_size_group->add_widget (auto_return_button); @@ -545,7 +476,7 @@ ARDOUR_UI::setup_transport () int col = 0; #define TCOL col, col + 1 - transport_table.attach (*tbox, TCOL, 0, 1 , SHRINK, SHRINK, 0, 0); + transport_table.attach (transport_ctrl, TCOL, 0, 1 , SHRINK, SHRINK, 0, 0); transport_table.attach (*ssbox, TCOL, 1, 2 , FILL, SHRINK, 0, 0); ++col; @@ -631,7 +562,6 @@ ARDOUR_UI::setup_transport () auditioning_alert_button.set_sensitive (false); auditioning_alert_button.set_visual_state (Gtkmm2ext::NoVisualState); - stop_button.set_active (true); set_transport_sensitivity (false); } #undef PX_SCALE @@ -794,17 +724,6 @@ ARDOUR_UI::error_blink (bool onoff) break; } } - -void -ARDOUR_UI::set_loop_sensitivity () -{ - if (!_session || _session->config.get_external_sync()) { - auto_loop_button.set_sensitive (false); - } else { - auto_loop_button.set_sensitive (_session && _session->locations()->auto_loop_location()); - } -} - void ARDOUR_UI::set_transport_sensitivity (bool yn) { @@ -859,29 +778,6 @@ ARDOUR_UI::click_button_clicked (GdkEventButton* ev) return true; } -bool -ARDOUR_UI::click_button_scroll (GdkEventScroll* ev) -{ - gain_t gain = Config->get_click_gain(); - float gain_db = accurate_coefficient_to_dB (gain); - - switch (ev->direction) { - case GDK_SCROLL_UP: - case GDK_SCROLL_LEFT: - gain_db += 1; - break; - case GDK_SCROLL_DOWN: - case GDK_SCROLL_RIGHT: - gain_db -= 1; - break; - } - gain_db = std::max (-60.f, gain_db); - gain = dB_to_coefficient (gain_db); - gain = std::min (gain, Config->get_max_gain()); - Config->set_click_gain (gain); - return true; -} - bool ARDOUR_UI::sync_button_clicked (GdkEventButton* ev) { diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index 8f1d080009..7592c34dcc 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -86,6 +86,8 @@ ARDOUR_UI::set_session (Session *s) { SessionHandlePtr::set_session (s); + transport_ctrl.set_session (s); + if (!_session) { WM::Manager::instance().set_session (s); /* Session option editor cannot exist across change-of-session */ @@ -158,8 +160,6 @@ ARDOUR_UI::set_session (Session *s) ActionManager::set_sensitive (ActionManager::point_selection_sensitive_actions, false); ActionManager::set_sensitive (ActionManager::playlist_selection_sensitive_actions, false); - rec_button.set_sensitive (true); - solo_alert_button.set_active (_session->soloing()); setup_session_options (); @@ -169,7 +169,6 @@ ARDOUR_UI::set_session (Session *s) _session->SaveSessionRequested.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::save_session_at_its_request, this, _1), gui_context()); _session->StateSaved.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_title, this), gui_context()); _session->RecordStateChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::record_state_changed, this), gui_context()); - _session->StepEditStatusChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::step_edit_status_change, this, _1), gui_context()); _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::map_transport_state, this), gui_context()); _session->DirtyChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_dirty_changed, this), gui_context()); @@ -179,7 +178,6 @@ ARDOUR_UI::set_session (Session *s) _session->locations()->added.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context()); _session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context()); _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ()); - _session->auto_loop_location_changed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::set_loop_sensitivity, this), gui_context ()); /* Clocks are on by default after we are connected to a session, so show that here. */ @@ -314,8 +312,6 @@ ARDOUR_UI::unload_session (bool hide_stuff) ActionManager::set_sensitive (ActionManager::session_sensitive_actions, false); - rec_button.set_sensitive (false); - WM::Manager::instance().set_session ((ARDOUR::Session*) 0); if (ARDOUR_UI::instance()->video_timeline) { diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index d5fac401b3..ca18c5f555 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -369,7 +369,6 @@ ARDOUR_UI::parameter_changed (std::string p) ActionManager::get_action ("Transport", "ToggleAutoReturn")->set_sensitive (false); ActionManager::get_action ("Transport", "ToggleFollowEdits")->set_sensitive (false); } - set_loop_sensitivity (); } else if (p == "follow-edits") { @@ -426,13 +425,6 @@ ARDOUR_UI::parameter_changed (std::string p) } } else if (p == "clicking") { ActionManager::map_some_state ("Transport", "ToggleClick", &RCConfiguration::get_clicking); - } else if (p == "click-record-only") { - // TODO set a flag, blink or gray-out metronome button while rolling, only - if (Config->get_click_record_only()) { - click_button.set_name ("generic button"); // XXX - } else { - click_button.set_name ("transport button"); - } } else if (p == "use-video-sync") { ActionManager::map_some_state ("Transport", "ToggleVideoSync", sigc::mem_fun (_session->config, &SessionConfiguration::get_use_video_sync)); } else if (p == "sync-source") { @@ -521,11 +513,6 @@ ARDOUR_UI::parameter_changed (std::string p) /* force a redraw */ gtk_rc_reset_styles (gtk_settings_get_default()); } - } else if (p == "click-gain") { - float gain_db = accurate_coefficient_to_dB (Config->get_click_gain()); - char tmp[32]; - snprintf(tmp, 31, "%+.1f", gain_db); - set_tip (click_button, string_compose (_("Enable/Disable metronome\n\nRight-click to access preferences\nMouse-wheel to modify level\nSignal Level: %1 dBFS"), tmp)); } } diff --git a/gtk2_ardour/transport_control.cc b/gtk2_ardour/transport_control.cc new file mode 100644 index 0000000000..2f08b4aac0 --- /dev/null +++ b/gtk2_ardour/transport_control.cc @@ -0,0 +1,70 @@ + +#include "actions.h" +#include "transport_control.h" + +#include "pbd/i18n.h" + +using namespace Gtk; + +TransportControlProvider::TransportControlProvider () + : roll_controllable (new TransportControllable ("transport roll", TransportControllable::Roll)) + , stop_controllable (new TransportControllable ("transport stop", TransportControllable::Stop)) + , goto_start_controllable (new TransportControllable ("transport goto start", TransportControllable::GotoStart)) + , goto_end_controllable (new TransportControllable ("transport goto end", TransportControllable::GotoEnd)) + , auto_loop_controllable (new TransportControllable ("transport auto loop", TransportControllable::AutoLoop)) + , play_selection_controllable (new TransportControllable ("transport play selection", TransportControllable::PlaySelection)) + , rec_controllable (new TransportControllable ("transport rec-enable", TransportControllable::RecordEnable)) +{ +} + +TransportControlProvider::TransportControllable::TransportControllable (std::string name, ToggleType tp) + : Controllable (name), type(tp) +{ +} + +void +TransportControlProvider::TransportControllable::set_value (double val, PBD::Controllable::GroupControlDisposition /*group_override*/) +{ + if (val < 0.5) { + /* do nothing: these are radio-style actions */ + return; + } + + const char *action = 0; + + switch (type) { + case Roll: + action = X_("Roll"); + break; + case Stop: + action = X_("Stop"); + break; + case GotoStart: + action = X_("GotoStart"); + break; + case GotoEnd: + action = X_("GotoEnd"); + break; + case AutoLoop: + action = X_("Loop"); + break; + case PlaySelection: + action = X_("PlaySelection"); + break; + case RecordEnable: + action = X_("Record"); + break; + default: + break; + } + + if (action == 0) { + return; + } + + Glib::RefPtr act = ActionManager::get_action ("Transport", action); + + if (act) { + act->activate (); + } +} diff --git a/gtk2_ardour/transport_control.h b/gtk2_ardour/transport_control.h new file mode 100644 index 0000000000..8e25c15179 --- /dev/null +++ b/gtk2_ardour/transport_control.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 Robin Gareus + * Copyright (C) 1999-2017 Paul Davis + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _gtkardour_transport_control_h_ +#define _gtkardour_transport_control_h_ + +#include +#include "pbd/controllable.h" + +/* This is an API implemenetd by AROUR_UI, + * and made available to transport-control-UIs + */ +class TransportControlProvider +{ +public: + TransportControlProvider (); + virtual ~TransportControlProvider () {} + + /* show metronome preferences */ + virtual bool click_button_clicked (GdkEventButton *) = 0; + + struct TransportControllable : public PBD::Controllable { + enum ToggleType { + Roll = 0, + Stop, + RecordEnable, + GotoStart, + GotoEnd, + AutoLoop, + PlaySelection, + }; + + TransportControllable (std::string name, ToggleType); + void set_value (double, PBD::Controllable::GroupControlDisposition group_override); + double get_value (void) const { return 0; } + + ToggleType type; + }; + + boost::shared_ptr roll_controllable; + boost::shared_ptr stop_controllable; + boost::shared_ptr goto_start_controllable; + boost::shared_ptr goto_end_controllable; + boost::shared_ptr auto_loop_controllable; + boost::shared_ptr play_selection_controllable; + boost::shared_ptr rec_controllable; +}; + +#endif diff --git a/gtk2_ardour/transport_control_ui.cc b/gtk2_ardour/transport_control_ui.cc new file mode 100644 index 0000000000..8102f51b43 --- /dev/null +++ b/gtk2_ardour/transport_control_ui.cc @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2017 Robin Gareus + * Copyright (C) 2013 Paul Davis + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include "ardour/dB.h" +#include "widgets/tooltips.h" +#include "gtkmm2ext/gui_thread.h" + +#include "actions.h" +#include "ardour_ui.h" +#include "timers.h" +#include "transport_control_ui.h" + +#include "pbd/i18n.h" + +using namespace Glib; +using namespace Gtk; +using namespace ARDOUR; +using namespace ArdourWidgets; + +TransportControlUI::TransportControlUI () +{ + Config->ParameterChanged.connect (config_connection, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::parameter_changed, this, _1), gui_context()); +} + +void +TransportControlUI::map_actions () +{ + /* setup actions */ + RefPtr act; + + act = ActionManager::get_action (X_("Transport"), X_("ToggleClick")); + click_button.set_related_action (act); + act = ActionManager::get_action (X_("Transport"), X_("Stop")); + stop_button.set_related_action (act); + act = ActionManager::get_action (X_("Transport"), X_("Roll")); + roll_button.set_related_action (act); + act = ActionManager::get_action (X_("Transport"), X_("Record")); + rec_button.set_related_action (act); + act = ActionManager::get_action (X_("Transport"), X_("GotoStart")); + goto_start_button.set_related_action (act); + act = ActionManager::get_action (X_("Transport"), X_("GotoEnd")); + goto_end_button.set_related_action (act); + act = ActionManager::get_action (X_("Transport"), X_("Loop")); + auto_loop_button.set_related_action (act); + act = ActionManager::get_action (X_("Transport"), X_("PlaySelection")); + play_selection_button.set_related_action (act); + + act = ActionManager::get_action (X_("MIDI"), X_("panic")); + midi_panic_button.set_related_action (act); + + /* tooltips depend on actions */ + set_tooltip (roll_button, _("Play from playhead")); + set_tooltip (stop_button, _("Stop playback")); + set_tooltip (rec_button, _("Toggle record")); + set_tooltip (play_selection_button, _("Play range/selection")); + set_tooltip (goto_start_button, _("Go to start of session")); + set_tooltip (goto_end_button, _("Go to end of session")); + set_tooltip (auto_loop_button, _("Play loop range")); + set_tooltip (midi_panic_button, _("MIDI Panic\nSend note off and reset controller messages on all MIDI channels")); + + /* set click_button tooltip */ + parameter_changed ("click-gain"); +} + +void +TransportControlUI::setup (TransportControlProvider* ui) +{ + click_button.signal_button_press_event().connect (sigc::mem_fun (*ui, &TransportControlProvider::click_button_clicked), false); + click_button.signal_scroll_event().connect (sigc::mem_fun (*this, &TransportControlUI::click_button_scroll), false); + + /* setup icons */ + + click_button.set_icon (ArdourIcon::TransportMetronom); + goto_start_button.set_icon (ArdourIcon::TransportStart); + goto_end_button.set_icon (ArdourIcon::TransportEnd); + roll_button.set_icon (ArdourIcon::TransportPlay); + stop_button.set_icon (ArdourIcon::TransportStop); + play_selection_button.set_icon (ArdourIcon::TransportRange); + auto_loop_button.set_icon (ArdourIcon::TransportLoop); + rec_button.set_icon (ArdourIcon::RecButton); + midi_panic_button.set_icon (ArdourIcon::TransportPanic); + + /* transport control size-group */ + + Glib::RefPtr transport_button_size_group = SizeGroup::create (SIZE_GROUP_BOTH); + transport_button_size_group->add_widget (goto_start_button); + transport_button_size_group->add_widget (goto_end_button); + transport_button_size_group->add_widget (auto_loop_button); + transport_button_size_group->add_widget (rec_button); + transport_button_size_group->add_widget (play_selection_button); + transport_button_size_group->add_widget (roll_button); + transport_button_size_group->add_widget (stop_button); + + transport_button_size_group->add_widget (midi_panic_button); + transport_button_size_group->add_widget (click_button); + +#define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale())) + + click_button.set_size_request (PX_SCALE(20), PX_SCALE(20)); + set_spacing (PX_SCALE(2)); + +#undef PX_SCALE + + pack_start (midi_panic_button, true, true, 0); + pack_start (click_button, true, true, 0); + pack_start (goto_start_button, true, true); + pack_start (goto_end_button, true, true); + pack_start (auto_loop_button, true, true); + pack_start (play_selection_button, true, true); + pack_start (roll_button, true, true); + pack_start (stop_button, true, true); + pack_start (rec_button, true, true, 3); + + roll_button.set_name ("transport button"); + stop_button.set_name ("transport button"); + goto_start_button.set_name ("transport button"); + goto_end_button.set_name ("transport button"); + auto_loop_button.set_name ("transport button"); + play_selection_button.set_name ("transport button"); + rec_button.set_name ("transport recenable button"); + midi_panic_button.set_name ("transport button"); // XXX ??? + click_button.set_name ("transport button"); + + roll_button.set_controllable (ui->roll_controllable); + stop_button.set_controllable (ui->stop_controllable); + goto_start_button.set_controllable (ui->goto_start_controllable); + goto_end_button.set_controllable (ui->goto_end_controllable); + auto_loop_button.set_controllable (ui->auto_loop_controllable); + play_selection_button.set_controllable (ui->play_selection_controllable); + rec_button.set_controllable (ui->rec_controllable); + + stop_button.set_active (true); + + Timers::blink_connect (sigc::mem_fun (*this, &TransportControlUI::blink_rec_enable)); +} + +void +TransportControlUI::set_session (ARDOUR::Session *s) +{ + SessionHandlePtr::set_session (s); + set_loop_sensitivity (); + + if (!_session) { + rec_button.set_sensitive (false); + return; + } + + _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::parameter_changed, this, _1), gui_context()); + _session->StepEditStatusChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::step_edit_status_change, this, _1), gui_context()); + _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::map_transport_state, this), gui_context()); + _session->auto_loop_location_changed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::set_loop_sensitivity, this), gui_context ()); + + rec_button.set_sensitive (true); +} + +void +TransportControlUI::parameter_changed (std::string p) +{ + if (p == "external-sync") { + set_loop_sensitivity (); + } else if (p == "click-record-only") { + // TODO set a flag, blink or gray-out metronome button while rolling, only + if (Config->get_click_record_only()) { + click_button.set_name ("generic button"); // XXX + } else { + click_button.set_name ("transport button"); + } + } else if (p == "click-gain") { + float gain_db = accurate_coefficient_to_dB (Config->get_click_gain()); + char tmp[32]; + snprintf(tmp, 31, "%+.1f", gain_db); + set_tooltip (click_button, string_compose (_("Enable/Disable metronome\n\nRight-click to access preferences\nMouse-wheel to modify level\nSignal Level: %1 dBFS"), tmp)); + } +} + +void +TransportControlUI::map_transport_state () +{ + if (!_session) { + auto_loop_button.unset_active_state (); + play_selection_button.unset_active_state (); + roll_button.unset_active_state (); + stop_button.set_active_state (Gtkmm2ext::ExplicitActive); + return; + } + + float sp = _session->transport_speed(); + + if (sp != 0.0f) { + + /* we're rolling */ + + if (_session->get_play_range()) { + + play_selection_button.set_active_state (Gtkmm2ext::ExplicitActive); + roll_button.unset_active_state (); + auto_loop_button.unset_active_state (); + + } else if (_session->get_play_loop ()) { + + auto_loop_button.set_active (true); + play_selection_button.set_active (false); + + if (Config->get_loop_is_mode()) { + roll_button.set_active (true); + } else { + roll_button.set_active (false); + } + + } else { + + roll_button.set_active (true); + play_selection_button.set_active (false); + auto_loop_button.set_active (false); + + } + + if (UIConfiguration::instance().get_follow_edits() && !_session->config.get_external_sync()) { + /* light up both roll and play-selection if they are joined */ + roll_button.set_active (true); + play_selection_button.set_active (true); + } + + stop_button.set_active (false); + + } else { + + stop_button.set_active (true); + roll_button.set_active (false); + play_selection_button.set_active (false); + if (Config->get_loop_is_mode ()) { + auto_loop_button.set_active (_session->get_play_loop()); + } else { + auto_loop_button.set_active (false); + } + } +} + +void +TransportControlUI::step_edit_status_change (bool yn) +{ + // XXX should really store pre-step edit status of things + // we make insensitive + + if (yn) { + rec_button.set_active_state (Gtkmm2ext::ImplicitActive); + rec_button.set_sensitive (false); + } else { + rec_button.unset_active_state ();; + rec_button.set_sensitive (true); + } +} + +void +TransportControlUI::set_loop_sensitivity () +{ + if (!_session || _session->config.get_external_sync()) { + auto_loop_button.set_sensitive (false); + } else { + auto_loop_button.set_sensitive (_session && _session->locations()->auto_loop_location()); + } +} + +void +TransportControlUI::blink_rec_enable (bool onoff) +{ + if (_session == 0) { + return; + } + + if (_session->step_editing()) { + return; + } + + Session::RecordState const r = _session->record_status (); + bool const h = _session->have_rec_enabled_track (); + + if (r == Session::Enabled || (r == Session::Recording && !h)) { + if (onoff) { + rec_button.set_active_state (Gtkmm2ext::ExplicitActive); + } else { + rec_button.set_active_state (Gtkmm2ext::Off); + } + } else if (r == Session::Recording && h) { + rec_button.set_active_state (Gtkmm2ext::ExplicitActive); + } else { + rec_button.unset_active_state (); + } +} + +bool +TransportControlUI::click_button_scroll (GdkEventScroll* ev) +{ + gain_t gain = Config->get_click_gain(); + float gain_db = accurate_coefficient_to_dB (gain); + + switch (ev->direction) { + case GDK_SCROLL_UP: + case GDK_SCROLL_LEFT: + gain_db += 1; + break; + case GDK_SCROLL_DOWN: + case GDK_SCROLL_RIGHT: + gain_db -= 1; + break; + } + gain_db = std::max (-60.f, gain_db); + gain = dB_to_coefficient (gain_db); + gain = std::min (gain, Config->get_max_gain()); + Config->set_click_gain (gain); + return true; +} diff --git a/gtk2_ardour/transport_control_ui.h b/gtk2_ardour/transport_control_ui.h new file mode 100644 index 0000000000..97476ea07f --- /dev/null +++ b/gtk2_ardour/transport_control_ui.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 Robin Gareus + * Copyright (C) 2013 Paul Davis + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _transport_control_ui_h_ +#define _transport_control_ui_h_ + +#include + +#include "pbd/signals.h" +#include "ardour/session_handle.h" +#include "widgets/ardour_button.h" + +namespace ARDOUR { + class Session; +} + +class TransportControlProvider; + +class TransportControlUI : public ARDOUR::SessionHandlePtr, public Gtk::HBox +{ +public: + TransportControlUI (); + + void setup (TransportControlProvider*); + void map_actions (); + void set_session (ARDOUR::Session *s); + + ArdourWidgets::ArdourButton& size_button () { return stop_button; } + +protected: + + void parameter_changed (std::string p); + + void blink_rec_enable (bool onoff); + void set_loop_sensitivity (); + void set_transport_sensitivity (bool); + void map_transport_state (); + void step_edit_status_change (bool yn); + + bool click_button_scroll (GdkEventScroll* ev); + + ArdourWidgets::ArdourButton roll_button; + ArdourWidgets::ArdourButton stop_button; + ArdourWidgets::ArdourButton goto_start_button; + ArdourWidgets::ArdourButton goto_end_button; + ArdourWidgets::ArdourButton auto_loop_button; + ArdourWidgets::ArdourButton play_selection_button; + ArdourWidgets::ArdourButton rec_button; + ArdourWidgets::ArdourButton midi_panic_button; + ArdourWidgets::ArdourButton click_button; + +private: + PBD::ScopedConnection config_connection; +}; + +#endif diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index e883f99cb9..2555de84c4 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -259,6 +259,8 @@ gtk2_ardour_sources = [ 'track_selection.cc', 'track_view_list.cc', 'transform_dialog.cc', + 'transport_control.cc', + 'transport_control_ui.cc', 'transpose_dialog.cc', 'ui_config.cc', 'utils.cc',