// SPDX-License-Identifier: GPL-2.0-or-later /** \file * CommandPalette: Class providing Command Palette feature * * Authors: * Abhay Raj Singh * * Copyright (C) 2020 Autors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #ifndef INKSCAPE_DIALOG_COMMAND_PALETTE_H #define INKSCAPE_DIALOG_COMMAND_PALETTE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inkscape.h" #include "ui/dialog/align-and-distribute.h" namespace Inkscape { namespace UI { namespace Dialog { // Enables using switch case enum class TypeOfVariant { NONE, UNKNOWN, BOOL, INT, DOUBLE, STRING, TUPLE_DD }; enum class CPMode { SEARCH, INPUT, // Input arguments SHELL, HISTORY }; enum class HistoryType { LPE, ACTION, OPEN_FILE, IMPORT_FILE, }; struct History { HistoryType history_type; std::string data; History(HistoryType ht, std::string &&data) : history_type(ht) , data(data) {} }; class CPHistoryXML { public: // constructors, asssignment, destructor CPHistoryXML(); ~CPHistoryXML(); // Handy wrappers for code clearity void add_action(const std::string &full_action_name); void add_import(const std::string &uri); void add_open(const std::string &uri); // Remember parameter for action void add_action_parameter(const std::string &full_action_name, const std::string ¶m); std::optional get_last_operation(); // To construct _CPHistory std::vector get_operation_history() const; // To get parameter history when an action is selected, LIFO stack like so more recent first std::vector get_action_parameter_history(const std::string &full_action_name) const; private: void save() const; void add_operation(const HistoryType history_type, const std::string &data); static std::optional _get_operation_type(Inkscape::XML::Node *operation); const std::string _file_path; Inkscape::XML::Document *_xml_doc; // handy for xml doc child Inkscape::XML::Node *_operations; Inkscape::XML::Node *_params; }; class CommandPalette { public: // API CommandPalette(); ~CommandPalette() = default; CommandPalette(CommandPalette const &) = delete; // no copy CommandPalette &operator=(CommandPalette const &) = delete; // no assignment void open(); void close(); void toggle(); Gtk::Box *get_base_widget(); private: // Helpers using ActionPtr = Glib::RefPtr; using ActionPtrName = std::pair; /** * Insert actions in _CPSuggestions */ void load_app_actions(); void load_win_doc_actions(); void append_recent_file_operation(const Glib::ustring &path, bool is_suggestion, bool is_import = true); bool generate_action_operation(const ActionPtrName &action_ptr_name, const bool is_suggestion); private: // Signal handlers void on_search(); int on_filter_general(Gtk::ListBoxRow *child); bool on_filter_full_action_name(Gtk::ListBoxRow *child); bool on_filter_recent_file(Gtk::ListBoxRow *child, bool const is_import); bool on_key_press_cpfilter_escape(GdkEventKey *evt); bool on_key_press_cpfilter_search_mode(GdkEventKey *evt); bool on_key_press_cpfilter_input_mode(GdkEventKey *evt, const ActionPtrName &action_ptr_name); bool on_key_press_cpfilter_history_mode(GdkEventKey *evt); /** * when search bar is empty */ void hide_suggestions(); /** * when search bar isn't empty */ void show_suggestions(); void on_row_activated(Gtk::ListBoxRow *activated_row); void on_history_selection_changed(Gtk::ListBoxRow *lb); bool operate_recent_file(Glib::ustring const &uri, bool const import); void on_action_fullname_clicked(const Glib::ustring &action_fullname); /** * Implements text matching logic */ static bool fuzzy_search(const Glib::ustring &subject, const Glib::ustring &search); static bool normal_search(const Glib::ustring &subject, const Glib::ustring &search); static bool fuzzy_tolerance_search(const Glib::ustring &subject, const Glib::ustring &search); static int fuzzy_points(const Glib::ustring &subject, const Glib::ustring &search); static int fuzzy_tolerance_points(const Glib::ustring &subject, const Glib::ustring &search); static int fuzzy_points_compare(int fuzzy_points_count_1, int fuzzy_points_count_2, int text_len_1, int text_len_2); int on_sort(Gtk::ListBoxRow *row1, Gtk::ListBoxRow *row2); void set_mode(CPMode mode); /** * Color addition in searched character */ void add_color(Gtk::Label *label, const Glib::ustring &search, const Glib::ustring &subject, bool tooltip=false); void remove_color(Gtk::Label *label, const Glib::ustring &subject, bool tooltip=false); static void add_color_description(Gtk::Label *label, const Glib::ustring &search); /** * Executes Action */ bool ask_action_parameter(const ActionPtrName &action); static ActionPtrName get_action_ptr_name(const Glib::ustring &full_action_name); bool execute_action(const ActionPtrName &action, const Glib::ustring &value); static TypeOfVariant get_action_variant_type(const ActionPtr &action_ptr); static std::pair get_name_desc(Gtk::ListBoxRow *child); Gtk::Label *get_full_action_name(Gtk::ListBoxRow *child); private: // variables // Widgets Glib::RefPtr _builder; Gtk::Box *_CPBase; Gtk::Box *_CPHeader; Gtk::Box *_CPListBase; Gtk::SearchBar *_CPSearchBar; Gtk::SearchEntry *_CPFilter; Gtk::ListBox *_CPSuggestions; Gtk::ListBox *_CPHistory; Gtk::ScrolledWindow *_CPSuggestionsScroll; Gtk::ScrolledWindow *_CPHistoryScroll; // Data const int _max_height_requestable = 360; Glib::ustring _search_text; // States bool _is_open = false; bool _win_doc_actions_loaded = false; // History CPHistoryXML _history_xml; /** * Remember the mode we are in helps in unnecessary signal disconnection and reconnection * Used by set_mode() */ CPMode _mode = CPMode::SHELL; // Default value other than SEARCH required // set_mode() switches between mode hence checks if it already in the target mode. // Constructed value is sometimes SEARCH being the first Item for now // set_mode() never attaches the on search listener then // This initialising value can be any thing other than the initial required mode // Example currently it's open in search mode /** * Stores the search connection to deactivate when not needed */ sigc::connection _cpfilter_search_connection; /** * Stores the key_press connection to deactivate when not needed */ sigc::connection _cpfilter_key_press_connection; }; } // namespace Dialog } // namespace UI } // namespace Inkscape #endif // INKSCAPE_DIALOG_COMMAND_PALETTE_H /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :