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