don't crash if there's no editor mixer and we remove a route
[ardour.git] / gtk2_ardour / editor_route_list.cc
1 /*
2     Copyright (C) 2000 Paul Davis 
3
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.
8
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.
13
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.
17
18 */
19
20 #include <algorithm>
21 #include <cstdlib>
22 #include <cmath>
23
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"
31
32 #include <ardour/route.h>
33 #include <ardour/audio_track.h>
34
35 #include "i18n.h"
36
37 using namespace sigc;
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace Gtk;
41 using namespace Glib;
42
43 const char* _order_key = N_("editor");
44
45 void
46 Editor::handle_new_route (Session::RouteList& routes)
47 {
48         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_new_route), routes));
49         
50         TimeAxisView *tv;
51         AudioTimeAxisView *atv;
52         TreeModel::Row parent;
53         TreeModel::Row row;
54
55         route_redisplay_does_not_sync_order_keys = true;
56         no_route_list_redisplay = true;
57
58         for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
59                 boost::shared_ptr<Route> route = (*x);
60
61                 if (route->hidden()) {
62                         continue;
63                 }
64                 
65                 tv = new AudioTimeAxisView (*this, *session, route, *track_canvas);
66                 //cerr << "Editor::handle_new_route() called on " << route->name() << endl;//DEBUG
67                 row = *(route_display_model->append ());
68                 
69                 row[route_display_columns.route] = route;
70                 row[route_display_columns.text] = route->name();
71                 row[route_display_columns.visible] = tv->marked_for_display();
72                 row[route_display_columns.tv] = tv;
73
74                 track_views.push_back (tv);
75                 
76                 if ((atv = dynamic_cast<AudioTimeAxisView*> (tv)) != 0) {
77                         /* added a new fresh one at the end */
78                         if (atv->route()->order_key(_order_key) == -1) {
79                                 atv->route()->set_order_key (_order_key, route_display_model->children().size()-1);
80                         }
81                         atv->effective_gain_display ();
82                 }
83
84                 route->gui_changed.connect (mem_fun(*this, &Editor::handle_gui_changes));
85                 tv->GoingAway.connect (bind (mem_fun(*this, &Editor::remove_route), tv));
86         }
87
88         no_route_list_redisplay = false;
89
90         redisplay_route_list ();
91
92         if (show_editor_mixer_when_tracks_arrive) {
93                 show_editor_mixer (true);
94         }
95         
96         route_redisplay_does_not_sync_order_keys = false;
97 }
98
99 void
100 Editor::handle_gui_changes (const string & what, void *src)
101 {
102         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_gui_changes), what, src));
103
104         if (what == "track_height") {
105                 /* Optional :make tracks change height while it happens, instead 
106                    of on first-idle
107                 */
108                 //track_canvas->update_now ();
109                 redisplay_route_list ();
110         }
111
112         if (what == "visible_tracks") {
113                 redisplay_route_list ();
114         }
115 }
116
117 void
118 Editor::remove_route (TimeAxisView *tv)
119 {
120         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::remove_route), tv));
121
122         TrackViewList::iterator i;
123         TreeModel::Children rows = route_display_model->children();
124         TreeModel::Children::iterator ri;
125         boost::shared_ptr<Route> route;
126         TimeAxisView* next_tv;
127
128         if (tv == entered_track) {
129                 entered_track = 0;
130         }
131
132         /* the core model has changed, there is no need to sync 
133            view orders.
134         */
135
136         route_redisplay_does_not_sync_order_keys = true;
137
138         for (ri = rows.begin(); ri != rows.end(); ++ri) {
139                 if ((*ri)[route_display_columns.tv] == tv) {
140                         route = (*ri)[route_display_columns.route];
141                         route_display_model->erase (ri);
142                         break;
143                 }
144         }
145
146         route_redisplay_does_not_sync_order_keys = false;
147
148         if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
149                 i = track_views.erase (i);
150                 
151                 if (track_views.empty()) {
152                         next_tv = 0;
153                 } else if (i == track_views.end()) {
154                         next_tv = track_views.front();
155                 } else {
156                         next_tv = (*i);
157                 }
158         }
159
160         if (current_mixer_strip && (current_mixer_strip->route() == route)) {
161
162                 if (next_tv) {
163                         set_selected_mixer_strip (*next_tv);
164                 } else {
165                         /* make the editor mixer strip go away by setting the
166                          * button to inactive (which also unticks the menu option)
167                          */
168                         
169                         ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
170                 }
171         }
172 }
173
174 void
175 Editor::route_name_changed (TimeAxisView *tv)
176 {
177         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::route_name_changed), tv));
178         
179         TreeModel::Children rows = route_display_model->children();
180         TreeModel::Children::iterator i;
181         
182         for (i = rows.begin(); i != rows.end(); ++i) {
183                 if ((*i)[route_display_columns.tv] == tv) {
184                         (*i)[route_display_columns.text] = tv->name();
185                         break;
186                 }
187         } 
188 }
189
190 void
191 Editor::update_route_visibility ()
192 {
193         TreeModel::Children rows = route_display_model->children();
194         TreeModel::Children::iterator i;
195         
196         no_route_list_redisplay = true;
197
198         for (i = rows.begin(); i != rows.end(); ++i) {
199                 TimeAxisView *tv = (*i)[route_display_columns.tv];
200                 (*i)[route_display_columns.visible] = tv->marked_for_display ();
201         }
202
203         no_route_list_redisplay = false;
204         redisplay_route_list ();
205 }
206
207 void
208 Editor::hide_track_in_display (TimeAxisView& tv, bool temponly)
209 {
210         TreeModel::Children rows = route_display_model->children();
211         TreeModel::Children::iterator i;
212
213         for (i = rows.begin(); i != rows.end(); ++i) {
214                 if ((*i)[route_display_columns.tv] == &tv) { 
215                         (*i)[route_display_columns.visible] = false;
216                         break;
217                 }
218         }
219
220         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
221
222         if (atv && current_mixer_strip && (atv->route() == current_mixer_strip->route())) {
223                 // this will hide the mixer strip
224                 set_selected_mixer_strip (tv);
225         }
226 }
227
228 void
229 Editor::show_track_in_display (TimeAxisView& tv)
230 {
231         TreeModel::Children rows = route_display_model->children();
232         TreeModel::Children::iterator i;
233         
234         for (i = rows.begin(); i != rows.end(); ++i) {
235                 if ((*i)[route_display_columns.tv] == &tv) { 
236                         (*i)[route_display_columns.visible] = true;
237                         break;
238                 }
239         }
240 }
241
242 void
243 Editor::sync_order_keys (const char *src)
244 {
245         vector<int> neworder;
246         TreeModel::Children rows = route_display_model->children();
247         TreeModel::Children::iterator ri;
248
249         if ((strcmp (src, _order_key) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) {
250                 return;
251         }
252
253         for (ri = rows.begin(); ri != rows.end(); ++ri) {
254                 neworder.push_back (0);
255         }
256
257         bool changed = false;
258         int order;
259
260         for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
261                 boost::shared_ptr<Route> route = (*ri)[route_display_columns.route];
262
263                 int old_key = order;
264                 int new_key = route->order_key (_order_key);
265
266                 neworder[new_key] = old_key;
267
268                 if (new_key != old_key) {
269                         changed = true;
270                 }
271         }
272
273         if (changed) {
274                 route_redisplay_does_not_reset_order_keys = true;
275                 route_display_model->reorder (neworder);
276                 route_redisplay_does_not_reset_order_keys = false;
277         }
278 }
279
280 void
281 Editor::redisplay_route_list ()
282 {
283         TreeModel::Children rows = route_display_model->children();
284         TreeModel::Children::iterator i;
285         uint32_t position;
286         uint32_t order;
287         int n;
288
289         if (no_route_list_redisplay) {
290                 return;
291         }
292
293         if (session && (rows.size() > session->nroutes())) {
294                 /* temporary condition during a drag-n-drop */
295                 return;
296         }
297
298         track_views.clear (); // we will reload as we go along
299
300         for (n = 0, order = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
301                 TimeAxisView *tv = (*i)[route_display_columns.tv];
302                 boost::shared_ptr<Route> route = (*i)[route_display_columns.route];
303
304                 track_views.push_back (tv);
305
306                 if (tv == 0) {
307                         // just a "title" row
308                         continue;
309                 }
310
311                 if (!route_redisplay_does_not_reset_order_keys) {
312                         
313                         /* this reorder is caused by user action, so reassign sort order keys
314                            to tracks.
315                         */
316                         
317                         route->set_order_key (_order_key, order);
318                 }
319
320                 bool visible = (*i)[route_display_columns.visible];
321                 
322                 if (visible) {
323                         tv->set_marked_for_display (true);
324                         position += tv->show_at (position, n, &edit_controls_vbox);
325                         tv->clip_to_viewport ();
326                 } else {
327                         tv->set_marked_for_display (false);
328                         tv->hide ();
329                 }
330                 
331                 ++order;
332                 ++n;
333         }
334
335         full_canvas_height = position + canvas_timebars_vsize;
336         vertical_adjustment.set_upper (full_canvas_height);
337         if ((vertical_adjustment.get_value() + canvas_height) > vertical_adjustment.get_upper()) {
338                 /* 
339                    We're increasing the size of the canvas while the bottom is visible.
340                    We scroll down to keep in step with the controls layout.
341                 */
342                 vertical_adjustment.set_value (full_canvas_height - canvas_height);
343         } 
344
345         if (!route_redisplay_does_not_reset_order_keys && !route_redisplay_does_not_sync_order_keys) {
346                 session->sync_order_keys (_order_key);
347         }
348 }
349
350 void
351 Editor::hide_all_tracks (bool with_select)
352 {
353         TreeModel::Children rows = route_display_model->children();
354         TreeModel::Children::iterator i;
355
356         no_route_list_redisplay = true;
357
358         for (i = rows.begin(); i != rows.end(); ++i) {
359                 
360                 TreeModel::Row row = (*i);
361                 TimeAxisView *tv = row[route_display_columns.tv];
362
363                 if (tv == 0) {
364                         continue;
365                 }
366                 
367                 row[route_display_columns.visible] = false;
368         }
369
370         no_route_list_redisplay = false;
371         redisplay_route_list ();
372
373         /* XXX this seems like a hack and half, but its not clear where to put this
374            otherwise.
375         */
376
377         //reset_scrolling_region ();
378 }
379
380 void
381 Editor::build_route_list_menu ()
382 {
383         using namespace Menu_Helpers;
384         using namespace Gtk;
385
386         route_list_menu = new Menu;
387         
388         MenuList& items = route_list_menu->items();
389         route_list_menu->set_name ("ArdourContextMenu");
390
391         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Editor::show_all_routes)));
392         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Editor::hide_all_routes)));
393         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Editor::show_all_audiotracks)));
394         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Editor::hide_all_audiotracks)));
395         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Editor::show_all_audiobus)));
396         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Editor::hide_all_audiobus)));
397
398 }
399
400 void
401 Editor::set_all_tracks_visibility (bool yn)
402 {
403         TreeModel::Children rows = route_display_model->children();
404         TreeModel::Children::iterator i;
405
406         no_route_list_redisplay = true;
407
408         for (i = rows.begin(); i != rows.end(); ++i) {
409
410                 TreeModel::Row row = (*i);
411                 TimeAxisView* tv = row[route_display_columns.tv];
412
413                 if (tv == 0) {
414                         continue;
415                 }
416                 
417                 (*i)[route_display_columns.visible] = yn;
418         }
419
420         no_route_list_redisplay = false;
421         redisplay_route_list ();
422 }
423
424 void
425 Editor::set_all_audio_visibility (int tracks, bool yn) 
426 {
427         TreeModel::Children rows = route_display_model->children();
428         TreeModel::Children::iterator i;
429
430         no_route_list_redisplay = true;
431
432         for (i = rows.begin(); i != rows.end(); ++i) {
433                 TreeModel::Row row = (*i);
434                 TimeAxisView* tv = row[route_display_columns.tv];
435                 AudioTimeAxisView* atv;
436
437                 if (tv == 0) {
438                         continue;
439                 }
440
441                 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
442                         switch (tracks) {
443                         case 0:
444                                 (*i)[route_display_columns.visible] = yn;
445                                 break;
446
447                         case 1:
448                                 if (atv->is_audio_track()) {
449                                         (*i)[route_display_columns.visible] = yn;
450                                 }
451                                 break;
452                                 
453                         case 2:
454                                 if (!atv->is_audio_track()) {
455                                         (*i)[route_display_columns.visible] = yn;
456                                 }
457                                 break;
458                         }
459                 }
460         }
461
462         no_route_list_redisplay = false;
463         redisplay_route_list ();
464 }
465
466 void
467 Editor::hide_all_routes ()
468 {
469         set_all_tracks_visibility (false);
470 }
471
472 void
473 Editor::show_all_routes ()
474 {
475         set_all_tracks_visibility (true);
476 }
477
478 void
479 Editor::show_all_audiobus ()
480 {
481         set_all_audio_visibility (2, true);
482 }
483 void
484 Editor::hide_all_audiobus ()
485 {
486         set_all_audio_visibility (2, false);
487 }
488
489 void
490 Editor::show_all_audiotracks()
491 {
492         set_all_audio_visibility (1, true);
493 }
494 void
495 Editor::hide_all_audiotracks ()
496 {
497         set_all_audio_visibility (1, false);
498 }
499
500 bool
501 Editor::route_list_display_button_press (GdkEventButton* ev)
502 {
503         if (Keyboard::is_context_menu_event (ev)) {
504                 show_route_list_menu ();
505                 return true;
506         }
507
508         TreeIter iter;
509         TreeModel::Path path;
510         TreeViewColumn* column;
511         int cellx;
512         int celly;
513         
514         if (!route_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
515                 return false;
516         }
517
518         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
519         case 0:
520                 if ((iter = route_display_model->get_iter (path))) {
521                         TimeAxisView* tv = (*iter)[route_display_columns.tv];
522                         if (tv) {
523                                 bool visible = (*iter)[route_display_columns.visible];
524                                 (*iter)[route_display_columns.visible] = !visible;
525                         }
526                 }
527                 return true;
528
529         case 1:
530                 /* allow normal processing to occur */
531                 return false;
532
533         default:
534                 break;
535         }
536
537         return false;
538 }
539
540 void
541 Editor::show_route_list_menu()
542 {
543         if (route_list_menu == 0) {
544                 build_route_list_menu ();
545         }
546
547         route_list_menu->popup (1, gtk_get_current_event_time());
548 }
549
550 bool
551 Editor::route_list_selection_filter (const Glib::RefPtr<TreeModel>& model, const TreeModel::Path& path, bool yn)
552 {
553         return true;
554 }
555
556 struct EditorOrderRouteSorter {
557     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
558             /* use of ">" forces the correct sort order */
559             return a->order_key (_order_key) < b->order_key (_order_key);
560     }
561 };
562
563 void
564 Editor::initial_route_list_display ()
565 {
566         boost::shared_ptr<Session::RouteList> routes = session->get_routes();
567         Session::RouteList r (*routes);
568         EditorOrderRouteSorter sorter;
569
570         r.sort (sorter);
571         
572         no_route_list_redisplay = true;
573
574         route_display_model->clear ();
575
576         handle_new_route (r);
577
578         no_route_list_redisplay = false;
579
580         redisplay_route_list ();
581 }
582
583 void
584 Editor::track_list_reorder (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter, int* new_order)
585 {
586         route_redisplay_does_not_sync_order_keys = true;
587         session->set_remote_control_ids();
588         redisplay_route_list ();
589         route_redisplay_does_not_sync_order_keys = false;
590 }
591
592 void
593 Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
594 {
595         /* never reset order keys because of a property change */
596         route_redisplay_does_not_reset_order_keys = true;
597         session->set_remote_control_ids();
598         redisplay_route_list ();
599         route_redisplay_does_not_reset_order_keys = false;
600 }
601
602 void
603 Editor::route_list_delete (const Gtk::TreeModel::Path& path)
604 {
605         /* this could require an order reset & sync */
606         session->set_remote_control_ids();
607         redisplay_route_list ();
608 }
609
610 void  
611 Editor::route_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
612                                                 int x, int y, 
613                                                 const SelectionData& data,
614                                                 guint info, guint time)
615 {
616         if (data.get_target() == "GTK_TREE_MODEL_ROW") {
617                 route_list_display.on_drag_data_received (context, x, y, data, info, time);
618                 return;
619         }
620         context->drag_finish (true, false, time);
621 }
622
623 void
624 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
625 {
626         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
627                 theslot (**i);
628         }
629 }
630
631 void
632 Editor::move_selected_tracks (bool up)
633 {
634         if (selection->tracks.empty()) {
635                 return;
636         }
637
638         typedef pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
639         list<ViewRoute> view_routes;
640         vector<int> neworder;
641         TreeModel::Children rows = route_display_model->children();
642         TreeModel::Children::iterator ri;
643
644         for (ri = rows.begin(); ri != rows.end(); ++ri) {
645                 TimeAxisView* tv = (*ri)[route_display_columns.tv];
646                 boost::shared_ptr<Route> route = (*ri)[route_display_columns.route];
647
648                 view_routes.push_back (ViewRoute (tv, route));
649         }
650
651         list<ViewRoute>::iterator trailing;
652         list<ViewRoute>::iterator leading;
653         
654         if (up) {
655                 
656                 trailing = view_routes.begin();
657                 leading = view_routes.begin();
658                 
659                 ++leading;
660                 
661                 while (leading != view_routes.end()) {
662                         if (selection->selected (leading->first)) {
663                                 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
664                                 leading = view_routes.erase (leading);
665                         } else {
666                                 ++leading;
667                                 ++trailing;
668                         }
669                 }
670
671         } else {
672
673                 /* if we could use reverse_iterator in list::insert, this code
674                    would be a beautiful reflection of the code above. but we can't
675                    and so it looks like a bit of a mess.
676                 */
677
678                 trailing = view_routes.end();
679                 leading = view_routes.end();
680
681                 --leading; if (leading == view_routes.begin()) { return; }
682                 --leading;
683                 --trailing;
684
685                 while (1) {
686
687                         if (selection->selected (leading->first)) {
688                                 list<ViewRoute>::iterator tmp;
689
690                                 /* need to insert *after* trailing, not *before* it,
691                                    which is what insert (iter, val) normally does.
692                                 */
693
694                                 tmp = trailing;
695                                 tmp++;
696
697                                 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
698                                         
699                                 /* can't use iter = cont.erase (iter); form here, because
700                                    we need iter to move backwards.
701                                 */
702
703                                 tmp = leading;
704                                 --tmp;
705
706                                 bool done = false;
707
708                                 if (leading == view_routes.begin()) {
709                                         /* the one we've just inserted somewhere else
710                                            was the first in the list. erase this copy,
711                                            and then break, because we're done.
712                                         */
713                                         done = true;
714                                 }
715
716                                 view_routes.erase (leading);
717                                 
718                                 if (done) {
719                                         break;
720                                 }
721
722                                 leading = tmp;
723
724                         } else {
725                                 if (leading == view_routes.begin()) {
726                                         break;
727                                 }
728                                 --leading;
729                                 --trailing;
730                         }
731                 };
732         }
733
734         for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
735                 neworder.push_back (leading->second->order_key (_order_key));
736         }
737
738         route_display_model->reorder (neworder);
739
740         session->sync_order_keys (_order_key);
741 }