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