2 Copyright (C) 2000 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 "gtk2ardour-config.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/xml++.h"
31 #include "pbd/failed_constructor.h"
33 #include <gtkmm/widget.h>
34 #include <gtkmm/box.h>
35 #include <gtkmm2ext/click_box.h>
36 #include <gtkmm2ext/fastmeter.h>
37 #include <gtkmm2ext/barcontroller.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/doi.h>
40 #include <gtkmm2ext/slider_controller.h>
41 #include <gtkmm2ext/application.h>
43 #include "midi++/manager.h"
45 #include "ardour/session.h"
46 #include "ardour/plugin.h"
47 #include "ardour/plugin_insert.h"
48 #include "ardour/ladspa_plugin.h"
50 #include "ardour/vst_plugin.h"
51 #include "vst_pluginui.h"
54 #include "ardour/lv2_plugin.h"
55 #include "lv2_plugin_ui.h"
60 #include "ardour_dialog.h"
61 #include "ardour_ui.h"
63 #include "plugin_ui.h"
65 #include "gui_thread.h"
66 #include "public_editor.h"
68 #include "latency_gui.h"
69 #include "plugin_eq_gui.h"
70 #include "new_plugin_preset_dialog.h"
75 using namespace ARDOUR
;
77 using namespace Gtkmm2ext
;
80 PluginUIWindow::PluginUIWindow (
82 boost::shared_ptr
<PluginInsert
> insert
,
87 , _keyboard_focused (false)
89 bool have_gui
= false;
91 Label
* label
= manage (new Label());
92 label
->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
94 std::cout
<< "SHOW UI " << insert
->plugin()->unique_id()
95 << " editor: " << editor
<< std::endl
;
96 if (editor
&& insert
->plugin()->has_editor()) {
97 switch (insert
->type()) {
99 have_gui
= create_vst_editor (insert
);
102 case ARDOUR::AudioUnit
:
103 have_gui
= create_audiounit_editor (insert
);
107 error
<< _("Eh? LADSPA plugins don't have editors!") << endmsg
;
111 have_gui
= create_lv2_editor (insert
);
116 error
<< _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
119 error
<< _("unknown type of editor-supplying plugin")
122 throw failed_constructor ();
128 GenericPluginUI
* pu
= new GenericPluginUI (insert
, scrollable
);
131 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
133 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME
);
135 signal_map_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::start_updating
));
136 signal_unmap_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::stop_updating
));
139 // set_position (Gtk::WIN_POS_MOUSE);
140 set_name ("PluginEditor");
141 add_events (Gdk::KEY_PRESS_MASK
|Gdk::KEY_RELEASE_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
);
143 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it
), reinterpret_cast<Window
*> (this)), false);
144 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away
, this), gui_context());
146 gint h
= _pluginui
->get_preferred_height ();
147 gint w
= _pluginui
->get_preferred_width ();
150 if (h
> 600) h
= 600;
151 if (w
> 600) w
= 600;
158 set_default_size (w
, h
);
161 PluginUIWindow::~PluginUIWindow ()
167 PluginUIWindow::set_parent (Gtk::Window
* win
)
173 PluginUIWindow::on_map ()
176 set_keep_above (true);
180 PluginUIWindow::on_enter_notify_event (GdkEventCrossing
*ev
)
182 Keyboard::the_keyboard().enter_window (ev
, this);
187 PluginUIWindow::on_leave_notify_event (GdkEventCrossing
*ev
)
189 Keyboard::the_keyboard().leave_window (ev
, this);
194 PluginUIWindow::on_focus_in_event (GdkEventFocus
*ev
)
196 Window::on_focus_in_event (ev
);
197 //Keyboard::the_keyboard().magic_widget_grab_focus ();
202 PluginUIWindow::on_focus_out_event (GdkEventFocus
*ev
)
204 Window::on_focus_out_event (ev
);
205 //Keyboard::the_keyboard().magic_widget_drop_focus ();
210 PluginUIWindow::on_show ()
212 set_role("plugin_ui");
215 _pluginui
->update_preset_list ();
216 _pluginui
->update_preset ();
220 if (_pluginui
->on_window_show (_title
)) {
226 // set_transient_for (*parent);
231 PluginUIWindow::on_hide ()
236 _pluginui
->on_window_hide ();
241 PluginUIWindow::set_title(const std::string
& title
)
243 Gtk::Window::set_title(title
);
249 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
> insert
)
251 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
>)
258 boost::shared_ptr
<VSTPlugin
> vp
;
260 if ((vp
= boost::dynamic_pointer_cast
<VSTPlugin
> (insert
->plugin())) == 0) {
261 error
<< _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
263 throw failed_constructor ();
265 VSTPluginUI
* vpu
= new VSTPluginUI (insert
, vp
);
268 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
270 vpu
->package (*this);
279 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
> insert
)
281 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
>)
288 _pluginui
= create_au_gui (insert
, &box
);
289 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
292 Application::instance()->ActivationChanged
.connect (mem_fun (*this, &PluginUIWindow::app_activated
));
300 PluginUIWindow::app_activated (bool yn
)
302 PluginUIWindow::app_activated (bool)
309 _pluginui
->activate ();
314 was_visible
= is_visible();
316 _pluginui
->deactivate ();
323 PluginUIWindow::create_lv2_editor(boost::shared_ptr
<PluginInsert
> insert
)
325 #if defined(HAVE_SLV2) || defined(HAVE_SUIL)
326 boost::shared_ptr
<LV2Plugin
> vp
;
328 if ((vp
= boost::dynamic_pointer_cast
<LV2Plugin
> (insert
->plugin())) == 0) {
329 error
<< _("create_lv2_editor called on non-LV2 plugin") << endmsg
;
330 throw failed_constructor ();
332 LV2PluginUI
* lpu
= new LV2PluginUI (insert
, vp
);
335 lpu
->package (*this);
345 PluginUIWindow::keyboard_focused (bool yn
)
347 _keyboard_focused
= yn
;
351 PluginUIWindow::on_key_press_event (GdkEventKey
* event
)
353 if (_keyboard_focused
) {
355 if (_pluginui
->non_gtk_gui()) {
356 _pluginui
->forward_key_event (event
);
358 return relay_key_press (event
, this);
363 /* for us to be getting key press events, there really
364 MUST be a _pluginui, but just to be safe, check ...
368 if (_pluginui
->non_gtk_gui()) {
369 /* pass editor window as the window for the event
370 to be handled in, not this one, because there are
371 no widgets in this window that we want to have
374 return relay_key_press (event
, &PublicEditor::instance());
376 return relay_key_press (event
, this);
385 PluginUIWindow::on_key_release_event (GdkEventKey
*event
)
387 if (_keyboard_focused
) {
389 if (_pluginui
->non_gtk_gui()) {
390 _pluginui
->forward_key_event (event
);
401 PluginUIWindow::plugin_going_away ()
403 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away
)
406 _pluginui
->stop_updating(0);
409 death_connection
.disconnect ();
411 delete_when_idle (this);
414 PlugUIBase::PlugUIBase (boost::shared_ptr
<PluginInsert
> pi
)
416 , plugin (insert
->plugin())
417 , add_button (_("Add"))
418 , save_button (_("Save"))
419 , delete_button (_("Delete"))
420 , bypass_button (_("Bypass"))
423 , plugin_analysis_expander (_("Plugin analysis"))
426 _preset_combo
.set_size_request (100, -1);
427 _preset_modified
.set_size_request (16, -1);
428 _preset_combo
.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected
));
431 _preset_box
.pack_start (_preset_combo
);
432 _preset_box
.pack_start (_preset_modified
);
434 update_preset_list ();
437 add_button
.set_name ("PluginAddButton");
438 add_button
.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting
));
440 save_button
.set_name ("PluginSaveButton");
441 save_button
.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting
));
443 delete_button
.set_name ("PluginDeleteButton");
444 delete_button
.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting
));
446 insert
->ActiveChanged
.connect (active_connection
, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed
, this, boost::weak_ptr
<Processor
>(insert
)), gui_context());
448 bypass_button
.set_active (!pi
->active());
450 bypass_button
.set_name ("PluginBypassButton");
451 bypass_button
.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled
));
452 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
454 focus_button
.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled
));
455 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
457 /* these images are not managed, so that we can remove them at will */
459 focus_out_image
= new Image (get_icon (X_("computer_keyboard")));
460 focus_in_image
= new Image (get_icon (X_("computer_keyboard_active")));
462 focus_button
.add (*focus_out_image
);
464 ARDOUR_UI::instance()->set_tip (focus_button
, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME
));
465 ARDOUR_UI::instance()->set_tip (bypass_button
, _("Click to enable/disable this plugin"));
467 plugin_analysis_expander
.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis
));
468 plugin_analysis_expander
.set_expanded(false);
470 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away
, this), gui_context());
472 plugin
->PresetAdded
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed
, this), gui_context ());
473 plugin
->PresetRemoved
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed
, this), gui_context ());
474 plugin
->PresetLoaded
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset
, this), gui_context ());
475 plugin
->ParameterChanged
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed
, this, _1
, _2
), gui_context ());
478 PlugUIBase::~PlugUIBase()
485 PlugUIBase::plugin_going_away ()
487 /* drop references to the plugin/insert */
490 death_connection
.disconnect ();
494 PlugUIBase::set_latency_label ()
496 framecnt_t
const l
= insert
->effective_latency ();
497 framecnt_t
const sr
= insert
->session().frame_rate ();
502 t
= string_compose (_("latency (%1 samples)"), l
);
504 t
= string_compose (_("latency (%1 ms)"), (float) l
/ ((float) sr
/ 1000.0f
));
507 latency_label
.set_text (t
);
511 PlugUIBase::latency_button_clicked ()
514 latency_gui
= new LatencyGUI (*(insert
.get()), insert
->session().frame_rate(), insert
->session().get_block_size());
515 latency_dialog
= new ArdourDialog (_("Edit Latency"), false, false);
516 latency_dialog
->get_vbox()->pack_start (*latency_gui
);
517 latency_dialog
->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label
));
520 latency_dialog
->show_all ();
524 PlugUIBase::processor_active_changed (boost::weak_ptr
<Processor
> weak_p
)
526 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed
, weak_p
)
527 boost::shared_ptr
<Processor
> p (weak_p
);
529 bypass_button
.set_active (!p
->active());
534 PlugUIBase::preset_selected ()
536 if (_no_load_preset
) {
540 if (_preset_combo
.get_active_text().length() > 0) {
541 const Plugin::PresetRecord
* pr
= plugin
->preset_by_label (_preset_combo
.get_active_text());
543 plugin
->load_preset (*pr
);
545 warning
<< string_compose(_("Plugin preset %1 not found"),
546 _preset_combo
.get_active_text()) << endmsg
;
552 PlugUIBase::add_plugin_setting ()
554 NewPluginPresetDialog
d (plugin
);
557 case Gtk::RESPONSE_ACCEPT
:
558 if (d
.name().empty()) {
563 plugin
->remove_preset (d
.name ());
566 Plugin::PresetRecord
const r
= plugin
->save_preset (d
.name());
567 if (!r
.uri
.empty ()) {
568 plugin
->load_preset (r
);
575 PlugUIBase::save_plugin_setting ()
577 string
const name
= _preset_combo
.get_active_text ();
578 plugin
->remove_preset (name
);
579 Plugin::PresetRecord
const r
= plugin
->save_preset (name
);
580 if (!r
.uri
.empty ()) {
581 plugin
->load_preset (r
);
586 PlugUIBase::delete_plugin_setting ()
588 plugin
->remove_preset (_preset_combo
.get_active_text ());
592 PlugUIBase::bypass_toggled ()
596 if ((x
= bypass_button
.get_active()) == insert
->active()) {
598 insert
->deactivate ();
606 PlugUIBase::focus_toggled (GdkEventButton
*)
608 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
609 Keyboard::the_keyboard().magic_widget_drop_focus();
610 focus_button
.remove ();
611 focus_button
.add (*focus_out_image
);
612 focus_out_image
->show ();
613 ARDOUR_UI::instance()->set_tip (focus_button
, string_compose (_("Click to allow the plugin to receive keyboard events that %1 would normally use as a shortcut"), PROGRAM_NAME
));
614 KeyboardFocused (false);
616 Keyboard::the_keyboard().magic_widget_grab_focus();
617 focus_button
.remove ();
618 focus_button
.add (*focus_in_image
);
619 focus_in_image
->show ();
620 ARDOUR_UI::instance()->set_tip (focus_button
, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME
));
621 KeyboardFocused (true);
628 PlugUIBase::toggle_plugin_analysis()
630 if (plugin_analysis_expander
.get_expanded() &&
631 !plugin_analysis_expander
.get_child()) {
634 eqgui
= new PluginEqGui (insert
);
637 Gtk::Window
*toplevel
= (Gtk::Window
*) plugin_analysis_expander
.get_ancestor (GTK_TYPE_WINDOW
);
640 toplevel
->get_size (pre_eq_size
.width
, pre_eq_size
.height
);
643 plugin_analysis_expander
.add (*eqgui
);
644 plugin_analysis_expander
.show_all ();
645 eqgui
->start_listening ();
648 if (!plugin_analysis_expander
.get_expanded()) {
650 // Hide & remove from expander
653 eqgui
->stop_listening ();
654 plugin_analysis_expander
.remove();
656 Gtk::Window
*toplevel
= (Gtk::Window
*) plugin_analysis_expander
.get_ancestor (GTK_TYPE_WINDOW
);
659 toplevel
->resize (pre_eq_size
.width
, pre_eq_size
.height
);
665 PlugUIBase::update_preset_list ()
667 vector
<string
> preset_labels
;
668 vector
<ARDOUR::Plugin::PresetRecord
> presets
= plugin
->get_presets();
672 for (vector
<ARDOUR::Plugin::PresetRecord
>::const_iterator i
= presets
.begin(); i
!= presets
.end(); ++i
) {
673 preset_labels
.push_back (i
->label
);
676 set_popdown_strings (_preset_combo
, preset_labels
);
682 PlugUIBase::update_preset ()
684 Plugin::PresetRecord p
= plugin
->last_preset();
687 _preset_combo
.set_active_text (p
.label
);
690 save_button
.set_sensitive (!p
.uri
.empty() && p
.user
);
691 delete_button
.set_sensitive (!p
.uri
.empty() && p
.user
);
693 update_preset_modified ();
697 PlugUIBase::update_preset_modified ()
699 if (plugin
->last_preset().uri
.empty()) {
700 _preset_modified
.set_text ("");
704 bool const c
= plugin
->parameter_changed_since_last_preset ();
705 if (_preset_modified
.get_text().empty() == c
) {
706 _preset_modified
.set_text (c
? "*" : "");
711 PlugUIBase::parameter_changed (uint32_t, float)
713 update_preset_modified ();
717 PlugUIBase::preset_added_or_removed ()
719 /* Update both the list and the currently-displayed preset */
720 update_preset_list ();