summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/selectorsdialog.h
blob: 1e14cfeae32c51b4120f1f3f0adcb2e34c535606 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// SPDX-License-Identifier: GPL-2.0-or-later
/** @file
 * @brief A dialog for CSS selectors
 */
/* Authors:
 *   Kamalpreet Kaur Grewal
 *   Tavmjong Bah
 *
 * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
 * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr>
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#ifndef SELECTORSDIALOG_H
#define SELECTORSDIALOG_H

#include <gtkmm/dialog.h>
#include <gtkmm/paned.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/switch.h>
#include <gtkmm/treemodelfilter.h>
#include <gtkmm/treeselection.h>
#include <gtkmm/treestore.h>
#include <gtkmm/treeview.h>
#include <memory>
#include <vector>

#include "ui/dialog/dialog-base.h"
#include "ui/dialog/styledialog.h"
#include "xml/helper-observer.h"

namespace Inkscape {
namespace UI {
namespace Dialog {

/**
 * @brief The SelectorsDialog class
 * A list of CSS selectors will show up in this dialog. This dialog allows one to
 * add and delete selectors. Elements can be added to and removed from the selectors
 * in the dialog. Selection of any selector row selects the matching  objects in
 * the drawing and vice-versa. (Only simple selectors supported for now.)
 *
 * This class must keep two things in sync:
 *   1. The text node of the style element.
 *   2. The Gtk::TreeModel.
 */
class SelectorsDialog : public DialogBase
{
public:
    // No default constructor, noncopyable, nonassignable
    SelectorsDialog();
    ~SelectorsDialog() override;
    SelectorsDialog(SelectorsDialog const &d) = delete;
    SelectorsDialog operator=(SelectorsDialog const &d) = delete;
    static SelectorsDialog &getInstance() { return *new SelectorsDialog(); }

    void update() override;
    void desktopReplaced() override;
    void documentReplaced() override;
    void selectionChanged(Selection *selection) override;

  private:
    // Monitor <style> element for changes.
    class NodeObserver;

    void removeObservers();

    // Monitor all objects for addition/removal/attribute change
    class NodeWatcher;
    enum SelectorType { CLASS, ID, TAG };
    void _nodeAdded(   Inkscape::XML::Node &repr );
    void _nodeRemoved( Inkscape::XML::Node &repr );
    void _nodeChanged( Inkscape::XML::Node &repr );
    // Data structure
    enum coltype { OBJECT, SELECTOR, OTHER };
    class ModelColumns : public Gtk::TreeModel::ColumnRecord {
    public:
        ModelColumns() {
            add(_colSelector);
            add(_colExpand);
            add(_colType);
            add(_colObj);
            add(_colProperties);
            add(_colVisible);
            add(_colSelected);
        }
        Gtk::TreeModelColumn<Glib::ustring> _colSelector;       // Selector or matching object id.
        Gtk::TreeModelColumn<bool> _colExpand;                  // Open/Close store row.
        Gtk::TreeModelColumn<gint> _colType;                    // Selector row or child object row.
        Gtk::TreeModelColumn<SPObject *> _colObj;               // Matching object (if any).
        Gtk::TreeModelColumn<Glib::ustring> _colProperties;     // List of properties.
        Gtk::TreeModelColumn<bool> _colVisible;                 // Make visible or not.
        Gtk::TreeModelColumn<gint> _colSelected;                // Make selected.
    };
    ModelColumns _mColumns;

    // Override Gtk::TreeStore to control drag-n-drop (only allow dragging and dropping of selectors).
    // See: https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-examples.html.en
    //
    // TreeStore implements simple drag and drop (DND) but there appears no way to know when a DND
    // has been completed (other than doing the whole DND ourselves). As a hack, we use
    // on_row_deleted to trigger write of style element.
    class TreeStore : public Gtk::TreeStore {
    protected:
        TreeStore();
        bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const override;
        bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& path,
                                     const Gtk::SelectionData& selection_data) const override;
        void on_row_deleted(const TreeModel::Path& path) override;

    public:
      static Glib::RefPtr<SelectorsDialog::TreeStore> create(SelectorsDialog *styledialog);

    private:
      SelectorsDialog *_selectorsdialog;
    };

    // TreeView
    Glib::RefPtr<Gtk::TreeModelFilter> _modelfilter;
    Glib::RefPtr<TreeStore> _store;
    Gtk::TreeView _treeView;
    Gtk::TreeModel::Path _lastpath;
    // Widgets
    StyleDialog *_style_dialog;
    Gtk::Paned _paned;
    Glib::RefPtr<Gtk::Adjustment> _vadj;
    Gtk::Box _button_box;
    Gtk::Box _selectors_box;
    Gtk::ScrolledWindow _scrolled_window_selectors;

    Gtk::Button _del;
    Gtk::Button _create;
    // Reading and writing the style element.
    Inkscape::XML::Node *_getStyleTextNode(bool create_if_missing = false);
    void _readStyleElement();
    void _writeStyleElement();

    // Update watchers
    std::unique_ptr<Inkscape::XML::NodeObserver> m_nodewatcher;
    std::unique_ptr<Inkscape::XML::NodeObserver> m_styletextwatcher;

    // Manipulate Tree
    void _addToSelector(Gtk::TreeModel::Row row);
    void _removeFromSelector(Gtk::TreeModel::Row row);
    Glib::ustring _getIdList(std::vector<SPObject *>);
    std::vector<SPObject *> _getObjVec(Glib::ustring selector);
    void _insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className);
    void _insertClass(SPObject *obj, const Glib::ustring &className);
    void _removeClass(const std::vector<SPObject *> &objVec, const Glib::ustring &className, bool all = false);
    void _removeClass(SPObject *obj, const Glib::ustring &className, bool all = false);
    void _toggleDirection(Gtk::RadioButton *vertical);
    void _showWidgets();

    void _selectObjects(int, int);
    // Variables
    double _scrollpos;
    bool _scrollock;
    bool _updating;                 // Prevent cyclic actions: read <-> write, select via dialog <-> via desktop
    Inkscape::XML::Node *m_root = nullptr;
    Inkscape::XML::Node *_textNode; // Track so we know when to add a NodeObserver.

    void _rowExpand(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
    void _rowCollapse(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
    void _closeDialog(Gtk::Dialog *textDialogPtr);

    Inkscape::XML::SignalObserver _objObserver; // Track object in selected row (for style change).

    // Signal and handlers - Internal
    void _addSelector();
    void _delSelector();
    static Glib::ustring _getSelectorClasses(Glib::ustring selector);
    bool _handleButtonEvent(GdkEventButton *event);
    void _buttonEventsSelectObjs(GdkEventButton *event);
    void _selectRow(); // Select row in tree when selection changed.
    void _vscroll();

    // GUI
    void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
};

} // namespace Dialogc
} // namespace UI
} // namespace Inkscape

#endif // SELECTORSDIALOG_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 :