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