extend filtering to VST + AU, tweak details, add filter-by-creator and by library
[ardour.git] / gtk2_ardour / mixer_ui.cc
1 /*
2     Copyright (C) 2000-2004 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 <sigc++/bind.h>
22
23 #include <gtkmm/accelmap.h>
24
25 #include <pbd/convert.h>
26 #include <glibmm/thread.h>
27
28 #include <gtkmm2ext/gtk_ui.h>
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/stop_signal.h>
31 #include <gtkmm2ext/window_title.h>
32
33 #include <ardour/session.h>
34 #include <ardour/audio_track.h>
35 #include <ardour/session_route.h>
36 #include <ardour/audio_diskstream.h>
37 #include <ardour/plugin_manager.h>
38
39 #include "keyboard.h"
40 #include "mixer_ui.h"
41 #include "mixer_strip.h"
42 #include "plugin_selector.h"
43 #include "ardour_ui.h"
44 #include "prompter.h"
45 #include "utils.h"
46 #include "actions.h"
47 #include "gui_thread.h"
48
49 #include "i18n.h"
50
51 using namespace ARDOUR;
52 using namespace PBD;
53 using namespace Gtk;
54 using namespace Glib;
55 using namespace Gtkmm2ext;
56 using namespace sigc;
57 using namespace std;
58
59 using PBD::atoi;
60
61 Mixer_UI::Mixer_UI ()
62         : Window (Gtk::WINDOW_TOPLEVEL)
63 {
64         _strip_width = Wide;
65         track_menu = 0;
66         mix_group_context_menu = 0;
67         no_track_list_redisplay = false;
68         in_group_row_change = false;
69         _visible = false;
70
71         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
72         scroller_base.set_name ("MixerWindow");
73         scroller_base.signal_button_release_event().connect (mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
74         // add as last item of strip packer
75         strip_packer.pack_end (scroller_base, true, true);
76
77         scroller.add (strip_packer);
78         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
79
80         track_model = ListStore::create (track_columns);
81         track_display.set_model (track_model);
82         track_display.append_column (_("Strips"), track_columns.text);
83         track_display.append_column (_("Show"), track_columns.visible);
84         track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
85         track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
86         track_display.get_column (0)->set_expand(true);
87         track_display.get_column (1)->set_expand(false);
88         track_display.set_name (X_("MixerTrackDisplayList"));
89         track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
90         track_display.set_reorderable (true);
91         track_display.set_headers_visible (true);
92
93         track_model->signal_row_deleted().connect (mem_fun (*this, &Mixer_UI::track_list_delete));
94         track_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::track_list_change));
95
96         CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
97         track_list_visible_cell->property_activatable() = true;
98         track_list_visible_cell->property_radio() = false;
99
100         track_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::track_display_button_press), false);
101
102         track_display_scroller.add (track_display);
103         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
104
105         group_model = ListStore::create (group_columns);
106         group_display.set_model (group_model);
107         group_display.append_column (_("Group"), group_columns.text);
108         group_display.append_column (_("Active"), group_columns.active);
109         group_display.append_column (_("Show"), group_columns.visible);
110         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
111         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
112         group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
113         group_display.get_column (0)->set_expand(true);
114         group_display.get_column (1)->set_expand(false);
115         group_display.get_column (2)->set_expand(false);
116         group_display.set_name ("MixerGroupList");
117         group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
118         group_display.set_reorderable (true);
119         group_display.set_headers_visible (true);
120         group_display.set_rules_hint (true);
121
122         /* name is directly editable */
123
124         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
125         name_cell->property_editable() = true;
126         name_cell->signal_edited().connect (mem_fun (*this, &Mixer_UI::mix_group_name_edit));
127
128         /* use checkbox for the active column */
129
130         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
131         active_cell->property_activatable() = true;
132         active_cell->property_radio() = false;
133
134         /* use checkbox for the visible column */
135
136         active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (2));
137         active_cell->property_activatable() = true;
138         active_cell->property_radio() = false;
139
140         group_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::mix_group_row_change));
141
142         group_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::group_display_button_press), false);
143
144         group_display_scroller.add (group_display);
145         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
146
147         HBox* mix_group_display_button_box = manage (new HBox());
148
149         Button* mix_group_add_button = manage (new Button ());
150         Button* mix_group_remove_button = manage (new Button ());
151
152         Widget* w;
153
154         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
155         w->show();
156         mix_group_add_button->add (*w);
157
158         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
159         w->show();
160         mix_group_remove_button->add (*w);
161
162         mix_group_display_button_box->set_homogeneous (true);
163
164         mix_group_add_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::new_mix_group));
165         mix_group_remove_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::remove_selected_mix_group));
166
167         mix_group_display_button_box->add (*mix_group_remove_button);
168         mix_group_display_button_box->add (*mix_group_add_button);
169
170         group_display_vbox.pack_start (group_display_scroller, true, true);
171         group_display_vbox.pack_start (*mix_group_display_button_box, false, false);
172
173         track_display_frame.set_name("BaseFrame");
174         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
175         track_display_frame.add(track_display_scroller);
176
177         group_display_frame.set_name ("BaseFrame");
178         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
179         group_display_frame.add (group_display_vbox);
180
181         rhs_pane1.pack1 (track_display_frame);
182         rhs_pane1.pack2 (group_display_frame);
183
184         list_vpacker.pack_start (rhs_pane1, true, true);
185
186         global_hpacker.pack_start (scroller, true, true);
187         global_hpacker.pack_start (out_packer, false, false, 12);
188
189         list_hpane.add1(list_vpacker);
190         list_hpane.add2(global_hpacker);
191
192         rhs_pane1.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
193                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
194         list_hpane.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
195                                                          static_cast<Gtk::Paned*> (&list_hpane)));
196         
197
198         rhs_pane1.set_data ("collapse-direction", (gpointer) 0);
199         list_hpane.set_data ("collapse-direction", (gpointer) 1);
200
201         global_vpacker.pack_start (list_hpane, true, true);
202
203         add (global_vpacker);
204         set_name ("MixerWindow");
205         
206         WindowTitle title(Glib::get_application_name());
207         title += _("Mixer");
208         set_title (title.get_string());
209
210         set_wmclass (X_("ardour_mixer"), "Ardour");
211
212         add_accel_group (ActionManager::ui_manager->get_accel_group());
213
214         signal_delete_event().connect (mem_fun (*this, &Mixer_UI::hide_window));
215         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
216
217         _plugin_selector = new PluginSelector (PluginManager::the_manager());
218
219         signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
220
221         _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
222 }
223
224 Mixer_UI::~Mixer_UI ()
225 {
226 }
227
228 void
229 Mixer_UI::ensure_float (Window& win)
230 {
231         win.set_transient_for (*this);
232 }
233
234 void
235 Mixer_UI::show_window ()
236 {
237         show_all ();
238
239         set_window_pos_and_size ();
240
241         /* now reset each strips width so the right widgets are shown */
242         MixerStrip* ms;
243
244         TreeModel::Children rows = track_model->children();
245         TreeModel::Children::iterator ri;
246
247         for (ri = rows.begin(); ri != rows.end(); ++ri) {
248                 ms = (*ri)[track_columns.strip];
249                 ms->set_width (ms->get_width(), ms->width_owner());
250         }
251         _visible = true;
252 }
253
254 bool
255 Mixer_UI::hide_window (GdkEventAny *ev)
256 {
257         get_window_pos_and_size ();
258
259         _visible = false;
260         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
261 }
262
263
264 void
265 Mixer_UI::add_strip (Session::RouteList& routes)
266 {
267         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), routes));
268         
269         MixerStrip* strip;
270
271         for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
272                 boost::shared_ptr<Route> route = (*x);
273
274                 if (route->hidden()) {
275                         return;
276                 }
277                 
278                 strip = new MixerStrip (*this, *session, route);
279                 strips.push_back (strip);
280                 
281                 if (strip->width_owner() != strip) {
282                         strip->set_width (_strip_width, this);
283                 }
284
285                 show_strip (strip);
286                 
287                 no_track_list_redisplay = true;
288                 
289                 TreeModel::Row row = *(track_model->append());
290                 row[track_columns.text] = route->name();
291                 
292                 row[track_columns.visible] = strip->marked_for_display();
293                 row[track_columns.route] = route;
294                 row[track_columns.strip] = strip;
295                 
296                 no_track_list_redisplay = false;
297                 redisplay_track_list ();
298                 
299                 route->name_changed.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
300                 strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
301                 
302                 strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
303         }
304 }
305
306 void
307 Mixer_UI::remove_strip (MixerStrip* strip)
308 {
309         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
310         
311         TreeModel::Children rows = track_model->children();
312         TreeModel::Children::iterator ri;
313         list<MixerStrip *>::iterator i;
314
315         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
316                 strips.erase (i);
317         }
318
319         for (ri = rows.begin(); ri != rows.end(); ++ri) {
320                 if ((*ri)[track_columns.strip] == strip) {
321                         track_model->erase (ri);
322                         break;
323                 }
324         }
325 }
326
327 void
328 Mixer_UI::follow_strip_selection ()
329 {
330         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
331                 (*i)->set_selected (_selection.selected ((*i)->route()));
332         }
333 }
334
335 bool
336 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
337 {
338         if (ev->button == 1) {
339
340                 /* this allows the user to click on the strip to terminate comment
341                    editing. XXX it needs improving so that we don't select the strip
342                    at the same time.
343                 */
344                 
345                 if (_selection.selected (strip->route())) {
346                         _selection.remove (strip->route());
347                 } else {
348                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
349                                 _selection.add (strip->route());
350                         } else {
351                                 _selection.set (strip->route());
352                         }
353                 }
354         }
355
356         return true;
357 }
358
359 void
360 Mixer_UI::connect_to_session (Session* sess)
361 {
362
363         session = sess;
364
365         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
366         set_state (*node);
367
368         WindowTitle title(session->name());
369         title += _("Mixer");
370         title += Glib::get_application_name();
371
372         set_title (title.get_string());
373
374         initial_track_display ();
375
376         session->GoingAway.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
377         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
378         session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
379         session->mix_group_removed.connect (mem_fun(*this, &Mixer_UI::mix_groups_changed));
380
381         mix_groups_changed ();
382         
383         _plugin_selector->set_session (session);
384
385         if (_visible) {
386                show_window();
387         }
388
389         start_updating ();
390 }
391
392 void
393 Mixer_UI::disconnect_from_session ()
394 {
395         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
396         
397         group_model->clear ();
398         _selection.clear ();
399
400         WindowTitle title(Glib::get_application_name());
401         title += _("Mixer");
402         set_title (title.get_string());
403         
404         stop_updating ();
405 }
406
407 void
408 Mixer_UI::show_strip (MixerStrip* ms)
409 {
410         TreeModel::Children rows = track_model->children();
411         TreeModel::Children::iterator i;
412         
413         for (i = rows.begin(); i != rows.end(); ++i) {
414         
415                 MixerStrip* strip = (*i)[track_columns.strip];
416                 if (strip == ms) {
417                         (*i)[track_columns.visible] = true;
418                         break;
419                 }
420         }
421 }
422
423 void
424 Mixer_UI::hide_strip (MixerStrip* ms)
425 {
426         TreeModel::Children rows = track_model->children();
427         TreeModel::Children::iterator i;
428         
429         for (i = rows.begin(); i != rows.end(); ++i) {
430                 
431                 MixerStrip* strip = (*i)[track_columns.strip];
432                 if (strip == ms) {
433                         (*i)[track_columns.visible] = false;
434                         break;
435                 }
436         }
437 }
438
439 gint
440 Mixer_UI::start_updating ()
441 {
442     fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
443     return 0;
444 }
445
446 gint
447 Mixer_UI::stop_updating ()
448 {
449     fast_screen_update_connection.disconnect();
450     return 0;
451 }
452
453 void
454 Mixer_UI::fast_update_strips ()
455 {
456         if (is_mapped () && session) {
457                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
458                         (*i)->fast_update ();
459                 }
460         }
461 }
462
463 void
464 Mixer_UI::set_all_strips_visibility (bool yn)
465 {
466         TreeModel::Children rows = track_model->children();
467         TreeModel::Children::iterator i;
468
469         no_track_list_redisplay = true;
470
471         for (i = rows.begin(); i != rows.end(); ++i) {
472
473                 TreeModel::Row row = (*i);
474                 MixerStrip* strip = row[track_columns.strip];
475                 
476                 if (strip == 0) {
477                         continue;
478                 }
479                 
480                 if (strip->route()->master() || strip->route()->control()) {
481                         continue;
482                 }
483
484                 (*i)[track_columns.visible] = yn;
485         }
486
487         no_track_list_redisplay = false;
488         redisplay_track_list ();
489 }
490
491
492 void
493 Mixer_UI::set_all_audio_visibility (int tracks, bool yn) 
494 {
495         TreeModel::Children rows = track_model->children();
496         TreeModel::Children::iterator i;
497
498         no_track_list_redisplay = true;
499
500         for (i = rows.begin(); i != rows.end(); ++i) {
501                 TreeModel::Row row = (*i);
502                 MixerStrip* strip = row[track_columns.strip];
503
504                 if (strip == 0) {
505                         continue;
506                 }
507
508                 if (strip->route()->master() || strip->route()->control()) {
509                         continue;
510                 }
511
512                 AudioTrack* at = strip->audio_track();
513
514                 switch (tracks) {
515                 case 0:
516                         (*i)[track_columns.visible] = yn;
517                         break;
518                         
519                 case 1:
520                         if (at) { /* track */
521                                 (*i)[track_columns.visible] = yn;
522                         }
523                         break;
524                         
525                 case 2:
526                         if (!at) { /* bus */
527                                 (*i)[track_columns.visible] = yn;
528                         }
529                         break;
530                 }
531         }
532
533         no_track_list_redisplay = false;
534         redisplay_track_list ();
535 }
536
537 void
538 Mixer_UI::hide_all_routes ()
539 {
540         set_all_strips_visibility (false);
541 }
542
543 void
544 Mixer_UI::show_all_routes ()
545 {
546         set_all_strips_visibility (true);
547 }
548
549 void
550 Mixer_UI::show_all_audiobus ()
551 {
552         set_all_audio_visibility (2, true);
553 }
554 void
555 Mixer_UI::hide_all_audiobus ()
556 {
557         set_all_audio_visibility (2, false);
558 }
559
560 void
561 Mixer_UI::show_all_audiotracks()
562 {
563         set_all_audio_visibility (1, true);
564 }
565 void
566 Mixer_UI::hide_all_audiotracks ()
567 {
568         set_all_audio_visibility (1, false);
569 }
570
571 void
572 Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
573 {
574         session->set_remote_control_ids();
575         redisplay_track_list ();
576 }
577
578 void
579 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path& path)
580 {
581         session->set_remote_control_ids();
582         redisplay_track_list ();
583 }
584
585 void
586 Mixer_UI::redisplay_track_list ()
587 {
588         TreeModel::Children rows = track_model->children();
589         TreeModel::Children::iterator i;
590         long order;
591
592         if (no_track_list_redisplay) {
593                 return;
594         }
595
596         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
597                 MixerStrip* strip = (*i)[track_columns.strip];
598
599                 if (strip == 0) {
600                         /* we're in the middle of changing a row, don't worry */
601                         continue;
602                 }
603
604                 bool visible = (*i)[track_columns.visible];
605
606                 if (visible) {
607                         strip->set_marked_for_display (true);
608                         strip->route()->set_order_key (N_("signal"), order);
609
610                         if (strip->packed()) {
611
612                                 if (strip->route()->master() || strip->route()->control()) {
613                                         out_packer.reorder_child (*strip, -1);
614                                 } else {
615                                         strip_packer.reorder_child (*strip, -1); /* put at end */
616                                 }
617
618                         } else {
619
620                                 if (strip->route()->master() || strip->route()->control()) {
621                                         out_packer.pack_start (*strip, false, false);
622                                 } else {
623                                         strip_packer.pack_start (*strip, false, false);
624                                 }
625                                 strip->set_packed (true);
626                                 strip->show_all ();
627                         }
628
629                 } else {
630
631                         if (strip->route()->master() || strip->route()->control()) {
632                                 /* do nothing, these cannot be hidden */
633                         } else {
634                                 if (strip->packed()) {
635                                         strip_packer.remove (*strip);
636                                         strip->set_packed (false);
637                                 }
638                         }
639                 }
640         }
641 }
642
643 struct SignalOrderRouteSorter {
644     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
645             /* use of ">" forces the correct sort order */
646             return a->order_key ("signal") < b->order_key ("signal");
647     }
648 };
649
650 void
651 Mixer_UI::initial_track_display ()
652 {
653         boost::shared_ptr<Session::RouteList> routes = session->get_routes();
654         Session::RouteList copy (*routes);
655         SignalOrderRouteSorter sorter;
656
657         copy.sort (sorter);
658         
659         no_track_list_redisplay = true;
660
661         track_model->clear ();
662
663         add_strip (copy);
664
665         no_track_list_redisplay = false;
666
667         redisplay_track_list ();
668 }
669
670 void
671 Mixer_UI::show_track_list_menu ()
672 {
673         if (track_menu == 0) {
674                 build_track_menu ();
675         }
676
677         track_menu->popup (1, gtk_get_current_event_time());
678 }
679
680 bool
681 Mixer_UI::track_display_button_press (GdkEventButton* ev)
682 {
683         if (Keyboard::is_context_menu_event (ev)) {
684                 show_track_list_menu ();
685                 return true;
686         }
687
688         TreeIter iter;
689         TreeModel::Path path;
690         TreeViewColumn* column;
691         int cellx;
692         int celly;
693         
694         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
695                 return false;
696         }
697
698         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
699         case 0:
700                 /* allow normal processing to occur */
701                 return false;
702
703         case 1: /* visibility */
704
705                 if ((iter = track_model->get_iter (path))) {
706                         MixerStrip* strip = (*iter)[track_columns.strip];
707                         if (strip) {
708
709                                 if (!strip->route()->master() && !strip->route()->control()) {
710                                         bool visible = (*iter)[track_columns.visible];
711                                         (*iter)[track_columns.visible] = !visible;
712                                 }
713                         }
714                 }
715                 return true;
716
717         default:
718                 break;
719         }
720
721         return false;
722 }
723
724
725 void
726 Mixer_UI::build_track_menu ()
727 {
728         using namespace Menu_Helpers;
729         using namespace Gtk;
730
731         track_menu = new Menu;
732         track_menu->set_name ("ArdourContextMenu");
733         MenuList& items = track_menu->items();
734         
735         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
736         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
737         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
738         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
739         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
740         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
741
742 }
743
744 void
745 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
746 {
747         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
748         
749         TreeModel::Children rows = track_model->children();
750         TreeModel::Children::iterator i;
751         
752         for (i = rows.begin(); i != rows.end(); ++i) {
753                 if ((*i)[track_columns.strip] == mx) {
754                         (*i)[track_columns.text] = mx->route()->name();
755                         return;
756                 }
757         } 
758
759         error << _("track display list item for renamed strip not found!") << endmsg;
760 }
761
762
763 void
764 Mixer_UI::build_mix_group_context_menu ()
765 {
766         using namespace Gtk::Menu_Helpers;
767
768         mix_group_context_menu = new Menu;
769         mix_group_context_menu->set_name ("ArdourContextMenu");
770         MenuList& items = mix_group_context_menu->items();
771
772         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
773         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
774         items.push_back (SeparatorElem());
775         items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
776         
777 }
778
779 bool
780 Mixer_UI::group_display_button_press (GdkEventButton* ev)
781 {
782         if (Keyboard::is_context_menu_event (ev)) {
783                 if (mix_group_context_menu == 0) {
784                         build_mix_group_context_menu ();
785                 }
786                 mix_group_context_menu->popup (1, ev->time);
787                 return true;
788         }
789
790
791         RouteGroup* group;
792         TreeIter iter;
793         TreeModel::Path path;
794         TreeViewColumn* column;
795         int cellx;
796         int celly;
797
798         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
799                 return false;
800         }
801
802         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
803         case 0:
804                 if (Keyboard::is_edit_event (ev)) {
805                         if ((iter = group_model->get_iter (path))) {
806                                 if ((group = (*iter)[group_columns.group]) != 0) {
807                                         // edit_mix_group (group);
808                                         return true;
809                                 }
810                         }
811                         
812                 } 
813                 break;
814
815         case 1:
816                 if ((iter = group_model->get_iter (path))) {
817                         bool active = (*iter)[group_columns.active];
818                         (*iter)[group_columns.active] = !active;
819                         return true;
820                 }
821                 break;
822                 
823         case 2:
824                 if ((iter = group_model->get_iter (path))) {
825                         bool visible = (*iter)[group_columns.visible];
826                         (*iter)[group_columns.visible] = !visible;
827                         return true;
828                 }
829                 break;
830
831         default:
832                 break;
833         }
834         
835         return false;
836  }
837
838 void
839 Mixer_UI::activate_all_mix_groups ()
840 {
841         Gtk::TreeModel::Children children = group_model->children();
842         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
843                 (*iter)[group_columns.active] = true;
844         }
845 }
846
847 void
848 Mixer_UI::disable_all_mix_groups ()
849 {
850         Gtk::TreeModel::Children children = group_model->children();
851         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
852                 (*iter)[group_columns.active] = false;
853         }
854 }
855
856 void
857 Mixer_UI::mix_groups_changed ()
858 {
859         ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
860
861         /* just rebuild the while thing */
862
863         group_model->clear ();
864
865         {
866                 TreeModel::Row row;
867                 row = *(group_model->append());
868                 row[group_columns.active] = false;
869                 row[group_columns.visible] = true;
870                 row[group_columns.text] = (_("-all-"));
871                 row[group_columns.group] = 0;
872         }
873
874         session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
875 }
876
877 void
878 Mixer_UI::new_mix_group ()
879 {
880         session->add_mix_group ("");
881 }
882
883 void
884 Mixer_UI::remove_selected_mix_group ()
885 {
886         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
887         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
888
889         if (rows.empty()) {
890                 return;
891         }
892
893         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
894         TreeIter iter;
895         
896         /* selection mode is single, so rows.begin() is it */
897
898         if ((iter = group_model->get_iter (*i))) {
899
900                 RouteGroup* rg = (*iter)[group_columns.group];
901
902                 if (rg) {
903                         session->remove_mix_group (*rg);
904                 }
905         }
906 }
907
908 void
909 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
910 {
911         if (in_group_row_change) {
912                 return;
913         }
914
915         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
916
917         /* force an update of any mixer strips that are using this group,
918            otherwise mix group names don't change in mixer strips 
919         */
920
921         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
922                 if ((*i)->mix_group() == group) {
923                         (*i)->mix_group_changed(0);
924                 }
925         }
926         
927         TreeModel::iterator i;
928         TreeModel::Children rows = group_model->children();
929         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
930
931         in_group_row_change = true;
932         
933         for (i = rows.begin(); i != rows.end(); ++i) {
934                 if ((*i)[group_columns.group] == group) {
935                         (*i)[group_columns.visible] = !group->is_hidden ();
936                         (*i)[group_columns.active] = group->is_active ();
937                         (*i)[group_columns.text] = group->name ();
938                         break;
939                 }
940         }
941
942         in_group_row_change = false;
943 }
944
945 void
946 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
947 {
948         RouteGroup* group;
949         TreeIter iter;
950
951         if ((iter = group_model->get_iter (path))) {
952         
953                 if ((group = (*iter)[group_columns.group]) == 0) {
954                         return;
955                 }
956                 
957                 if (new_text != group->name()) {
958                         group->set_name (new_text);
959                 }
960         }
961 }
962
963 void 
964 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
965 {
966         RouteGroup* group;
967
968         if (in_group_row_change) {
969                 return;
970         }
971
972         if ((group = (*iter)[group_columns.group]) == 0) {
973                 return;
974         }
975
976         if ((*iter)[group_columns.visible]) {
977                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
978                         if ((*i)->mix_group() == group) {
979                                 show_strip (*i);
980                         }
981                 }
982         } else {
983                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
984                         if ((*i)->mix_group() == group) {
985                                 hide_strip (*i);
986                         }
987                 }
988         } 
989
990         bool active = (*iter)[group_columns.active];
991         group->set_active (active, this);
992
993         Glib::ustring name = (*iter)[group_columns.text];
994
995         if (name != group->name()) {
996                 group->set_name (name);
997         }
998 }
999
1000 void
1001 Mixer_UI::add_mix_group (RouteGroup* group)
1002
1003 {
1004         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1005         bool focus = false;
1006
1007         in_group_row_change = true;
1008
1009         TreeModel::Row row = *(group_model->append());
1010         row[group_columns.active] = group->is_active();
1011         row[group_columns.visible] = true;
1012         row[group_columns.group] = group;
1013         if (!group->name().empty()) {
1014                 row[group_columns.text] = group->name();
1015         } else {
1016                 row[group_columns.text] = _("unnamed");
1017                 focus = true;
1018         }
1019
1020         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1021         
1022         if (focus) {
1023                 TreeViewColumn* col = group_display.get_column (0);
1024                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1025                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1026         }
1027
1028         in_group_row_change = false;
1029 }
1030
1031 bool
1032 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1033 {
1034         using namespace Menu_Helpers;
1035
1036         if (Keyboard::is_context_menu_event (ev)) {
1037                 ARDOUR_UI::instance()->add_route (this);
1038                 return true;
1039         }
1040
1041         return false;
1042 }
1043
1044 void
1045 Mixer_UI::set_strip_width (Width w)
1046 {
1047         _strip_width = w;
1048
1049         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1050                 (*i)->set_width (w, this);
1051         }
1052 }
1053
1054 void
1055 Mixer_UI::set_window_pos_and_size ()
1056 {
1057         resize (m_width, m_height);
1058         move (m_root_x, m_root_y);
1059 }
1060
1061         void
1062 Mixer_UI::get_window_pos_and_size ()
1063 {
1064         get_position(m_root_x, m_root_y);
1065         get_size(m_width, m_height);
1066 }
1067
1068 int
1069 Mixer_UI::set_state (const XMLNode& node)
1070 {
1071         const XMLProperty* prop;
1072         XMLNode* geometry;
1073         
1074         if ((geometry = find_named_node (node, "geometry")) == 0) {
1075
1076                 m_width = default_width;
1077                 m_height = default_height;
1078                 m_root_x = 1;
1079                 m_root_y = 1;
1080
1081         } else {
1082
1083                 m_width = atoi(geometry->property("x_size")->value().c_str());
1084                 m_height = atoi(geometry->property("y_size")->value().c_str());
1085                 m_root_x = atoi(geometry->property("x_pos")->value().c_str());
1086                 m_root_y = atoi(geometry->property("y_pos")->value().c_str());
1087         }
1088
1089         set_window_pos_and_size ();
1090
1091         if ((prop = node.property ("narrow-strips"))) {
1092                 if (prop->value() == "yes") {
1093                         set_strip_width (Narrow);
1094                 } else {
1095                         set_strip_width (Wide);
1096                 }
1097         }
1098
1099         if ((prop = node.property ("show-mixer"))) {
1100                 if (prop->value() == "yes") {
1101                        _visible = true;
1102                 }
1103         }
1104
1105         return 0;
1106 }
1107
1108 XMLNode&
1109 Mixer_UI::get_state (void)
1110 {
1111         XMLNode* node = new XMLNode ("Mixer");
1112
1113         if (is_realized()) {
1114                 Glib::RefPtr<Gdk::Window> win = get_window();
1115         
1116                 get_window_pos_and_size ();
1117
1118                 XMLNode* geometry = new XMLNode ("geometry");
1119                 char buf[32];
1120                 snprintf(buf, sizeof(buf), "%d", m_width);
1121                 geometry->add_property(X_("x_size"), string(buf));
1122                 snprintf(buf, sizeof(buf), "%d", m_height);
1123                 geometry->add_property(X_("y_size"), string(buf));
1124                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1125                 geometry->add_property(X_("x_pos"), string(buf));
1126                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1127                 geometry->add_property(X_("y_pos"), string(buf));
1128                 
1129                 // written only for compatibility, they are not used.
1130                 snprintf(buf, sizeof(buf), "%d", 0);
1131                 geometry->add_property(X_("x_off"), string(buf));
1132                 snprintf(buf, sizeof(buf), "%d", 0);
1133                 geometry->add_property(X_("y_off"), string(buf));
1134
1135                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1136                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1137                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1138                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1139
1140                 node->add_child_nocopy (*geometry);
1141         }
1142
1143         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1144
1145         node->add_property ("show-mixer", _visible ? "yes" : "no");
1146
1147         return *node;
1148 }
1149
1150
1151 void 
1152 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1153 {
1154         int pos;
1155         XMLProperty* prop = 0;
1156         char buf[32];
1157         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1158         XMLNode* geometry;
1159         int width, height;
1160         static int32_t done[3] = { 0, 0, 0 };
1161
1162         if ((geometry = find_named_node (*node, "geometry")) == 0) {
1163                 width = default_width;
1164                 height = default_height;
1165         } else {
1166                 width = atoi(geometry->property("x_size")->value());
1167                 height = atoi(geometry->property("y_size")->value());
1168         }
1169
1170         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1171
1172                 if (done[0]) {
1173                         return;
1174                 }
1175
1176                 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1177                         pos = height / 3;
1178                         snprintf (buf, sizeof(buf), "%d", pos);
1179                 } else {
1180                         pos = atoi (prop->value());
1181                 }
1182
1183                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1184                         rhs_pane1.set_position (pos);
1185                 }
1186
1187         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1188
1189                 if (done[2]) {
1190                         return;
1191                 }
1192
1193                 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1194                         pos = 75;
1195                         snprintf (buf, sizeof(buf), "%d", pos);
1196                 } else {
1197                         pos = atoi (prop->value());
1198                 }
1199
1200                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1201                         list_hpane.set_position (pos);
1202                 }
1203         }
1204 }
1205
1206 bool
1207 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1208 {
1209         return key_press_focus_accelerator_handler (*this, ev);
1210 }