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