start editing new edit/mix group names immediately after they are added
[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_display.set_model (Glib::RefPtr<TreeModel>(0));
832         group_model->clear ();
833
834         {
835                 TreeModel::Row row;
836                 row = *(group_model->append());
837                 row[group_columns.active] = false;
838                 row[group_columns.visible] = true;
839                 row[group_columns.text] = (_("-all-"));
840                 row[group_columns.group] = 0;
841         }
842
843         session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
844         group_display.set_model (group_model);
845 }
846
847
848 void
849 Mixer_UI::new_mix_group ()
850 {
851 #if 0
852         ArdourPrompter prompter;
853         string result;
854
855         prompter.set_prompt (_("Name for new mix group"));
856         prompter.show_all ();
857         
858         switch (prompter.run ()) {
859         case Gtk::RESPONSE_ACCEPT:
860                 prompter.get_result (result);
861                 if (result.length()) {
862                         session->add_mix_group (result);
863                 }       
864                 break;
865         }
866 #else 
867         session->add_mix_group ("");
868 #endif
869
870 }
871
872 void
873 Mixer_UI::remove_selected_mix_group ()
874 {
875         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
876         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
877
878         if (rows.empty()) {
879                 return;
880         }
881
882         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
883         TreeIter iter;
884         
885         /* selection mode is single, so rows.begin() is it */
886
887         if ((iter = group_model->get_iter (*i))) {
888
889                 RouteGroup* rg = (*iter)[group_columns.group];
890
891                 if (rg) {
892                         session->remove_mix_group (*rg);
893                 }
894         }
895 }
896
897 void
898 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
899 {
900         if (in_group_row_change) {
901                 return;
902         }
903
904         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
905         
906         TreeModel::iterator i;
907         TreeModel::Children rows = group_model->children();
908         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
909
910         in_group_row_change = true;
911         
912         for (i = rows.begin(); i != rows.end(); ++i) {
913                 if ((*i)[group_columns.group] == group) {
914                         (*i)[group_columns.visible] = !group->is_hidden ();
915                         (*i)[group_columns.active] = group->is_active ();
916                         (*i)[group_columns.text] = group->name ();
917                         break;
918                 }
919         }
920
921         in_group_row_change = false;
922 }
923
924 void
925 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
926 {
927         RouteGroup* group;
928         TreeIter iter;
929
930         if ((iter = group_model->get_iter (path))) {
931         
932                 if ((group = (*iter)[group_columns.group]) == 0) {
933                         return;
934                 }
935                 
936                 if (new_text != group->name()) {
937                         group->set_name (new_text);
938                 }
939         }
940 }
941
942 void 
943 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
944 {
945         RouteGroup* group;
946
947         if (in_group_row_change) {
948                 return;
949         }
950
951         if ((group = (*iter)[group_columns.group]) == 0) {
952                 return;
953         }
954
955         if ((*iter)[group_columns.visible]) {
956                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
957                         if ((*i)->mix_group() == group) {
958                                 show_strip (*i);
959                         }
960                 }
961         } else {
962                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
963                         if ((*i)->mix_group() == group) {
964                                 hide_strip (*i);
965                         }
966                 }
967         } 
968
969         bool active = (*iter)[group_columns.active];
970         group->set_active (active, this);
971
972         Glib::ustring name = (*iter)[group_columns.text];
973
974         if (name != group->name()) {
975                 group->set_name (name);
976         }
977 }
978
979 void
980 Mixer_UI::add_mix_group (RouteGroup* group)
981
982 {
983         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
984         bool focus = false;
985
986         TreeModel::Row row = *(group_model->append());
987         row[group_columns.active] = group->is_active();
988         row[group_columns.visible] = true;
989         row[group_columns.group] = group;
990         if (!group->name().empty()) {
991                 row[group_columns.text] = group->name();
992         } else {
993                 row[group_columns.text] = _("unnamed");
994                 focus = true;
995         }
996
997         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
998         
999         if (focus) {
1000                 TreeViewColumn* col = group_display.get_column (0);
1001                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1002                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1003         }
1004 }
1005
1006 bool
1007 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1008 {
1009         using namespace Menu_Helpers;
1010
1011         if (Keyboard::is_context_menu_event (ev)) {
1012                 ARDOUR_UI::instance()->add_route();
1013                 return true;
1014         }
1015
1016         return false;
1017 }
1018
1019 void
1020 Mixer_UI::set_strip_width (Width w)
1021 {
1022         _strip_width = w;
1023
1024         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1025                 (*i)->set_width (w);
1026         }
1027 }
1028
1029
1030 int
1031 Mixer_UI::set_state (const XMLNode& node)
1032 {
1033         const XMLProperty* prop;
1034         XMLNode* geometry;
1035         Gdk::Geometry g;
1036         int x, y, xoff, yoff;
1037         
1038         if ((geometry = find_named_node (node, "geometry")) == 0) {
1039
1040                 g.base_width = default_width;
1041                 g.base_height = default_height;
1042                 x = 1;
1043                 y = 1;
1044                 xoff = 0;
1045                 yoff = 21;
1046
1047         } else {
1048
1049                 g.base_width = atoi(geometry->property("x_size")->value().c_str());
1050                 g.base_height = atoi(geometry->property("y_size")->value().c_str());
1051                 x = atoi(geometry->property("x_pos")->value().c_str());
1052                 y = atoi(geometry->property("y_pos")->value().c_str());
1053                 xoff = atoi(geometry->property("x_off")->value().c_str());
1054                 yoff = atoi(geometry->property("y_off")->value().c_str());
1055         }
1056
1057         set_geometry_hints (global_vpacker, g, Gdk::HINT_BASE_SIZE);
1058         set_default_size(g.base_width, g.base_height);
1059         move (x, y);
1060
1061         if ((prop = node.property ("narrow-strips"))) {
1062                 if (prop->value() == "yes") {
1063                         set_strip_width (Narrow);
1064                 } else {
1065                         set_strip_width (Wide);
1066                 }
1067         }
1068
1069         return 0;
1070 }
1071
1072 XMLNode&
1073 Mixer_UI::get_state (void)
1074 {
1075         XMLNode* node = new XMLNode ("Mixer");
1076
1077         if (is_realized()) {
1078                 Glib::RefPtr<Gdk::Window> win = get_window();
1079                 
1080                 int x, y, xoff, yoff, width, height;
1081                 win->get_root_origin(x, y);
1082                 win->get_position(xoff, yoff);
1083                 win->get_size(width, height);
1084
1085                 XMLNode* geometry = new XMLNode ("geometry");
1086                 char buf[32];
1087                 snprintf(buf, sizeof(buf), "%d", width);
1088                 geometry->add_property(X_("x_size"), string(buf));
1089                 snprintf(buf, sizeof(buf), "%d", height);
1090                 geometry->add_property(X_("y_size"), string(buf));
1091                 snprintf(buf, sizeof(buf), "%d", x);
1092                 geometry->add_property(X_("x_pos"), string(buf));
1093                 snprintf(buf, sizeof(buf), "%d", y);
1094                 geometry->add_property(X_("y_pos"), string(buf));
1095                 snprintf(buf, sizeof(buf), "%d", xoff);
1096                 geometry->add_property(X_("x_off"), string(buf));
1097                 snprintf(buf, sizeof(buf), "%d", yoff);
1098                 geometry->add_property(X_("y_off"), string(buf));
1099
1100                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1101                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1102                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1103                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1104
1105                 node->add_child_nocopy (*geometry);
1106         }
1107
1108         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1109
1110         return *node;
1111 }
1112
1113
1114 void 
1115 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1116 {
1117         int pos;
1118         XMLProperty* prop = 0;
1119         char buf[32];
1120         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1121         XMLNode* geometry;
1122         int width, height;
1123         static int32_t done[3] = { 0, 0, 0 };
1124
1125         if ((geometry = find_named_node (*node, "geometry")) == 0) {
1126                 width = default_width;
1127                 height = default_height;
1128         } else {
1129                 width = atoi(geometry->property("x_size")->value());
1130                 height = atoi(geometry->property("y_size")->value());
1131         }
1132
1133         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1134
1135                 if (done[0]) {
1136                         return;
1137                 }
1138
1139                 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1140                         pos = height / 3;
1141                         snprintf (buf, sizeof(buf), "%d", pos);
1142                 } else {
1143                         pos = atoi (prop->value());
1144                 }
1145
1146                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1147                         rhs_pane1.set_position (pos);
1148                 }
1149
1150         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1151
1152                 if (done[2]) {
1153                         return;
1154                 }
1155
1156                 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1157                         pos = 75;
1158                         snprintf (buf, sizeof(buf), "%d", pos);
1159                 } else {
1160                         pos = atoi (prop->value());
1161                 }
1162
1163                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1164                         list_hpane.set_position (pos);
1165                 }
1166         }
1167 }
1168
1169 bool
1170 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1171 {
1172         return key_press_focus_accelerator_handler (*this, ev);
1173 }