c9423f0490ab927ff3f3a690a4fcbfa7f2bbce04
[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/session_route.h>
34 #include <ardour/diskstream.h>
35 #include <ardour/plugin_manager.h>
36
37 #include "mixer_ui.h"
38 #include "mixer_strip.h"
39 #include "plugin_selector.h"
40 #include "ardour_ui.h"
41 #include "prompter.h"
42 #include "utils.h"
43 #include "actions.h"
44 #include "gui_thread.h"
45
46 #include "i18n.h"
47
48 using namespace ARDOUR;
49 using namespace Gtk;
50 using namespace Glib;
51 using namespace Gtkmm2ext;
52 using namespace sigc;
53
54 Mixer_UI::Mixer_UI (AudioEngine& eng)
55         : Window (Gtk::WINDOW_TOPLEVEL),
56           engine (eng)
57 {
58         _strip_width = Wide;
59         track_menu = 0;
60
61         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
62         set_state (*node);
63
64         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
65         scroller_base.set_name ("MixerWindow");
66         scroller_base.signal_button_release_event().connect (mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
67         // add as last item of strip packer
68         strip_packer.pack_end (scroller_base, true, true);
69
70         scroller.add (strip_packer);
71         scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
72
73         track_display_model = ListStore::create (track_display_columns);
74         track_display.set_model (track_display_model);
75         track_display.append_column (_("Strips"), track_display_columns.text);
76         track_display.set_name (X_("MixerTrackDisplayList"));
77         track_display.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE);
78         track_display.set_reorderable (true);
79         track_display.set_size_request (75, -1);
80         track_display.set_headers_visible (true);
81         track_display.set_headers_clickable (true);
82         track_display_scroller.add (track_display);
83         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
84
85         group_display_model = ListStore::create (group_display_columns);
86         group_display.set_model (group_display_model);
87         group_display.append_column (_("active"), group_display_columns.active);
88         group_display.append_column (_("groupname"), group_display_columns.text);
89         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
90         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
91
92         /* use checkbox for the active column */
93
94         CellRendererToggle *active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (0));
95         active_cell->property_activatable() = true;
96         active_cell->property_radio() = false;
97         
98         group_display.set_name ("MixerGroupList");
99         group_display.set_reorderable (true);
100         group_display.set_size_request (true);
101         group_display_scroller.add (group_display);
102         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
103
104         snapshot_display_model = ListStore::create (snapshot_display_columns);
105         snapshot_display.set_model (snapshot_display_model);
106         snapshot_display.append_column (X_("mixgroups"), snapshot_display_columns.visible_name);
107         snapshot_display.set_name ("MixerSnapshotDisplayList");
108         snapshot_display.set_size_request (75, -1);
109         snapshot_display.set_reorderable (true);
110         snapshot_display_scroller.add (snapshot_display);
111         snapshot_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
112
113         group_display_vbox.pack_start (group_display_button, false, false);
114         group_display_vbox.pack_start (group_display_scroller, true, true);
115
116         track_display_frame.set_name("BaseFrame");
117         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
118         track_display_frame.add(track_display_scroller);
119
120         group_display_frame.set_name ("BaseFrame");
121         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
122         group_display_frame.add (group_display_vbox);
123
124         rhs_pane1.add1 (track_display_frame);
125         rhs_pane1.add2 (rhs_pane2);
126
127         rhs_pane2.add1 (group_display_frame);
128         rhs_pane2.add2 (snapshot_display_scroller);
129
130         list_vpacker.pack_start (rhs_pane1, true, true);
131
132         global_hpacker.pack_start (scroller, true, true);
133         global_hpacker.pack_start (out_packer, false, false);
134
135         list_hpane.add1(list_vpacker);
136         list_hpane.add2(global_hpacker);
137
138         rhs_pane1.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
139                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
140         rhs_pane2.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
141                                                         static_cast<Gtk::Paned*> (&rhs_pane2)));
142         list_hpane.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
143                                                          static_cast<Gtk::Paned*> (&list_hpane)));
144         
145
146         rhs_pane1.set_data ("collapse-direction", (gpointer) 0);
147         rhs_pane2.set_data ("collapse-direction", (gpointer) 0);
148         list_hpane.set_data ("collapse-direction", (gpointer) 1);
149
150         rhs_pane1.signal_button_release_event().connect (bind (ptr_fun (pane_handler), static_cast<Paned*>(&rhs_pane1)));
151         rhs_pane2.signal_button_release_event().connect (bind (ptr_fun (pane_handler), static_cast<Paned*>(&rhs_pane2)));
152         list_hpane.signal_button_release_event().connect (bind (ptr_fun (pane_handler), static_cast<Paned*>(&list_hpane)));
153         
154         global_vpacker.pack_start (list_hpane, true, true);
155
156         add (global_vpacker);
157         set_name ("MixerWindow");
158         set_title (_("ardour: mixer"));
159         set_wmclass (_("ardour_mixer"), "Ardour");
160
161         add_accel_group (ActionManager::ui_manager->get_accel_group());
162
163         signal_delete_event().connect (bind (ptr_fun (just_hide_it), static_cast<Gtk::Window *>(this)));
164         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
165
166         track_display.get_selection()->signal_changed().connect (mem_fun(*this, &Mixer_UI::track_display_selection_changed));
167         reorder_connection = track_display_model->signal_rows_reordered().connect (mem_fun (*this, &Mixer_UI::track_display_reordered_proxy));
168
169         group_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::group_display_button_press));
170         group_display.get_selection()->signal_changed().connect (mem_fun (*this, &Mixer_UI::group_display_selection_changed));
171
172         snapshot_display.get_selection()->signal_changed().connect (mem_fun(*this, &Mixer_UI::snapshot_display_selection_changed));
173         snapshot_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::snapshot_display_button_press));
174
175         _plugin_selector = new PluginSelector (PluginManager::the_manager());
176
177         signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
178
179         _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
180 }
181
182 Mixer_UI::~Mixer_UI ()
183 {
184 }
185
186 void
187 Mixer_UI::ensure_float (Window& win)
188 {
189         win.set_transient_for (*this);
190 }
191
192 void
193 Mixer_UI::show_window ()
194 {
195         show_all ();
196
197         /* now reset each strips width so the right widgets are shown */
198         MixerStrip* ms;
199
200         TreeModel::Children rows = track_display_model->children();
201         TreeModel::Children::iterator ri;
202
203         for (ri = rows.begin(); ri != rows.end(); ++ri) {
204                 ms = (*ri)[track_display_columns.strip];
205                 ms->set_width (ms->get_width());
206         }
207 }
208
209 void
210 Mixer_UI::add_strip (Route* route)
211 {
212         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), route));
213         
214         MixerStrip* strip;
215
216         if (route->hidden()) {
217                 return;
218         }
219
220         strip = new MixerStrip (*this, *session, *route);
221         strips.push_back (strip);
222
223         strip->set_width (_strip_width);
224         show_strip (strip);
225
226         reorder_connection.block();
227         
228         TreeModel::Row row = *(track_display_model->append());
229         row[track_display_columns.text] = route->name();
230         row[track_display_columns.route] = route;
231         row[track_display_columns.strip] = strip;
232
233         reorder_connection.unblock();
234         
235         // if (strip->marked_for_display() || strip->packed()) {
236         // track_display.get_selection()->select (row);
237         // }
238         
239         route->name_changed.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
240         strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
241
242         strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
243
244 //      if (width() < gdk_screen_width()) {
245 //              set_size_request (width() + (_strip_width == Wide ? 75 : 50), height());
246 //      }
247 }
248
249 void
250 Mixer_UI::remove_strip (MixerStrip* strip)
251 {
252         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
253         
254         TreeModel::Children rows = track_display_model->children();
255         TreeModel::Children::iterator ri;
256         list<MixerStrip *>::iterator i;
257
258         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
259                 strips.erase (i);
260         }
261
262         for (ri = rows.begin(); ri != rows.end(); ++ri) {
263                 if ((*ri)[track_display_columns.strip] == strip) {
264                         track_display_model->erase (ri);
265                         break;
266                 }
267         }
268 }
269
270 void
271 Mixer_UI::follow_strip_selection ()
272 {
273         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
274                 (*i)->set_selected (_selection.selected (&(*i)->route()));
275         }
276 }
277
278 gint
279 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
280 {
281         if (ev->button == 1) {
282
283                 /* this allows the user to click on the strip to terminate comment
284                    editing. XXX it needs improving so that we don't select the strip
285                    at the same time.
286                 */
287                 
288                 if (_selection.selected (&strip->route())) {
289                         _selection.remove (&strip->route());
290                 } else {
291                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
292                                 _selection.add (&strip->route());
293                         } else {
294                                 _selection.set (&strip->route());
295                         }
296                 }
297         }
298
299         return TRUE;
300 }
301
302 void
303 Mixer_UI::connect_to_session (Session* sess)
304 {
305         session = sess;
306
307         string wintitle = _("ardour: mixer: ");
308         wintitle += session->name();
309         set_title (wintitle);
310
311         // GTK2FIX: do we really need to do this?
312         // track_display.set_model (RefPtr<TreeStore>(0));
313         track_display_model->clear ();
314         session->foreach_route (this, &Mixer_UI::add_strip);
315         // track_display.set_model (track_display_model);
316
317         session->going_away.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
318         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
319         session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
320
321         session->foreach_mix_group(this, &Mixer_UI::add_mix_group);
322         
323         session->StateSaved.connect (mem_fun(*this, &Mixer_UI::session_state_saved));
324         redisplay_snapshots ();
325         
326         _plugin_selector->set_session (session);
327
328         start_updating ();
329 }
330
331 void
332 Mixer_UI::disconnect_from_session ()
333 {
334         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
335         
336         group_display_model->clear ();
337         set_title (_("ardour: mixer"));
338         stop_updating ();
339         hide_all_strips (false);
340 }
341
342 void
343 Mixer_UI::hide_all_strips (bool with_select)
344 {
345         TreeModel::Children rows = track_display_model->children();
346         TreeModel::Children::iterator i;
347
348         // GTK2FIX
349         // track_display_list.freeze ();
350         
351         for (i = rows.begin(); i != rows.end(); ++i) {
352                 
353                 TreeModel::Row row = (*i);
354                 MixerStrip* ms = row[track_display_columns.strip];
355                 
356                 if (with_select) {
357                         track_display.get_selection()->unselect (i);
358                 } else {
359                         hide_strip (ms);
360                 }
361         }
362
363         // track_display_list.thaw ();
364 }
365
366 void
367 Mixer_UI::unselect_all_strips ()
368 {
369         hide_all_strips (false);
370 }
371
372 void
373 Mixer_UI::select_all_strips ()
374 {
375         TreeModel::Children rows = track_display_model->children();
376         TreeModel::Children::iterator i;
377
378         for (i = rows.begin(); i != rows.end(); ++i) {
379                 track_display.get_selection()->select (i);
380         }
381 }
382
383 void
384 Mixer_UI::strip_select_op (bool audiotrack, bool select)
385 {
386         MixerStrip* ms;
387         TreeModel::Children rows = track_display_model->children();
388         TreeModel::Children::iterator i;
389
390         // GTK2FIX
391         // track_display_list.freeze ();
392         
393         for (i = rows.begin(); i != rows.end(); ++i) {
394                 ms = (*i)[track_display_columns.strip];
395
396                 if (ms->is_audio_track() == audiotrack) {
397                         if (select) {
398                                 track_display.get_selection()->select (i);
399                         } else {
400                                 track_display.get_selection()->unselect (i);
401                         }
402                 }
403         }
404         
405         // track_display_list.thaw ();  
406 }
407
408 void
409 Mixer_UI::select_all_audiotrack_strips ()
410 {
411         strip_select_op (true, true);
412 }
413 void
414 Mixer_UI::unselect_all_audiotrack_strips ()
415 {
416         strip_select_op (true, false);
417 }
418
419 void
420 Mixer_UI::select_all_audiobus_strips ()
421 {
422         strip_select_op (false, true);
423 }
424
425 void
426 Mixer_UI::unselect_all_audiobus_strips ()
427 {
428         strip_select_op (false, false);
429 }
430
431 void
432 Mixer_UI::show_strip (MixerStrip* ms)
433 {
434         if (!ms->packed()) {
435                 
436                 if (ms->route().master() || ms->route().control()) {
437                         out_packer.pack_start (*ms, false, false);
438                 } else {
439                         strip_packer.pack_start (*ms, false, false);
440                 }
441                 ms->set_packed (true);
442                 ms->show ();
443          }
444  }
445
446  void
447  Mixer_UI::hide_strip (MixerStrip* ms)
448  {
449          if (ms->packed()) {
450                  if (ms->route().master() || ms->route().control()) {
451                          out_packer.remove (*ms);
452                  } else {
453                          strip_packer.remove (*ms);
454                  }
455                  ms->set_packed (false);
456          }
457  }
458
459  gint
460  Mixer_UI::start_updating ()
461  {
462          screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::update_strips));
463          fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
464          return 0;
465  }
466
467  gint
468  Mixer_UI::stop_updating ()
469  {
470          screen_update_connection.disconnect();
471          fast_screen_update_connection.disconnect();
472          return 0;
473  }
474
475  void
476  Mixer_UI::update_strips ()
477  {
478          if (is_mapped () && session) {
479                  for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
480                          (*i)->update ();
481                  }
482          }
483  }
484
485  void
486  Mixer_UI::fast_update_strips ()
487  {
488          if (is_mapped () && session) {
489                  for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
490                          (*i)->fast_update ();
491                  }
492          }
493  }
494
495  void
496  Mixer_UI::snapshot_display_selection_changed ()
497  {
498          TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
499
500          Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
501
502          if (session->snap_name() == snap_name) {
503                  return;
504          }
505
506          ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
507  }
508
509  bool
510  Mixer_UI::snapshot_display_button_press (GdkEventButton* ev)
511  {
512          return false;
513  }
514
515  void
516  Mixer_UI::track_display_selection_changed ()
517  {
518          MixerStrip* strip;
519          TreeModel::Children rows = track_display_model->children();
520          TreeModel::Children::iterator i;
521          Glib::RefPtr<TreeSelection> selection = track_display.get_selection();
522
523          for (i = rows.begin(); i != rows.end(); ++i) {
524                  if (selection->is_selected (i)) {
525                          strip = (*i)[track_display_columns.strip];
526                          strip->set_marked_for_display  (true);
527                          show_strip (strip);
528                  } else {
529                          strip = (*i)[track_display_columns.strip];
530                          strip->set_marked_for_display (false);
531                          hide_strip (strip);
532                  }
533          }
534
535          track_display_reordered ();
536  }
537
538  void
539  Mixer_UI::select_strip_op (MixerStrip *strip, bool yn)
540  {
541          TreeModel::Children rows = track_display_model->children();
542          TreeModel::Children::iterator i;
543          Glib::RefPtr<TreeSelection> selection = track_display.get_selection();
544
545          for (i = rows.begin(); i != rows.end(); ++i) {
546                  if ((*i)[track_display_columns.strip] == strip) {
547                          if (yn) {
548                                  selection->select (*i);
549                          } else {
550                                  selection->unselect (*i);
551                          }
552                          break;
553                  }
554          }
555  }
556
557  void
558  Mixer_UI::unselect_strip_in_display (MixerStrip *strip)
559  {
560          select_strip_op (strip, true);
561  }
562  void
563  Mixer_UI::select_strip_in_display (MixerStrip *strip)
564  {
565          select_strip_op (strip, false);
566  }
567
568  void
569  Mixer_UI::track_display_reordered_proxy (const TreeModel::Path& path, const TreeModel::iterator& i, int* n)
570  {
571          track_display_reordered ();
572  }
573
574  void
575  Mixer_UI::track_display_reordered ()
576  {
577          TreeModel::Children rows = track_display_model->children();
578          TreeModel::Children::iterator i;
579          long order;
580
581          for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
582                  MixerStrip* strip = (*i)[track_display_columns.strip];
583
584                  if (strip->marked_for_display()) {
585                          strip->route().set_order_key (N_("signal"), order);
586                          if (strip->packed()) {
587                                  cerr << "reorder strip @ " << strip << endl;
588                                  strip_packer.reorder_child (*strip, -1); /* put at end */
589                          }
590                 }
591         }
592 }
593
594 void
595 Mixer_UI::track_column_click (gint col)
596 {
597         if (track_menu == 0) {
598                 build_track_menu ();
599         }
600
601         track_menu->popup (0, 0);
602 }
603
604 void
605 Mixer_UI::build_track_menu ()
606 {
607         using namespace Menu_Helpers;
608         using namespace Gtk;
609
610         track_menu = new Menu;
611         track_menu->set_name ("ArdourContextMenu");
612         MenuList& items = track_menu->items();
613         track_menu->set_name ("ArdourContextMenu");
614         
615         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::select_all_strips)));
616         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::unselect_all_strips)));
617         items.push_back (MenuElem (_("Show All AudioTrack MixerStrips"), mem_fun(*this, &Mixer_UI::select_all_audiotrack_strips)));
618         items.push_back (MenuElem (_("Hide All AudioTrack MixerStrips"), mem_fun(*this, &Mixer_UI::unselect_all_audiotrack_strips)));
619         items.push_back (MenuElem (_("Show All AudioBus MixerStrips"), mem_fun(*this, &Mixer_UI::select_all_audiobus_strips)));
620         items.push_back (MenuElem (_("Hide All AudioBus MixerStrips"), mem_fun(*this, &Mixer_UI::unselect_all_audiobus_strips)));
621
622 }
623
624 void
625 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
626 {
627         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
628         
629         TreeModel::Children rows = track_display_model->children();
630         TreeModel::Children::iterator i;
631         
632         for (i = rows.begin(); i != rows.end(); ++i) {
633                 if ((*i)[track_display_columns.strip] == mx) {
634                         (*i)[track_display_columns.text] = mx->route().name();
635                         return;
636                 }
637         } 
638
639         error << _("track display list item for renamed strip not found!") << endmsg;
640 }
641
642 void
643 Mixer_UI::new_mix_group ()
644 {
645         ArdourPrompter prompter;
646         string result;
647
648         prompter.set_prompt (_("Name for new mix group"));
649         prompter.show_all ();
650         
651         switch (prompter.run ()) {
652         case GTK_RESPONSE_ACCEPT:
653                 prompter.get_result (result);
654                 if (result.length()) {
655                         session->add_mix_group (result);
656                 }       
657                 break;
658         }
659 }
660
661 // GTK2FIX
662 //void
663 //Mixer_UI::group_display_button_clicked ()
664 //{
665 //      if (session) {
666 //              new_mix_group ();
667 //      }
668 //}
669
670 bool
671 Mixer_UI::group_display_button_press (GdkEventButton* ev)
672 {
673         RouteGroup* group;
674
675         TreeIter iter;
676         TreeModel::Path path;
677         TreeViewColumn* column;
678         int cellx;
679         int celly;
680
681         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
682                 return false;
683         }
684         
685         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
686         case 0:
687                 /* active column click */
688                 
689                 if ((iter = group_display_model->get_iter (path))) {
690                         /* path points to a valid node */
691                         if ((group = (*iter)[group_display_columns.group]) != 0) {
692                                 group->set_active (!group->is_active (), this);
693                         }
694                 }
695                 break;
696
697         case 1:
698                 if (Keyboard::is_edit_event (ev)) {
699                         // RouteGroup* group = (RouteGroup *) group_display.row(row).get_data ();
700                         // edit_mix_group (group);
701
702                 } else {
703                         /* allow regular select to occur */
704                         return false;
705                 }
706                 break;
707         }
708                 
709         return true;
710 }
711
712 void
713 Mixer_UI::group_display_selection_changed ()
714 {
715         TreeModel::iterator i;
716         TreeModel::Children rows = group_display_model->children();
717         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
718
719         for (i = rows.begin(); i != rows.end(); ++i) {
720                 RouteGroup* group;
721
722                 group = (*i)[group_display_columns.group];
723
724                 if (selection->is_selected (i)) {
725                         group->set_hidden (true, this);
726                 } else {
727                         group->set_hidden (true, this);
728                 }
729         }
730
731         track_display_reordered ();
732 }
733
734 void
735 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
736 {
737         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
738         
739         if (src != this) {
740                 // select row
741         }
742
743         TreeModel::Children rows = group_display_model->children();
744         TreeModel::Children::iterator gi;
745         TreeModel::Children::iterator ti;
746
747         for (gi = rows.begin(); gi != rows.end(); ++gi) {
748                 if ((*gi)[group_display_columns.group] == group) {
749                         break;
750                 }
751         }
752
753         if (gi == rows.end()) {
754                 return;
755         }
756                 
757         rows = track_display_model->children();
758
759         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
760                 if ((*i)->route().mix_group() == group) {
761                         if (group->is_hidden ()) {
762                                 unselect_strip_in_display(*i);
763                                 group_display.get_selection()->unselect (*gi);
764                         } else {
765                                 select_strip_in_display(*i);
766                                 group_display.get_selection()->select (*gi);
767                         }
768                 }
769         }
770 }
771
772 void
773 Mixer_UI::add_mix_group (RouteGroup* group)
774
775 {
776         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
777
778         TreeModel::Row row = *(group_display_model->append());
779         row[group_display_columns.active] = group->is_active();
780         row[group_display_columns.text] = group->name();
781         row[group_display_columns.group] = group;
782
783         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
784 }
785
786 void
787 Mixer_UI::redisplay_snapshots ()
788 {
789         if (session == 0) {
790                 return;
791         }
792
793         vector<string*>* states;
794         if ((states = session->possible_states()) == 0) {
795                 return;
796         }
797
798         snapshot_display_model->clear ();
799
800         for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
801                 string statename = *(*i);
802                 const TreeModel::Row & row = *snapshot_display_model->append();
803                 row[snapshot_display_columns.visible_name] = statename;
804                 row[snapshot_display_columns.real_name] = statename;
805         }
806
807         delete states;
808 }
809
810 void
811 Mixer_UI::session_state_saved (string snap_name)
812 {
813         ENSURE_GUI_THREAD (bind (mem_fun(*this, &Mixer_UI::session_state_saved), snap_name));
814         redisplay_snapshots ();
815 }
816
817 gint
818 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
819 {
820         using namespace Menu_Helpers;
821
822         if (Keyboard::is_context_menu_event (ev)) {
823                 ARDOUR_UI::instance()->add_route();
824                 return TRUE;
825         }
826
827         return FALSE;
828 }
829
830 void
831 Mixer_UI::set_strip_width (Width w)
832 {
833         _strip_width = w;
834
835         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
836                 (*i)->set_width (w);
837         }
838 }
839
840
841 int
842 Mixer_UI::set_state (const XMLNode& node)
843 {
844         const XMLProperty* prop;
845         XMLNode* geometry;
846         int x, y, width, height, xoff, yoff;
847         
848         if ((geometry = find_named_node (node, "geometry")) == 0) {
849
850                 width = default_width;
851                 height = default_height;
852                 x = 1;
853                 y = 1;
854                 xoff = 0;
855                 yoff = 21;
856
857         } else {
858
859                 width = atoi(geometry->property("x_size")->value().c_str());
860                 height = atoi(geometry->property("y_size")->value().c_str());
861                 x = atoi(geometry->property("x_pos")->value().c_str());
862                 y = atoi(geometry->property("y_pos")->value().c_str());
863                 xoff = atoi(geometry->property("x_off")->value().c_str());
864                 yoff = atoi(geometry->property("y_off")->value().c_str());
865         }
866                 
867         set_default_size(width, height);
868         // GTK2FIX
869         // set_uposition(x, y-yoff);
870
871         if ((prop = node.property ("narrow-strips"))) {
872                 if (prop->value() == "yes") {
873                         set_strip_width (Narrow);
874                 } else {
875                         set_strip_width (Wide);
876                 }
877         }
878
879         return 0;
880 }
881
882 XMLNode&
883 Mixer_UI::get_state (void)
884 {
885         XMLNode* node = new XMLNode ("Mixer");
886
887         if (is_realized()) {
888                 Glib::RefPtr<Gdk::Window> win = get_window();
889                 
890                 int x, y, xoff, yoff, width, height;
891                 win->get_root_origin(x, y);
892                 win->get_position(xoff, yoff);
893                 win->get_size(width, height);
894
895                 XMLNode* geometry = new XMLNode ("geometry");
896                 char buf[32];
897                 snprintf(buf, sizeof(buf), "%d", width);
898                 geometry->add_property(X_("x_size"), string(buf));
899                 snprintf(buf, sizeof(buf), "%d", height);
900                 geometry->add_property(X_("y_size"), string(buf));
901                 snprintf(buf, sizeof(buf), "%d", x);
902                 geometry->add_property(X_("x_pos"), string(buf));
903                 snprintf(buf, sizeof(buf), "%d", y);
904                 geometry->add_property(X_("y_pos"), string(buf));
905                 snprintf(buf, sizeof(buf), "%d", xoff);
906                 geometry->add_property(X_("x_off"), string(buf));
907                 snprintf(buf, sizeof(buf), "%d", yoff);
908                 geometry->add_property(X_("y_off"), string(buf));
909
910                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
911                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
912                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane2)->gobj()));
913                 geometry->add_property(X_("mixer_rhs_pane2_pos"), string(buf));
914                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
915                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
916
917                 node->add_child_nocopy (*geometry);
918         }
919
920         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
921
922         return *node;
923 }
924
925
926 void 
927 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
928 {
929         int pos;
930         XMLProperty* prop = 0;
931         char buf[32];
932         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
933         XMLNode* geometry;
934         int width, height;
935         static int32_t done[3] = { 0, 0, 0 };
936
937         if ((geometry = find_named_node (*node, "geometry")) == 0) {
938                 width = default_width;
939                 height = default_height;
940         } else {
941                 width = atoi(geometry->property("x_size")->value());
942                 height = atoi(geometry->property("y_size")->value());
943         }
944
945         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
946
947                 if (done[0]) {
948                         return;
949                 }
950
951                 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
952                         pos = height / 3;
953                         snprintf (buf, sizeof(buf), "%d", pos);
954                 } else {
955                         pos = atoi (prop->value());
956                 }
957
958                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
959                         rhs_pane1.set_position (pos);
960                 }
961
962         } else if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
963
964                 if (done[1]) {
965                         return;
966                 }
967
968                 if (!geometry || (prop = geometry->property("mixer_rhs_pane2_pos")) == 0) {
969                         pos = height / 3;
970                         snprintf (buf, sizeof(buf), "%d", pos);
971                 } else {
972                         pos = atoi (prop->value());
973                 }
974
975                 if ((done[1] = GTK_WIDGET(rhs_pane2.gobj())->allocation.height > pos)) {
976                         rhs_pane2.set_position (pos);
977                 }
978
979         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
980
981                 if (done[2]) {
982                         return;
983                 }
984
985                 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
986                         pos = 75;
987                         snprintf (buf, sizeof(buf), "%d", pos);
988                 } else {
989                         pos = atoi (prop->value());
990                 }
991
992                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
993                         list_hpane.set_position (pos);
994                 }
995         }
996 }
997