1 #ifndef SEEN_INKSCAPE_SELECTION_H
2 #define SEEN_INKSCAPE_SELECTION_H
4 /** \file
5 * Inkscape::Selection: per-desktop selection container
6 *
7 * Authors:
8 * Lauris Kaplinski <lauris@kaplinski.com>
9 * MenTaLguY <mental@rydia.net>
10 * bulia byak <buliabyak@users.sf.net>
11 *
12 * Copyright (C) 2004-2005 MenTaLguY
13 * Copyright (C) 1999-2002 Lauris Kaplinski
14 * Copyright (C) 2001-2002 Ximian, Inc.
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 #include <vector>
20 #include <map>
21 #include <sigc++/sigc++.h>
23 #include "libnr/nr-rect.h"
24 #include "libnr/nr-convex-hull.h"
25 #include "forward.h"
26 #include "gc-managed.h"
27 #include "gc-finalized.h"
28 #include "gc-anchored.h"
29 #include "gc-soft-ptr.h"
30 #include "util/list.h"
31 #include "sp-item.h"
33 class SPItem;
35 namespace Inkscape {
36 namespace XML {
37 class Node;
38 }
39 }
41 namespace Inkscape {
43 /**
44 * @brief The set of selected SPObjects for a given desktop.
45 *
46 * This class represents the set of selected SPItems for a given
47 * SPDesktop.
48 *
49 * An SPObject and its parent cannot be simultaneously selected;
50 * selecting an SPObjects has the side-effect of unselecting any of
51 * its children which might have been selected.
52 *
53 * This is a per-desktop object that keeps the list of selected objects
54 * at the given desktop. Both SPItem and SPRepr lists can be retrieved
55 * from the selection. Many actions operate on the selection, so it is
56 * widely used throughout the code.
57 * It also implements its own asynchronous notification signals that
58 * UI elements can listen to.
59 */
60 class Selection : public Inkscape::GC::Managed<>,
61 public Inkscape::GC::Finalized,
62 public Inkscape::GC::Anchored
63 {
64 public:
65 /**
66 * Constructs an selection object, bound to a particular
67 * SPDesktop
68 *
69 * @param desktop the desktop in question
70 */
71 Selection(SPDesktop *desktop);
72 ~Selection();
74 /**
75 * @brief Returns the desktop the selection is bound to
76 *
77 * @return the desktop the selection is bound to
78 */
79 SPDesktop *desktop() { return _desktop; }
81 /**
82 * @brief Returns active layer for selection (currentLayer or its parent)
83 *
84 * @return layer item the selection is bound to
85 */
86 SPObject *activeContext();
88 /**
89 * @brief Add an SPObject to the set of selected objects
90 *
91 * @param obj the SPObject to add
92 */
93 void add(SPObject *obj, bool persist_selection_context = false);
95 /**
96 * @brief Add an XML node's SPObject to the set of selected objects
97 *
98 * @param the xml node of the item to add
99 */
100 void add(XML::Node *repr) { add(_objectForXMLNode(repr)); }
102 /**
103 * @brief Set the selection to a single specific object
104 *
105 * @param obj the object to select
106 */
107 void set(SPObject *obj, bool persist_selection_context = false);
109 /**
110 * @brief Set the selection to an XML node's SPObject
111 *
112 * @param repr the xml node of the item to select
113 */
114 void set(XML::Node *repr) { set(_objectForXMLNode(repr)); }
116 /**
117 * @brief Removes an item from the set of selected objects
118 *
119 * It is ok to call this method for an unselected item.
120 *
121 * @param item the item to unselect
122 */
123 void remove(SPObject *obj);
125 /**
126 * @brief Removes an item if selected, adds otherwise
127 *
128 * @param item the item to unselect
129 */
130 void toggle(SPObject *obj);
132 /**
133 * @brief Removes an item from the set of selected objects
134 *
135 * It is ok to call this method for an unselected item.
136 *
137 * @param repr the xml node of the item to remove
138 */
139 void remove(XML::Node *repr) { remove(_objectForXMLNode(repr)); }
141 /**
142 * @brief Selects exactly the specified objects
143 *
144 * @param objs the objects to select
145 */
146 void setList(GSList const *objs);
148 /**
149 * @brief Adds the specified objects to selection, without deselecting first
150 *
151 * @param objs the objects to select
152 */
153 void addList(GSList const *objs);
155 /**
156 * @brief Clears the selection and selects the specified objects
157 *
158 * @param repr a list of xml nodes for the items to select
159 */
160 void setReprList(GSList const *reprs);
162 /** \brief Add items from an STL iterator range to the selection
163 * \param from the begin iterator
164 * \param to the end iterator
165 */
166 template <typename InputIterator>
167 void add(InputIterator from, InputIterator to) {
168 _invalidateCachedLists();
169 while ( from != to ) {
170 _add(*from);
171 ++from;
172 }
173 _emitChanged();
174 }
176 /**
177 * @brief Unselects all selected objects.
178 */
179 void clear();
181 /**
182 * @brief Returns true if no items are selected
183 */
184 bool isEmpty() const { return _objs == NULL; }
186 /**
187 * @brief Returns true if the given object is selected
188 */
189 bool includes(SPObject *obj) const;
191 /**
192 * @brief Returns true if the given item is selected
193 */
194 bool includes(XML::Node *repr) const {
195 return includes(_objectForXMLNode(repr));
196 }
198 /**
199 * @brief Returns a single selected object
200 *
201 * @return NULL unless exactly one object is selected
202 */
203 SPObject *single();
205 /**
206 * @brief Returns a single selected item
207 *
208 * @return NULL unless exactly one object is selected
209 */
210 SPItem *singleItem();
212 /**
213 * @brief Returns a single selected object's xml node
214 *
215 * @return NULL unless exactly one object is selected
216 */
217 XML::Node *singleRepr();
219 /** @brief Returns the list of selected objects */
220 GSList const *list();
221 /** @brief Returns the list of selected SPItems */
222 GSList const *itemList();
223 /** @brief Returns a list of the xml nodes of all selected objects */
224 /// \todo only returns reprs of SPItems currently; need a separate
225 /// method for that
226 GSList const *reprList();
228 /** @brief Returns the number of layers in which there are selected objects */
229 guint numberOfLayers();
231 /** @brief Returns the number of parents to which the selected objects belong */
232 guint numberOfParents();
234 /** @brief Returns the bounding rectangle of the selection */
235 NRRect *bounds(NRRect *dest, SPItem::BBoxType type = SPItem::APPROXIMATE_BBOX) const;
236 /** @brief Returns the bounding rectangle of the selection */
237 NR::Maybe<NR::Rect> bounds(SPItem::BBoxType type = SPItem::APPROXIMATE_BBOX) const;
239 /**
240 * @brief Returns the bounding rectangle of the selection
241 *
242 * \todo how is this different from bounds()?
243 */
244 NRRect *boundsInDocument(NRRect *dest, SPItem::BBoxType type = SPItem::APPROXIMATE_BBOX) const;
246 /**
247 * @brief Returns the bounding rectangle of the selection
248 *
249 * \todo how is this different from bounds()?
250 */
251 NR::Maybe<NR::Rect> boundsInDocument(SPItem::BBoxType type = SPItem::APPROXIMATE_BBOX) const;
253 /**
254 * @brief Returns the rotation/skew center of the selection
255 */
256 NR::Maybe<NR::Point> center() const;
258 /**
259 * @brief Gets the selection's snap points.
260 * @return Selection's snap points
261 */
262 std::vector<NR::Point> getSnapPoints() const;
264 /**
265 * @brief Gets the snap points of a selection that form a convex hull.
266 * @return Selection's convex hull points
267 */
268 std::vector<NR::Point> getSnapPointsConvexHull() const;
270 /**
271 * @brief Connects a slot to be notified of selection changes
272 *
273 * This method connects the given slot such that it will
274 * be called upon any change in the set of selected objects.
275 *
276 * @param slot the slot to connect
277 *
278 * @return the resulting connection
279 */
280 sigc::connection connectChanged(sigc::slot<void, Selection *> const &slot) {
281 return _changed_signal.connect(slot);
282 }
284 /**
285 * @brief Connects a slot to be notified of selected
286 * object modifications
287 *
288 * This method connects the given slot such that it will
289 * receive notifications whenever any selected item is
290 * modified.
291 *
292 * @param slot the slot to connect
293 *
294 * @return the resulting connection
295 *
296 */
297 sigc::connection connectModified(sigc::slot<void, Selection *, guint> const &slot)
298 {
299 return _modified_signal.connect(slot);
300 }
302 private:
303 /** @brief no copy */
304 Selection(Selection const &);
305 /** @brief no assign */
306 void operator=(Selection const &);
308 /** @brief Issues modification notification signals */
309 static gboolean _emit_modified(Selection *selection);
310 /** @brief Schedules an item modification signal to be sent */
311 void _schedule_modified(SPObject *obj, guint flags);
313 /** @brief Issues modified selection signal */
314 void _emitModified(guint flags);
315 /** @brief Issues changed selection signal */
316 void _emitChanged(bool persist_selection_context = false);
318 void _invalidateCachedLists();
320 /** @brief unselect all descendants of the given item */
321 void _removeObjectDescendants(SPObject *obj);
322 /** @brief unselect all ancestors of the given item */
323 void _removeObjectAncestors(SPObject *obj);
324 /** @brief clears the selection (without issuing a notification) */
325 void _clear();
326 /** @brief adds an object (without issuing a notification) */
327 void _add(SPObject *obj);
328 /** @brief removes an object (without issuing a notification) */
329 void _remove(SPObject *obj);
330 /** @brief returns the SPObject corresponding to an xml node (if any) */
331 SPObject *_objectForXMLNode(XML::Node *repr) const;
332 /** @brief Releases an active layer object that is being removed */
333 void _releaseContext(SPObject *obj);
335 mutable GSList *_objs;
336 mutable GSList *_reprs;
337 mutable GSList *_items;
339 GC::soft_ptr<SPDesktop> _desktop;
340 SPObject* _selection_context;
341 guint _flags;
342 guint _idle;
344 std::map<SPObject *, sigc::connection> _modified_connections;
345 std::map<SPObject *, sigc::connection> _release_connections;
346 sigc::connection _context_release_connection;
348 sigc::signal<void, Selection *> _changed_signal;
349 sigc::signal<void, Selection *, guint> _modified_signal;
350 };
352 }
354 #endif
355 /*
356 Local Variables:
357 mode:c++
358 c-file-style:"stroustrup"
359 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
360 indent-tabs-mode:nil
361 fill-column:99
362 End:
363 */
364 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :