remove all lines to avoid recompiles after commits
[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] = true;
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                                 strip_packer.remove (*strip);
618                                 strip->set_packed (false);
619                         }
620                 }
621         }
622 }
623
624 struct SignalOrderRouteSorter {
625     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
626             /* use of ">" forces the correct sort order */
627             return a->order_key ("signal") < b->order_key ("signal");
628     }
629 };
630
631 void
632 Mixer_UI::initial_track_display ()
633 {
634         boost::shared_ptr<Session::RouteList> routes = session->get_routes();
635         Session::RouteList copy (*routes);
636         SignalOrderRouteSorter sorter;
637
638         copy.sort (sorter);
639         
640         no_track_list_redisplay = true;
641
642         track_model->clear ();
643
644         add_strip (copy);
645
646         no_track_list_redisplay = false;
647
648         redisplay_track_list ();
649 }
650
651 void
652 Mixer_UI::show_track_list_menu ()
653 {
654         if (track_menu == 0) {
655                 build_track_menu ();
656         }
657
658         track_menu->popup (1, gtk_get_current_event_time());
659 }
660
661 bool
662 Mixer_UI::track_display_button_press (GdkEventButton* ev)
663 {
664         if (Keyboard::is_context_menu_event (ev)) {
665                 show_track_list_menu ();
666                 return true;
667         }
668
669         TreeIter iter;
670         TreeModel::Path path;
671         TreeViewColumn* column;
672         int cellx;
673         int celly;
674         
675         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
676                 return false;
677         }
678
679         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
680         case 0:
681                 /* allow normal processing to occur */
682                 return false;
683
684         case 1: /* visibility */
685
686                 if ((iter = track_model->get_iter (path))) {
687                         MixerStrip* strip = (*iter)[track_columns.strip];
688                         if (strip) {
689
690                                 if (!strip->route()->master() && !strip->route()->control()) {
691                                         bool visible = (*iter)[track_columns.visible];
692                                         (*iter)[track_columns.visible] = !visible;
693                                 }
694                         }
695                 }
696                 return true;
697
698         default:
699                 break;
700         }
701
702         return false;
703 }
704
705
706 void
707 Mixer_UI::build_track_menu ()
708 {
709         using namespace Menu_Helpers;
710         using namespace Gtk;
711
712         track_menu = new Menu;
713         track_menu->set_name ("ArdourContextMenu");
714         MenuList& items = track_menu->items();
715         
716         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
717         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
718         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
719         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
720         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
721         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
722
723 }
724
725 void
726 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
727 {
728         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
729         
730         TreeModel::Children rows = track_model->children();
731         TreeModel::Children::iterator i;
732         
733         for (i = rows.begin(); i != rows.end(); ++i) {
734                 if ((*i)[track_columns.strip] == mx) {
735                         (*i)[track_columns.text] = mx->route()->name();
736                         return;
737                 }
738         } 
739
740         error << _("track display list item for renamed strip not found!") << endmsg;
741 }
742
743
744 void
745 Mixer_UI::build_mix_group_context_menu ()
746 {
747         using namespace Gtk::Menu_Helpers;
748
749         mix_group_context_menu = new Menu;
750         mix_group_context_menu->set_name ("ArdourContextMenu");
751         MenuList& items = mix_group_context_menu->items();
752
753         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
754         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
755         items.push_back (SeparatorElem());
756         items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
757         
758 }
759
760 bool
761 Mixer_UI::group_display_button_press (GdkEventButton* ev)
762 {
763         if (Keyboard::is_context_menu_event (ev)) {
764                 if (mix_group_context_menu == 0) {
765                         build_mix_group_context_menu ();
766                 }
767                 mix_group_context_menu->popup (1, ev->time);
768                 return true;
769         }
770
771
772         RouteGroup* group;
773         TreeIter iter;
774         TreeModel::Path path;
775         TreeViewColumn* column;
776         int cellx;
777         int celly;
778
779         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
780                 return false;
781         }
782
783         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
784         case 0:
785                 if (Keyboard::is_edit_event (ev)) {
786                         if ((iter = group_model->get_iter (path))) {
787                                 if ((group = (*iter)[group_columns.group]) != 0) {
788                                         // edit_mix_group (group);
789                                         return true;
790                                 }
791                         }
792                         
793                 } 
794                 break;
795
796         case 1:
797                 if ((iter = group_model->get_iter (path))) {
798                         bool active = (*iter)[group_columns.active];
799                         (*iter)[group_columns.active] = !active;
800                         return true;
801                 }
802                 break;
803                 
804         case 2:
805                 if ((iter = group_model->get_iter (path))) {
806                         bool visible = (*iter)[group_columns.visible];
807                         (*iter)[group_columns.visible] = !visible;
808                         return true;
809                 }
810                 break;
811
812         default:
813                 break;
814         }
815         
816         return false;
817  }
818
819 void
820 Mixer_UI::activate_all_mix_groups ()
821 {
822         Gtk::TreeModel::Children children = group_model->children();
823         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
824                 (*iter)[group_columns.active] = true;
825         }
826 }
827
828 void
829 Mixer_UI::disable_all_mix_groups ()
830 {
831         Gtk::TreeModel::Children children = group_model->children();
832         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
833                 (*iter)[group_columns.active] = false;
834         }
835 }
836
837 void
838 Mixer_UI::mix_groups_changed ()
839 {
840         ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
841
842         /* just rebuild the while thing */
843
844         group_model->clear ();
845
846         {
847                 TreeModel::Row row;
848                 row = *(group_model->append());
849                 row[group_columns.active] = false;
850                 row[group_columns.visible] = true;
851                 row[group_columns.text] = (_("-all-"));
852                 row[group_columns.group] = 0;
853         }
854
855         session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
856 }
857
858 void
859 Mixer_UI::new_mix_group ()
860 {
861         session->add_mix_group ("");
862 }
863
864 void
865 Mixer_UI::remove_selected_mix_group ()
866 {
867         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
868         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
869
870         if (rows.empty()) {
871                 return;
872         }
873
874         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
875         TreeIter iter;
876         
877         /* selection mode is single, so rows.begin() is it */
878
879         if ((iter = group_model->get_iter (*i))) {
880
881                 RouteGroup* rg = (*iter)[group_columns.group];
882
883                 if (rg) {
884                         session->remove_mix_group (*rg);
885                 }
886         }
887 }
888
889 void
890 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
891 {
892         if (in_group_row_change) {
893                 return;
894         }
895
896         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
897         
898         TreeModel::iterator i;
899         TreeModel::Children rows = group_model->children();
900         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
901
902         in_group_row_change = true;
903         
904         for (i = rows.begin(); i != rows.end(); ++i) {
905                 if ((*i)[group_columns.group] == group) {
906                         (*i)[group_columns.visible] = !group->is_hidden ();
907                         (*i)[group_columns.active] = group->is_active ();
908                         (*i)[group_columns.text] = group->name ();
909                         break;
910                 }
911         }
912
913         in_group_row_change = false;
914 }
915
916 void
917 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
918 {
919         RouteGroup* group;
920         TreeIter iter;
921
922         if ((iter = group_model->get_iter (path))) {
923         
924                 if ((group = (*iter)[group_columns.group]) == 0) {
925                         return;
926                 }
927                 
928                 if (new_text != group->name()) {
929                         group->set_name (new_text);
930                 }
931         }
932 }
933
934 void 
935 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
936 {
937         RouteGroup* group;
938
939         if (in_group_row_change) {
940                 return;
941         }
942
943         if ((group = (*iter)[group_columns.group]) == 0) {
944                 return;
945         }
946
947         if ((*iter)[group_columns.visible]) {
948                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
949                         if ((*i)->mix_group() == group) {
950                                 show_strip (*i);
951                         }
952                 }
953         } else {
954                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
955                         if ((*i)->mix_group() == group) {
956                                 hide_strip (*i);
957                         }
958                 }
959         } 
960
961         bool active = (*iter)[group_columns.active];
962         group->set_active (active, this);
963
964         Glib::ustring name = (*iter)[group_columns.text];
965
966         if (name != group->name()) {
967                 group->set_name (name);
968         }
969 }
970
971 void
972 Mixer_UI::add_mix_group (RouteGroup* group)
973
974 {
975         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
976         bool focus = false;
977
978         in_group_row_change = true;
979
980         TreeModel::Row row = *(group_model->append());
981         row[group_columns.active] = group->is_active();
982         row[group_columns.visible] = true;
983         row[group_columns.group] = group;
984         if (!group->name().empty()) {
985                 row[group_columns.text] = group->name();
986         } else {
987                 row[group_columns.text] = _("unnamed");
988                 focus = true;
989         }
990
991         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
992         
993         if (focus) {
994                 TreeViewColumn* col = group_display.get_column (0);
995                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
996                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
997         }
998
999         in_group_row_change = false;
1000 }
1001
1002 bool
1003 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1004 {
1005         using namespace Menu_Helpers;
1006
1007         if (Keyboard::is_context_menu_event (ev)) {
1008                 ARDOUR_UI::instance()->add_route();
1009                 return true;
1010         }
1011
1012         return false;
1013 }
1014
1015 void
1016 Mixer_UI::set_strip_width (Width w)
1017 {
1018         _strip_width = w;
1019
1020         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1021                 (*i)->set_width (w);
1022         }
1023 }
1024
1025
1026 int
1027 Mixer_UI::set_state (const XMLNode& node)
1028 {
1029         const XMLProperty* prop;
1030         XMLNode* geometry;
1031         Gdk::Geometry g;
1032         int x, y, xoff, yoff;
1033         
1034         if ((geometry = find_named_node (node, "geometry")) == 0) {
1035
1036                 g.base_width = default_width;
1037                 g.base_height = default_height;
1038                 x = 1;
1039                 y = 1;
1040                 xoff = 0;
1041                 yoff = 21;
1042
1043         } else {
1044
1045                 g.base_width = atoi(geometry->property("x_size")->value().c_str());
1046                 g.base_height = atoi(geometry->property("y_size")->value().c_str());
1047                 x = atoi(geometry->property("x_pos")->value().c_str());
1048                 y = atoi(geometry->property("y_pos")->value().c_str());
1049                 xoff = atoi(geometry->property("x_off")->value().c_str());
1050                 yoff = atoi(geometry->property("y_off")->value().c_str());
1051         }
1052
1053         set_geometry_hints (global_vpacker, g, Gdk::HINT_BASE_SIZE);
1054         set_default_size(g.base_width, g.base_height);
1055         move (x, y);
1056
1057         if ((prop = node.property ("narrow-strips"))) {
1058                 if (prop->value() == "yes") {
1059                         set_strip_width (Narrow);
1060                 } else {
1061                         set_strip_width (Wide);
1062                 }
1063         }
1064
1065         if ((prop = node.property ("show-mixer"))) {
1066                 if (prop->value() == "yes") {
1067                        _visible = true;
1068                 }
1069         }
1070
1071         return 0;
1072 }
1073
1074 XMLNode&
1075 Mixer_UI::get_state (void)
1076 {
1077         XMLNode* node = new XMLNode ("Mixer");
1078
1079         if (is_realized()) {
1080                 Glib::RefPtr<Gdk::Window> win = get_window();
1081                 
1082                 int x, y, xoff, yoff, width, height;
1083                 win->get_root_origin(x, y);
1084                 win->get_position(xoff, yoff);
1085                 win->get_size(width, height);
1086
1087                 XMLNode* geometry = new XMLNode ("geometry");
1088                 char buf[32];
1089                 snprintf(buf, sizeof(buf), "%d", width);
1090                 geometry->add_property(X_("x_size"), string(buf));
1091                 snprintf(buf, sizeof(buf), "%d", height);
1092                 geometry->add_property(X_("y_size"), string(buf));
1093                 snprintf(buf, sizeof(buf), "%d", x);
1094                 geometry->add_property(X_("x_pos"), string(buf));
1095                 snprintf(buf, sizeof(buf), "%d", y);
1096                 geometry->add_property(X_("y_pos"), string(buf));
1097                 snprintf(buf, sizeof(buf), "%d", xoff);
1098                 geometry->add_property(X_("x_off"), string(buf));
1099                 snprintf(buf, sizeof(buf), "%d", yoff);
1100                 geometry->add_property(X_("y_off"), string(buf));
1101
1102                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1103                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1104                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1105                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1106
1107                 node->add_child_nocopy (*geometry);
1108         }
1109
1110         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1111
1112         node->add_property ("show-mixer", _visible ? "yes" : "no");
1113
1114         return *node;
1115 }
1116
1117
1118 void 
1119 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1120 {
1121         int pos;
1122         XMLProperty* prop = 0;
1123         char buf[32];
1124         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1125         XMLNode* geometry;
1126         int width, height;
1127         static int32_t done[3] = { 0, 0, 0 };
1128
1129         if ((geometry = find_named_node (*node, "geometry")) == 0) {
1130                 width = default_width;
1131                 height = default_height;
1132         } else {
1133                 width = atoi(geometry->property("x_size")->value());
1134                 height = atoi(geometry->property("y_size")->value());
1135         }
1136
1137         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1138
1139                 if (done[0]) {
1140                         return;
1141                 }
1142
1143                 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1144                         pos = height / 3;
1145                         snprintf (buf, sizeof(buf), "%d", pos);
1146                 } else {
1147                         pos = atoi (prop->value());
1148                 }
1149
1150                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1151                         rhs_pane1.set_position (pos);
1152                 }
1153
1154         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1155
1156                 if (done[2]) {
1157                         return;
1158                 }
1159
1160                 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1161                         pos = 75;
1162                         snprintf (buf, sizeof(buf), "%d", pos);
1163                 } else {
1164                         pos = atoi (prop->value());
1165                 }
1166
1167                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1168                         list_hpane.set_position (pos);
1169                 }
1170         }
1171 }
1172
1173 bool
1174 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1175 {
1176         return key_press_focus_accelerator_handler (*this, ev);
1177 }