1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /** @file 3 * @brief A dialog for CSS selectors 4 */ 5 /* Authors: 6 * Kamalpreet Kaur Grewal 7 * Tavmjong Bah 8 * 9 * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> 10 * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr> 11 * 12 * Released under GNU GPL v2+, read the file 'COPYING' for more information. 13 */ 14 15 #ifndef SELECTORSDIALOG_H 16 #define SELECTORSDIALOG_H 17 18 #include <gtkmm/dialog.h> 19 #include <gtkmm/paned.h> 20 #include <gtkmm/radiobutton.h> 21 #include <gtkmm/scrolledwindow.h> 22 #include <gtkmm/switch.h> 23 #include <gtkmm/treemodelfilter.h> 24 #include <gtkmm/treeselection.h> 25 #include <gtkmm/treestore.h> 26 #include <gtkmm/treeview.h> 27 #include <memory> 28 #include <vector> 29 30 #include "ui/dialog/dialog-base.h" 31 #include "ui/dialog/styledialog.h" 32 #include "xml/helper-observer.h" 33 34 namespace Inkscape { 35 namespace UI { 36 namespace Dialog { 37 38 /** 39 * @brief The SelectorsDialog class 40 * A list of CSS selectors will show up in this dialog. This dialog allows one to 41 * add and delete selectors. Elements can be added to and removed from the selectors 42 * in the dialog. Selection of any selector row selects the matching objects in 43 * the drawing and vice-versa. (Only simple selectors supported for now.) 44 * 45 * This class must keep two things in sync: 46 * 1. The text node of the style element. 47 * 2. The Gtk::TreeModel. 48 */ 49 class SelectorsDialog : public DialogBase 50 { 51 public: 52 ~SelectorsDialog() override; 53 // No default constructor, noncopyable, nonassignable 54 SelectorsDialog(); 55 SelectorsDialog(SelectorsDialog const &d) = delete; 56 SelectorsDialog operator=(SelectorsDialog const &d) = delete; getInstance()57 static SelectorsDialog &getInstance() { return *new SelectorsDialog(); } 58 59 private: 60 // Monitor <style> element for changes. 61 class NodeObserver; 62 63 // Monitor all objects for addition/removal/attribute change 64 class NodeWatcher; 65 enum SelectorType { CLASS, ID, TAG }; 66 void _nodeAdded( Inkscape::XML::Node &repr ); 67 void _nodeRemoved( Inkscape::XML::Node &repr ); 68 void _nodeChanged( Inkscape::XML::Node &repr ); 69 // Data structure 70 enum coltype { OBJECT, SELECTOR, OTHER }; 71 class ModelColumns : public Gtk::TreeModel::ColumnRecord { 72 public: ModelColumns()73 ModelColumns() { 74 add(_colSelector); 75 add(_colExpand); 76 add(_colType); 77 add(_colObj); 78 add(_colProperties); 79 add(_colVisible); 80 add(_colSelected); 81 } 82 Gtk::TreeModelColumn<Glib::ustring> _colSelector; // Selector or matching object id. 83 Gtk::TreeModelColumn<bool> _colExpand; // Open/Close store row. 84 Gtk::TreeModelColumn<gint> _colType; // Selector row or child object row. 85 Gtk::TreeModelColumn<SPObject *> _colObj; // Matching object (if any). 86 Gtk::TreeModelColumn<Glib::ustring> _colProperties; // List of properties. 87 Gtk::TreeModelColumn<bool> _colVisible; // Make visible or not. 88 Gtk::TreeModelColumn<gint> _colSelected; // Make selected. 89 }; 90 ModelColumns _mColumns; 91 92 // Override Gtk::TreeStore to control drag-n-drop (only allow dragging and dropping of selectors). 93 // See: https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-examples.html.en 94 // 95 // TreeStore implements simple drag and drop (DND) but there appears no way to know when a DND 96 // has been completed (other than doing the whole DND ourselves). As a hack, we use 97 // on_row_deleted to trigger write of style element. 98 class TreeStore : public Gtk::TreeStore { 99 protected: 100 TreeStore(); 101 bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const override; 102 bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& path, 103 const Gtk::SelectionData& selection_data) const override; 104 void on_row_deleted(const TreeModel::Path& path) override; 105 106 public: 107 static Glib::RefPtr<SelectorsDialog::TreeStore> create(SelectorsDialog *styledialog); 108 109 private: 110 SelectorsDialog *_selectorsdialog; 111 }; 112 113 // TreeView 114 Glib::RefPtr<Gtk::TreeModelFilter> _modelfilter; 115 Glib::RefPtr<TreeStore> _store; 116 Gtk::TreeView _treeView; 117 Gtk::TreeModel::Path _lastpath; 118 // Widgets 119 StyleDialog *_style_dialog; 120 Gtk::Paned _paned; 121 Glib::RefPtr<Gtk::Adjustment> _vadj; 122 Gtk::Box _button_box; 123 Gtk::Box _selectors_box; 124 Gtk::ScrolledWindow _scrolled_window_selectors; 125 126 Gtk::Button _del; 127 Gtk::Button _create; 128 // Reading and writing the style element. 129 Inkscape::XML::Node *_getStyleTextNode(bool create_if_missing = false); 130 void _readStyleElement(); 131 void _writeStyleElement(); 132 133 // Update watchers 134 std::unique_ptr<Inkscape::XML::NodeObserver> m_nodewatcher; 135 std::unique_ptr<Inkscape::XML::NodeObserver> m_styletextwatcher; 136 void _updateWatchers(SPDesktop *); 137 138 // Manipulate Tree 139 void _addToSelector(Gtk::TreeModel::Row row); 140 void _removeFromSelector(Gtk::TreeModel::Row row); 141 Glib::ustring _getIdList(std::vector<SPObject *>); 142 std::vector<SPObject *> _getObjVec(Glib::ustring selector); 143 void _insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className); 144 void _insertClass(SPObject *obj, const Glib::ustring &className); 145 void _removeClass(const std::vector<SPObject *> &objVec, const Glib::ustring &className, bool all = false); 146 void _removeClass(SPObject *obj, const Glib::ustring &className, bool all = false); 147 void _toggleDirection(Gtk::RadioButton *vertical); 148 void _showWidgets(); 149 void _resized(); 150 void _childresized(); 151 void _panedresized(Gtk::Allocation allocation); 152 153 void _selectObjects(int, int); 154 // Variables 155 double _scroolpos; 156 bool _scroollock; 157 bool _updating; // Prevent cyclic actions: read <-> write, select via dialog <-> via desktop 158 Inkscape::XML::Node *m_root = nullptr; 159 Inkscape::XML::Node *_textNode; // Track so we know when to add a NodeObserver. 160 161 void update() override; 162 void _handleSelectionChanged(); 163 void _rowExpand(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path); 164 void _rowCollapse(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path); 165 void _closeDialog(Gtk::Dialog *textDialogPtr); 166 167 Inkscape::XML::SignalObserver _objObserver; // Track object in selected row (for style change). 168 169 // Signal and handlers - Internal 170 void _addSelector(); 171 void _delSelector(); 172 bool _handleButtonEvent(GdkEventButton *event); 173 void _buttonEventsSelectObjs(GdkEventButton *event); 174 void _selectRow(); // Select row in tree when selection changed. 175 void _vscrool(); 176 177 // GUI 178 void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip); 179 }; 180 181 } // namespace Dialogc 182 } // namespace UI 183 } // namespace Inkscape 184 185 #endif // SELECTORSDIALOG_H 186 187 /* 188 Local Variables: 189 mode:c++ 190 c-file-style:"stroustrup" 191 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) 192 indent-tabs-mode:nil 193 fill-column:99 194 End: 195 */ 196 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : 197