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