13
0

Separate and consolidate Transport-Control-UI code

This commit is contained in:
Robin Gareus 2017-12-22 20:20:59 +01:00
parent 34a926fc51
commit aab787f686
10 changed files with 552 additions and 360 deletions

View File

@ -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<void (string)> 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<Action> 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 ()
{

View File

@ -37,7 +37,6 @@
#include "pbd/xml++.h"
#include "pbd/controllable.h"
#include <gtkmm/box.h>
#include <gtkmm/frame.h>
#include <gtkmm/label.h>
@ -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<TransportControllable> roll_controllable;
boost::shared_ptr<TransportControllable> stop_controllable;
boost::shared_ptr<TransportControllable> goto_start_controllable;
boost::shared_ptr<TransportControllable> goto_end_controllable;
boost::shared_ptr<TransportControllable> auto_loop_controllable;
boost::shared_ptr<TransportControllable> play_selection_controllable;
boost::shared_ptr<TransportControllable> 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<std::string> hits);
bool click_button_clicked (GdkEventButton *);
bool click_button_scroll (GdkEventScroll *);
bool sync_button_clicked (GdkEventButton *);
VisibilityGroup _status_bar_visibility;

View File

@ -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<Action> 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<SizeGroup> 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<SizeGroup> 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)
{

View File

@ -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) {

View File

@ -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));
}
}

View File

@ -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<Action> act = ActionManager::get_action ("Transport", action);
if (act) {
act->activate ();
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
* 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 <gtkmm/widget.h>
#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<TransportControllable> roll_controllable;
boost::shared_ptr<TransportControllable> stop_controllable;
boost::shared_ptr<TransportControllable> goto_start_controllable;
boost::shared_ptr<TransportControllable> goto_end_controllable;
boost::shared_ptr<TransportControllable> auto_loop_controllable;
boost::shared_ptr<TransportControllable> play_selection_controllable;
boost::shared_ptr<TransportControllable> rec_controllable;
};
#endif

View File

@ -0,0 +1,330 @@
/*
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
* 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 <gtkmm/sizegroup.h>
#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<Action> 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<SizeGroup> 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;
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
* 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 <gtkmm/box.h>
#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

View File

@ -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',