ca10dd430055f7fa13bb0a278a3057b8bbdd3cc7
[ardour.git] / gtk2_ardour / panner_ui.cc
1 /*
2   Copyright (C) 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 #include <limits.h>
20
21 #include "ardour/io.h"
22 #include "ardour/dB.h"
23 #include <gtkmm2ext/utils.h>
24 #include <gtkmm2ext/stop_signal.h>
25 #include <gtkmm2ext/barcontroller.h>
26 #include "midi++/manager.h"
27 #include "pbd/fastlog.h"
28
29 #include "ardour_ui.h"
30 #include "panner_ui.h"
31 #include "panner2d.h"
32 #include "utils.h"
33 #include "panner.h"
34 #include "gui_thread.h"
35
36 #include "ardour/delivery.h"
37 #include "ardour/session.h"
38 #include "ardour/panner.h"
39 #include "ardour/route.h"
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
46 using namespace Gtkmm2ext;
47 using namespace Gtk;
48 using namespace sigc;
49
50 const int PannerUI::pan_bar_height = 30;
51
52 PannerUI::PannerUI (Session& s)
53         : _session (s),
54           _current_nouts (-1),
55           hAdjustment(0.0, 0.0, 0.0),
56           vAdjustment(0.0, 0.0, 0.0),
57           panning_viewport(hAdjustment, vAdjustment),
58           panning_up_arrow (Gtk::ARROW_UP, Gtk::SHADOW_OUT),
59           panning_down_arrow (Gtk::ARROW_DOWN, Gtk::SHADOW_OUT),
60           panning_link_button (_("link")),
61           pan_automation_style_button (""),
62           pan_automation_state_button ("")
63 {
64         ignore_toggle = false;
65         pan_menu = 0;
66         pan_astate_menu = 0;
67         pan_astyle_menu = 0;
68         in_pan_update = false;
69
70         pan_automation_style_button.set_name ("MixerAutomationModeButton");
71         pan_automation_state_button.set_name ("MixerAutomationPlaybackButton");
72
73         ARDOUR_UI::instance()->tooltips().set_tip (pan_automation_state_button, _("Pan automation mode"));
74         ARDOUR_UI::instance()->tooltips().set_tip (pan_automation_style_button, _("Pan automation type"));
75
76         //set_size_request_to_display_given_text (pan_automation_state_button, X_("O"), 2, 2);
77         //set_size_request_to_display_given_text (pan_automation_style_button, X_("0"), 2, 2);
78
79         pan_bar_packer.set_size_request (-1, 61);
80         panning_viewport.set_size_request (-1, 61);
81         panning_viewport.set_name (X_("BaseFrame"));
82
83         ARDOUR_UI::instance()->tooltips().set_tip (panning_link_button,
84                                                    _("panning link control"));
85         ARDOUR_UI::instance()->tooltips().set_tip (panning_link_direction_button,
86                                                    _("panning link direction"));
87
88         pan_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
89         pan_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
90
91         pan_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &PannerUI::pan_automation_style_button_event), false);
92         pan_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &PannerUI::pan_automation_state_button_event), false);
93
94         panning_link_button.set_name (X_("PanningLinkButton"));
95         panning_link_direction_button.set_name (X_("PanningLinkDirectionButton"));
96
97         panning_link_box.pack_start (panning_link_button, true, true);
98         panning_link_box.pack_start (panning_link_direction_button, true, true);
99         panning_link_box.pack_start (pan_automation_state_button, true, true);
100
101         /* the pixmap will be reset at some point, but the key thing is that
102            we need a pixmap in the button just to get started.
103         */
104         panning_link_direction_button.add (*(manage (new Image (get_xpm("forwardblarrow.xpm")))));
105
106         panning_link_direction_button.signal_clicked().connect
107                 (mem_fun(*this, &PannerUI::panning_link_direction_clicked));
108
109         panning_link_button.signal_button_press_event().connect
110                 (mem_fun(*this, &PannerUI::panning_link_button_press), false);
111         panning_link_button.signal_button_release_event().connect
112                 (mem_fun(*this, &PannerUI::panning_link_button_release), false);
113
114         panning_up.set_border_width (3);
115         panning_down.set_border_width (3);
116         panning_up.add (panning_up_arrow);
117         panning_down.add (panning_down_arrow);
118         panning_up.set_name (X_("PanScrollerBase"));
119         panning_down.set_name (X_("PanScrollerBase"));
120         panning_up_arrow.set_name (X_("PanScrollerArrow"));
121         panning_down_arrow.set_name (X_("PanScrollerArrow"));
122
123         pan_vbox.set_spacing (2);
124         pan_vbox.pack_start (panning_viewport, Gtk::PACK_SHRINK);
125         pan_vbox.pack_start (panning_link_box, Gtk::PACK_SHRINK);
126
127         pack_start (pan_vbox, true, true);
128
129         panner = 0;
130         big_window = 0;
131
132         set_width(Narrow);
133 }
134
135 void
136 PannerUI::set_panner (boost::shared_ptr<Panner> p)
137 {
138         connections.clear ();
139
140         delete pan_astyle_menu;
141         pan_astyle_menu = 0;
142
143         delete pan_astate_menu;
144         pan_astate_menu = 0;
145
146         _panner = p;
147
148         delete panner;
149         panner = 0;
150
151         if (!_panner) {
152                 return;
153         }
154
155         connections.push_back (_panner->Changed.connect (mem_fun(*this, &PannerUI::panner_changed)));
156         connections.push_back (_panner->LinkStateChanged.connect (mem_fun(*this, &PannerUI::update_pan_linkage)));
157         connections.push_back (_panner->StateChanged.connect (mem_fun(*this, &PannerUI::update_pan_state)));
158
159         setup_pan ();
160
161         pan_changed (0);
162         update_pan_sensitive ();
163         update_pan_linkage ();
164         pan_automation_state_changed ();
165
166 #if WHERE_DOES_THIS_LIVE
167         pan_bar_packer.show();
168         panning_viewport.show();
169         panning_up.show();
170         panning_up_arrow.show();
171         panning_down.show();
172         panning_down_arrow.show();
173         pan_vbox.show();
174         panning_link_button.show();
175         panning_link_direction_button.show();
176         panning_link_box.show();
177         pan_automation_style_button.show();
178         pan_automation_state_button.show();
179         show();
180 #endif
181 }
182
183 void
184 PannerUI::build_astate_menu ()
185 {
186         using namespace Menu_Helpers;
187
188         if (pan_astate_menu == 0) {
189                 pan_astate_menu = new Menu;
190                 pan_astate_menu->set_name ("ArdourContextMenu");
191         } else {
192                 pan_astate_menu->items().clear ();
193         }
194
195         pan_astate_menu->items().push_back (MenuElem (_("Manual"), bind (
196                         mem_fun (_panner.get(), &Panner::set_automation_state),
197                         (AutoState) Off)));
198         pan_astate_menu->items().push_back (MenuElem (_("Play"), bind (
199                         mem_fun (_panner.get(), &Panner::set_automation_state),
200                         (AutoState) Play)));
201         pan_astate_menu->items().push_back (MenuElem (_("Write"), bind (
202                         mem_fun (_panner.get(), &Panner::set_automation_state),
203                         (AutoState) Write)));
204         pan_astate_menu->items().push_back (MenuElem (_("Touch"), bind (
205                         mem_fun (_panner.get(), &Panner::set_automation_state),
206                         (AutoState) Touch)));
207
208 }
209
210 void
211 PannerUI::build_astyle_menu ()
212 {
213         using namespace Menu_Helpers;
214
215         if (pan_astyle_menu == 0) {
216                 pan_astyle_menu = new Menu;
217                 pan_astyle_menu->set_name ("ArdourContextMenu");
218         } else {
219                 pan_astyle_menu->items().clear();
220         }
221
222         pan_astyle_menu->items().push_back (MenuElem (_("Trim")));
223         pan_astyle_menu->items().push_back (MenuElem (_("Abs")));
224 }
225
226 boost::shared_ptr<PBD::Controllable>
227 PannerUI::get_controllable()
228 {
229         return pan_bars[0]->get_controllable();
230 }
231
232 bool
233 PannerUI::panning_link_button_press (GdkEventButton*)
234 {
235         return true;
236 }
237
238 bool
239 PannerUI::panning_link_button_release (GdkEventButton*)
240 {
241         if (!ignore_toggle) {
242                 _panner->set_linked (!_panner->linked());
243         }
244         return true;
245 }
246
247 void
248 PannerUI::panning_link_direction_clicked()
249 {
250         switch (_panner->link_direction()) {
251         case Panner::SameDirection:
252                 _panner->set_link_direction (Panner::OppositeDirection);
253                 break;
254         default:
255                 _panner->set_link_direction (Panner::SameDirection);
256                 break;
257         }
258 }
259
260 void
261 PannerUI::update_pan_linkage ()
262 {
263         ENSURE_GUI_THREAD(mem_fun(*this, &PannerUI::update_pan_linkage));
264
265         bool x = _panner->linked();
266         bool bx = panning_link_button.get_active();
267
268         if (x != bx) {
269
270                 ignore_toggle = true;
271                 panning_link_button.set_active (x);
272                 ignore_toggle = false;
273         }
274
275         panning_link_direction_button.set_sensitive (x);
276
277         switch (_panner->link_direction()) {
278         case Panner::SameDirection:
279                 panning_link_direction_button.set_image (*(manage (new Image (get_xpm ("forwardblarrow.xpm")))));
280                 break;
281         default:
282                 panning_link_direction_button.set_image (*(manage (new Image (get_xpm("revdblarrow.xpm")))));
283                 break;
284         }
285 }
286
287 void
288 PannerUI::set_width (Width w)
289 {
290         switch (w) {
291         case Wide:
292                 panning_link_button.set_label (_("link"));
293                 break;
294         case Narrow:
295                 panning_link_button.set_label (_("L"));
296                 break;
297         }
298
299         _width = w;
300 }
301
302
303 PannerUI::~PannerUI ()
304 {
305         for (vector<Adjustment*>::iterator i = pan_adjustments.begin(); i != pan_adjustments.end(); ++i) {
306                 delete (*i);
307         }
308
309         for (vector<PannerBar*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
310                 delete (*i);
311         }
312
313         delete panner;
314         delete big_window;
315         delete pan_menu;
316         delete pan_astyle_menu;
317         delete pan_astate_menu;
318 }
319
320
321 void
322 PannerUI::panner_changed ()
323 {
324         ENSURE_GUI_THREAD (mem_fun(*this, &PannerUI::panner_changed));
325         setup_pan ();
326         pan_changed (0);
327 }
328
329 void
330 PannerUI::update_pan_state ()
331 {
332         /* currently nothing to do */
333         // ENSURE_GUI_THREAD (mem_fun(*this, &PannerUI::update_panner_state));
334 }
335
336 void
337 PannerUI::setup_pan ()
338 {
339         if (!_panner) {
340                 return;
341         }
342
343         uint32_t nouts = _panner->nouts();
344
345         if (int32_t (nouts) == _current_nouts) {
346                 return;
347         }
348
349         _current_nouts = nouts;
350
351         cout << "Setup pan for " << _panner->name() << endl;
352
353         if (nouts == 0 || nouts == 1) {
354
355                 while (!pan_adjustments.empty()) {
356                         delete pan_bars.back();
357                         pan_bars.pop_back ();
358                         delete pan_adjustments.back();
359                         pan_adjustments.pop_back ();
360                 }
361
362                 /* stick something into the panning viewport so that it redraws */
363
364                 EventBox* eb = manage (new EventBox());
365                 panning_viewport.remove ();
366                 panning_viewport.add (*eb);
367                 panning_viewport.show_all ();
368
369         } else if (nouts == 2) {
370
371                 vector<Adjustment*>::size_type asz;
372                 uint32_t npans = _panner->npanners();
373
374                 while (!pan_adjustments.empty()) {
375                         delete pan_bars.back();
376                         pan_bars.pop_back ();
377                         delete pan_adjustments.back();
378                         pan_adjustments.pop_back ();
379                 }
380
381                 while ((asz = pan_adjustments.size()) < npans) {
382
383                         float x, rx;
384                         PannerBar* bc;
385
386                         /* initialize adjustment with 0.0 (L) or 1.0 (R) for the first and second panners,
387                            which serves as a default, otherwise use current value */
388
389                         rx = _panner->pan_control( asz)->get_value();
390
391                         if (npans == 1) {
392                                 x = 0.5;
393                         } else if (asz == 0) {
394                                 x = 0.0;
395                         } else if (asz == 1) {
396                                 x = 1.0;
397                         } else {
398                                 x = rx;
399                         }
400
401                         pan_adjustments.push_back (new Adjustment (x, 0, 1.0, 0.005, 0.05));
402                         bc = new PannerBar (*pan_adjustments[asz],
403                                 boost::static_pointer_cast<PBD::Controllable>( _panner->pan_control( asz )) );
404
405                         /* now set adjustment with current value of panner, then connect the signals */
406                         pan_adjustments.back()->set_value(rx);
407                         pan_adjustments.back()->signal_value_changed().connect (bind (mem_fun(*this, &PannerUI::pan_adjustment_changed), (uint32_t) asz));
408
409                         _panner->pan_control( asz )->Changed.connect (bind (mem_fun(*this, &PannerUI::pan_value_changed), (uint32_t) asz));
410
411
412                         bc->set_name ("PanSlider");
413                         bc->set_shadow_type (Gtk::SHADOW_NONE);
414
415                         boost::shared_ptr<AutomationControl> ac = _panner->pan_control (asz);
416
417                         if (asz) {
418                                 bc->StartGesture.connect (mem_fun (*ac, &AutomationControl::start_touch));
419                                 bc->StopGesture.connect (mem_fun (*ac, &AutomationControl::stop_touch));
420                         }
421
422                         char buf[64];
423                         snprintf (buf, sizeof (buf), _("panner for channel %zu"), asz + 1);
424                         ARDOUR_UI::instance()->tooltips().set_tip (bc->event_widget(), buf);
425
426                         bc->event_widget().signal_button_release_event().connect
427                                 (bind (mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) asz));
428
429                         bc->set_size_request (-1, pan_bar_height);
430
431                         pan_bars.push_back (bc);
432                         pan_bar_packer.pack_start (*bc, false, false);
433                 }
434
435                 /* now that we actually have the pan bars,
436                    set their sensitivity based on current
437                    automation state.
438                 */
439
440                 update_pan_sensitive ();
441
442                 panning_viewport.remove ();
443                 panning_viewport.add (pan_bar_packer);
444                 panning_viewport.show_all ();
445
446         } else {
447
448                 if (!panner) {
449                         panner = new Panner2d (_panner, 61);
450                         panner->set_name ("MixerPanZone");
451                         panner->show ();
452
453                         panner->signal_button_press_event().connect
454                                 (bind (mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) 0), false);
455                 }
456
457                 update_pan_sensitive ();
458                 panner->reset (nouts);
459                 if (big_window) {
460                         big_window->reset (_panner->npanners());
461                 }
462                 panner->set_size_request (-1, 61);
463
464                 /* and finally, add it to the panner frame */
465
466                 panning_viewport.remove ();
467                 panning_viewport.add (*panner);
468                 panning_viewport.show_all ();
469         }
470 }
471
472 bool
473 PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
474 {
475         switch (ev->button) {
476         case 1:
477                 if (panner && ev->type == GDK_2BUTTON_PRESS) {
478                         if (!big_window) {
479                                 big_window = new Panner2dWindow (_panner, 400, _panner->npanners());
480                         }
481                         big_window->show ();
482                         return true;
483                 }
484                 break;
485
486         case 3:
487                 if (pan_menu == 0) {
488                         pan_menu = manage (new Menu);
489                         pan_menu->set_name ("ArdourContextMenu");
490                 }
491                 build_pan_menu (which);
492                 pan_menu->popup (1, ev->time);
493                 return true;
494                 break;
495         default:
496                 return false;
497         }
498
499         return false; // what's wrong with gcc?
500 }
501
502 void
503 PannerUI::build_pan_menu (uint32_t which)
504 {
505         using namespace Menu_Helpers;
506         MenuList& items (pan_menu->items());
507
508         items.clear ();
509
510         items.push_back (CheckMenuElem (_("Mute")));
511
512         /* set state first, connect second */
513
514         (dynamic_cast<CheckMenuItem*> (&items.back()))->set_active (_panner->streampanner(which).muted());
515         (dynamic_cast<CheckMenuItem*> (&items.back()))->signal_toggled().connect
516                 (bind (mem_fun(*this, &PannerUI::pan_mute), which));
517
518         items.push_back (CheckMenuElem (_("Bypass"), mem_fun(*this, &PannerUI::pan_bypass_toggle)));
519         bypass_menu_item = static_cast<CheckMenuItem*> (&items.back());
520
521         /* set state first, connect second */
522
523         bypass_menu_item->set_active (_panner->bypassed());
524         bypass_menu_item->signal_toggled().connect (mem_fun(*this, &PannerUI::pan_bypass_toggle));
525
526         items.push_back (MenuElem (_("Reset"), bind (mem_fun (*this, &PannerUI::pan_reset), which)));
527         items.push_back (SeparatorElem());
528         items.push_back (MenuElem (_("Reset all"), mem_fun (*this, &PannerUI::pan_reset_all)));
529 }
530
531 void
532 PannerUI::pan_mute (uint32_t which)
533 {
534         StreamPanner& sp = _panner->streampanner(which);
535         sp.set_muted (!sp.muted());
536 }
537
538 void
539 PannerUI::pan_bypass_toggle ()
540 {
541         if (bypass_menu_item && (_panner->bypassed() != bypass_menu_item->get_active())) {
542                 _panner->set_bypassed (!_panner->bypassed());
543         }
544 }
545
546 void
547 PannerUI::pan_reset (uint32_t which)
548 {
549         _panner->reset_streampanner (which);
550 }
551
552 void
553 PannerUI::pan_reset_all ()
554 {
555         _panner->reset_to_default ();
556 }
557
558 void
559 PannerUI::effective_pan_display ()
560 {
561         if (_panner->empty()) {
562                 return;
563         }
564
565         switch (_panner->nouts()) {
566         case 0:
567         case 1:
568                 /* relax */
569                 break;
570
571         case 2:
572                 update_pan_bars (true);
573                 break;
574
575         default:
576                 //panner->move_puck (pan_value (v, right), 0.5);
577                 break;
578         }
579 }
580
581 void
582 PannerUI::pan_changed (void *src)
583 {
584         if (src == this) {
585                 return;
586         }
587
588         switch (_panner->npanners()) {
589         case 0:
590                 panning_link_direction_button.set_sensitive (false);
591                 panning_link_button.set_sensitive (false);
592                 return;
593         case 1:
594                 panning_link_direction_button.set_sensitive (false);
595                 panning_link_button.set_sensitive (false);
596                 break;
597         default:
598                 panning_link_direction_button.set_sensitive (true);
599                 panning_link_button.set_sensitive (true);
600         }
601
602         uint32_t nouts = _panner->nouts();
603
604         switch (nouts) {
605         case 0:
606         case 1:
607                 /* relax */
608                 break;
609
610         case 2:
611                 /* bring pan bar state up to date */
612                 update_pan_bars (false);
613                 break;
614
615         default:
616                 // panner->move_puck (pan_value (pans[0], pans[1]), 0.5);
617                 break;
618         }
619 }
620
621 void
622 PannerUI::pan_adjustment_changed (uint32_t which)
623 {
624         if (!in_pan_update && which < _panner->npanners()) {
625
626                 float xpos;
627                 float val = pan_adjustments[which]->get_value ();
628                 xpos = _panner->pan_control( which )->get_value();
629
630                 /* add a kinda-sorta detent for the middle */
631
632                 if (val != 0.5 && Panner::equivalent (val, 0.5)) {
633                         /* this is going to be reentrant, so just
634                            return after it.
635                         */
636
637                         in_pan_update = true;
638                         pan_adjustments[which]->set_value (0.5);
639                         in_pan_update = false;
640                         return;
641                 }
642
643                 if (!Panner::equivalent (val, xpos)) {
644
645                         _panner->streampanner(which).set_position (val);
646                         /* XXX
647                            the panner objects have no access to the session,
648                            so do this here. ick.
649                         */
650                         _session.set_dirty();
651                 }
652         }
653 }
654
655 void
656 PannerUI::pan_value_changed (uint32_t which)
657 {
658         ENSURE_GUI_THREAD (bind (mem_fun(*this, &PannerUI::pan_value_changed), which));
659
660         if (_panner->npanners() > 1 && which < _panner->npanners()) {
661                 float xpos;
662                 float val = pan_adjustments[which]->get_value ();
663
664                 _panner->streampanner(which).get_position (xpos);
665
666                 if (!Panner::equivalent (val, xpos)) {
667                         in_pan_update = true;
668                         pan_adjustments[which]->set_value (xpos);
669                         in_pan_update = false;
670                 }
671         }
672 }
673
674 void
675 PannerUI::update_pan_bars (bool only_if_aplay)
676 {
677         uint32_t n;
678         vector<Adjustment*>::iterator i;
679
680         in_pan_update = true;
681
682         /* this runs during automation playback, and moves the bar controllers
683            and/or pucks around.
684         */
685
686         for (i = pan_adjustments.begin(), n = 0; i != pan_adjustments.end(); ++i, ++n) {
687                 float xpos, val;
688
689                 if (only_if_aplay) {
690                         boost::shared_ptr<AutomationList> alist (_panner->streampanner(n).pan_control()->alist());
691
692                         if (!alist->automation_playback()) {
693                                 continue;
694                         }
695                 }
696
697                 _panner->streampanner(n).get_effective_position (xpos);
698                 val = (*i)->get_value ();
699
700                 if (!Panner::equivalent (val, xpos)) {
701                         (*i)->set_value (xpos);
702                 }
703         }
704
705         in_pan_update = false;
706 }
707
708 void
709 PannerUI::update_pan_sensitive ()
710 {
711         bool sensitive = !(_panner->automation_state() & Play);
712
713         switch (_panner->nouts()) {
714         case 0:
715         case 1:
716                 break;
717         case 2:
718                 for (vector<PannerBar*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
719                         (*i)->set_sensitive (sensitive);
720                 }
721                 break;
722         default:
723                 if (panner) {
724                         panner->set_sensitive (sensitive);
725                 }
726                 if (big_window) {
727                         big_window->set_sensitive (sensitive);
728                 }
729                 break;
730         }
731 }
732
733 gint
734 PannerUI::pan_automation_state_button_event (GdkEventButton *ev)
735 {
736         using namespace Menu_Helpers;
737
738         if (ev->type == GDK_BUTTON_RELEASE) {
739                 return TRUE;
740         }
741
742         switch (ev->button) {
743         case 1:
744                 if (pan_astate_menu == 0) {
745                         build_astate_menu ();
746                 }
747                 pan_astate_menu->popup (1, ev->time);
748                 break;
749         default:
750                 break;
751         }
752
753         return TRUE;
754 }
755
756 gint
757 PannerUI::pan_automation_style_button_event (GdkEventButton *ev)
758 {
759         if (ev->type == GDK_BUTTON_RELEASE) {
760                 return TRUE;
761         }
762
763         switch (ev->button) {
764         case 1:
765                 if (pan_astyle_menu == 0) {
766                         build_astyle_menu ();
767                 }
768                 pan_astyle_menu->popup (1, ev->time);
769                 break;
770         default:
771                 break;
772         }
773         return TRUE;
774 }
775
776 void
777 PannerUI::pan_automation_style_changed ()
778 {
779         ENSURE_GUI_THREAD(mem_fun(*this, &PannerUI::pan_automation_style_changed));
780
781         switch (_width) {
782         case Wide:
783                 pan_automation_style_button.set_label (astyle_string(_panner->automation_style()));
784                 break;
785         case Narrow:
786                 pan_automation_style_button.set_label (short_astyle_string(_panner->automation_style()));
787                 break;
788         }
789 }
790
791 void
792 PannerUI::pan_automation_state_changed ()
793 {
794         ENSURE_GUI_THREAD(mem_fun(*this, &PannerUI::pan_automation_state_changed));
795
796         bool x;
797
798         switch (_width) {
799         case Wide:
800           pan_automation_state_button.set_label (astate_string(_panner->automation_state()));
801                 break;
802         case Narrow:
803           pan_automation_state_button.set_label (short_astate_string(_panner->automation_state()));
804                 break;
805         }
806
807         /* when creating a new session, we get to create busses (and
808            sometimes tracks) with no outputs by the time they get
809            here.
810         */
811
812         if (_panner->empty()) {
813                 return;
814         }
815
816         x = (_panner->streampanner(0).pan_control()->alist()->automation_state() != Off);
817
818         if (pan_automation_state_button.get_active() != x) {
819         ignore_toggle = true;
820                 pan_automation_state_button.set_active (x);
821                 ignore_toggle = false;
822         }
823
824         update_pan_sensitive ();
825
826         /* start watching automation so that things move */
827
828         pan_watching.disconnect();
829
830         if (x) {
831                 pan_watching = ARDOUR_UI::RapidScreenUpdate.connect (mem_fun (*this, &PannerUI::effective_pan_display));
832         }
833 }
834
835 string
836 PannerUI::astate_string (AutoState state)
837 {
838         return _astate_string (state, false);
839 }
840
841 string
842 PannerUI::short_astate_string (AutoState state)
843 {
844         return _astate_string (state, true);
845 }
846
847 string
848 PannerUI::_astate_string (AutoState state, bool shrt)
849 {
850         string sstr;
851
852         switch (state) {
853         case Off:
854                 sstr = (shrt ? "M" : _("M"));
855                 break;
856         case Play:
857                 sstr = (shrt ? "P" : _("P"));
858                 break;
859         case Touch:
860                 sstr = (shrt ? "T" : _("T"));
861                 break;
862         case Write:
863                 sstr = (shrt ? "W" : _("W"));
864                 break;
865         }
866
867         return sstr;
868 }
869
870 string
871 PannerUI::astyle_string (AutoStyle style)
872 {
873         return _astyle_string (style, false);
874 }
875
876 string
877 PannerUI::short_astyle_string (AutoStyle style)
878 {
879         return _astyle_string (style, true);
880 }
881
882 string
883 PannerUI::_astyle_string (AutoStyle style, bool shrt)
884 {
885         if (style & Trim) {
886                 return _("Trim");
887         } else {
888                 /* XXX it might different in different languages */
889
890                 return (shrt ? _("Abs") : _("Abs"));
891         }
892 }