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