User toggling of editor region/route/etc list, ala editor mixer.
[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 #include <cassert>
24
25 #include "editor.h"
26 #include "keyboard.h"
27 #include "ardour_ui.h"
28 #include "audio_time_axis.h"
29 #include "midi_time_axis.h"
30 #include "mixer_strip.h"
31 #include "gui_thread.h"
32 #include "actions.h"
33
34 #include <pbd/unknown_type.h>
35
36 #include <ardour/route.h>
37
38 #include "i18n.h"
39
40 using namespace sigc;
41 using namespace ARDOUR;
42 using namespace PBD;
43 using namespace Gtk;
44
45
46 void
47 Editor::handle_new_route (Session::RouteList& routes)
48 {
49         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_new_route), routes));
50         
51         TimeAxisView *tv;
52         RouteTimeAxisView *rtv;
53         TreeModel::Row parent;
54         TreeModel::Row row;
55
56         for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
57                 boost::shared_ptr<Route> route = (*x);
58
59                 if (route->hidden()) {
60                         continue;
61                 }
62                 
63                 if (route->default_type() == ARDOUR::DataType::AUDIO)
64                         tv = new AudioTimeAxisView (*this, *session, route, track_canvas);
65                 else if (route->default_type() == ARDOUR::DataType::MIDI)
66                         tv = new MidiTimeAxisView (*this, *session, route, track_canvas);
67                 else
68                         throw unknown_type();
69                 
70 #if 0
71                 if (route_display_model->children().size() == 0) {
72                         
73                         /* set up basic entries */
74                         
75                         TreeModel::Row row;
76                         
77                         row = *(route_display_model->append());  // path = "0"
78                         row[route_display_columns.text] = _("Busses");
79                         row[route_display_columns.tv] = 0;
80                         row = *(route_display_model->append());  // path = "1"
81                         row[route_display_columns.text] = _("Tracks");
82                         row[route_display_columns.tv] = 0;
83                         
84                 }
85                 
86                 if (dynamic_cast<AudioTrack*>(route.get()) != 0) {
87                         TreeModel::iterator iter = route_display_model->get_iter ("1");  // audio tracks 
88                         parent = *iter;
89                 } else {
90                         TreeModel::iterator iter = route_display_model->get_iter ("0");  // busses
91                         parent = *iter;
92                 }
93                 
94                 
95                 row = *(route_display_model->append (parent.children()));
96 #else 
97                 row = *(route_display_model->append ());
98 #endif
99                 
100                 row[route_display_columns.text] = route->name();
101                 row[route_display_columns.visible] = tv->marked_for_display();
102                 row[route_display_columns.tv] = tv;
103                 
104                 track_views.push_back (tv);
105                 
106                 ignore_route_list_reorder = true;
107                 
108                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv)) != 0) {
109                         /* added a new fresh one at the end */
110                         if (rtv->route()->order_key(N_("editor")) == -1) {
111                                 rtv->route()->set_order_key (N_("editor"), route_display_model->children().size()-1);
112                         }
113                 }
114                 
115                 ignore_route_list_reorder = false;
116                 
117                 route->gui_changed.connect (mem_fun(*this, &Editor::handle_gui_changes));
118                 
119                 tv->GoingAway.connect (bind (mem_fun(*this, &Editor::remove_route), tv));
120         }
121
122         if (show_editor_mixer_when_tracks_arrive) {
123                 show_editor_mixer (true);
124         }
125
126         editor_mixer_button.set_sensitive(true);
127         editor_list_button.set_sensitive(true);
128 }
129
130 void
131 Editor::handle_gui_changes (const string & what, void *src)
132 {
133         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::handle_gui_changes), what, src));
134         
135         if (what == "track_height") {
136                 redisplay_route_list ();
137         }
138 }
139
140
141 void
142 Editor::remove_route (TimeAxisView *tv)
143 {
144         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::remove_route), tv));
145
146         
147         TrackViewList::iterator i;
148         TreeModel::Children rows = route_display_model->children();
149         TreeModel::Children::iterator ri;
150
151         if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
152                 track_views.erase (i);
153         }
154
155         for (ri = rows.begin(); ri != rows.end(); ++ri) {
156                 if ((*ri)[route_display_columns.tv] == tv) {
157                         route_display_model->erase (ri);
158                         break;
159                 }
160         }
161         /* since the editor mixer goes away when you remove a route, set the
162          * button to inactive and untick the menu option
163          */
164         editor_mixer_button.set_active(false);
165         ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
166
167         /* and disable if all tracks and/or routes are gone */
168
169         if (track_views.size() == 0) {
170                 editor_mixer_button.set_sensitive(false);
171                 
172                 editor_list_button.set_active(false);
173                 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-list");
174                 editor_list_button.set_sensitive(false);
175         }
176 }
177
178 void
179 Editor::route_name_changed (TimeAxisView *tv)
180 {
181         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::route_name_changed), tv));
182         
183         TreeModel::Children rows = route_display_model->children();
184         TreeModel::Children::iterator i;
185         
186         for (i = rows.begin(); i != rows.end(); ++i) {
187                 if ((*i)[route_display_columns.tv] == tv) {
188                         (*i)[route_display_columns.text] = tv->name();
189                         break;
190                 }
191         } 
192
193 }
194
195 void
196 Editor::hide_track_in_display (TimeAxisView& tv)
197 {
198         TreeModel::Children rows = route_display_model->children();
199         TreeModel::Children::iterator i;
200
201         for (i = rows.begin(); i != rows.end(); ++i) {
202                 if ((*i)[route_display_columns.tv] == &tv) { 
203                         (*i)[route_display_columns.visible] = false;
204                         break;
205                 }
206         }
207
208         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
209
210         if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
211                 // this will hide the mixer strip
212                 set_selected_mixer_strip (tv);
213         }
214 }
215
216 void
217 Editor::show_track_in_display (TimeAxisView& tv)
218 {
219         TreeModel::Children rows = route_display_model->children();
220         TreeModel::Children::iterator i;
221         
222         for (i = rows.begin(); i != rows.end(); ++i) {
223                 if ((*i)[route_display_columns.tv] == &tv) { 
224                         (*i)[route_display_columns.visible] = true;
225                         tv.set_marked_for_display (true);
226                         break;
227                 }
228         }
229 }
230
231 void
232 Editor::route_list_reordered (const TreeModel::Path& path,const TreeModel::iterator& iter,int* what)
233 {
234         redisplay_route_list ();
235 }
236
237 void
238 Editor::redisplay_route_list ()
239 {
240         TreeModel::Children rows = route_display_model->children();
241         TreeModel::Children::iterator i;
242         uint32_t position;
243         uint32_t order;
244         int n;
245         
246         if (no_route_list_redisplay) {
247                 return;
248         }
249
250         for (n = 0, order = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
251                 TimeAxisView *tv = (*i)[route_display_columns.tv];
252                 RouteTimeAxisView* rt; 
253
254                 if (tv == 0) {
255                         // just a "title" row
256                         continue;
257                 }
258
259                 if (!ignore_route_list_reorder) {
260                         
261                         /* this reorder is caused by user action, so reassign sort order keys
262                            to tracks.
263                         */
264                         
265                         if ((rt = dynamic_cast<RouteTimeAxisView*> (tv)) != 0) {
266                                 rt->route()->set_order_key (N_("editor"), order);
267                                 ++order;
268                         }
269                 }
270
271                 bool visible = (*i)[route_display_columns.visible];
272
273                 if (visible) {
274                         tv->set_marked_for_display (true);
275                         position += tv->show_at (position, n, &edit_controls_vbox);
276                 } else {
277                         tv->hide ();
278                 }
279                 
280                 n++;
281                 
282         }
283
284         full_canvas_height = position;
285
286         /* make sure the cursors stay on top of every newly added track */
287
288         cursor_group->raise_to_top ();
289
290         reset_scrolling_region ();
291 }
292
293 void
294 Editor::hide_all_tracks (bool with_select)
295 {
296         TreeModel::Children rows = route_display_model->children();
297         TreeModel::Children::iterator i;
298
299         no_route_list_redisplay = true;
300
301         for (i = rows.begin(); i != rows.end(); ++i) {
302                 
303                 TreeModel::Row row = (*i);
304                 TimeAxisView *tv = row[route_display_columns.tv];
305
306                 if (tv == 0) {
307                         continue;
308                 }
309                 
310                 row[route_display_columns.visible] = false;
311         }
312
313         no_route_list_redisplay = false;
314         redisplay_route_list ();
315
316         /* XXX this seems like a hack and half, but its not clear where to put this
317            otherwise.
318         */
319
320         reset_scrolling_region ();
321 }
322
323 void
324 Editor::build_route_list_menu ()
325 {
326         using namespace Menu_Helpers;
327         using namespace Gtk;
328
329         route_list_menu = new Menu;
330         
331         MenuList& items = route_list_menu->items();
332         route_list_menu->set_name ("ArdourContextMenu");
333
334         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Editor::show_all_routes)));
335         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Editor::hide_all_routes)));
336         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Editor::show_all_audiotracks)));
337         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Editor::hide_all_audiotracks)));
338         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Editor::show_all_audiobus)));
339         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Editor::hide_all_audiobus)));
340
341 }
342
343 void
344 Editor::set_all_tracks_visibility (bool yn)
345 {
346         TreeModel::Children rows = route_display_model->children();
347         TreeModel::Children::iterator i;
348
349         no_route_list_redisplay = true;
350
351         for (i = rows.begin(); i != rows.end(); ++i) {
352
353                 TreeModel::Row row = (*i);
354                 TimeAxisView* tv = row[route_display_columns.tv];
355
356                 if (tv == 0) {
357                         continue;
358                 }
359                 
360                 (*i)[route_display_columns.visible] = yn;
361         }
362
363         no_route_list_redisplay = false;
364         redisplay_route_list ();
365 }
366
367 void
368 Editor::set_all_audio_visibility (int tracks, bool yn) 
369 {
370         TreeModel::Children rows = route_display_model->children();
371         TreeModel::Children::iterator i;
372
373         no_route_list_redisplay = true;
374
375         for (i = rows.begin(); i != rows.end(); ++i) {
376                 TreeModel::Row row = (*i);
377                 TimeAxisView* tv = row[route_display_columns.tv];
378                 AudioTimeAxisView* atv;
379
380                 if (tv == 0) {
381                         continue;
382                 }
383
384                 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
385                         switch (tracks) {
386                         case 0:
387                                 (*i)[route_display_columns.visible] = yn;
388                                 break;
389
390                         case 1:
391                                 if (atv->is_audio_track()) {
392                                         (*i)[route_display_columns.visible] = yn;
393                                 }
394                                 break;
395                                 
396                         case 2:
397                                 if (!atv->is_audio_track()) {
398                                         (*i)[route_display_columns.visible] = yn;
399                                 }
400                                 break;
401                         }
402                 }
403         }
404
405         no_route_list_redisplay = false;
406         redisplay_route_list ();
407 }
408
409 void
410 Editor::hide_all_routes ()
411 {
412         set_all_tracks_visibility (false);
413 }
414
415 void
416 Editor::show_all_routes ()
417 {
418         set_all_tracks_visibility (true);
419 }
420
421 void
422 Editor::show_all_audiobus ()
423 {
424         set_all_audio_visibility (2, true);
425 }
426 void
427 Editor::hide_all_audiobus ()
428 {
429         set_all_audio_visibility (2, false);
430 }
431
432 void
433 Editor::show_all_audiotracks()
434 {
435         set_all_audio_visibility (1, true);
436 }
437 void
438 Editor::hide_all_audiotracks ()
439 {
440         set_all_audio_visibility (1, false);
441 }
442
443 bool
444 Editor::route_list_display_button_press (GdkEventButton* ev)
445 {
446         if (Keyboard::is_context_menu_event (ev)) {
447                 show_route_list_menu ();
448                 return true;
449         }
450
451         TreeIter iter;
452         TreeModel::Path path;
453         TreeViewColumn* column;
454         int cellx;
455         int celly;
456         
457         if (!route_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
458                 return false;
459         }
460
461         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
462         case 0:
463                 if ((iter = route_display_model->get_iter (path))) {
464                         TimeAxisView* tv = (*iter)[route_display_columns.tv];
465                         if (tv) {
466                                 bool visible = (*iter)[route_display_columns.visible];
467                                 (*iter)[route_display_columns.visible] = !visible;
468                         }
469                 }
470                 return true;
471
472         case 1:
473                 /* allow normal processing to occur */
474                 return false;
475
476         default:
477                 break;
478         }
479
480         return false;
481 }
482
483 void
484 Editor::show_route_list_menu()
485 {
486         if (route_list_menu == 0) {
487                 build_route_list_menu ();
488         }
489
490         route_list_menu->popup (1, gtk_get_current_event_time());
491 }
492
493 bool
494 Editor::route_list_selection_filter (const Glib::RefPtr<TreeModel>& model, const TreeModel::Path& path, bool yn)
495 {
496         return true;
497 }
498
499 struct EditorOrderRouteSorter {
500     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
501             /* use of ">" forces the correct sort order */
502             return a->order_key ("editor") < b->order_key ("editor");
503     }
504 };
505
506 void
507 Editor::initial_route_list_display ()
508 {
509         boost::shared_ptr<Session::RouteList> routes = session->get_routes();
510         Session::RouteList r (*routes);
511         EditorOrderRouteSorter sorter;
512
513         r.sort (sorter);
514         
515         no_route_list_redisplay = true;
516
517         route_display_model->clear ();
518
519         handle_new_route (r);
520
521         no_route_list_redisplay = false;
522
523         redisplay_route_list ();
524 }
525
526 void
527 Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
528 {
529         session->set_remote_control_ids();
530         redisplay_route_list ();
531 }
532
533 void
534 Editor::route_list_delete (const Gtk::TreeModel::Path& path)
535 {
536         session->set_remote_control_ids();
537         redisplay_route_list ();
538 }