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 (Gtk::Window
* win
, boost::shared_ptr
<PluginInsert
> insert
, bool scrollable
)
83 , _keyboard_focused (false)
85 bool have_gui
= false;
87 Label
* label
= manage (new Label());
88 label
->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
90 if (insert
->plugin()->has_editor()) {
91 switch (insert
->type()) {
93 have_gui
= create_vst_editor (insert
);
96 case ARDOUR::AudioUnit
:
97 have_gui
= create_audiounit_editor (insert
);
101 error
<< _("Eh? LADSPA plugins don't have editors!") << endmsg
;
105 have_gui
= create_lv2_editor (insert
);
110 error
<< _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
113 error
<< _("unknown type of editor-supplying plugin")
116 throw failed_constructor ();
123 GenericPluginUI
* pu
= new GenericPluginUI (insert
, scrollable
);
126 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
128 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME
);
130 signal_map_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::start_updating
));
131 signal_unmap_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::stop_updating
));
134 // set_position (Gtk::WIN_POS_MOUSE);
135 set_name ("PluginEditor");
136 add_events (Gdk::KEY_PRESS_MASK
|Gdk::KEY_RELEASE_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
);
138 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it
), reinterpret_cast<Window
*> (this)), false);
139 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away
, this), gui_context());
141 gint h
= _pluginui
->get_preferred_height ();
142 gint w
= _pluginui
->get_preferred_width ();
145 if (h
> 600) h
= 600;
146 if (w
> 600) w
= 600;
153 set_default_size (w
, h
);
156 PluginUIWindow::~PluginUIWindow ()
162 PluginUIWindow::set_parent (Gtk::Window
* win
)
168 PluginUIWindow::on_map ()
171 set_keep_above (true);
175 PluginUIWindow::on_enter_notify_event (GdkEventCrossing
*ev
)
177 Keyboard::the_keyboard().enter_window (ev
, this);
182 PluginUIWindow::on_leave_notify_event (GdkEventCrossing
*ev
)
184 Keyboard::the_keyboard().leave_window (ev
, this);
189 PluginUIWindow::on_focus_in_event (GdkEventFocus
*ev
)
191 Window::on_focus_in_event (ev
);
192 //Keyboard::the_keyboard().magic_widget_grab_focus ();
197 PluginUIWindow::on_focus_out_event (GdkEventFocus
*ev
)
199 Window::on_focus_out_event (ev
);
200 //Keyboard::the_keyboard().magic_widget_drop_focus ();
205 PluginUIWindow::on_show ()
207 set_role("plugin_ui");
210 _pluginui
->update_preset_list ();
211 _pluginui
->update_preset ();
215 if (_pluginui
->on_window_show (_title
)) {
221 // set_transient_for (*parent);
226 PluginUIWindow::on_hide ()
231 _pluginui
->on_window_hide ();
236 PluginUIWindow::set_title(const std::string
& title
)
238 Gtk::Window::set_title(title
);
244 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
> insert
)
246 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
>)
253 boost::shared_ptr
<VSTPlugin
> vp
;
255 if ((vp
= boost::dynamic_pointer_cast
<VSTPlugin
> (insert
->plugin())) == 0) {
256 error
<< _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
258 throw failed_constructor ();
260 VSTPluginUI
* vpu
= new VSTPluginUI (insert
, vp
);
263 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
265 vpu
->package (*this);
274 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
> insert
)
276 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
>)
283 _pluginui
= create_au_gui (insert
, &box
);
284 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
287 Application::instance()->ActivationChanged
.connect (mem_fun (*this, &PluginUIWindow::app_activated
));
295 PluginUIWindow::app_activated (bool yn
)
297 PluginUIWindow::app_activated (bool)
304 _pluginui
->activate ();
309 was_visible
= is_visible();
311 _pluginui
->deactivate ();
318 PluginUIWindow::create_lv2_editor(boost::shared_ptr
<PluginInsert
> insert
)
320 #if defined(HAVE_SLV2) || defined(HAVE_SUIL)
321 boost::shared_ptr
<LV2Plugin
> vp
;
323 if ((vp
= boost::dynamic_pointer_cast
<LV2Plugin
> (insert
->plugin())) == 0) {
324 error
<< _("create_lv2_editor called on non-LV2 plugin") << endmsg
;
325 throw failed_constructor ();
327 LV2PluginUI
* lpu
= new LV2PluginUI (insert
, vp
);
330 lpu
->package (*this);
340 PluginUIWindow::keyboard_focused (bool yn
)
342 _keyboard_focused
= yn
;
346 PluginUIWindow::on_key_press_event (GdkEventKey
* event
)
348 if (_keyboard_focused
) {
350 if (_pluginui
->non_gtk_gui()) {
351 _pluginui
->forward_key_event (event
);
353 return relay_key_press (event
, this);
358 /* for us to be getting key press events, there really
359 MUST be a _pluginui, but just to be safe, check ...
363 if (_pluginui
->non_gtk_gui()) {
364 /* pass editor window as the window for the event
365 to be handled in, not this one, because there are
366 no widgets in this window that we want to have
369 return relay_key_press (event
, &PublicEditor::instance());
371 return relay_key_press (event
, this);
380 PluginUIWindow::on_key_release_event (GdkEventKey
*event
)
382 if (_keyboard_focused
) {
384 if (_pluginui
->non_gtk_gui()) {
385 _pluginui
->forward_key_event (event
);
396 PluginUIWindow::plugin_going_away ()
398 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away
)
401 _pluginui
->stop_updating(0);
404 death_connection
.disconnect ();
406 delete_when_idle (this);
409 PlugUIBase::PlugUIBase (boost::shared_ptr
<PluginInsert
> pi
)
411 , plugin (insert
->plugin())
412 , add_button (_("Add"))
413 , save_button (_("Save"))
414 , delete_button (_("Delete"))
415 , bypass_button (_("Bypass"))
418 , plugin_analysis_expander (_("Plugin analysis"))
421 _preset_combo
.set_size_request (100, -1);
422 _preset_modified
.set_size_request (16, -1);
423 _preset_combo
.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected
));
426 _preset_box
.pack_start (_preset_combo
);
427 _preset_box
.pack_start (_preset_modified
);
429 update_preset_list ();
432 add_button
.set_name ("PluginAddButton");
433 add_button
.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting
));
435 save_button
.set_name ("PluginSaveButton");
436 save_button
.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting
));
438 delete_button
.set_name ("PluginDeleteButton");
439 delete_button
.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting
));
441 insert
->ActiveChanged
.connect (active_connection
, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed
, this, boost::weak_ptr
<Processor
>(insert
)), gui_context());
443 bypass_button
.set_active (!pi
->active());
445 bypass_button
.set_name ("PluginBypassButton");
446 bypass_button
.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled
));
447 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
449 focus_button
.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled
));
450 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
452 /* these images are not managed, so that we can remove them at will */
454 focus_out_image
= new Image (get_icon (X_("computer_keyboard")));
455 focus_in_image
= new Image (get_icon (X_("computer_keyboard_active")));
457 focus_button
.add (*focus_out_image
);
459 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
));
460 ARDOUR_UI::instance()->set_tip (bypass_button
, _("Click to enable/disable this plugin"));
462 plugin_analysis_expander
.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis
));
463 plugin_analysis_expander
.set_expanded(false);
465 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away
, this), gui_context());
467 plugin
->PresetAdded
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed
, this), gui_context ());
468 plugin
->PresetRemoved
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed
, this), gui_context ());
469 plugin
->PresetLoaded
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset
, this), gui_context ());
470 plugin
->ParameterChanged
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed
, this, _1
, _2
), gui_context ());
473 PlugUIBase::~PlugUIBase()
480 PlugUIBase::plugin_going_away ()
482 /* drop references to the plugin/insert */
485 death_connection
.disconnect ();
489 PlugUIBase::set_latency_label ()
491 framecnt_t
const l
= insert
->effective_latency ();
492 framecnt_t
const sr
= insert
->session().frame_rate ();
497 t
= string_compose (_("latency (%1 samples)"), l
);
499 t
= string_compose (_("latency (%1 ms)"), (float) l
/ ((float) sr
/ 1000.0f
));
502 latency_label
.set_text (t
);
506 PlugUIBase::latency_button_clicked ()
509 latency_gui
= new LatencyGUI (*(insert
.get()), insert
->session().frame_rate(), insert
->session().get_block_size());
510 latency_dialog
= new ArdourDialog (_("Edit Latency"), false, false);
511 latency_dialog
->get_vbox()->pack_start (*latency_gui
);
512 latency_dialog
->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label
));
515 latency_dialog
->show_all ();
519 PlugUIBase::processor_active_changed (boost::weak_ptr
<Processor
> weak_p
)
521 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed
, weak_p
)
522 boost::shared_ptr
<Processor
> p (weak_p
);
524 bypass_button
.set_active (!p
->active());
529 PlugUIBase::preset_selected ()
531 if (_no_load_preset
) {
535 if (_preset_combo
.get_active_text().length() > 0) {
536 const Plugin::PresetRecord
* pr
= plugin
->preset_by_label (_preset_combo
.get_active_text());
538 plugin
->load_preset (*pr
);
540 warning
<< string_compose(_("Plugin preset %1 not found"),
541 _preset_combo
.get_active_text()) << endmsg
;
547 PlugUIBase::add_plugin_setting ()
549 NewPluginPresetDialog
d (plugin
);
552 case Gtk::RESPONSE_ACCEPT
:
553 if (d
.name().empty()) {
558 plugin
->remove_preset (d
.name ());
561 Plugin::PresetRecord
const r
= plugin
->save_preset (d
.name());
562 if (!r
.uri
.empty ()) {
563 plugin
->load_preset (r
);
570 PlugUIBase::save_plugin_setting ()
572 string
const name
= _preset_combo
.get_active_text ();
573 plugin
->remove_preset (name
);
574 Plugin::PresetRecord
const r
= plugin
->save_preset (name
);
575 if (!r
.uri
.empty ()) {
576 plugin
->load_preset (r
);
581 PlugUIBase::delete_plugin_setting ()
583 plugin
->remove_preset (_preset_combo
.get_active_text ());
587 PlugUIBase::bypass_toggled ()
591 if ((x
= bypass_button
.get_active()) == insert
->active()) {
593 insert
->deactivate ();
601 PlugUIBase::focus_toggled (GdkEventButton
*)
603 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
604 Keyboard::the_keyboard().magic_widget_drop_focus();
605 focus_button
.remove ();
606 focus_button
.add (*focus_out_image
);
607 focus_out_image
->show ();
608 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
));
609 KeyboardFocused (false);
611 Keyboard::the_keyboard().magic_widget_grab_focus();
612 focus_button
.remove ();
613 focus_button
.add (*focus_in_image
);
614 focus_in_image
->show ();
615 ARDOUR_UI::instance()->set_tip (focus_button
, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME
));
616 KeyboardFocused (true);
623 PlugUIBase::toggle_plugin_analysis()
625 if (plugin_analysis_expander
.get_expanded() &&
626 !plugin_analysis_expander
.get_child()) {
629 eqgui
= new PluginEqGui (insert
);
632 Gtk::Window
*toplevel
= (Gtk::Window
*) plugin_analysis_expander
.get_ancestor (GTK_TYPE_WINDOW
);
635 toplevel
->get_size (pre_eq_size
.width
, pre_eq_size
.height
);
638 plugin_analysis_expander
.add (*eqgui
);
639 plugin_analysis_expander
.show_all ();
640 eqgui
->start_listening ();
643 if (!plugin_analysis_expander
.get_expanded()) {
645 // Hide & remove from expander
648 eqgui
->stop_listening ();
649 plugin_analysis_expander
.remove();
651 Gtk::Window
*toplevel
= (Gtk::Window
*) plugin_analysis_expander
.get_ancestor (GTK_TYPE_WINDOW
);
654 toplevel
->resize (pre_eq_size
.width
, pre_eq_size
.height
);
660 PlugUIBase::update_preset_list ()
662 vector
<string
> preset_labels
;
663 vector
<ARDOUR::Plugin::PresetRecord
> presets
= plugin
->get_presets();
667 for (vector
<ARDOUR::Plugin::PresetRecord
>::const_iterator i
= presets
.begin(); i
!= presets
.end(); ++i
) {
668 preset_labels
.push_back (i
->label
);
671 set_popdown_strings (_preset_combo
, preset_labels
);
677 PlugUIBase::update_preset ()
679 Plugin::PresetRecord p
= plugin
->last_preset();
682 _preset_combo
.set_active_text (p
.label
);
685 save_button
.set_sensitive (!p
.uri
.empty() && p
.user
);
686 delete_button
.set_sensitive (!p
.uri
.empty() && p
.user
);
688 update_preset_modified ();
692 PlugUIBase::update_preset_modified ()
694 if (plugin
->last_preset().uri
.empty()) {
695 _preset_modified
.set_text ("");
699 bool const c
= plugin
->parameter_changed_since_last_preset ();
700 if (_preset_modified
.get_text().empty() == c
) {
701 _preset_modified
.set_text (c
? "*" : "");
706 PlugUIBase::parameter_changed (uint32_t, float)
708 update_preset_modified ();
712 PlugUIBase::preset_added_or_removed ()
714 /* Update both the list and the currently-displayed preset */
715 update_preset_list ();