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
199
200
201
202
203
204
205
206
207
208
209
|
// 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 "ui/dialog/desktop-tracker.h"
#include "ui/dialog/dialog-manager.h"
#include "ui/dialog/styledialog.h"
#include "ui/widget/panel.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 <ui/widget/panel.h>
#include "xml/helper-observer.h"
#include <memory>
#include <vector>
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 Widget::Panel {
public:
~SelectorsDialog() override;
// No default constructor, noncopyable, nonassignable
SelectorsDialog();
SelectorsDialog(SelectorsDialog const &d) = delete;
SelectorsDialog operator=(SelectorsDialog const &d) = delete;
static SelectorsDialog &getInstance() { return *new SelectorsDialog(); }
private:
// Monitor <style> element for changes.
class NodeObserver;
// 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<std::vector<SPObject *> > _colObj; // List of matching objects.
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;
void _updateWatchers(SPDesktop *);
// 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 _resized();
void _childresized();
void _panedresized(Gtk::Allocation allocation);
void _selectObjects(int, int);
// Variables
double _scroolpos;
bool _scroollock;
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.
// Signals and handlers - External
sigc::connection _document_replaced_connection;
sigc::connection _desktop_changed_connection;
sigc::connection _selection_changed_connection;
void _handleDocumentReplaced(SPDesktop* desktop, SPDocument *document);
void _handleDesktopChanged(SPDesktop* desktop);
void _handleSelectionChanged();
void _panedrealized();
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);
DesktopTracker _desktopTracker;
Inkscape::XML::SignalObserver _objObserver; // Track object in selected row (for style change).
// Signal and handlers - Internal
void _addSelector();
void _delSelector();
bool _handleButtonEvent(GdkEventButton *event);
void _buttonEventsSelectObjs(GdkEventButton *event);
void _selectRow(); // Select row in tree when selection changed.
void _vscrool();
// 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 :
|