2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include <sigc++/bind.h>
28 #include <gtkmm/accelmap.h>
30 #include "pbd/convert.h"
31 #include "pbd/stacktrace.h"
32 #include "pbd/unwind.h"
34 #include <glibmm/threads.h>
36 #include <gtkmm2ext/gtk_ui.h>
37 #include <gtkmm2ext/keyboard.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/tearoff.h>
40 #include <gtkmm2ext/window_title.h>
41 #include <gtkmm2ext/doi.h>
43 #include "ardour/amp.h"
44 #include "ardour/debug.h"
45 #include "ardour/audio_track.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/plugin_manager.h"
48 #include "ardour/route_group.h"
49 #include "ardour/session.h"
50 #include "ardour/vca.h"
51 #include "ardour/vca_manager.h"
55 #include "mixer_strip.h"
56 #include "monitor_section.h"
57 #include "plugin_selector.h"
58 #include "public_editor.h"
59 #include "ardour_ui.h"
62 #include "route_sorter.h"
64 #include "gui_thread.h"
65 #include "mixer_group_tabs.h"
67 #include "ui_config.h"
68 #include "vca_master_strip.h"
72 using namespace ARDOUR;
73 using namespace ARDOUR_UI_UTILS;
77 using namespace Gtkmm2ext;
83 Mixer_UI* Mixer_UI::_instance = 0;
89 _instance = new Mixer_UI;
96 : Tabbable (_content, _("Mixer"))
97 , no_track_list_redisplay (false)
98 , in_group_row_change (false)
100 , _monitor_section (0)
101 , _plugin_selector (0)
102 , _strip_width (UIConfiguration::instance().get_default_narrow_ms() ? Narrow : Wide)
103 , ignore_reorder (false)
104 , _in_group_rebuild_or_clear (false)
105 , _route_deletion_in_progress (false)
106 , _following_editor_selection (false)
108 , _show_mixer_list (true)
110 Stripable::PresentationInfoChange.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_treeview_from_presentation_info, this), gui_context());
112 /* bindings was already set in MixerActor constructor */
114 _content.set_data ("ardour-bindings", bindings);
116 scroller.set_can_default (true);
117 // set_default (scroller);
119 scroller_base.set_flags (Gtk::CAN_FOCUS);
120 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
121 scroller_base.set_name ("MixerWindow");
122 scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
124 /* set up drag-n-drop */
125 vector<TargetEntry> target_table;
126 target_table.push_back (TargetEntry ("PluginFavoritePtr"));
127 scroller_base.drag_dest_set (target_table);
128 scroller_base.signal_drag_data_received().connect (sigc::mem_fun(*this, &Mixer_UI::scroller_drag_data_received));
130 // add as last item of strip packer
131 strip_packer.pack_end (scroller_base, true, true);
133 _group_tabs = new MixerGroupTabs (this);
134 VBox* b = manage (new VBox);
135 b->pack_start (*_group_tabs, PACK_SHRINK);
136 b->pack_start (strip_packer);
140 scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
142 setup_track_display ();
144 group_model = ListStore::create (group_columns);
145 group_display.set_model (group_model);
146 group_display.append_column (_("Group"), group_columns.text);
147 group_display.append_column (_("Show"), group_columns.visible);
148 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
149 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
150 group_display.get_column (0)->set_expand(true);
151 group_display.get_column (1)->set_expand(false);
152 group_display.set_name ("EditGroupList");
153 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
154 group_display.set_reorderable (true);
155 group_display.set_headers_visible (true);
156 group_display.set_rules_hint (true);
157 group_display.set_can_focus(false);
159 /* name is directly editable */
161 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
162 name_cell->property_editable() = true;
163 name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
165 /* use checkbox for the active column */
167 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
168 active_cell->property_activatable() = true;
169 active_cell->property_radio() = false;
171 group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
172 /* We use this to notice drag-and-drop reorders of the group list */
173 group_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_deleted));
174 group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
176 group_display_scroller.add (group_display);
177 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
179 HBox* route_group_display_button_box = manage (new HBox());
181 Button* route_group_add_button = manage (new Button ());
182 Button* route_group_remove_button = manage (new Button ());
186 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
188 route_group_add_button->add (*w);
190 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
192 route_group_remove_button->add (*w);
194 route_group_display_button_box->set_homogeneous (true);
196 route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
197 route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
199 route_group_display_button_box->add (*route_group_add_button);
200 route_group_display_button_box->add (*route_group_remove_button);
202 group_display_vbox.pack_start (group_display_scroller, true, true);
203 group_display_vbox.pack_start (*route_group_display_button_box, false, false);
205 group_display_frame.set_name ("BaseFrame");
206 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
207 group_display_frame.add (group_display_vbox);
210 list<TargetEntry> target_list;
211 target_list.push_back (TargetEntry ("PluginPresetPtr"));
213 favorite_plugins_model = PluginTreeStore::create (favorite_plugins_columns);
214 favorite_plugins_display.set_model (favorite_plugins_model);
215 favorite_plugins_display.append_column (_("Favorite Plugins"), favorite_plugins_columns.name);
216 favorite_plugins_display.set_name ("EditGroupList");
217 favorite_plugins_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
218 favorite_plugins_display.set_reorderable (false);
219 favorite_plugins_display.set_headers_visible (true);
220 favorite_plugins_display.set_rules_hint (true);
221 favorite_plugins_display.set_can_focus (false);
222 favorite_plugins_display.add_object_drag (favorite_plugins_columns.plugin.index(), "PluginFavoritePtr");
223 favorite_plugins_display.set_drag_column (favorite_plugins_columns.name.index());
224 favorite_plugins_display.add_drop_targets (target_list);
225 favorite_plugins_display.signal_row_activated().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_activated));
226 favorite_plugins_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_button_press), false);
227 favorite_plugins_display.signal_drop.connect (sigc::mem_fun (*this, &Mixer_UI::plugin_drop));
228 favorite_plugins_display.signal_row_expanded().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
229 favorite_plugins_display.signal_row_collapsed().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
230 favorite_plugins_model->signal_row_has_child_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::sync_treeview_favorite_ui_state));
232 favorite_plugins_scroller.add (favorite_plugins_display);
233 favorite_plugins_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
235 favorite_plugins_frame.set_name ("BaseFrame");
236 favorite_plugins_frame.set_shadow_type (Gtk::SHADOW_IN);
237 favorite_plugins_frame.add (favorite_plugins_scroller);
239 rhs_pane1.add (favorite_plugins_frame);
240 rhs_pane1.add (track_display_frame);
241 rhs_pane2.add (rhs_pane1);
242 rhs_pane2.add (group_display_frame);
244 list_vpacker.pack_start (rhs_pane2, true, true);
246 vca_scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
247 vca_scroller_base.set_name ("MixerWindow");
248 vca_scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::masters_scroller_button_release), false);
249 vca_packer.pack_end (vca_scroller_base, true, true);
251 vca_scroller.add (vca_packer);
252 vca_scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
253 vca_scroller.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
255 inner_pane.add (scroller);
256 inner_pane.add (vca_scroller);
258 global_hpacker.pack_start (inner_pane, true, true);
259 global_hpacker.pack_start (out_packer, false, false);
261 list_hpane.add (list_vpacker);
262 list_hpane.add (global_hpacker);
264 _content.pack_start (list_hpane, true, true);
268 route_group_display_button_box->show();
269 route_group_add_button->show();
270 route_group_remove_button->show();
273 _content.set_name ("MixerWindow");
275 global_hpacker.show();
277 scroller_base.show();
278 scroller_hpacker.show();
279 mixer_scroller_vpacker.show();
281 group_display_button_label.show();
282 group_display_button.show();
283 group_display_scroller.show();
284 favorite_plugins_scroller.show();
285 group_display_vbox.show();
286 group_display_frame.show();
287 favorite_plugins_frame.show();
294 vca_scroller_base.show();
297 group_display.show();
298 favorite_plugins_display.show();
300 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
304 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::escape, this), gui_context());
306 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
307 _plugin_selector = new PluginSelector (PluginManager::instance ());
309 #error implement deferred Plugin-Favorite list
311 PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
312 PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
313 ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
316 Mixer_UI::~Mixer_UI ()
318 if (_monitor_section) {
319 monitor_section_detached ();
320 delete _monitor_section;
322 delete _plugin_selector;
332 Mixer_UI::track_editor_selection ()
334 PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
338 Mixer_UI::use_own_window (bool and_fill_it)
340 bool new_window = !own_window();
342 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
345 if (win && new_window) {
346 win->set_name ("MixerWindow");
347 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
348 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
349 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
350 win->set_data ("ardour-bindings", bindings);
358 Mixer_UI::show_window ()
360 Tabbable::show_window ();
362 /* show/hide group tabs as required */
363 parameter_changed ("show-group-tabs");
365 /* now reset each strips width so the right widgets are shown */
368 TreeModel::Children rows = track_model->children();
369 TreeModel::Children::iterator ri;
371 for (ri = rows.begin(); ri != rows.end(); ++ri) {
372 ms = (*ri)[track_columns.strip];
376 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
377 /* Fix visibility of mixer strip stuff */
378 ms->parameter_changed (X_("mixer-element-visibility"));
381 /* force focus into main area */
382 scroller_base.grab_focus ();
386 Mixer_UI::add_masters (VCAList& vcas)
388 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
390 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
392 TreeModel::Row row = *(track_model->append());
393 row[track_columns.text] = (*v)->name();
394 row[track_columns.visible] = true;
395 row[track_columns.vca] = vms;
397 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
400 redisplay_track_list ();
404 Mixer_UI::remove_master (VCAMasterStrip* vms)
406 if (_session && _session->deletion_in_progress()) {
407 /* its all being taken care of */
411 TreeModel::Children rows = track_model->children();
412 TreeModel::Children::iterator ri;
414 for (ri = rows.begin(); ri != rows.end(); ++ri) {
415 if ((*ri)[track_columns.vca] == vms) {
416 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
417 track_model->erase (ri);
424 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
426 using namespace Menu_Helpers;
428 if (Keyboard::is_context_menu_event (ev)) {
429 ARDOUR_UI::instance()->add_route ();
437 Mixer_UI::add_strips (RouteList& routes)
439 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
440 uint32_t nroutes = 0;
442 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
443 boost::shared_ptr<Route> r = (*it)[track_columns.route];
451 if (r->presentation_info().group_order() == (routes.front()->presentation_info().group_order() + routes.size())) {
458 _selection.clear_routes ();
464 no_track_list_redisplay = true;
465 track_display.set_model (Glib::RefPtr<ListStore>());
467 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
468 boost::shared_ptr<Route> route = (*x);
470 if (route->is_auditioner()) {
474 if (route->is_monitor()) {
476 if (!_monitor_section) {
477 _monitor_section = new MonitorSection (_session);
479 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
481 _monitor_section->tearoff().set_state (*mnode);
485 out_packer.pack_end (_monitor_section->tearoff(), false, false);
486 _monitor_section->set_session (_session);
487 _monitor_section->tearoff().show_all ();
489 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
490 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
492 monitor_section_attached ();
494 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
496 /* no regular strip shown for control out */
501 strip = new MixerStrip (*this, _session, route);
502 strips.push_back (strip);
504 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
506 if (strip->width_owner() != strip) {
507 strip->set_width_enum (_strip_width, this);
512 TreeModel::Row row = *(track_model->insert(insert_iter));
513 row[track_columns.text] = route->name();
514 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
515 row[track_columns.route] = route;
516 row[track_columns.strip] = strip;
517 row[track_columns.vca] = 0;
520 _selection.add (strip);
523 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
525 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
526 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
529 } catch (const std::exception& e) {
530 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
533 no_track_list_redisplay = false;
534 track_display.set_model (track_model);
536 sync_presentation_info_from_treeview ();
537 redisplay_track_list ();
541 Mixer_UI::deselect_all_strip_processors ()
543 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
544 (*i)->deselect_all_processors();
549 Mixer_UI::select_strip (MixerStrip& ms, bool add)
552 _selection.add (&ms);
554 _selection.set (&ms);
559 Mixer_UI::select_none ()
561 _selection.clear_routes();
562 deselect_all_strip_processors();
566 Mixer_UI::delete_processors ()
568 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
569 (*i)->delete_processors();
575 Mixer_UI::remove_strip (MixerStrip* strip)
577 if (_session && _session->deletion_in_progress()) {
578 /* its all being taken care of */
582 TreeModel::Children rows = track_model->children();
583 TreeModel::Children::iterator ri;
584 list<MixerStrip *>::iterator i;
586 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
590 for (ri = rows.begin(); ri != rows.end(); ++ri) {
591 if ((*ri)[track_columns.strip] == strip) {
592 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
593 track_model->erase (ri);
600 Mixer_UI::sync_presentation_info_from_treeview ()
602 if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
606 TreeModel::Children rows = track_model->children();
612 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
614 TreeModel::Children::iterator ri;
618 for (ri = rows.begin(); ri != rows.end(); ++ri) {
619 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
620 bool visible = (*ri)[track_columns.visible];
627 if (route->presentation_info().special()) {
632 route->presentation_info().set_flag (PresentationInfo::Hidden);
634 route->presentation_info().unset_flag (PresentationInfo::Hidden);
637 if (order != route->presentation_info().group_order()) {
638 route->set_presentation_group_order_explicit (order);
646 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
647 _session->notify_presentation_info_change ();
652 Mixer_UI::sync_treeview_from_presentation_info ()
654 if (!_session || _session->deletion_in_progress()) {
658 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
660 /* we could get here after either a change in the Mixer or Editor sort
661 * order, but either way, the mixer order keys reflect the intended
662 * order for the GUI, so reorder the treeview model to match it.
665 vector<int> neworder;
666 TreeModel::Children rows = track_model->children();
667 uint32_t old_order = 0;
668 bool changed = false;
675 uint32_t vca_cnt = 0;
676 uint32_t max_route_order_key = 0;
678 /* count number of Routes in track_model (there may be some odd reason
679 why this is not the same as the number in the session, but here we
680 care about the track model.
683 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
684 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
686 max_route_order_key = max (route->presentation_info().group_order(), max_route_order_key);
690 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
691 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
693 /* VCAs need to sort after all routes. We don't display
694 * them in the same place (March 2016), but we don't
695 * want them intermixed in the track_model
697 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
699 sorted.push_back (OrderKeys (old_order, route->presentation_info().group_order()));
703 SortByNewDisplayOrder cmp;
705 sort (sorted.begin(), sorted.end(), cmp);
706 neworder.assign (sorted.size(), 0);
710 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
712 neworder[n] = sr->old_display_order;
714 if (sr->old_display_order != n) {
720 Unwinder<bool> uw (ignore_reorder, true);
721 track_model->reorder (neworder);
724 redisplay_track_list ();
728 Mixer_UI::follow_editor_selection ()
730 if (_following_editor_selection) {
734 _following_editor_selection = true;
735 _selection.block_routes_changed (true);
737 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
739 _selection.clear_routes ();
741 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
742 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
744 MixerStrip* ms = strip_by_route (rtav->route());
751 _following_editor_selection = false;
752 _selection.block_routes_changed (false);
757 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
759 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
760 if ((*i)->route() == r) {
769 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
771 if (ev->button == 1) {
772 if (_selection.selected (strip)) {
773 /* primary-click: toggle selection state of strip */
774 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
775 _selection.remove (strip);
776 } else if (_selection.routes.size() > 1) {
777 /* de-select others */
778 _selection.set (strip);
781 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
782 _selection.add (strip);
783 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
785 if (!_selection.selected(strip)) {
787 /* extend selection */
789 vector<MixerStrip*> tmp;
790 bool accumulate = false;
791 bool found_another = false;
793 tmp.push_back (strip);
795 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
797 /* hit clicked strip, start accumulating till we hit the first
806 } else if (_selection.selected (*i)) {
807 /* hit selected strip. if currently accumulating others,
808 we're done. if not accumulating others, start doing so.
810 found_another = true;
825 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
829 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
833 _selection.set (strip);
842 Mixer_UI::set_session (Session* sess)
844 SessionHandlePtr::set_session (sess);
846 if (_plugin_selector) {
847 _plugin_selector->set_session (_session);
850 _group_tabs->set_session (sess);
856 refill_favorite_plugins();
858 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
859 set_state (*node, 0);
863 initial_track_display ();
865 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
866 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
867 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
868 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
869 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
870 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
871 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
873 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
875 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
877 route_groups_changed ();
886 Mixer_UI::session_going_away ()
888 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
890 _in_group_rebuild_or_clear = true;
891 group_model->clear ();
892 _in_group_rebuild_or_clear = false;
895 track_model->clear ();
897 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
901 if (_monitor_section) {
902 _monitor_section->tearoff().hide_visible ();
905 monitor_section_detached ();
911 SessionHandlePtr::session_going_away ();
918 Mixer_UI::track_visibility_changed (std::string const & path)
920 if (_session && _session->deletion_in_progress()) {
926 if ((iter = track_model->get_iter (path))) {
927 MixerStrip* strip = (*iter)[track_columns.strip];
929 bool visible = (*iter)[track_columns.visible];
931 if (strip->set_marked_for_display (!visible)) {
932 update_track_visibility ();
939 Mixer_UI::update_track_visibility ()
941 TreeModel::Children rows = track_model->children();
942 TreeModel::Children::iterator i;
945 Unwinder<bool> uw (no_track_list_redisplay, true);
947 for (i = rows.begin(); i != rows.end(); ++i) {
948 MixerStrip *strip = (*i)[track_columns.strip];
950 (*i)[track_columns.visible] = strip->marked_for_display ();
954 /* force presentation catch up with visibility changes
957 sync_presentation_info_from_treeview ();
960 redisplay_track_list ();
964 Mixer_UI::show_strip (MixerStrip* ms)
966 TreeModel::Children rows = track_model->children();
967 TreeModel::Children::iterator i;
969 for (i = rows.begin(); i != rows.end(); ++i) {
971 MixerStrip* strip = (*i)[track_columns.strip];
973 (*i)[track_columns.visible] = true;
974 redisplay_track_list ();
981 Mixer_UI::hide_strip (MixerStrip* ms)
983 TreeModel::Children rows = track_model->children();
984 TreeModel::Children::iterator i;
986 for (i = rows.begin(); i != rows.end(); ++i) {
988 MixerStrip* strip = (*i)[track_columns.strip];
990 (*i)[track_columns.visible] = false;
991 redisplay_track_list ();
998 Mixer_UI::start_updating ()
1000 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1005 Mixer_UI::stop_updating ()
1007 fast_screen_update_connection.disconnect();
1012 Mixer_UI::fast_update_strips ()
1014 if (_content.is_mapped () && _session) {
1015 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1016 (*i)->fast_update ();
1022 Mixer_UI::set_all_strips_visibility (bool yn)
1024 TreeModel::Children rows = track_model->children();
1025 TreeModel::Children::iterator i;
1028 Unwinder<bool> uw (no_track_list_redisplay, true);
1030 for (i = rows.begin(); i != rows.end(); ++i) {
1032 TreeModel::Row row = (*i);
1033 MixerStrip* strip = row[track_columns.strip];
1039 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1043 (*i)[track_columns.visible] = yn;
1047 redisplay_track_list ();
1052 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1054 TreeModel::Children rows = track_model->children();
1055 TreeModel::Children::iterator i;
1058 Unwinder<bool> uw (no_track_list_redisplay, true);
1060 for (i = rows.begin(); i != rows.end(); ++i) {
1061 TreeModel::Row row = (*i);
1062 MixerStrip* strip = row[track_columns.strip];
1068 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1072 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1073 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1077 (*i)[track_columns.visible] = yn;
1081 if (at) { /* track */
1082 (*i)[track_columns.visible] = yn;
1087 if (!at && !mt) { /* bus */
1088 (*i)[track_columns.visible] = yn;
1093 if (mt) { /* midi-track */
1094 (*i)[track_columns.visible] = yn;
1101 redisplay_track_list ();
1105 Mixer_UI::hide_all_routes ()
1107 set_all_strips_visibility (false);
1111 Mixer_UI::show_all_routes ()
1113 set_all_strips_visibility (true);
1117 Mixer_UI::show_all_audiobus ()
1119 set_all_audio_midi_visibility (2, true);
1122 Mixer_UI::hide_all_audiobus ()
1124 set_all_audio_midi_visibility (2, false);
1128 Mixer_UI::show_all_audiotracks()
1130 set_all_audio_midi_visibility (1, true);
1133 Mixer_UI::hide_all_audiotracks ()
1135 set_all_audio_midi_visibility (1, false);
1139 Mixer_UI::show_all_miditracks()
1141 set_all_audio_midi_visibility (3, true);
1144 Mixer_UI::hide_all_miditracks ()
1146 set_all_audio_midi_visibility (3, false);
1151 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1153 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1154 sync_presentation_info_from_treeview ();
1158 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1160 /* this happens as the second step of a DnD within the treeview as well
1161 as when a row/route is actually deleted.
1163 if it was a deletion then we have to force a redisplay because
1164 order keys may not have changed.
1167 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1168 sync_presentation_info_from_treeview ();
1170 if (_route_deletion_in_progress) {
1171 redisplay_track_list ();
1176 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1178 TreeModel::Children rows = track_model->children();
1180 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1182 MixerStrip* strip = (*i)[track_columns.strip];
1185 /* we're in the middle of changing a row, don't worry */
1189 if (!strip->route()) {
1190 /* non-route element */
1194 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1198 if (strip->route()->slaved_to (vca)) {
1200 strip->set_gui_property ("visible", true);
1202 if (strip->packed()) {
1203 strip_packer.reorder_child (*strip, -1); /* put at end */
1205 strip_packer.pack_start (*strip, false, false);
1206 strip->set_packed (true);
1211 strip->set_gui_property ("visible", false);
1213 if (strip->packed()) {
1214 strip_packer.remove (*strip);
1215 strip->set_packed (false);
1222 Mixer_UI::redisplay_track_list ()
1224 if (no_track_list_redisplay) {
1228 boost::shared_ptr<VCA> sv = spilled_vca.lock ();
1231 spill_redisplay (sv);
1235 TreeModel::Children rows = track_model->children();
1236 TreeModel::Children::iterator i;
1237 uint32_t n_masters = 0;
1239 container_clear (vca_packer);
1240 vca_packer.pack_end (vca_scroller_base, true, true);
1242 for (i = rows.begin(); i != rows.end(); ++i) {
1244 VCAMasterStrip* vms = (*i)[track_columns.vca];
1247 vca_packer.pack_start (*vms, false, false);
1253 MixerStrip* strip = (*i)[track_columns.strip];
1256 /* we're in the middle of changing a row, don't worry */
1260 bool const visible = (*i)[track_columns.visible];
1263 strip->set_gui_property ("visible", true);
1265 if (strip->packed()) {
1267 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1268 out_packer.reorder_child (*strip, -1);
1271 strip_packer.reorder_child (*strip, -1); /* put at end */
1276 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1277 out_packer.pack_start (*strip, false, false);
1279 strip_packer.pack_start (*strip, false, false);
1281 strip->set_packed (true);
1286 strip->set_gui_property ("visible", false);
1288 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1289 /* do nothing, these cannot be hidden */
1291 if (strip->packed()) {
1292 strip_packer.remove (*strip);
1293 strip->set_packed (false);
1299 /* update visibility of VCA assign buttons */
1301 if (n_masters == 0) {
1302 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1303 vca_scroller.hide ();
1305 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1306 vca_scroller.show ();
1309 _group_tabs->set_dirty ();
1313 Mixer_UI::strip_width_changed ()
1315 _group_tabs->set_dirty ();
1318 TreeModel::Children rows = track_model->children();
1319 TreeModel::Children::iterator i;
1322 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1323 MixerStrip* strip = (*i)[track_columns.strip];
1329 bool visible = (*i)[track_columns.visible];
1332 strip->queue_draw();
1339 struct PresentationInfoRouteSorter
1341 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1342 return a->presentation_info().global_order () < b->presentation_info().global_order ();
1347 Mixer_UI::initial_track_display ()
1349 boost::shared_ptr<RouteList> routes = _session->get_routes();
1350 RouteList copy (*routes);
1351 PresentationInfoRouteSorter sorter;
1356 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1357 Unwinder<bool> uw2 (ignore_reorder, true);
1359 track_model->clear ();
1360 VCAList vcas = _session->vca_manager().vcas();
1365 redisplay_track_list ();
1369 Mixer_UI::show_track_list_menu ()
1371 if (track_menu == 0) {
1372 build_track_menu ();
1375 track_menu->popup (1, gtk_get_current_event_time());
1379 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1381 if (Keyboard::is_context_menu_event (ev)) {
1382 show_track_list_menu ();
1390 Mixer_UI::build_track_menu ()
1392 using namespace Menu_Helpers;
1393 using namespace Gtk;
1395 track_menu = new Menu;
1396 track_menu->set_name ("ArdourContextMenu");
1397 MenuList& items = track_menu->items();
1399 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1400 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1401 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1402 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1403 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1404 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1405 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1406 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1411 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1413 if (!what_changed.contains (ARDOUR::Properties::name)) {
1417 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1419 TreeModel::Children rows = track_model->children();
1420 TreeModel::Children::iterator i;
1422 for (i = rows.begin(); i != rows.end(); ++i) {
1423 if ((*i)[track_columns.strip] == mx) {
1424 (*i)[track_columns.text] = mx->route()->name();
1429 error << _("track display list item for renamed strip not found!") << endmsg;
1433 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1435 TreeModel::Path path;
1436 TreeViewColumn* column;
1440 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1441 _group_tabs->get_menu(0)->popup (1, ev->time);
1445 TreeIter iter = group_model->get_iter (path);
1447 _group_tabs->get_menu(0)->popup (1, ev->time);
1451 RouteGroup* group = (*iter)[group_columns.group];
1453 if (Keyboard::is_context_menu_event (ev)) {
1454 _group_tabs->get_menu(group)->popup (1, ev->time);
1458 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1460 if (Keyboard::is_edit_event (ev)) {
1462 // edit_route_group (group);
1464 group_display.queue_draw();
1473 bool visible = (*iter)[group_columns.visible];
1474 (*iter)[group_columns.visible] = !visible;
1476 group_display.queue_draw();
1489 Mixer_UI::activate_all_route_groups ()
1491 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1495 Mixer_UI::disable_all_route_groups ()
1497 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1501 Mixer_UI::route_groups_changed ()
1503 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1505 _in_group_rebuild_or_clear = true;
1507 /* just rebuild the while thing */
1509 group_model->clear ();
1512 /* this is currently not used,
1513 * Mixer_UI::group_display_button_press() has a case for it,
1514 * and a commented edit_route_group() but that's n/a since 2011.
1516 * This code is left as reminder that
1517 * row[group_columns.group] = 0 has special meaning.
1521 row = *(group_model->append());
1522 row[group_columns.visible] = true;
1523 row[group_columns.text] = (_("-all-"));
1524 row[group_columns.group] = 0;
1528 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1530 _group_tabs->set_dirty ();
1531 _in_group_rebuild_or_clear = false;
1535 Mixer_UI::new_route_group ()
1539 _group_tabs->run_new_group_dialog (rl, false);
1543 Mixer_UI::remove_selected_route_group ()
1545 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1546 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1552 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1555 /* selection mode is single, so rows.begin() is it */
1557 if ((iter = group_model->get_iter (*i))) {
1559 RouteGroup* rg = (*iter)[group_columns.group];
1562 _session->remove_route_group (*rg);
1568 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1570 if (in_group_row_change) {
1574 /* force an update of any mixer strips that are using this group,
1575 otherwise mix group names don't change in mixer strips
1578 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1579 if ((*i)->route_group() == group) {
1580 (*i)->route_group_changed();
1584 TreeModel::iterator i;
1585 TreeModel::Children rows = group_model->children();
1586 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1588 in_group_row_change = true;
1590 for (i = rows.begin(); i != rows.end(); ++i) {
1591 if ((*i)[group_columns.group] == group) {
1592 (*i)[group_columns.visible] = !group->is_hidden ();
1593 (*i)[group_columns.text] = group->name ();
1598 in_group_row_change = false;
1600 if (change.contains (Properties::name)) {
1601 _group_tabs->set_dirty ();
1604 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1605 if ((*j)->route_group() == group) {
1606 if (group->is_hidden ()) {
1616 Mixer_UI::show_mixer_list (bool yn)
1619 list_vpacker.show ();
1621 list_vpacker.hide ();
1624 _show_mixer_list = yn;
1628 Mixer_UI::show_monitor_section (bool yn)
1630 if (!monitor_section()) {
1633 if (monitor_section()->tearoff().torn_off()) {
1638 monitor_section()->tearoff().show();
1640 monitor_section()->tearoff().hide();
1645 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1650 if ((iter = group_model->get_iter (path))) {
1652 if ((group = (*iter)[group_columns.group]) == 0) {
1656 if (new_text != group->name()) {
1657 group->set_name (new_text);
1663 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1667 if (in_group_row_change) {
1671 if ((group = (*iter)[group_columns.group]) == 0) {
1675 std::string name = (*iter)[group_columns.text];
1677 if (name != group->name()) {
1678 group->set_name (name);
1681 bool hidden = !(*iter)[group_columns.visible];
1683 if (hidden != group->is_hidden ()) {
1684 group->set_hidden (hidden, this);
1688 /** Called when a group model row is deleted, but also when the model is
1689 * reordered by a user drag-and-drop; the latter is what we are
1690 * interested in here.
1693 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1695 if (_in_group_rebuild_or_clear) {
1699 /* Re-write the session's route group list so that the new order is preserved */
1701 list<RouteGroup*> new_list;
1703 Gtk::TreeModel::Children children = group_model->children();
1704 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1705 RouteGroup* g = (*i)[group_columns.group];
1707 new_list.push_back (g);
1711 _session->reorder_route_groups (new_list);
1716 Mixer_UI::add_route_group (RouteGroup* group)
1718 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1721 in_group_row_change = true;
1723 TreeModel::Row row = *(group_model->append());
1724 row[group_columns.visible] = !group->is_hidden ();
1725 row[group_columns.group] = group;
1726 if (!group->name().empty()) {
1727 row[group_columns.text] = group->name();
1729 row[group_columns.text] = _("unnamed");
1733 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1736 TreeViewColumn* col = group_display.get_column (0);
1737 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1738 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1741 _group_tabs->set_dirty ();
1743 in_group_row_change = false;
1747 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1749 using namespace Menu_Helpers;
1751 if (Keyboard::is_context_menu_event (ev)) {
1752 ARDOUR_UI::instance()->add_route ();
1760 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1762 printf ("Mixer_UI::scroller_drag_data_received\n");
1763 if (data.get_target() != "PluginFavoritePtr") {
1764 context->drag_finish (false, false, time);
1768 const void * d = data.get_data();
1769 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1771 PluginPresetList nfos;
1773 tv->get_object_drag_data (nfos, &source);
1775 Route::ProcessorList pl;
1778 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1779 PluginPresetPtr ppp = (*i);
1780 PluginInfoPtr pip = ppp->_pip;
1781 if (!pip->is_instrument ()) {
1784 ARDOUR_UI::instance()->session_add_midi_track ((RouteGroup*) 0, 1, _("MIDI"), Config->get_strict_io (), pip, ppp->_preset.valid ? &ppp->_preset : 0, PresentationInfo::max_order);
1788 context->drag_finish (ok, false, time);
1792 Mixer_UI::set_strip_width (Width w, bool save)
1796 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1797 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1802 struct PluginStateSorter {
1804 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1805 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1806 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1807 if (aiter != _user.end() && biter != _user.end()) {
1808 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1810 if (aiter != _user.end()) {
1813 if (biter != _user.end()) {
1816 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1819 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1821 std::list<std::string> _user;
1825 Mixer_UI::set_state (const XMLNode& node, int version)
1827 XMLProperty const * prop;
1829 Tabbable::set_state (node, version);
1831 if ((prop = node.property ("narrow-strips"))) {
1832 if (string_is_affirmative (prop->value())) {
1833 set_strip_width (Narrow);
1835 set_strip_width (Wide);
1839 if ((prop = node.property ("show-mixer"))) {
1840 if (string_is_affirmative (prop->value())) {
1845 if ((prop = node.property ("maximised"))) {
1846 bool yn = string_is_affirmative (prop->value());
1847 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1849 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1850 bool fs = tact && tact->get_active();
1852 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1856 if ((prop = node.property ("show-mixer-list"))) {
1857 bool yn = string_is_affirmative (prop->value());
1858 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1860 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1862 /* do it twice to force the change */
1863 tact->set_active (!yn);
1864 tact->set_active (yn);
1868 XMLNode* plugin_order;
1869 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1870 store_current_favorite_order ();
1871 std::list<string> order;
1872 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1873 XMLNodeConstIterator i;
1874 for (i = kids.begin(); i != kids.end(); ++i) {
1875 if ((prop = (*i)->property ("unique-id"))) {
1876 std::string unique_id = prop->value();
1877 order.push_back (unique_id);
1878 if ((prop = (*i)->property ("expanded"))) {
1879 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1883 PluginStateSorter cmp (order);
1884 favorite_order.sort (cmp);
1885 sync_treeview_from_favorite_order ();
1891 Mixer_UI::get_state ()
1893 XMLNode* node = new XMLNode (X_("Mixer"));
1896 node->add_child_nocopy (Tabbable::get_state());
1898 snprintf(buf,sizeof(buf), "%f", rhs_pane1.get_divider());
1899 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1900 snprintf(buf,sizeof(buf), "%f", rhs_pane2.get_divider());
1901 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1902 snprintf(buf,sizeof(buf), "%f", list_hpane.get_divider());
1903 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1904 snprintf(buf,sizeof(buf), "%f", inner_pane.get_divider());
1905 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1907 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1908 node->add_property ("show-mixer", _visible ? "yes" : "no");
1909 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1910 node->add_property ("maximised", _maximised ? "yes" : "no");
1912 store_current_favorite_order ();
1913 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1915 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1916 XMLNode* p = new XMLNode ("PluginInfo");
1917 p->add_property ("sort", cnt);
1918 p->add_property ("unique-id", (*i)->unique_id);
1919 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1920 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1922 plugin_order->add_child_nocopy (*p);
1924 node->add_child_nocopy (*plugin_order);
1930 Mixer_UI::scroll_left ()
1932 if (!scroller.get_hscrollbar()) return;
1933 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1934 /* stupid GTK: can't rely on clamping across versions */
1935 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1939 Mixer_UI::scroll_right ()
1941 if (!scroller.get_hscrollbar()) return;
1942 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1943 /* stupid GTK: can't rely on clamping across versions */
1944 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1948 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1950 switch (ev->direction) {
1951 case GDK_SCROLL_LEFT:
1955 if (ev->state & Keyboard::TertiaryModifier) {
1961 case GDK_SCROLL_RIGHT:
1965 case GDK_SCROLL_DOWN:
1966 if (ev->state & Keyboard::TertiaryModifier) {
1978 Mixer_UI::parameter_changed (string const & p)
1980 if (p == "show-group-tabs") {
1981 bool const s = _session->config.get_show_group_tabs ();
1983 _group_tabs->show ();
1985 _group_tabs->hide ();
1987 } else if (p == "default-narrow_ms") {
1988 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
1989 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1990 (*i)->set_width_enum (s ? Narrow : Wide, this);
1992 } else if (p == "use-monitor-bus") {
1993 if (_session && !_session->monitor_out()) {
1994 monitor_section_detached ();
2000 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2002 g->set_active (a, this);
2006 Mixer_UI::plugin_selector()
2008 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2009 if (!_plugin_selector)
2010 _plugin_selector = new PluginSelector (PluginManager::instance());
2013 return _plugin_selector;
2017 Mixer_UI::setup_track_display ()
2019 track_model = ListStore::create (track_columns);
2020 track_display.set_model (track_model);
2021 track_display.append_column (_("Strips"), track_columns.text);
2022 track_display.append_column (_("Show"), track_columns.visible);
2023 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2024 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2025 track_display.get_column (0)->set_expand(true);
2026 track_display.get_column (1)->set_expand(false);
2027 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2028 track_display.set_name (X_("EditGroupList"));
2029 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2030 track_display.set_reorderable (true);
2031 track_display.set_headers_visible (true);
2032 track_display.set_can_focus(false);
2034 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2035 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2037 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2038 track_list_visible_cell->property_activatable() = true;
2039 track_list_visible_cell->property_radio() = false;
2040 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2042 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2044 track_display_scroller.add (track_display);
2045 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2047 VBox* v = manage (new VBox);
2049 v->pack_start (track_display_scroller, true, true);
2051 Button* b = manage (new Button);
2053 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2057 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2059 v->pack_start (*b, false, false);
2061 track_display_frame.set_name("BaseFrame");
2062 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2063 track_display_frame.add (*v);
2065 track_display_scroller.show();
2066 track_display_frame.show();
2067 track_display.show();
2071 Mixer_UI::new_track_or_bus ()
2073 ARDOUR_UI::instance()->add_route ();
2077 Mixer_UI::update_title ()
2079 if (!own_window()) {
2086 if (_session->snap_name() != _session->name()) {
2087 n = _session->snap_name ();
2089 n = _session->name ();
2092 if (_session->dirty ()) {
2096 WindowTitle title (n);
2097 title += S_("Window|Mixer");
2098 title += Glib::get_application_name ();
2099 own_window()->set_title (title.get_string());
2103 WindowTitle title (S_("Window|Mixer"));
2104 title += Glib::get_application_name ();
2105 own_window()->set_title (title.get_string());
2110 Mixer_UI::strip_by_x (int x)
2112 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2115 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2116 x2 = x1 + (*i)->get_width();
2118 if (x >= x1 && x <= x2) {
2127 Mixer_UI::set_route_targets_for_operation ()
2129 _route_targets.clear ();
2131 if (!_selection.empty()) {
2132 _route_targets = _selection.routes;
2136 // removed "implicit" selections of strips, after discussion on IRC
2141 Mixer_UI::monitor_section_going_away ()
2143 if (_monitor_section) {
2144 monitor_section_detached ();
2145 out_packer.remove (_monitor_section->tearoff());
2146 _monitor_section->set_session (0);
2147 delete _monitor_section;
2148 _monitor_section = 0;
2153 Mixer_UI::toggle_midi_input_active (bool flip_others)
2155 boost::shared_ptr<RouteList> rl (new RouteList);
2158 set_route_targets_for_operation ();
2160 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2161 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2164 rl->push_back ((*r)->route());
2165 onoff = !mt->input_active();
2169 _session->set_exclusive_input_active (rl, onoff, flip_others);
2173 Mixer_UI::maximise_mixer_space ()
2175 if (!own_window()) {
2183 _window->fullscreen ();
2188 Mixer_UI::restore_mixer_space ()
2190 if (!own_window()) {
2198 own_window()->unfullscreen();
2203 Mixer_UI::monitor_section_attached ()
2205 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2206 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2207 act->set_sensitive (true);
2208 tact->set_active ();
2212 Mixer_UI::monitor_section_detached ()
2214 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2215 act->set_sensitive (false);
2219 Mixer_UI::store_current_favorite_order ()
2221 typedef Gtk::TreeModel::Children type_children;
2222 type_children children = favorite_plugins_model->children();
2223 favorite_order.clear();
2224 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2226 Gtk::TreeModel::Row row = *iter;
2227 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2228 favorite_order.push_back (ppp->_pip);
2229 std::string name = row[favorite_plugins_columns.name];
2230 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2235 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2237 Gtk::TreeModel::Row row = *iter;
2238 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2240 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2244 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2246 PluginManager& manager (PluginManager::instance());
2247 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2248 if (manager.get_status (*i) != PluginManager::Favorite) {
2251 result.push_back (*i);
2255 struct PluginCustomSorter {
2257 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2258 PluginInfoList::const_iterator aiter = _user.begin();
2259 PluginInfoList::const_iterator biter = _user.begin();
2260 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2261 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2263 if (aiter != _user.end() && biter != _user.end()) {
2264 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2266 if (aiter != _user.end()) {
2269 if (biter != _user.end()) {
2272 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2274 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2276 PluginInfoList _user;
2280 Mixer_UI::refill_favorite_plugins ()
2282 PluginInfoList plugs;
2283 PluginManager& mgr (PluginManager::instance());
2286 refiller (plugs, mgr.lv2_plugin_info ());
2288 #ifdef WINDOWS_VST_SUPPORT
2289 refiller (plugs, mgr.windows_vst_plugin_info ());
2291 #ifdef LXVST_SUPPORT
2292 refiller (plugs, mgr.lxvst_plugin_info ());
2294 #ifdef AUDIOUNIT_SUPPORT
2295 refiller (plugs, mgr.au_plugin_info ());
2297 refiller (plugs, mgr.ladspa_plugin_info ());
2298 refiller (plugs, mgr.lua_plugin_info ());
2300 store_current_favorite_order ();
2302 PluginCustomSorter cmp (favorite_order);
2305 favorite_order = plugs;
2307 sync_treeview_from_favorite_order ();
2311 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2314 if (!(iter = favorite_plugins_model->get_iter (path))) {
2317 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2321 PluginInfoPtr pip = ppp->_pip;
2322 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2323 if (favorite_ui_state[pip->unique_id]) {
2324 favorite_plugins_display.expand_row (path, true);
2330 Mixer_UI::sync_treeview_from_favorite_order ()
2332 favorite_plugins_model->clear ();
2333 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2334 PluginInfoPtr pip = (*i);
2336 TreeModel::Row newrow = *(favorite_plugins_model->append());
2337 newrow[favorite_plugins_columns.name] = (*i)->name;
2338 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2343 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2344 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2345 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2346 child_row[favorite_plugins_columns.name] = (*j).label;
2347 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2349 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2350 if (favorite_ui_state[pip->unique_id]) {
2351 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2358 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2360 using namespace Gtk::Menu_Helpers;
2362 Gtk::Menu* m = manage (new Menu);
2363 MenuList& items = m->items ();
2365 if (_selection.routes.empty()) {
2366 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2368 items.push_back (MenuElem (_("Add at the top"),
2369 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2370 items.push_back (MenuElem (_("Add Pre-Fader"),
2371 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2372 items.push_back (MenuElem (_("Add Post-Fader"),
2373 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2374 items.push_back (MenuElem (_("Add at the end"),
2375 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2378 items.push_back (SeparatorElem());
2380 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2382 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2383 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2384 // we cannot currently delete AU presets
2385 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2386 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2390 m->popup (ev->button, ev->time);
2394 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2396 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2397 TreeModel::Path path;
2398 TreeViewColumn* column;
2400 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2401 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2403 selection->unselect_all();
2404 selection->select(path);
2407 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2409 popup_note_context_menu (ev);
2417 Mixer_UI::selected_plugin ()
2419 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2421 return PluginPresetPtr();
2423 Gtk::TreeModel::iterator iter = selection->get_selected();
2425 return PluginPresetPtr();
2427 return (*iter)[favorite_plugins_columns.plugin];
2431 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2433 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2435 add_favorite_processor (ppp, pos);
2440 Mixer_UI::delete_selected_preset ()
2445 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2446 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2449 PluginPtr plugin = ppp->_pip->load (*_session);
2450 plugin->get_presets();
2451 plugin->remove_preset (ppp->_preset.label);
2455 Mixer_UI::remove_selected_from_favorites ()
2457 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2461 PluginManager::PluginStatusType status = PluginManager::Normal;
2462 PluginManager& manager (PluginManager::instance());
2464 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2465 manager.save_statuses ();
2469 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2472 if (!(iter = favorite_plugins_model->get_iter (path))) {
2475 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2476 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2480 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2482 if (!_session || _selection.routes.empty()) {
2486 PluginInfoPtr pip = ppp->_pip;
2487 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2488 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2489 if (!rt) { continue; }
2491 PluginPtr p = pip->load (*_session);
2492 if (!p) { continue; }
2494 if (ppp->_preset.valid) {
2495 p->load_preset (ppp->_preset);
2498 Route::ProcessorStreams err;
2499 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2503 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2506 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2513 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2517 if (!np->display_to_user()) {
2520 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2521 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2526 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2530 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2537 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2539 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2543 // only allow to re-order top-level items
2545 if (TreePath::get_from_selection_data (data, src)) {
2546 if (src.up() && src.up()) {
2551 // don't allow to drop as child-rows.
2552 Gtk::TreeModel::Path _dest = dest; // un const
2553 const bool is_child = _dest.up (); // explicit bool for clang
2554 if (!is_child || _dest.empty ()) {
2561 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2563 if (data.get_target() != "PluginPresetPtr") {
2566 if (data.get_length() != sizeof (PluginPresetPtr)) {
2569 const void *d = data.get_data();
2570 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2572 PluginManager::PluginStatusType status = PluginManager::Favorite;
2573 PluginManager& manager (PluginManager::instance());
2575 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2576 manager.save_statuses ();
2580 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2582 /* call protected MixerActor:: method */
2587 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2589 /* call protected MixerActor:: method */
2594 Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
2596 boost::shared_ptr<VCA> v = spilled_vca.lock();
2599 show_vca_change (vca); /* EMIT SIGNAL */
2600 redisplay_track_list ();
2605 Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
2607 return vca == spilled_vca.lock();