1 /*
2 * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3 * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
5 * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
6 * Copyright (C) 2008-2012 Carl Hetherington <carl@carlh.net>
7 * Copyright (C) 2009 Sampo Savolainen <v2@iki.fi>
8 * Copyright (C) 2012-2015 Tim Mayberry <mojofunk@gmail.com>
9 * Copyright (C) 2013-2015 Nick Mainsbridge <mainsbridge@gmail.com>
10 * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
11 * Copyright (C) 2014-2017 Ben Loftis <ben@harrisonconsoles.com>
12 * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
13 * Copyright (C) 2017 Johannes Mueller <github@johannes-mueller.org>
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License along
26 * with this program; if not, write to the Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 */
29
30 #include <map>
31 #include <boost/algorithm/string.hpp>
32
33 #include <gtkmm/stock.h>
34
35 #include "pbd/memento_command.h"
36 #include "pbd/controllable.h"
37 #include "pbd/enumwriter.h"
38
39 #include "ardour/dB.h"
40 #include "ardour/route_group.h"
41 #include "ardour/solo_isolate_control.h"
42 #include "ardour/vca.h"
43 #include "ardour/vca_manager.h"
44 #include "ardour/audio_track.h"
45 #include "ardour/audio_port.h"
46 #include "ardour/audioengine.h"
47 #include "ardour/filename_extensions.h"
48 #include "ardour/midi_track.h"
49 #include "ardour/monitor_control.h"
50 #include "ardour/internal_send.h"
51 #include "ardour/panner_shell.h"
52 #include "ardour/polarity_processor.h"
53 #include "ardour/profile.h"
54 #include "ardour/phase_control.h"
55 #include "ardour/send.h"
56 #include "ardour/route.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlists.h"
59 #include "ardour/solo_mute_release.h"
60 #include "ardour/template_utils.h"
61
62 #include "gtkmm2ext/gtk_ui.h"
63 #include "gtkmm2ext/doi.h"
64 #include "gtkmm2ext/gtk_ui.h"
65 #include "gtkmm2ext/utils.h"
66
67 #include "widgets/ardour_button.h"
68 #include "widgets/binding_proxy.h"
69 #include "widgets/prompter.h"
70
71 #include "ardour_dialog.h"
72 #include "ardour_ui.h"
73 #include "automation_time_axis.h"
74 #include "editor.h"
75 #include "group_tabs.h"
76 #include "gui_object.h"
77 #include "gui_thread.h"
78 #include "keyboard.h"
79 #include "latency_gui.h"
80 #include "mixer_strip.h"
81 #include "mixer_ui.h"
82 #include "patch_change_widget.h"
83 #include "playlist_selector.h"
84 #include "plugin_pin_dialog.h"
85 #include "rgb_macros.h"
86 #include "route_time_axis.h"
87 #include "route_ui.h"
88 #include "save_template_dialog.h"
89 #include "timers.h"
90 #include "ui_config.h"
91 #include "utils.h"
92
93 #include "pbd/i18n.h"
94
95 using namespace Gtk;
96 using namespace Gtkmm2ext;
97 using namespace ARDOUR;
98 using namespace ARDOUR_UI_UTILS;
99 using namespace ArdourWidgets;
100 using namespace PBD;
101 using namespace std;
102
103 uint32_t RouteUI::_max_invert_buttons = 3;
104 PBD::Signal1<void, boost::shared_ptr<Route> > RouteUI::BusSendDisplayChanged;
105 boost::weak_ptr<Route> RouteUI::_showing_sends_to;
106 std::string RouteUI::program_port_prefix;
107
108 RouteUI::IOSelectorMap RouteUI::input_selectors;
109 RouteUI::IOSelectorMap RouteUI::output_selectors;
110
111 void
delete_ioselector(IOSelectorMap & m,boost::shared_ptr<ARDOUR::Route> r)112 RouteUI::delete_ioselector (IOSelectorMap& m, boost::shared_ptr<ARDOUR::Route> r)
113 {
114 if (!r) {
115 return;
116 }
117 IOSelectorMap::iterator i = m.find (r->id ());
118 if (i != m.end ()) {
119 delete i->second;
120 m.erase (i);
121 }
122 }
123
RouteUI(ARDOUR::Session * sess)124 RouteUI::RouteUI (ARDOUR::Session* sess)
125 : monitor_input_button (0)
126 , monitor_disk_button (0)
127 , mute_menu(0)
128 , solo_menu(0)
129 , sends_menu(0)
130 , playlist_action_menu (0)
131 , _playlist_selector(0)
132 , _record_menu(0)
133 , _comment_window(0)
134 , _comment_area(0)
135 , _invert_menu(0)
136 {
137 if (program_port_prefix.empty()) {
138 // compare to gtk2_ardour/port_group.cc
139 string lpn (PROGRAM_NAME);
140 boost::to_lower (lpn);
141 program_port_prefix = lpn + ":"; // e.g. "ardour:"
142 }
143
144 if (sess) {
145 init ();
146 }
147 }
148
~RouteUI()149 RouteUI::~RouteUI()
150 {
151 if (_route) {
152 ARDOUR_UI::instance()->gui_object_state->remove_node (route_state_id());
153 }
154
155 delete_patch_change_dialog ();
156
157 delete_ioselector (input_selectors, _route);
158 delete_ioselector (output_selectors, _route);
159
160 _route.reset (); /* drop reference to route, so that it can be cleaned up */
161 route_connections.drop_connections ();
162
163 delete solo_menu;
164 delete mute_menu;
165 delete sends_menu;
166 delete monitor_input_button;
167 delete monitor_disk_button;
168 delete playlist_action_menu;
169 delete _record_menu;
170 delete _comment_window;
171 delete _invert_menu;
172 delete _playlist_selector;
173
174 send_blink_connection.disconnect ();
175 rec_blink_connection.disconnect ();
176 }
177
178 void
init()179 RouteUI::init ()
180 {
181 self_destruct = true;
182 _playlist_selector = 0;
183 mute_menu = 0;
184 solo_menu = 0;
185 sends_menu = 0;
186 _record_menu = 0;
187 _invert_menu = 0;
188 pre_fader_mute_check = 0;
189 post_fader_mute_check = 0;
190 listen_mute_check = 0;
191 main_mute_check = 0;
192 solo_safe_check = 0;
193 solo_isolated_check = 0;
194 solo_isolated_led = 0;
195 solo_safe_led = 0;
196 _solo_release = 0;
197 _mute_release = 0;
198 denormal_menu_item = 0;
199 _step_edit_item = 0;
200 _rec_safe_item = 0;
201 _ignore_comment_edit = false;
202 _i_am_the_modifier = 0;
203 _n_polarity_invert = 0;
204
205 setup_invert_buttons ();
206
207 mute_button = manage (new ArdourButton);
208 mute_button->set_name ("mute button");
209 UI::instance()->set_tip (mute_button, _("Mute this track"), "");
210
211 solo_button = manage (new ArdourButton);
212 solo_button->set_name ("solo button");
213 solo_button->set_no_show_all (true);
214
215 rec_enable_button = manage (new ArdourButton);
216 rec_enable_button->set_name ("record enable button");
217 rec_enable_button->set_icon (ArdourIcon::RecButton);
218 UI::instance()->set_tip (rec_enable_button, _("Enable recording on this track"), "");
219
220 if (UIConfiguration::instance().get_blink_rec_arm()) {
221 rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::blink_rec_display));
222 }
223
224 show_sends_button = manage (new ArdourButton);
225 show_sends_button->set_name ("send alert button");
226 UI::instance()->set_tip (show_sends_button, _("Show the strips that send to this bus, and control them from the faders"), "");
227
228 monitor_input_button = new ArdourButton (ArdourButton::default_elements);
229 monitor_input_button->set_name ("monitor button");
230 monitor_input_button->set_text (_("In"));
231 UI::instance()->set_tip (monitor_input_button, _("Monitor input"), "");
232 monitor_input_button->set_no_show_all (true);
233
234 monitor_disk_button = new ArdourButton (ArdourButton::default_elements);
235 monitor_disk_button->set_name ("monitor button");
236 monitor_disk_button->set_text (_("Disk"));
237 UI::instance()->set_tip (monitor_disk_button, _("Monitor playback"), "");
238 monitor_disk_button->set_no_show_all (true);
239
240 _session->SoloChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::solo_changed_so_update_mute, this), gui_context());
241 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::check_rec_enable_sensitivity, this), gui_context());
242 _session->RecordStateChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::session_rec_enable_changed, this), gui_context());
243 _session->MonitorBusAddedOrRemoved.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_button, this), gui_context());
244
245 _session->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteUI::parameter_changed, this, _1), gui_context());
246 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteUI::parameter_changed, this, _1), gui_context());
247 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (this, &RouteUI::parameter_changed));
248
249 rec_enable_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::rec_enable_press), false);
250 rec_enable_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::rec_enable_release), false);
251
252 show_sends_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::show_sends_press), false);
253 show_sends_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::show_sends_release), false);
254
255 solo_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::solo_press), false);
256 solo_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::solo_release), false);
257 mute_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::mute_press), false);
258 mute_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::mute_release), false);
259
260 monitor_input_button->set_distinct_led_click (false);
261 monitor_disk_button->set_distinct_led_click (false);
262
263 monitor_input_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_input_press), false);
264 monitor_input_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_input_release), false);
265
266 monitor_disk_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_disk_press), false);
267 monitor_disk_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_disk_release), false);
268
269 BusSendDisplayChanged.connect_same_thread (*this, boost::bind(&RouteUI::bus_send_display_changed, this, _1));
270 }
271
272 void
reset()273 RouteUI::reset ()
274 {
275 route_connections.drop_connections ();
276
277 delete solo_menu;
278 solo_menu = 0;
279
280 delete mute_menu;
281 mute_menu = 0;
282
283 delete_patch_change_dialog ();
284 _color_picker.reset ();
285
286 denormal_menu_item = 0;
287 }
288
289 void
self_delete()290 RouteUI::self_delete ()
291 {
292 delete this;
293 }
294
295 void
set_session(ARDOUR::Session * s)296 RouteUI::set_session (ARDOUR::Session*s)
297 {
298 SessionHandlePtr::set_session (s);
299
300 if (!s) {
301 /* This is needed to clean out IDs of sends, when using output selector
302 * with MixerStrip::_current_delivery.
303 * It's also prudent to hide/destroy input-selectors early, before delayed
304 * self_delete() can do that in the ~RouteUI.
305 */
306 for (IOSelectorMap::const_iterator i = input_selectors.begin(); i != input_selectors.end() ; ++i) {
307 delete i->second;
308 }
309 for (IOSelectorMap::const_iterator i = output_selectors.begin(); i != output_selectors.end() ; ++i) {
310 delete i->second;
311 }
312 input_selectors.clear ();
313 output_selectors.clear ();
314 }
315 }
316
317 void
set_route(boost::shared_ptr<Route> rp)318 RouteUI::set_route (boost::shared_ptr<Route> rp)
319 {
320 reset ();
321
322 _route = rp;
323
324 if (!_route->presentation_info().color_set()) {
325 /* deal with older 4.x color, which was stored in the GUI object state */
326
327 string p = ARDOUR_UI::instance()->gui_object_state->get_string (route_state_id(), X_("color"));
328
329 if (!p.empty()) {
330
331 /* old v4.x or earlier session. Use this information */
332
333 int red, green, blue;
334 char colon;
335
336 stringstream ss (p);
337
338 /* old color format version was:
339
340 16bit value for red:16 bit value for green:16 bit value for blue
341
342 decode to rgb ..
343 */
344
345 ss >> red;
346 ss >> colon;
347 ss >> green;
348 ss >> colon;
349 ss >> blue;
350
351 red >>= 2;
352 green >>= 2;
353 blue >>= 2;
354
355 _route->presentation_info().set_color (RGBA_TO_UINT (red, green, blue, 255));
356 }
357 }
358
359 if (set_color_from_route()) {
360 set_color (gdk_color_to_rgba (AxisView::unique_random_color ()));
361 }
362
363 if (self_destruct) {
364 rp->DropReferences.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::self_delete, this), gui_context());
365 }
366
367 mute_button->set_controllable (_route->mute_control());
368 solo_button->set_controllable (_route->solo_control());
369
370 _route->active_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_active_changed, this), gui_context());
371
372 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::comment_changed, this), gui_context());
373
374 _route->mute_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_mute_display, this), gui_context());
375 _route->solo_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
376 _route->solo_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
377 _route->solo_isolate_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
378 _route->phase_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_polarity_display, this), gui_context());
379
380 if (is_track()) {
381 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteUI::map_frozen, this), gui_context());
382 track_mode_changed();
383 }
384
385
386 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_property_changed, this, _1), gui_context());
387 _route->presentation_info().PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_gui_changed, this, _1), gui_context ());
388
389 _route->polarity()->ConfigurationChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::setup_invert_buttons, this), gui_context());
390
391 if (_session->writable() && is_track()) {
392 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
393
394 t->rec_enable_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
395 t->rec_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
396
397 rec_enable_button->show();
398 rec_enable_button->set_controllable (t->rec_enable_control());
399
400 if (is_midi_track()) {
401 midi_track()->StepEditStatusChange.connect (route_connections, invalidator (*this),
402 boost::bind (&RouteUI::step_edit_changed, this, _1), gui_context());
403 }
404
405 }
406
407 /* this will work for busses and tracks, and needs to be called to
408 set up the name entry/name label display.
409 */
410
411 if (is_track()) {
412 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
413 t->monitoring_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_monitoring_display, this), gui_context());
414
415 update_monitoring_display ();
416 }
417
418 mute_button->unset_flags (Gtk::CAN_FOCUS);
419 solo_button->unset_flags (Gtk::CAN_FOCUS);
420
421 mute_button->show();
422
423 if (_route->is_monitor() || _route->is_master()) {
424 solo_button->hide ();
425 } else {
426 solo_button->show();
427 }
428
429 map_frozen ();
430
431 setup_invert_buttons ();
432
433 boost::shared_ptr<Route> s = _showing_sends_to.lock ();
434 bus_send_display_changed (s);
435
436 update_mute_display ();
437 update_solo_display ();
438 update_solo_button ();
439
440 if (!UIConfiguration::instance().get_blink_rec_arm()) {
441 blink_rec_display(true); // set initial rec-en button state
442 }
443
444 check_rec_enable_sensitivity ();
445 maybe_add_route_print_mgr ();
446 route_color_changed();
447 route_gui_changed (PropertyChange (Properties::selected));
448 }
449
450 bool
mute_press(GdkEventButton * ev)451 RouteUI::mute_press (GdkEventButton* ev)
452 {
453 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
454 return true;
455 }
456
457 //if this is a binding action, let the ArdourButton handle it
458 if (BindingProxy::is_bind_action(ev) )
459 return false;
460
461 if (Keyboard::is_context_menu_event (ev)) {
462
463 if (mute_menu == 0){
464 build_mute_menu();
465 }
466
467 mute_menu->popup(0,ev->time);
468
469 return true;
470
471 } else {
472
473 if (Keyboard::is_button2_event (ev)) {
474 // button2-click is "momentary"
475
476 _mute_release = new SoloMuteRelease (_route->mute_control()->muted ());
477 }
478
479 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
480
481 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
482
483 /* toggle mute on everything (but
484 * exclude the master and monitor)
485 *
486 * because we are going to erase
487 * elements of the list we need to work
488 * on a copy.
489 */
490
491 boost::shared_ptr<RouteList> copy (new RouteList);
492
493 *copy = *_session->get_routes ();
494
495 for (RouteList::iterator i = copy->begin(); i != copy->end(); ) {
496 if ((*i)->is_master() || (*i)->is_monitor()) {
497 i = copy->erase (i);
498 } else {
499 ++i;
500 }
501 }
502
503 if (_mute_release) {
504 _mute_release->set (copy);
505 }
506
507 _session->set_controls (route_list_to_control_list (copy, &Stripable::mute_control), _route->muted_by_self() ? 0.0 : 1.0, Controllable::UseGroup);
508
509 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
510
511 /* Primary-button1 inverts the implication of
512 the group being active. If the group is
513 active (for mute), then this modifier means
514 "do not apply to mute". If the group is
515 inactive (for mute), then this modifier
516 means "apply to route". This is all
517 accomplished by passing just the actual
518 route, along with the InverseGroup group
519 control disposition.
520
521 NOTE: Primary-button2 is MIDI learn.
522 */
523
524 boost::shared_ptr<RouteList> rl;
525
526 if (ev->button == 1) {
527
528 rl.reset (new RouteList);
529 rl->push_back (_route);
530
531 if (_mute_release) {
532 _mute_release->set (rl);
533 }
534
535 boost::shared_ptr<MuteControl> mc = _route->mute_control();
536 mc->start_touch (_session->audible_sample ());
537 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), _route->muted_by_self() ? 0.0 : 1.0, Controllable::InverseGroup);
538 }
539
540 } else {
541
542 /* plain click applies change to this route */
543
544 boost::shared_ptr<RouteList> rl (new RouteList);
545 rl->push_back (_route);
546
547 if (_mute_release) {
548 _mute_release->set (rl);
549 }
550
551 boost::shared_ptr<MuteControl> mc = _route->mute_control();
552 mc->start_touch (_session->audible_sample ());
553 mc->set_value (!_route->muted_by_self(), Controllable::UseGroup);
554 }
555 }
556 }
557
558 return false;
559 }
560
561 bool
mute_release(GdkEventButton *)562 RouteUI::mute_release (GdkEventButton* /*ev*/)
563 {
564 if (_mute_release) {
565 _mute_release->release (_session, true);
566 delete _mute_release;
567 _mute_release = 0;
568 }
569
570 _route->mute_control()->stop_touch (_session->audible_sample ());
571
572 return false;
573 }
574
575 void
edit_output_configuration()576 RouteUI::edit_output_configuration ()
577 {
578 boost::shared_ptr<Send> send = boost::dynamic_pointer_cast<Send>(_current_delivery);
579 if (send && !boost::dynamic_pointer_cast<InternalSend>(send)) {
580 send.reset ();
581 }
582
583 PBD::ID id = send ? send->id () : _route->id ();
584
585 if (output_selectors.find (id) == output_selectors.end ()) {
586 output_selectors[_route->id ()] = new IOSelectorWindow (_session, send ? send->output () : _route->output ());
587 }
588
589 IOSelectorWindow* w = output_selectors[id];
590
591 if (w->is_visible()) {
592 w->get_toplevel()->get_window()->raise();
593 } else {
594 w->present ();
595 }
596
597 //w->set_keep_above (true);
598 }
599
600 void
edit_input_configuration()601 RouteUI::edit_input_configuration ()
602 {
603 if (input_selectors.find (_route->id ()) == input_selectors.end ()) {
604 input_selectors[_route->id ()] = new IOSelectorWindow (_session, _route->input());
605 }
606
607 IOSelectorWindow* w = input_selectors[_route->id ()];
608
609 if (w->is_visible()) {
610 w->get_toplevel()->get_window()->raise();
611 } else {
612 w->present ();
613 }
614
615 //w->set_keep_above (true);
616 }
617
618 bool
solo_press(GdkEventButton * ev)619 RouteUI::solo_press(GdkEventButton* ev)
620 {
621 /* ignore double/triple clicks */
622
623 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
624 return true;
625 }
626
627 //if this is a binding action, let the ArdourButton handle it
628 if (BindingProxy::is_bind_action(ev) )
629 return false;
630
631 if (Keyboard::is_context_menu_event (ev)) {
632
633 if (! (solo_isolated_led && solo_isolated_led->is_visible()) ||
634 ! (solo_safe_led && solo_safe_led->is_visible())) {
635
636 if (solo_menu == 0) {
637 build_solo_menu ();
638 }
639
640 solo_menu->popup (1, ev->time);
641 }
642
643 } else {
644
645 if (Keyboard::is_button2_event (ev)) {
646
647 // button2-click is "momentary"
648 _solo_release = new SoloMuteRelease (_route->self_soloed());
649 }
650
651 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
652
653 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
654
655 /* Primary-Tertiary-click applies change to all routes */
656
657 if (_solo_release) {
658 _solo_release->set (_session->get_routes ());
659 }
660
661 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_control), !_route->solo_control()->get_value(), Controllable::UseGroup);
662
663 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) || (!_route->self_soloed() && Config->get_exclusive_solo ())) {
664
665 /* Primary-Secondary-click: exclusively solo this track */
666
667 if (_solo_release) {
668 _session->prepare_momentary_solo (_solo_release, true, _route);
669 } else {
670 /* clear solo state */
671 _session->prepare_momentary_solo (0, true, _route);
672 }
673
674 DisplaySuspender ds;
675 _route->solo_control()->set_value (1.0, Controllable::NoGroup);
676
677 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
678
679 // shift-click: toggle solo isolated status
680
681 _route->solo_isolate_control()->set_value (_route->solo_isolate_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup);
682 delete _solo_release;
683 _solo_release = 0;
684
685 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
686
687 /* Primary-button1: solo mix group.
688 NOTE: Primary-button2 is MIDI learn.
689 */
690
691 /* Primary-button1 applies change to the mix group even if it is not active
692 NOTE: Primary-button2 is MIDI learn.
693 */
694
695 boost::shared_ptr<RouteList> rl;
696
697 if (ev->button == 1) {
698
699 /* Primary-button1 inverts the implication of
700 the group being active. If the group is
701 active (for solo), then this modifier means
702 "do not apply to solo". If the group is
703 inactive (for mute), then this modifier
704 means "apply to route". This is all
705 accomplished by passing just the actual
706 route, along with the InverseGroup group
707 control disposition.
708
709 NOTE: Primary-button2 is MIDI learn.
710 */
711
712 rl.reset (new RouteList);
713 rl->push_back (_route);
714
715 #if 0 // XX why? _solo_release is deleted below
716 if (_solo_release) {
717 _solo_release->set (rl);
718 }
719 #endif
720
721 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_control), !_route->self_soloed(), Controllable::InverseGroup);
722 }
723
724 delete _solo_release;
725 _solo_release = 0;
726
727 } else {
728
729 /* click: solo this route */
730
731 boost::shared_ptr<RouteList> rl (new RouteList);
732 rl->push_back (route());
733
734 if (_solo_release) {
735 _solo_release->set (rl);
736 }
737
738 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_control), !_route->self_soloed(), Controllable::UseGroup);
739 }
740 }
741 }
742
743 return false;
744 }
745
746 bool
solo_release(GdkEventButton *)747 RouteUI::solo_release (GdkEventButton* /*ev*/)
748 {
749 if (_solo_release) {
750 _solo_release->release (_session, false);
751 delete _solo_release;
752 _solo_release = 0;
753 }
754
755 return false;
756 }
757
758 bool
rec_enable_press(GdkEventButton * ev)759 RouteUI::rec_enable_press(GdkEventButton* ev)
760 {
761 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
762 return true;
763 }
764
765 //if this is a binding action, let the ArdourButton handle it
766 if (BindingProxy::is_bind_action(ev) )
767 return false;
768
769 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
770 return false;
771 }
772
773 if (is_midi_track()) {
774
775 /* rec-enable button exits from step editing, but not context click */
776
777 if (!Keyboard::is_context_menu_event (ev) && midi_track()->step_editing()) {
778 midi_track()->set_step_editing (false);
779 return false;
780 }
781 }
782
783 if (is_track() && rec_enable_button) {
784
785 if (Keyboard::is_button2_event (ev)) {
786
787 //rec arm does not have a momentary mode
788 return false;
789
790 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
791
792 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::NoGroup);
793
794 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
795
796 /* Primary-button1 applies change to the route group (even if it is not active)
797 NOTE: Primary-button2 is MIDI learn.
798 */
799
800 if (ev->button == 1) {
801
802 boost::shared_ptr<RouteList> rl;
803
804 rl.reset (new RouteList);
805 rl->push_back (_route);
806
807 _session->set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::InverseGroup);
808 }
809
810 } else if (Keyboard::is_context_menu_event (ev)) {
811
812 /* do this on release */
813
814 } else {
815
816 boost::shared_ptr<Track> trk = track();
817 trk->rec_enable_control()->set_value (!trk->rec_enable_control()->get_value(), Controllable::UseGroup);
818 }
819 }
820
821 return false;
822 }
823
824 void
update_monitoring_display()825 RouteUI::update_monitoring_display ()
826 {
827 if (!_route) {
828 return;
829 }
830
831 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
832
833 if (!t) {
834 return;
835 }
836
837 MonitorState ms = t->monitoring_state();
838
839 if (t->monitoring_control()->monitoring_choice() & MonitorInput) {
840 monitor_input_button->set_active_state (Gtkmm2ext::ExplicitActive);
841 } else {
842 if (ms & MonitoringInput) {
843 monitor_input_button->set_active_state (Gtkmm2ext::ImplicitActive);
844 } else {
845 monitor_input_button->unset_active_state ();
846 }
847 }
848
849 if (t->monitoring_control()->monitoring_choice() & MonitorDisk) {
850 monitor_disk_button->set_active_state (Gtkmm2ext::ExplicitActive);
851 } else {
852 if (ms & MonitoringDisk) {
853 monitor_disk_button->set_active_state (Gtkmm2ext::ImplicitActive);
854 } else {
855 monitor_disk_button->unset_active_state ();
856 }
857 }
858 }
859
860 bool
monitor_input_press(GdkEventButton *)861 RouteUI::monitor_input_press(GdkEventButton*)
862 {
863 return false;
864 }
865
866 bool
monitor_input_release(GdkEventButton * ev)867 RouteUI::monitor_input_release(GdkEventButton* ev)
868 {
869 return monitor_release (ev, MonitorInput);
870 }
871
872 bool
monitor_disk_press(GdkEventButton *)873 RouteUI::monitor_disk_press (GdkEventButton*)
874 {
875 return false;
876 }
877
878 bool
monitor_disk_release(GdkEventButton * ev)879 RouteUI::monitor_disk_release (GdkEventButton* ev)
880 {
881 return monitor_release (ev, MonitorDisk);
882 }
883
884 bool
monitor_release(GdkEventButton * ev,MonitorChoice monitor_choice)885 RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
886 {
887 if (ev->button != 1) {
888 return false;
889 }
890
891 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
892
893 if (!t) {
894 return true;
895 }
896
897 MonitorChoice mc;
898 boost::shared_ptr<RouteList> rl;
899
900 if (t->monitoring_control()->monitoring_choice() & monitor_choice) {
901 mc = MonitorChoice (t->monitoring_control()->monitoring_choice() & ~monitor_choice);
902 } else {
903 mc = MonitorChoice (t->monitoring_control()->monitoring_choice() | monitor_choice);
904 }
905
906 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
907 /* Primary-Tertiary-click applies change to all routes */
908 rl = _session->get_routes ();
909 _session->set_controls (route_list_to_control_list (rl, &Stripable::monitoring_control), (double) mc, Controllable::NoGroup);
910 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
911 /* Primary-click overrides group */
912 rl.reset (new RouteList);
913 rl->push_back (route());
914 _session->set_controls (route_list_to_control_list (rl, &Stripable::monitoring_control), (double) mc, Controllable::InverseGroup);
915 } else {
916 rl.reset (new RouteList);
917 rl->push_back (route());
918 _session->set_controls (route_list_to_control_list (rl, &Stripable::monitoring_control), (double) mc, Controllable::UseGroup);
919 }
920
921 return false;
922 }
923
924 void
build_record_menu()925 RouteUI::build_record_menu ()
926 {
927 if (!_record_menu) {
928 _record_menu = new Menu;
929 _record_menu->set_name ("ArdourContextMenu");
930 using namespace Menu_Helpers;
931 MenuList& items = _record_menu->items();
932
933 items.push_back (CheckMenuElem (_("Rec-Safe"), sigc::mem_fun (*this, &RouteUI::toggle_rec_safe)));
934 _rec_safe_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
935
936 if (is_midi_track()) {
937 items.push_back (SeparatorElem());
938 items.push_back (CheckMenuElem (_("Step Entry"), sigc::mem_fun (*this, &RouteUI::toggle_step_edit)));
939 _step_edit_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
940 }
941 }
942
943 if (_step_edit_item) {
944 if (track()->rec_enable_control()->get_value()) {
945 _step_edit_item->set_sensitive (false);
946 }
947 _step_edit_item->set_active (midi_track()->step_editing());
948 }
949 if (_rec_safe_item) {
950 _rec_safe_item->set_sensitive (!_route->rec_enable_control()->get_value());
951 _rec_safe_item->set_active (_route->rec_safe_control()->get_value());
952 }
953 }
954
955 void
toggle_step_edit()956 RouteUI::toggle_step_edit ()
957 {
958 if (!is_midi_track() || track()->rec_enable_control()->get_value()) {
959 return;
960 }
961
962 midi_track()->set_step_editing (_step_edit_item->get_active());
963 }
964
965 void
toggle_rec_safe()966 RouteUI::toggle_rec_safe ()
967 {
968 boost::shared_ptr<AutomationControl> rs = _route->rec_safe_control();
969
970 if (!rs) {
971 return;
972 }
973
974 /* This check is made inside the control too, but dong it here can't
975 * hurt.
976 */
977
978 if (_route->rec_enable_control()->get_value()) {
979 return;
980 }
981
982 rs->set_value (_rec_safe_item->get_active (), Controllable::UseGroup);
983 }
984
985 void
step_edit_changed(bool yn)986 RouteUI::step_edit_changed (bool yn)
987 {
988 if (yn) {
989 if (rec_enable_button) {
990 rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive);
991 }
992
993 start_step_editing ();
994
995 if (_step_edit_item) {
996 _step_edit_item->set_active (true);
997 }
998
999 } else {
1000
1001 if (rec_enable_button) {
1002 rec_enable_button->unset_active_state ();
1003 }
1004
1005 stop_step_editing ();
1006
1007 if (_step_edit_item) {
1008 _step_edit_item->set_active (false);
1009 }
1010 }
1011 }
1012
1013 bool
rec_enable_release(GdkEventButton * ev)1014 RouteUI::rec_enable_release (GdkEventButton* ev)
1015 {
1016 if (Keyboard::is_context_menu_event (ev)) {
1017 build_record_menu ();
1018 if (_record_menu) {
1019 _record_menu->popup (1, ev->time);
1020 }
1021 return false;
1022 }
1023
1024 return false;
1025 }
1026
1027 void
build_sends_menu()1028 RouteUI::build_sends_menu ()
1029 {
1030 using namespace Menu_Helpers;
1031
1032 sends_menu = new Menu;
1033 sends_menu->set_name ("ArdourContextMenu");
1034 MenuList& items = sends_menu->items();
1035
1036 if (!is_foldbackbus ()) {
1037 items.push_back (
1038 MenuElem(_("Assign all tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, false))
1039 );
1040
1041 items.push_back (
1042 MenuElem(_("Assign all tracks and busses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, true))
1043 );
1044
1045 items.push_back (
1046 MenuElem(_("Assign all tracks (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PostFader, false))
1047 );
1048
1049 items.push_back (
1050 MenuElem(_("Assign all tracks and busses (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PostFader, true))
1051 );
1052 }
1053
1054 items.push_back (
1055 MenuElem(_("Assign selected tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, false))
1056 );
1057
1058 if (!is_foldbackbus ()) {
1059 items.push_back (
1060 MenuElem(_("Assign selected tracks and busses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, true)));
1061 }
1062
1063 items.push_back (
1064 MenuElem(_("Assign selected tracks (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PostFader, false))
1065 );
1066
1067 if (!is_foldbackbus ()) {
1068 items.push_back (
1069 MenuElem(_("Assign selected tracks and busses (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PostFader, true))
1070 );
1071 }
1072
1073 items.push_back (SeparatorElem());
1074
1075 items.push_back (MenuElem(_("Copy track/bus gains to sends"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_from_track)));
1076 items.push_back (MenuElem(_("Set sends gain to -inf"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_zero)));
1077 items.push_back (MenuElem(_("Set sends gain to 0dB"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_unity)));
1078
1079 }
1080
1081 void
create_sends(Placement p,bool include_buses)1082 RouteUI::create_sends (Placement p, bool include_buses)
1083 {
1084 _session->globally_add_internal_sends (_route, p, include_buses);
1085 }
1086
1087 void
create_selected_sends(Placement p,bool include_buses)1088 RouteUI::create_selected_sends (Placement p, bool include_buses)
1089 {
1090 boost::shared_ptr<RouteList> rlist (new RouteList);
1091 TrackSelection& selected_tracks (ARDOUR_UI::instance()->the_editor().get_selection().tracks);
1092
1093 for (TrackSelection::iterator i = selected_tracks.begin(); i != selected_tracks.end(); ++i) {
1094 RouteTimeAxisView* rtv;
1095 RouteUI* rui;
1096 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
1097 if ((rui = dynamic_cast<RouteUI*>(rtv)) != 0) {
1098 if (include_buses || boost::dynamic_pointer_cast<AudioTrack>(rui->route())) {
1099 rlist->push_back (rui->route());
1100 }
1101 }
1102 }
1103 }
1104
1105 _session->add_internal_sends (_route, p, rlist);
1106 }
1107
1108 void
set_sends_gain_from_track()1109 RouteUI::set_sends_gain_from_track ()
1110 {
1111 _session->globally_set_send_gains_from_track (_route);
1112 }
1113
1114 void
set_sends_gain_to_zero()1115 RouteUI::set_sends_gain_to_zero ()
1116 {
1117 _session->globally_set_send_gains_to_zero (_route);
1118 }
1119
1120 void
set_sends_gain_to_unity()1121 RouteUI::set_sends_gain_to_unity ()
1122 {
1123 _session->globally_set_send_gains_to_unity (_route);
1124 }
1125
1126 bool
show_sends_press(GdkEventButton * ev)1127 RouteUI::show_sends_press(GdkEventButton* ev)
1128 {
1129 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
1130 return true;
1131 }
1132
1133 if (!is_track() && show_sends_button) {
1134
1135 if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1136
1137 // do nothing on midi sigc::bind event
1138 return false;
1139
1140 } else if (Keyboard::is_context_menu_event (ev)) {
1141
1142 if (sends_menu == 0) {
1143 build_sends_menu ();
1144 }
1145
1146 sends_menu->popup (0, ev->time);
1147
1148 } else if (ev->button == 1) {
1149
1150 boost::shared_ptr<Route> s = _showing_sends_to.lock ();
1151
1152 if (s == _route) {
1153 set_showing_sends_to (boost::shared_ptr<Route> ());
1154 Mixer_UI::instance()->show_spill (boost::shared_ptr<ARDOUR::Stripable>());
1155 } else {
1156 set_showing_sends_to (_route);
1157 Mixer_UI::instance()->show_spill (_route);
1158 }
1159 }
1160 return true;
1161 }
1162
1163 return false;
1164 }
1165
1166 bool
show_sends_release(GdkEventButton *)1167 RouteUI::show_sends_release (GdkEventButton*)
1168 {
1169 return true;
1170 }
1171
1172 void
send_blink(bool onoff)1173 RouteUI::send_blink (bool onoff)
1174 {
1175 if (!show_sends_button) {
1176 return;
1177 }
1178
1179 if (onoff) {
1180 show_sends_button->set_active_state (Gtkmm2ext::ExplicitActive);
1181 } else {
1182 show_sends_button->unset_active_state ();
1183 }
1184 }
1185
1186 Gtkmm2ext::ActiveState
solo_active_state(boost::shared_ptr<Stripable> s)1187 RouteUI::solo_active_state (boost::shared_ptr<Stripable> s)
1188 {
1189 boost::shared_ptr<SoloControl> sc = s->solo_control();
1190
1191 if (!sc) {
1192 return Gtkmm2ext::Off;
1193 }
1194
1195 if (!sc->can_solo()) {
1196 return Gtkmm2ext::Off;
1197 }
1198
1199
1200 if (sc->self_soloed()) {
1201 return Gtkmm2ext::ExplicitActive;
1202 } else if (sc->soloed_by_others()) {
1203 return Gtkmm2ext::ImplicitActive;
1204 } else {
1205 return Gtkmm2ext::Off;
1206 }
1207 }
1208
1209 Gtkmm2ext::ActiveState
solo_isolate_active_state(boost::shared_ptr<Stripable> s)1210 RouteUI::solo_isolate_active_state (boost::shared_ptr<Stripable> s)
1211 {
1212 boost::shared_ptr<SoloIsolateControl> sc = s->solo_isolate_control();
1213
1214 if (!sc) {
1215 return Gtkmm2ext::Off;
1216 }
1217
1218 if (s->is_master() || s->is_monitor()) {
1219 return Gtkmm2ext::Off;
1220 }
1221
1222 if (sc->solo_isolated()) {
1223 return Gtkmm2ext::ExplicitActive;
1224 } else {
1225 return Gtkmm2ext::Off;
1226 }
1227 }
1228
1229 Gtkmm2ext::ActiveState
solo_safe_active_state(boost::shared_ptr<Stripable> s)1230 RouteUI::solo_safe_active_state (boost::shared_ptr<Stripable> s)
1231 {
1232 boost::shared_ptr<SoloSafeControl> sc = s->solo_safe_control();
1233
1234 if (!sc) {
1235 return Gtkmm2ext::Off;
1236 }
1237
1238 if (s->is_master() || s->is_monitor()) {
1239 return Gtkmm2ext::Off;
1240 }
1241
1242 if (sc->solo_safe()) {
1243 return Gtkmm2ext::ExplicitActive;
1244 } else {
1245 return Gtkmm2ext::Off;
1246 }
1247 }
1248
1249 void
update_solo_display()1250 RouteUI::update_solo_display ()
1251 {
1252 bool yn = _route->solo_safe_control()->solo_safe ();
1253
1254 if (solo_safe_check && solo_safe_check->get_active() != yn) {
1255 solo_safe_check->set_active (yn);
1256 }
1257
1258 yn = _route->solo_isolate_control()->solo_isolated ();
1259
1260 if (solo_isolated_check && solo_isolated_check->get_active() != yn) {
1261 solo_isolated_check->set_active (yn);
1262 }
1263
1264 set_button_names ();
1265
1266 if (solo_isolated_led) {
1267 if (_route->solo_isolate_control()->solo_isolated()) {
1268 solo_isolated_led->set_active_state (Gtkmm2ext::ExplicitActive);
1269 } else {
1270 solo_isolated_led->unset_active_state ();
1271 }
1272 }
1273
1274 if (solo_safe_led) {
1275 if (_route->solo_safe_control()->solo_safe()) {
1276 solo_safe_led->set_active_state (Gtkmm2ext::ExplicitActive);
1277 } else {
1278 solo_safe_led->unset_active_state ();
1279 }
1280 }
1281
1282 solo_button->set_active_state (solo_active_state (_route));
1283
1284 /* some changes to solo status can affect mute display, so catch up
1285 */
1286
1287 update_mute_display ();
1288 }
1289
1290 void
solo_changed_so_update_mute()1291 RouteUI::solo_changed_so_update_mute ()
1292 {
1293 update_mute_display ();
1294 }
1295
1296 ActiveState
mute_active_state(Session *,boost::shared_ptr<Stripable> s)1297 RouteUI::mute_active_state (Session*, boost::shared_ptr<Stripable> s)
1298 {
1299 boost::shared_ptr<MuteControl> mc = s->mute_control();
1300
1301 if (s->is_monitor()) {
1302 return Gtkmm2ext::Off;
1303 }
1304
1305 if (!mc) {
1306 return Gtkmm2ext::Off;
1307 }
1308
1309 if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
1310
1311 if (mc->muted_by_self ()) {
1312 /* full mute */
1313 return Gtkmm2ext::ExplicitActive;
1314 } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
1315 /* this will reflect both solo mutes AND master mutes */
1316 return Gtkmm2ext::ImplicitActive;
1317 } else {
1318 /* no mute at all */
1319 return Gtkmm2ext::Off;
1320 }
1321
1322 } else {
1323
1324 if (mc->muted_by_self()) {
1325 /* full mute */
1326 return Gtkmm2ext::ExplicitActive;
1327 } else if (mc->muted_by_masters ()) {
1328 /* this shows only master mutes, not mute-by-others-soloing */
1329 return Gtkmm2ext::ImplicitActive;
1330 } else {
1331 /* no mute at all */
1332 return Gtkmm2ext::Off;
1333 }
1334 }
1335
1336 return ActiveState(0);
1337 }
1338
1339 void
update_mute_display()1340 RouteUI::update_mute_display ()
1341 {
1342 if (!_route) {
1343 return;
1344 }
1345
1346 mute_button->set_active_state (mute_active_state (_session, _route));
1347 }
1348
1349
1350 void
route_rec_enable_changed()1351 RouteUI::route_rec_enable_changed ()
1352 {
1353 blink_rec_display (true); //this lets the button change "immediately" rather than wait for the next blink
1354 }
1355
1356 void
session_rec_enable_changed()1357 RouteUI::session_rec_enable_changed ()
1358 {
1359 blink_rec_display (true); //this lets the button change "immediately" rather than wait for the next blink
1360 }
1361
1362 void
blink_rec_display(bool blinkOn)1363 RouteUI::blink_rec_display (bool blinkOn)
1364 {
1365 if (!rec_enable_button || !_route) {
1366 return;
1367 }
1368
1369 if (boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1370 return;
1371 }
1372
1373 if (!is_track()) {
1374 return;
1375 }
1376
1377 if (track()->rec_enable_control()->get_value()) {
1378 switch (_session->record_status ()) {
1379 case Session::Recording:
1380 rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive);
1381 break;
1382
1383 case Session::Disabled:
1384 case Session::Enabled:
1385 if (UIConfiguration::instance().get_blink_rec_arm()) {
1386 rec_enable_button->set_active_state ( blinkOn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off );
1387 } else {
1388 rec_enable_button->set_active_state ( ImplicitActive );
1389 }
1390 break;
1391 }
1392
1393 if (_step_edit_item) {
1394 _step_edit_item->set_sensitive (false);
1395 }
1396
1397 } else {
1398 rec_enable_button->unset_active_state ();
1399
1400 if (_step_edit_item) {
1401 _step_edit_item->set_sensitive (true);
1402 }
1403 }
1404
1405 check_rec_enable_sensitivity ();
1406 }
1407
1408 void
build_solo_menu(void)1409 RouteUI::build_solo_menu (void)
1410 {
1411 using namespace Menu_Helpers;
1412
1413 solo_menu = new Menu;
1414 solo_menu->set_name ("ArdourContextMenu");
1415 MenuList& items = solo_menu->items();
1416 Gtk::CheckMenuItem* check;
1417
1418 check = new Gtk::CheckMenuItem(_("Solo Isolate"));
1419 check->set_active (_route->solo_isolate_control()->solo_isolated());
1420 check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_isolated), check));
1421 items.push_back (CheckMenuElem(*check));
1422 solo_isolated_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
1423 check->show_all();
1424
1425 check = new Gtk::CheckMenuItem(_("Solo Safe"));
1426 check->set_active (_route->solo_safe_control()->solo_safe());
1427 check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_safe), check));
1428 items.push_back (CheckMenuElem(*check));
1429 solo_safe_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
1430 check->show_all();
1431 }
1432
1433 void
build_mute_menu(void)1434 RouteUI::build_mute_menu(void)
1435 {
1436 using namespace Menu_Helpers;
1437
1438 mute_menu = new Menu;
1439 mute_menu->set_name ("ArdourContextMenu");
1440
1441 MenuList& items = mute_menu->items();
1442
1443 pre_fader_mute_check = manage (new Gtk::CheckMenuItem(_("Pre Fader Sends")));
1444 init_mute_menu(MuteMaster::PreFader, pre_fader_mute_check);
1445 pre_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PreFader, pre_fader_mute_check));
1446 items.push_back (CheckMenuElem(*pre_fader_mute_check));
1447 pre_fader_mute_check->show_all();
1448
1449 post_fader_mute_check = manage (new Gtk::CheckMenuItem(_("Post Fader Sends")));
1450 init_mute_menu(MuteMaster::PostFader, post_fader_mute_check);
1451 post_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PostFader, post_fader_mute_check));
1452 items.push_back (CheckMenuElem(*post_fader_mute_check));
1453 post_fader_mute_check->show_all();
1454
1455 listen_mute_check = manage (new Gtk::CheckMenuItem(_("Control Outs")));
1456 init_mute_menu(MuteMaster::Listen, listen_mute_check);
1457 listen_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Listen, listen_mute_check));
1458 items.push_back (CheckMenuElem(*listen_mute_check));
1459 listen_mute_check->show_all();
1460
1461 main_mute_check = manage (new Gtk::CheckMenuItem(_("Main Outs")));
1462 init_mute_menu(MuteMaster::Main, main_mute_check);
1463 main_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Main, main_mute_check));
1464 items.push_back (CheckMenuElem(*main_mute_check));
1465 main_mute_check->show_all();
1466
1467 _route->mute_points_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::muting_change, this), gui_context());
1468 }
1469
1470 void
init_mute_menu(MuteMaster::MutePoint mp,Gtk::CheckMenuItem * check)1471 RouteUI::init_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
1472 {
1473 check->set_active (_route->mute_control()->mute_points() & mp);
1474 }
1475
1476 void
toggle_mute_menu(MuteMaster::MutePoint mp,Gtk::CheckMenuItem * check)1477 RouteUI::toggle_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
1478 {
1479 if (check->get_active()) {
1480 _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() | mp));
1481 } else {
1482 _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() & ~mp));
1483 }
1484 }
1485
1486 void
muting_change()1487 RouteUI::muting_change ()
1488 {
1489 ENSURE_GUI_THREAD (*this, &RouteUI::muting_change)
1490
1491 bool yn;
1492 MuteMaster::MutePoint current = _route->mute_control()->mute_points ();
1493
1494 yn = (current & MuteMaster::PreFader);
1495
1496 if (pre_fader_mute_check->get_active() != yn) {
1497 pre_fader_mute_check->set_active (yn);
1498 }
1499
1500 yn = (current & MuteMaster::PostFader);
1501
1502 if (post_fader_mute_check->get_active() != yn) {
1503 post_fader_mute_check->set_active (yn);
1504 }
1505
1506 yn = (current & MuteMaster::Listen);
1507
1508 if (listen_mute_check->get_active() != yn) {
1509 listen_mute_check->set_active (yn);
1510 }
1511
1512 yn = (current & MuteMaster::Main);
1513
1514 if (main_mute_check->get_active() != yn) {
1515 main_mute_check->set_active (yn);
1516 }
1517 }
1518
1519 bool
solo_isolate_button_release(GdkEventButton * ev)1520 RouteUI::solo_isolate_button_release (GdkEventButton* ev)
1521 {
1522 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
1523 return true;
1524 }
1525
1526 bool view = solo_isolated_led->active_state();
1527 bool model = _route->solo_isolate_control()->solo_isolated();
1528
1529 /* called BEFORE the view has changed */
1530
1531 if (ev->button == 1) {
1532 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1533
1534 if (model) {
1535 /* disable isolate for all routes */
1536 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_isolate_control), 0.0, Controllable::NoGroup);
1537 } else {
1538 /* enable isolate for all routes */
1539 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_isolate_control), 1.0, Controllable::NoGroup);
1540 }
1541
1542 } else {
1543
1544 if (model == view) {
1545
1546 /* flip just this route */
1547
1548 boost::shared_ptr<RouteList> rl (new RouteList);
1549 rl->push_back (_route);
1550 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_isolate_control), view ? 0.0 : 1.0, Controllable::NoGroup);
1551 }
1552 }
1553 }
1554
1555 return false;
1556 }
1557
1558 bool
solo_safe_button_release(GdkEventButton * ev)1559 RouteUI::solo_safe_button_release (GdkEventButton* ev)
1560 {
1561 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
1562 return true;
1563 }
1564
1565 bool view = solo_safe_led->active_state();
1566 bool model = _route->solo_safe_control()->solo_safe();
1567
1568 if (ev->button == 1) {
1569 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1570 boost::shared_ptr<RouteList> rl (_session->get_routes());
1571 if (model) {
1572 /* disable solo safe for all routes */
1573 DisplaySuspender ds;
1574 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
1575 (*i)->solo_safe_control()->set_value (0.0, Controllable::NoGroup);
1576 }
1577 } else {
1578 /* enable solo safe for all routes */
1579 DisplaySuspender ds;
1580 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
1581 (*i)->solo_safe_control()->set_value (1.0, Controllable::NoGroup);
1582 }
1583 }
1584 }
1585 else {
1586 if (model == view) {
1587 /* flip just this route */
1588 _route->solo_safe_control()->set_value (view ? 0.0 : 1.0, Controllable::NoGroup);
1589 }
1590 }
1591 }
1592
1593 return false;
1594 }
1595
1596 void
toggle_solo_isolated(Gtk::CheckMenuItem * check)1597 RouteUI::toggle_solo_isolated (Gtk::CheckMenuItem* check)
1598 {
1599 bool view = check->get_active();
1600 bool model = _route->solo_isolate_control()->solo_isolated();
1601
1602 /* called AFTER the view has changed */
1603
1604 if (model != view) {
1605 _route->solo_isolate_control()->set_value (view ? 1.0 : 0.0, Controllable::UseGroup);
1606 }
1607 }
1608
1609 void
toggle_solo_safe(Gtk::CheckMenuItem * check)1610 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
1611 {
1612 _route->solo_safe_control()->set_value (check->get_active() ? 1.0 : 0.0, Controllable::UseGroup);
1613 }
1614
1615 void
delete_patch_change_dialog()1616 RouteUI::delete_patch_change_dialog ()
1617 {
1618 if (!_route) {
1619 return;
1620 }
1621 delete _route->patch_selector_dialog ();
1622 _route->set_patch_selector_dialog (0);
1623 }
1624
1625 PatchChangeGridDialog*
patch_change_dialog() const1626 RouteUI::patch_change_dialog () const
1627 {
1628 return _route->patch_selector_dialog ();
1629 }
1630
1631 void
select_midi_patch()1632 RouteUI::select_midi_patch ()
1633 {
1634 if (patch_change_dialog ()) {
1635 patch_change_dialog()->present ();
1636 return;
1637 }
1638
1639 /* note: RouteTimeAxisView is resoponsible to updating
1640 * the Dialog (PatchChangeGridDialog::refresh())
1641 * when the midnam model changes.
1642 */
1643 PatchChangeGridDialog* d = new PatchChangeGridDialog (_route);
1644 _route->set_patch_selector_dialog (d);
1645 d->present ();
1646 }
1647
1648 /** Ask the user to choose a colour, and then apply that color to my route */
1649 void
choose_color()1650 RouteUI::choose_color ()
1651 {
1652 _color_picker.popup (_route);
1653 }
1654
1655 /** Set the route's own color. This may not be used for display if
1656 * the route is in a group which shares its color with its routes.
1657 */
1658 void
set_color(uint32_t c)1659 RouteUI::set_color (uint32_t c)
1660 {
1661 _route->presentation_info().set_color (c);
1662 }
1663
1664 /** @return GUI state ID for things that are common to the route in all its representations */
1665 string
route_state_id() const1666 RouteUI::route_state_id () const
1667 {
1668 return string_compose (X_("route %1"), _route->id().to_s());
1669 }
1670
1671 int
set_color_from_route()1672 RouteUI::set_color_from_route ()
1673 {
1674 if (_route->presentation_info().color_set()) {
1675 return 0; /* nothing to do */
1676 }
1677
1678 return 1; /* pick a color */
1679 }
1680
1681 /** @return true if this name should be used for the route, otherwise false */
1682 bool
verify_new_route_name(const std::string & name)1683 RouteUI::verify_new_route_name (const std::string& name)
1684 {
1685 if (name.find (':') == string::npos) {
1686 return true;
1687 }
1688
1689 MessageDialog colon_msg (
1690 _("The use of colons (':') is discouraged in track and bus names.\nDo you want to use this new name?"),
1691 false, MESSAGE_QUESTION, BUTTONS_NONE
1692 );
1693
1694 colon_msg.add_button (_("Use the new name"), Gtk::RESPONSE_ACCEPT);
1695 colon_msg.add_button (_("Re-edit the name"), Gtk::RESPONSE_CANCEL);
1696
1697 return (colon_msg.run () == Gtk::RESPONSE_ACCEPT);
1698 }
1699
1700 void
route_rename()1701 RouteUI::route_rename ()
1702 {
1703 ArdourWidgets::Prompter name_prompter (true);
1704 string result;
1705 bool done = false;
1706
1707 if (is_track()) {
1708 name_prompter.set_title (_("Rename Track"));
1709 } else {
1710 name_prompter.set_title (_("Rename Bus"));
1711 }
1712 name_prompter.set_prompt (_("New name:"));
1713 name_prompter.set_initial_text (_route->name());
1714 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1715 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1716 name_prompter.show_all ();
1717
1718 while (!done) {
1719 switch (name_prompter.run ()) {
1720 case Gtk::RESPONSE_ACCEPT:
1721 name_prompter.get_result (result);
1722 name_prompter.hide ();
1723 if (result.length()) {
1724 if (verify_new_route_name (result)) {
1725 _route->set_name (result);
1726 done = true;
1727 } else {
1728 /* back to name prompter */
1729 }
1730
1731 } else {
1732 /* nothing entered, just get out of here */
1733 done = true;
1734 }
1735 break;
1736 default:
1737 done = true;
1738 break;
1739 }
1740 }
1741
1742 return;
1743
1744 }
1745
1746 void
toggle_comment_editor()1747 RouteUI::toggle_comment_editor ()
1748 {
1749 if (_comment_window && _comment_window->is_visible ()) {
1750 _comment_window->hide ();
1751 } else {
1752 open_comment_editor ();
1753 }
1754 }
1755
1756
1757 void
open_comment_editor()1758 RouteUI::open_comment_editor ()
1759 {
1760 if (_comment_window == 0) {
1761 setup_comment_editor ();
1762 }
1763
1764 string title;
1765 title = _route->name();
1766 title += _(": comment editor");
1767
1768 _comment_window->set_title (title);
1769 _comment_window->present();
1770 }
1771
1772 void
setup_comment_editor()1773 RouteUI::setup_comment_editor ()
1774 {
1775 _comment_window = new ArdourWindow (""); // title will be reset to show route
1776 _comment_window->set_skip_taskbar_hint (true);
1777 _comment_window->signal_hide().connect (sigc::mem_fun(*this, &MixerStrip::comment_editor_done_editing));
1778 _comment_window->set_default_size (400, 200);
1779
1780 _comment_area = manage (new TextView());
1781 _comment_area->set_name ("MixerTrackCommentArea");
1782 _comment_area->set_wrap_mode (WRAP_WORD);
1783 _comment_area->set_editable (true);
1784 _comment_area->get_buffer()->set_text (_route->comment());
1785 _comment_area->show ();
1786
1787 _comment_window->add (*_comment_area);
1788 }
1789
1790 void
comment_changed()1791 RouteUI::comment_changed ()
1792 {
1793 _ignore_comment_edit = true;
1794 if (_comment_area) {
1795 _comment_area->get_buffer()->set_text (_route->comment());
1796 }
1797 _ignore_comment_edit = false;
1798 }
1799
1800 void
comment_editor_done_editing()1801 RouteUI::comment_editor_done_editing ()
1802 {
1803 ENSURE_GUI_THREAD (*this, &MixerStrip::comment_editor_done_editing, src)
1804
1805 string const str = _comment_area->get_buffer()->get_text();
1806 if (str == _route->comment ()) {
1807 return;
1808 }
1809
1810 _route->set_comment (str, this);
1811 }
1812
1813 void
set_route_active(bool a,bool apply_to_selection)1814 RouteUI::set_route_active (bool a, bool apply_to_selection)
1815 {
1816 if (apply_to_selection) {
1817 ARDOUR_UI::instance()->the_editor().get_selection().tracks.foreach_route_ui (boost::bind (&RouteUI::set_route_active, _1, a, false));
1818 } else {
1819 _route->set_active (a, this);
1820 }
1821 }
1822
1823 void
duplicate_selected_routes()1824 RouteUI::duplicate_selected_routes ()
1825 {
1826 ARDOUR_UI::instance()->start_duplicate_routes();
1827 }
1828
1829 void
toggle_denormal_protection()1830 RouteUI::toggle_denormal_protection ()
1831 {
1832 if (denormal_menu_item) {
1833
1834 bool x;
1835
1836 ENSURE_GUI_THREAD (*this, &RouteUI::toggle_denormal_protection)
1837
1838 if ((x = denormal_menu_item->get_active()) != _route->denormal_protection()) {
1839 _route->set_denormal_protection (x);
1840 }
1841 }
1842 }
1843
1844 void
denormal_protection_changed()1845 RouteUI::denormal_protection_changed ()
1846 {
1847 if (denormal_menu_item) {
1848 denormal_menu_item->set_active (_route->denormal_protection());
1849 }
1850 }
1851
1852 void
disconnect_input()1853 RouteUI::disconnect_input ()
1854 {
1855 _route->input()->disconnect (this);
1856 }
1857
1858 void
disconnect_output()1859 RouteUI::disconnect_output ()
1860 {
1861 _route->output()->disconnect (this);
1862 }
1863
1864 bool
is_track() const1865 RouteUI::is_track () const
1866 {
1867 return boost::dynamic_pointer_cast<Track>(_route) != 0;
1868 }
1869
1870 bool
is_master() const1871 RouteUI::is_master () const
1872 {
1873 return _route && _route->is_master ();
1874 }
1875
1876 bool
is_foldbackbus() const1877 RouteUI::is_foldbackbus () const
1878 {
1879 return _route && _route->is_foldbackbus ();
1880 }
1881
1882 boost::shared_ptr<Track>
track() const1883 RouteUI::track() const
1884 {
1885 return boost::dynamic_pointer_cast<Track>(_route);
1886 }
1887
1888 bool
is_audio_track() const1889 RouteUI::is_audio_track () const
1890 {
1891 return boost::dynamic_pointer_cast<AudioTrack>(_route) != 0;
1892 }
1893
1894 boost::shared_ptr<AudioTrack>
audio_track() const1895 RouteUI::audio_track() const
1896 {
1897 return boost::dynamic_pointer_cast<AudioTrack>(_route);
1898 }
1899
1900 bool
is_midi_track() const1901 RouteUI::is_midi_track () const
1902 {
1903 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1904 }
1905
1906 boost::shared_ptr<MidiTrack>
midi_track() const1907 RouteUI::midi_track() const
1908 {
1909 return boost::dynamic_pointer_cast<MidiTrack>(_route);
1910 }
1911
1912 bool
has_audio_outputs() const1913 RouteUI::has_audio_outputs () const
1914 {
1915 return (_route->n_outputs().n_audio() > 0);
1916 }
1917
1918 void
map_frozen()1919 RouteUI::map_frozen ()
1920 {
1921 ENSURE_GUI_THREAD (*this, &RouteUI::map_frozen)
1922
1923 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
1924
1925 if (at) {
1926 check_rec_enable_sensitivity ();
1927 }
1928 }
1929
1930 void
save_as_template_dialog_response(int response,SaveTemplateDialog * d)1931 RouteUI::save_as_template_dialog_response (int response, SaveTemplateDialog* d)
1932 {
1933 if (response == RESPONSE_ACCEPT) {
1934 const string name = d->get_template_name ();
1935 const string desc = d->get_description ();
1936 const string path = Glib::build_filename(ARDOUR::user_route_template_directory (), name + ARDOUR::template_suffix);
1937
1938 if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { /* file already exists. */
1939 bool overwrite = overwrite_file_dialog (*d,
1940 _("Confirm Template Overwrite"),
1941 _("A template already exists with that name. Do you want to overwrite it?"));
1942
1943 if (!overwrite) {
1944 d->show ();
1945 return;
1946 }
1947 }
1948 _route->save_as_template (path, name, desc);
1949 }
1950
1951 delete d;
1952 }
1953
1954 void
save_as_template()1955 RouteUI::save_as_template ()
1956 {
1957 const std::string dir = ARDOUR::user_route_template_directory ();
1958
1959 if (g_mkdir_with_parents (dir.c_str(), 0755)) {
1960 error << string_compose (_("Cannot create template directory %1"), dir) << endmsg;
1961 return;
1962 }
1963
1964 SaveTemplateDialog* d = new SaveTemplateDialog (_route->name(), _route->comment());
1965 d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::save_as_template_dialog_response), d));
1966 d->show ();
1967 }
1968
1969 void
check_rec_enable_sensitivity()1970 RouteUI::check_rec_enable_sensitivity ()
1971 {
1972 if (!rec_enable_button) {
1973 assert (0); // This should not happen
1974 return;
1975 }
1976 if (!_session->writable()) {
1977 rec_enable_button->set_sensitive (false);
1978 return;
1979 }
1980
1981 if (_session->transport_rolling() && rec_enable_button->active_state() && Config->get_disable_disarm_during_roll()) {
1982 rec_enable_button->set_sensitive (false);
1983 } else if (is_audio_track () && track()->freeze_state() == AudioTrack::Frozen) {
1984 rec_enable_button->set_sensitive (false);
1985 } else {
1986 rec_enable_button->set_sensitive (true);
1987 }
1988 if (_route && _route->rec_safe_control () && _route->rec_safe_control()->get_value()) {
1989 rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
1990 } else {
1991 rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
1992 }
1993 update_monitoring_display ();
1994 }
1995
1996 void
update_solo_button()1997 RouteUI::update_solo_button ()
1998 {
1999 set_button_names ();
2000
2001 if (Config->get_solo_control_is_listen_control()) {
2002 UI::instance()->set_tip (solo_button, _("Listen to this track"), "");
2003 } else {
2004 UI::instance()->set_tip (solo_button, _("Mute other (non-soloed) tracks"), "");
2005 }
2006 }
2007
2008 void
parameter_changed(string const & p)2009 RouteUI::parameter_changed (string const & p)
2010 {
2011 /* this handles RC and per-session parameter changes */
2012
2013 if (p == "disable-disarm-during-roll") {
2014 check_rec_enable_sensitivity ();
2015 } else if (p == "solo-control-is-listen-control" || p == "listen-position") {
2016 update_solo_button ();
2017 } else if (p == "session-monitoring") {
2018 update_monitoring_display ();
2019 } else if (p == "auto-input") {
2020 update_monitoring_display ();
2021 } else if (p == "layered-record-mode") {
2022 update_monitoring_display ();
2023 } else if (p == "auto-input-does-talkback") {
2024 update_monitoring_display ();
2025 } else if (p == "blink-rec-arm") {
2026 if (UIConfiguration::instance().get_blink_rec_arm()) {
2027 rec_blink_connection.disconnect ();
2028 rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::blink_rec_display));
2029 } else {
2030 rec_blink_connection.disconnect ();
2031 RouteUI::blink_rec_display(false);
2032 }
2033 }
2034 }
2035
2036 void
setup_invert_buttons()2037 RouteUI::setup_invert_buttons ()
2038 {
2039 uint32_t const N = _route ? _route->phase_control()->size() : 0;
2040
2041 if (_n_polarity_invert == N) {
2042 /* buttons are already setup for this strip, but we should still set the values */
2043 update_polarity_display ();
2044 return;
2045 }
2046 _n_polarity_invert = N;
2047
2048 /* remove old invert buttons */
2049 for (vector<ArdourButton*>::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i) {
2050 invert_button_box.remove (**i);
2051 }
2052
2053 _invert_buttons.clear ();
2054
2055 if (N == 0) {
2056 return;
2057 }
2058
2059 uint32_t const to_add = (N <= _max_invert_buttons) ? N : 1;
2060
2061 for (uint32_t i = 0; i < to_add; ++i) {
2062 ArdourButton* b = manage (new ArdourButton);
2063 b->signal_button_press_event().connect (sigc::mem_fun (*this, &RouteUI::invert_press), false);
2064 b->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_release), i), false);
2065
2066 b->set_name (X_("invert button"));
2067 if (to_add == 1) {
2068 if (N > 1) {
2069 b->set_text (string_compose (X_("Ø (%1)"), N));
2070 } else {
2071 b->set_text (X_("Ø"));
2072 }
2073 } else {
2074 b->set_text (string_compose (X_("Ø%1"), i + 1));
2075 }
2076
2077 if (N <= _max_invert_buttons) {
2078 UI::instance()->set_tip (*b, string_compose (_("Left-click to invert polarity of channel %1 of this track. Right-click to show menu."), i + 1));
2079 } else {
2080 UI::instance()->set_tip (*b, _("Click to show a menu of channels to invert polarity"));
2081 }
2082
2083 _invert_buttons.push_back (b);
2084 invert_button_box.pack_start (*b);
2085 }
2086
2087 invert_button_box.set_spacing (1);
2088 invert_button_box.show_all ();
2089
2090 update_polarity_display ();
2091 }
2092
2093 void
update_polarity_display()2094 RouteUI::update_polarity_display ()
2095 {
2096 if (!_route) {
2097 return;
2098 }
2099
2100 uint32_t const N = _route->phase_control()->size();
2101 if (N > _max_invert_buttons) {
2102
2103 /* One button for many channels; explicit active if all channels are inverted,
2104 implicit active if some are, off if none are.
2105 */
2106
2107 ArdourButton* b = _invert_buttons.front ();
2108
2109 if (_route->phase_control()->count() == _route->phase_control()->size()) {
2110 b->set_active_state (Gtkmm2ext::ExplicitActive);
2111 } else if (_route->phase_control()->any()) {
2112 b->set_active_state (Gtkmm2ext::ImplicitActive);
2113 } else {
2114 b->set_active_state (Gtkmm2ext::Off);
2115 }
2116
2117 } else {
2118
2119 /* One button per channel; just set active */
2120
2121 int j = 0;
2122 for (vector<ArdourButton*>::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i, ++j) {
2123 (*i)->set_active (_route->phase_control()->inverted (j));
2124 }
2125
2126 }
2127 }
2128
2129 bool
invert_release(GdkEventButton * ev,uint32_t i)2130 RouteUI::invert_release (GdkEventButton* ev, uint32_t i)
2131 {
2132 if (ev->button == 1 && i < _invert_buttons.size()) {
2133 uint32_t const N = _route->phase_control()->size();
2134 if (N <= _max_invert_buttons) {
2135 /* left-click inverts phase so long as we have a button per channel */
2136 _route->phase_control()->set_phase_invert (i, !_invert_buttons[i]->get_active());
2137 return false;
2138 }
2139 }
2140 return false;
2141 }
2142
2143 bool
invert_press(GdkEventButton * ev)2144 RouteUI::invert_press (GdkEventButton* ev)
2145 {
2146 using namespace Menu_Helpers;
2147
2148 uint32_t const N = _route->phase_control()->size();
2149 if (N <= _max_invert_buttons && ev->button != 3) {
2150 /* If we have an invert button per channel, we only pop
2151 up a menu on right-click; left click is handled
2152 on release.
2153 */
2154 return false;
2155 }
2156
2157 delete _invert_menu;
2158 _invert_menu = new Menu;
2159 _invert_menu->set_name ("ArdourContextMenu");
2160 MenuList& items = _invert_menu->items ();
2161
2162 for (uint32_t i = 0; i < N; ++i) {
2163 items.push_back (CheckMenuElem (string_compose (X_("Ø%1"), i + 1), sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_menu_toggled), i)));
2164 Gtk::CheckMenuItem* e = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
2165 ++_i_am_the_modifier;
2166 e->set_active (_route->phase_control()->inverted (i));
2167 --_i_am_the_modifier;
2168 }
2169
2170 _invert_menu->popup (0, ev->time);
2171
2172 return true;
2173 }
2174
2175 void
invert_menu_toggled(uint32_t c)2176 RouteUI::invert_menu_toggled (uint32_t c)
2177 {
2178 if (_i_am_the_modifier) {
2179 return;
2180 }
2181
2182 _route->phase_control()->set_phase_invert (c, !_route->phase_control()->inverted (c));
2183 }
2184
2185 void
set_invert_sensitive(bool yn)2186 RouteUI::set_invert_sensitive (bool yn)
2187 {
2188 for (vector<ArdourButton*>::iterator b = _invert_buttons.begin(); b != _invert_buttons.end(); ++b) {
2189 (*b)->set_sensitive (yn);
2190 }
2191 }
2192
2193 /** The Route's gui_changed signal has been emitted */
2194 void
route_gui_changed(PropertyChange const & what_changed)2195 RouteUI::route_gui_changed (PropertyChange const& what_changed)
2196 {
2197 if (what_changed.contains (Properties::color)) {
2198 if (set_color_from_route () == 0) {
2199 route_color_changed ();
2200 }
2201 }
2202 }
2203
2204 void
track_mode_changed(void)2205 RouteUI::track_mode_changed (void)
2206 {
2207 assert(is_track());
2208 rec_enable_button->set_icon (ArdourIcon::RecButton);
2209 rec_enable_button->queue_draw();
2210 }
2211
2212 /** @return the color that this route should use; it maybe its own,
2213 * or it maybe that of its route group.
2214 */
2215 Gdk::Color
route_color() const2216 RouteUI::route_color () const
2217 {
2218 Gdk::Color c;
2219 RouteGroup* g = _route->route_group ();
2220 string p;
2221
2222 if (g && g->is_color()) {
2223 set_color_from_rgba (c, GroupTabs::group_color (g));
2224 } else {
2225 set_color_from_rgba (c, _route->presentation_info().color());
2226 }
2227
2228 return c;
2229 }
2230
2231 void
set_showing_sends_to(boost::shared_ptr<Route> send_to)2232 RouteUI::set_showing_sends_to (boost::shared_ptr<Route> send_to)
2233 {
2234 _showing_sends_to = send_to;
2235 BusSendDisplayChanged (send_to); /* EMIT SIGNAL */
2236 }
2237
2238 void
bus_send_display_changed(boost::shared_ptr<Route> send_to)2239 RouteUI::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2240 {
2241 if (_route == send_to) {
2242 show_sends_button->set_active (true);
2243 send_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::send_blink));
2244 } else {
2245 show_sends_button->set_active (false);
2246 send_blink_connection.disconnect ();
2247 }
2248 }
2249
2250 RouteGroup*
route_group() const2251 RouteUI::route_group() const
2252 {
2253 return _route->route_group();
2254 }
2255
2256
RoutePinWindowProxy(std::string const & name,boost::shared_ptr<ARDOUR::Route> route)2257 RoutePinWindowProxy::RoutePinWindowProxy(std::string const &name, boost::shared_ptr<ARDOUR::Route> route)
2258 : WM::ProxyBase (name, string())
2259 , _route (boost::weak_ptr<Route> (route))
2260 {
2261 route->DropReferences.connect (going_away_connection, MISSING_INVALIDATOR, boost::bind (&RoutePinWindowProxy::route_going_away, this), gui_context());
2262 }
2263
~RoutePinWindowProxy()2264 RoutePinWindowProxy::~RoutePinWindowProxy()
2265 {
2266 _window = 0;
2267 }
2268
2269 ARDOUR::SessionHandlePtr*
session_handle()2270 RoutePinWindowProxy::session_handle ()
2271 {
2272 ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
2273 if (aw) { return aw; }
2274 return 0;
2275 }
2276
2277 Gtk::Window*
get(bool create)2278 RoutePinWindowProxy::get (bool create)
2279 {
2280 boost::shared_ptr<Route> r = _route.lock ();
2281 if (!r) {
2282 return 0;
2283 }
2284
2285 if (!_window) {
2286 if (!create) {
2287 return 0;
2288 }
2289 _window = new PluginPinDialog (r);
2290 ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
2291 if (aw) {
2292 aw->set_session (_session);
2293 }
2294 _window->show_all ();
2295 }
2296 return _window;
2297 }
2298
2299 void
route_going_away()2300 RoutePinWindowProxy::route_going_away ()
2301 {
2302 delete _window;
2303 _window = 0;
2304 WM::Manager::instance().remove (this);
2305 going_away_connection.disconnect();
2306 delete this;
2307 }
2308
2309 void
maybe_add_route_print_mgr()2310 RouteUI::maybe_add_route_print_mgr ()
2311 {
2312 if (_route->pinmgr_proxy ()) {
2313 return;
2314 }
2315 RoutePinWindowProxy* wp = new RoutePinWindowProxy (
2316 string_compose ("RPM-%1", _route->id()), _route);
2317 wp->set_session (_session);
2318
2319 const XMLNode* ui_xml = _session->extra_xml (X_("UI"));
2320 if (ui_xml) {
2321 wp->set_state (*ui_xml, 0);
2322 }
2323
2324 #if 0
2325 void* existing_ui = _route->pinmgr_proxy ();
2326 if (existing_ui) {
2327 wp->use_window (*(reinterpret_cast<Gtk::Window*>(existing_ui)));
2328 }
2329 #endif
2330 _route->set_pingmgr_proxy (wp);
2331
2332 WM::Manager::instance().register_window (wp);
2333 }
2334
2335 void
manage_pins()2336 RouteUI::manage_pins ()
2337 {
2338 RoutePinWindowProxy* proxy = _route->pinmgr_proxy ();
2339 if (proxy) {
2340 proxy->get (true);
2341 proxy->present();
2342 }
2343 }
2344
2345 void
fan_out(bool to_busses,bool group)2346 RouteUI::fan_out (bool to_busses, bool group)
2347 {
2348 Mixer_UI::instance()->fan_out (_route, to_busses, group);
2349 }
2350
2351 bool
mark_hidden(bool yn)2352 RouteUI::mark_hidden (bool yn)
2353 {
2354 if (yn != _route->presentation_info().hidden()) {
2355 _route->presentation_info().set_hidden (yn);
2356 return true; // things changed
2357 }
2358 return false;
2359 }
2360
2361 boost::shared_ptr<Stripable>
stripable() const2362 RouteUI::stripable () const
2363 {
2364 return _route;
2365 }
2366
2367 void
set_disk_io_point(DiskIOPoint diop)2368 RouteUI::set_disk_io_point (DiskIOPoint diop)
2369 {
2370 if (_route && is_track()) {
2371 track()->set_disk_io_point (diop);
2372 }
2373 }
2374
2375
2376 std::string
playlist_tip() const2377 RouteUI::playlist_tip () const
2378 {
2379 if (!is_track()) {
2380 return "";
2381 }
2382
2383 RouteGroup* rg = route_group ();
2384 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
2385 string group_string = "." + rg->name() + ".";
2386
2387 string take_name = track()->playlist()->name();
2388 string::size_type idx = take_name.find(group_string);
2389
2390 if (idx != string::npos) {
2391 /* find the bit containing the take number / name */
2392 take_name = take_name.substr (idx + group_string.length());
2393
2394 /* set the playlist button tooltip to the take name */
2395 return string_compose(_("Take: %1.%2"),
2396 Gtkmm2ext::markup_escape_text (rg->name()),
2397 Gtkmm2ext::markup_escape_text (take_name));
2398 }
2399 }
2400
2401 /* set the playlist button tooltip to the playlist name */
2402 return _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name());
2403 }
2404
2405 std::string
resolve_new_group_playlist_name(std::string const & basename,vector<boost::shared_ptr<Playlist>> const & playlists)2406 RouteUI::resolve_new_group_playlist_name (std::string const& basename, vector<boost::shared_ptr<Playlist> > const& playlists)
2407 {
2408 std::string ret (basename);
2409
2410 std::string const group_string = "." + route_group()->name() + ".";
2411
2412 // iterate through all playlists
2413 int maxnumber = 0;
2414 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
2415 std::string tmp = (*i)->name();
2416
2417 std::string::size_type idx = tmp.find(group_string);
2418 // find those which belong to this group
2419 if (idx != string::npos) {
2420 tmp = tmp.substr(idx + group_string.length());
2421
2422 // and find the largest current number
2423 int x = atoi(tmp);
2424 if (x > maxnumber) {
2425 maxnumber = x;
2426 }
2427 }
2428 }
2429
2430 maxnumber++;
2431
2432 char buf[32];
2433 snprintf (buf, sizeof(buf), "%d", maxnumber);
2434
2435 ret = _route->name() + "." + route_group()->name () + "." + buf;
2436
2437 return ret;
2438 }
2439
2440 void
use_new_playlist(std::string name,std::string gid,vector<boost::shared_ptr<Playlist>> const & playlists_before_op,bool copy)2441 RouteUI::use_new_playlist (std::string name, std::string gid, vector<boost::shared_ptr<Playlist> > const& playlists_before_op, bool copy)
2442 {
2443 boost::shared_ptr<Track> tr = track ();
2444 if (!tr) {
2445 return;
2446 }
2447
2448 boost::shared_ptr<const Playlist> pl = tr->playlist();
2449 if (!pl) {
2450 return;
2451 }
2452
2453 if (copy) {
2454 tr->use_copy_playlist ();
2455 } else {
2456 tr->use_default_new_playlist ();
2457 }
2458 tr->playlist()->set_name (name);
2459 tr->playlist()->set_pgroup_id (gid);
2460 }
2461
2462 void
clear_playlist()2463 RouteUI::clear_playlist ()
2464 {
2465 boost::shared_ptr<Track> tr = track ();
2466 if (!tr) {
2467 return;
2468 }
2469
2470 boost::shared_ptr<Playlist> pl = tr->playlist();
2471 if (!pl) {
2472 return;
2473 }
2474
2475 ARDOUR_UI::instance()->the_editor().clear_playlist (pl);
2476 }
2477
2478 void
build_playlist_menu()2479 RouteUI::build_playlist_menu ()
2480 {
2481 using namespace Menu_Helpers;
2482
2483 if (!is_track()) {
2484 return;
2485 }
2486
2487 PublicEditor* editor = &ARDOUR_UI::instance()->the_editor();
2488
2489 delete playlist_action_menu;
2490 playlist_action_menu = new Menu;
2491 playlist_action_menu->set_name ("ArdourContextMenu");
2492
2493 MenuList& playlist_items = playlist_action_menu->items();
2494 playlist_action_menu->set_name ("ArdourContextMenu");
2495 playlist_items.clear();
2496
2497 RadioMenuItem::Group playlist_group;
2498 boost::shared_ptr<Track> tr = track ();
2499
2500 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists()->playlists_for_track (tr);
2501
2502 /* sort the playlists */
2503 PlaylistSorterByID cmp;
2504 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
2505
2506 /* add the playlists to the menu */
2507 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
2508 string text = (*i)->name();
2509 playlist_items.push_back (RadioMenuElem (playlist_group, text));
2510 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
2511 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
2512
2513 if (tr->playlist()->id() == (*i)->id()) {
2514 item->set_active();
2515 }
2516 }
2517
2518 playlist_items.push_back (SeparatorElem());
2519 playlist_items.push_back (MenuElem(_("Select ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_selector)));
2520
2521 playlist_items.push_back (SeparatorElem());
2522 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::rename_current_playlist)));
2523 playlist_items.push_back (SeparatorElem());
2524
2525 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
2526 playlist_items.push_back (MenuElem (_("New Playlist..."), sigc::bind(sigc::mem_fun(editor, &PublicEditor::new_playlists_for_grouped_tracks), this, false)));
2527 playlist_items.push_back (MenuElem (_("Copy Playlist..."), sigc::bind(sigc::mem_fun(editor, &PublicEditor::new_playlists_for_grouped_tracks), this, true)));
2528 } else {
2529 playlist_items.push_back (MenuElem (_("New Playlist (for group)"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::new_playlists_for_grouped_tracks), this, false)));
2530 playlist_items.push_back (MenuElem (_("Copy Playlist (for group)"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::new_playlists_for_grouped_tracks), this, true)));
2531 }
2532
2533 playlist_items.push_back (SeparatorElem());
2534 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
2535 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::clear_grouped_playlists), this)));
2536 } else {
2537 playlist_items.push_back (MenuElem (_("Clear Current (for group)"), sigc::bind(sigc::mem_fun(editor, &PublicEditor::clear_grouped_playlists), this)));
2538 }
2539 playlist_items.push_back (SeparatorElem());
2540
2541 Menu* advanced_menu = manage (new Menu);
2542 MenuList& advanced_items = advanced_menu->items();
2543 advanced_items.push_back (MenuElem(_("Copy from ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_copy_selector)));
2544 advanced_items.push_back (MenuElem(_("Share with ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_share_selector)));
2545 advanced_items.push_back (MenuElem(_("Steal from ..."), sigc::mem_fun(*this, &RouteUI::show_playlist_steal_selector)));
2546 playlist_items.push_back (MenuElem (_("Advanced"), *advanced_menu));
2547 }
2548
2549 void
use_playlist(RadioMenuItem * item,boost::weak_ptr<Playlist> wpl)2550 RouteUI::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
2551 {
2552 assert (is_track());
2553
2554 // exit if we were triggered by deactivating the old playlist
2555 if (item && !item->get_active()) {
2556 return;
2557 }
2558
2559 boost::shared_ptr<Playlist> pl (wpl.lock());
2560
2561 if (!pl) {
2562 return;
2563 }
2564
2565 if (track()->playlist() == pl) {
2566 // exit when use_playlist is called by the creation of the playlist menu
2567 // or the playlist choice is unchanged
2568 return;
2569 }
2570
2571 track()->use_playlist (track()->data_type(), pl);
2572
2573 RouteGroup* rg = route_group();
2574
2575 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
2576
2577 std::string pgrp_id = pl->pgroup_id();
2578 if (pgrp_id.length()>0) { //easy: find other pl's with the same group id
2579 boost::shared_ptr<RouteList> rl (rg->route_list());
2580 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
2581 if ((*i) == this->route()) {
2582 continue;
2583 }
2584 if ((*i)->route_group() != rg) {
2585 continue;
2586 }
2587 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
2588 if (!track) {
2589 continue;
2590 }
2591 boost::shared_ptr<Playlist> ipl = session()->playlists()->for_pgroup(pgrp_id, track->id());
2592 if (ipl) {
2593 track->use_playlist(track->data_type(), ipl);
2594 }
2595 }
2596 } else { //fallback to prior behavior ... try to find matching names /*DEPRECATED*/
2597 std::string take_name = pl->name();
2598 std::string group_string = "." + rg->name() + ".";
2599
2600 std::string::size_type idx = take_name.find(group_string);
2601
2602 if (idx == std::string::npos)
2603 return;
2604
2605 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
2606
2607 boost::shared_ptr<RouteList> rl (rg->route_list());
2608 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
2609 if ((*i) == this->route()) {
2610 continue;
2611 }
2612
2613 std::string playlist_name = (*i)->name()+group_string+take_name;
2614
2615 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
2616 if (!track) {
2617 continue;
2618 }
2619
2620 if (track->freeze_state() == Track::Frozen) {
2621 /* Don't change playlists of frozen tracks */
2622 continue;
2623 }
2624
2625 boost::shared_ptr<Playlist> ipl = session()->playlists()->by_name(playlist_name);
2626 if (!ipl) {
2627 // No playlist for this track for this take yet, make it
2628 track->use_default_new_playlist();
2629 track->playlist()->set_name(playlist_name);
2630 } else {
2631 track->use_playlist(track->data_type(), ipl);
2632 }
2633 }
2634 } //fallback
2635 }
2636 }
2637
2638 void
show_playlist_selector()2639 RouteUI::show_playlist_selector ()
2640 {
2641 if (!_playlist_selector) {
2642 _playlist_selector = new PlaylistSelector();
2643 _playlist_selector->set_session(_session);
2644 }
2645
2646 _playlist_selector->prepare(this, PlaylistSelector::plSelect);
2647 _playlist_selector->show_all ();
2648 }
2649
2650 void
show_playlist_copy_selector()2651 RouteUI::show_playlist_copy_selector ()
2652 {
2653 if (!_playlist_selector) {
2654 _playlist_selector = new PlaylistSelector();
2655 _playlist_selector->set_session(_session);
2656 }
2657
2658 _playlist_selector->prepare(this, PlaylistSelector::plCopy);
2659 _playlist_selector->show_all ();
2660 }
2661
2662 void
show_playlist_share_selector()2663 RouteUI::show_playlist_share_selector ()
2664 {
2665 if (!_playlist_selector) {
2666 _playlist_selector = new PlaylistSelector();
2667 _playlist_selector->set_session(_session);
2668 }
2669
2670 _playlist_selector->prepare(this, PlaylistSelector::plShare);
2671 _playlist_selector->show_all ();
2672 }
2673
2674 void
show_playlist_steal_selector()2675 RouteUI::show_playlist_steal_selector ()
2676 {
2677 if (!_playlist_selector) {
2678 _playlist_selector = new PlaylistSelector();
2679 _playlist_selector->set_session(_session);
2680 }
2681
2682 _playlist_selector->prepare(this, PlaylistSelector::plSteal);
2683 _playlist_selector->show_all ();
2684 }
2685
2686 void
rename_current_playlist()2687 RouteUI::rename_current_playlist ()
2688 {
2689 Prompter prompter (true);
2690 string name;
2691
2692 boost::shared_ptr<Track> tr = track();
2693 if (!tr) {
2694 return;
2695 }
2696
2697 boost::shared_ptr<Playlist> pl = tr->playlist();
2698 if (!pl) {
2699 return;
2700 }
2701
2702 prompter.set_title (_("Rename Playlist"));
2703 prompter.set_prompt (_("New name for playlist:"));
2704 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2705 prompter.set_initial_text (pl->name());
2706 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2707
2708 while (true) {
2709 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
2710 break;
2711 }
2712 prompter.get_result (name);
2713 if (name.length()) {
2714 if (_session->playlists()->by_name (name)) {
2715 prompter.set_prompt (_("That name is already in use. Use this instead?"));
2716 prompter.set_initial_text (Playlist::bump_name (name, *_session));
2717 } else {
2718 break;
2719 }
2720 }
2721 }
2722
2723 if (name.length()) {
2724 vector<boost::shared_ptr<Playlist> > playlists_gr = _session->playlists()->playlists_for_pgroup (pl->pgroup_id());
2725 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_gr.begin(); i != playlists_gr.end(); ++i) {
2726 (*i)->set_name (name);
2727 }
2728 }
2729 }
2730