42f09e99839d02fef0f7088784d1b6d23b5dac83
1 /**
2 * \brief Find dialog
3 *
4 * Authors:
5 * Bryce W. Harrington <bryce@bryceharrington.org>
6 * Johan Engelen <goejendaagh@zonnet.nl>
7 *
8 * Copyright (C) 2004-2006 Authors
9 *
10 * Released under GNU GPL. Read the file 'COPYING' for more information.
11 */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #include "find.h"
18 #include "verbs.h"
20 #include "message-stack.h"
21 #include "helper/window.h"
22 #include "macros.h"
23 #include "inkscape.h"
24 #include "document.h"
25 #include "desktop.h"
26 #include "selection.h"
27 #include "desktop-handles.h"
29 #include "dialog-events.h"
30 #include "../prefs-utils.h"
31 #include "../verbs.h"
32 #include "../interface.h"
33 #include "../sp-text.h"
34 #include "../sp-flowtext.h"
35 #include "../text-editing.h"
36 #include "../sp-tspan.h"
37 #include "../selection-chemistry.h"
38 #include "../sp-defs.h"
39 #include "../sp-rect.h"
40 #include "../sp-ellipse.h"
41 #include "../sp-star.h"
42 #include "../sp-spiral.h"
43 #include "../sp-path.h"
44 #include "../sp-line.h"
45 #include "../sp-polyline.h"
46 #include "../sp-item-group.h"
47 #include "../sp-use.h"
48 #include "../sp-image.h"
49 #include "../sp-offset.h"
50 #include <xml/repr.h>
53 namespace Inkscape {
54 namespace UI {
55 namespace Dialog {
57 Find::Find()
58 : Dialog ("dialogs.find", SP_VERB_DIALOG_FIND),
59 _entry_text(_("_Text: "), _("Find objects by their text content (exact or partial match)")),
60 _entry_id(_("_ID: "), _("Find objects by the value of the id attribute (exact or partial match)")),
61 _entry_style(_("_Style: "), _("Find objects by the value of the style attribute (exact or partial match)")),
62 _entry_attribute(_("_Attribute: "), _("Find objects by the name of an attribute (exact or partial match)")),
63 _check_search_selection(_("Search in s_election"), _("Limit search to the current selection")),
64 _check_search_layer(_("Search in current _layer"), _("Limit search to the current layer")),
65 _check_include_hidden(_("Include _hidden"), _("Include hidden objects in search")),
66 _check_include_locked(_("Include l_ocked"), _("Include locked objects in search")),
68 _check_all(_("All types"), _("Search in all object types")),
69 _check_all_shapes(_("All shapes"), _("Search all shapes")),
70 _check_rects(_("Rectangles"), _("Search rectangles")),
71 _check_ellipses(_("Ellipses"), _("Search ellipses, arcs, circles")),
72 _check_stars(_("Stars"), _("Search stars and polygons")),
73 _check_spirals(_("Spirals"), _("Search spirals")),
74 _check_paths(_("Paths"), _("Search paths, lines, polylines")),
75 _check_texts(_("Texts"), _("Search text objects")),
76 _check_groups(_("Groups"), _("Search groups")),
77 _check_clones(_("Clones"), _("Search clones")),
78 _check_images(_("Images"), _("Search images")),
79 _check_offsets(_("Offsets"), _("Search offset objects")),
81 _button_clear(_("_Clear"), _("Clear values")),
82 _button_find(_("_Find"), _("Select objects matching all of the fields you filled in"))
83 {
84 // Top level vbox
85 Gtk::VBox *vbox = get_vbox();
86 vbox->set_spacing(4);
88 vbox->pack_start(_entry_text, true, true);
89 vbox->pack_start(_entry_id, true, true);
90 vbox->pack_start(_entry_style, true, true);
91 vbox->pack_start(_entry_attribute, true, true);
93 vbox->pack_start(_check_all, true, true);
94 vbox->pack_start(_check_all_shapes, true, true);
95 vbox->pack_start(_check_rects, true, true);
96 vbox->pack_start(_check_ellipses, true, true);
97 vbox->pack_start(_check_stars, true, true);
98 vbox->pack_start(_check_spirals, true, true);
99 vbox->pack_start(_check_paths, true, true);
100 vbox->pack_start(_check_texts, true, true);
101 vbox->pack_start(_check_groups, true, true);
102 vbox->pack_start(_check_clones, true, true);
103 vbox->pack_start(_check_images, true, true);
104 vbox->pack_start(_check_offsets, true, true);
106 vbox->pack_start(_check_search_selection, true, true);
107 vbox->pack_start(_check_search_layer, true, true);
108 vbox->pack_start(_check_include_hidden, true, true);
109 vbox->pack_start(_check_include_locked, true, true);
111 vbox->pack_start(_button_clear, true, true);
112 vbox->pack_start(_button_find, true, true);
114 _button_clear.signal_clicked().connect(sigc::mem_fun(*this, &Find::onClear));
115 _button_find.signal_clicked().connect(sigc::mem_fun(*this, &Find::onFind));
117 set_default (_button_find); // activatable by Enter
121 show_all_children();
122 }
124 Find::~Find()
125 {
126 }
129 /*########################################################################
130 # FIND helper functions
131 ########################################################################*/
134 bool
135 Find::item_id_match (SPItem *item, const gchar *id, bool exact)
136 {
137 if (SP_OBJECT_REPR (item) == NULL)
138 return false;
140 if (SP_IS_STRING(item)) // SPStrings have "on demand" ids which are useless for searching
141 return false;
143 const gchar *item_id = (SP_OBJECT_REPR (item))->attribute("id");
144 if (item_id == NULL)
145 return false;
147 if (exact) {
148 return ((bool) !strcmp(item_id, id));
149 } else {
150 // g_print ("strstr: %s %s: %s\n", item_id, id, strstr(item_id, id) != NULL? "yes":"no");
151 return ((bool) (strstr(item_id, id) != NULL));
152 }
153 }
155 bool
156 Find::item_text_match (SPItem *item, const gchar *text, bool exact)
157 {
158 if (SP_OBJECT_REPR (item) == NULL)
159 return false;
161 if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
162 const gchar *item_text = sp_te_get_string_multiline (item);
163 if (item_text == NULL)
164 return false;
165 bool ret;
166 if (exact) {
167 ret = ((bool) !strcasecmp(item_text, text));
168 } else {
169 //FIXME: strcasestr
170 ret = ((bool) (strstr(item_text, text) != NULL));
171 }
172 g_free ((void*) item_text);
173 return ret;
174 }
175 return false;
176 }
178 bool
179 Find::item_style_match (SPItem *item, const gchar *text, bool exact)
180 {
181 if (SP_OBJECT_REPR (item) == NULL)
182 return false;
184 const gchar *item_text = (SP_OBJECT_REPR (item))->attribute("style");
185 if (item_text == NULL)
186 return false;
188 if (exact) {
189 return ((bool) !strcmp(item_text, text));
190 } else {
191 return ((bool) (strstr(item_text, text) != NULL));
192 }
193 }
195 bool
196 Find::item_attr_match (SPItem *item, const gchar *name, bool exact)
197 {
198 if (SP_OBJECT_REPR (item) == NULL)
199 return false;
201 if (exact) {
202 const gchar *attr_value = (SP_OBJECT_REPR (item))->attribute(name);
203 return ((bool) (attr_value != NULL));
204 } else {
205 return SP_OBJECT_REPR (item)->matchAttributeName(name);
206 }
207 }
210 GSList *
211 Find::filter_fields (GSList *l, bool exact)
212 {
213 const gchar* text = _entry_text.getEntry()->get_text().c_str();
214 const gchar* id = _entry_id.getEntry()->get_text().c_str();
215 const gchar* style = _entry_style.getEntry()->get_text().c_str();
216 const gchar* attr = _entry_attribute.getEntry()->get_text().c_str();
218 GSList *in = l;
219 GSList *out = NULL;
220 if (strlen (text) != 0) {
221 for (GSList *i = in; i != NULL; i = i->next) {
222 if (item_text_match (SP_ITEM(i->data), text, exact)) {
223 out = g_slist_prepend (out, i->data);
224 }
225 }
226 } else {
227 out = in;
228 }
230 in = out;
231 out = NULL;
232 if (strlen (id) != 0) {
233 for (GSList *i = in; i != NULL; i = i->next) {
234 if (item_id_match (SP_ITEM(i->data), id, exact)) {
235 out = g_slist_prepend (out, i->data);
236 }
237 }
238 } else {
239 out = in;
240 }
242 in = out;
243 out = NULL;
244 if (strlen (style) != 0) {
245 for (GSList *i = in; i != NULL; i = i->next) {
246 if (item_style_match (SP_ITEM(i->data), style, exact)) {
247 out = g_slist_prepend (out, i->data);
248 }
249 }
250 } else {
251 out = in;
252 }
254 in = out;
255 out = NULL;
256 if (strlen (attr) != 0) {
257 for (GSList *i = in; i != NULL; i = i->next) {
258 if (item_attr_match (SP_ITEM(i->data), attr, exact)) {
259 out = g_slist_prepend (out, i->data);
260 }
261 }
262 } else {
263 out = in;
264 }
266 return out;
267 }
270 bool
271 Find::item_type_match (SPItem *item)
272 {
273 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
275 if (SP_IS_RECT(item)) {
276 return (_check_all_shapes.get_active() || _check_rects.get_active());
278 } else if (SP_IS_GENERICELLIPSE(item) || SP_IS_ELLIPSE(item) || SP_IS_ARC(item) || SP_IS_CIRCLE(item)) {
279 return (_check_all_shapes.get_active() || _check_ellipses.get_active());
281 } else if (SP_IS_STAR(item) || SP_IS_POLYGON(item)) {
282 return (_check_all_shapes.get_active() || _check_stars.get_active());
284 } else if (SP_IS_SPIRAL(item)) {
285 return (_check_all_shapes.get_active() || _check_spirals.get_active());
287 } else if (SP_IS_PATH(item) || SP_IS_LINE(item) || SP_IS_POLYLINE(item)) {
288 return (_check_paths.get_active());
290 } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_STRING(item)) {
291 return (_check_texts.get_active());
293 } else if (SP_IS_GROUP(item) && !desktop->isLayer(item) ) { // never select layers!
294 return (_check_groups.get_active());
296 } else if (SP_IS_USE(item)) {
297 return (_check_clones.get_active());
299 } else if (SP_IS_IMAGE(item)) {
300 return (_check_images.get_active());
302 } else if (SP_IS_OFFSET(item)) {
303 return (_check_offsets.get_active());
304 }
306 return false;
307 }
309 GSList *
310 Find::filter_types (GSList *l)
311 {
312 if (_check_all.get_active()) return l;
314 GSList *n = NULL;
315 for (GSList *i = l; i != NULL; i = i->next) {
316 if (item_type_match (SP_ITEM(i->data))) {
317 n = g_slist_prepend (n, i->data);
318 }
319 }
320 return n;
321 }
324 GSList *
325 Find::filter_list (GSList *l, bool exact)
326 {
327 l = filter_fields (l, exact);
328 l = filter_types (l);
329 return l;
330 }
332 GSList *
333 Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked)
334 {
335 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
337 if (SP_IS_DEFS(r))
338 return l; // we're not interested in items in defs
340 if (!strcmp (SP_OBJECT_REPR (r)->name(), "svg:metadata"))
341 return l; // we're not interested in metadata
343 for (SPObject *child = sp_object_first_child(r); child; child = SP_OBJECT_NEXT (child)) {
344 if (SP_IS_ITEM (child) && !SP_OBJECT_IS_CLONED (child) && !desktop->isLayer(SP_ITEM(child))) {
345 if ((hidden || !desktop->itemIsHidden(SP_ITEM(child))) && (locked || !SP_ITEM(child)->isLocked())) {
346 l = g_slist_prepend (l, child);
347 }
348 }
349 l = all_items (child, l, hidden, locked);
350 }
351 return l;
352 }
354 GSList *
355 Find::all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked)
356 {
357 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
359 for (GSList *i = (GSList *) s->itemList(); i != NULL; i = i->next) {
360 if (SP_IS_ITEM (i->data) && !SP_OBJECT_IS_CLONED (i->data) && !desktop->isLayer(SP_ITEM(i->data))) {
361 if (!ancestor || ancestor->isAncestorOf(SP_OBJECT (i->data))) {
362 if ((hidden || !desktop->itemIsHidden(SP_ITEM(i->data))) && (locked || !SP_ITEM(i->data)->isLocked())) {
363 l = g_slist_prepend (l, i->data);
364 }
365 }
366 }
367 if (!ancestor || ancestor->isAncestorOf(SP_OBJECT (i->data))) {
368 l = all_items (SP_OBJECT (i->data), l, hidden, locked);
369 }
370 }
371 return l;
372 }
376 /*########################################################################
377 # BUTTON CLICK HANDLERS (callbacks)
378 ########################################################################*/
380 void
381 Find::onClear()
382 {
383 _entry_text.getEntry()->set_text(Glib::ustring(""));
384 _entry_id.getEntry()->set_text(Glib::ustring(""));
385 _entry_style.getEntry()->set_text(Glib::ustring(""));
386 _entry_attribute.getEntry()->set_text(Glib::ustring(""));
388 _check_all.set_active();
389 }
393 void
394 Find::onFind()
395 {
396 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
398 bool hidden = _check_include_hidden.get_active();
399 bool locked = _check_include_locked.get_active();
401 GSList *l = NULL;
402 if (_check_search_selection.get_active()) {
403 if (_check_search_layer.get_active()) {
404 l = all_selection_items (desktop->selection, l, desktop->currentLayer(), hidden, locked);
405 } else {
406 l = all_selection_items (desktop->selection, l, NULL, hidden, locked);
407 }
408 } else {
409 if (_check_search_layer.get_active()) {
410 l = all_items (desktop->currentLayer(), l, hidden, locked);
411 } else {
412 l = all_items (SP_DOCUMENT_ROOT (sp_desktop_document (desktop)), l, hidden, locked);
413 }
414 }
415 guint all = g_slist_length (l);
417 bool exact = true;
418 GSList *n = NULL;
419 n = filter_list (l, exact);
420 if (n == NULL) {
421 exact = false;
422 n = filter_list (l, exact);
423 }
425 if (n != NULL) {
426 int count = g_slist_length (n);
427 desktop->messageStack()->flashF(Inkscape::NORMAL_MESSAGE,
428 // TRANSLATORS: "%s" is replaced with "exact" or "partial" when this string is displayed
429 ngettext("<b>%d</b> object found (out of <b>%d</b>), %s match.",
430 "<b>%d</b> objects found (out of <b>%d</b>), %s match.",
431 count),
432 count, all, exact? _("exact") : _("partial"));
434 Inkscape::Selection *selection = sp_desktop_selection (desktop);
435 selection->clear();
436 selection->setList(n);
437 scroll_to_show_item (desktop, SP_ITEM(n->data));
438 } else {
439 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No objects found"));
440 }
441 }
446 } // namespace Dialog
447 } // namespace UI
448 } // namespace Inkscape