2 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2013 Paul Davis
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <gtkmm/sizegroup.h>
22 #include "ardour/dB.h"
23 #include "ardour/profile.h"
24 #include "widgets/tooltips.h"
25 #include "gtkmm2ext/gui_thread.h"
28 #include "ardour_ui.h"
30 #include "transport_control_ui.h"
36 using namespace ARDOUR;
37 using namespace ArdourWidgets;
39 TransportControlUI::TransportControlUI ()
41 Config->ParameterChanged.connect (config_connection, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::parameter_changed, this, _1), gui_context());
45 TransportControlUI::map_actions ()
50 act = ActionManager::get_action (X_("Transport"), X_("ToggleClick"));
51 click_button.set_related_action (act);
52 act = ActionManager::get_action (X_("Transport"), X_("Stop"));
53 stop_button.set_related_action (act);
54 act = ActionManager::get_action (X_("Transport"), X_("Roll"));
55 roll_button.set_related_action (act);
56 act = ActionManager::get_action (X_("Transport"), X_("Record"));
57 rec_button.set_related_action (act);
58 act = ActionManager::get_action (X_("Transport"), X_("GotoStart"));
59 goto_start_button.set_related_action (act);
60 act = ActionManager::get_action (X_("Transport"), X_("GotoEnd"));
61 goto_end_button.set_related_action (act);
62 act = ActionManager::get_action (X_("Transport"), X_("Loop"));
63 auto_loop_button.set_related_action (act);
64 act = ActionManager::get_action (X_("Transport"), X_("PlaySelection"));
65 play_selection_button.set_related_action (act);
67 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
68 midi_panic_button.set_related_action (act);
70 /* tooltips depend on actions */
71 set_tooltip (roll_button, _("Play from playhead"));
72 set_tooltip (stop_button, _("Stop playback"));
73 set_tooltip (rec_button, _("Toggle record"));
74 set_tooltip (play_selection_button, _("Play range/selection"));
75 set_tooltip (goto_start_button, _("Go to start of session"));
76 set_tooltip (goto_end_button, _("Go to end of session"));
77 set_tooltip (auto_loop_button, _("Play loop range"));
78 set_tooltip (midi_panic_button, _("MIDI Panic\nSend note off and reset controller messages on all MIDI channels"));
80 /* set click_button tooltip */
81 parameter_changed ("click-gain");
85 TransportControlUI::setup (TransportControlProvider* ui)
87 click_button.signal_button_press_event().connect (sigc::mem_fun (*ui, &TransportControlProvider::click_button_clicked), false);
88 click_button.signal_scroll_event().connect (sigc::mem_fun (*this, &TransportControlUI::click_button_scroll), false);
92 click_button.set_icon (ArdourIcon::TransportMetronom);
93 goto_start_button.set_icon (ArdourIcon::TransportStart);
94 goto_end_button.set_icon (ArdourIcon::TransportEnd);
95 roll_button.set_icon (ArdourIcon::TransportPlay);
96 stop_button.set_icon (ArdourIcon::TransportStop);
97 play_selection_button.set_icon (ArdourIcon::TransportRange);
98 auto_loop_button.set_icon (ArdourIcon::TransportLoop);
99 rec_button.set_icon (ArdourIcon::RecButton);
100 midi_panic_button.set_icon (ArdourIcon::TransportPanic);
102 /* transport control size-group */
104 Glib::RefPtr<SizeGroup> transport_button_size_group = SizeGroup::create (SIZE_GROUP_BOTH);
105 transport_button_size_group->add_widget (goto_start_button);
106 transport_button_size_group->add_widget (goto_end_button);
107 transport_button_size_group->add_widget (auto_loop_button);
108 transport_button_size_group->add_widget (rec_button);
109 if (!ARDOUR::Profile->get_mixbus()) {
110 /*note: since we aren't showing this button, it doesn't get allocated
111 * and therefore blows-up the size-group. so remove it.
113 transport_button_size_group->add_widget (play_selection_button);
115 transport_button_size_group->add_widget (roll_button);
116 transport_button_size_group->add_widget (stop_button);
118 transport_button_size_group->add_widget (midi_panic_button);
119 transport_button_size_group->add_widget (click_button);
121 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
123 click_button.set_size_request (PX_SCALE(20), PX_SCALE(20));
124 set_spacing (PX_SCALE(2));
128 if (!ARDOUR::Profile->get_mixbus()) {
129 pack_start (midi_panic_button, true, true, 0);
131 pack_start (midi_panic_button, true, true, 3);
133 pack_start (click_button, true, true, 0);
134 pack_start (goto_start_button, true, true);
135 pack_start (goto_end_button, true, true);
136 pack_start (auto_loop_button, true, true);
137 if (!ARDOUR::Profile->get_mixbus()) {
138 pack_start (play_selection_button, true, true);
140 pack_start (roll_button, true, true);
141 pack_start (stop_button, true, true);
142 pack_start (rec_button, true, true, 3);
144 roll_button.set_name ("transport button");
145 stop_button.set_name ("transport button");
146 goto_start_button.set_name ("transport button");
147 goto_end_button.set_name ("transport button");
148 auto_loop_button.set_name ("transport button");
149 play_selection_button.set_name ("transport button");
150 rec_button.set_name ("transport recenable button");
151 midi_panic_button.set_name ("transport button"); // XXX ???
152 click_button.set_name ("transport button");
154 roll_button.set_controllable (ui->roll_controllable);
155 stop_button.set_controllable (ui->stop_controllable);
156 goto_start_button.set_controllable (ui->goto_start_controllable);
157 goto_end_button.set_controllable (ui->goto_end_controllable);
158 auto_loop_button.set_controllable (ui->auto_loop_controllable);
159 play_selection_button.set_controllable (ui->play_selection_controllable);
160 rec_button.set_controllable (ui->rec_controllable);
162 stop_button.set_active (true);
164 Timers::blink_connect (sigc::mem_fun (*this, &TransportControlUI::blink_rec_enable));
168 TransportControlUI::set_session (ARDOUR::Session *s)
170 SessionHandlePtr::set_session (s);
171 set_loop_sensitivity ();
172 map_transport_state ();
175 rec_button.set_sensitive (false);
179 _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::parameter_changed, this, _1), gui_context());
180 _session->StepEditStatusChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::step_edit_status_change, this, _1), gui_context());
181 _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::map_transport_state, this), gui_context());
182 _session->auto_loop_location_changed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&TransportControlUI::set_loop_sensitivity, this), gui_context ());
184 rec_button.set_sensitive (true);
188 TransportControlUI::parameter_changed (std::string p)
190 if (p == "external-sync") {
191 set_loop_sensitivity ();
192 } else if (p == "click-record-only") {
193 // TODO set a flag, blink or gray-out metronome button while rolling, only
194 if (Config->get_click_record_only()) {
195 click_button.set_name ("generic button"); // XXX
197 click_button.set_name ("transport button");
199 } else if (p == "click-gain") {
200 float gain_db = accurate_coefficient_to_dB (Config->get_click_gain());
202 snprintf(tmp, 31, "%+.1f", gain_db);
203 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));
208 TransportControlUI::map_transport_state ()
211 auto_loop_button.unset_active_state ();
212 play_selection_button.unset_active_state ();
213 roll_button.unset_active_state ();
214 stop_button.set_active_state (Gtkmm2ext::ExplicitActive);
218 float sp = _session->transport_speed();
224 if (_session->get_play_range()) {
226 play_selection_button.set_active_state (Gtkmm2ext::ExplicitActive);
227 roll_button.unset_active_state ();
228 auto_loop_button.unset_active_state ();
230 } else if (_session->get_play_loop ()) {
232 auto_loop_button.set_active (true);
233 play_selection_button.set_active (false);
235 if (Config->get_loop_is_mode()) {
236 roll_button.set_active (true);
238 roll_button.set_active (false);
243 roll_button.set_active (true);
244 play_selection_button.set_active (false);
245 auto_loop_button.set_active (false);
249 if (UIConfiguration::instance().get_follow_edits() && !_session->config.get_external_sync()) {
250 /* light up both roll and play-selection if they are joined */
251 roll_button.set_active (true);
252 play_selection_button.set_active (true);
255 stop_button.set_active (false);
259 stop_button.set_active (true);
260 roll_button.set_active (false);
261 play_selection_button.set_active (false);
262 if (Config->get_loop_is_mode ()) {
263 auto_loop_button.set_active (_session->get_play_loop());
265 auto_loop_button.set_active (false);
271 TransportControlUI::step_edit_status_change (bool yn)
273 // XXX should really store pre-step edit status of things
274 // we make insensitive
277 rec_button.set_active_state (Gtkmm2ext::ImplicitActive);
278 rec_button.set_sensitive (false);
280 rec_button.unset_active_state ();;
281 rec_button.set_sensitive (true);
286 TransportControlUI::set_loop_sensitivity ()
288 if (!_session || _session->config.get_external_sync()) {
289 auto_loop_button.set_sensitive (false);
291 auto_loop_button.set_sensitive (_session && _session->locations()->auto_loop_location());
296 TransportControlUI::blink_rec_enable (bool onoff)
302 if (_session->step_editing()) {
306 Session::RecordState const r = _session->record_status ();
307 bool const h = _session->have_rec_enabled_track ();
309 if (r == Session::Enabled || (r == Session::Recording && !h)) {
311 rec_button.set_active_state (Gtkmm2ext::ExplicitActive);
313 rec_button.set_active_state (Gtkmm2ext::Off);
315 } else if (r == Session::Recording && h) {
316 rec_button.set_active_state (Gtkmm2ext::ExplicitActive);
318 rec_button.unset_active_state ();
323 TransportControlUI::click_button_scroll (GdkEventScroll* ev)
325 gain_t gain = Config->get_click_gain();
326 float gain_db = accurate_coefficient_to_dB (gain);
328 switch (ev->direction) {
330 case GDK_SCROLL_LEFT:
333 case GDK_SCROLL_DOWN:
334 case GDK_SCROLL_RIGHT:
338 gain_db = std::max (-60.f, gain_db);
339 gain = dB_to_coefficient (gain_db);
340 gain = std::min (gain, Config->get_max_gain());
341 Config->set_click_gain (gain);