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::on_tv_rec_enable_toggled (Glib::ustring const & path_string)
114 // Get the model row that has been toggled.
115 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
117 TimeAxisView *tv = row[_columns.tv];
118 AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
120 if (atv != 0 && atv->is_audio_track()){
121 atv->get_diskstream()->set_record_enabled(!atv->get_diskstream()->record_enabled());
126 EditorRoutes::build_menu ()
128 using namespace Menu_Helpers;
133 MenuList& items = _menu->items();
134 _menu->set_name ("ArdourContextMenu");
136 items.push_back (MenuElem (_("Show All"), mem_fun (*this, &EditorRoutes::show_all_routes)));
137 items.push_back (MenuElem (_("Hide All"), mem_fun (*this, &EditorRoutes::hide_all_routes)));
138 items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
139 items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
140 items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun (*this, &EditorRoutes::show_all_audiobus)));
141 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
146 EditorRoutes::show_menu ()
152 _menu->popup (1, gtk_get_current_event_time());
155 const char* _order_key = N_("editor");
158 EditorRoutes::redisplay ()
160 TreeModel::Children rows = _model->children();
161 TreeModel::Children::iterator i;
169 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
170 TimeAxisView *tv = (*i)[_columns.tv];
171 boost::shared_ptr<Route> route = (*i)[_columns.route];
174 // just a "title" row
178 if (!_redisplay_does_not_reset_order_keys) {
180 /* this reorder is caused by user action, so reassign sort order keys
184 route->set_order_key (_order_key, n);
187 bool visible = (*i)[_columns.visible];
189 /* show or hide the TimeAxisView */
191 tv->set_marked_for_display (true);
192 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
193 tv->clip_to_viewport ();
195 tv->set_marked_for_display (false);
202 /* whenever we go idle, update the track view list to reflect the new order.
203 we can't do this here, because we could mess up something that is traversing
204 the track order and has caused a redisplay of the list.
207 Glib::signal_idle().connect (mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
209 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
210 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
212 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
214 We're increasing the size of the canvas while the bottom is visible.
215 We scroll down to keep in step with the controls layout.
217 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
220 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
221 _editor->current_session()->sync_order_keys (_order_key);
226 EditorRoutes::route_deleted (Gtk::TreeModel::Path const & path)
228 /* this could require an order reset & sync */
229 _editor->current_session()->set_remote_control_ids();
230 _ignore_reorder = true;
232 _ignore_reorder = false;
237 EditorRoutes::changed (Gtk::TreeModel::Path const & path, Gtk::TreeModel::iterator const & iter)
239 /* never reset order keys because of a property change */
240 _redisplay_does_not_reset_order_keys = true;
241 _editor->current_session()->set_remote_control_ids();
243 _redisplay_does_not_reset_order_keys = false;
247 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
251 _redisplay_does_not_sync_order_keys = true;
252 suspend_redisplay ();
254 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
256 row = *(_model->append ());
258 row[_columns.text] = (*x)->route()->name();
259 row[_columns.visible] = (*x)->marked_for_display();
260 row[_columns.tv] = *x;
261 row[_columns.route] = (*x)->route ();
262 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
264 _ignore_reorder = true;
266 /* added a new fresh one at the end */
267 if ((*x)->route()->order_key(_order_key) == -1) {
268 (*x)->route()->set_order_key (_order_key, _model->children().size()-1);
271 _ignore_reorder = false;
273 boost::weak_ptr<Route> wr ((*x)->route());
274 (*x)->route()->gui_changed.connect (mem_fun (*this, &EditorRoutes::handle_gui_changes));
275 (*x)->route()->NameChanged.connect (bind (mem_fun (*this, &EditorRoutes::route_name_changed), wr));
276 (*x)->GoingAway.connect (bind (mem_fun (*this, &EditorRoutes::route_removed), *x));
278 if ((*x)->is_track()) {
279 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
280 t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &EditorRoutes::update_rec_display));
285 _redisplay_does_not_sync_order_keys = false;
289 EditorRoutes::handle_gui_changes (string const & what, void *src)
291 ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRoutes::handle_gui_changes), what, src));
293 if (what == "track_height") {
294 /* Optional :make tracks change height while it happens, instead
297 //update_canvas_now ();
301 if (what == "visible_tracks") {
307 EditorRoutes::route_removed (TimeAxisView *tv)
309 ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRoutes::route_removed), tv));
311 TreeModel::Children rows = _model->children();
312 TreeModel::Children::iterator ri;
314 /* the core model has changed, there is no need to sync
318 _redisplay_does_not_sync_order_keys = true;
320 for (ri = rows.begin(); ri != rows.end(); ++ri) {
321 if ((*ri)[_columns.tv] == tv) {
327 _redisplay_does_not_sync_order_keys = false;
331 EditorRoutes::route_name_changed (boost::weak_ptr<Route> r)
333 ENSURE_GUI_THREAD (bind (mem_fun (*this, &EditorRoutes::route_name_changed), r));
335 boost::shared_ptr<Route> route = r.lock ();
340 TreeModel::Children rows = _model->children();
341 TreeModel::Children::iterator i;
343 for (i = rows.begin(); i != rows.end(); ++i) {
344 boost::shared_ptr<Route> t = (*i)[_columns.route];
346 (*i)[_columns.text] = route->name();
353 EditorRoutes::update_visibility ()
355 TreeModel::Children rows = _model->children();
356 TreeModel::Children::iterator i;
358 suspend_redisplay ();
360 for (i = rows.begin(); i != rows.end(); ++i) {
361 TimeAxisView *tv = (*i)[_columns.tv];
362 (*i)[_columns.visible] = tv->marked_for_display ();
363 cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
370 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
372 TreeModel::Children rows = _model->children();
373 TreeModel::Children::iterator i;
375 for (i = rows.begin(); i != rows.end(); ++i) {
376 if ((*i)[_columns.tv] == &tv) {
377 (*i)[_columns.visible] = false;
384 EditorRoutes::show_track_in_display (TimeAxisView& tv)
386 TreeModel::Children rows = _model->children();
387 TreeModel::Children::iterator i;
389 for (i = rows.begin(); i != rows.end(); ++i) {
390 if ((*i)[_columns.tv] == &tv) {
391 (*i)[_columns.visible] = true;
398 EditorRoutes::reordered (TreeModel::Path const & path, TreeModel::iterator const & iter, int* what)
405 EditorRoutes::sync_order_keys (char const * src)
407 vector<int> neworder;
408 TreeModel::Children rows = _model->children();
409 TreeModel::Children::iterator ri;
411 ARDOUR::Session* s = _editor->current_session ();
413 if ((strcmp (src, _order_key) == 0) || !s || (s->state_of_the_state() & Session::Loading) || rows.empty()) {
417 for (ri = rows.begin(); ri != rows.end(); ++ri) {
418 neworder.push_back (0);
421 bool changed = false;
424 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
425 boost::shared_ptr<Route> route = (*ri)[_columns.route];
428 int new_key = route->order_key (_order_key);
430 neworder[new_key] = old_key;
432 if (new_key != old_key) {
438 _redisplay_does_not_reset_order_keys = true;
439 _model->reorder (neworder);
440 _redisplay_does_not_reset_order_keys = false;
446 EditorRoutes::hide_all_tracks (bool with_select)
448 TreeModel::Children rows = _model->children();
449 TreeModel::Children::iterator i;
451 suspend_redisplay ();
453 for (i = rows.begin(); i != rows.end(); ++i) {
455 TreeModel::Row row = (*i);
456 TimeAxisView *tv = row[_columns.tv];
462 row[_columns.visible] = false;
467 /* XXX this seems like a hack and half, but its not clear where to put this
471 //reset_scrolling_region ();
475 EditorRoutes::set_all_tracks_visibility (bool yn)
477 TreeModel::Children rows = _model->children();
478 TreeModel::Children::iterator i;
480 suspend_redisplay ();
482 for (i = rows.begin(); i != rows.end(); ++i) {
484 TreeModel::Row row = (*i);
485 TimeAxisView* tv = row[_columns.tv];
491 (*i)[_columns.visible] = yn;
498 EditorRoutes::set_all_audio_visibility (int tracks, bool yn)
500 TreeModel::Children rows = _model->children();
501 TreeModel::Children::iterator i;
503 suspend_redisplay ();
505 for (i = rows.begin(); i != rows.end(); ++i) {
506 TreeModel::Row row = (*i);
507 TimeAxisView* tv = row[_columns.tv];
508 AudioTimeAxisView* atv;
514 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
517 (*i)[_columns.visible] = yn;
521 if (atv->is_audio_track()) {
522 (*i)[_columns.visible] = yn;
527 if (!atv->is_audio_track()) {
528 (*i)[_columns.visible] = yn;
539 EditorRoutes::hide_all_routes ()
541 set_all_tracks_visibility (false);
545 EditorRoutes::show_all_routes ()
547 set_all_tracks_visibility (true);
551 EditorRoutes::show_all_audiobus ()
553 set_all_audio_visibility (2, true);
556 EditorRoutes::hide_all_audiobus ()
558 set_all_audio_visibility (2, false);
562 EditorRoutes::show_all_audiotracks()
564 set_all_audio_visibility (1, true);
567 EditorRoutes::hide_all_audiotracks ()
569 set_all_audio_visibility (1, false);
573 EditorRoutes::button_press (GdkEventButton* ev)
575 if (Keyboard::is_context_menu_event (ev)) {
581 TreeModel::Path path;
582 TreeViewColumn* column;
586 if (!_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
590 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
593 /* allow normal processing to occur */
596 if ((iter = _model->get_iter (path))) {
597 TimeAxisView* tv = (*iter)[_columns.tv];
599 bool visible = (*iter)[_columns.visible];
600 (*iter)[_columns.visible] = !visible;
606 /* allow normal processing to occur */
617 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const &, bool)
622 struct EditorOrderRouteSorter {
623 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
624 /* use of ">" forces the correct sort order */
625 return a->order_key (_order_key) < b->order_key (_order_key);
630 EditorRoutes::initial_display ()
632 boost::shared_ptr<RouteList> routes = _editor->current_session()->get_routes();
633 RouteList r (*routes);
634 EditorOrderRouteSorter sorter;
638 suspend_redisplay ();
641 _editor->handle_new_route (r);
643 /* don't show master bus in a new session */
645 if (ARDOUR_UI::instance()->session_is_new ()) {
647 TreeModel::Children rows = _model->children();
648 TreeModel::Children::iterator i;
650 _no_redisplay = true;
652 for (i = rows.begin(); i != rows.end(); ++i) {
653 TimeAxisView *tv = (*i)[_columns.tv];
654 RouteTimeAxisView *rtv;
656 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
657 if (rtv->route()->is_master()) {
658 _display.get_selection()->unselect (i);
663 _no_redisplay = false;
671 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const & path, Gtk::TreeModel::iterator const & iter, int* new_order)
673 _redisplay_does_not_sync_order_keys = true;
674 _editor->current_session()->set_remote_control_ids();
676 _redisplay_does_not_sync_order_keys = false;
680 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
682 const SelectionData& data,
683 guint info, guint time)
685 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
686 _display.on_drag_data_received (context, x, y, data, info, time);
690 context->drag_finish (true, false, time);
694 EditorRoutes::move_selected_tracks (bool up)
696 if (_editor->selection->tracks.empty()) {
700 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
701 std::list<ViewRoute> view_routes;
702 std::vector<int> neworder;
703 TreeModel::Children rows = _model->children();
704 TreeModel::Children::iterator ri;
706 for (ri = rows.begin(); ri != rows.end(); ++ri) {
707 TimeAxisView* tv = (*ri)[_columns.tv];
708 boost::shared_ptr<Route> route = (*ri)[_columns.route];
710 view_routes.push_back (ViewRoute (tv, route));
713 list<ViewRoute>::iterator trailing;
714 list<ViewRoute>::iterator leading;
718 trailing = view_routes.begin();
719 leading = view_routes.begin();
723 while (leading != view_routes.end()) {
724 if (_editor->selection->selected (leading->first)) {
725 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
726 leading = view_routes.erase (leading);
735 /* if we could use reverse_iterator in list::insert, this code
736 would be a beautiful reflection of the code above. but we can't
737 and so it looks like a bit of a mess.
740 trailing = view_routes.end();
741 leading = view_routes.end();
743 --leading; if (leading == view_routes.begin()) { return; }
749 if (_editor->selection->selected (leading->first)) {
750 list<ViewRoute>::iterator tmp;
752 /* need to insert *after* trailing, not *before* it,
753 which is what insert (iter, val) normally does.
759 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
761 /* can't use iter = cont.erase (iter); form here, because
762 we need iter to move backwards.
770 if (leading == view_routes.begin()) {
771 /* the one we've just inserted somewhere else
772 was the first in the list. erase this copy,
773 and then break, because we're done.
778 view_routes.erase (leading);
787 if (leading == view_routes.begin()) {
796 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
797 neworder.push_back (leading->second->order_key (_order_key));
800 _model->reorder (neworder);
802 _editor->current_session()->sync_order_keys (_order_key);
806 EditorRoutes::update_rec_display ()
808 TreeModel::Children rows = _model->children();
809 TreeModel::Children::iterator i;
811 for (i = rows.begin(); i != rows.end(); ++i) {
812 boost::shared_ptr<Route> route = (*i)[_columns.route];
814 if (boost::dynamic_pointer_cast<Track>(route)) {
816 if (route->record_enabled()){
817 (*i)[_columns.rec_enabled] = true;
819 (*i)[_columns.rec_enabled] = false;
826 EditorRoutes::views () const
828 list<TimeAxisView*> v;
829 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
830 v.push_back ((*i)[_columns.tv]);
837 EditorRoutes::clear ()
839 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
841 _display.set_model (_model);