do not pass a non-zero offset to plugins AFTER the first call to connect_and_run...
[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         for (n = 0, order = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
299                 TimeAxisView *tv = (*i)[route_display_columns.tv];
300                 boost::shared_ptr<Route> route = (*i)[route_display_columns.route];
301
302                 if (tv == 0) {
303                         // just a "title" row
304                         continue;
305                 }
306
307                 if (!route_redisplay_does_not_reset_order_keys) {
308                         
309                         /* this reorder is caused by user action, so reassign sort order keys
310                            to tracks.
311                         */
312                         
313                         route->set_order_key (_order_key, order);
314                 }
315
316                 bool visible = (*i)[route_display_columns.visible];
317                 
318                 if (visible) {
319                         tv->set_marked_for_display (true);
320                         position += tv->show_at (position, n, &edit_controls_vbox);
321                         tv->clip_to_viewport ();
322                 } else {
323                         tv->set_marked_for_display (false);
324                         tv->hide ();
325                 }
326                 
327                 ++order;
328                 ++n;
329         }
330
331         /* whenever we go idle, update the track view list to reflect the new order.
332            we can't do this here, because we could mess up something that is traversing
333            the track order and has caused a redisplay of the list.
334         */
335
336         Glib::signal_idle().connect (mem_fun (*this, &Editor::sync_track_view_list_and_route_list));
337
338         full_canvas_height = position + canvas_timebars_vsize;
339         vertical_adjustment.set_upper (full_canvas_height);
340         if ((vertical_adjustment.get_value() + canvas_height) > vertical_adjustment.get_upper()) {
341                 /* 
342                    We're increasing the size of the canvas while the bottom is visible.
343                    We scroll down to keep in step with the controls layout.
344                 */
345                 vertical_adjustment.set_value (full_canvas_height - canvas_height);
346         } 
347
348         if (!route_redisplay_does_not_reset_order_keys && !route_redisplay_does_not_sync_order_keys) {
349                 session->sync_order_keys (_order_key);
350         }
351 }
352
353 bool
354 Editor::sync_track_view_list_and_route_list ()
355 {
356         TreeModel::Children rows = route_display_model->children();
357         TreeModel::Children::iterator i;
358
359         track_views.clear ();
360
361         for (i = rows.begin(); i != rows.end(); ++i) {
362                 TimeAxisView *tv = (*i)[route_display_columns.tv];
363                 track_views.push_back (tv);
364         }
365         
366         return false; // do not call again (until needed)
367 }
368
369 void
370 Editor::hide_all_tracks (bool with_select)
371 {
372         TreeModel::Children rows = route_display_model->children();
373         TreeModel::Children::iterator i;
374
375         no_route_list_redisplay = true;
376
377         for (i = rows.begin(); i != rows.end(); ++i) {
378                 
379                 TreeModel::Row row = (*i);
380                 TimeAxisView *tv = row[route_display_columns.tv];
381
382                 if (tv == 0) {
383                         continue;
384                 }
385                 
386                 row[route_display_columns.visible] = false;
387         }
388
389         no_route_list_redisplay = false;
390         redisplay_route_list ();
391
392         /* XXX this seems like a hack and half, but its not clear where to put this
393            otherwise.
394         */
395
396         //reset_scrolling_region ();
397 }
398
399 void
400 Editor::build_route_list_menu ()
401 {
402         using namespace Menu_Helpers;
403         using namespace Gtk;
404
405         route_list_menu = new Menu;
406         
407         MenuList& items = route_list_menu->items();
408         route_list_menu->set_name ("ArdourContextMenu");
409
410         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Editor::show_all_routes)));
411         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Editor::hide_all_routes)));
412         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Editor::show_all_audiotracks)));
413         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Editor::hide_all_audiotracks)));
414         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Editor::show_all_audiobus)));
415         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Editor::hide_all_audiobus)));
416
417 }
418
419 void
420 Editor::set_all_tracks_visibility (bool yn)
421 {
422         TreeModel::Children rows = route_display_model->children();
423         TreeModel::Children::iterator i;
424
425         no_route_list_redisplay = true;
426
427         for (i = rows.begin(); i != rows.end(); ++i) {
428
429                 TreeModel::Row row = (*i);
430                 TimeAxisView* tv = row[route_display_columns.tv];
431
432                 if (tv == 0) {
433                         continue;
434                 }
435                 
436                 (*i)[route_display_columns.visible] = yn;
437         }
438
439         no_route_list_redisplay = false;
440         redisplay_route_list ();
441 }
442
443 void
444 Editor::set_all_audio_visibility (int tracks, bool yn) 
445 {
446         TreeModel::Children rows = route_display_model->children();
447         TreeModel::Children::iterator i;
448
449         no_route_list_redisplay = true;
450
451         for (i = rows.begin(); i != rows.end(); ++i) {
452                 TreeModel::Row row = (*i);
453                 TimeAxisView* tv = row[route_display_columns.tv];
454                 AudioTimeAxisView* atv;
455
456                 if (tv == 0) {
457                         continue;
458                 }
459
460                 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
461                         switch (tracks) {
462                         case 0:
463                                 (*i)[route_display_columns.visible] = yn;
464                                 break;
465
466                         case 1:
467                                 if (atv->is_audio_track()) {
468                                         (*i)[route_display_columns.visible] = yn;
469                                 }
470                                 break;
471                                 
472                         case 2:
473                                 if (!atv->is_audio_track()) {
474                                         (*i)[route_display_columns.visible] = yn;
475                                 }
476                                 break;
477                         }
478                 }
479         }
480
481         no_route_list_redisplay = false;
482         redisplay_route_list ();
483 }
484
485 void
486 Editor::hide_all_routes ()
487 {
488         set_all_tracks_visibility (false);
489 }
490
491 void
492 Editor::show_all_routes ()
493 {
494         set_all_tracks_visibility (true);
495 }
496
497 void
498 Editor::show_all_audiobus ()
499 {
500         set_all_audio_visibility (2, true);
501 }
502 void
503 Editor::hide_all_audiobus ()
504 {
505         set_all_audio_visibility (2, false);
506 }
507
508 void
509 Editor::show_all_audiotracks()
510 {
511         set_all_audio_visibility (1, true);
512 }
513 void
514 Editor::hide_all_audiotracks ()
515 {
516         set_all_audio_visibility (1, false);
517 }
518
519 bool
520 Editor::route_list_display_button_press (GdkEventButton* ev)
521 {
522         if (Keyboard::is_context_menu_event (ev)) {
523                 show_route_list_menu ();
524                 return true;
525         }
526
527         TreeIter iter;
528         TreeModel::Path path;
529         TreeViewColumn* column;
530         int cellx;
531         int celly;
532         
533         if (!route_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
534                 return false;
535         }
536
537         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
538         case 0:
539                 if ((iter = route_display_model->get_iter (path))) {
540                         TimeAxisView* tv = (*iter)[route_display_columns.tv];
541                         if (tv) {
542                                 bool visible = (*iter)[route_display_columns.visible];
543                                 (*iter)[route_display_columns.visible] = !visible;
544                         }
545                 }
546                 return true;
547
548         case 1:
549                 /* allow normal processing to occur */
550                 return false;
551
552         default:
553                 break;
554         }
555
556         return false;
557 }
558
559 void
560 Editor::show_route_list_menu()
561 {
562         if (route_list_menu == 0) {
563                 build_route_list_menu ();
564         }
565
566         route_list_menu->popup (1, gtk_get_current_event_time());
567 }
568
569 bool
570 Editor::route_list_selection_filter (const Glib::RefPtr<TreeModel>& model, const TreeModel::Path& path, bool yn)
571 {
572         return true;
573 }
574
575 struct EditorOrderRouteSorter {
576     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
577             /* use of ">" forces the correct sort order */
578             return a->order_key (_order_key) < b->order_key (_order_key);
579     }
580 };
581
582 void
583 Editor::initial_route_list_display ()
584 {
585         boost::shared_ptr<Session::RouteList> routes = session->get_routes();
586         Session::RouteList r (*routes);
587         EditorOrderRouteSorter sorter;
588
589         r.sort (sorter);
590         
591         no_route_list_redisplay = true;
592
593         route_display_model->clear ();
594
595         handle_new_route (r);
596
597         no_route_list_redisplay = false;
598
599         redisplay_route_list ();
600 }
601
602 void
603 Editor::track_list_reorder (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter, int* new_order)
604 {
605         route_redisplay_does_not_sync_order_keys = true;
606         session->set_remote_control_ids();
607         redisplay_route_list ();
608         route_redisplay_does_not_sync_order_keys = false;
609 }
610
611 void
612 Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
613 {
614         /* never reset order keys because of a property change */
615         route_redisplay_does_not_reset_order_keys = true;
616         session->set_remote_control_ids();
617         redisplay_route_list ();
618         route_redisplay_does_not_reset_order_keys = false;
619 }
620
621 void
622 Editor::route_list_delete (const Gtk::TreeModel::Path& path)
623 {
624         /* this could require an order reset & sync */
625         session->set_remote_control_ids();
626         redisplay_route_list ();
627 }
628
629 void  
630 Editor::route_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
631                                                 int x, int y, 
632                                                 const SelectionData& data,
633                                                 guint info, guint time)
634 {
635         if (data.get_target() == "GTK_TREE_MODEL_ROW") {
636                 route_list_display.on_drag_data_received (context, x, y, data, info, time);
637                 return;
638         }
639         context->drag_finish (true, false, time);
640 }
641
642 void
643 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
644 {
645         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
646                 theslot (**i);
647         }
648 }
649
650 void
651 Editor::move_selected_tracks (bool up)
652 {
653         if (selection->tracks.empty()) {
654                 return;
655         }
656
657         typedef pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
658         list<ViewRoute> view_routes;
659         vector<int> neworder;
660         TreeModel::Children rows = route_display_model->children();
661         TreeModel::Children::iterator ri;
662
663         for (ri = rows.begin(); ri != rows.end(); ++ri) {
664                 TimeAxisView* tv = (*ri)[route_display_columns.tv];
665                 boost::shared_ptr<Route> route = (*ri)[route_display_columns.route];
666
667                 view_routes.push_back (ViewRoute (tv, route));
668         }
669
670         list<ViewRoute>::iterator trailing;
671         list<ViewRoute>::iterator leading;
672         
673         if (up) {
674                 
675                 trailing = view_routes.begin();
676                 leading = view_routes.begin();
677                 
678                 ++leading;
679                 
680                 while (leading != view_routes.end()) {
681                         if (selection->selected (leading->first)) {
682                                 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
683                                 leading = view_routes.erase (leading);
684                         } else {
685                                 ++leading;
686                                 ++trailing;
687                         }
688                 }
689
690         } else {
691
692                 /* if we could use reverse_iterator in list::insert, this code
693                    would be a beautiful reflection of the code above. but we can't
694                    and so it looks like a bit of a mess.
695                 */
696
697                 trailing = view_routes.end();
698                 leading = view_routes.end();
699
700                 --leading; if (leading == view_routes.begin()) { return; }
701                 --leading;
702                 --trailing;
703
704                 while (1) {
705
706                         if (selection->selected (leading->first)) {
707                                 list<ViewRoute>::iterator tmp;
708
709                                 /* need to insert *after* trailing, not *before* it,
710                                    which is what insert (iter, val) normally does.
711                                 */
712
713                                 tmp = trailing;
714                                 tmp++;
715
716                                 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
717                                         
718                                 /* can't use iter = cont.erase (iter); form here, because
719                                    we need iter to move backwards.
720                                 */
721
722                                 tmp = leading;
723                                 --tmp;
724
725                                 bool done = false;
726
727                                 if (leading == view_routes.begin()) {
728                                         /* the one we've just inserted somewhere else
729                                            was the first in the list. erase this copy,
730                                            and then break, because we're done.
731                                         */
732                                         done = true;
733                                 }
734
735                                 view_routes.erase (leading);
736                                 
737                                 if (done) {
738                                         break;
739                                 }
740
741                                 leading = tmp;
742
743                         } else {
744                                 if (leading == view_routes.begin()) {
745                                         break;
746                                 }
747                                 --leading;
748                                 --trailing;
749                         }
750                 };
751         }
752
753         for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
754                 neworder.push_back (leading->second->order_key (_order_key));
755         }
756
757         route_display_model->reorder (neworder);
758
759         session->sync_order_keys (_order_key);
760 }