more cleanups of PannerUI, add start/end gesture support to mono panner
[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 "gui_thread.h"
33 #include "stereo_panner.h"
34 #include "mono_panner.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 = 40;
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         _stereo_panner = 0;
71         _ignore_width_change = false;
72         _ignore_position_change = false;
73
74         pan_automation_style_button.set_name ("MixerAutomationModeButton");
75         pan_automation_state_button.set_name ("MixerAutomationPlaybackButton");
76
77         ARDOUR_UI::instance()->set_tip (pan_automation_state_button, _("Pan automation mode"));
78         ARDOUR_UI::instance()->set_tip (pan_automation_style_button, _("Pan automation type"));
79
80         //set_size_request_to_display_given_text (pan_automation_state_button, X_("O"), 2, 2);
81         //set_size_request_to_display_given_text (pan_automation_style_button, X_("0"), 2, 2);
82
83         panning_viewport.set_name (X_("BaseFrame"));
84
85         ARDOUR_UI::instance()->set_tip (panning_link_button,
86                                                    _("panning link control"));
87         ARDOUR_UI::instance()->set_tip (panning_link_direction_button,
88                                                    _("panning link direction"));
89
90         pan_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
91         pan_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
92
93         pan_automation_style_button.signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_automation_style_button_event), false);
94         pan_automation_state_button.signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_automation_state_button_event), false);
95
96         panning_link_button.set_name (X_("PanningLinkButton"));
97         panning_link_direction_button.set_name (X_("PanningLinkDirectionButton"));
98
99         panning_link_box.pack_start (panning_link_button, true, true);
100         panning_link_box.pack_start (panning_link_direction_button, true, true);
101         panning_link_box.pack_start (pan_automation_state_button, true, true);
102
103         /* the pixmap will be reset at some point, but the key thing is that
104            we need a pixmap in the button just to get started.
105         */
106         panning_link_direction_button.add (*(manage (new Image (get_xpm("forwardblarrow.xpm")))));
107
108         panning_link_direction_button.signal_clicked().connect
109                 (sigc::mem_fun(*this, &PannerUI::panning_link_direction_clicked));
110
111         panning_link_button.signal_button_press_event().connect
112                 (sigc::mem_fun(*this, &PannerUI::panning_link_button_press), false);
113         panning_link_button.signal_button_release_event().connect
114                 (sigc::mem_fun(*this, &PannerUI::panning_link_button_release), false);
115
116         panning_up.set_border_width (3);
117         panning_down.set_border_width (3);
118         panning_up.add (panning_up_arrow);
119         panning_down.add (panning_down_arrow);
120         panning_up.set_name (X_("PanScrollerBase"));
121         panning_down.set_name (X_("PanScrollerBase"));
122         panning_up_arrow.set_name (X_("PanScrollerArrow"));
123         panning_down_arrow.set_name (X_("PanScrollerArrow"));
124
125         pan_vbox.set_spacing (2);
126         pan_vbox.pack_start (panning_viewport, Gtk::PACK_SHRINK);
127         pan_vbox.pack_start (panning_link_box, Gtk::PACK_SHRINK);
128         
129         pack_start (pan_vbox, true, true);
130
131         twod_panner = 0;
132         big_window = 0;
133
134         set_width(Narrow);
135 }
136
137 void
138 PannerUI::set_panner (boost::shared_ptr<Panner> p)
139 {
140         connections.drop_connections ();
141
142         delete pan_astyle_menu;
143         pan_astyle_menu = 0;
144
145         delete pan_astate_menu;
146         pan_astate_menu = 0;
147
148         _panner = p;
149
150         delete twod_panner;
151         twod_panner = 0;
152
153         delete _stereo_panner;
154         _stereo_panner = 0;
155
156         if (!_panner) {
157                 return;
158         }
159
160         _panner->Changed.connect (connections, invalidator (*this), boost::bind (&PannerUI::panner_changed, this, this), gui_context());
161         _panner->LinkStateChanged.connect (connections, invalidator (*this), boost::bind (&PannerUI::update_pan_linkage, this), gui_context());
162         _panner->StateChanged.connect (connections, invalidator (*this), boost::bind (&PannerUI::update_pan_state, this), gui_context());
163
164         /* new panner object, force complete reset of panner GUI
165          */
166
167         _current_nouts = 0;
168         _current_npans = 0;
169
170         panner_changed (0);
171         update_pan_sensitive ();
172         update_pan_linkage ();
173         pan_automation_state_changed ();
174
175 }
176
177 void
178 PannerUI::build_astate_menu ()
179 {
180         using namespace Menu_Helpers;
181
182         if (pan_astate_menu == 0) {
183                 pan_astate_menu = new Menu;
184                 pan_astate_menu->set_name ("ArdourContextMenu");
185         } else {
186                 pan_astate_menu->items().clear ();
187         }
188
189         pan_astate_menu->items().push_back (MenuElem (_("Manual"), sigc::bind (
190                         sigc::mem_fun (_panner.get(), &Panner::set_automation_state),
191                         (AutoState) Off)));
192         pan_astate_menu->items().push_back (MenuElem (_("Play"), sigc::bind (
193                         sigc::mem_fun (_panner.get(), &Panner::set_automation_state),
194                         (AutoState) Play)));
195         pan_astate_menu->items().push_back (MenuElem (_("Write"), sigc::bind (
196                         sigc::mem_fun (_panner.get(), &Panner::set_automation_state),
197                         (AutoState) Write)));
198         pan_astate_menu->items().push_back (MenuElem (_("Touch"), sigc::bind (
199                         sigc::mem_fun (_panner.get(), &Panner::set_automation_state),
200                         (AutoState) Touch)));
201
202 }
203
204 void
205 PannerUI::build_astyle_menu ()
206 {
207         using namespace Menu_Helpers;
208
209         if (pan_astyle_menu == 0) {
210                 pan_astyle_menu = new Menu;
211                 pan_astyle_menu->set_name ("ArdourContextMenu");
212         } else {
213                 pan_astyle_menu->items().clear();
214         }
215
216         pan_astyle_menu->items().push_back (MenuElem (_("Trim")));
217         pan_astyle_menu->items().push_back (MenuElem (_("Abs")));
218 }
219
220 boost::shared_ptr<PBD::Controllable>
221 PannerUI::get_controllable()
222 {
223         assert (!pan_bars.empty());
224         return pan_bars[0]->get_controllable();
225 }
226
227 bool
228 PannerUI::panning_link_button_press (GdkEventButton*)
229 {
230         return true;
231 }
232
233 bool
234 PannerUI::panning_link_button_release (GdkEventButton*)
235 {
236         if (!ignore_toggle) {
237                 _panner->set_linked (!_panner->linked());
238         }
239         return true;
240 }
241
242 void
243 PannerUI::panning_link_direction_clicked()
244 {
245         switch (_panner->link_direction()) {
246         case Panner::SameDirection:
247                 _panner->set_link_direction (Panner::OppositeDirection);
248                 break;
249         default:
250                 _panner->set_link_direction (Panner::SameDirection);
251                 break;
252         }
253 }
254
255 void
256 PannerUI::update_pan_linkage ()
257 {
258         ENSURE_GUI_THREAD (*this, &PannerUI::update_pan_linkage)
259
260         bool const x = _panner->linked();
261         bool const bx = panning_link_button.get_active();
262
263         if (x != bx) {
264
265                 ignore_toggle = true;
266                 panning_link_button.set_active (x);
267                 ignore_toggle = false;
268         }
269
270         panning_link_direction_button.set_sensitive (x);
271
272         switch (_panner->link_direction()) {
273         case Panner::SameDirection:
274                 panning_link_direction_button.set_image (*(manage (new Image (get_xpm ("forwardblarrow.xpm")))));
275                 break;
276         default:
277                 panning_link_direction_button.set_image (*(manage (new Image (get_xpm("revdblarrow.xpm")))));
278                 break;
279         }
280 }
281
282 void
283 PannerUI::on_size_allocate (Allocation& a)
284 {
285         HBox::on_size_allocate (a);
286 }
287
288 void
289 PannerUI::set_width (Width w)
290 {
291         switch (w) {
292         case Wide:
293                 panning_link_button.set_label (_("link"));
294                 break;
295         case Narrow:
296                 panning_link_button.set_label (_("L"));
297                 break;
298         }
299
300         _width = w;
301 }
302
303
304 PannerUI::~PannerUI ()
305 {
306         for (vector<MonoPanner*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
307                 delete (*i);
308         }
309
310         delete twod_panner;
311         delete big_window;
312         delete pan_menu;
313         delete pan_astyle_menu;
314         delete pan_astate_menu;
315         delete _stereo_panner;
316 }
317
318
319 void
320 PannerUI::panner_changed (void* src)
321 {
322         ENSURE_GUI_THREAD (*this, &PannerUI::panner_changed)
323
324         setup_pan ();
325
326         if (src == this) {
327                 return;
328         }
329
330         switch (_panner->npanners()) {
331         case 0:
332                 panning_link_direction_button.set_sensitive (false);
333                 panning_link_button.set_sensitive (false);
334                 return;
335         case 1:
336                 panning_link_direction_button.set_sensitive (false);
337                 panning_link_button.set_sensitive (false);
338                 break;
339         default:
340                 panning_link_direction_button.set_sensitive (_panner->linked ());
341                 panning_link_button.set_sensitive (true);
342         }
343
344         uint32_t const nouts = _panner->nouts();
345
346         switch (nouts) {
347         case 0:
348         case 1:
349                 /* relax */
350                 break;
351
352         case 2:
353                 break;
354
355         default:
356                 // panner->move_puck (pan_value (pans[0], pans[1]), 0.5);
357                 break;
358         }
359 }
360
361 void
362 PannerUI::update_pan_state ()
363 {
364         /* currently nothing to do */
365         // ENSURE_GUI_THREAD (*this, &PannerUI::update_panner_state)
366 }
367
368 void
369 PannerUI::setup_pan ()
370 {
371         if (!_panner) {
372                 return;
373         }
374
375         uint32_t const nouts = _panner->nouts();
376         uint32_t const npans = _panner->npanners();
377
378         cerr << "Pan setup, outs == " << nouts << " pans = " << npans << endl;
379
380         if (int32_t (nouts) == _current_nouts && int32_t (npans) == _current_npans) {
381                 return;
382         }
383
384         _current_nouts = nouts;
385         _current_npans = npans;
386
387         panning_viewport.remove ();
388
389         delete twod_panner;
390         twod_panner = 0;
391         delete _stereo_panner;
392         _stereo_panner = 0;
393
394         if (nouts == 0 || nouts == 1) {
395
396                 while (!pan_bars.empty()) {
397                         delete pan_bars.back();
398                         pan_bars.pop_back ();
399                 }
400
401                 /* stick something into the panning viewport so that it redraws */
402
403                 EventBox* eb = manage (new EventBox());
404                 panning_viewport.add (*eb);
405
406         } else if (nouts == 2) {
407
408                 vector<Adjustment*>::size_type p;
409
410                 while (!pan_bars.empty()) {
411                         delete pan_bars.back();
412                         pan_bars.pop_back ();
413                 }
414
415                 if (npans == 2) {
416
417                         /* add integrated 2in/2out panner GUI */
418
419                         _stereo_panner = new StereoPanner (_panner->direction_control(), 
420                                                            _panner->width_control());
421                         _stereo_panner->set_size_request (-1, pan_bar_height);
422                         panning_viewport.add (*_stereo_panner);
423
424                 } else {
425                         
426                         /* N-in/2out - just use a set of single-channel panners */
427
428                         while ((p = pan_bars.size()) < npans) {
429                                 
430                                 MonoPanner* mp;
431                                 boost::shared_ptr<AutomationControl> ac = _panner->pan_control (p);
432
433                                 mp = new MonoPanner (_panner->pan_control (p));
434                                 
435                                 mp->StartGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch), 
436                                                                       boost::weak_ptr<AutomationControl> (ac)));
437                                 mp->StopGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch), 
438                                                                      boost::weak_ptr<AutomationControl>(ac)));
439
440                                 mp->signal_button_release_event().connect
441                                         (sigc::bind (sigc::mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) p));
442                                 
443                                 mp->set_size_request (-1, pan_bar_height);
444                                 
445                                 pan_bars.push_back (mp);
446                                 pan_bar_packer.pack_start (*mp, false, false);
447                         }
448                         
449                         /* now that we actually have the pan bars,
450                            set their sensitivity based on current
451                            automation state.
452                         */
453                         
454                         update_pan_sensitive ();
455                         panning_viewport.add (pan_bar_packer);
456                 }
457
458
459         } else {
460
461                 if (!twod_panner) {
462                         twod_panner = new Panner2d (_panner, 61);
463                         twod_panner->set_name ("MixerPanZone");
464                         twod_panner->show ();
465
466                         twod_panner->signal_button_press_event().connect
467                                 (sigc::bind (sigc::mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) 0), false);
468                 }
469
470                 update_pan_sensitive ();
471                 twod_panner->reset (npans);
472                 if (big_window) {
473                         big_window->reset (npans);
474                 }
475                 twod_panner->set_size_request (-1, 61);
476
477                 /* and finally, add it to the panner frame */
478
479                 panning_viewport.add (*twod_panner);
480         }
481
482         panning_viewport.show_all ();
483 }
484
485 void
486 PannerUI::start_touch (boost::weak_ptr<AutomationControl> wac)
487 {
488         boost::shared_ptr<AutomationControl> ac = wac.lock();
489         if (!ac) {
490                 return;
491         }
492         ac->start_touch (ac->session().transport_frame());
493 }
494
495 void
496 PannerUI::stop_touch (boost::weak_ptr<AutomationControl> wac)
497 {
498         boost::shared_ptr<AutomationControl> ac = wac.lock();
499         if (!ac) {
500                 return;
501         }
502         ac->stop_touch (false, ac->session().transport_frame());
503 }
504
505 bool
506 PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
507 {
508         switch (ev->button) {
509         case 1:
510                 if (twod_panner && ev->type == GDK_2BUTTON_PRESS) {
511                         if (!big_window) {
512                                 big_window = new Panner2dWindow (_panner, 400, _panner->npanners());
513                         }
514                         big_window->show ();
515                         return true;
516                 }
517                 break;
518
519         case 3:
520                 if (pan_menu == 0) {
521                         pan_menu = manage (new Menu);
522                         pan_menu->set_name ("ArdourContextMenu");
523                 }
524                 build_pan_menu (which);
525                 pan_menu->popup (1, ev->time);
526                 return true;
527                 break;
528         default:
529                 return false;
530         }
531
532         return false; // what's wrong with gcc?
533 }
534
535 void
536 PannerUI::build_pan_menu (uint32_t which)
537 {
538         using namespace Menu_Helpers;
539         MenuList& items (pan_menu->items());
540
541         items.clear ();
542
543         items.push_back (CheckMenuElem (_("Mute")));
544
545         /* set state first, connect second */
546
547         (dynamic_cast<CheckMenuItem*> (&items.back()))->set_active (_panner->streampanner(which).muted());
548         (dynamic_cast<CheckMenuItem*> (&items.back()))->signal_toggled().connect
549                 (sigc::bind (sigc::mem_fun(*this, &PannerUI::pan_mute), which));
550
551         items.push_back (CheckMenuElem (_("Bypass"), sigc::mem_fun(*this, &PannerUI::pan_bypass_toggle)));
552         bypass_menu_item = static_cast<CheckMenuItem*> (&items.back());
553
554         /* set state first, connect second */
555
556         bypass_menu_item->set_active (_panner->bypassed());
557         bypass_menu_item->signal_toggled().connect (sigc::mem_fun(*this, &PannerUI::pan_bypass_toggle));
558
559         items.push_back (MenuElem (_("Reset"), sigc::bind (sigc::mem_fun (*this, &PannerUI::pan_reset), which)));
560         items.push_back (SeparatorElem());
561         items.push_back (MenuElem (_("Reset all"), sigc::mem_fun (*this, &PannerUI::pan_reset_all)));
562 }
563
564 void
565 PannerUI::pan_mute (uint32_t which)
566 {
567         StreamPanner& sp = _panner->streampanner(which);
568         sp.set_muted (!sp.muted());
569 }
570
571 void
572 PannerUI::pan_bypass_toggle ()
573 {
574         if (bypass_menu_item && (_panner->bypassed() != bypass_menu_item->get_active())) {
575                 _panner->set_bypassed (!_panner->bypassed());
576         }
577 }
578
579 void
580 PannerUI::pan_reset (uint32_t which)
581 {
582         _panner->reset_streampanner (which);
583 }
584
585 void
586 PannerUI::pan_reset_all ()
587 {
588         _panner->reset_to_default ();
589 }
590
591 void
592 PannerUI::effective_pan_display ()
593 {
594         if (_stereo_panner) {
595                 _stereo_panner->queue_draw ();
596         } else if (twod_panner) {
597                 twod_panner->queue_draw ();
598         } else {
599                 for (vector<MonoPanner*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
600                         (*i)->queue_draw ();
601                 }
602         }
603 }
604
605 void
606 PannerUI::update_pan_sensitive ()
607 {
608         bool const sensitive = !(_panner->mono()) && !(_panner->automation_state() & Play);
609
610         switch (_panner->nouts()) {
611         case 0:
612         case 1:
613                 break;
614         case 2:
615                 for (vector<MonoPanner*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
616                         (*i)->set_sensitive (sensitive);
617                 }
618                 break;
619         default:
620                 if (twod_panner) {
621                         twod_panner->set_sensitive (sensitive);
622                 }
623                 if (big_window) {
624                         big_window->set_sensitive (sensitive);
625                 }
626                 break;
627         }
628 }
629
630 gint
631 PannerUI::pan_automation_state_button_event (GdkEventButton *ev)
632 {
633         using namespace Menu_Helpers;
634
635         if (ev->type == GDK_BUTTON_RELEASE) {
636                 return TRUE;
637         }
638
639         switch (ev->button) {
640         case 1:
641                 if (pan_astate_menu == 0) {
642                         build_astate_menu ();
643                 }
644                 pan_astate_menu->popup (1, ev->time);
645                 break;
646         default:
647                 break;
648         }
649
650         return TRUE;
651 }
652
653 gint
654 PannerUI::pan_automation_style_button_event (GdkEventButton *ev)
655 {
656         if (ev->type == GDK_BUTTON_RELEASE) {
657                 return TRUE;
658         }
659
660         switch (ev->button) {
661         case 1:
662                 if (pan_astyle_menu == 0) {
663                         build_astyle_menu ();
664                 }
665                 pan_astyle_menu->popup (1, ev->time);
666                 break;
667         default:
668                 break;
669         }
670         return TRUE;
671 }
672
673 void
674 PannerUI::pan_automation_style_changed ()
675 {
676         ENSURE_GUI_THREAD (*this, &PannerUI::pan_automation_style_changed)
677
678         switch (_width) {
679         case Wide:
680                 pan_automation_style_button.set_label (astyle_string(_panner->automation_style()));
681                 break;
682         case Narrow:
683                 pan_automation_style_button.set_label (short_astyle_string(_panner->automation_style()));
684                 break;
685         }
686 }
687
688 void
689 PannerUI::pan_automation_state_changed ()
690 {
691         ENSURE_GUI_THREAD (*this, &PannerUI::pan_automation_state_changed)
692
693         bool x;
694
695         switch (_width) {
696         case Wide:
697           pan_automation_state_button.set_label (astate_string(_panner->automation_state()));
698                 break;
699         case Narrow:
700           pan_automation_state_button.set_label (short_astate_string(_panner->automation_state()));
701                 break;
702         }
703
704         /* when creating a new session, we get to create busses (and
705            sometimes tracks) with no outputs by the time they get
706            here.
707         */
708
709         if (_panner->empty()) {
710                 return;
711         }
712
713         x = (_panner->streampanner(0).pan_control()->alist()->automation_state() != Off);
714
715         if (pan_automation_state_button.get_active() != x) {
716         ignore_toggle = true;
717                 pan_automation_state_button.set_active (x);
718                 ignore_toggle = false;
719         }
720
721         update_pan_sensitive ();
722
723         /* start watching automation so that things move */
724
725         pan_watching.disconnect();
726
727         if (x) {
728                 pan_watching = ARDOUR_UI::RapidScreenUpdate.connect (sigc::mem_fun (*this, &PannerUI::effective_pan_display));
729         }
730 }
731
732 string
733 PannerUI::astate_string (AutoState state)
734 {
735         return _astate_string (state, false);
736 }
737
738 string
739 PannerUI::short_astate_string (AutoState state)
740 {
741         return _astate_string (state, true);
742 }
743
744 string
745 PannerUI::_astate_string (AutoState state, bool shrt)
746 {
747         string sstr;
748
749         switch (state) {
750         case Off:
751                 sstr = (shrt ? "M" : _("M"));
752                 break;
753         case Play:
754                 sstr = (shrt ? "P" : _("P"));
755                 break;
756         case Touch:
757                 sstr = (shrt ? "T" : _("T"));
758                 break;
759         case Write:
760                 sstr = (shrt ? "W" : _("W"));
761                 break;
762         }
763
764         return sstr;
765 }
766
767 string
768 PannerUI::astyle_string (AutoStyle style)
769 {
770         return _astyle_string (style, false);
771 }
772
773 string
774 PannerUI::short_astyle_string (AutoStyle style)
775 {
776         return _astyle_string (style, true);
777 }
778
779 string
780 PannerUI::_astyle_string (AutoStyle style, bool shrt)
781 {
782         if (style & Trim) {
783                 return _("Trim");
784         } else {
785                 /* XXX it might different in different languages */
786
787                 return (shrt ? _("Abs") : _("Abs"));
788         }
789 }
790
791 void
792 PannerUI::set_mono (bool yn)
793 {
794         _panner->set_mono (yn);
795         update_pan_sensitive ();
796 }
797
798 void
799 PannerUI::show_width ()
800 {
801 }
802
803 void
804 PannerUI::width_adjusted ()
805 {
806 }
807
808 void
809 PannerUI::show_position ()
810 {
811 }
812
813 void
814 PannerUI::position_adjusted ()
815 {
816 }