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
));
130 Gtk::HBox *hbox = new Gtk::HBox();
131 hbox->pack_start( *pu);
132 // TODO: this should be nicer
133 hbox->pack_start( eqgui_bin );
138 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME
);
140 signal_map_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::start_updating
));
141 signal_unmap_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::stop_updating
));
144 // set_position (Gtk::WIN_POS_MOUSE);
145 set_name ("PluginEditor");
146 add_events (Gdk::KEY_PRESS_MASK
|Gdk::KEY_RELEASE_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
);
148 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it
), reinterpret_cast<Window
*> (this)), false);
149 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away
, this), gui_context());
151 gint h
= _pluginui
->get_preferred_height ();
152 gint w
= _pluginui
->get_preferred_width ();
155 if (h
> 600) h
= 600;
156 if (w
> 600) w
= 600;
163 set_default_size (w
, h
);
166 PluginUIWindow::~PluginUIWindow ()
172 PluginUIWindow::set_parent (Gtk::Window
* win
)
178 PluginUIWindow::on_map ()
181 set_keep_above (true);
185 PluginUIWindow::on_enter_notify_event (GdkEventCrossing
*ev
)
187 Keyboard::the_keyboard().enter_window (ev
, this);
192 PluginUIWindow::on_leave_notify_event (GdkEventCrossing
*ev
)
194 Keyboard::the_keyboard().leave_window (ev
, this);
199 PluginUIWindow::on_focus_in_event (GdkEventFocus
*ev
)
201 Window::on_focus_in_event (ev
);
202 //Keyboard::the_keyboard().magic_widget_grab_focus ();
207 PluginUIWindow::on_focus_out_event (GdkEventFocus
*ev
)
209 Window::on_focus_out_event (ev
);
210 //Keyboard::the_keyboard().magic_widget_drop_focus ();
215 PluginUIWindow::on_show ()
217 set_role("plugin_ui");
220 _pluginui
->update_preset_list ();
221 _pluginui
->update_preset ();
225 if (_pluginui
->on_window_show (_title
)) {
231 // set_transient_for (*parent);
236 PluginUIWindow::on_hide ()
241 _pluginui
->on_window_hide ();
246 PluginUIWindow::set_title(const std::string
& title
)
248 Gtk::Window::set_title(title
);
254 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
> insert
)
256 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
>)
263 boost::shared_ptr
<VSTPlugin
> vp
;
265 if ((vp
= boost::dynamic_pointer_cast
<VSTPlugin
> (insert
->plugin())) == 0) {
266 error
<< _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
268 throw failed_constructor ();
270 VSTPluginUI
* vpu
= new VSTPluginUI (insert
, vp
);
273 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
275 vpu
->package (*this);
284 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
> insert
)
286 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
>)
293 _pluginui
= create_au_gui (insert
, &box
);
294 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
297 Application::instance()->ActivationChanged
.connect (mem_fun (*this, &PluginUIWindow::app_activated
));
305 PluginUIWindow::app_activated (bool yn
)
307 PluginUIWindow::app_activated (bool)
311 cerr
<< "APP activated ? " << yn
<< endl
;
315 _pluginui
->activate ();
320 was_visible
= is_visible();
322 _pluginui
->deactivate ();
329 PluginUIWindow::create_lv2_editor(boost::shared_ptr
<PluginInsert
> insert
)
335 boost::shared_ptr
<LV2Plugin
> vp
;
337 if ((vp
= boost::dynamic_pointer_cast
<LV2Plugin
> (insert
->plugin())) == 0) {
338 error
<< _("create_lv2_editor called on non-LV2 plugin") << endmsg
;
339 throw failed_constructor ();
341 LV2PluginUI
* lpu
= new LV2PluginUI (insert
, vp
);
344 lpu
->package (*this);
352 PluginUIWindow::keyboard_focused (bool yn
)
354 _keyboard_focused
= yn
;
358 PluginUIWindow::on_key_press_event (GdkEventKey
* event
)
360 if (_keyboard_focused
) {
362 if (_pluginui
->non_gtk_gui()) {
363 _pluginui
->forward_key_event (event
);
365 return relay_key_press (event
, this);
370 /* for us to be getting key press events, there really
371 MUST be a _pluginui, but just to be safe, check ...
375 if (_pluginui
->non_gtk_gui()) {
376 /* pass editor window as the window for the event
377 to be handled in, not this one, because there are
378 no widgets in this window that we want to have
381 return relay_key_press (event
, &PublicEditor::instance());
383 return relay_key_press (event
, this);
392 PluginUIWindow::on_key_release_event (GdkEventKey
*event
)
394 if (_keyboard_focused
) {
396 if (_pluginui
->non_gtk_gui()) {
397 _pluginui
->forward_key_event (event
);
408 PluginUIWindow::plugin_going_away ()
410 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away
)
413 _pluginui
->stop_updating(0);
416 death_connection
.disconnect ();
418 delete_when_idle (this);
421 PlugUIBase::PlugUIBase (boost::shared_ptr
<PluginInsert
> pi
)
423 plugin (insert
->plugin()),
424 add_button (_("Add")),
425 save_button (_("Save")),
426 delete_button (_("Delete")),
427 bypass_button (_("Bypass")),
429 plugin_analysis_expander (_("Plugin analysis"))
431 _preset_combo
.set_size_request (100, -1);
432 _preset_modified
.set_size_request (16, -1);
433 _preset_combo
.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::preset_selected
));
436 _preset_box
.pack_start (_preset_combo
);
437 _preset_box
.pack_start (_preset_modified
);
439 update_preset_list ();
442 add_button
.set_name ("PluginAddButton");
443 add_button
.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::add_plugin_setting
));
445 save_button
.set_name ("PluginSaveButton");
446 save_button
.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting
));
448 delete_button
.set_name ("PluginDeleteButton");
449 delete_button
.signal_clicked().connect (sigc::mem_fun (*this, &PlugUIBase::delete_plugin_setting
));
451 insert
->ActiveChanged
.connect (active_connection
, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed
, this, boost::weak_ptr
<Processor
>(insert
)), gui_context());
453 bypass_button
.set_active (!pi
->active());
455 bypass_button
.set_name ("PluginBypassButton");
456 bypass_button
.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled
));
457 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
459 focus_button
.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled
));
460 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
462 /* these images are not managed, so that we can remove them at will */
464 focus_out_image
= new Image (get_icon (X_("computer_keyboard")));
465 focus_in_image
= new Image (get_icon (X_("computer_keyboard_active")));
467 focus_button
.add (*focus_out_image
);
469 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
));
470 ARDOUR_UI::instance()->set_tip (bypass_button
, _("Click to enable/disable this plugin"));
472 plugin_analysis_expander
.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis
));
473 plugin_analysis_expander
.set_expanded(false);
475 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away
, this), gui_context());
477 plugin
->PresetAdded
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed
, this), gui_context ());
478 plugin
->PresetRemoved
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::preset_added_or_removed
, this), gui_context ());
479 plugin
->PresetLoaded
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::update_preset
, this), gui_context ());
480 plugin
->ParameterChanged
.connect (*this, invalidator (*this), boost::bind (&PlugUIBase::parameter_changed
, this, _1
, _2
), gui_context ());
483 PlugUIBase::~PlugUIBase()
489 PlugUIBase::plugin_going_away ()
491 /* drop references to the plugin/insert */
494 death_connection
.disconnect ();
498 PlugUIBase::set_latency_label ()
501 framecnt_t
const l
= insert
->effective_latency ();
502 framecnt_t
const sr
= insert
->session().frame_rate ();
505 snprintf (buf
, sizeof (buf
), "latency (%" PRId64
" samples)", l
);
507 snprintf (buf
, sizeof (buf
), "latency (%.2f msecs)", (float) l
/ ((float) sr
/ 1000.0f
));
510 latency_label
.set_text (buf
);
514 PlugUIBase::latency_button_clicked ()
517 latency_gui
= new LatencyGUI (*(insert
.get()), insert
->session().frame_rate(), insert
->session().get_block_size());
518 latency_dialog
= new ArdourDialog ("Edit Latency", false, false);
519 latency_dialog
->get_vbox()->pack_start (*latency_gui
);
520 latency_dialog
->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label
));
523 latency_dialog
->show_all ();
527 PlugUIBase::processor_active_changed (boost::weak_ptr
<Processor
> weak_p
)
529 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed
, weak_p
)
530 boost::shared_ptr
<Processor
> p (weak_p
);
532 bypass_button
.set_active (!p
->active());
537 PlugUIBase::preset_selected ()
539 if (_no_load_preset
) {
543 if (_preset_combo
.get_active_text().length() > 0) {
544 const Plugin::PresetRecord
* pr
= plugin
->preset_by_label (_preset_combo
.get_active_text());
546 plugin
->load_preset (*pr
);
548 warning
<< string_compose(_("Plugin preset %1 not found"),
549 _preset_combo
.get_active_text()) << endmsg
;
555 PlugUIBase::add_plugin_setting ()
557 NewPluginPresetDialog
d (plugin
);
560 case Gtk::RESPONSE_ACCEPT
:
561 if (d
.name().empty()) {
566 plugin
->remove_preset (d
.name ());
569 Plugin::PresetRecord
const r
= plugin
->save_preset (d
.name());
570 if (!r
.uri
.empty ()) {
571 plugin
->load_preset (r
);
578 PlugUIBase::save_plugin_setting ()
580 string
const name
= _preset_combo
.get_active_text ();
581 plugin
->remove_preset (name
);
582 Plugin::PresetRecord
const r
= plugin
->save_preset (name
);
583 if (!r
.uri
.empty ()) {
584 plugin
->load_preset (r
);
589 PlugUIBase::delete_plugin_setting ()
591 plugin
->remove_preset (_preset_combo
.get_active_text ());
595 PlugUIBase::bypass_toggled ()
599 if ((x
= bypass_button
.get_active()) == insert
->active()) {
601 insert
->deactivate ();
609 PlugUIBase::focus_toggled (GdkEventButton
*)
611 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
612 Keyboard::the_keyboard().magic_widget_drop_focus();
613 focus_button
.remove ();
614 focus_button
.add (*focus_out_image
);
615 focus_out_image
->show ();
616 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
));
617 KeyboardFocused (false);
619 Keyboard::the_keyboard().magic_widget_grab_focus();
620 focus_button
.remove ();
621 focus_button
.add (*focus_in_image
);
622 focus_in_image
->show ();
623 ARDOUR_UI::instance()->set_tip (focus_button
, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME
));
624 KeyboardFocused (true);
631 PlugUIBase::toggle_plugin_analysis()
633 if (plugin_analysis_expander
.get_expanded() &&
634 !plugin_analysis_expander
.get_child()) {
636 PluginEqGui
*foo
= new PluginEqGui(insert
);
637 plugin_analysis_expander
.add( *foo
);
638 plugin_analysis_expander
.show_all();
643 if (!plugin_analysis_expander
.get_expanded() &&
644 (gui
= plugin_analysis_expander
.get_child())) {
647 //plugin_analysis_expander.remove(*gui);
648 plugin_analysis_expander
.remove();
652 Gtk::Widget
*toplevel
= plugin_analysis_expander
.get_toplevel();
654 std::cerr
<< "No toplevel widget?!?!" << std::endl
;
658 Gtk::Container
*cont
= dynamic_cast<Gtk::Container
*>(toplevel
);
660 std::cerr
<< "Toplevel widget is not a container?!?" << std::endl
;
664 Gtk::Allocation
alloc(0, 0, 50, 50); // Just make it small
665 toplevel
->size_allocate(alloc
);
670 PlugUIBase::update_preset_list ()
672 vector
<string
> preset_labels
;
673 vector
<ARDOUR::Plugin::PresetRecord
> presets
= plugin
->get_presets();
677 for (vector
<ARDOUR::Plugin::PresetRecord
>::const_iterator i
= presets
.begin(); i
!= presets
.end(); ++i
) {
678 preset_labels
.push_back (i
->label
);
681 set_popdown_strings (_preset_combo
, preset_labels
);
687 PlugUIBase::update_preset ()
689 Plugin::PresetRecord p
= plugin
->last_preset();
692 _preset_combo
.set_active_text (p
.label
);
695 save_button
.set_sensitive (!p
.uri
.empty() && p
.user
);
696 delete_button
.set_sensitive (!p
.uri
.empty() && p
.user
);
698 update_preset_modified ();
702 PlugUIBase::update_preset_modified ()
704 if (plugin
->last_preset().uri
.empty()) {
705 _preset_modified
.set_text ("");
709 bool const c
= plugin
->parameter_changed_since_last_preset ();
710 if (_preset_modified
.get_text().empty() == c
) {
711 _preset_modified
.set_text (c
? "*" : "");
716 PlugUIBase::parameter_changed (uint32_t, float)
718 update_preset_modified ();
722 PlugUIBase::preset_added_or_removed ()
724 /* Update both the list and the currently-displayed preset */
725 update_preset_list ();