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