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