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