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