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