Brought back visual indication of delete dragging.
[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         mix_group_display_button_box->show();
224         mix_group_add_button->show();
225         mix_group_remove_button->show();
226
227         global_hpacker.show();
228         global_vpacker.show();
229         scroller.show();
230         scroller_base.show();
231         scroller_hpacker.show();
232         mixer_scroller_vpacker.show();
233         list_vpacker.show();
234         group_display_button_label.show();
235         group_display_button.show();
236         track_display_scroller.show();
237         group_display_scroller.show();
238         group_display_vbox.show();
239         track_display_frame.show();
240         group_display_frame.show();
241         rhs_pane1.show();
242         strip_packer.show();
243         out_packer.show();
244         list_hpane.show();
245         track_display.show();
246         group_display.show();
247 }
248
249 Mixer_UI::~Mixer_UI ()
250 {
251 }
252
253 void
254 Mixer_UI::ensure_float (Window& win)
255 {
256         win.set_transient_for (*this);
257 }
258
259 void
260 Mixer_UI::show_window ()
261 {
262         present ();
263
264         set_window_pos_and_size ();
265
266         /* now reset each strips width so the right widgets are shown */
267         MixerStrip* ms;
268
269         TreeModel::Children rows = track_model->children();
270         TreeModel::Children::iterator ri;
271
272         for (ri = rows.begin(); ri != rows.end(); ++ri) {
273                 ms = (*ri)[track_columns.strip];
274                 ms->set_width (ms->get_width(), ms->width_owner());
275         }
276         _visible = true;
277 }
278
279 bool
280 Mixer_UI::hide_window (GdkEventAny *ev)
281 {
282         get_window_pos_and_size ();
283
284         _visible = false;
285         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
286 }
287
288
289 void
290 Mixer_UI::add_strip (Session::RouteList& routes)
291 {
292         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), routes));
293         
294         MixerStrip* strip;
295
296         for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
297                 boost::shared_ptr<Route> route = (*x);
298
299                 if (route->is_hidden()) {
300                         return;
301                 }
302                 
303                 strip = new MixerStrip (*this, *session, route);
304                 strips.push_back (strip);
305                 
306                 if (strip->width_owner() != strip) {
307                         strip->set_width (_strip_width, this);
308                 }
309
310                 show_strip (strip);
311                 
312                 no_track_list_redisplay = true;
313                 
314                 TreeModel::Row row = *(track_model->append());
315                 row[track_columns.text] = route->name();
316                 
317                 row[track_columns.visible] = strip->marked_for_display();
318                 row[track_columns.route] = route;
319                 row[track_columns.strip] = strip;
320                 
321                 no_track_list_redisplay = false;
322                 redisplay_track_list ();
323                 
324                 route->NameChanged.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
325                 strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
326                 
327                 strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
328         }
329 }
330
331 void
332 Mixer_UI::remove_strip (MixerStrip* strip)
333 {
334         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
335         
336         TreeModel::Children rows = track_model->children();
337         TreeModel::Children::iterator ri;
338         list<MixerStrip *>::iterator i;
339
340         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
341                 strips.erase (i);
342         }
343
344         for (ri = rows.begin(); ri != rows.end(); ++ri) {
345                 if ((*ri)[track_columns.strip] == strip) {
346                         track_model->erase (ri);
347                         break;
348                 }
349         }
350 }
351
352 void
353 Mixer_UI::follow_strip_selection ()
354 {
355         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
356                 (*i)->set_selected (_selection.selected ((*i)->route()));
357         }
358 }
359
360 bool
361 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
362 {
363         if (ev->button == 1) {
364
365                 /* this allows the user to click on the strip to terminate comment
366                    editing. XXX it needs improving so that we don't select the strip
367                    at the same time.
368                 */
369                 
370                 if (_selection.selected (strip->route())) {
371                         _selection.remove (strip->route());
372                 } else {
373                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
374                                 _selection.add (strip->route());
375                         } else {
376                                 _selection.set (strip->route());
377                         }
378                 }
379         }
380
381         return true;
382 }
383
384 void
385 Mixer_UI::connect_to_session (Session* sess)
386 {
387
388         session = sess;
389
390         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
391         set_state (*node);
392
393         WindowTitle title(session->name());
394         title += _("Mixer");
395         title += Glib::get_application_name();
396
397         set_title (title.get_string());
398
399         initial_track_display ();
400
401         session->GoingAway.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
402         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
403         session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
404         session->mix_group_removed.connect (mem_fun(*this, &Mixer_UI::mix_groups_changed));
405
406         mix_groups_changed ();
407         
408         _plugin_selector->set_session (session);
409
410         if (_visible) {
411                show_window();
412         }
413
414         start_updating ();
415 }
416
417 void
418 Mixer_UI::disconnect_from_session ()
419 {
420         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
421         
422         group_model->clear ();
423         _selection.clear ();
424
425         WindowTitle title(Glib::get_application_name());
426         title += _("Mixer");
427         set_title (title.get_string());
428         
429         stop_updating ();
430 }
431
432 void
433 Mixer_UI::show_strip (MixerStrip* ms)
434 {
435         TreeModel::Children rows = track_model->children();
436         TreeModel::Children::iterator i;
437         
438         for (i = rows.begin(); i != rows.end(); ++i) {
439         
440                 MixerStrip* strip = (*i)[track_columns.strip];
441                 if (strip == ms) {
442                         (*i)[track_columns.visible] = true;
443                         break;
444                 }
445         }
446 }
447
448 void
449 Mixer_UI::hide_strip (MixerStrip* ms)
450 {
451         TreeModel::Children rows = track_model->children();
452         TreeModel::Children::iterator i;
453         
454         for (i = rows.begin(); i != rows.end(); ++i) {
455                 
456                 MixerStrip* strip = (*i)[track_columns.strip];
457                 if (strip == ms) {
458                         (*i)[track_columns.visible] = false;
459                         break;
460                 }
461         }
462 }
463
464 gint
465 Mixer_UI::start_updating ()
466 {
467     fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
468     return 0;
469 }
470
471 gint
472 Mixer_UI::stop_updating ()
473 {
474     fast_screen_update_connection.disconnect();
475     return 0;
476 }
477
478 void
479 Mixer_UI::fast_update_strips ()
480 {
481         if (is_mapped () && session) {
482                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
483                         (*i)->fast_update ();
484                 }
485         }
486 }
487
488 void
489 Mixer_UI::set_all_strips_visibility (bool yn)
490 {
491         TreeModel::Children rows = track_model->children();
492         TreeModel::Children::iterator i;
493
494         no_track_list_redisplay = true;
495
496         for (i = rows.begin(); i != rows.end(); ++i) {
497
498                 TreeModel::Row row = (*i);
499                 MixerStrip* strip = row[track_columns.strip];
500                 
501                 if (strip == 0) {
502                         continue;
503                 }
504                 
505                 if (strip->route()->is_master() || strip->route()->is_control()) {
506                         continue;
507                 }
508
509                 (*i)[track_columns.visible] = yn;
510         }
511
512         no_track_list_redisplay = false;
513         redisplay_track_list ();
514 }
515
516
517 void
518 Mixer_UI::set_all_audio_visibility (int tracks, bool yn) 
519 {
520         TreeModel::Children rows = track_model->children();
521         TreeModel::Children::iterator i;
522
523         no_track_list_redisplay = true;
524
525         for (i = rows.begin(); i != rows.end(); ++i) {
526                 TreeModel::Row row = (*i);
527                 MixerStrip* strip = row[track_columns.strip];
528
529                 if (strip == 0) {
530                         continue;
531                 }
532
533                 if (strip->route()->is_master() || strip->route()->is_control()) {
534                         continue;
535                 }
536
537                 boost::shared_ptr<AudioTrack> at = strip->audio_track();
538
539                 switch (tracks) {
540                 case 0:
541                         (*i)[track_columns.visible] = yn;
542                         break;
543                         
544                 case 1:
545                         if (at) { /* track */
546                                 (*i)[track_columns.visible] = yn;
547                         }
548                         break;
549                         
550                 case 2:
551                         if (!at) { /* bus */
552                                 (*i)[track_columns.visible] = yn;
553                         }
554                         break;
555                 }
556         }
557
558         no_track_list_redisplay = false;
559         redisplay_track_list ();
560 }
561
562 void
563 Mixer_UI::hide_all_routes ()
564 {
565         set_all_strips_visibility (false);
566 }
567
568 void
569 Mixer_UI::show_all_routes ()
570 {
571         set_all_strips_visibility (true);
572 }
573
574 void
575 Mixer_UI::show_all_audiobus ()
576 {
577         set_all_audio_visibility (2, true);
578 }
579 void
580 Mixer_UI::hide_all_audiobus ()
581 {
582         set_all_audio_visibility (2, false);
583 }
584
585 void
586 Mixer_UI::show_all_audiotracks()
587 {
588         set_all_audio_visibility (1, true);
589 }
590 void
591 Mixer_UI::hide_all_audiotracks ()
592 {
593         set_all_audio_visibility (1, false);
594 }
595
596 void
597 Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
598 {
599         session->set_remote_control_ids();
600         redisplay_track_list ();
601 }
602
603 void
604 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path& path)
605 {
606         session->set_remote_control_ids();
607         redisplay_track_list ();
608 }
609
610 void
611 Mixer_UI::redisplay_track_list ()
612 {
613         TreeModel::Children rows = track_model->children();
614         TreeModel::Children::iterator i;
615         long order;
616
617         if (no_track_list_redisplay) {
618                 return;
619         }
620
621         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
622                 MixerStrip* strip = (*i)[track_columns.strip];
623
624                 if (strip == 0) {
625                         /* we're in the middle of changing a row, don't worry */
626                         continue;
627                 }
628
629                 bool visible = (*i)[track_columns.visible];
630
631                 if (visible) {
632                         strip->set_marked_for_display (true);
633                         strip->route()->set_order_key (N_("signal"), order);
634
635                         if (strip->packed()) {
636
637                                 if (strip->route()->is_master() || strip->route()->is_control()) {
638                                         out_packer.reorder_child (*strip, -1);
639                                 } else {
640                                         strip_packer.reorder_child (*strip, -1); /* put at end */
641                                 }
642
643                         } else {
644
645                                 if (strip->route()->is_master() || strip->route()->is_control()) {
646                                         out_packer.pack_start (*strip, false, false);
647                                 } else {
648                                         strip_packer.pack_start (*strip, false, false);
649                                 }
650                                 strip->set_packed (true);
651                                 //strip->show();
652                         }
653
654                 } else {
655
656                         if (strip->route()->is_master() || strip->route()->is_control()) {
657                                 /* do nothing, these cannot be hidden */
658                         } else {
659                                 if (strip->packed()) {
660                                         strip_packer.remove (*strip);
661                                         strip->set_packed (false);
662                                 }
663                         }
664                 }
665         }
666 }
667
668 struct SignalOrderRouteSorter {
669     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
670             /* use of ">" forces the correct sort order */
671             return a->order_key ("signal") < b->order_key ("signal");
672     }
673 };
674
675 void
676 Mixer_UI::initial_track_display ()
677 {
678         boost::shared_ptr<Session::RouteList> routes = session->get_routes();
679         Session::RouteList copy (*routes);
680         SignalOrderRouteSorter sorter;
681
682         copy.sort (sorter);
683         
684         no_track_list_redisplay = true;
685
686         track_model->clear ();
687
688         add_strip (copy);
689
690         no_track_list_redisplay = false;
691
692         redisplay_track_list ();
693 }
694
695 void
696 Mixer_UI::show_track_list_menu ()
697 {
698         if (track_menu == 0) {
699                 build_track_menu ();
700         }
701
702         track_menu->popup (1, gtk_get_current_event_time());
703 }
704
705 bool
706 Mixer_UI::track_display_button_press (GdkEventButton* ev)
707 {
708         if (Keyboard::is_context_menu_event (ev)) {
709                 show_track_list_menu ();
710                 return true;
711         }
712
713         TreeIter iter;
714         TreeModel::Path path;
715         TreeViewColumn* column;
716         int cellx;
717         int celly;
718         
719         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
720                 return false;
721         }
722
723         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
724         case 0:
725                 /* allow normal processing to occur */
726                 return false;
727
728         case 1: /* visibility */
729
730                 if ((iter = track_model->get_iter (path))) {
731                         MixerStrip* strip = (*iter)[track_columns.strip];
732                         if (strip) {
733
734                                 if (!strip->route()->is_master() && !strip->route()->is_control()) {
735                                         bool visible = (*iter)[track_columns.visible];
736                                         (*iter)[track_columns.visible] = !visible;
737                                 }
738                         }
739                 }
740                 return true;
741
742         default:
743                 break;
744         }
745
746         return false;
747 }
748
749
750 void
751 Mixer_UI::build_track_menu ()
752 {
753         using namespace Menu_Helpers;
754         using namespace Gtk;
755
756         track_menu = new Menu;
757         track_menu->set_name ("ArdourContextMenu");
758         MenuList& items = track_menu->items();
759         
760         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
761         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
762         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
763         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
764         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
765         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
766
767 }
768
769 void
770 Mixer_UI::strip_name_changed (MixerStrip* mx)
771 {
772         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), mx));
773         
774         TreeModel::Children rows = track_model->children();
775         TreeModel::Children::iterator i;
776         
777         for (i = rows.begin(); i != rows.end(); ++i) {
778                 if ((*i)[track_columns.strip] == mx) {
779                         (*i)[track_columns.text] = mx->route()->name();
780                         return;
781                 }
782         } 
783
784         error << _("track display list item for renamed strip not found!") << endmsg;
785 }
786
787
788 void
789 Mixer_UI::build_mix_group_context_menu ()
790 {
791         using namespace Gtk::Menu_Helpers;
792
793         mix_group_context_menu = new Menu;
794         mix_group_context_menu->set_name ("ArdourContextMenu");
795         MenuList& items = mix_group_context_menu->items();
796
797         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
798         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
799         items.push_back (SeparatorElem());
800         items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
801         
802 }
803
804 bool
805 Mixer_UI::group_display_button_press (GdkEventButton* ev)
806 {
807         if (Keyboard::is_context_menu_event (ev)) {
808                 if (mix_group_context_menu == 0) {
809                         build_mix_group_context_menu ();
810                 }
811                 mix_group_context_menu->popup (1, ev->time);
812                 return true;
813         }
814
815
816         RouteGroup* group;
817         TreeIter iter;
818         TreeModel::Path path;
819         TreeViewColumn* column;
820         int cellx;
821         int celly;
822
823         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
824                 return false;
825         }
826
827         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
828         case 0:
829                 if (Keyboard::is_edit_event (ev)) {
830                         if ((iter = group_model->get_iter (path))) {
831                                 if ((group = (*iter)[group_columns.group]) != 0) {
832                                         // edit_mix_group (group);
833                                         return true;
834                                 }
835                         }
836                         
837                 } 
838                 break;
839
840         case 1:
841                 if ((iter = group_model->get_iter (path))) {
842                         bool active = (*iter)[group_columns.active];
843                         (*iter)[group_columns.active] = !active;
844                         return true;
845                 }
846                 break;
847                 
848         case 2:
849                 if ((iter = group_model->get_iter (path))) {
850                         bool visible = (*iter)[group_columns.visible];
851                         (*iter)[group_columns.visible] = !visible;
852                         return true;
853                 }
854                 break;
855
856         default:
857                 break;
858         }
859         
860         return false;
861  }
862
863 void
864 Mixer_UI::activate_all_mix_groups ()
865 {
866         Gtk::TreeModel::Children children = group_model->children();
867         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
868                 (*iter)[group_columns.active] = true;
869         }
870 }
871
872 void
873 Mixer_UI::disable_all_mix_groups ()
874 {
875         Gtk::TreeModel::Children children = group_model->children();
876         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
877                 (*iter)[group_columns.active] = false;
878         }
879 }
880
881 void
882 Mixer_UI::mix_groups_changed ()
883 {
884         ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
885
886         /* just rebuild the while thing */
887
888         group_model->clear ();
889
890         {
891                 TreeModel::Row row;
892                 row = *(group_model->append());
893                 row[group_columns.active] = false;
894                 row[group_columns.visible] = true;
895                 row[group_columns.text] = (_("-all-"));
896                 row[group_columns.group] = 0;
897         }
898
899         session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
900 }
901
902 void
903 Mixer_UI::new_mix_group ()
904 {
905         session->add_mix_group ("");
906 }
907
908 void
909 Mixer_UI::remove_selected_mix_group ()
910 {
911         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
912         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
913
914         if (rows.empty()) {
915                 return;
916         }
917
918         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
919         TreeIter iter;
920         
921         /* selection mode is single, so rows.begin() is it */
922
923         if ((iter = group_model->get_iter (*i))) {
924
925                 RouteGroup* rg = (*iter)[group_columns.group];
926
927                 if (rg) {
928                         session->remove_mix_group (*rg);
929                 }
930         }
931 }
932
933 void
934 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
935 {
936         if (in_group_row_change) {
937                 return;
938         }
939
940         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
941
942         /* force an update of any mixer strips that are using this group,
943            otherwise mix group names don't change in mixer strips 
944         */
945
946         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
947                 if ((*i)->mix_group() == group) {
948                         (*i)->mix_group_changed(0);
949                 }
950         }
951         
952         TreeModel::iterator i;
953         TreeModel::Children rows = group_model->children();
954         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
955
956         in_group_row_change = true;
957         
958         for (i = rows.begin(); i != rows.end(); ++i) {
959                 if ((*i)[group_columns.group] == group) {
960                         (*i)[group_columns.visible] = !group->is_hidden ();
961                         (*i)[group_columns.active] = group->is_active ();
962                         (*i)[group_columns.text] = group->name ();
963                         break;
964                 }
965         }
966
967         in_group_row_change = false;
968 }
969
970 void
971 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
972 {
973         RouteGroup* group;
974         TreeIter iter;
975
976         if ((iter = group_model->get_iter (path))) {
977         
978                 if ((group = (*iter)[group_columns.group]) == 0) {
979                         return;
980                 }
981                 
982                 if (new_text != group->name()) {
983                         group->set_name (new_text);
984                 }
985         }
986 }
987
988 void 
989 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
990 {
991         RouteGroup* group;
992
993         if (in_group_row_change) {
994                 return;
995         }
996
997         if ((group = (*iter)[group_columns.group]) == 0) {
998                 return;
999         }
1000
1001         if ((*iter)[group_columns.visible]) {
1002                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1003                         if ((*i)->mix_group() == group) {
1004                                 show_strip (*i);
1005                         }
1006                 }
1007         } else {
1008                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1009                         if ((*i)->mix_group() == group) {
1010                                 hide_strip (*i);
1011                         }
1012                 }
1013         } 
1014
1015         bool active = (*iter)[group_columns.active];
1016         group->set_active (active, this);
1017
1018         Glib::ustring name = (*iter)[group_columns.text];
1019
1020         if (name != group->name()) {
1021                 group->set_name (name);
1022         }
1023 }
1024
1025 void
1026 Mixer_UI::add_mix_group (RouteGroup* group)
1027
1028 {
1029         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1030         bool focus = false;
1031
1032         in_group_row_change = true;
1033
1034         TreeModel::Row row = *(group_model->append());
1035         row[group_columns.active] = group->is_active();
1036         row[group_columns.visible] = true;
1037         row[group_columns.group] = group;
1038         if (!group->name().empty()) {
1039                 row[group_columns.text] = group->name();
1040         } else {
1041                 row[group_columns.text] = _("unnamed");
1042                 focus = true;
1043         }
1044
1045         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1046         
1047         if (focus) {
1048                 TreeViewColumn* col = group_display.get_column (0);
1049                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1050                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1051         }
1052
1053         in_group_row_change = false;
1054 }
1055
1056 bool
1057 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1058 {
1059         using namespace Menu_Helpers;
1060
1061         if (Keyboard::is_context_menu_event (ev)) {
1062                 ARDOUR_UI::instance()->add_route (this);
1063                 return true;
1064         }
1065
1066         return false;
1067 }
1068
1069 void
1070 Mixer_UI::set_strip_width (Width w)
1071 {
1072         _strip_width = w;
1073
1074         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1075                 (*i)->set_width (w, this);
1076         }
1077 }
1078
1079 void
1080 Mixer_UI::set_window_pos_and_size ()
1081 {
1082         resize (m_width, m_height);
1083         move (m_root_x, m_root_y);
1084 }
1085
1086         void
1087 Mixer_UI::get_window_pos_and_size ()
1088 {
1089         get_position(m_root_x, m_root_y);
1090         get_size(m_width, m_height);
1091 }
1092
1093 int
1094 Mixer_UI::set_state (const XMLNode& node)
1095 {
1096         const XMLProperty* prop;
1097         XMLNode* geometry;
1098         
1099         if ((geometry = find_named_node (node, "geometry")) == 0) {
1100
1101                 m_width = default_width;
1102                 m_height = default_height;
1103                 m_root_x = 1;
1104                 m_root_y = 1;
1105
1106         } else {
1107
1108                 m_width = atoi(geometry->property("x_size")->value().c_str());
1109                 m_height = atoi(geometry->property("y_size")->value().c_str());
1110                 m_root_x = atoi(geometry->property("x_pos")->value().c_str());
1111                 m_root_y = atoi(geometry->property("y_pos")->value().c_str());
1112         }
1113
1114         set_window_pos_and_size ();
1115
1116         if ((prop = node.property ("narrow-strips"))) {
1117                 if (prop->value() == "yes") {
1118                         set_strip_width (Narrow);
1119                 } else {
1120                         set_strip_width (Wide);
1121                 }
1122         }
1123
1124         if ((prop = node.property ("show-mixer"))) {
1125                 if (prop->value() == "yes") {
1126                        _visible = true;
1127                 }
1128         }
1129
1130         return 0;
1131 }
1132
1133 XMLNode&
1134 Mixer_UI::get_state (void)
1135 {
1136         XMLNode* node = new XMLNode ("Mixer");
1137
1138         if (is_realized()) {
1139                 Glib::RefPtr<Gdk::Window> win = get_window();
1140         
1141                 get_window_pos_and_size ();
1142
1143                 XMLNode* geometry = new XMLNode ("geometry");
1144                 char buf[32];
1145                 snprintf(buf, sizeof(buf), "%d", m_width);
1146                 geometry->add_property(X_("x_size"), string(buf));
1147                 snprintf(buf, sizeof(buf), "%d", m_height);
1148                 geometry->add_property(X_("y_size"), string(buf));
1149                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1150                 geometry->add_property(X_("x_pos"), string(buf));
1151                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1152                 geometry->add_property(X_("y_pos"), string(buf));
1153                 
1154                 // written only for compatibility, they are not used.
1155                 snprintf(buf, sizeof(buf), "%d", 0);
1156                 geometry->add_property(X_("x_off"), string(buf));
1157                 snprintf(buf, sizeof(buf), "%d", 0);
1158                 geometry->add_property(X_("y_off"), string(buf));
1159
1160                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1161                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1162                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1163                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1164
1165                 node->add_child_nocopy (*geometry);
1166         }
1167
1168         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1169
1170         node->add_property ("show-mixer", _visible ? "yes" : "no");
1171
1172         return *node;
1173 }
1174
1175
1176 void 
1177 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1178 {
1179         int pos;
1180         XMLProperty* prop = 0;
1181         char buf[32];
1182         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1183         XMLNode* geometry;
1184         int width, height;
1185         static int32_t done[3] = { 0, 0, 0 };
1186
1187         if ((geometry = find_named_node (*node, "geometry")) == 0) {
1188                 width = default_width;
1189                 height = default_height;
1190         } else {
1191                 width = atoi(geometry->property("x_size")->value());
1192                 height = atoi(geometry->property("y_size")->value());
1193         }
1194
1195         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1196
1197                 if (done[0]) {
1198                         return;
1199                 }
1200
1201                 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1202                         pos = height / 3;
1203                         snprintf (buf, sizeof(buf), "%d", pos);
1204                 } else {
1205                         pos = atoi (prop->value());
1206                 }
1207
1208                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1209                         rhs_pane1.set_position (pos);
1210                 }
1211
1212         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1213
1214                 if (done[2]) {
1215                         return;
1216                 }
1217
1218                 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1219                         pos = 75;
1220                         snprintf (buf, sizeof(buf), "%d", pos);
1221                 } else {
1222                         pos = atoi (prop->value());
1223                 }
1224
1225                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1226                         list_hpane.set_position (pos);
1227                 }
1228         }
1229 }
1230
1231 bool
1232 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1233 {
1234         return key_press_focus_accelerator_handler (*this, ev);
1235 }