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