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