+ for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
+
+ neworder[n] = sr->old_display_order;
+
+ if (sr->old_display_order != n) {
+ changed = true;
+ }
+
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("MIXER change order for %1 from %2 to %3\n",
+ sr->route->name(), sr->old_display_order, n));
+ }
+
+ if (changed) {
+ Unwinder<bool> uw (ignore_reorder, true);
+ track_model->reorder (neworder);
+ }
+
+ redisplay_track_list ();
+}
+
+void
+Mixer_UI::follow_editor_selection ()
+{
+ if (_following_editor_selection) {
+ return;
+ }
+
+ _following_editor_selection = true;
+ _selection.block_routes_changed (true);
+
+ TrackSelection& s (PublicEditor::instance().get_selection().tracks);
+
+ _selection.clear_routes ();
+
+ for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
+ RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
+ if (rtav) {
+ MixerStrip* ms = strip_by_route (rtav->route());
+ if (ms) {
+ _selection.add (ms);
+ }
+ }
+ }
+
+ _following_editor_selection = false;
+ _selection.block_routes_changed (false);
+}
+
+
+MixerStrip*
+Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
+{
+ for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
+ if ((*i)->route() == r) {
+ return (*i);
+ }
+ }
+
+ return 0;
+}
+
+bool
+Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
+{
+ if (ev->button == 1) {
+ if (_selection.selected (strip)) {
+ /* primary-click: toggle selection state of strip */
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+ _selection.remove (strip);
+ } else if (_selection.routes.size() > 1) {
+ /* de-select others */
+ _selection.set (strip);
+ }
+ } else {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+ _selection.add (strip);
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
+
+ if (!_selection.selected(strip)) {
+
+ /* extend selection */
+
+ vector<MixerStrip*> tmp;
+ bool accumulate = false;
+ bool found_another = false;
+
+ tmp.push_back (strip);
+
+ for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
+ if ((*i) == strip) {
+ /* hit clicked strip, start accumulating till we hit the first
+ selected strip
+ */
+ if (accumulate) {
+ /* done */
+ break;
+ } else {
+ accumulate = true;
+ }
+ } else if (_selection.selected (*i)) {
+ /* hit selected strip. if currently accumulating others,
+ we're done. if not accumulating others, start doing so.
+ */
+ found_another = true;
+ if (accumulate) {
+ /* done */
+ break;
+ } else {
+ accumulate = true;
+ }
+ } else {
+ if (accumulate) {
+ tmp.push_back (*i);
+ }
+ }
+ }
+
+ if (found_another) {
+ for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
+ _selection.add (*i);
+ }
+ } else
+ _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
+ }
+
+ } else {
+ _selection.set (strip);
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+Mixer_UI::set_session (Session* sess)
+{
+ SessionHandlePtr::set_session (sess);
+
+ if (_plugin_selector) {
+ _plugin_selector->set_session (_session);
+ }
+
+ _group_tabs->set_session (sess);
+
+ if (!_session) {
+ return;
+ }
+
+ XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
+ set_state (*node);
+
+ update_title ();
+
+ initial_track_display ();
+
+ _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
+ _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
+ _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
+ _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
+ _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
+ _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
+ _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
+
+ Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
+
+ route_groups_changed ();
+
+ if (_visible) {
+ show_window();
+ }
+
+ start_updating ();
+}
+
+void
+Mixer_UI::session_going_away ()
+{
+ ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
+
+ _in_group_rebuild_or_clear = true;
+ group_model->clear ();
+ _in_group_rebuild_or_clear = false;
+
+ _selection.clear ();
+ track_model->clear ();
+
+ for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
+ delete (*i);
+ }
+
+ if (_monitor_section) {
+ _monitor_section->tearoff().hide_visible ();
+ }
+
+ strips.clear ();
+
+ stop_updating ();
+
+ SessionHandlePtr::session_going_away ();
+
+ _session = 0;
+ update_title ();
+}
+
+void
+Mixer_UI::track_visibility_changed (std::string const & path)
+{
+ if (_session && _session->deletion_in_progress()) {
+ return;
+ }
+
+ TreeIter iter;
+
+ if ((iter = track_model->get_iter (path))) {
+ MixerStrip* strip = (*iter)[track_columns.strip];
+ if (strip) {
+ bool visible = (*iter)[track_columns.visible];
+
+ if (strip->set_marked_for_display (!visible)) {
+ update_track_visibility ();
+ }
+ }
+ }
+}
+
+void
+Mixer_UI::update_track_visibility ()
+{
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+
+ {
+ Unwinder<bool> uw (no_track_list_redisplay, true);
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+ MixerStrip *strip = (*i)[track_columns.strip];
+ (*i)[track_columns.visible] = strip->marked_for_display ();
+ }
+
+ /* force route order keys catch up with visibility changes
+ */
+
+ sync_order_keys_from_treeview ();
+ }
+
+ redisplay_track_list ();
+}
+
+void
+Mixer_UI::show_strip (MixerStrip* ms)
+{
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+
+ MixerStrip* strip = (*i)[track_columns.strip];
+ if (strip == ms) {
+ (*i)[track_columns.visible] = true;
+ redisplay_track_list ();
+ break;
+ }
+ }
+}
+
+void
+Mixer_UI::hide_strip (MixerStrip* ms)
+{
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+
+ MixerStrip* strip = (*i)[track_columns.strip];
+ if (strip == ms) {
+ (*i)[track_columns.visible] = false;
+ redisplay_track_list ();
+ break;
+ }
+ }
+}
+
+gint
+Mixer_UI::start_updating ()
+{
+ fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
+ return 0;
+}
+
+gint
+Mixer_UI::stop_updating ()
+{
+ fast_screen_update_connection.disconnect();
+ return 0;
+}
+
+void
+Mixer_UI::fast_update_strips ()
+{
+ if (is_mapped () && _session) {
+ for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
+ (*i)->fast_update ();
+ }
+ }
+}
+
+void
+Mixer_UI::set_all_strips_visibility (bool yn)
+{
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+
+ {
+ Unwinder<bool> uw (no_track_list_redisplay, true);
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+
+ TreeModel::Row row = (*i);
+ MixerStrip* strip = row[track_columns.strip];
+
+ if (strip == 0) {
+ continue;
+ }
+
+ if (strip->route()->is_master() || strip->route()->is_monitor()) {
+ continue;
+ }
+
+ (*i)[track_columns.visible] = yn;
+ }
+ }
+
+ redisplay_track_list ();
+}
+
+
+void
+Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
+{
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+
+ {
+ Unwinder<bool> uw (no_track_list_redisplay, true);
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+ TreeModel::Row row = (*i);
+ MixerStrip* strip = row[track_columns.strip];
+
+ if (strip == 0) {
+ continue;
+ }
+
+ if (strip->route()->is_master() || strip->route()->is_monitor()) {
+ continue;
+ }
+
+ boost::shared_ptr<AudioTrack> at = strip->audio_track();
+ boost::shared_ptr<MidiTrack> mt = strip->midi_track();
+
+ switch (tracks) {
+ case 0:
+ (*i)[track_columns.visible] = yn;
+ break;
+
+ case 1:
+ if (at) { /* track */
+ (*i)[track_columns.visible] = yn;
+ }
+ break;
+
+ case 2:
+ if (!at && !mt) { /* bus */
+ (*i)[track_columns.visible] = yn;
+ }
+ break;
+
+ case 3:
+ if (mt) { /* midi-track */
+ (*i)[track_columns.visible] = yn;
+ }
+ break;
+ }
+ }
+ }
+
+ redisplay_track_list ();
+}
+
+void
+Mixer_UI::hide_all_routes ()
+{
+ set_all_strips_visibility (false);
+}
+
+void
+Mixer_UI::show_all_routes ()
+{
+ set_all_strips_visibility (true);
+}
+
+void
+Mixer_UI::show_all_audiobus ()
+{
+ set_all_audio_midi_visibility (2, true);
+}
+void
+Mixer_UI::hide_all_audiobus ()
+{
+ set_all_audio_midi_visibility (2, false);
+}
+
+void
+Mixer_UI::show_all_audiotracks()
+{
+ set_all_audio_midi_visibility (1, true);
+}
+void
+Mixer_UI::hide_all_audiotracks ()
+{
+ set_all_audio_midi_visibility (1, false);
+}
+
+void
+Mixer_UI::show_all_miditracks()
+{
+ set_all_audio_midi_visibility (3, true);
+}
+void
+Mixer_UI::hide_all_miditracks ()
+{
+ set_all_audio_midi_visibility (3, false);
+}
+
+
+void
+Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
+{
+ DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
+ sync_order_keys_from_treeview ();
+}
+
+void
+Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
+{
+ /* this happens as the second step of a DnD within the treeview as well
+ as when a row/route is actually deleted.
+
+ if it was a deletion then we have to force a redisplay because
+ order keys may not have changed.
+ */
+
+ DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
+ sync_order_keys_from_treeview ();
+
+ if (_route_deletion_in_progress) {
+ redisplay_track_list ();
+ }
+}
+
+void
+Mixer_UI::redisplay_track_list ()
+{
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+
+ if (no_track_list_redisplay) {
+ return;
+ }
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+
+ MixerStrip* strip = (*i)[track_columns.strip];
+
+ if (strip == 0) {
+ /* we're in the middle of changing a row, don't worry */
+ continue;
+ }
+
+ bool const visible = (*i)[track_columns.visible];
+
+ if (visible) {
+ strip->set_gui_property ("visible", true);
+
+ if (strip->packed()) {
+
+ if (strip->route()->is_master() || strip->route()->is_monitor()) {
+ out_packer.reorder_child (*strip, -1);
+
+ } else {
+ strip_packer.reorder_child (*strip, -1); /* put at end */
+ }
+
+ } else {
+
+ if (strip->route()->is_master() || strip->route()->is_monitor()) {
+ out_packer.pack_start (*strip, false, false);
+ } else {
+ strip_packer.pack_start (*strip, false, false);
+ }
+ strip->set_packed (true);
+ }
+
+ } else {
+
+ strip->set_gui_property ("visible", false);
+
+ if (strip->route()->is_master() || strip->route()->is_monitor()) {
+ /* do nothing, these cannot be hidden */
+ } else {
+ if (strip->packed()) {
+ strip_packer.remove (*strip);
+ strip->set_packed (false);
+ }
+ }
+ }
+ }
+
+ _group_tabs->set_dirty ();
+}
+
+void
+Mixer_UI::strip_width_changed ()
+{
+ _group_tabs->set_dirty ();
+
+#ifdef GTKOSX
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+ long order;
+
+ for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
+ MixerStrip* strip = (*i)[track_columns.strip];
+
+ if (strip == 0) {
+ continue;
+ }
+
+ bool visible = (*i)[track_columns.visible];
+
+ if (visible) {
+ strip->queue_draw();
+ }
+ }
+#endif
+
+}
+
+void
+Mixer_UI::initial_track_display ()
+{
+ boost::shared_ptr<RouteList> routes = _session->get_routes();
+ RouteList copy (*routes);
+ ARDOUR::SignalOrderRouteSorter sorter;
+
+ copy.sort (sorter);
+
+ {
+ Unwinder<bool> uw1 (no_track_list_redisplay, true);
+ Unwinder<bool> uw2 (ignore_reorder, true);
+
+ track_model->clear ();
+ add_strips (copy);
+ }
+
+ _session->sync_order_keys ();
+
+ redisplay_track_list ();
+}
+
+void
+Mixer_UI::show_track_list_menu ()
+{
+ if (track_menu == 0) {
+ build_track_menu ();
+ }
+
+ track_menu->popup (1, gtk_get_current_event_time());
+}
+
+bool
+Mixer_UI::track_display_button_press (GdkEventButton* ev)
+{
+ if (Keyboard::is_context_menu_event (ev)) {
+ show_track_list_menu ();
+ return true;
+ }
+
+ return false;
+}
+
+void
+Mixer_UI::build_track_menu ()
+{
+ using namespace Menu_Helpers;
+ using namespace Gtk;
+
+ track_menu = new Menu;
+ track_menu->set_name ("ArdourContextMenu");
+ MenuList& items = track_menu->items();
+
+ items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
+ items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
+ items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
+ items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
+ items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
+ items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
+ items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
+ items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
+
+}
+
+void
+Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
+{
+ if (!what_changed.contains (ARDOUR::Properties::name)) {
+ return;
+ }
+
+ ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
+
+ TreeModel::Children rows = track_model->children();
+ TreeModel::Children::iterator i;
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+ if ((*i)[track_columns.strip] == mx) {
+ (*i)[track_columns.text] = mx->route()->name();
+ return;
+ }
+ }
+
+ error << _("track display list item for renamed strip not found!") << endmsg;
+}
+
+bool
+Mixer_UI::group_display_button_press (GdkEventButton* ev)
+{
+ TreeModel::Path path;
+ TreeViewColumn* column;
+ int cellx;
+ int celly;
+
+ if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
+ _group_tabs->get_menu(0)->popup (1, ev->time);
+ return true;
+ }
+
+ TreeIter iter = group_model->get_iter (path);
+ if (!iter) {
+ _group_tabs->get_menu(0)->popup (1, ev->time);
+ return true;
+ }
+
+ RouteGroup* group = (*iter)[group_columns.group];
+
+ if (Keyboard::is_context_menu_event (ev)) {
+ _group_tabs->get_menu(group)->popup (1, ev->time);
+ return true;
+ }
+
+ switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
+ case 0:
+ if (Keyboard::is_edit_event (ev)) {
+ if (group) {
+ // edit_route_group (group);
+#ifdef GTKOSX
+ group_display.queue_draw();
+#endif
+ return true;
+ }
+ }
+ break;
+
+ case 1:
+ {
+ bool visible = (*iter)[group_columns.visible];
+ (*iter)[group_columns.visible] = !visible;
+#ifdef GTKOSX
+ group_display.queue_draw();
+#endif
+ return true;
+ }
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+void
+Mixer_UI::activate_all_route_groups ()
+{
+ _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
+}
+
+void
+Mixer_UI::disable_all_route_groups ()
+{
+ _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
+}
+
+void
+Mixer_UI::route_groups_changed ()
+{
+ ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
+
+ _in_group_rebuild_or_clear = true;
+
+ /* just rebuild the while thing */
+
+ group_model->clear ();
+
+#if 0
+ /* this is currently not used,
+ * Mixer_UI::group_display_button_press() has a case for it,
+ * and a commented edit_route_group() but that's n/a since 2011.
+ *
+ * This code is left as reminder that
+ * row[group_columns.group] = 0 has special meaning.
+ */
+ {
+ TreeModel::Row row;
+ row = *(group_model->append());
+ row[group_columns.visible] = true;
+ row[group_columns.text] = (_("-all-"));
+ row[group_columns.group] = 0;
+ }
+#endif
+
+ _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
+
+ _group_tabs->set_dirty ();
+ _in_group_rebuild_or_clear = false;
+}
+
+void
+Mixer_UI::new_route_group ()
+{
+ RouteList rl;
+
+ _group_tabs->run_new_group_dialog (rl);
+}
+
+void
+Mixer_UI::remove_selected_route_group ()
+{
+ Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
+ TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
+
+ if (rows.empty()) {
+ return;
+ }
+
+ TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
+ TreeIter iter;
+
+ /* selection mode is single, so rows.begin() is it */
+
+ if ((iter = group_model->get_iter (*i))) {
+
+ RouteGroup* rg = (*iter)[group_columns.group];
+
+ if (rg) {
+ _session->remove_route_group (*rg);
+ }
+ }
+}
+
+void
+Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
+{
+ if (in_group_row_change) {
+ return;
+ }
+
+ /* force an update of any mixer strips that are using this group,
+ otherwise mix group names don't change in mixer strips
+ */
+
+ for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
+ if ((*i)->route_group() == group) {
+ (*i)->route_group_changed();
+ }
+ }
+
+ TreeModel::iterator i;
+ TreeModel::Children rows = group_model->children();
+ Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
+
+ in_group_row_change = true;
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+ if ((*i)[group_columns.group] == group) {
+ (*i)[group_columns.visible] = !group->is_hidden ();
+ (*i)[group_columns.text] = group->name ();
+ break;
+ }
+ }
+
+ in_group_row_change = false;
+
+ if (change.contains (Properties::name)) {
+ _group_tabs->set_dirty ();
+ }
+
+ for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
+ if ((*j)->route_group() == group) {
+ if (group->is_hidden ()) {
+ hide_strip (*j);
+ } else {
+ show_strip (*j);
+ }
+ }
+ }
+}
+
+void
+Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
+{
+ RouteGroup* group;
+ TreeIter iter;
+
+ if ((iter = group_model->get_iter (path))) {
+
+ if ((group = (*iter)[group_columns.group]) == 0) {
+ return;
+ }
+
+ if (new_text != group->name()) {
+ group->set_name (new_text);
+ }
+ }
+}
+
+void
+Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
+{
+ RouteGroup* group;
+
+ if (in_group_row_change) {
+ return;
+ }
+
+ if ((group = (*iter)[group_columns.group]) == 0) {
+ return;
+ }
+
+ std::string name = (*iter)[group_columns.text];
+
+ if (name != group->name()) {
+ group->set_name (name);
+ }
+
+ bool hidden = !(*iter)[group_columns.visible];
+
+ if (hidden != group->is_hidden ()) {
+ group->set_hidden (hidden, this);
+ }
+}
+
+/** Called when a group model row is deleted, but also when the model is
+ * reordered by a user drag-and-drop; the latter is what we are
+ * interested in here.
+ */
+void
+Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
+{
+ if (_in_group_rebuild_or_clear) {
+ return;
+ }
+
+ /* Re-write the session's route group list so that the new order is preserved */
+
+ list<RouteGroup*> new_list;
+
+ Gtk::TreeModel::Children children = group_model->children();
+ for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
+ RouteGroup* g = (*i)[group_columns.group];
+ if (g) {
+ new_list.push_back (g);
+ }
+ }
+
+ _session->reorder_route_groups (new_list);
+}
+
+
+void
+Mixer_UI::add_route_group (RouteGroup* group)
+{
+ ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
+ bool focus = false;
+
+ in_group_row_change = true;
+
+ TreeModel::Row row = *(group_model->append());
+ row[group_columns.visible] = !group->is_hidden ();
+ row[group_columns.group] = group;
+ if (!group->name().empty()) {
+ row[group_columns.text] = group->name();
+ } else {
+ row[group_columns.text] = _("unnamed");
+ focus = true;
+ }
+
+ group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
+
+ if (focus) {
+ TreeViewColumn* col = group_display.get_column (0);
+ CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
+ group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
+ }
+
+ _group_tabs->set_dirty ();
+
+ in_group_row_change = false;