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