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