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