29c991a995a8e6c6141c6a952e29326c727cc732
[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 #include "pbd/stacktrace.h"
29
30 #include "ardour_ui.h"
31 #include "panner_ui.h"
32 #include "panner2d.h"
33 #include "utils.h"
34 #include "panner.h"
35 #include "gui_thread.h"
36
37 #include "ardour/delivery.h"
38 #include "ardour/session.h"
39 #include "ardour/panner.h"
40 #include "ardour/route.h"
41
42 #include "i18n.h"
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace Gtkmm2ext;
48 using namespace Gtk;
49 using namespace sigc;
50
51 const int PannerUI::pan_bar_height = 30;
52
53 PannerUI::PannerUI (Session& s)
54         : _session (s),
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* ev)
234 {
235         return true;
236 }
237
238 bool
239 PannerUI::panning_link_button_release (GdkEventButton* ev)
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         cerr << "Setup pan for " << _panner->name() << endl;
340         // PBD::stacktrace (cerr, 5);
341
342         if (!_panner) {
343                 return;
344         }
345
346         uint32_t nouts = _panner->nouts();
347
348         cerr << "\tnouts = " << nouts << endl;
349
350         if (nouts == 0 || nouts == 1) {
351
352                 while (!pan_adjustments.empty()) {
353                         delete pan_bars.back();
354                         pan_bars.pop_back ();
355                         delete pan_adjustments.back();
356                         pan_adjustments.pop_back ();
357                 }
358
359                 /* stick something into the panning viewport so that it redraws */
360
361                 EventBox* eb = manage (new EventBox());
362                 panning_viewport.remove ();
363                 panning_viewport.add (*eb);
364                 panning_viewport.show_all ();
365
366         } else if (nouts == 2) {
367
368                 vector<Adjustment*>::size_type asz;
369                 uint32_t npans = _panner->npanners();
370
371                 while (!pan_adjustments.empty()) {
372                         delete pan_bars.back();
373                         pan_bars.pop_back ();
374                         delete pan_adjustments.back();
375                         pan_adjustments.pop_back ();
376                 }
377
378                 while ((asz = pan_adjustments.size()) < npans) {
379
380                         float x, rx;
381                         PannerBar* bc;
382
383                         /* initialize adjustment with 0.0 (L) or 1.0 (R) for the first and second panners,
384                            which serves as a default, otherwise use current value */
385
386                         rx = _panner->pan_control( asz)->get_value();
387
388                         if (npans == 1) {
389                                 x = 0.5;
390                         } else if (asz == 0) {
391                                 x = 0.0;
392                         } else if (asz == 1) {
393                                 x = 1.0;
394                         } else {
395                                 x = rx;
396                         }
397
398                         pan_adjustments.push_back (new Adjustment (x, 0, 1.0, 0.005, 0.05));
399                         bc = new PannerBar (*pan_adjustments[asz],
400                                 boost::static_pointer_cast<PBD::Controllable>( _panner->pan_control( asz )) );
401
402                         /* now set adjustment with current value of panner, then connect the signals */
403                         pan_adjustments.back()->set_value(rx);
404                         pan_adjustments.back()->signal_value_changed().connect (bind (mem_fun(*this, &PannerUI::pan_adjustment_changed), (uint32_t) asz));
405
406                         _panner->pan_control( asz )->Changed.connect (bind (mem_fun(*this, &PannerUI::pan_value_changed), (uint32_t) asz));
407
408                         
409                         bc->set_name ("PanSlider");
410                         bc->set_shadow_type (Gtk::SHADOW_NONE);
411                         
412                         boost::shared_ptr<AutomationControl> ac = _panner->pan_control (asz);
413                         
414                         if (asz) {
415                                 bc->StartGesture.connect (mem_fun (*ac, &AutomationControl::start_touch));
416                                 bc->StopGesture.connect (mem_fun (*ac, &AutomationControl::stop_touch));
417                         }
418
419                         char buf[64];
420                         snprintf (buf, sizeof (buf), _("panner for channel %zu"), asz + 1);
421                         ARDOUR_UI::instance()->tooltips().set_tip (bc->event_widget(), buf);
422
423                         bc->event_widget().signal_button_release_event().connect
424                                 (bind (mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) asz));
425
426                         bc->set_size_request (-1, pan_bar_height);
427
428                         pan_bars.push_back (bc);
429                         pan_bar_packer.pack_start (*bc, false, false);
430                 }
431
432                 /* now that we actually have the pan bars,
433                    set their sensitivity based on current
434                    automation state.
435                 */
436
437                 update_pan_sensitive ();
438
439                 panning_viewport.remove ();
440                 panning_viewport.add (pan_bar_packer);
441                 panning_viewport.show_all ();
442
443         } else {
444
445                 if (!panner) {
446                         panner = new Panner2d (_panner, 61);
447                         panner->set_name ("MixerPanZone");
448                         panner->show ();
449  
450                         panner->signal_button_press_event().connect
451                                 (bind (mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) 0), false);
452                 }
453                 
454                 update_pan_sensitive ();
455                 panner->reset (nouts);
456                 if (big_window) {
457                         big_window->reset (_panner->npanners());
458                 }
459                 panner->set_size_request (-1, 61);
460
461                 /* and finally, add it to the panner frame */
462
463                 panning_viewport.remove ();
464                 panning_viewport.add (*panner);
465                 panning_viewport.show_all ();
466         }
467 }
468
469 bool
470 PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
471 {
472         switch (ev->button) {
473         case 1:
474                 if (panner && ev->type == GDK_2BUTTON_PRESS) {
475                         if (!big_window) {
476                                 big_window = new Panner2dWindow (_panner, 400, _panner->npanners());
477                         }
478                         big_window->show ();
479                         return true;
480                 }
481                 break;
482
483         case 3:
484                 if (pan_menu == 0) {
485                         pan_menu = manage (new Menu);
486                         pan_menu->set_name ("ArdourContextMenu");
487                 }
488                 build_pan_menu (which);
489                 pan_menu->popup (1, ev->time);
490                 return true;
491                 break;
492         default:
493                 return false;
494         }
495
496         return false; // what's wrong with gcc?
497 }
498
499 void
500 PannerUI::build_pan_menu (uint32_t which)
501 {
502         using namespace Menu_Helpers;
503         MenuList& items (pan_menu->items());
504
505         items.clear ();
506
507         items.push_back (CheckMenuElem (_("Mute")));
508         
509         /* set state first, connect second */
510
511         (dynamic_cast<CheckMenuItem*> (&items.back()))->set_active (_panner->streampanner(which).muted());
512         (dynamic_cast<CheckMenuItem*> (&items.back()))->signal_toggled().connect
513                 (bind (mem_fun(*this, &PannerUI::pan_mute), which));
514
515         items.push_back (CheckMenuElem (_("Bypass"), mem_fun(*this, &PannerUI::pan_bypass_toggle)));
516         bypass_menu_item = static_cast<CheckMenuItem*> (&items.back());
517
518         /* set state first, connect second */
519
520         bypass_menu_item->set_active (_panner->bypassed());
521         bypass_menu_item->signal_toggled().connect (mem_fun(*this, &PannerUI::pan_bypass_toggle));
522
523         items.push_back (MenuElem (_("Reset"), bind (mem_fun (*this, &PannerUI::pan_reset), which)));
524         items.push_back (SeparatorElem());
525         items.push_back (MenuElem (_("Reset all"), mem_fun (*this, &PannerUI::pan_reset_all)));
526 }
527
528 void
529 PannerUI::pan_mute (uint32_t which)
530 {
531         StreamPanner& sp = _panner->streampanner(which);
532         sp.set_muted (!sp.muted());
533 }
534
535 void
536 PannerUI::pan_bypass_toggle ()
537 {
538         if (bypass_menu_item && (_panner->bypassed() != bypass_menu_item->get_active())) {
539                 _panner->set_bypassed (!_panner->bypassed());
540         }
541 }
542
543 void
544 PannerUI::pan_reset (uint32_t which)
545 {
546         _panner->reset_streampanner (which);
547 }
548
549 void
550 PannerUI::pan_reset_all ()
551 {
552         _panner->reset_to_default ();
553 }
554
555 void
556 PannerUI::effective_pan_display ()
557 {
558         if (_panner->empty()) {
559                 return;
560         }
561
562         switch (_panner->nouts()) {
563         case 0: 
564         case 1:
565                 /* relax */
566                 break;
567
568         case 2:
569                 update_pan_bars (true);
570                 break;
571
572         default:
573                 //panner->move_puck (pan_value (v, right), 0.5);
574                 break;
575         }
576 }
577
578 void
579 PannerUI::pan_changed (void *src)
580 {
581         if (src == this) {
582                 return;
583         }
584
585         switch (_panner->npanners()) {
586         case 0:
587                 panning_link_direction_button.set_sensitive (false);
588                 panning_link_button.set_sensitive (false);
589                 return;
590         case 1:
591                 panning_link_direction_button.set_sensitive (false);
592                 panning_link_button.set_sensitive (false);
593                 break;
594         default:
595                 panning_link_direction_button.set_sensitive (true);
596                 panning_link_button.set_sensitive (true);
597         }
598
599         uint32_t nouts = _panner->nouts();
600
601         switch (nouts) {
602         case 0:
603         case 1:
604                 /* relax */
605                 break;
606
607         case 2:
608                 /* bring pan bar state up to date */
609                 update_pan_bars (false);
610                 break;
611
612         default:
613                 // panner->move_puck (pan_value (pans[0], pans[1]), 0.5);
614                 break;
615         }
616 }
617
618 void
619 PannerUI::pan_adjustment_changed (uint32_t which)
620 {
621         if (!in_pan_update && which < _panner->npanners()) {
622
623                 float xpos;
624                 float val = pan_adjustments[which]->get_value ();
625                 xpos = _panner->pan_control( which )->get_value();
626
627                 /* add a kinda-sorta detent for the middle */
628                 
629                 if (val != 0.5 && Panner::equivalent (val, 0.5)) {
630                         /* this is going to be reentrant, so just 
631                            return after it.
632                         */
633
634                         in_pan_update = true;
635                         pan_adjustments[which]->set_value (0.5);
636                         in_pan_update = false;
637                         return;
638                 }
639                 
640                 if (!Panner::equivalent (val, xpos)) {
641
642                         _panner->streampanner(which).set_position (val);
643                         /* XXX 
644                            the panner objects have no access to the session,
645                            so do this here. ick.
646                         */
647                         _session.set_dirty();
648                 }
649         }
650 }
651
652 void
653 PannerUI::pan_value_changed (uint32_t which)
654 {
655         ENSURE_GUI_THREAD (bind (mem_fun(*this, &PannerUI::pan_value_changed), which));
656                                                            
657         if (_panner->npanners() > 1 && which < _panner->npanners()) {
658                 float xpos;
659                 float val = pan_adjustments[which]->get_value ();
660
661                 _panner->streampanner(which).get_position (xpos);
662
663                 if (!Panner::equivalent (val, xpos)) {
664                         in_pan_update = true;
665                         pan_adjustments[which]->set_value (xpos);
666                         in_pan_update = false;
667                 }
668         }
669 }       
670
671 void
672 PannerUI::update_pan_bars (bool only_if_aplay)
673 {
674         uint32_t n;
675         vector<Adjustment*>::iterator i;
676
677         in_pan_update = true;
678
679         /* this runs during automation playback, and moves the bar controllers
680            and/or pucks around.
681         */
682
683         for (i = pan_adjustments.begin(), n = 0; i != pan_adjustments.end(); ++i, ++n) {
684                 float xpos, val;
685
686                 if (only_if_aplay) {
687                         boost::shared_ptr<AutomationList> alist (_panner->streampanner(n).pan_control()->alist());
688                         
689                         if (!alist->automation_playback()) {
690                                 continue;
691                         }
692                 }
693
694                 _panner->streampanner(n).get_effective_position (xpos);
695                 val = (*i)->get_value ();
696                 
697                 if (!Panner::equivalent (val, xpos)) {
698                         (*i)->set_value (xpos);
699                 }
700         }
701
702         in_pan_update = false;
703 }
704
705 void
706 PannerUI::update_pan_sensitive () 
707 {
708         bool sensitive = !(_panner->automation_state() & Play);
709
710         switch (_panner->nouts()) {
711         case 0:
712         case 1:
713                 break;
714         case 2:
715                 for (vector<PannerBar*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
716                         (*i)->set_sensitive (sensitive);
717                 }
718                 break;
719         default:
720                 if (panner) {
721                         panner->set_sensitive (sensitive);
722                 }
723                 if (big_window) {
724                         big_window->set_sensitive (sensitive);
725                 }
726                 break;
727         }
728 }
729
730 gint
731 PannerUI::pan_automation_state_button_event (GdkEventButton *ev)
732 {
733         using namespace Menu_Helpers;
734
735         if (ev->type == GDK_BUTTON_RELEASE) {
736                 return TRUE;
737         }
738
739         switch (ev->button) {
740         case 1:
741                 if (pan_astate_menu == 0) {
742                         build_astate_menu ();
743                 }
744                 pan_astate_menu->popup (1, ev->time);
745                 break;
746         default:
747                 break;
748         }
749
750         return TRUE;
751 }
752
753 gint
754 PannerUI::pan_automation_style_button_event (GdkEventButton *ev)
755 {
756         if (ev->type == GDK_BUTTON_RELEASE) {
757                 return TRUE;
758         }
759
760         switch (ev->button) {
761         case 1:
762                 if (pan_astyle_menu == 0) {
763                         build_astyle_menu ();
764                 }
765                 pan_astyle_menu->popup (1, ev->time);
766                 break;
767         default:
768                 break;
769         }
770         return TRUE;
771 }
772
773 void
774 PannerUI::pan_automation_style_changed ()
775 {
776         ENSURE_GUI_THREAD(mem_fun(*this, &PannerUI::pan_automation_style_changed));
777         
778         switch (_width) {
779         case Wide:
780                 pan_automation_style_button.set_label (astyle_string(_panner->automation_style()));
781                 break;
782         case Narrow:
783                 pan_automation_style_button.set_label (short_astyle_string(_panner->automation_style()));
784                 break;
785         }
786 }
787
788 void
789 PannerUI::pan_automation_state_changed ()
790 {
791         ENSURE_GUI_THREAD(mem_fun(*this, &PannerUI::pan_automation_state_changed));
792         
793         bool x;
794
795         switch (_width) {
796         case Wide:
797           pan_automation_state_button.set_label (astate_string(_panner->automation_state()));
798                 break;
799         case Narrow:
800           pan_automation_state_button.set_label (short_astate_string(_panner->automation_state()));
801                 break;
802         }
803
804         /* when creating a new session, we get to create busses (and
805            sometimes tracks) with no outputs by the time they get
806            here.
807         */
808
809         if (_panner->empty()) {
810                 return;
811         }
812
813         x = (_panner->streampanner(0).pan_control()->alist()->automation_state() != Off);
814
815         if (pan_automation_state_button.get_active() != x) {
816         ignore_toggle = true;
817                 pan_automation_state_button.set_active (x);
818                 ignore_toggle = false;
819         }
820
821         update_pan_sensitive ();
822
823         /* start watching automation so that things move */
824
825         pan_watching.disconnect();
826
827         if (x) {
828                 pan_watching = ARDOUR_UI::RapidScreenUpdate.connect (mem_fun (*this, &PannerUI::effective_pan_display));
829         }
830 }
831
832 string
833 PannerUI::astate_string (AutoState state)
834 {
835         return _astate_string (state, false);
836 }
837
838 string
839 PannerUI::short_astate_string (AutoState state)
840 {
841         return _astate_string (state, true);
842 }
843
844 string
845 PannerUI::_astate_string (AutoState state, bool shrt)
846 {
847         string sstr;
848
849         switch (state) {
850         case Off:
851                 sstr = (shrt ? "M" : _("M"));
852                 break;
853         case Play:
854                 sstr = (shrt ? "P" : _("P"));
855                 break;
856         case Touch:
857                 sstr = (shrt ? "T" : _("T"));
858                 break;
859         case Write:
860                 sstr = (shrt ? "W" : _("W"));
861                 break;
862         }
863
864         return sstr;
865 }
866
867 string
868 PannerUI::astyle_string (AutoStyle style)
869 {
870         return _astyle_string (style, false);
871 }
872
873 string
874 PannerUI::short_astyle_string (AutoStyle style)
875 {
876         return _astyle_string (style, true);
877 }
878
879 string
880 PannerUI::_astyle_string (AutoStyle style, bool shrt)
881 {
882         if (style & Trim) {
883                 return _("Trim");
884         } else {
885                 /* XXX it might different in different languages */
886
887                 return (shrt ? _("Abs") : _("Abs"));
888         }
889 }