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