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