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