make AU Cocoa plugin views with "client-side-windows" versions of GTK+; make keyboard...
[ardour2.git] / gtk2_ardour / editor_route_list.cc
blobd73c8176ea6d58772761eae6a94f57a887663437
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 redisplay_route_list ();
120 if (what == "visible_tracks") {
121 redisplay_route_list ();
125 void
126 Editor::remove_route (TimeAxisView *tv)
128 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::remove_route), tv));
130 TrackViewList::iterator i;
131 TreeModel::Children rows = route_display_model->children();
132 TreeModel::Children::iterator ri;
133 boost::shared_ptr<Route> route;
134 TimeAxisView* next_tv = 0;
136 if (tv == entered_track) {
137 entered_track = 0;
140 /* the core model has changed, there is no need to sync
141 view orders.
144 route_redisplay_does_not_sync_order_keys = true;
146 for (ri = rows.begin(); ri != rows.end(); ++ri) {
147 if ((*ri)[route_display_columns.tv] == tv) {
148 route = (*ri)[route_display_columns.route];
149 route_display_model->erase (ri);
150 break;
154 route_redisplay_does_not_sync_order_keys = false;
156 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
157 i = track_views.erase (i);
159 if (track_views.empty()) {
160 next_tv = 0;
161 } else if (i == track_views.end()) {
162 next_tv = track_views.front();
163 } else {
164 next_tv = (*i);
168 if (current_mixer_strip && (current_mixer_strip->route() == route)) {
170 if (next_tv) {
171 set_selected_mixer_strip (*next_tv);
172 } else {
173 /* make the editor mixer strip go away by setting the
174 * button to inactive (which also unticks the menu option)
177 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
182 void
183 Editor::route_name_changed (TimeAxisView *tv)
185 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::route_name_changed), tv));
187 TreeModel::Children rows = route_display_model->children();
188 TreeModel::Children::iterator i;
190 for (i = rows.begin(); i != rows.end(); ++i) {
191 if ((*i)[route_display_columns.tv] == tv) {
192 (*i)[route_display_columns.text] = tv->name();
193 break;
198 void
199 Editor::update_route_visibility ()
201 TreeModel::Children rows = route_display_model->children();
202 TreeModel::Children::iterator i;
204 no_route_list_redisplay = true;
206 for (i = rows.begin(); i != rows.end(); ++i) {
207 TimeAxisView *tv = (*i)[route_display_columns.tv];
208 (*i)[route_display_columns.visible] = tv->marked_for_display ();
211 no_route_list_redisplay = false;
212 redisplay_route_list ();
215 void
216 Editor::hide_track_in_display (TimeAxisView& tv, bool temponly)
218 TreeModel::Children rows = route_display_model->children();
219 TreeModel::Children::iterator i;
221 for (i = rows.begin(); i != rows.end(); ++i) {
222 if ((*i)[route_display_columns.tv] == &tv) {
223 (*i)[route_display_columns.visible] = false;
224 break;
228 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
230 if (atv && current_mixer_strip && (atv->route() == current_mixer_strip->route())) {
231 // this will hide the mixer strip
232 set_selected_mixer_strip (tv);
236 void
237 Editor::show_track_in_display (TimeAxisView& tv)
239 TreeModel::Children rows = route_display_model->children();
240 TreeModel::Children::iterator i;
242 for (i = rows.begin(); i != rows.end(); ++i) {
243 if ((*i)[route_display_columns.tv] == &tv) {
244 (*i)[route_display_columns.visible] = true;
245 break;
250 void
251 Editor::sync_order_keys (const char *src)
253 vector<int> neworder;
254 TreeModel::Children rows = route_display_model->children();
255 TreeModel::Children::iterator ri;
257 if ((strcmp (src, _order_key) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) {
258 return;
261 for (ri = rows.begin(); ri != rows.end(); ++ri) {
262 neworder.push_back (0);
265 bool changed = false;
266 int order;
268 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
269 boost::shared_ptr<Route> route = (*ri)[route_display_columns.route];
271 int old_key = order;
272 int new_key = route->order_key (_order_key);
274 neworder[new_key] = old_key;
276 if (new_key != old_key) {
277 changed = true;
281 if (changed) {
282 route_redisplay_does_not_reset_order_keys = true;
283 route_display_model->reorder (neworder);
284 route_redisplay_does_not_reset_order_keys = false;
288 void
289 Editor::redisplay_route_list ()
291 TreeModel::Children rows = route_display_model->children();
292 TreeModel::Children::iterator i;
293 uint32_t position;
294 uint32_t order;
295 int n;
297 if (no_route_list_redisplay) {
298 return;
301 if (session && (rows.size() > session->nroutes())) {
302 /* temporary condition during a drag-n-drop */
303 return;
306 for (n = 0, order = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
307 TimeAxisView *tv = (*i)[route_display_columns.tv];
308 boost::shared_ptr<Route> route = (*i)[route_display_columns.route];
310 if (tv == 0) {
311 // just a "title" row
312 continue;
315 if (!route_redisplay_does_not_reset_order_keys) {
317 /* this reorder is caused by user action, so reassign sort order keys
318 to tracks.
321 route->set_order_key (_order_key, order);
324 bool visible = (*i)[route_display_columns.visible];
326 if (visible) {
327 tv->set_marked_for_display (true);
328 position += tv->show_at (position, n, &edit_controls_vbox);
329 tv->clip_to_viewport ();
330 } else {
331 tv->set_marked_for_display (false);
332 tv->hide ();
335 ++order;
336 ++n;
339 /* whenever we go idle, update the track view list to reflect the new order.
340 we can't do this here, because we could mess up something that is traversing
341 the track order and has caused a redisplay of the list.
344 Glib::signal_idle().connect (mem_fun (*this, &Editor::sync_track_view_list_and_route_list));
346 full_canvas_height = position + canvas_timebars_vsize;
347 vertical_adjustment.set_upper (full_canvas_height);
348 if ((vertical_adjustment.get_value() + canvas_height) > vertical_adjustment.get_upper()) {
350 We're increasing the size of the canvas while the bottom is visible.
351 We scroll down to keep in step with the controls layout.
353 vertical_adjustment.set_value (full_canvas_height - canvas_height);
356 if (!route_redisplay_does_not_reset_order_keys && !route_redisplay_does_not_sync_order_keys) {
357 session->sync_order_keys (_order_key);
361 bool
362 Editor::sync_track_view_list_and_route_list ()
364 TreeModel::Children rows = route_display_model->children();
365 TreeModel::Children::iterator i;
367 track_views.clear ();
369 for (i = rows.begin(); i != rows.end(); ++i) {
370 TimeAxisView *tv = (*i)[route_display_columns.tv];
371 track_views.push_back (tv);
374 return false; // do not call again (until needed)
377 void
378 Editor::hide_all_tracks (bool with_select)
380 TreeModel::Children rows = route_display_model->children();
381 TreeModel::Children::iterator i;
383 no_route_list_redisplay = true;
385 for (i = rows.begin(); i != rows.end(); ++i) {
387 TreeModel::Row row = (*i);
388 TimeAxisView *tv = row[route_display_columns.tv];
390 if (tv == 0) {
391 continue;
394 row[route_display_columns.visible] = false;
397 no_route_list_redisplay = false;
398 redisplay_route_list ();
400 /* XXX this seems like a hack and half, but its not clear where to put this
401 otherwise.
404 //reset_scrolling_region ();
407 void
408 Editor::build_route_list_menu ()
410 using namespace Menu_Helpers;
411 using namespace Gtk;
413 route_list_menu = new Menu;
415 MenuList& items = route_list_menu->items();
416 route_list_menu->set_name ("ArdourContextMenu");
418 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Editor::show_all_routes)));
419 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Editor::hide_all_routes)));
420 items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Editor::show_all_audiotracks)));
421 items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Editor::hide_all_audiotracks)));
422 items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Editor::show_all_audiobus)));
423 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Editor::hide_all_audiobus)));
424 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), mem_fun (*this, &Editor::show_tracks_with_regions_at_playhead)));
427 void
428 Editor::set_all_tracks_visibility (bool yn)
430 TreeModel::Children rows = route_display_model->children();
431 TreeModel::Children::iterator i;
433 no_route_list_redisplay = true;
435 for (i = rows.begin(); i != rows.end(); ++i) {
437 TreeModel::Row row = (*i);
438 TimeAxisView* tv = row[route_display_columns.tv];
440 if (tv == 0) {
441 continue;
444 (*i)[route_display_columns.visible] = yn;
447 no_route_list_redisplay = false;
448 redisplay_route_list ();
451 void
452 Editor::set_all_audio_visibility (int tracks, bool yn)
454 TreeModel::Children rows = route_display_model->children();
455 TreeModel::Children::iterator i;
457 no_route_list_redisplay = true;
459 for (i = rows.begin(); i != rows.end(); ++i) {
460 TreeModel::Row row = (*i);
461 TimeAxisView* tv = row[route_display_columns.tv];
462 AudioTimeAxisView* atv;
464 if (tv == 0) {
465 continue;
468 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
469 switch (tracks) {
470 case 0:
471 (*i)[route_display_columns.visible] = yn;
472 break;
474 case 1:
475 if (atv->is_audio_track()) {
476 (*i)[route_display_columns.visible] = yn;
478 break;
480 case 2:
481 if (!atv->is_audio_track()) {
482 (*i)[route_display_columns.visible] = yn;
484 break;
489 no_route_list_redisplay = false;
490 redisplay_route_list ();
493 void
494 Editor::hide_all_routes ()
496 set_all_tracks_visibility (false);
499 void
500 Editor::show_all_routes ()
502 set_all_tracks_visibility (true);
505 void
506 Editor::show_all_audiobus ()
508 set_all_audio_visibility (2, true);
510 void
511 Editor::hide_all_audiobus ()
513 set_all_audio_visibility (2, false);
516 void
517 Editor::show_all_audiotracks()
519 set_all_audio_visibility (1, true);
521 void
522 Editor::hide_all_audiotracks ()
524 set_all_audio_visibility (1, false);
527 void
528 Editor::show_tracks_with_regions_at_playhead ()
530 boost::shared_ptr<Session::RouteList> const regions = session->get_routes_with_regions_at (session->transport_frame ());
532 //suspend_redisplay ();
534 TreeModel::Children rows = route_display_model->children ();
535 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
536 boost::shared_ptr<Route> route = (*i)[route_display_columns.route];
538 bool found = false;
539 for (Session::RouteList::iterator x = (*regions).begin(); x != (*regions).end(); ++x) {
540 if ((*x) == route)
541 found = true;
544 (*i)[route_display_columns.visible] = found;
547 no_route_list_redisplay = false;
548 redisplay_route_list ();
550 //resume_redisplay ();
553 bool
554 Editor::route_list_display_button_press (GdkEventButton* ev)
556 if (Keyboard::is_context_menu_event (ev)) {
557 show_route_list_menu ();
558 return true;
561 TreeIter iter;
562 TreeModel::Path path;
563 TreeViewColumn* column;
564 int cellx;
565 int celly;
567 if (!route_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
568 return false;
571 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
572 case 0:
573 if ((iter = route_display_model->get_iter (path))) {
574 TimeAxisView* tv = (*iter)[route_display_columns.tv];
575 if (tv) {
576 bool visible = (*iter)[route_display_columns.visible];
577 (*iter)[route_display_columns.visible] = !visible;
580 return true;
582 case 1:
583 /* allow normal processing to occur */
584 return false;
586 default:
587 break;
590 return false;
593 void
594 Editor::show_route_list_menu()
596 if (route_list_menu == 0) {
597 build_route_list_menu ();
600 route_list_menu->popup (1, gtk_get_current_event_time());
603 bool
604 Editor::route_list_selection_filter (const Glib::RefPtr<TreeModel>& model, const TreeModel::Path& path, bool yn)
606 return true;
609 struct EditorOrderRouteSorter {
610 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
611 /* use of ">" forces the correct sort order */
612 return a->order_key (_order_key) < b->order_key (_order_key);
616 void
617 Editor::initial_route_list_display ()
619 boost::shared_ptr<Session::RouteList> routes = session->get_routes();
620 Session::RouteList r (*routes);
621 EditorOrderRouteSorter sorter;
623 r.sort (sorter);
625 no_route_list_redisplay = true;
627 route_display_model->clear ();
629 handle_new_route (r);
631 no_route_list_redisplay = false;
633 redisplay_route_list ();
636 void
637 Editor::track_list_reorder (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter, int* new_order)
639 route_redisplay_does_not_sync_order_keys = true;
640 session->set_remote_control_ids();
641 redisplay_route_list ();
642 route_redisplay_does_not_sync_order_keys = false;
645 void
646 Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
648 /* never reset order keys because of a property change */
649 route_redisplay_does_not_reset_order_keys = true;
650 session->set_remote_control_ids();
651 redisplay_route_list ();
652 route_redisplay_does_not_reset_order_keys = false;
655 void
656 Editor::route_list_delete (const Gtk::TreeModel::Path& path)
658 /* this could require an order reset & sync */
659 session->set_remote_control_ids();
660 redisplay_route_list ();
663 void
664 Editor::route_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
665 int x, int y,
666 const SelectionData& data,
667 guint info, guint time)
669 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
670 route_list_display.on_drag_data_received (context, x, y, data, info, time);
671 return;
673 context->drag_finish (true, false, time);
676 void
677 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
679 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
680 theslot (**i);
684 void
685 Editor::move_selected_tracks (bool up)
687 if (selection->tracks.empty()) {
688 return;
691 typedef pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
692 list<ViewRoute> view_routes;
693 vector<int> neworder;
694 TreeModel::Children rows = route_display_model->children();
695 TreeModel::Children::iterator ri;
697 for (ri = rows.begin(); ri != rows.end(); ++ri) {
698 TimeAxisView* tv = (*ri)[route_display_columns.tv];
699 boost::shared_ptr<Route> route = (*ri)[route_display_columns.route];
701 view_routes.push_back (ViewRoute (tv, route));
704 list<ViewRoute>::iterator trailing;
705 list<ViewRoute>::iterator leading;
707 if (up) {
709 trailing = view_routes.begin();
710 leading = view_routes.begin();
712 ++leading;
714 while (leading != view_routes.end()) {
715 if (selection->selected (leading->first)) {
716 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
717 leading = view_routes.erase (leading);
718 } else {
719 ++leading;
720 ++trailing;
724 } else {
726 /* if we could use reverse_iterator in list::insert, this code
727 would be a beautiful reflection of the code above. but we can't
728 and so it looks like a bit of a mess.
731 trailing = view_routes.end();
732 leading = view_routes.end();
734 --leading; if (leading == view_routes.begin()) { return; }
735 --leading;
736 --trailing;
738 while (1) {
740 if (selection->selected (leading->first)) {
741 list<ViewRoute>::iterator tmp;
743 /* need to insert *after* trailing, not *before* it,
744 which is what insert (iter, val) normally does.
747 tmp = trailing;
748 tmp++;
750 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
752 /* can't use iter = cont.erase (iter); form here, because
753 we need iter to move backwards.
756 tmp = leading;
757 --tmp;
759 bool done = false;
761 if (leading == view_routes.begin()) {
762 /* the one we've just inserted somewhere else
763 was the first in the list. erase this copy,
764 and then break, because we're done.
766 done = true;
769 view_routes.erase (leading);
771 if (done) {
772 break;
775 leading = tmp;
777 } else {
778 if (leading == view_routes.begin()) {
779 break;
781 --leading;
782 --trailing;
787 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
788 neworder.push_back (leading->second->order_key (_order_key));
791 route_display_model->reorder (neworder);
793 session->sync_order_keys (_order_key);