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