fix setting of BWF info (from 3.0). thanks carl
[ardour2.git] / gtk2_ardour / editor_route_list.cc
blobb5e2a29e3bfc7075e43f9532bff961c7c24ddc63
1 /*
2 Copyright (C) 2000 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.
20 #include <algorithm>
21 #include <cstdlib>
22 #include <cmath>
24 #include "editor.h"
25 #include "keyboard.h"
26 #include "ardour_ui.h"
27 #include "audio_time_axis.h"
28 #include "mixer_strip.h"
29 #include "gui_thread.h"
30 #include "actions.h"
32 #include <ardour/route.h>
33 #include <ardour/audio_track.h>
34 #include <ardour/route_group.h>
36 #include "i18n.h"
38 using namespace sigc;
39 using namespace ARDOUR;
40 using namespace PBD;
41 using namespace Gtk;
42 using namespace Glib;
44 const char* _order_key = N_("editor");
46 void
47 Editor::handle_new_route (Session::RouteList& routes)
49 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_new_route), routes));
51 TimeAxisView *tv;
52 AudioTimeAxisView *atv;
53 TreeModel::Row parent;
54 TreeModel::Row row;
56 route_redisplay_does_not_sync_order_keys = true;
57 no_route_list_redisplay = true;
59 for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
60 boost::shared_ptr<Route> route = (*x);
62 if (route->hidden()) {
63 continue;
66 tv = new AudioTimeAxisView (*this, *session, route, *track_canvas);
67 //cerr << "Editor::handle_new_route() called on " << route->name() << endl;//DEBUG
68 row = *(route_display_model->append ());
70 row[route_display_columns.route] = route;
71 row[route_display_columns.text] = route->name();
72 row[route_display_columns.visible] = tv->marked_for_display();
73 row[route_display_columns.tv] = tv;
75 RouteGroup *group = route->edit_group();
76 if (group) {
77 if (tv->marked_for_display()) {
78 group->set_hidden(false, this);
79 group_flags_changed(this, group);
83 track_views.push_back (tv);
85 if ((atv = dynamic_cast<AudioTimeAxisView*> (tv)) != 0) {
86 /* added a new fresh one at the end */
87 if (atv->route()->order_key(_order_key) == -1) {
88 atv->route()->set_order_key (_order_key, route_display_model->children().size()-1);
90 atv->effective_gain_display ();
93 route->gui_changed.connect (mem_fun(*this, &Editor::handle_gui_changes));
94 tv->GoingAway.connect (bind (mem_fun(*this, &Editor::remove_route), tv));
97 no_route_list_redisplay = false;
99 redisplay_route_list ();
101 if (show_editor_mixer_when_tracks_arrive) {
102 show_editor_mixer (true);
105 route_redisplay_does_not_sync_order_keys = false;
108 void
109 Editor::handle_gui_changes (const string & what, void *src)
111 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_gui_changes), what, src));
113 if (what == "track_height") {
114 /* Optional :make tracks change height while it happens, instead
115 of on first-idle
117 //track_canvas->update_now ();
118 redisplay_route_list ();
121 if (what == "visible_tracks") {
122 redisplay_route_list ();
126 void
127 Editor::remove_route (TimeAxisView *tv)
129 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::remove_route), tv));
131 TrackViewList::iterator i;
132 TreeModel::Children rows = route_display_model->children();
133 TreeModel::Children::iterator ri;
134 boost::shared_ptr<Route> route;
135 TimeAxisView* next_tv = 0;
137 if (tv == entered_track) {
138 entered_track = 0;
141 /* the core model has changed, there is no need to sync
142 view orders.
145 route_redisplay_does_not_sync_order_keys = true;
147 for (ri = rows.begin(); ri != rows.end(); ++ri) {
148 if ((*ri)[route_display_columns.tv] == tv) {
149 route = (*ri)[route_display_columns.route];
150 route_display_model->erase (ri);
151 break;
155 route_redisplay_does_not_sync_order_keys = false;
157 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
158 i = track_views.erase (i);
160 if (track_views.empty()) {
161 next_tv = 0;
162 } else if (i == track_views.end()) {
163 next_tv = track_views.front();
164 } else {
165 next_tv = (*i);
169 if (current_mixer_strip && (current_mixer_strip->route() == route)) {
171 if (next_tv) {
172 set_selected_mixer_strip (*next_tv);
173 } else {
174 /* make the editor mixer strip go away by setting the
175 * button to inactive (which also unticks the menu option)
178 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
183 void
184 Editor::route_name_changed (TimeAxisView *tv)
186 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::route_name_changed), tv));
188 TreeModel::Children rows = route_display_model->children();
189 TreeModel::Children::iterator i;
191 for (i = rows.begin(); i != rows.end(); ++i) {
192 if ((*i)[route_display_columns.tv] == tv) {
193 (*i)[route_display_columns.text] = tv->name();
194 break;
199 void
200 Editor::update_route_visibility ()
202 TreeModel::Children rows = route_display_model->children();
203 TreeModel::Children::iterator i;
205 no_route_list_redisplay = true;
207 for (i = rows.begin(); i != rows.end(); ++i) {
208 TimeAxisView *tv = (*i)[route_display_columns.tv];
209 (*i)[route_display_columns.visible] = tv->marked_for_display ();
212 no_route_list_redisplay = false;
213 redisplay_route_list ();
216 void
217 Editor::hide_track_in_display (TimeAxisView& tv, bool temponly)
219 TreeModel::Children rows = route_display_model->children();
220 TreeModel::Children::iterator i;
222 for (i = rows.begin(); i != rows.end(); ++i) {
223 if ((*i)[route_display_columns.tv] == &tv) {
224 (*i)[route_display_columns.visible] = false;
225 break;
229 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
231 if (atv && current_mixer_strip && (atv->route() == current_mixer_strip->route())) {
232 // this will hide the mixer strip
233 set_selected_mixer_strip (tv);
237 void
238 Editor::show_track_in_display (TimeAxisView& tv)
240 TreeModel::Children rows = route_display_model->children();
241 TreeModel::Children::iterator i;
243 for (i = rows.begin(); i != rows.end(); ++i) {
244 if ((*i)[route_display_columns.tv] == &tv) {
245 (*i)[route_display_columns.visible] = true;
246 break;
251 void
252 Editor::sync_order_keys (const char *src)
254 vector<int> neworder;
255 TreeModel::Children rows = route_display_model->children();
256 TreeModel::Children::iterator ri;
258 if ((strcmp (src, _order_key) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) {
259 return;
262 for (ri = rows.begin(); ri != rows.end(); ++ri) {
263 neworder.push_back (0);
266 bool changed = false;
267 int order;
269 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
270 boost::shared_ptr<Route> route = (*ri)[route_display_columns.route];
272 int old_key = order;
273 int new_key = route->order_key (_order_key);
275 neworder[new_key] = old_key;
277 if (new_key != old_key) {
278 changed = true;
282 if (changed) {
283 route_redisplay_does_not_reset_order_keys = true;
284 route_display_model->reorder (neworder);
285 route_redisplay_does_not_reset_order_keys = false;
289 void
290 Editor::redisplay_route_list ()
292 TreeModel::Children rows = route_display_model->children();
293 TreeModel::Children::iterator i;
294 uint32_t position;
295 uint32_t order;
296 int n;
298 if (no_route_list_redisplay) {
299 return;
302 if (session && (rows.size() > session->nroutes())) {
303 /* temporary condition during a drag-n-drop */
304 return;
307 for (n = 0, order = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
308 TimeAxisView *tv = (*i)[route_display_columns.tv];
309 boost::shared_ptr<Route> route = (*i)[route_display_columns.route];
311 if (tv == 0) {
312 // just a "title" row
313 continue;
316 if (!route_redisplay_does_not_reset_order_keys) {
318 /* this reorder is caused by user action, so reassign sort order keys
319 to tracks.
322 route->set_order_key (_order_key, order);
325 bool visible = (*i)[route_display_columns.visible];
327 if (visible) {
328 tv->set_marked_for_display (true);
329 position += tv->show_at (position, n, &edit_controls_vbox);
330 tv->clip_to_viewport ();
331 } else {
332 tv->set_marked_for_display (false);
333 tv->hide ();
336 ++order;
337 ++n;
340 /* whenever we go idle, update the track view list to reflect the new order.
341 we can't do this here, because we could mess up something that is traversing
342 the track order and has caused a redisplay of the list.
345 Glib::signal_idle().connect (mem_fun (*this, &Editor::sync_track_view_list_and_route_list));
347 full_canvas_height = position + canvas_timebars_vsize;
348 vertical_adjustment.set_upper (full_canvas_height);
349 if ((vertical_adjustment.get_value() + canvas_height) > vertical_adjustment.get_upper()) {
351 We're increasing the size of the canvas while the bottom is visible.
352 We scroll down to keep in step with the controls layout.
354 vertical_adjustment.set_value (full_canvas_height - canvas_height);
357 if (!route_redisplay_does_not_reset_order_keys && !route_redisplay_does_not_sync_order_keys) {
358 session->sync_order_keys (_order_key);
362 bool
363 Editor::sync_track_view_list_and_route_list ()
365 TreeModel::Children rows = route_display_model->children();
366 TreeModel::Children::iterator i;
368 track_views.clear ();
370 for (i = rows.begin(); i != rows.end(); ++i) {
371 TimeAxisView *tv = (*i)[route_display_columns.tv];
372 track_views.push_back (tv);
375 return false; // do not call again (until needed)
378 void
379 Editor::hide_all_tracks (bool with_select)
381 TreeModel::Children rows = route_display_model->children();
382 TreeModel::Children::iterator i;
384 no_route_list_redisplay = true;
386 for (i = rows.begin(); i != rows.end(); ++i) {
388 TreeModel::Row row = (*i);
389 TimeAxisView *tv = row[route_display_columns.tv];
391 if (tv == 0) {
392 continue;
395 row[route_display_columns.visible] = false;
398 no_route_list_redisplay = false;
399 redisplay_route_list ();
401 /* XXX this seems like a hack and half, but its not clear where to put this
402 otherwise.
405 //reset_scrolling_region ();
408 void
409 Editor::build_route_list_menu ()
411 using namespace Menu_Helpers;
412 using namespace Gtk;
414 route_list_menu = new Menu;
416 MenuList& items = route_list_menu->items();
417 route_list_menu->set_name ("ArdourContextMenu");
419 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Editor::show_all_routes)));
420 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Editor::hide_all_routes)));
421 items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Editor::show_all_audiotracks)));
422 items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Editor::hide_all_audiotracks)));
423 items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Editor::show_all_audiobus)));
424 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Editor::hide_all_audiobus)));
428 void
429 Editor::set_all_tracks_visibility (bool yn)
431 TreeModel::Children rows = route_display_model->children();
432 TreeModel::Children::iterator i;
434 no_route_list_redisplay = true;
436 for (i = rows.begin(); i != rows.end(); ++i) {
438 TreeModel::Row row = (*i);
439 TimeAxisView* tv = row[route_display_columns.tv];
441 if (tv == 0) {
442 continue;
445 (*i)[route_display_columns.visible] = yn;
448 no_route_list_redisplay = false;
449 redisplay_route_list ();
452 void
453 Editor::set_all_audio_visibility (int tracks, bool yn)
455 TreeModel::Children rows = route_display_model->children();
456 TreeModel::Children::iterator i;
458 no_route_list_redisplay = true;
460 for (i = rows.begin(); i != rows.end(); ++i) {
461 TreeModel::Row row = (*i);
462 TimeAxisView* tv = row[route_display_columns.tv];
463 AudioTimeAxisView* atv;
465 if (tv == 0) {
466 continue;
469 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
470 switch (tracks) {
471 case 0:
472 (*i)[route_display_columns.visible] = yn;
473 break;
475 case 1:
476 if (atv->is_audio_track()) {
477 (*i)[route_display_columns.visible] = yn;
479 break;
481 case 2:
482 if (!atv->is_audio_track()) {
483 (*i)[route_display_columns.visible] = yn;
485 break;
490 no_route_list_redisplay = false;
491 redisplay_route_list ();
494 void
495 Editor::hide_all_routes ()
497 set_all_tracks_visibility (false);
500 void
501 Editor::show_all_routes ()
503 set_all_tracks_visibility (true);
506 void
507 Editor::show_all_audiobus ()
509 set_all_audio_visibility (2, true);
511 void
512 Editor::hide_all_audiobus ()
514 set_all_audio_visibility (2, false);
517 void
518 Editor::show_all_audiotracks()
520 set_all_audio_visibility (1, true);
522 void
523 Editor::hide_all_audiotracks ()
525 set_all_audio_visibility (1, false);
528 bool
529 Editor::route_list_display_button_press (GdkEventButton* ev)
531 if (Keyboard::is_context_menu_event (ev)) {
532 show_route_list_menu ();
533 return true;
536 TreeIter iter;
537 TreeModel::Path path;
538 TreeViewColumn* column;
539 int cellx;
540 int celly;
542 if (!route_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
543 return false;
546 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
547 case 0:
548 if ((iter = route_display_model->get_iter (path))) {
549 TimeAxisView* tv = (*iter)[route_display_columns.tv];
550 if (tv) {
551 bool visible = (*iter)[route_display_columns.visible];
552 (*iter)[route_display_columns.visible] = !visible;
555 return true;
557 case 1:
558 /* allow normal processing to occur */
559 return false;
561 default:
562 break;
565 return false;
568 void
569 Editor::show_route_list_menu()
571 if (route_list_menu == 0) {
572 build_route_list_menu ();
575 route_list_menu->popup (1, gtk_get_current_event_time());
578 bool
579 Editor::route_list_selection_filter (const Glib::RefPtr<TreeModel>& model, const TreeModel::Path& path, bool yn)
581 return true;
584 struct EditorOrderRouteSorter {
585 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
586 /* use of ">" forces the correct sort order */
587 return a->order_key (_order_key) < b->order_key (_order_key);
591 void
592 Editor::initial_route_list_display ()
594 boost::shared_ptr<Session::RouteList> routes = session->get_routes();
595 Session::RouteList r (*routes);
596 EditorOrderRouteSorter sorter;
598 r.sort (sorter);
600 no_route_list_redisplay = true;
602 route_display_model->clear ();
604 handle_new_route (r);
606 no_route_list_redisplay = false;
608 redisplay_route_list ();
611 void
612 Editor::track_list_reorder (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter, int* new_order)
614 route_redisplay_does_not_sync_order_keys = true;
615 session->set_remote_control_ids();
616 redisplay_route_list ();
617 route_redisplay_does_not_sync_order_keys = false;
620 void
621 Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
623 /* never reset order keys because of a property change */
624 route_redisplay_does_not_reset_order_keys = true;
625 session->set_remote_control_ids();
626 redisplay_route_list ();
627 route_redisplay_does_not_reset_order_keys = false;
630 void
631 Editor::route_list_delete (const Gtk::TreeModel::Path& path)
633 /* this could require an order reset & sync */
634 session->set_remote_control_ids();
635 redisplay_route_list ();
638 void
639 Editor::route_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
640 int x, int y,
641 const SelectionData& data,
642 guint info, guint time)
644 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
645 route_list_display.on_drag_data_received (context, x, y, data, info, time);
646 return;
648 context->drag_finish (true, false, time);
651 void
652 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
654 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
655 theslot (**i);
659 void
660 Editor::move_selected_tracks (bool up)
662 if (selection->tracks.empty()) {
663 return;
666 typedef pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
667 list<ViewRoute> view_routes;
668 vector<int> neworder;
669 TreeModel::Children rows = route_display_model->children();
670 TreeModel::Children::iterator ri;
672 for (ri = rows.begin(); ri != rows.end(); ++ri) {
673 TimeAxisView* tv = (*ri)[route_display_columns.tv];
674 boost::shared_ptr<Route> route = (*ri)[route_display_columns.route];
676 view_routes.push_back (ViewRoute (tv, route));
679 list<ViewRoute>::iterator trailing;
680 list<ViewRoute>::iterator leading;
682 if (up) {
684 trailing = view_routes.begin();
685 leading = view_routes.begin();
687 ++leading;
689 while (leading != view_routes.end()) {
690 if (selection->selected (leading->first)) {
691 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
692 leading = view_routes.erase (leading);
693 } else {
694 ++leading;
695 ++trailing;
699 } else {
701 /* if we could use reverse_iterator in list::insert, this code
702 would be a beautiful reflection of the code above. but we can't
703 and so it looks like a bit of a mess.
706 trailing = view_routes.end();
707 leading = view_routes.end();
709 --leading; if (leading == view_routes.begin()) { return; }
710 --leading;
711 --trailing;
713 while (1) {
715 if (selection->selected (leading->first)) {
716 list<ViewRoute>::iterator tmp;
718 /* need to insert *after* trailing, not *before* it,
719 which is what insert (iter, val) normally does.
722 tmp = trailing;
723 tmp++;
725 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
727 /* can't use iter = cont.erase (iter); form here, because
728 we need iter to move backwards.
731 tmp = leading;
732 --tmp;
734 bool done = false;
736 if (leading == view_routes.begin()) {
737 /* the one we've just inserted somewhere else
738 was the first in the list. erase this copy,
739 and then break, because we're done.
741 done = true;
744 view_routes.erase (leading);
746 if (done) {
747 break;
750 leading = tmp;
752 } else {
753 if (leading == view_routes.begin()) {
754 break;
756 --leading;
757 --trailing;
762 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
763 neworder.push_back (leading->second->order_key (_order_key));
766 route_display_model->reorder (neworder);
768 session->sync_order_keys (_order_key);