2 Copyright (C) 2000-2009 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.
27 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
29 #include "ardour/diskstream.h"
33 #include "ardour_ui.h"
34 #include "audio_time_axis.h"
35 #include "midi_time_axis.h"
36 #include "mixer_strip.h"
37 #include "gui_thread.h"
40 #include "editor_group_tabs.h"
41 #include "editor_routes.h"
43 #include "pbd/unknown_type.h"
45 #include "ardour/route.h"
51 using namespace ARDOUR;
54 using namespace Gtkmm2ext;
57 EditorRoutes::EditorRoutes (Editor* e)
58 : EditorComponent (e),
59 _ignore_reorder (false),
60 _no_redisplay (false),
61 _redisplay_does_not_sync_order_keys (false),
62 _redisplay_does_not_reset_order_keys (false),
65 _scroller.add (_display);
66 _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
68 _model = ListStore::create (_columns);
69 _display.set_model (_model);
71 CellRendererPixbufToggle* rec_col_renderer = manage (new CellRendererPixbufToggle());
73 rec_col_renderer->set_active_pixbuf (::get_icon("record_normal_red"));
74 rec_col_renderer->set_inactive_pixbuf (::get_icon("record_disabled_grey"));
76 rec_col_renderer->signal_toggled().connect (mem_fun (*this, &EditorRoutes::on_tv_rec_enable_toggled));
78 Gtk::TreeViewColumn* rec_state_column = manage (new TreeViewColumn("Rec", *rec_col_renderer));
79 rec_state_column->add_attribute(rec_col_renderer->property_active(), _columns.rec_enabled);
80 rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
82 _display.append_column (*rec_state_column);
83 _display.append_column (_("Show"), _columns.visible);
84 _display.append_column (_("Name"), _columns.text);
86 _display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
87 _display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
88 _display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
90 _display.set_headers_visible (true);
91 _display.set_name ("TrackListDisplay");
92 _display.get_selection()->set_mode (SELECTION_NONE);
93 _display.set_reorderable (true);
94 _display.set_rules_hint (true);
95 _display.set_size_request (100, -1);
96 _display.add_object_drag (_columns.route.index(), "routes");
98 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (1));
100 visible_cell->property_activatable() = true;
101 visible_cell->property_radio() = false;
103 _model->signal_row_deleted().connect (mem_fun (*this, &EditorRoutes::route_deleted));
104 _model->signal_row_changed().connect (mem_fun (*this, &EditorRoutes::changed));
105 _model->signal_rows_reordered().connect (mem_fun (*this, &EditorRoutes::reordered));
106 _display.signal_button_press_event().connect (mem_fun (*this, &EditorRoutes::button_press), false);
108 Route::SyncOrderKeys.connect (mem_fun (*this, &EditorRoutes::sync_order_keys));
112 EditorRoutes::connect_to_session (Session* s)
114 EditorComponent::connect_to_session (s);
120 EditorRoutes::on_tv_rec_enable_toggled (Glib::ustring const & path_string)
122 // Get the model row that has been toggled.
123 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
125 TimeAxisView *tv = row[_columns.tv];
126 AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
128 if (atv != 0 && atv->is_audio_track()){
129 atv->get_diskstream()->set_record_enabled(!atv->get_diskstream()->record_enabled());
134 EditorRoutes::build_menu ()
136 using namespace Menu_Helpers;
141 MenuList& items = _menu->items();
142 _menu->set_name ("ArdourContextMenu");
144 items.push_back (MenuElem (_("Show All"), mem_fun (*this, &EditorRoutes::show_all_routes)));
145 items.push_back (MenuElem (_("Hide All"), mem_fun (*this, &EditorRoutes::hide_all_routes)));
146 items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
147 items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
148 items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun (*this, &EditorRoutes::show_all_audiobus)));
149 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
154 EditorRoutes::show_menu ()
160 _menu->popup (1, gtk_get_current_event_time());
164 EditorRoutes::redisplay ()
166 TreeModel::Children rows = _model->children();
167 TreeModel::Children::iterator i;
175 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
176 TimeAxisView *tv = (*i)[_columns.tv];
177 boost::shared_ptr<Route> route = (*i)[_columns.route];
180 // just a "title" row
184 if (!_redisplay_does_not_reset_order_keys) {
186 /* this reorder is caused by user action, so reassign sort order keys
190 route->set_order_key (N_ ("editor"), n);
193 bool visible = (*i)[_columns.visible];
195 /* show or hide the TimeAxisView */
197 tv->set_marked_for_display (true);
198 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
199 tv->clip_to_viewport ();
201 tv->set_marked_for_display (false);
208 /* whenever we go idle, update the track view list to reflect the new order.
209 we can't do this here, because we could mess up something that is traversing
210 the track order and has caused a redisplay of the list.
213 Glib::signal_idle().connect (mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
215 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
216 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
218 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
220 We're increasing the size of the canvas while the bottom is visible.
221 We scroll down to keep in step with the controls layout.
223 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
226 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
227 _session->sync_order_keys (N_ ("editor"));
232 EditorRoutes::route_deleted (Gtk::TreeModel::Path const & path)
234 /* this could require an order reset & sync */
235 _session->set_remote_control_ids();
236 _ignore_reorder = true;
238 _ignore_reorder = false;
243 EditorRoutes::changed (Gtk::TreeModel::Path const & path, Gtk::TreeModel::iterator const & iter)
245 /* never reset order keys because of a property change */
246 _redisplay_does_not_reset_order_keys = true;
247 _session->set_remote_control_ids();
249 _redisplay_does_not_reset_order_keys = false;
253 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
257 _redisplay_does_not_sync_order_keys = true;
258 suspend_redisplay ();
260 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
262 row = *(_model->append ());
264 row[_columns.text] = (*x)->route()->name();
265 row[_columns.visible] = (*x)->marked_for_display();
266 row[_columns.tv] = *x;
267 row[_columns.route] = (*x)->route ();
268 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
270 _ignore_reorder = true;
272 /* added a new fresh one at the end */
273 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
274 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
277 _ignore_reorder = false;
279 boost::weak_ptr<Route> wr ((*x)->route());
280 (*x)->route()->gui_changed.connect (mem_fun (*this, &EditorRoutes::handle_gui_changes));
281 (*x)->route()->NameChanged.connect (bind (mem_fun (*this, &EditorRoutes::route_name_changed), wr));
282 (*x)->GoingAway.connect (bind (mem_fun (*this, &EditorRoutes::route_removed), *x));
284 if ((*x)->is_track()) {
285 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
286 t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &EditorRoutes::update_rec_display));
291 _redisplay_does_not_sync_order_keys = false;
295 EditorRoutes::handle_gui_changes (string const & what, void *src)
297 ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRoutes::handle_gui_changes), what, src));
299 if (what == "track_height") {
300 /* Optional :make tracks change height while it happens, instead
303 //update_canvas_now ();
307 if (what == "visible_tracks") {
313 EditorRoutes::route_removed (TimeAxisView *tv)
315 ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRoutes::route_removed), tv));
317 TreeModel::Children rows = _model->children();
318 TreeModel::Children::iterator ri;
320 /* the core model has changed, there is no need to sync
324 _redisplay_does_not_sync_order_keys = true;
326 for (ri = rows.begin(); ri != rows.end(); ++ri) {
327 if ((*ri)[_columns.tv] == tv) {
333 _redisplay_does_not_sync_order_keys = false;
337 EditorRoutes::route_name_changed (boost::weak_ptr<Route> r)
339 ENSURE_GUI_THREAD (bind (mem_fun (*this, &EditorRoutes::route_name_changed), r));
341 boost::shared_ptr<Route> route = r.lock ();
346 TreeModel::Children rows = _model->children();
347 TreeModel::Children::iterator i;
349 for (i = rows.begin(); i != rows.end(); ++i) {
350 boost::shared_ptr<Route> t = (*i)[_columns.route];
352 (*i)[_columns.text] = route->name();
359 EditorRoutes::update_visibility ()
361 TreeModel::Children rows = _model->children();
362 TreeModel::Children::iterator i;
364 suspend_redisplay ();
366 for (i = rows.begin(); i != rows.end(); ++i) {
367 TimeAxisView *tv = (*i)[_columns.tv];
368 (*i)[_columns.visible] = tv->marked_for_display ();
369 cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
376 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
378 TreeModel::Children rows = _model->children();
379 TreeModel::Children::iterator i;
381 for (i = rows.begin(); i != rows.end(); ++i) {
382 if ((*i)[_columns.tv] == &tv) {
383 (*i)[_columns.visible] = false;
390 EditorRoutes::show_track_in_display (TimeAxisView& tv)
392 TreeModel::Children rows = _model->children();
393 TreeModel::Children::iterator i;
395 for (i = rows.begin(); i != rows.end(); ++i) {
396 if ((*i)[_columns.tv] == &tv) {
397 (*i)[_columns.visible] = true;
404 EditorRoutes::reordered (TreeModel::Path const & path, TreeModel::iterator const & iter, int* what)
409 /** If src == "editor", take editor order keys from each route and use them to rearrange the
410 * route list so that the visual arrangement of routes matches the order keys from the routes.
413 EditorRoutes::sync_order_keys (string const & src)
415 vector<int> neworder;
416 TreeModel::Children rows = _model->children();
417 TreeModel::Children::iterator ri;
419 if (src != N_ ("editor") || !_session || (_session->state_of_the_state() & Session::Loading) || rows.empty()) {
423 for (ri = rows.begin(); ri != rows.end(); ++ri) {
424 neworder.push_back (0);
427 bool changed = false;
430 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
431 boost::shared_ptr<Route> route = (*ri)[_columns.route];
434 int new_key = route->order_key (N_ ("editor"));
436 neworder[new_key] = old_key;
438 if (new_key != old_key) {
444 _redisplay_does_not_reset_order_keys = true;
445 _model->reorder (neworder);
446 _redisplay_does_not_reset_order_keys = false;
452 EditorRoutes::hide_all_tracks (bool with_select)
454 TreeModel::Children rows = _model->children();
455 TreeModel::Children::iterator i;
457 suspend_redisplay ();
459 for (i = rows.begin(); i != rows.end(); ++i) {
461 TreeModel::Row row = (*i);
462 TimeAxisView *tv = row[_columns.tv];
468 row[_columns.visible] = false;
473 /* XXX this seems like a hack and half, but its not clear where to put this
477 //reset_scrolling_region ();
481 EditorRoutes::set_all_tracks_visibility (bool yn)
483 TreeModel::Children rows = _model->children();
484 TreeModel::Children::iterator i;
486 suspend_redisplay ();
488 for (i = rows.begin(); i != rows.end(); ++i) {
490 TreeModel::Row row = (*i);
491 TimeAxisView* tv = row[_columns.tv];
497 (*i)[_columns.visible] = yn;
504 EditorRoutes::set_all_audio_visibility (int tracks, bool yn)
506 TreeModel::Children rows = _model->children();
507 TreeModel::Children::iterator i;
509 suspend_redisplay ();
511 for (i = rows.begin(); i != rows.end(); ++i) {
512 TreeModel::Row row = (*i);
513 TimeAxisView* tv = row[_columns.tv];
514 AudioTimeAxisView* atv;
520 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
523 (*i)[_columns.visible] = yn;
527 if (atv->is_audio_track()) {
528 (*i)[_columns.visible] = yn;
533 if (!atv->is_audio_track()) {
534 (*i)[_columns.visible] = yn;
545 EditorRoutes::hide_all_routes ()
547 set_all_tracks_visibility (false);
551 EditorRoutes::show_all_routes ()
553 set_all_tracks_visibility (true);
557 EditorRoutes::show_all_audiobus ()
559 set_all_audio_visibility (2, true);
562 EditorRoutes::hide_all_audiobus ()
564 set_all_audio_visibility (2, false);
568 EditorRoutes::show_all_audiotracks()
570 set_all_audio_visibility (1, true);
573 EditorRoutes::hide_all_audiotracks ()
575 set_all_audio_visibility (1, false);
579 EditorRoutes::button_press (GdkEventButton* ev)
581 if (Keyboard::is_context_menu_event (ev)) {
587 TreeModel::Path path;
588 TreeViewColumn* column;
592 if (!_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
596 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
599 /* allow normal processing to occur */
602 if ((iter = _model->get_iter (path))) {
603 TimeAxisView* tv = (*iter)[_columns.tv];
605 bool visible = (*iter)[_columns.visible];
606 (*iter)[_columns.visible] = !visible;
612 /* allow normal processing to occur */
623 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const &, bool)
628 struct EditorOrderRouteSorter {
629 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
630 /* use of ">" forces the correct sort order */
631 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
636 EditorRoutes::initial_display ()
638 boost::shared_ptr<RouteList> routes = _session->get_routes();
639 RouteList r (*routes);
640 EditorOrderRouteSorter sorter;
644 suspend_redisplay ();
647 _editor->handle_new_route (r);
649 /* don't show master bus in a new session */
651 if (ARDOUR_UI::instance()->session_is_new ()) {
653 TreeModel::Children rows = _model->children();
654 TreeModel::Children::iterator i;
656 _no_redisplay = true;
658 for (i = rows.begin(); i != rows.end(); ++i) {
659 TimeAxisView *tv = (*i)[_columns.tv];
660 RouteTimeAxisView *rtv;
662 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
663 if (rtv->route()->is_master()) {
664 _display.get_selection()->unselect (i);
669 _no_redisplay = false;
677 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const & path, Gtk::TreeModel::iterator const & iter, int* new_order)
679 _redisplay_does_not_sync_order_keys = true;
680 _session->set_remote_control_ids();
682 _redisplay_does_not_sync_order_keys = false;
686 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
688 const SelectionData& data,
689 guint info, guint time)
691 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
692 _display.on_drag_data_received (context, x, y, data, info, time);
696 context->drag_finish (true, false, time);
700 EditorRoutes::move_selected_tracks (bool up)
702 if (_editor->selection->tracks.empty()) {
706 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
707 std::list<ViewRoute> view_routes;
708 std::vector<int> neworder;
709 TreeModel::Children rows = _model->children();
710 TreeModel::Children::iterator ri;
712 for (ri = rows.begin(); ri != rows.end(); ++ri) {
713 TimeAxisView* tv = (*ri)[_columns.tv];
714 boost::shared_ptr<Route> route = (*ri)[_columns.route];
716 view_routes.push_back (ViewRoute (tv, route));
719 list<ViewRoute>::iterator trailing;
720 list<ViewRoute>::iterator leading;
724 trailing = view_routes.begin();
725 leading = view_routes.begin();
729 while (leading != view_routes.end()) {
730 if (_editor->selection->selected (leading->first)) {
731 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
732 leading = view_routes.erase (leading);
741 /* if we could use reverse_iterator in list::insert, this code
742 would be a beautiful reflection of the code above. but we can't
743 and so it looks like a bit of a mess.
746 trailing = view_routes.end();
747 leading = view_routes.end();
749 --leading; if (leading == view_routes.begin()) { return; }
755 if (_editor->selection->selected (leading->first)) {
756 list<ViewRoute>::iterator tmp;
758 /* need to insert *after* trailing, not *before* it,
759 which is what insert (iter, val) normally does.
765 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
767 /* can't use iter = cont.erase (iter); form here, because
768 we need iter to move backwards.
776 if (leading == view_routes.begin()) {
777 /* the one we've just inserted somewhere else
778 was the first in the list. erase this copy,
779 and then break, because we're done.
784 view_routes.erase (leading);
793 if (leading == view_routes.begin()) {
802 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
803 neworder.push_back (leading->second->order_key (N_ ("editor")));
806 _model->reorder (neworder);
808 _session->sync_order_keys (N_ ("editor"));
812 EditorRoutes::update_rec_display ()
814 TreeModel::Children rows = _model->children();
815 TreeModel::Children::iterator i;
817 for (i = rows.begin(); i != rows.end(); ++i) {
818 boost::shared_ptr<Route> route = (*i)[_columns.route];
820 if (boost::dynamic_pointer_cast<Track>(route)) {
822 if (route->record_enabled()){
823 (*i)[_columns.rec_enabled] = true;
825 (*i)[_columns.rec_enabled] = false;
832 EditorRoutes::views () const
834 list<TimeAxisView*> v;
835 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
836 v.push_back ((*i)[_columns.tv]);
843 EditorRoutes::clear ()
845 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
847 _display.set_model (_model);