diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index 66b503779d..e2e4540ef8 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -28,6 +28,7 @@ #include #include #include "gtkmm2ext/utils.h" +#include "gtkmm2ext/colors.h" #include "ardour/dB.h" #include "ardour/rc_configuration.h" @@ -36,12 +37,16 @@ #include "ardour/utils.h" #include "pbd/configuration.h" +#include "pbd/match.h" #include "pbd/replace_all.h" #include "pbd/strsplit.h" +#include "widgets/frame.h" + #include "gui_thread.h" #include "option_editor.h" #include "public_editor.h" +#include "ui_config.h" #include "utils.h" #include "pbd/i18n.h" @@ -58,9 +63,13 @@ OptionEditorComponent::add_widget_to_page (OptionEditorPage* p, Gtk::Widget* w) if (!_note.empty ()) { ++m; } + _frame = manage (new ArdourWidgets::Frame); + _frame->add (*w); + _frame->set_draw (false); + _frame->set_edge_color (UIConfiguration::instance().color (X_("preference highlight"))); p->table.resize (m, 3); - p->table.attach (*w, 1, 3, n, n + 1, FILL | EXPAND); + p->table.attach (*_frame, 1, 3, n, n + 1, FILL | EXPAND); maybe_add_note (p, n + 1); } @@ -74,8 +83,13 @@ OptionEditorComponent::add_widgets_to_page (OptionEditorPage* p, Gtk::Widget* wa ++m; } + _frame = manage (new ArdourWidgets::Frame); + _frame->add (*wa); + _frame->set_draw (false); + _frame->set_edge_color (UIConfiguration::instance().color (X_("preference highlight"))); + p->table.resize (m, 3); - p->table.attach (*wa, 1, 2, n, n + 1, FILL); + p->table.attach (*_frame, 1, 2, n, n + 1, FILL); Alignment* a = manage (new Alignment (0, 0.5, 0, 1.0)); a->add (*wb); @@ -101,6 +115,44 @@ OptionEditorComponent::set_note (string const & n) _note = n; } +void +OptionEditorComponent::highlight () +{ + if (_frame) { + _frame->set_draw (true); + } +} + +void +OptionEditorComponent::end_highlight () +{ + if (_frame) { + _frame->set_draw (false); + } +} + +std::string +OptionEditorComponent::get_metadata () const +{ + if (!_metadata.empty()) { + return _metadata; + } + + gchar* tt = gtk_widget_get_tooltip_text (const_cast(this)->tip_widget().gobj()); + + if (tt) { + return tt; + } + + return string(); +} + +void +OptionEditorComponent::set_metadata (std::string const & str) +{ + _metadata = str; +} + /*--------------------------*/ OptionEditorHeading::OptionEditorHeading (string const & h) @@ -747,6 +799,8 @@ OptionEditorMiniPage::add_to_page (OptionEditorPage* p) OptionEditor::OptionEditor (PBD::Configuration* c) : _config (c) , option_tree (TreeStore::create (option_columns)) + , search_results (0) + , search_not_found_count (0) , option_treeview (option_tree) { using namespace Notebook_Helpers; @@ -766,6 +820,18 @@ OptionEditor::OptionEditor (PBD::Configuration* c) /* Watch out for changes to parameters */ _config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&OptionEditor::parameter_changed, this, _1), gui_context()); + + search_entry.show (); + search_entry.set_text (_("Search here...")); + search_entry.set_name (X_("ShadedEntry")); + set_size_request_to_display_given_text (search_entry, X_("a long enough search string"), 2, 2); + search_packer.pack_start (search_entry, true, true); + search_packer.show (); + + search_entry.signal_activate().connect (sigc::mem_fun (*this, &OptionEditor::search)); + search_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &OptionEditor::search_key_press), false); + search_entry.signal_focus_in_event().connect (sigc::mem_fun (*this, &OptionEditor::search_key_focus), false); + search_entry.signal_focus_out_event().connect (sigc::mem_fun (*this, &OptionEditor::search_key_focus), false); } OptionEditor::~OptionEditor () @@ -778,6 +844,124 @@ OptionEditor::~OptionEditor () } } +bool +OptionEditor::search_key_focus (GdkEventFocus* ev) +{ + if (ev->in) { + if (search_entry.get_name () == X_("ShadedEntry")) { + search_entry.set_text (""); + search_entry.set_name (X_("GtkEntry")); + } + } else { + if (search_entry.get_text().empty() && search_entry.get_name () != X_("ShadedEntry")) { + search_entry.set_text (_("Search here...")); + search_entry.set_name (X_("ShadedEntry")); + } + } + return false; +} + +bool +OptionEditor::search_key_press (GdkEventKey* ev) +{ + if (search_entry.get_name () == X_("ShadedEntry")) { + search_entry.set_text (""); + search_entry.set_name (X_("GtkEntry")); + } + return false; +} + +void +OptionEditor::search () +{ + string search_for = search_entry.get_text(); + + not_found_callback (); + + if (search_for.empty()) { + return; + } + + if (!search_results || search_for != last_search_string) { + + /* (re)build search results */ + + delete search_results; + search_results = new std::vector; + + for (auto p : pages()) { + for (auto oc : p.second->components) { + string metadata (oc->get_metadata()); + if (PBD::match_search_strings (metadata, search_for)) { + search_results->push_back (SearchResult (p.first, *oc)); + } + } + } + + if (search_results->empty()) { + not_found (); + delete search_results; + search_results = 0; + return; + } + + last_search_string = search_for; + search_iterator = search_results->begin (); + + } else { + + /* have results and still searching for the same string. End + * highlight of previous find (if not at end) and move on to + * the next if we can. + */ + + if (search_iterator != search_results->end()) { + search_iterator->component.end_highlight (); + ++search_iterator; + } + + if (search_iterator == search_results->end()) { + + search_iterator = search_results->begin(); + search_highlight ((*search_iterator).page_title, (*search_iterator).component); + search_iterator++; + + return; + } + } + + /* Move to next result, and highlight it */ + + search_highlight ((*search_iterator).page_title, (*search_iterator).component); +} + +void +OptionEditor::not_found () +{ + search_entry.set_sensitive (false); + not_found_timeout = Glib::signal_timeout().connect (mem_fun (*this, &OptionEditor::not_found_callback), 250); + search_not_found_count++; +} + +bool +OptionEditor::not_found_callback () +{ + search_entry.set_sensitive (true); + search_entry.grab_focus (); + search_not_found_count = 0; + return false; +} + +void +OptionEditor::search_highlight (std::string const & page_title, OptionEditorComponent& component) +{ + if (current_page() != page_title) { + set_current_page (page_title); + } + component.highlight (); +} + + /** Called when a configuration parameter has been changed. * @param p Parameter name. */ @@ -793,6 +977,20 @@ OptionEditor::parameter_changed (std::string const & p) } } +std::string +OptionEditor::current_page () +{ + Glib::RefPtr selection = option_treeview.get_selection(); + TreeModel::const_iterator iter = selection->get_selected(); + + if (iter) { + TreeModel::Row row = *iter; + return row[option_columns.name]; + } + + return string(); +} + void OptionEditor::treeview_row_selected () { @@ -964,7 +1162,9 @@ OptionEditorContainer::OptionEditorContainer (PBD::Configuration* c) f->add (treeview()); f->set_shadow_type (Gtk::SHADOW_OUT); f->set_border_width (0); - hpacker.pack_start (*f, false, false, 4); + treeview_packer.pack_start (*f, true, true); + + hpacker.pack_start (treeview_packer, false, false, 4); hpacker.pack_start (notebook(), false, false); pack_start (hpacker, true, true); @@ -975,18 +1175,19 @@ OptionEditorWindow::OptionEditorWindow (PBD::Configuration* c, string const& str : OptionEditor (c) , ArdourWindow (str) { - container.set_border_width (4); + hpacker.set_border_width (4); Frame* f = manage (new Frame ()); + f->add (treeview()); f->set_shadow_type (Gtk::SHADOW_OUT); f->set_border_width (0); - hpacker.pack_start (*f, false, false); + vpacker.pack_start (*f, true, true); + + hpacker.pack_start (vpacker, false, false); hpacker.pack_start (notebook(), true, true, 4); - container.pack_start (hpacker, true, true); - hpacker.show_all (); - container.show (); + vpacker.show (); - add (container); + add (hpacker); } diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index f4dfa9d86f..1635b77d39 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -64,12 +64,17 @@ namespace PBD { class Configuration; } +namespace ArdourWidgets { + class Frame; +} + class OptionEditorPage; /** Base class for components of an OptionEditor dialog */ class OptionEditorComponent { public: + OptionEditorComponent() : _frame (0) {} virtual ~OptionEditorComponent() {} /** Called when a configuration parameter's value has changed. @@ -90,10 +95,18 @@ public: virtual Gtk::Widget& tip_widget() = 0; + virtual std::string get_metadata() const; + void set_metadata (std::string const &); + + void highlight (); + void end_highlight (); + protected: void maybe_add_note (OptionEditorPage *, int); std::string _note; + ArdourWidgets::Frame* _frame; + std::string _metadata; }; /** A component which provides a subheading within the dialog */ @@ -705,7 +718,9 @@ public: void add_option (std::string const &, OptionEditorComponent *); void add_page (std::string const &, Gtk::Widget& page_widget); + std::string current_page (); /* ought to be const but .. hard */ void set_current_page (std::string const &); + std::map& pages() { return _pages; } protected: virtual void parameter_changed (std::string const &); @@ -729,6 +744,32 @@ protected: OptionColumns option_columns; Glib::RefPtr option_tree; + /* searching */ + + Gtk::Entry search_entry; + Gtk::Label search_label; + Gtk::Button search_button; + Gtk::HBox search_packer; + struct SearchResult { + SearchResult (std::string const & p, OptionEditorComponent& c) : page_title (p), component (c) {} + + std::string page_title; + OptionEditorComponent& component; + }; + typedef std::vector SearchResults; + SearchResults* search_results; + SearchResults::iterator search_iterator; + std::string last_search_string; + int search_not_found_count; + sigc::connection not_found_timeout; + + void search (); + void search_highlight (std::string const & page_title, OptionEditorComponent&); + bool not_found_callback (); + bool search_key_press (GdkEventKey*); + bool search_key_focus (GdkEventFocus*); + void not_found (); + private: PBD::ScopedConnection config_connection; Gtk::Notebook _notebook; @@ -747,7 +788,8 @@ class OptionEditorContainer : public OptionEditor, public Gtk::VBox public: OptionEditorContainer (PBD::Configuration *); ~OptionEditorContainer() {} -private: + + Gtk::VBox treeview_packer; Gtk::HBox hpacker; }; @@ -757,8 +799,8 @@ class OptionEditorWindow : public OptionEditor, public ArdourWindow public: OptionEditorWindow (PBD::Configuration *, std::string const &); ~OptionEditorWindow() {} -private: - Gtk::VBox container; +protected: + Gtk::VBox vpacker; Gtk::HBox hpacker; }; diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index 82fb75c284..ca2edc8dc9 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -1133,6 +1133,7 @@ class FontScalingOptions : public HSliderOption _hscale.add_mark(250, Gtk::POS_TOP, empty); set_note (_("Adjusting the scale requires an application restart for fully accurate re-layout.")); + set_metadata (_("fonts font size scaling readable readability")); } void changed () @@ -3800,7 +3801,7 @@ These settings will only take effect after %1 is restarted.\n\ add_option (_("Transport"), bo); Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(), _("When enabled plugins will be reset at transport stop. When disabled plugins will be left unchanged at transport stop.\n\nThis mostly affects plugins with a \"tail\" like Reverbs.")); - + bo->highlight (); /* PLUGINS ******************************************************************/ #if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT || defined MACVST_SUPPORT || defined AUDIOUNIT_SUPPORT || defined VST3_SUPPORT) @@ -4809,6 +4810,10 @@ These settings will only take effect after %1 is restarted.\n\ } set_current_page (_("General")); + + /* Place the search entry */ + + treeview_packer.pack_end (search_packer, false, false); } bool diff --git a/gtk2_ardour/rc_option_editor.h b/gtk2_ardour/rc_option_editor.h index 310609eb81..13cecb3920 100644 --- a/gtk2_ardour/rc_option_editor.h +++ b/gtk2_ardour/rc_option_editor.h @@ -91,3 +91,4 @@ private: }; #endif /* __gtk_ardour_rc_option_editor_h__ */ + diff --git a/gtk2_ardour/session_option_editor.cc b/gtk2_ardour/session_option_editor.cc index 436c02e11b..a8731190c7 100644 --- a/gtk2_ardour/session_option_editor.cc +++ b/gtk2_ardour/session_option_editor.cc @@ -422,6 +422,10 @@ SessionOptionEditor::SessionOptionEditor (Session* s) add_option (_("Misc"), new FooOption (btn)); set_current_page (_("Timecode")); + + /* Place the search entry */ + + vpacker.pack_end (search_packer, false, false); } void