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