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