Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
find.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Authors:
4 * Bryce W. Harrington <bryce@bryceharrington.org>
5 * Johan Engelen <goejendaagh@zonnet.nl>
6 * Jon A. Cruz <jon@joncruz.org>
7 * Abhishek Sharma
8 *
9 * Copyright (C) 2004-2006 Authors
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include "find.h"
15
16#include <glibmm/i18n.h>
17#include <glibmm/regex.h>
18#include <gtkmm/entry.h>
19#include <gtkmm/enums.h>
20#include <gtkmm/sizegroup.h>
21
22#include "desktop.h"
23#include "document-undo.h"
24#include "document.h"
25#include "inkscape.h"
26#include "layer-manager.h"
27#include "message-stack.h"
28#include "selection.h"
29#include "selection-chemistry.h"
30#include "text-editing.h"
31#include "object/sp-defs.h"
32#include "object/sp-ellipse.h"
33#include "object/sp-flowdiv.h"
34#include "object/sp-flowtext.h"
35#include "object/sp-image.h"
36#include "object/sp-line.h"
37#include "object/sp-offset.h"
38#include "object/sp-path.h"
39#include "object/sp-polyline.h"
40#include "object/sp-rect.h"
41#include "object/sp-root.h"
42#include "object/sp-spiral.h"
43#include "object/sp-star.h"
44#include "object/sp-text.h"
45#include "object/sp-tref.h"
46#include "object/sp-tspan.h"
47#include "object/sp-use.h"
48#include "ui/dialog-events.h"
49#include "ui/icon-names.h"
50#include "ui/pack.h"
52#include "xml/node-iterators.h"
53
54namespace Inkscape::UI::Dialog {
55
57 : DialogBase("/dialogs/find", "Find"),
58
59 entry_find(_("F_ind:"), _("Find objects by their content or properties (exact or partial match)")),
60 entry_replace(_("R_eplace:"), _("Replace match with this value")),
61 label_group{Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL)},
62
63 check_scope_all(_("_All")),
64 check_scope_layer(_("Current _layer")),
65 check_scope_selection(_("Sele_ction")),
66 check_searchin_text(_("_Text")),
67 check_searchin_property(_("_Properties")),
68 frame_searchin(_("Search in")),
69 frame_scope(_("Scope")),
70
71 check_case_sensitive(_("Case sensiti_ve")),
72 check_exact_match(_("E_xact match")),
73 check_include_hidden(_("Include _hidden")),
74 check_include_locked(_("Include loc_ked")),
75 expander_options(_("Options")),
76 frame_options(_("General")),
77
78 check_ids(_("_ID")),
79 check_attributename(_("Attribute _name")),
80 check_attributevalue(_("Attri_bute value")),
81 check_style(_("_Style")),
82 check_font(_("F_ont")),
83 check_desc(_("_Desc")),
84 check_title(_("Title")),
85 frame_properties(_("Properties")),
86
87 check_alltypes(_("All types")),
88 check_rects(_("Rectangles")),
89 check_ellipses(_("Ellipses")),
90 check_stars(_("Stars")),
91 check_spirals(_("Spirals")),
92 check_paths(_("Paths")),
93 check_texts(_("Texts")),
94 check_groups(_("Groups")),
95 check_clones(
96 //TRANSLATORS: "Clones" is a noun indicating type of object to find
97 C_("Find dialog", "Clones")),
98
99 check_images(_("Images")),
100 check_offsets(_("Offsets")),
101 frame_types(_("Object types")),
102
103 _left_size_group(Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL)),
104 _right_size_group(Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL)),
105
106 status(""),
107 button_find(_("_Find")),
108 button_replace(_("_Replace All")),
109 _action_replace(false),
110 blocked(false),
111
112 hbox_searchin(Gtk::Orientation::HORIZONTAL),
113 vbox_scope(Gtk::Orientation::VERTICAL),
114 vbox_searchin(Gtk::Orientation::VERTICAL),
115 vbox_options1(Gtk::Orientation::VERTICAL),
116 vbox_options2(Gtk::Orientation::VERTICAL),
117 hbox_options(Gtk::Orientation::HORIZONTAL),
118 vbox_expander(Gtk::Orientation::VERTICAL),
119 hbox_properties(Gtk::Orientation::HORIZONTAL),
120 vbox_properties1(Gtk::Orientation::VERTICAL),
121 vbox_properties2(Gtk::Orientation::VERTICAL),
122 vbox_types1(Gtk::Orientation::VERTICAL),
123 vbox_types2(Gtk::Orientation::VERTICAL),
124 hbox_types(Gtk::Orientation::HORIZONTAL),
125 hboxbutton_row(Gtk::Orientation::HORIZONTAL)
126{
127 auto const label1 = entry_find.getLabel();
128 entry_find.getEntry()->set_hexpand();
129 entry_find.getEntry()->set_halign(Gtk::Align::FILL);
130 label_group->add_widget(*label1);
131 label1->set_xalign(0);
132 label1->set_hexpand(false);
133 auto const label2 = entry_replace.getLabel();
134 entry_replace.getEntry()->set_hexpand();
135 entry_replace.getEntry()->set_halign(Gtk::Align::FILL);
136 label_group->add_widget(*label2);
137 label2->set_xalign(0);
138 label2->set_hexpand(false);
139
140 static constexpr int MARGIN = 4;
141 set_margin_start(MARGIN);
142 set_margin_end(MARGIN);
143 entry_find.set_margin_top(MARGIN);
144 entry_replace.set_margin_top(MARGIN);
145 frame_searchin.set_margin_top(MARGIN);
146 frame_scope.set_margin_top(MARGIN);
147 button_find.set_use_underline();
148 button_find.set_tooltip_text(_("Select all objects matching the selection criteria"));
149 button_replace.set_use_underline();
150 button_replace.set_tooltip_text(_("Replace all matches"));
151 check_scope_all.set_use_underline();
152 check_scope_all.set_tooltip_text(_("Search in all layers"));
153 check_scope_all.set_active(true);
154 check_scope_layer.set_use_underline();
155 check_scope_layer.set_tooltip_text(_("Limit search to the current layer"));
156 check_scope_selection.set_use_underline();
157 check_scope_selection.set_tooltip_text(_("Limit search to the current selection"));
158 check_searchin_text.set_use_underline();
159 check_searchin_text.set_tooltip_text(_("Search in text objects"));
160 check_searchin_text.set_active(true);
161 check_searchin_property.set_use_underline();
162 check_searchin_property.set_tooltip_text(_("Search in object properties, styles, attributes and IDs"));
163 check_case_sensitive.set_use_underline();
164 check_case_sensitive.set_tooltip_text(_("Match upper/lower case"));
165 check_case_sensitive.set_active(false);
166 check_exact_match.set_use_underline();
167 check_exact_match.set_tooltip_text(_("Match whole objects only"));
168 check_exact_match.set_active(false);
169 check_include_hidden.set_use_underline();
170 check_include_hidden.set_tooltip_text(_("Include hidden objects in search"));
171 check_include_hidden.set_active(false);
172 check_include_locked.set_use_underline();
173 check_include_locked.set_tooltip_text(_("Include locked objects in search"));
174 check_include_locked.set_active(false);
175 check_ids.set_use_underline();
176 check_ids.set_tooltip_text(_("Search ID name"));
177 check_ids.set_active(true);
178 check_attributename.set_use_underline();
179 check_attributename.set_tooltip_text(_("Search attribute name"));
180 check_attributename.set_active(false);
181 check_attributevalue.set_use_underline();
182 check_attributevalue.set_tooltip_text(_("Search attribute value"));
183 check_attributevalue.set_active(true);
184 check_style.set_use_underline();
185 check_style.set_tooltip_text(_("Search style"));
186 check_style.set_active(true);
187 check_font.set_use_underline();
188 check_font.set_tooltip_text(_("Search fonts"));
189 check_font.set_active(false);
190 check_desc.set_use_underline();
191 check_desc.set_tooltip_text(_("Search description"));
192 check_desc.set_active(false);
193 check_title.set_use_underline();
194 check_title.set_tooltip_text(_("Search title"));
195 check_title.set_active(false);
196 check_alltypes.set_use_underline();
197 check_alltypes.set_tooltip_text(_("Search all object types"));
198 check_alltypes.set_active(true);
199 check_rects.set_use_underline();
200 check_rects.set_tooltip_text(_("Search rectangles"));
201 check_rects.set_active(false);
202 check_ellipses.set_use_underline();
203 check_ellipses.set_tooltip_text(_("Search ellipses, arcs, circles"));
204 check_ellipses.set_active(false);
205 check_stars.set_use_underline();
206 check_stars.set_tooltip_text(_("Search stars and polygons"));
207 check_stars.set_active(false);
208 check_spirals.set_use_underline();
209 check_spirals.set_tooltip_text(_("Search spirals"));
210 check_spirals.set_active(false);
211 check_paths.set_use_underline();
212 check_paths.set_tooltip_text(_("Search paths, lines, polylines"));
213 check_paths.set_active(false);
214 check_texts.set_use_underline();
215 check_texts.set_tooltip_text(_("Search text objects"));
216 check_texts.set_active(false);
217 check_groups.set_use_underline();
218 check_groups.set_tooltip_text(_("Search groups"));
219 check_groups.set_active(false),
220 check_clones.set_use_underline();
221 check_clones.set_tooltip_text(_("Search clones"));
222 check_clones.set_active(false);
223 check_images.set_use_underline();
224 check_images.set_tooltip_text(_("Search images"));
225 check_images.set_active(false);
226 check_offsets.set_use_underline();
227 check_offsets.set_tooltip_text(_("Search offset objects"));
228 check_offsets.set_active(false);
229
230 entry_find.getEntry()->set_width_chars(25);
231 entry_replace.getEntry()->set_width_chars(25);
232
236 frame_searchin.set_child(vbox_searchin);
237
243 hbox_searchin.set_spacing(12);
246 frame_scope.set_child(vbox_scope);
247
256 hbox_options.set_spacing(4);
259 frame_options.set_child(hbox_options);
260
268 vbox_properties2.set_valign(Gtk::Align::START);
269 _left_size_group->add_widget(check_ids);
270 _left_size_group->add_widget(check_style);
271 _left_size_group->add_widget(check_font);
272 _left_size_group->add_widget(check_desc);
273 _left_size_group->add_widget(check_title);
276 hbox_properties.set_spacing(4);
280
292 vbox_types2.set_valign(Gtk::Align::END);
293 _left_size_group->add_widget(check_alltypes);
294 _left_size_group->add_widget(check_paths);
295 _left_size_group->add_widget(check_texts);
296 _left_size_group->add_widget(check_groups);
297 _left_size_group->add_widget(check_clones);
298 _left_size_group->add_widget(check_images);
299 _right_size_group->add_widget(check_offsets);
300 _right_size_group->add_widget(check_rects);
302 _right_size_group->add_widget(check_stars);
303 _right_size_group->add_widget(check_spirals);
304 hbox_types.set_spacing(4);
307 frame_types.set_child(hbox_types);
308
309 vbox_expander.set_spacing(4);
313
314 expander_options.set_use_underline();
316
317 box_buttons.set_spacing(6);
318 box_buttons.set_homogeneous(true);
320 UI::pack_end(box_buttons, button_find, false, true);
321 hboxbutton_row.set_spacing(6);
324
325 set_spacing(6);
326 UI::pack_start(*this, entry_find, false, false);
327 UI::pack_start(*this, entry_replace, false, false);
328 UI::pack_start(*this, hbox_searchin, false, false);
329 UI::pack_start(*this, expander_options, false, false);
330 UI::pack_end(*this, hboxbutton_row, false, false);
331
332 checkProperties.push_back(&check_ids);
333 checkProperties.push_back(&check_style);
334 checkProperties.push_back(&check_font);
335 checkProperties.push_back(&check_desc);
336 checkProperties.push_back(&check_title);
339
340 checkTypes.push_back(&check_paths);
341 checkTypes.push_back(&check_texts);
342 checkTypes.push_back(&check_groups);
343 checkTypes.push_back(&check_clones);
344 checkTypes.push_back(&check_images);
345 checkTypes.push_back(&check_offsets);
346 checkTypes.push_back(&check_rects);
347 checkTypes.push_back(&check_ellipses);
348 checkTypes.push_back(&check_stars);
349 checkTypes.push_back(&check_spirals);
350
351 // set signals to handle clicks
352 expander_options.property_expanded().signal_changed().connect(sigc::mem_fun(*this, &Find::onExpander));
353 button_find.signal_clicked().connect(sigc::mem_fun(*this, &Find::onFind));
354 button_replace.signal_clicked().connect(sigc::mem_fun(*this, &Find::onReplace));
355 check_searchin_text.signal_toggled().connect(sigc::mem_fun(*this, &Find::onSearchinText));
356 check_searchin_property.signal_toggled().connect(sigc::mem_fun(*this, &Find::onSearchinProperty));
357 check_alltypes.signal_toggled().connect(sigc::mem_fun(*this, &Find::onToggleAlltypes));
358
359 for (auto & checkProperty : checkProperties) {
360 checkProperty->signal_toggled().connect(sigc::mem_fun(*this, &Find::onToggleCheck));
361 }
362
363 for (auto & checkType : checkTypes) {
364 checkType->signal_toggled().connect(sigc::mem_fun(*this, &Find::onToggleCheck));
365 }
366
369
370 button_find.set_receives_default();
371 //button_find.grab_default(); // activatable by Enter
372
373 entry_find.getEntry()->grab_focus();
374}
375
376Find::~Find() = default;
377
379{
380 if (auto selection = getSelection()) {
382 if (item && entry_find.getEntry()->get_text_length() == 0) {
383 Glib::ustring str = sp_te_get_string_multiline(item);
384 if (!str.empty()) {
385 entry_find.getEntry()->set_text(str);
386 }
387 }
388 }
389}
390
392{
393 if (!blocked) {
394 status.set_text("");
395 }
396}
397
398/*########################################################################
399# FIND helper functions
400########################################################################*/
401
402Glib::ustring Find::find_replace(const gchar *str, const gchar *find, const gchar *replace, bool exact, bool casematch, bool replaceall)
403{
404 Glib::ustring ustr = str;
405 Glib::ustring ufind = find;
406 gsize replace_length = Glib::ustring(replace).length();
407 if (!casematch) {
408 ufind = ufind.lowercase();
409 }
410 gsize n = find_strcmp_pos(ustr.c_str(), ufind.c_str(), exact, casematch);
411 while (n != std::string::npos) {
412 ustr.replace(n, ufind.length(), replace);
413 if (!replaceall) {
414 return ustr;
415 }
416 // Start the next search after the last replace character to avoid infinite loops (replace "a" with "aaa" etc)
417 n = find_strcmp_pos(ustr.c_str(), ufind.c_str(), exact, casematch, n + replace_length);
418 }
419 return ustr;
420}
421
422gsize Find::find_strcmp_pos(const gchar *str, const gchar *find, bool exact, bool casematch, gsize start/*=0*/)
423{
424 Glib::ustring ustr = str ? str : "";
425 Glib::ustring ufind = find;
426
427 if (!casematch) {
428 ustr = ustr.lowercase();
429 ufind = ufind.lowercase();
430 }
431
432 gsize pos = std::string::npos;
433 if (exact) {
434 if (ustr == ufind) {
435 pos = 0;
436 }
437 } else {
438 pos = ustr.find(ufind, start);
439 }
440
441 return pos;
442}
443
444
445bool Find::find_strcmp(const gchar *str, const gchar *find, bool exact, bool casematch)
446{
447 return (std::string::npos != find_strcmp_pos(str, find, exact, casematch));
448}
449
450bool Find::item_desc_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace)
451{
452 gchar* desc = item->desc();
453 bool found = find_strcmp(desc, text, exact, casematch);
454 if (found && replace) {
455 Glib::ustring r = find_replace(desc, text, entry_replace.getEntry()->get_text().c_str(), exact, casematch, replace);
456 item->setDesc(r.c_str());
457 }
458 g_free(desc);
459 return found;
460}
461
462bool Find::item_title_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace)
463{
464 gchar* title = item->title();
465 bool found = find_strcmp(title, text, exact, casematch);
466 if (found && replace) {
467 Glib::ustring r = find_replace(title, text, entry_replace.getEntry()->get_text().c_str(), exact, casematch, replace);
468 item->setTitle(r.c_str());
469 }
470 g_free(title);
471 return found;
472}
473
474bool Find::item_text_match (SPItem *item, const gchar *find, bool exact, bool casematch, bool replace/*=false*/)
475{
476 if (item->getRepr() == nullptr) {
477 return false;
478 }
479
480 Glib::ustring item_text = sp_te_get_string_multiline(item);
481
482 if (!item_text.empty()) {
483 bool found = find_strcmp(item_text.c_str(), find, exact, casematch);
484
485 if (found && replace) {
486 Glib::ustring ufind = find;
487 if (!casematch) {
488 ufind = ufind.lowercase();
489 }
490
491 Inkscape::Text::Layout const *layout = te_get_layout (item);
492 if (!layout) {
493 return found;
494 }
495
496 Glib::ustring replace = entry_replace.getEntry()->get_text();
497 gsize n = find_strcmp_pos(item_text.c_str(), ufind.c_str(), exact, casematch);
498 static Inkscape::Text::Layout::iterator _begin_w;
500 while (n != std::string::npos) {
501 _begin_w = layout->charIndexToIterator(n);
502 _end_w = layout->charIndexToIterator(n + ufind.length());
503 sp_te_replace(item, _begin_w, _end_w, replace.c_str());
504 item_text = sp_te_get_string_multiline (item);
505 n = find_strcmp_pos(item_text.c_str(), ufind.c_str(), exact, casematch, n + replace.length());
506 }
507 }
508
509 return found;
510 }
511 return false;
512}
513
514
515bool Find::item_id_match (SPItem *item, const gchar *id, bool exact, bool casematch, bool replace/*=false*/)
516{
517 if (!item->getRepr()) {
518 return false;
519 }
520
521 const gchar *item_id = item->getRepr()->attribute("id");
522 if (!item_id) {
523 return false;
524 }
525
526 bool found = find_strcmp(item_id, id, exact, casematch);
527
528 if (found && replace) {
529 gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
530 Glib::ustring new_item_style = find_replace(item_id, id, replace_text , exact, casematch, true);
531 if (new_item_style != item_id) {
532 item->setAttribute("id", new_item_style);
533 }
534 g_free(replace_text);
535 }
536
537 return found;
538}
539
540bool Find::item_style_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/)
541{
542 if (item->getRepr() == nullptr) {
543 return false;
544 }
545
546 gchar *item_style = g_strdup(item->getRepr()->attribute("style"));
547 if (item_style == nullptr) {
548 return false;
549 }
550
551 bool found = find_strcmp(item_style, text, exact, casematch);
552
553 if (found && replace) {
554 gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
555 Glib::ustring new_item_style = find_replace(item_style, text, replace_text , exact, casematch, true);
556 if (new_item_style != item_style) {
557 item->setAttribute("style", new_item_style);
558 }
559 g_free(replace_text);
560 }
561
562 g_free(item_style);
563 return found;
564}
565
566bool Find::item_attr_match(SPItem *item, const gchar *text, bool exact, bool /*casematch*/, bool replace/*=false*/)
567{
568 bool found = false;
569
570 if (item->getRepr() == nullptr) {
571 return false;
572 }
573
574 gchar *attr_value = g_strdup(item->getRepr()->attribute(text));
575 if (exact) {
576 found = (attr_value != nullptr);
577 } else {
578 found = item->getRepr()->matchAttributeName(text);
579 }
580 g_free(attr_value);
581
582 // TODO - Rename attribute name ?
583 if (found && replace) {
584 found = false;
585 }
586
587 return found;
588}
589
590bool Find::item_attrvalue_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/)
591{
592 bool ret = false;
593
594 if (item->getRepr() == nullptr) {
595 return false;
596 }
597
598 for (const auto & iter:item->getRepr()->attributeList()) {
599 const gchar* key = g_quark_to_string(iter.key);
600 gchar *attr_value = g_strdup(item->getRepr()->attribute(key));
601 bool found = find_strcmp(attr_value, text, exact, casematch);
602 if (found) {
603 ret = true;
604 }
605
606 if (found && replace) {
607 gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
608 Glib::ustring new_item_style = find_replace(attr_value, text, replace_text , exact, casematch, true);
609 if (new_item_style != attr_value) {
610 item->setAttribute(key, new_item_style);
611 }
612 }
613
614 g_free(attr_value);
615 }
616
617 return ret;
618}
619
620
621bool Find::item_font_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool /*replace*/ /*=false*/)
622{
623 bool ret = false;
624
625 if (item->getRepr() == nullptr) {
626 return false;
627 }
628
629 const gchar *item_style = item->getRepr()->attribute("style");
630 if (item_style == nullptr) {
631 return false;
632 }
633
634 std::vector<Glib::ustring> vFontTokenNames;
635 vFontTokenNames.emplace_back("font-family:");
636 vFontTokenNames.emplace_back("-inkscape-font-specification:");
637
638 std::vector<Glib::ustring> vStyleTokens = Glib::Regex::split_simple(";", item_style);
639 for (auto & vStyleToken : vStyleTokens) {
640 Glib::ustring token = vStyleToken;
641 for (const auto & vFontTokenName : vFontTokenNames) {
642 if ( token.find(vFontTokenName) != std::string::npos) {
643 Glib::ustring font1 = Glib::ustring(vFontTokenName).append(text);
644 bool found = find_strcmp(token.c_str(), font1.c_str(), exact, casematch);
645 if (found) {
646 ret = true;
647 if (_action_replace) {
648 gchar *replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str());
649 gchar *orig_str = g_strdup(token.c_str());
650 // Exact match fails since the "font-family:" is in the token, since the find was exact it still works with false below
651 Glib::ustring new_item_style = find_replace(orig_str, text, replace_text , false /*exact*/, casematch, true);
652 if (new_item_style != orig_str) {
653 vStyleToken = new_item_style;
654 }
655 g_free(orig_str);
656 g_free(replace_text);
657 }
658 }
659 }
660 }
661 }
662
663 if (ret && _action_replace) {
664 Glib::ustring new_item_style;
665 for (const auto & vStyleToken : vStyleTokens) {
666 new_item_style.append(vStyleToken).append(";");
667 }
668 new_item_style.erase(new_item_style.size()-1);
669 item->setAttribute("style", new_item_style);
670 }
671
672 return ret;
673}
674
675
676std::vector<SPItem*> Find::filter_fields (std::vector<SPItem*> &l, bool exact, bool casematch)
677{
678 Glib::ustring tmp = entry_find.getEntry()->get_text();
679 if (tmp.empty()) {
680 return l;
681 }
682 gchar* text = g_strdup(tmp.c_str());
683
684 std::vector<SPItem*> in = l;
685 std::vector<SPItem*> out;
686
687 if (check_searchin_text.get_active()) {
688 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
689 SPObject *obj = *i;
690 auto item = cast<SPItem>(obj);
691 g_assert(item != nullptr);
692 if (item_text_match(item, text, exact, casematch)) {
693 if (out.end()==find(out.begin(),out.end(), *i)) {
694 out.push_back(*i);
695 if (_action_replace) {
696 item_text_match(item, text, exact, casematch, _action_replace);
697 }
698 }
699 }
700 }
701 }
702 else if (check_searchin_property.get_active()) {
703
704 bool ids = check_ids.get_active();
705 bool style = check_style.get_active();
706 bool font = check_font.get_active();
707 bool desc = check_desc.get_active();
708 bool title = check_title.get_active();
709 bool attrname = check_attributename.get_active();
710 bool attrvalue = check_attributevalue.get_active();
711
712 if (ids) {
713 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
714 SPObject *obj = *i;
715 auto item = cast<SPItem>(obj);
716 if (item_id_match(item, text, exact, casematch)) {
717 if (out.end()==find(out.begin(),out.end(), *i)) {
718 out.push_back(*i);
719 if (_action_replace) {
720 item_id_match(item, text, exact, casematch, _action_replace);
721 }
722 }
723 }
724 }
725 }
726
727
728 if (style) {
729 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
730 SPObject *obj = *i;
731 auto item = cast<SPItem>(obj);
732 g_assert(item != nullptr);
733 if (item_style_match(item, text, exact, casematch)) {
734 if (out.end()==find(out.begin(),out.end(), *i)){
735 out.push_back(*i);
736 if (_action_replace) {
737 item_style_match(item, text, exact, casematch, _action_replace);
738 }
739 }
740 }
741 }
742 }
743
744
745 if (attrname) {
746 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
747 SPObject *obj = *i;
748 auto item = cast<SPItem>(obj);
749 g_assert(item != nullptr);
750 if (item_attr_match(item, text, exact, casematch)) {
751 if (out.end()==find(out.begin(),out.end(), *i)) {
752 out.push_back(*i);
753 if (_action_replace) {
754 item_attr_match(item, text, exact, casematch, _action_replace);
755 }
756 }
757 }
758 }
759 }
760
761
762 if (attrvalue) {
763 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
764 SPObject *obj = *i;
765 auto item = cast<SPItem>(obj);
766 g_assert(item != nullptr);
767 if (item_attrvalue_match(item, text, exact, casematch)) {
768 if (out.end()==find(out.begin(),out.end(), *i)) {
769 out.push_back(*i);
770 if (_action_replace) {
771 item_attrvalue_match(item, text, exact, casematch, _action_replace);
772 }
773 }
774 }
775 }
776 }
777
778
779 if (font) {
780 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
781 SPObject *obj = *i;
782 auto item = cast<SPItem>(obj);
783 g_assert(item != nullptr);
784 if (item_font_match(item, text, exact, casematch)) {
785 if (out.end()==find(out.begin(),out.end(),*i)) {
786 out.push_back(*i);
787 if (_action_replace) {
788 item_font_match(item, text, exact, casematch, _action_replace);
789 }
790 }
791 }
792 }
793 }
794 if (desc) {
795 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
796 SPObject *obj = *i;
797 auto item = cast<SPItem>(obj);
798 g_assert(item != nullptr);
799 if (item_desc_match(item, text, exact, casematch)) {
800 if (out.end()==find(out.begin(),out.end(),*i)) {
801 out.push_back(*i);
802 if (_action_replace) {
803 item_desc_match(item, text, exact, casematch, _action_replace);
804 }
805 }
806 }
807 }
808 }
809 if (title) {
810 for (std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; ++i) {
811 SPObject *obj = *i;
812 auto item = cast<SPItem>(obj);
813 g_assert(item != nullptr);
814 if (item_title_match(item, text, exact, casematch)) {
815 if (out.end()==find(out.begin(),out.end(),*i)) {
816 out.push_back(*i);
817 if (_action_replace) {
818 item_title_match(item, text, exact, casematch, _action_replace);
819 }
820 }
821 }
822 }
823 }
824
825 }
826
827 g_free(text);
828
829 return out;
830}
831
832
834{
835 bool all =check_alltypes.get_active();
836
837 if (is<SPRect>(item)) {
838 return ( all ||check_rects.get_active());
839
840 } else if (is<SPGenericEllipse>(item)) {
841 return ( all || check_ellipses.get_active());
842
843 } else if (is<SPStar>(item) || is<SPPolygon>(item)) {
844 return ( all || check_stars.get_active());
845
846 } else if (is<SPSpiral>(item)) {
847 return ( all || check_spirals.get_active());
848
849 } else if (is<SPPath>(item) || is<SPLine>(item) || is<SPPolyLine>(item)) {
850 return (all || check_paths.get_active());
851
852 } else if (is<SPText>(item) || is<SPTSpan>(item) ||
853 is<SPTRef>(item) ||
854 is<SPFlowtext>(item) || is<SPFlowdiv>(item) ||
855 is<SPFlowtspan>(item) || is<SPFlowpara>(item)) {
856 return (all || check_texts.get_active());
857
858 } else if (is<SPGroup>(item) &&
859 !getDesktop()->layerManager().isLayer(item)) { // never select layers!
860 return (all || check_groups.get_active());
861
862 } else if (is<SPUse>(item)) {
863 return (all || check_clones.get_active());
864
865 } else if (is<SPImage>(item)) {
866 return (all || check_images.get_active());
867
868 } else if (is<SPOffset>(item)) {
869 return (all || check_offsets.get_active());
870 }
871
872 return false;
873}
874
875std::vector<SPItem*> Find::filter_types (std::vector<SPItem*> &l)
876{
877 std::vector<SPItem*> n;
878 for (std::vector<SPItem*>::const_reverse_iterator i=l.rbegin(); l.rend() != i; ++i) {
879 SPObject *obj = *i;
880 auto item = cast<SPItem>(obj);
881 g_assert(item != nullptr);
882 if (item_type_match(item)) {
883 n.push_back(*i);
884 }
885 }
886 return n;
887}
888
889
890std::vector<SPItem*> &Find::filter_list (std::vector<SPItem*> &l, bool exact, bool casematch)
891{
892 l = filter_types (l);
893 l = filter_fields (l, exact, casematch);
894 return l;
895}
896
897std::vector<SPItem*> &Find::all_items (SPObject *r, std::vector<SPItem*> &l, bool hidden, bool locked)
898{
899 if (is<SPDefs>(r)) {
900 return l; // we're not interested in items in defs
901 }
902
903 if (!strcmp(r->getRepr()->name(), "svg:metadata")) {
904 return l; // we're not interested in metadata
905 }
906
907 auto desktop = getDesktop();
908 for (auto& child: r->children) {
909 auto item = cast<SPItem>(&child);
910 if (item && !child.cloned && !desktop->layerManager().isLayer(item)) {
911 if ((hidden || !desktop->itemIsHidden(item)) && (locked || !item->isLocked())) {
912 l.insert(l.begin(),(SPItem*)&child);
913 }
914 }
915 l = all_items (&child, l, hidden, locked);
916 }
917 return l;
918}
919
920std::vector<SPItem*> &Find::all_selection_items (Inkscape::Selection *s, std::vector<SPItem*> &l, SPObject *ancestor, bool hidden, bool locked)
921{
922 auto desktop = getDesktop();
923 auto itemlist = s->items();
924 for (auto i=boost::rbegin(itemlist); boost::rend(itemlist) != i; ++i) {
925 SPObject *obj = *i;
926 auto item = cast<SPItem>(obj);
927 g_assert(item != nullptr);
928 if (item && !item->cloned && !desktop->layerManager().isLayer(item)) {
929 if (!ancestor || ancestor->isAncestorOf(item)) {
930 if ((hidden || !desktop->itemIsHidden(item)) && (locked || !item->isLocked())) {
931 l.push_back(*i);
932 }
933 }
934 }
935 if (!ancestor || ancestor->isAncestorOf(item)) {
936 l = all_items(item, l, hidden, locked);
937 }
938 }
939 return l;
940}
941
942
943
944/*########################################################################
945# BUTTON CLICK HANDLERS (callbacks)
946########################################################################*/
947
949{
950 _action_replace = false;
951 onAction();
952
953 // Return focus to the find entry
954 entry_find.getEntry()->grab_focus();
955}
956
958{
959 if (entry_find.getEntry()->get_text().length() < 1) {
960 status.set_text(_("Nothing to replace"));
961 return;
962 }
963 _action_replace = true;
964 onAction();
965
966 // Return focus to the find entry
967 entry_find.getEntry()->grab_focus();
968}
969
971{
972 auto desktop = getDesktop();
973 bool hidden = check_include_hidden.get_active();
974 bool locked = check_include_locked.get_active();
975 bool exact = check_exact_match.get_active();
976 bool casematch = check_case_sensitive.get_active();
977 blocked = true;
978
979 std::vector<SPItem*> l;
980 if (check_scope_selection.get_active()) {
981 if (check_scope_layer.get_active()) {
983 } else {
984 l = all_selection_items (desktop->getSelection(), l, nullptr, hidden, locked);
985 }
986 } else {
987 if (check_scope_layer.get_active()) {
988 l = all_items (desktop->layerManager().currentLayer(), l, hidden, locked);
989 } else {
990 l = all_items(desktop->getDocument()->getRoot(), l, hidden, locked);
991 }
992 }
993 guint all = l.size();
994
995 std::vector<SPItem*> n = filter_list (l, exact, casematch);
996
997 if (!n.empty()) {
998 int count = n.size();
1000 // TRANSLATORS: "%s" is replaced with "exact" or "partial" when this string is displayed
1001 ngettext("<b>%d</b> object found (out of <b>%d</b>), %s match.",
1002 "<b>%d</b> objects found (out of <b>%d</b>), %s match.",
1003 count),
1004 count, all, exact? _("exact") : _("partial"));
1005 if (_action_replace){
1006 // TRANSLATORS: "%1" is replaced with the number of matches
1007 status.set_text(Glib::ustring::compose(ngettext("%1 match replaced","%1 matches replaced",count), count));
1008 }
1009 else {
1010 // TRANSLATORS: "%1" is replaced with the number of matches
1011 status.set_text(Glib::ustring::compose(ngettext("%1 object found","%1 objects found",count), count));
1012 bool attributenameyok = !check_attributename.get_active();
1013 button_replace.set_sensitive(attributenameyok);
1014 }
1015
1017 selection->clear();
1018 selection->setList(n);
1019 SPObject *obj = n[0];
1020 auto item = cast<SPItem>(obj);
1021 g_assert(item != nullptr);
1023
1024 if (_action_replace) {
1025 DocumentUndo::done(desktop->getDocument(), _("Replace text or property"), INKSCAPE_ICON("draw-text"));
1026 }
1027
1028 } else {
1029 status.set_text(_("Nothing found"));
1030 if (!check_scope_selection.get_active()) {
1032 selection->clear();
1033 }
1034 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No objects found"));
1035 }
1036 blocked = false;
1037
1038}
1039
1041{
1042 bool objectok = false;
1043 status.set_text("");
1044
1045 if (check_alltypes.get_active()) {
1046 objectok = true;
1047 }
1048 for (auto & checkType : checkTypes) {
1049 if (checkType->get_active()) {
1050 objectok = true;
1051 }
1052 }
1053
1054 if (!objectok) {
1055 status.set_text(_("Select an object type"));
1056 }
1057
1058
1059 bool propertyok = false;
1060
1061 if (!check_searchin_property.get_active()) {
1062 propertyok = true;
1063 } else {
1064
1065 for (auto & checkProperty : checkProperties) {
1066 if (checkProperty->get_active()) {
1067 propertyok = true;
1068 }
1069 }
1070 }
1071
1072 if (!propertyok) {
1073 status.set_text(_("Select a property"));
1074 }
1075
1076 // Can't replace attribute names
1077 // bool attributenameyok = !check_attributename.get_active();
1078
1079 button_find.set_sensitive(objectok && propertyok);
1080 // button_replace.set_sensitive(objectok && propertyok && attributenameyok);
1081 button_replace.set_sensitive(false);
1082}
1083
1085{
1086 bool all =check_alltypes.get_active();
1087 for (auto & checkType : checkTypes) {
1088 checkType->set_sensitive(!all);
1089 }
1090
1091 onToggleCheck();
1092}
1093
1095{
1096 searchinToggle(false);
1097 onToggleCheck();
1098}
1099
1101{
1102 searchinToggle(true);
1103 onToggleCheck();
1104}
1105
1107{
1108 for (auto & checkProperty : checkProperties) {
1109 checkProperty->set_sensitive(on);
1110 }
1111}
1112
1114{
1115 if (!expander_options.get_expanded())
1117}
1118
1119/*########################################################################
1120# UTILITY
1121########################################################################*/
1122
1124{
1125 // TODO: resize dialog window when the expander is closed
1126 // set_size_request(-1, -1);
1127}
1128
1129} // namespace Inkscape::UI::Dialog
1130
1131/*
1132 Local Variables:
1133 mode:c++
1134 c-file-style:"stroustrup"
1135 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1136 indent-tabs-mode:nil
1137 fill-column:99
1138 End:
1139*/
1140// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
TODO: insert short description here.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
SPGroup * currentLayer() const
Returns current top layer.
bool isLayer(SPObject *object) const
True if object is a layer.
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
MessageId flashF(MessageType type, char const *format,...) G_GNUC_PRINTF(3
temporarily pushes a message onto the stack using printf-like formatting
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
boost::enable_if< boost::is_base_of< SPObject, T >, void >::type setList(const std::vector< T * > &objs)
Selects exactly the specified objects.
Definition object-set.h:299
void clear()
Unselects all selected objects.
SPItem * singleItem()
Returns a single selected item.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
Holds a position within the glyph output of Layout.
Definition Layout-TNG.h:981
Generates the layout for either wrapped or non-wrapped text and stores the result.
Definition Layout-TNG.h:144
iterator charIndexToIterator(int char_index) const
Returns an iterator pointing at the given character index.
DialogBase is the base class for the dialog system.
Definition dialog-base.h:42
Selection * getSelection() const
Definition dialog-base.h:84
SPDesktop * getDesktop() const
Definition dialog-base.h:79
Gtk::CheckButton check_include_locked
Definition find.h:225
void onFind()
Callbacks for pressing the dialog buttons.
Definition find.cpp:948
Gtk::CheckButton check_texts
Definition find.h:262
Gtk::CheckButton check_groups
Definition find.h:263
Gtk::CheckButton check_ellipses
Definition find.h:258
Gtk::Box vbox_properties2
Definition find.h:245
Gtk::CheckButton check_scope_selection
Definition find.h:210
Gtk::CheckButton check_ids
Property type widgets.
Definition find.h:236
Gtk::Button button_replace
Definition find.h:287
gsize find_strcmp_pos(const gchar *str, const gchar *find, bool exact, bool casematch, gsize start=0)
Find a string within a string and return the position with options for exact, casematching and search...
Definition find.cpp:422
Glib::RefPtr< Gtk::SizeGroup > _left_size_group
Definition find.h:272
std::vector< Gtk::CheckButton * > checkTypes
A vector of all the check option widgets for easy processing.
Definition find.h:278
bool item_id_match(SPItem *item, const gchar *id, bool exact, bool casematch, bool replace=false)
Returns true if the SPItem 'item' has the same id.
Definition find.cpp:515
void desktopReplaced() override
Called when the desktop has certainly changed.
Definition find.cpp:378
Gtk::CheckButton check_stars
Definition find.h:259
Gtk::CheckButton check_images
Definition find.h:265
Glib::RefPtr< Gtk::SizeGroup > _right_size_group
Definition find.h:273
Gtk::CheckButton check_case_sensitive
General option widgets.
Definition find.h:222
Gtk::CheckButton check_include_hidden
Definition find.h:224
void searchinToggle(bool on)
Toggle all the properties checkboxes.
Definition find.cpp:1106
Gtk::CheckButton check_paths
Definition find.h:261
Gtk::Box vbox_properties1
Definition find.h:244
bool item_attr_match(SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false)
Returns true if found the SPItem 'item' has the same attribute name.
Definition find.cpp:566
UI::Widget::Frame frame_searchin
Definition find.h:216
std::vector< SPItem * > & all_items(SPObject *r, std::vector< SPItem * > &l, bool hidden, bool locked)
recursive function to return a list of all the items in the SPObject tree
Definition find.cpp:897
bool find_strcmp(const gchar *str, const gchar *find, bool exact, bool casematch)
Find a string within a string and returns true if found with options for exact and casematching.
Definition find.cpp:445
Gtk::Expander expander_options
Definition find.h:230
bool item_attrvalue_match(SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false)
Returns true if the SPItem 'item' has the same attribute value.
Definition find.cpp:590
bool item_type_match(SPItem *item)
Definition find.cpp:833
Gtk::CheckButton check_style
Definition find.h:239
Gtk::CheckButton check_desc
Definition find.h:241
std::vector< SPItem * > filter_fields(std::vector< SPItem * > &l, bool exact, bool casematch)
Function to filter a list of items based on the item type by calling each item_XXX_match function.
Definition find.cpp:676
Gtk::CheckButton check_clones
Definition find.h:264
UI::Widget::Entry entry_find
Definition find.h:201
Gtk::CheckButton check_searchin_property
Definition find.h:212
Gtk::CheckButton check_scope_layer
Definition find.h:209
Gtk::CheckButton check_alltypes
Object type widgets.
Definition find.h:256
Gtk::CheckButton check_font
Definition find.h:240
bool item_style_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false)
Returns true if the SPItem 'item' has the same text in the style attribute.
Definition find.cpp:540
std::vector< SPItem * > filter_types(std::vector< SPItem * > &l)
Definition find.cpp:875
UI::Widget::Entry entry_replace
Definition find.h:202
bool item_font_match(SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false)
Returns true if the SPItem 'item' has the same font values.
Definition find.cpp:621
Gtk::CheckButton check_offsets
Definition find.h:266
UI::Widget::Frame frame_types
Definition find.h:270
Gtk::CheckButton check_attributename
Definition find.h:237
Glib::RefPtr< Gtk::SizeGroup > label_group
Definition find.h:203
Gtk::Button button_find
Definition find.h:286
Gtk::CheckButton check_exact_match
Definition find.h:223
Gtk::CheckButton check_searchin_text
Definition find.h:211
std::vector< SPItem * > & filter_list(std::vector< SPItem * > &l, bool exact, bool casematch)
Definition find.cpp:890
std::vector< Gtk::CheckButton * > checkProperties
A vector of all the properties widgets for easy processing.
Definition find.h:251
Gtk::CheckButton check_scope_all
Scope and search in widgets.
Definition find.h:208
UI::Widget::Frame frame_scope
Definition find.h:217
Gtk::CheckButton check_spirals
Definition find.h:260
bool item_desc_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false)
Returns true if the SPItem 'item' has a <title> or <desc> child that matches.
Definition find.cpp:450
bool item_text_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false)
Returns true if the SPItem 'item' has the same text content.
Definition find.cpp:474
Gtk::Label status
Action Buttons and status.
Definition find.h:285
UI::Widget::Frame frame_options
Definition find.h:231
void selectionChanged(Selection *selection) override
Definition find.cpp:391
UI::Widget::Frame frame_properties
Definition find.h:246
std::vector< SPItem * > & all_selection_items(Inkscape::Selection *s, std::vector< SPItem * > &l, SPObject *ancestor, bool hidden, bool locked)
to return a list of all the selected items
Definition find.cpp:920
Gtk::CheckButton check_attributevalue
Definition find.h:238
bool item_title_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false)
Definition find.cpp:462
void squeeze_window()
Shrink the dialog size when the expander widget is closed Currently not working, no known way to do t...
Definition find.cpp:1123
Gtk::CheckButton check_title
Definition find.h:242
bool _action_replace
Finding or replacing.
Definition find.h:294
Glib::ustring find_replace(const gchar *str, const gchar *find, const gchar *replace, bool exact, bool casematch, bool replaceall)
Replace a string with another string with options for exact and casematching and replace once/all.
Definition find.cpp:402
Gtk::CheckButton check_rects
Definition find.h:257
Gtk::Entry const * getEntry() const
Definition entry.h:31
Gtk::Label const * getLabel() const
Definition labelled.h:51
virtual char const * name() const =0
Get the name of the element node.
virtual const AttributeVector & attributeList() const =0
Get a list of the node's attributes.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual bool matchAttributeName(char const *partial_name) const =0
Check whether this node has any attribute that matches a string.
SPDocument * getDocument() const
Definition desktop.h:189
bool itemIsHidden(SPItem const *item) const
Definition desktop.cpp:264
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::LayerManager & layerManager()
Definition desktop.h:287
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
Base class for visual SVG elements.
Definition sp-item.h:109
bool isLocked() const
Definition sp-item.cpp:218
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
char * desc() const
Returns the description of this object, or NULL if there is none.
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
bool setDesc(char const *desc, bool verbatim=false)
Sets the description of this object.
bool setTitle(char const *title, bool verbatim=false)
Sets the title of this object.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char * title() const
Returns the title of this object, or NULL if there is none.
bool isAncestorOf(SPObject const *object) const
True if object is non-NULL and this is some in/direct parent of object.
unsigned int cloned
Definition sp-object.h:180
ChildrenList children
Definition sp-object.h:907
Editable view implementation.
Event handler for dialog windows.
TODO: insert short description here.
Find dialog.
Macro for icon names used in Inkscape.
SPItem * item
Raw stack of active status messages.
Definition desktop.h:50
Dialog code.
Definition desktop.h:117
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
Definition pack.cpp:153
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
@ NORMAL_MESSAGE
Definition message.h:26
@ WARNING_MESSAGE
Definition message.h:28
static cairo_user_data_key_t key
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Ocnode * child[8]
Definition quantize.cpp:33
void scroll_to_show_item(SPDesktop *desktop, SPItem *item)
If item is not entirely visible then adjust visible area to centre on the centre on of item.
TODO: insert short description here.
TODO: insert short description here.
SVG <image> implementation.
TODO: insert short description here.
SPRoot: SVG <svg> implementation.
SVG <tref> implementation, see sp-tref.cpp.
TODO: insert short description here.
Glib::ustring sp_te_get_string_multiline(SPItem const *text)
Gets a text-only representation of the given text or flowroot object, replacing line break elements w...
Inkscape::Text::Layout const * te_get_layout(SPItem const *item)
Inkscape::Text::Layout::iterator sp_te_replace(SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, gchar const *utf8)
std::unique_ptr< Toolbar >(* create)()
Definition toolbars.cpp:56