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