2 Copyright (C) 2004 Paul Davis
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.
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.
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.
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"
28 #include "ardour_ui.h"
29 #include "panner_ui.h"
32 #include "gui_thread.h"
33 #include "stereo_panner.h"
34 #include "mono_panner.h"
36 #include "ardour/delivery.h"
37 #include "ardour/session.h"
38 #include "ardour/panner.h"
39 #include "ardour/pannable.h"
40 #include "ardour/panner_shell.h"
41 #include "ardour/route.h"
46 using namespace ARDOUR
;
48 using namespace Gtkmm2ext
;
51 const int PannerUI::pan_bar_height
= 35;
53 PannerUI::PannerUI (Session
* s
)
56 , pan_automation_style_button ("")
57 , pan_automation_state_button ("")
61 ignore_toggle
= false;
65 in_pan_update
= false;
67 _ignore_width_change
= false;
68 _ignore_position_change
= false;
70 pan_automation_style_button
.set_name ("MixerAutomationModeButton");
71 pan_automation_state_button
.set_name ("MixerAutomationPlaybackButton");
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"));
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);
79 pan_automation_style_button
.unset_flags (Gtk::CAN_FOCUS
);
80 pan_automation_state_button
.unset_flags (Gtk::CAN_FOCUS
);
82 pan_automation_style_button
.signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_automation_style_button_event
), false);
83 pan_automation_state_button
.signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_automation_state_button_event
), false);
85 pan_vbox
.set_spacing (2);
86 pack_start (pan_vbox
, true, true);
95 PannerUI::set_panner (boost::shared_ptr
<PannerShell
> ps
, boost::shared_ptr
<Panner
> p
)
97 /* note that the panshell might not change here (i.e. ps == _panshell)
100 connections
.drop_connections ();
102 delete pan_astyle_menu
;
105 delete pan_astate_menu
;
114 delete _stereo_panner
;
121 _panshell
->Changed
.connect (connections
, invalidator (*this), boost::bind (&PannerUI::panshell_changed
, this), gui_context());
122 _panner
->StateChanged
.connect (connections
, invalidator (*this), boost::bind (&PannerUI::update_pan_state
, this), gui_context());
124 /* new panner object, force complete reset of panner GUI
131 update_pan_sensitive ();
132 pan_automation_state_changed ();
136 PannerUI::build_astate_menu ()
138 using namespace Menu_Helpers
;
140 if (pan_astate_menu
== 0) {
141 pan_astate_menu
= new Menu
;
142 pan_astate_menu
->set_name ("ArdourContextMenu");
144 pan_astate_menu
->items().clear ();
147 pan_astate_menu
->items().push_back (MenuElem (_("Manual"), sigc::bind (
148 sigc::mem_fun (_panner
.get(), &Panner::set_automation_state
),
150 pan_astate_menu
->items().push_back (MenuElem (_("Play"), sigc::bind (
151 sigc::mem_fun (_panner
.get(), &Panner::set_automation_state
),
153 pan_astate_menu
->items().push_back (MenuElem (_("Write"), sigc::bind (
154 sigc::mem_fun (_panner
.get(), &Panner::set_automation_state
),
155 (AutoState
) Write
)));
156 pan_astate_menu
->items().push_back (MenuElem (_("Touch"), sigc::bind (
157 sigc::mem_fun (_panner
.get(), &Panner::set_automation_state
),
158 (AutoState
) Touch
)));
163 PannerUI::build_astyle_menu ()
165 using namespace Menu_Helpers
;
167 if (pan_astyle_menu
== 0) {
168 pan_astyle_menu
= new Menu
;
169 pan_astyle_menu
->set_name ("ArdourContextMenu");
171 pan_astyle_menu
->items().clear();
174 pan_astyle_menu
->items().push_back (MenuElem (_("Trim")));
175 pan_astyle_menu
->items().push_back (MenuElem (_("Abs")));
178 boost::shared_ptr
<PBD::Controllable
>
179 PannerUI::get_controllable()
181 assert (!pan_bars
.empty());
182 return pan_bars
[0]->get_controllable();
186 PannerUI::on_size_allocate (Allocation
& a
)
188 HBox::on_size_allocate (a
);
192 PannerUI::set_width (Width w
)
197 PannerUI::~PannerUI ()
199 for (vector
<MonoPanner
*>::iterator i
= pan_bars
.begin(); i
!= pan_bars
.end(); ++i
) {
206 delete pan_astyle_menu
;
207 delete pan_astate_menu
;
208 delete _stereo_panner
;
213 PannerUI::panshell_changed ()
215 set_panner (_panshell
, _panshell
->panner());
220 PannerUI::update_pan_state ()
222 /* currently nothing to do */
226 PannerUI::setup_pan ()
232 uint32_t const nouts
= _panner
->out().n_audio();
233 uint32_t const nins
= _panner
->in().n_audio();
235 if (int32_t (nouts
) == _current_nouts
&& int32_t (nins
) == _current_nins
) {
239 _current_nins
= nins
;
240 _current_nouts
= nouts
;
242 container_clear (pan_vbox
);
246 delete _stereo_panner
;
249 if (nouts
== 0 || nouts
== 1) {
251 delete _stereo_panner
;
254 /* stick something into the panning viewport so that it redraws */
256 EventBox
* eb
= manage (new EventBox());
257 pan_vbox
.pack_start (*eb
, false, false);
259 } else if (nouts
== 2) {
263 /* add integrated 2in/2out panner GUI */
265 boost::shared_ptr
<Pannable
> pannable
= _panner
->pannable();
267 _stereo_panner
= new StereoPanner (_panner
);
268 _stereo_panner
->set_size_request (-1, pan_bar_height
);
269 pan_vbox
.pack_start (*_stereo_panner
, false, false);
271 boost::shared_ptr
<AutomationControl
> ac
;
273 ac
= pannable
->pan_azimuth_control
;
274 _stereo_panner
->StartPositionGesture
.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch
),
275 boost::weak_ptr
<AutomationControl
> (ac
)));
276 _stereo_panner
->StopPositionGesture
.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch
),
277 boost::weak_ptr
<AutomationControl
>(ac
)));
279 ac
= pannable
->pan_width_control
;
280 _stereo_panner
->StartWidthGesture
.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch
),
281 boost::weak_ptr
<AutomationControl
> (ac
)));
282 _stereo_panner
->StopWidthGesture
.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch
),
283 boost::weak_ptr
<AutomationControl
>(ac
)));
285 } else if (nins
== 1) {
289 boost::shared_ptr
<Pannable
> pannable
= _panner
->pannable();
290 boost::shared_ptr
<AutomationControl
> ac
= pannable
->pan_azimuth_control
;
292 mp
= new MonoPanner (ac
);
294 mp
->StartGesture
.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch
),
295 boost::weak_ptr
<AutomationControl
> (ac
)));
296 mp
->StopGesture
.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch
),
297 boost::weak_ptr
<AutomationControl
>(ac
)));
299 mp
->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event
));
301 mp
->set_size_request (-1, pan_bar_height
);
303 update_pan_sensitive ();
304 pan_vbox
.pack_start (*mp
, false, false);
307 warning
<< string_compose (_("No panner user interface is currently available for %1-in/2out tracks/busses"),
315 twod_panner
= new Panner2d (_panner
, 61);
316 twod_panner
->set_name ("MixerPanZone");
317 twod_panner
->show ();
318 twod_panner
->signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event
), false);
321 update_pan_sensitive ();
322 twod_panner
->reset (nins
);
324 big_window
->reset (nins
);
326 twod_panner
->set_size_request (-1, 61);
328 /* and finally, add it to the panner frame */
330 pan_vbox
.pack_start (*twod_panner
, false, false);
333 pan_vbox
.show_all ();
337 PannerUI::start_touch (boost::weak_ptr
<AutomationControl
> wac
)
339 boost::shared_ptr
<AutomationControl
> ac
= wac
.lock();
343 ac
->start_touch (ac
->session().transport_frame());
347 PannerUI::stop_touch (boost::weak_ptr
<AutomationControl
> wac
)
349 boost::shared_ptr
<AutomationControl
> ac
= wac
.lock();
353 ac
->stop_touch (false, ac
->session().transport_frame());
357 PannerUI::pan_button_event (GdkEventButton
* ev
)
359 switch (ev
->button
) {
361 if (twod_panner
&& ev
->type
== GDK_2BUTTON_PRESS
) {
363 big_window
= new Panner2dWindow (_panner
, 400, _panner
->in().n_audio());
372 pan_menu
= manage (new Menu
);
373 pan_menu
->set_name ("ArdourContextMenu");
376 pan_menu
->popup (1, ev
->time
);
383 return false; // what's wrong with gcc?
387 PannerUI::build_pan_menu ()
389 using namespace Menu_Helpers
;
390 MenuList
& items (pan_menu
->items());
394 items
.push_back (CheckMenuElem (_("Bypass"), sigc::mem_fun(*this, &PannerUI::pan_bypass_toggle
)));
395 bypass_menu_item
= static_cast<CheckMenuItem
*> (&items
.back());
397 /* set state first, connect second */
399 bypass_menu_item
->set_active (_panner
->bypassed());
400 bypass_menu_item
->signal_toggled().connect (sigc::mem_fun(*this, &PannerUI::pan_bypass_toggle
));
402 items
.push_back (MenuElem (_("Reset"), sigc::mem_fun (*this, &PannerUI::pan_reset
)));
406 PannerUI::pan_bypass_toggle ()
408 if (bypass_menu_item
&& (_panner
->bypassed() != bypass_menu_item
->get_active())) {
409 _panner
->set_bypassed (!_panner
->bypassed());
414 PannerUI::pan_reset ()
420 PannerUI::effective_pan_display ()
422 if (_stereo_panner
) {
423 _stereo_panner
->queue_draw ();
424 } else if (twod_panner
) {
425 twod_panner
->queue_draw ();
427 for (vector
<MonoPanner
*>::iterator i
= pan_bars
.begin(); i
!= pan_bars
.end(); ++i
) {
434 PannerUI::update_pan_sensitive ()
436 bool const sensitive
= !(_panner
->pannable()->automation_state() & Play
);
439 pan_vbox
.set_sensitive (sensitive
);
442 big_window
->set_sensitive (sensitive
);
447 PannerUI::pan_automation_state_button_event (GdkEventButton
*ev
)
449 using namespace Menu_Helpers
;
451 if (ev
->type
== GDK_BUTTON_RELEASE
) {
455 switch (ev
->button
) {
457 if (pan_astate_menu
== 0) {
458 build_astate_menu ();
460 pan_astate_menu
->popup (1, ev
->time
);
470 PannerUI::pan_automation_style_button_event (GdkEventButton
*ev
)
472 if (ev
->type
== GDK_BUTTON_RELEASE
) {
476 switch (ev
->button
) {
478 if (pan_astyle_menu
== 0) {
479 build_astyle_menu ();
481 pan_astyle_menu
->popup (1, ev
->time
);
490 PannerUI::pan_automation_style_changed ()
492 ENSURE_GUI_THREAD (*this, &PannerUI::pan_automation_style_changed
)
496 pan_automation_style_button
.set_label (astyle_string(_panner
->automation_style()));
499 pan_automation_style_button
.set_label (short_astyle_string(_panner
->automation_style()));
505 PannerUI::pan_automation_state_changed ()
507 boost::shared_ptr
<Pannable
> pannable (_panner
->pannable());
511 pan_automation_state_button
.set_label (astate_string(pannable
->automation_state()));
514 pan_automation_state_button
.set_label (short_astate_string(pannable
->automation_state()));
518 bool x
= (pannable
->automation_state() != Off
);
520 if (pan_automation_state_button
.get_active() != x
) {
521 ignore_toggle
= true;
522 pan_automation_state_button
.set_active (x
);
523 ignore_toggle
= false;
526 update_pan_sensitive ();
528 /* start watching automation so that things move */
530 pan_watching
.disconnect();
533 pan_watching
= ARDOUR_UI::RapidScreenUpdate
.connect (sigc::mem_fun (*this, &PannerUI::effective_pan_display
));
538 PannerUI::astate_string (AutoState state
)
540 return _astate_string (state
, false);
544 PannerUI::short_astate_string (AutoState state
)
546 return _astate_string (state
, true);
550 PannerUI::_astate_string (AutoState state
, bool shrt
)
556 sstr
= (shrt
? "M" : _("M"));
559 sstr
= (shrt
? "P" : _("P"));
562 sstr
= (shrt
? "T" : _("T"));
565 sstr
= (shrt
? "W" : _("W"));
573 PannerUI::astyle_string (AutoStyle style
)
575 return _astyle_string (style
, false);
579 PannerUI::short_astyle_string (AutoStyle style
)
581 return _astyle_string (style
, true);
585 PannerUI::_astyle_string (AutoStyle style
, bool shrt
)
590 /* XXX it might different in different languages */
592 return (shrt
? _("Abs") : _("Abs"));
597 PannerUI::show_width ()
602 PannerUI::width_adjusted ()
607 PannerUI::show_position ()
612 PannerUI::position_adjusted ()