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"
53 #include "ardour/lv2_plugin.h"
54 #include "lv2_plugin_ui.h"
59 #include "ardour_dialog.h"
60 #include "ardour_ui.h"
62 #include "plugin_ui.h"
64 #include "gui_thread.h"
65 #include "public_editor.h"
67 #include "latency_gui.h"
68 #include "plugin_eq_gui.h"
73 using namespace ARDOUR
;
75 using namespace Gtkmm2ext
;
78 PluginUIWindow::PluginUIWindow (Gtk::Window
* win
, boost::shared_ptr
<PluginInsert
> insert
, bool scrollable
)
81 , _keyboard_focused (false)
83 bool have_gui
= false;
85 Label
* label
= manage (new Label());
86 label
->set_markup ("<b>THIS IS THE PLUGIN UI</b>");
88 if (insert
->plugin()->has_editor()) {
89 switch (insert
->type()) {
91 have_gui
= create_vst_editor (insert
);
94 case ARDOUR::AudioUnit
:
95 have_gui
= create_audiounit_editor (insert
);
99 error
<< _("Eh? LADSPA plugins don't have editors!") << endmsg
;
103 have_gui
= create_lv2_editor (insert
);
108 error
<< _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
111 error
<< _("unknown type of editor-supplying plugin")
114 throw failed_constructor ();
121 GenericPluginUI
* pu
= new GenericPluginUI (insert
, scrollable
);
124 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
128 Gtk::HBox *hbox = new Gtk::HBox();
129 hbox->pack_start( *pu);
130 // TODO: this should be nicer
131 hbox->pack_start( eqgui_bin );
136 set_wmclass (X_("ardour_plugin_editor"), PROGRAM_NAME
);
138 signal_map_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::start_updating
));
139 signal_unmap_event().connect (sigc::mem_fun (*pu
, &GenericPluginUI::stop_updating
));
142 // set_position (Gtk::WIN_POS_MOUSE);
143 set_name ("PluginEditor");
144 add_events (Gdk::KEY_PRESS_MASK
|Gdk::KEY_RELEASE_MASK
|Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
);
146 signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it
), reinterpret_cast<Window
*> (this)), false);
147 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PluginUIWindow::plugin_going_away
, this), gui_context());
149 gint h
= _pluginui
->get_preferred_height ();
150 gint w
= _pluginui
->get_preferred_width ();
153 if (h
> 600) h
= 600;
154 if (w
> 600) w
= 600;
161 set_default_size (w
, h
);
164 PluginUIWindow::~PluginUIWindow ()
170 PluginUIWindow::set_parent (Gtk::Window
* win
)
176 PluginUIWindow::on_map ()
179 set_keep_above (true);
183 PluginUIWindow::on_enter_notify_event (GdkEventCrossing
*ev
)
185 Keyboard::the_keyboard().enter_window (ev
, this);
190 PluginUIWindow::on_leave_notify_event (GdkEventCrossing
*ev
)
192 Keyboard::the_keyboard().leave_window (ev
, this);
197 PluginUIWindow::on_focus_in_event (GdkEventFocus
*ev
)
199 Window::on_focus_in_event (ev
);
200 //Keyboard::the_keyboard().magic_widget_grab_focus ();
205 PluginUIWindow::on_focus_out_event (GdkEventFocus
*ev
)
207 Window::on_focus_out_event (ev
);
208 //Keyboard::the_keyboard().magic_widget_drop_focus ();
213 PluginUIWindow::on_show ()
215 set_role("plugin_ui");
218 _pluginui
->update_presets ();
222 if (_pluginui
->on_window_show (_title
)) {
228 // set_transient_for (*parent);
233 PluginUIWindow::on_hide ()
238 _pluginui
->on_window_hide ();
243 PluginUIWindow::set_title(const std::string
& title
)
245 //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl;
246 Gtk::Window::set_title(title
);
252 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
> insert
)
254 PluginUIWindow::create_vst_editor(boost::shared_ptr
<PluginInsert
>)
261 boost::shared_ptr
<VSTPlugin
> vp
;
263 if ((vp
= boost::dynamic_pointer_cast
<VSTPlugin
> (insert
->plugin())) == 0) {
264 error
<< _("unknown type of editor-supplying plugin (note: no VST support in this version of ardour)")
266 throw failed_constructor ();
268 VSTPluginUI
* vpu
= new VSTPluginUI (insert
, vp
);
271 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
273 vpu
->package (*this);
282 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
> insert
)
284 PluginUIWindow::create_audiounit_editor (boost::shared_ptr
<PluginInsert
>)
291 _pluginui
= create_au_gui (insert
, &box
);
292 _pluginui
->KeyboardFocused
.connect (sigc::mem_fun (*this, &PluginUIWindow::keyboard_focused
));
295 Application::instance()->ActivationChanged
.connect (mem_fun (*this, &PluginUIWindow::app_activated
));
303 PluginUIWindow::app_activated (bool yn
)
305 PluginUIWindow::app_activated (bool)
309 cerr
<< "APP activated ? " << yn
<< endl
;
313 _pluginui
->activate ();
318 was_visible
= is_visible();
320 _pluginui
->deactivate ();
327 PluginUIWindow::create_lv2_editor(boost::shared_ptr
<PluginInsert
> insert
)
333 boost::shared_ptr
<LV2Plugin
> vp
;
335 if ((vp
= boost::dynamic_pointer_cast
<LV2Plugin
> (insert
->plugin())) == 0) {
336 error
<< _("create_lv2_editor called on non-LV2 plugin") << endmsg
;
337 throw failed_constructor ();
339 LV2PluginUI
* lpu
= new LV2PluginUI (insert
, vp
);
342 lpu
->package (*this);
350 PluginUIWindow::keyboard_focused (bool yn
)
352 _keyboard_focused
= yn
;
356 PluginUIWindow::on_key_press_event (GdkEventKey
* event
)
358 if (_keyboard_focused
) {
360 if (_pluginui
->non_gtk_gui()) {
361 _pluginui
->forward_key_event (event
);
363 return relay_key_press (event
, this);
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);
382 PluginUIWindow::on_key_release_event (GdkEventKey
*event
)
384 if (_keyboard_focused
) {
386 if (_pluginui
->non_gtk_gui()) {
387 _pluginui
->forward_key_event (event
);
398 PluginUIWindow::plugin_going_away ()
400 ENSURE_GUI_THREAD (*this, &PluginUIWindow::plugin_going_away
)
403 _pluginui
->stop_updating(0);
406 death_connection
.disconnect ();
408 delete_when_idle (this);
411 PlugUIBase::PlugUIBase (boost::shared_ptr
<PluginInsert
> pi
)
413 plugin (insert
->plugin()),
414 save_button(_("Add")),
415 bypass_button (_("Bypass")),
417 plugin_analysis_expander (_("Plugin analysis"))
419 //preset_combo.set_use_arrows_always(true);
421 preset_combo
.set_size_request (100, -1);
422 preset_combo
.set_active_text ("");
423 preset_combo
.signal_changed().connect(sigc::mem_fun(*this, &PlugUIBase::setting_selected
));
424 no_load_preset
= false;
426 save_button
.set_name ("PluginSaveButton");
427 save_button
.signal_clicked().connect(sigc::mem_fun(*this, &PlugUIBase::save_plugin_setting
));
429 insert
->ActiveChanged
.connect (active_connection
, invalidator (*this), boost::bind (&PlugUIBase::processor_active_changed
, this, boost::weak_ptr
<Processor
>(insert
)), gui_context());
431 bypass_button
.set_active (!pi
->active());
433 bypass_button
.set_name ("PluginBypassButton");
434 bypass_button
.signal_toggled().connect (sigc::mem_fun(*this, &PlugUIBase::bypass_toggled
));
435 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
437 focus_button
.signal_button_release_event().connect (sigc::mem_fun(*this, &PlugUIBase::focus_toggled
));
438 focus_button
.add_events (Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
);
440 /* these images are not managed, so that we can remove them at will */
442 focus_out_image
= new Image (get_icon (X_("computer_keyboard")));
443 focus_in_image
= new Image (get_icon (X_("computer_keyboard_active")));
445 focus_button
.add (*focus_out_image
);
447 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
));
448 ARDOUR_UI::instance()->set_tip (bypass_button
, _("Click to enable/disable this plugin"));
450 plugin_analysis_expander
.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &PlugUIBase::toggle_plugin_analysis
));
451 plugin_analysis_expander
.set_expanded(false);
453 insert
->DropReferences
.connect (death_connection
, invalidator (*this), boost::bind (&PlugUIBase::plugin_going_away
, this), gui_context());
456 PlugUIBase::~PlugUIBase()
462 PlugUIBase::plugin_going_away ()
464 /* drop references to the plugin/insert */
467 death_connection
.disconnect ();
471 PlugUIBase::set_latency_label ()
474 nframes_t l
= insert
->effective_latency ();
475 nframes_t sr
= insert
->session().frame_rate();
478 snprintf (buf
, sizeof (buf
), "latency (%d samples)", l
);
480 snprintf (buf
, sizeof (buf
), "latency (%.2f msecs)", (float) l
/ ((float) sr
/ 1000.0f
));
483 latency_label
.set_text (buf
);
487 PlugUIBase::latency_button_clicked ()
490 latency_gui
= new LatencyGUI (*(insert
.get()), insert
->session().frame_rate(), insert
->session().get_block_size());
491 latency_dialog
= new ArdourDialog ("Edit Latency", false, false);
492 latency_dialog
->get_vbox()->pack_start (*latency_gui
);
493 latency_dialog
->signal_hide().connect (sigc::mem_fun (*this, &PlugUIBase::set_latency_label
));
496 latency_dialog
->show_all ();
500 PlugUIBase::processor_active_changed (boost::weak_ptr
<Processor
> weak_p
)
502 ENSURE_GUI_THREAD (*this, &PlugUIBase::processor_active_changed
, weak_p
)
503 boost::shared_ptr
<Processor
> p (weak_p
);
505 bypass_button
.set_active (!p
->active());
510 PlugUIBase::setting_selected()
512 if (no_load_preset
) {
516 if (preset_combo
.get_active_text().length() > 0) {
517 const Plugin::PresetRecord
* pr
= plugin
->preset_by_label(preset_combo
.get_active_text());
519 plugin
->load_preset(pr
->uri
);
521 warning
<< string_compose(_("Plugin preset %1 not found"),
522 preset_combo
.get_active_text()) << endmsg
;
528 PlugUIBase::save_plugin_setting ()
530 ArdourPrompter
prompter (true);
531 prompter
.set_title(_("New Preset"));
532 prompter
.set_prompt(_("Name of New Preset:"));
533 prompter
.add_button (Gtk::Stock::ADD
, Gtk::RESPONSE_ACCEPT
);
534 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
535 prompter
.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY
);
540 switch (prompter
.run ()) {
541 case Gtk::RESPONSE_ACCEPT
:
543 prompter
.get_result(name
);
545 if (plugin
->save_preset(name
)) {
547 no_load_preset
= true;
548 preset_combo
.set_active_text (name
);
549 no_load_preset
= false;
557 PlugUIBase::bypass_toggled ()
561 if ((x
= bypass_button
.get_active()) == insert
->active()) {
563 insert
->deactivate ();
571 PlugUIBase::focus_toggled (GdkEventButton
*)
573 if (Keyboard::the_keyboard().some_magic_widget_has_focus()) {
574 Keyboard::the_keyboard().magic_widget_drop_focus();
575 focus_button
.remove ();
576 focus_button
.add (*focus_out_image
);
577 focus_out_image
->show ();
578 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
));
579 KeyboardFocused (false);
581 Keyboard::the_keyboard().magic_widget_grab_focus();
582 focus_button
.remove ();
583 focus_button
.add (*focus_in_image
);
584 focus_in_image
->show ();
585 ARDOUR_UI::instance()->set_tip (focus_button
, string_compose (_("Click to allow normal use of %1 keyboard shortcuts"), PROGRAM_NAME
));
586 KeyboardFocused (true);
593 PlugUIBase::toggle_plugin_analysis()
595 if (plugin_analysis_expander
.get_expanded() &&
596 !plugin_analysis_expander
.get_child()) {
598 PluginEqGui
*foo
= new PluginEqGui(insert
);
599 plugin_analysis_expander
.add( *foo
);
600 plugin_analysis_expander
.show_all();
605 if (!plugin_analysis_expander
.get_expanded() &&
606 (gui
= plugin_analysis_expander
.get_child())) {
609 //plugin_analysis_expander.remove(*gui);
610 plugin_analysis_expander
.remove();
614 Gtk::Widget
*toplevel
= plugin_analysis_expander
.get_toplevel();
616 std::cerr
<< "No toplevel widget?!?!" << std::endl
;
620 Gtk::Container
*cont
= dynamic_cast<Gtk::Container
*>(toplevel
);
622 std::cerr
<< "Toplevel widget is not a container?!?" << std::endl
;
626 Gtk::Allocation
alloc(0, 0, 50, 50); // Just make it small
627 toplevel
->size_allocate(alloc
);
632 PlugUIBase::update_presets ()
634 vector
<string
> preset_labels
;
635 vector
<ARDOUR::Plugin::PresetRecord
> presets
= plugin
->get_presets();
637 no_load_preset
= true;
639 for (vector
<ARDOUR::Plugin::PresetRecord
>::const_iterator i
= presets
.begin(); i
!= presets
.end(); ++i
) {
640 preset_labels
.push_back(i
->label
);
643 set_popdown_strings (preset_combo
, preset_labels
);
645 no_load_preset
= false;