Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
document-resources.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
9
10#include <cairo.h>
11#include <cairomm/enums.h>
12#include <cairomm/refptr.h>
13#include <cairomm/surface.h>
14#include <cstddef>
15#include <gdkmm/pixbuf.h>
16#include <gdkmm/rgba.h>
17#include <gdkmm/texture.h>
18#include <giomm/liststore.h>
19#include <glib/gi18n.h>
20#include <glibmm/error.h>
21#include <glibmm/fileutils.h>
22#include <glibmm/main.h>
23#include <glibmm/markup.h>
24#include <glibmm/miscutils.h>
25#include <glibmm/refptr.h>
26#include <glibmm/uriutils.h>
27#include <glibmm/ustring.h>
28#include <gtkmm/boolfilter.h>
29#include <gtkmm/builder.h>
30#include <gtkmm/button.h>
31#include <gtkmm/celleditable.h>
32#include <gtkmm/cellrendererpixbuf.h>
33#include <gtkmm/cellrenderertext.h>
34#include <gtkmm/columnview.h>
35#include <gtkmm/columnviewcolumn.h>
36#include <gtkmm/columnviewrow.h>
37#include <gtkmm/dialog.h>
38#include <gtkmm/drawingarea.h>
39#include <gtkmm/enums.h>
40#include <gtkmm/filterlistmodel.h>
41#include <gtkmm/grid.h>
42#include <gtkmm/iconview.h>
43#include <gtkmm/image.h>
44#include <gtkmm/label.h>
45#include <gtkmm/liststore.h>
46#include <gtkmm/noselection.h>
47#include <gtkmm/paned.h>
48#include <gtkmm/searchentry2.h>
49#include <gtkmm/signallistitemfactory.h>
50#include <gtkmm/sortlistmodel.h>
51#include <gtkmm/stack.h>
52#include <gtkmm/stringsorter.h>
53#include <gtkmm/treemodelfilter.h>
54#include <gtkmm/treemodelsort.h>
55#include <gtkmm/treeselection.h>
56#include <gtkmm/treeview.h>
57#include <gtkmm/window.h>
58#include <map>
59#include <memory>
60#include <string>
61
62#include "colors/color.h"
63#include "colors/color-set.h"
64#include "desktop.h"
65#include "document-undo.h"
66#include "inkscape.h"
69#include "object/sp-defs.h"
70#include "object/sp-filter.h"
71#include "object/sp-font.h"
72#include "object/sp-gradient.h"
73#include "object/sp-image.h"
75#include "object/sp-marker.h"
77#include "object/sp-object.h"
78#include "object/sp-path.h"
79#include "object/sp-pattern.h"
80#include "object/sp-root.h"
81#include "object/sp-symbol.h"
82#include "object/sp-use.h"
83#include "rdf.h"
84#include "selection.h"
85#include "style.h"
86#include "ui/builder-utils.h"
89#include "ui/icon-names.h"
90#include "ui/themes.h"
91#include "ui/util.h"
93#include "util/trim.h"
95
96namespace Inkscape::UI::Dialog {
97
98struct InfoItem : public Glib::Object {
99 Glib::ustring item;
100 Glib::ustring value;
101 uint32_t count;
102 SPObject* object;
103
104 static Glib::RefPtr<InfoItem> create(const Glib::ustring& item, const Glib::ustring& value, uint32_t count, SPObject* object) {
105 auto infoitem = Glib::make_refptr_for_instance<InfoItem>(new InfoItem());
106 infoitem->item = item;
107 infoitem->value = value;
108 infoitem->count = count;
109 infoitem->object = object;
110 return infoitem;
111 }
112 static Glib::RefPtr<InfoItem> create(const Glib::ustring& item, const Glib::ustring& value) {
113 return create(item, value, 0, nullptr);
114 }
115};
116
120
121const std::unordered_map<std::string, Resources> g_id_to_resource = {
122 {"colors", Colors},
123 {"swatches", Swatches},
124 {"fonts", Fonts},
125 {"stats", Stats},
126 {"styles", Styles},
127 {"patterns", Patterns},
128 {"symbols", Symbols},
129 {"markers", Markers},
130 {"gradients", Gradients},
131 {"images", Images},
132 {"filters", Filters},
133 {"external", External},
134 {"metadata", Metadata},
135 // to do: SVG fonts
136 // other resources
137};
138
139std::size_t get_resource_count(details::Statistics const &stats, Resources const rsrc)
140{
141 switch (rsrc) {
142 case Colors: return stats.colors;
143 case Swatches: return stats.swatches;
144 case Fonts: return stats.fonts;
145 case Symbols: return stats.symbols;
146 case Gradients: return stats.gradients;
147 case Patterns: return stats.patterns;
148 case Images: return stats.images;
149 case Filters: return stats.filters;
150 case Markers: return stats.markers;
151 case Metadata: return stats.metadata;
152 case Styles: return stats.styles;
153 case External: return stats.external_uris;
154
155 case Stats: return 1;
156
157 default:
158 break;
159 }
160 return 0;
161}
162
163Resources id_to_resource(const std::string& id) {
164 auto it = g_id_to_resource.find(id);
165 if (it == end(g_id_to_resource)) return Stats;
166
167 return it->second;
168}
169
170std::size_t get_resource_count(std::string const &id, details::Statistics const &stats)
171{
172 auto it = g_id_to_resource.find(id);
173 if (it == end(g_id_to_resource)) return 0;
174
175 return get_resource_count(stats, it->second);
176}
177
178bool is_resource_present(std::string const &id, details::Statistics const &stats)
179{
180 return get_resource_count(id, stats) > 0;
181}
182
183Glib::RefPtr<Gio::File> choose_file(Glib::ustring title, Gtk::Window* parent,
184 Glib::ustring mime_type, Glib::ustring file_name) {
185 static std::string current_folder;
186 return Inkscape::choose_file_save(title, parent, mime_type, file_name, current_folder);
187}
188
189void save_gimp_palette(std::string fname, const std::vector<int>& colors, const char* name) {
190 try {
191 std::ostringstream ost;
192 ost << "GIMP Palette\n";
193 if (name && *name) {
194 ost << "Name: " << name << "\n";
195 }
196 ost << "#\n";
197 for (auto c : colors) {
198 auto r = (c >> 16) & 0xff;
199 auto g = (c >> 8) & 0xff;
200 auto b = c & 0xff;
201 ost << r << ' ' << g << ' ' << b << '\n';
202 }
203 Glib::file_set_contents(fname, ost.str());
204 }
205 catch (Glib::Error const &ex) {
206 g_warning("Error saving color palette: %s", ex.what());
207 }
208 catch (...) {
209 g_warning("Error saving color palette.");
210 }
211}
212
213void extract_colors(Gtk::Window* parent, const std::vector<int>& colors, const char* name) {
214 if (colors.empty() || !parent) return;
215
216 auto file = choose_file(_("Export Color Palette"), parent, "application/color-palette", "color-palette.gpl");
217 if (!file) return;
218
219 // export palette
220 save_gimp_palette(file->get_path(), colors, name);
221}
222
223static void delete_object(SPObject* object, Inkscape::Selection* selection) {
224 if (!object || !selection) return;
225
226 auto document = object->document;
227
228 if (auto pattern = cast<SPPattern>(object)) {
229 // delete action fails for patterns; remove them by deleting them directly
230 pattern->deleteObject(true);
231 DocumentUndo::done(document, _("Delete pattern"), INKSCAPE_ICON("document-resources"));
232 }
233 else if (auto gradient = cast<SPGradient>(object)) {
234 // delete action fails for gradients; remove them by deleting them directly
235 gradient->deleteObject(true);
236 DocumentUndo::done(document, _("Delete gradient"), INKSCAPE_ICON("document-resources"));
237 }
238 else {
239 selection->set(object);
240 selection->deleteItems();
241 }
242}
243
244namespace details {
245
246// editing "inkscape:label"
247Glib::ustring get_inkscape_label(const SPObject& object) {
248 auto label = object.getAttribute("inkscape:label");
249 return Glib::ustring(label ? label : "");
250}
251void set_inkscape_label(SPObject& object, const Glib::ustring& label) {
252 object.setAttribute("inkscape:label", label.c_str());
253}
254
255// editing title element
256Glib::ustring get_title(const SPObject& object) {
257 auto title = object.title();
258 Glib::ustring str(title ? title : "");
259 g_free(title);
260 return str;
261}
262void set_title(SPObject& object, const Glib::ustring& title) {
263 object.setTitle(title.c_str());
264}
265
266struct ResourceItem : public Glib::Object {
267 Glib::ustring id;
268 Glib::ustring label;
269 Glib::RefPtr<Gdk::Texture> image;
270 bool editable;
271 SPObject* object;
272 int color;
273
274 static Glib::RefPtr<ResourceItem> create(
275 const Glib::ustring& id,
276 Glib::ustring& label,
277 Glib::RefPtr<Gdk::Texture> image,
278 SPObject* object,
279 bool editable = false,
280 uint32_t rgb24color = 0
281 ) {
282 auto item = Glib::make_refptr_for_instance<ResourceItem>(new ResourceItem());
283 item->id = id;
284 item->label = label;
285 item->image = image;
286 item->object = object;
287 item->editable = editable;
288 item->color = rgb24color;
289 return item;
290 }
291};
292
293} // namespace details
294
295// label editing: get/set functions for various object types;
296// by default "inkscape:label" will be used (expressed as SPObject);
297// if some types need exceptions to this ruke, they can provide their own edit functions;
298// note: all most-derived types need to be listed to specify overrides
299std::map<std::type_index, std::function<Glib::ustring (const SPObject&)>> g_get_label = {
300 // default: editing "inkscape:label" as a description;
301 // patterns use Inkscape-specific "inkscape:label" attribute;
302 // gradients can also use labels instead of IDs;
303 // filters; to do - editing in a tree view;
304 // images can use both, label & title; defaulting to label for consistency
306 // exception: symbols use <title> element for description
307 {typeid(SPSymbol), details::get_title},
308 // markers use stockid for some reason - label: to do
310};
311
312std::map<std::type_index, std::function<void (SPObject&, const Glib::ustring&)>> g_set_label = {
314 {typeid(SPSymbol), details::set_title},
316};
317
318struct ResourceTextItem : public Glib::Object {
319 Glib::ustring id;
320 Glib::ustring name;
321 Glib::ustring icon;
322
323 static Glib::RefPtr<ResourceTextItem> create(const Glib::ustring& id, Glib::ustring& name, Glib::ustring& icon) {
324 auto item = Glib::make_refptr_for_instance<ResourceTextItem>(new ResourceTextItem());
325 item->id = id;
326 item->name = name;
327 item->icon = icon;
328 return item;
329 }
330};
331
332// liststore columns from glade file
333constexpr int COL_NAME = 0;
334constexpr int COL_ID = 1;
335constexpr int COL_ICON = 2;
336constexpr int COL_COUNT = 3;
337
339 : DialogBase("/dialogs/document-resources", "DocumentResources"),
340 _builder(create_builder("dialog-document-resources.glade")),
341 _gridview(get_widget<Gtk::GridView>(_builder, "iconview")),
342 // _treeview(get_widget<Gtk::TreeView>(_builder, "treeview")),
343 _listview(get_widget<Gtk::ColumnView>(_builder, "listview")),
344 _selector(get_widget<Gtk::ListView>(_builder, "tree")),
345 _edit(get_widget<Gtk::Button>(_builder, "edit")),
346 _select(get_widget<Gtk::Button>(_builder, "select")),
347 _delete(get_widget<Gtk::Button>(_builder, "delete")),
348 _extract(get_widget<Gtk::Button>(_builder, "extract")),
349 _search(get_widget<Gtk::SearchEntry2>(_builder, "search")) {
350
351 // _info_store = Gio::ListStore<InfoItem>::create();
353 // _info_store = Gtk::ListStore::create(g_info_columns);
354 // _item_store = Gtk::ListStore::create(g_item_columns);
356 _info_filter = Gtk::BoolFilter::create({});
357 auto filtered_info = Gtk::FilterListModel::create(_info_store, _info_filter);
358 // auto filtered_info = Gtk::TreeModelFilter::create(_info_store);
359 _item_filter = std::make_unique<TextMatchingFilter>([](const Glib::RefPtr<Glib::ObjectBase>& item){
360 auto ptr = std::dynamic_pointer_cast<details::ResourceItem>(item);
361 return ptr ? ptr->label : Glib::ustring();
362 });
363 auto filtered_items = Gtk::FilterListModel::create(_item_store, _item_filter->get_filter()); // Gtk::TreeModelFilter::create(_item_store);
364 // auto model = Gtk::TreeModelSort::create(filtered_items);
365 auto sorter = Gtk::StringSorter::create(Gtk::ClosureExpression<Glib::ustring>::create([this](auto& item){
366 auto ptr = std::dynamic_pointer_cast<details::ResourceItem>(item);
367 return ptr ? ptr->label : "";
368 }));
369 auto model = Gtk::SortListModel::create(filtered_items, sorter);
370 // model->set_sort_column(g_item_columns.label.index(), Gtk::SortType::ASCENDING);
371
373 auto rsrc = std::dynamic_pointer_cast<details::ResourceItem>(ptr);
374 if (!rsrc) return {};
375
376 auto name = Glib::Markup::escape_text(rsrc->label);
377 return { .label_markup = name, .image = rsrc->image, .tooltip = rsrc->label };
378 });
379 _gridview.add_css_class("grid-view-small");
380 _gridview.set_factory(_item_factory->get_factory());
381 _item_selection_model = Gtk::SingleSelection::create(model);
383
384 append(get_widget<Gtk::Box>(_builder, "main"));
385
386 // _iconview.set_model(model);
387 // _iconview.set_text_column(g_item_columns.label);
388 // _label_renderer = dynamic_cast<Gtk::CellRendererText*>(_iconview.get_first_cell());
389 // assert(_label_renderer);
390 // _label_renderer->property_editable() = true;
391 // _label_renderer->signal_editing_started().connect([this](Gtk::CellEditable * const cell, Glib::ustring const &path){
392 // start_editing(cell, path);
393 // });
394 // _label_renderer->signal_edited().connect([this](Glib::ustring const &path, Glib::ustring const &new_text){
395 // end_editing(path, new_text);
396 // });
397
398 // _iconview.pack_start(_image_renderer);
399 // _iconview.add_attribute(_image_renderer, "surface", g_item_columns.image);
400
401 auto set_up_label = [](auto& list_item){
402 auto label = Gtk::make_managed<Gtk::Label>();
403 label->set_xalign(0);
404 list_item->set_child(*label);
405 };
406 auto bind_label = [](auto& list_item, const Glib::ustring& markup){
407 auto label = dynamic_cast<Gtk::Label*>(list_item->get_child());
408 if (label) label->set_markup(markup);
409 };
410
411 _listview.add_css_class("list-view-small");
412 auto cols = _listview.get_columns();
413 for (int i = 0; i < cols->get_n_items(); ++i) {
414 auto info_factory = Gtk::SignalListItemFactory::create();
415 info_factory->signal_setup().connect(set_up_label);
416 info_factory->signal_bind().connect([this, i, bind_label](auto& list_item) {
417 auto item = std::dynamic_pointer_cast<InfoItem>(list_item->get_item());
418 if (!item) return;
419 Glib::ustring text;
420 if (i == 0) text = item->item;
421 else if (i == 1) { text = item->count ? std::to_string(item->count) : ""; }
422 else text = item->value;
423 bind_label(list_item, text);
424 });
425 cols->get_typed_object<Gtk::ColumnViewColumn>(i)->set_factory(info_factory);
426 }
427 _listview.set_model(Gtk::NoSelection::create(filtered_info));
428
429 auto refilter_info = [this]() {
430 auto expression = Gtk::ClosureExpression<bool>::create([this](auto& item){
431 auto ptr = std::dynamic_pointer_cast<InfoItem>(item);
432 if (!ptr) return false;
433
434 auto str = _search.get_text().lowercase();
435 if (str.empty()) return true;
436
437 return ptr->value.lowercase().find(str) != Glib::ustring::npos;
438 });
439 _info_filter->set_expression(expression);
440 };
441 refilter_info();
442
443 auto treestore = get_object<Gtk::ListStore>(_builder, "liststore");
445 treestore->foreach([&](auto& path, auto& it) {
446 Glib::ustring id;
447 it->get_value(COL_ID, id);
448 Glib::ustring icon;
449 it->get_value(COL_ICON, icon);
450 Glib::ustring name;
451 it->get_value(COL_NAME, name);
452 store->append(ResourceTextItem::create(id, name, icon));
453 return false;
454 });
455
456 auto factory_1 = Gtk::SignalListItemFactory::create();
457 factory_1->signal_setup().connect([this](auto& list_item) {
458 auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
459 box->add_css_class("item-box");
460 auto image = Gtk::make_managed<Gtk::Image>();
461 image->set_icon_size(Gtk::IconSize::NORMAL);
462 box->append(*image);
463 box->append(*Gtk::make_managed<Gtk::Label>());
464 list_item->set_child(*box);
465 });
466
467 factory_1->signal_bind().connect([this](auto& list_item) {
468 auto item = std::dynamic_pointer_cast<ResourceTextItem>(list_item->get_item());
469 if (!item) return;
470 auto box = dynamic_cast<Gtk::Box*>(list_item->get_child());
471 if (!box) return;
472 auto image = dynamic_cast<Gtk::Image*>(box->get_first_child());
473 if (!image) return;
474 auto label = dynamic_cast<Gtk::Label*>(image->get_next_sibling());
475 if (!label) return;
476
477 bool separator = item->id == "-";
478 box->set_hexpand();
479 image->set_from_icon_name(item->icon != "-" ? item->icon : "");
480 image->set_visible(!separator);
481 label->set_text(separator ? "" : item->name);
482 label->set_hexpand();
483 label->set_xalign(0);
484 label->set_margin_start(3);
485 label->set_visible(!separator);
486
487 // disable selecting separator
488 list_item->set_activatable(!separator);
489 list_item->set_selectable(!separator);
490 if (separator) box->add_css_class("separator"); else box->remove_css_class("separator");
491 // list_item->set_focusable(!separator);
492 });
493
494 _selector.add_css_class("list-view-small");
495 _selector.set_factory(factory_1);
496
497 _filter = Gtk::BoolFilter::create({});
498 auto filtered_model = Gtk::FilterListModel::create(store, _filter);
499 _selection_model = Gtk::SingleSelection::create(filtered_model);
500 _selection_change = _selection_model->signal_selection_changed().connect([=,this](auto pos, auto count) {
501 if (auto item = std::dynamic_pointer_cast<ResourceTextItem>(_selection_model->get_selected_item())) {
502 select_page(item->id);
503 }
504 });
505 _selector.set_model(_selection_model);
506
507 _categories = Gtk::TreeModelFilter::create(treestore);
508 _categories->set_visible_func([this](Gtk::TreeModel::const_iterator const &it){
509 Glib::ustring id;
510 it->get_value(COL_ID, id);
511 return id == "-" || is_resource_present(id, _stats);
512 });
513
514 _wr.setUpdating(true); // set permanently
515
516 for (auto entity = rdf_work_entities; entity && entity->name; ++entity) {
517 if (entity->editable != RDF_EDIT_GENERIC) continue;
518
520 _rdf_list.push_back(w);
521 }
522
523 auto paned = &get_widget<Gtk::Paned>(_builder, "paned");
524 auto const move = [=, this](){
525 auto pos = paned->get_position();
526 get_widget<Gtk::Label>(_builder, "spacer").set_size_request(pos);
527 };
528 paned->property_position().signal_changed().connect([move](){ move(); });
529 move();
530
531 _edit.signal_clicked().connect([this]{
532 auto sel = _item_selection_model->get_selected_item();
533 // auto sel = _iconview.get_selected_items();
534 if (sel) {
535 // todo: investigate why this doesn't work initially:
536 // _iconview.set_cursor(sel.front(), true);
537 // TODO: enter edit mode
538 // _item_selection_model->
539 }
540 // treeview todo if needed
541 });
542
543 // selectable elements can be selected on the canvas;
544 // even elements in <defs> can be selected (same as in XML dialog)
545 _select.signal_clicked().connect([this]{
546 auto document = getDocument();
547 auto desktop = getDesktop();
548 if (!document || !desktop) return;
549
550 if (auto rsrc = selected_item()) {
551 // Glib::ustring id = row[g_item_columns.id];
552 if (auto object = document->getObjectById(rsrc->id)) {
553 // select object
554 desktop->getSelection()->set(object);
555 }
556 }
557 else {
558 // to do: select from treeview if needed
559 }
560 });
561
562 _search.signal_search_changed().connect([this, refilter_info](){
563 refilter_info();
564 _item_filter->refilter(_search.get_text());
565 });
566
567 _delete.signal_clicked().connect([this]{
568 // delete selected object
569 if (auto rsrc = selected_item()) {
570 delete_object(rsrc->object, getDesktop()->getSelection());
571 // do not wait for refresh on idle, as double click delete button can cause crash
573 }
574 });
575
576 _extract.signal_clicked().connect([this]{
577 auto const window = dynamic_cast<Gtk::Window *>(get_root());
578
579 switch (_showing_resource) {
580 case Images:
581 // extract selected image
582 if (auto rsrc = selected_item()) {
583 extract_image(window, cast<SPImage>(rsrc->object));
584 }
585 break;
586 case Colors:
587 // export colors into a GIMP palette
588 if (_document) {
589 std::vector<int> colors;
590 const size_t N = _item_store->get_n_items();
591 colors.reserve(N);
592 for (size_t i = 0; i < N; ++i) {
593 if (auto r = _item_store->get_typed_object<details::ResourceItem>(i)) {
594 colors.push_back(r->color);
595 }
596 }
597 extract_colors(window, colors, _document->getDocumentName());
598 }
599 break;
600 default:
601 // nothing else so far
602 break;
603 }
604 });
605
606 _item_selection_model->signal_selection_changed().connect([this](auto pos, auto count) {
608 });
609
610}
611
612std::shared_ptr<details::ResourceItem> DocumentResources::selected_item() {
613 auto sel = _item_selection_model->get_selected_item();
614 auto rsrc = std::dynamic_pointer_cast<details::ResourceItem>(sel);
615 return rsrc;
616}
617
619 if (!_gridview.get_visible()) return;
620
621 auto single_sel = !!selected_item();
622
623 _edit.set_sensitive(single_sel);
624 _extract.set_sensitive(single_sel || _showing_resource == Colors);
625 _delete.set_sensitive(single_sel);
626 _select.set_sensitive(single_sel);
627}
628
629Cairo::RefPtr<Cairo::Surface> render_color(uint32_t rgb, double size, double radius, int device_scale) {
630 Cairo::RefPtr<Cairo::Surface> nul;
631 return add_background_to_image(nul, rgb, size / 2, radius, device_scale, 0x7f7f7f00);
632}
633
635 auto style = obj.style;
636
637 auto add = [&colors](Colors::Color const &c) {
638 colors.set(c.toString(), c);
639 };
640
641 if (style->stroke.set && style->stroke.isColor()) {
642 add(style->stroke.getColor());
643 }
644 if (style->color.set) {
645 add(style->color.getColor());
646 }
647 if (style->fill.set) {
648 add(style->fill.getColor());
649 }
650 if (style->solid_color.set) {
651 add(style->solid_color.getColor());
652 }
653}
654
655// traverse all nodes starting from given 'object'
656template<typename V>
657void apply_visitor(SPObject& object, V&& visitor) {
658 visitor(object);
659
660 // SPUse inserts referenced object as a child; skip it
661 if (is<SPUse>(&object)) return;
662
663 for (auto&& child : object.children) {
664 apply_visitor(child, visitor);
665 }
666}
667
669 if (object) {
670 apply_visitor(*object, [&](SPObject& obj){ collect_object_colors(obj, colors); });
671 }
672}
673
674void collect_used_fonts(SPObject& object, std::set<std::string>& fonts) {
675 auto style = object.style;
676
677 if (style->font_specification.set) {
678 auto fspec = style->font_specification.value();
679 if (fspec && *fspec) {
680 fonts.insert(fspec);
681 }
682 }
683 else if (style->font.set) {
684 // some SVG files won't have Inkscape-specific fontspec; read font settings instead
685 auto font = style->font.get_value();
686 if (style->font_style.set) {
687 font += ' ' + style->font_style.get_value();
688 }
689 fonts.insert(font);
690 }
691}
692
693std::set<std::string> collect_fontspecs(SPObject* object) {
694 std::set<std::string> fonts;
695 if (object) {
696 apply_visitor(*object, [&](SPObject& obj){ collect_used_fonts(obj, fonts); });
697 }
698 return fonts;
699}
700
701template<typename T>
702bool filter_element(T& object) { return true; }
703
704template<>
705bool filter_element<SPPattern>(SPPattern& object) { return object.hasChildren(); }
706
707template<>
708bool filter_element<SPGradient>(SPGradient& object) { return object.hasStops(); }
709
710template<typename T>
711std::vector<T*> collect_items(SPObject* object, bool (*filter)(T&) = filter_element<T>) {
712 std::vector<T*> items;
713 if (object) {
714 apply_visitor(*object, [&](SPObject& obj){
715 if (auto t = cast<T>(&obj)) {
716 if (filter(*t)) items.push_back(t);
717 }
718 });
719 }
720 return items;
721}
722
723std::unordered_map<std::string, size_t> collect_styles(SPObject* root) {
724 std::unordered_map<std::string, size_t> map;
725 if (!root) return map;
726
727 apply_visitor(*root, [&](SPObject& obj){
728 if (auto style = obj.getAttribute("style")) {
729 map[style]++;
730 }
731 });
732
733 return map;
734}
735
737 bool present = false;
738 if (auto href = Inkscape::getHrefAttribute(*obj.getRepr()).second) {
739 if (*href && *href != '#' && *href != '?') {
740 auto scheme = Glib::uri_parse_scheme(href);
741 // There are tens of schemes: https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
742 // TODO: Which ones to collect as external resources?
743 if (scheme == "file" || scheme == "http" || scheme == "https" || scheme.empty()) {
744 present = true;
745 }
746 }
747 }
748 return present;
749}
750
753
754 if (!root) {
755 return stats;
756 }
757
758 Colors::ColorSet colors;
759 std::set<std::string> fonts;
760
761 apply_visitor(*root, [&](SPObject& obj){
762 // order of tests is important; derived classes first, before base,
763 // so meshgradient first, gradient next
764
765 if (auto pattern = cast<SPPattern>(&obj)) {
766 if (filter_element(*pattern)) {
767 stats.patterns++;
768 }
769 }
770 else if (is<SPMeshGradient>(&obj)) {
771 stats.meshgradients++;
772 }
773 else if (auto gradient = cast<SPGradient>(&obj)) {
774 if (filter_element(*gradient)) {
775 if (gradient->isSwatch()) {
776 stats.swatches++;
777 }
778 else {
779 stats.gradients++;
780 }
781 }
782 }
783 else if (auto marker = cast<SPMarker>(&obj)) {
784 if (filter_element(*marker)) {
785 stats.markers++;
786 }
787 }
788 else if (auto symbol = cast<SPSymbol>(&obj)) {
789 if (filter_element(*symbol)) {
790 stats.symbols++;
791 }
792 }
793 else if (is<SPFont>(&obj)) { // SVG font
794 stats.svg_fonts++;
795 }
796 else if (is<SPImage>(&obj)) {
797 stats.images++;
798 }
799 else if (auto group = cast<SPGroup>(&obj)) {
800 if (strcmp(group->getRepr()->name(), "svg:g") == 0) {
801 switch (group->layerMode()) {
802 case SPGroup::GROUP:
803 stats.groups++;
804 break;
805 case SPGroup::LAYER:
806 stats.layers++;
807 break;
808 }
809 }
810 }
811 else if (is<SPPath>(&obj)) {
812 stats.paths++;
813 }
814 else if (is<SPFilter>(&obj)) {
815 stats.filters++;
816 }
817 else if (is<ColorProfile>(&obj)) {
818 stats.colorprofiles++;
819 }
820
821 if (auto style = obj.getAttribute("style")) {
822 if (*style) stats.styles++;
823 }
824
825 if (has_external_ref(obj)) {
826 stats.external_uris++;
827 }
828
829 collect_object_colors(obj, colors);
830 collect_used_fonts(obj, fonts);
831
832 // verify:
833 stats.nodes++;
834 });
835
836 stats.colors = colors.size();
837 stats.fonts = fonts.size();
838
839 return stats;
840}
841
842details::Statistics DocumentResources::collect_statistics()
843{
844 auto root = _document ? _document->getRoot() : nullptr;
846
847 if (_document) {
848 for (auto& el : _rdf_list) {
849 bool read_only = true;
850 el.update(_document, read_only);
851 if (!el.content().empty()) stats.metadata++;
852 }
853 }
854
855 return stats;
856}
857
858void DocumentResources::rebuild_stats() {
859 _stats = collect_statistics();
860
861 if (auto desktop = getDesktop()) {
862 _wr.setDesktop(desktop);
863 }
864
865 // filter visible categories
866 auto expression = Gtk::ClosureExpression<bool>::create([this](auto& item){
867 auto ptr = std::dynamic_pointer_cast<ResourceTextItem>(item);
868 if (!ptr) return false;
869 // check for "-", which is a separator
870 if (ptr->id == "-") return true; // hidden until it can be made unselectable
871 // show only categories that have some entries
872 auto count = get_resource_count(ptr->id, _stats);
873 return count > 0;
874 });
875 _filter->set_expression(expression);
876
877 _categories->refilter();
878 _categories->foreach_iter([this](Gtk::TreeModel::iterator const &it){
879 Glib::ustring id;
880 it->get_value(COL_ID, id);
881 auto count = get_resource_count(id, _stats);
882 if (id == "stats") count = 0; // don't show count 1 for "overview"
883 it->set_value(COL_COUNT, count);
884 return false; // false means continue
885 });
886}
887
888void DocumentResources::documentReplaced() {
889 _document = getDocument();
890 if (_document) {
891 _document_modified = _document->connectModified([this](unsigned){
892 // brute force refresh, but throttled
893 _idle_refresh = Glib::signal_timeout().connect([this]{
894 rebuild_stats();
895 refresh_current_page();
896 return false;
897 }, 200);
898 });
899 }
900 else {
901 _document_modified.disconnect();
902 }
903
904 rebuild_stats();
905 refresh_current_page();
906}
907
908void DocumentResources::refresh_current_page() {
909 auto page = _cur_page_id;
910 if (!is_resource_present(page, _stats)) {
911 page = "stats";
912 _selection_model->set_selected(0);
913 }
914
915 auto selected = _selection_model->get_selected_item();
916 if (auto item = std::dynamic_pointer_cast<ResourceTextItem>(selected)) {
917 refresh_page(item->id);
918 }
919}
920
921void DocumentResources::selectionModified(Inkscape::Selection* selection, unsigned flags)
922{
923 // no op so far
924}
925
926auto get_id = [](const SPObject* object) { auto id = object->getId(); return id ? id : ""; };
927auto label_fmt = [](const char* label, const Glib::ustring& id) { return label && *label ? label : '#' + id; };
928
929void add_colors(Glib::RefPtr<Gio::ListStoreBase>& item_store, Colors::ColorSet const &colors, int device_scale) {
930 for (auto&& it : colors) {
931 const auto& color = it.second;
932
933 Glib::ustring name = color.toString();
934 auto rgba32 = color.toRGBA(0xff);
935 auto rgb24 = rgba32 >> 8;
936
937 int size = 20;
938 double radius = 2.0;
939 auto image = to_texture(render_color(rgba32, size, radius, device_scale));
940
941 item_store->append(details::ResourceItem::create(name, name, image, nullptr, false, rgb24));
942 }
943}
944
945void _add_items_with_images(Glib::RefPtr<Gio::ListStoreBase>& item_store, const std::vector<SPObject*>& items, double width, double height, int device_scale, bool use_title, object_renderer::options opt) {
946 object_renderer renderer;
947 item_store->freeze_notify();
948
949 for (auto item : items) {
950 Glib::ustring id = get_id(item);
951 Glib::ustring label;
952 if (use_title) {
953 auto title = item->title();
954 label = label_fmt(title, id);
955 g_free(title);
956 }
957 else {
958 auto labelstr = item->getAttribute("inkscape:label");
959 label = label_fmt(labelstr, id);
960 }
961 auto image = to_texture(renderer.render(*item, width, height, device_scale, opt));
962 item_store->append(details::ResourceItem::create(id, label, image, item));
963 }
964
965 item_store->thaw_notify();
966}
967
968template<typename T>
969void add_items_with_images(Glib::RefPtr<Gio::ListStoreBase> item_store, const std::vector<T*>& items, double width, double height, int device_scale, bool use_title = false, object_renderer::options opt = {}) {
970 static_assert(std::is_base_of<SPObject, T>::value);
971 _add_items_with_images(item_store, reinterpret_cast<const std::vector<SPObject*>&>(items), width, height, device_scale, use_title, opt);
972}
973
974void add_fonts(Glib::RefPtr<Gio::ListStoreBase> store, const std::set<std::string>& fontspecs) {
975 size_t i = 1;
976 for (auto&& fs : fontspecs) {
977 auto item = Glib::ustring::compose("%1 %2", _("Font"), i++);
978 auto name = Glib::Markup::escape_text(fs);
979 auto value = Glib::ustring::format(
980 "<span allow_breaks='false' size='xx-large' font='", fs, "'>", name, "</span>\n",
981 "<span allow_breaks='false' size='small' alpha='60%'>", name, "</span>"
982 );
983 store->append(InfoItem::create(item, value));
984 }
985}
986
987void add_stats(Glib::RefPtr<Gio::ListStoreBase> info_store, SPDocument* document, const details::Statistics& stats) {
988 auto read_only = true;
989 auto license = document ? rdf_get_license(document, read_only) : nullptr;
990
991 std::pair<const char*, std::string> str[] = {
992 {_("Document"), document && document->getDocumentFilename() ? document->getDocumentFilename() : "-"},
993 {_("License"), license && license->name ? license->name : "-"},
994 {_("Metadata"), stats.metadata > 0 ? C_("Adjective for Metadata status", "Present") : "-"},
995 };
996 for (auto& pair : str) {
997 info_store->append(InfoItem::create(pair.first, Glib::Markup::escape_text(pair.second)));
998 }
999
1000 std::pair<const char*, size_t> kv[] = {
1001 {_("Colors"), stats.colors},
1002 {_("Color profiles"), stats.colorprofiles},
1003 {_("Swatches"), stats.swatches},
1004 {_("Fonts"), stats.fonts},
1005 {_("Gradients"), stats.gradients},
1006 {_("Mesh gradients"), stats.meshgradients},
1007 {_("Patterns"), stats.patterns},
1008 {_("Symbols"), stats.symbols},
1009 {_("Markers"), stats.markers},
1010 {_("Filters"), stats.filters},
1011 {_("Images"), stats.images},
1012 {_("SVG fonts"), stats.svg_fonts},
1013 {_("Layers"), stats.layers},
1014 {_("Total elements"), stats.nodes},
1015 {_("Groups"), stats.groups},
1016 {_("Paths"), stats.paths},
1017 {_("External URIs"), stats.external_uris},
1018 };
1019 for (auto& pair : kv) {
1020 info_store->append(InfoItem::create(pair.first, pair.second ? std::to_string(pair.second) : "-"));
1021 }
1022}
1023
1024void add_metadata(Glib::RefPtr<Gio::ListStoreBase> info_store, SPDocument* document,
1025 const boost::ptr_vector<Inkscape::UI::Widget::EntityEntry>& rdf_list) {
1026
1027 for (auto& entry : rdf_list) {
1028 auto label = entry._label.get_label();
1029 Util::trim(label, ":");
1030 info_store->append(InfoItem::create(label, Glib::Markup::escape_text(entry.content())));
1031 }
1032}
1033
1034void add_filters(Glib::RefPtr<Gio::ListStoreBase> info_store, const std::vector<SPFilter*>& filters) {
1035 for (auto& filter : filters) {
1036 auto label = filter->getAttribute("inkscape:label");
1037 auto name = Glib::ustring(label ? label : filter->getId());
1038 std::ostringstream ost;
1039 bool first = true;
1040 for (auto& obj : filter->children) {
1041 if (auto primitive = cast<SPFilterPrimitive>(&obj)) {
1042 if (!first) ost << ", ";
1043 Glib::ustring name = primitive->getRepr()->name();
1044 if (name.find("svg:") != std::string::npos) {
1045 name.erase(name.find("svg:"), 4);
1046 }
1047 ost << name;
1048 first = false;
1049 }
1050 }
1051 info_store->append(InfoItem::create(name, ost.str()));
1052 }
1053}
1054
1055void add_styles(Glib::RefPtr<Gio::ListStoreBase> info_store, const std::unordered_map<std::string, size_t>& map) {
1056 std::vector<std::string> vect;
1057 vect.reserve(map.size());
1058 for (auto style : map) {
1059 vect.emplace_back(style.first);
1060 }
1061 std::sort(vect.begin(), vect.end());
1062 info_store->freeze_notify();
1063 int n = 1;
1064 for (auto& style : vect) {
1065 info_store->append(InfoItem::create(_("Style ") + std::to_string(n++), Glib::Markup::escape_text(style), map.find(style)->second, nullptr));
1066 }
1067 info_store->thaw_notify();
1068}
1069
1070void add_refs(Glib::RefPtr<Gio::ListStoreBase> info_store, const std::vector<SPObject*>& objects) {
1071 info_store->freeze_notify();
1072 for (auto& obj : objects) {
1073 auto href = Inkscape::getHrefAttribute(*obj->getRepr()).second;
1074 if (!href) continue;
1075
1076 info_store->append(InfoItem::create(label_fmt(nullptr, get_id(obj)), href, 0, obj));
1077 }
1078 info_store->thaw_notify();
1079}
1080
1081void DocumentResources::select_page(const Glib::ustring& id) {
1082 if (_cur_page_id == id.raw()) return;
1083
1084 _cur_page_id = id;
1085 refresh_page(id);
1086}
1087
1088void DocumentResources::clear_stores() {
1089 _item_store->freeze_notify();
1090 _item_store->remove_all();
1091 _item_store->thaw_notify();
1092
1093 _info_store->freeze_notify();
1094 _info_store->remove_all();
1095 _info_store->thaw_notify();
1096}
1097
1098void DocumentResources::refresh_page(const Glib::ustring& id) {
1099 auto rsrc = id_to_resource(id);
1100
1101 // GTK spits out a lot of warnings and errors from filtered model.
1102 // I don't know how to fix them.
1103 // https://gitlab.gnome.org/GNOME/gtk/-/issues/1150
1104 // Clear sorting? Remove filtering?
1105 // GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
1106
1107 clear_stores();
1108
1109 auto root = _document ? _document->getRoot() : nullptr;
1110 auto defs = _document ? _document->getDefs() : nullptr;
1111
1112 int device_scale = get_scale_factor();
1113 auto tab = "iconview";
1114 auto has_count = false;
1115 auto item_width = 90;
1116 auto const color = get_color();
1117 auto label_editable = false;
1118 auto items_selectable = true;
1119 auto can_delete = false; // enable where supported
1120 auto can_extract = false;
1121
1122 switch (rsrc) {
1123 case Colors:
1124 {
1125 Colors::ColorSet colors;
1126 collect_colors(root, colors);
1127 add_colors(_item_store, colors, device_scale);
1128 item_width = 70;
1129 items_selectable = false; // to do: make selectable?
1130 can_extract = true;
1131 break;
1132 }
1133 case Symbols:
1134 {
1135 auto opt = object_renderer::options();
1136 if (auto const window = dynamic_cast<Gtk::Window *>(get_root());
1137 INKSCAPE.themecontext->isCurrentThemeDark(window))
1138 {
1139 // white background for typically black symbols, so they don't disappear in a dark theme
1140 opt.solid_background(0xf0f0f0ff, 3, 3);
1141 }
1142 opt.symbol_style_from_use();
1143 add_items_with_images(_item_store, collect_items<SPSymbol>(defs), 70, 60, device_scale, true, opt);
1144 }
1145 label_editable = true;
1146 can_delete = true;
1147 break;
1148
1149 case Patterns:
1150 add_items_with_images(_item_store, collect_items<SPPattern>(defs), 80, 70, device_scale);
1151 label_editable = true;
1152 can_delete = true;
1153 break;
1154
1155 case Markers:
1156 add_items_with_images(_item_store, collect_items<SPMarker>(defs), 70, 60, device_scale, false,
1157 object_renderer::options().foreground(color));
1158 label_editable = true;
1159 can_delete = true;
1160 break;
1161
1162 case Gradients:
1163 add_items_with_images(_item_store,
1164 collect_items<SPGradient>(defs, [](auto& g){ return filter_element(g) && !g.isSwatch(); }),
1165 180, 22, device_scale);
1166 label_editable = true;
1167 can_delete = true;
1168 break;
1169
1170 case Swatches:
1171 add_items_with_images(_item_store,
1172 collect_items<SPGradient>(defs, [](auto& g){ return filter_element(g) && g.isSwatch(); }),
1173 100, 22, device_scale);
1174 label_editable = true;
1175 can_delete = true;
1176 break;
1177
1178 case Fonts:
1179 add_fonts(_info_store, collect_fontspecs(root));
1180 tab = "treeview";
1181 items_selectable = false;
1182 break;
1183
1184 case Filters:
1185 add_filters(_info_store, collect_items<SPFilter>(defs));
1186 label_editable = true;
1187 tab = "treeview";
1188 items_selectable = false; // to do: make selectable
1189 break;
1190
1191 case Styles:
1192 add_styles(_info_store, collect_styles(root));
1193 tab = "treeview";
1194 has_count = true;
1195 items_selectable = false; // to do: make selectable?
1196 break;
1197
1198 case Images:
1199 add_items_with_images(_item_store, collect_items<SPImage>(root), 110, 110, device_scale);
1200 label_editable = true;
1201 can_extract = true;
1202 can_delete = true;
1203 break;
1204
1205 case External:
1206 add_refs(_info_store, collect_items<SPObject>(root, [](auto& obj){ return has_external_ref(obj); }));
1207 tab = "treeview";
1208 items_selectable = false; // to do: make selectable
1209 break;
1210
1211 case Stats:
1212 add_stats(_info_store, _document, _stats);
1213 tab = "treeview";
1214 items_selectable = false;
1215 break;
1216
1217 case Metadata:
1218 add_metadata(_info_store, _document, _rdf_list);
1219 tab = "treeview";
1220 items_selectable = false;
1221 break;
1222 }
1223
1224 _showing_resource = rsrc;
1225
1226 _listview.get_columns()->get_typed_object<Gtk::ColumnViewColumn>(1)->set_visible(has_count);
1227 //TODO
1228 // _label_renderer->property_editable() = label_editable;
1229 _edit .set_visible(label_editable);
1230 _select .set_visible(items_selectable);
1231 _delete .set_visible(can_delete);
1232 _extract.set_visible(can_extract);
1233
1234 //TODO
1235 // _iconview.set_item_width(item_width);
1236 get_widget<Gtk::Stack>(_builder, "stack").set_visible_child(tab);
1237 update_buttons();
1238}
1239
1240//TODO
1241void DocumentResources::end_editing(const Glib::ustring& path, const Glib::ustring& new_text) {
1242 // auto model = _iconview.get_model();
1243 // Gtk::TreeModel::Row row = *model->get_iter(path);
1244 // if (!row) return;
1245 return;
1246
1247 SPObject* object = 0; // row[g_item_columns.object];
1248 if (!object) {
1249 g_warning("Missing object ptr, cannot edit object's name.");
1250 return;
1251 }
1252
1253 // try object-specific edit functions first; if not present fall back to generic
1254 auto getter = g_get_label[typeid(*object)];
1255 auto setter = g_set_label[typeid(*object)];
1256 if (!getter || !setter) {
1257 getter = g_get_label[typeid(SPObject)];
1258 setter = g_set_label[typeid(SPObject)];
1259 }
1260
1261 auto name = getter(*object);
1262 if (new_text == name) return;
1263
1264 setter(*object, new_text);
1265
1266 // auto id = get_id(object);
1267 // row[g_item_columns.label] = label_fmt(new_text.c_str(), id);
1268
1269 if (auto document = object->document) {
1270 DocumentUndo::done(document, _("Edit object title"), INKSCAPE_ICON("document-resources"));
1271 }
1272}
1273
1274} // namespace Inkscape::UI::Dialog
1275
1276/*
1277 Local Variables:
1278 mode:c++
1279 c-file-style:"stroustrup"
1280 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1281 indent-tabs-mode:nil
1282 fill-column:99
1283 End:
1284*/
1285// vim:filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99:
Gtk builder utilities.
uint64_t page
Definition canvas.cpp:171
Fragment store
Definition canvas.cpp:155
bool set(Color const &color)
Remove any other colors and set to just this one color.
unsigned size() const
Return the number of items in the color set.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
void deleteItems(bool skip_undo=false)
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
Definition selection.h:118
DialogBase is the base class for the dialog system.
Definition dialog-base.h:42
Selection * getSelection() const
Definition dialog-base.h:84
SPDocument * getDocument() const
Definition dialog-base.h:83
SPDesktop * getDesktop() const
Definition dialog-base.h:79
Glib::RefPtr< Gtk::SingleSelection > _item_selection_model
std::unique_ptr< IconViewItemFactory > _item_factory
Glib::RefPtr< Gtk::BoolFilter > _info_filter
Glib::RefPtr< Gtk::TreeModelFilter > _categories
std::shared_ptr< details::ResourceItem > selected_item()
std::unique_ptr< TextMatchingFilter > _item_filter
boost::ptr_vector< Inkscape::UI::Widget::EntityEntry > _rdf_list
Glib::RefPtr< Gio::ListStoreBase > _item_store
Glib::RefPtr< Gtk::BoolFilter > _filter
Glib::RefPtr< Gtk::SingleSelection > _selection_model
Glib::RefPtr< Gio::ListStoreBase > _info_store
Glib::RefPtr< Gtk::Builder > _builder
static std::unique_ptr< IconViewItemFactory > create(std::function< ItemData(Glib::RefPtr< Glib::ObjectBase > &)> get_item)
static EntityEntry * create(rdf_work_entity_t *ent, Registry &wr)
Cairo::RefPtr< Cairo::Surface > render(SPObject &object, double width, double height, double device_scale, options options={})
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Typed SVG document implementation.
Definition document.h:101
char const * getDocumentFilename() const
Definition document.h:229
SPObject * getObjectById(std::string const &id) const
char const * getDocumentName() const
basename or other human-readable label for the document.
Definition document.h:236
Gradient.
Definition sp-gradient.h:86
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
char const * label() const
Gets the author-visible label property for the object or a default if no label is defined.
SPDocument * document
Definition sp-object.h:188
char * id
Definition sp-object.h:192
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
char * title() const
Returns the title of this object, or NULL if there is none.
SPObject of the color-profile object found a direct child of defs.
A set of colors which can be modified together used for color pickers.
const double w
Definition conic-4.cpp:19
RootCluster root
double c[8][4]
Editable view implementation.
static char const *const parent
Definition dir-util.cpp:70
A simple dialog for previewing document resources.
TODO: insert short description here.
Macro for icon names used in Inkscape.
std::unique_ptr< Magick::Image > image
SPItem * item
Glib::ustring label
Definition desktop.h:50
Glib::ustring get_inkscape_label(const SPObject &object)
void set_inkscape_label(SPObject &object, const Glib::ustring &label)
Glib::ustring get_title(const SPObject &object)
void set_title(SPObject &object, const Glib::ustring &title)
Dialog code.
Definition desktop.h:117
void add_items_with_images(Glib::RefPtr< Gio::ListStoreBase > item_store, const std::vector< T * > &items, double width, double height, int device_scale, bool use_title=false, object_renderer::options opt={})
void add_colors(Glib::RefPtr< Gio::ListStoreBase > &item_store, Colors::ColorSet const &colors, int device_scale)
Resources id_to_resource(const std::string &id)
void add_filters(Glib::RefPtr< Gio::ListStoreBase > info_store, const std::vector< SPFilter * > &filters)
void add_fonts(Glib::RefPtr< Gio::ListStoreBase > store, const std::set< std::string > &fontspecs)
Cairo::RefPtr< Cairo::Surface > render_color(uint32_t rgb, double size, double radius, int device_scale)
bool filter_element< SPPattern >(SPPattern &object)
bool is_resource_present(std::string const &id, details::Statistics const &stats)
void add_stats(Glib::RefPtr< Gio::ListStoreBase > info_store, SPDocument *document, const details::Statistics &stats)
std::map< std::type_index, std::function< Glib::ustring(const SPObject &)> > g_get_label
static void delete_object(SPObject *object, Inkscape::Selection *selection)
void _add_items_with_images(Glib::RefPtr< Gio::ListStoreBase > &item_store, const std::vector< SPObject * > &items, double width, double height, int device_scale, bool use_title, object_renderer::options opt)
void extract_colors(Gtk::Window *parent, const std::vector< int > &colors, const char *name)
void collect_object_colors(SPObject &obj, Colors::ColorSet &colors)
std::set< std::string > collect_fontspecs(SPObject *object)
details::Statistics collect_statistics(SPObject *root)
const std::unordered_map< std::string, Resources > g_id_to_resource
void collect_colors(SPObject *object, Colors::ColorSet &colors)
bool has_external_ref(SPObject &obj)
std::map< std::type_index, std::function< void(SPObject &, const Glib::ustring &)> > g_set_label
void add_styles(Glib::RefPtr< Gio::ListStoreBase > info_store, const std::unordered_map< std::string, size_t > &map)
void add_refs(Glib::RefPtr< Gio::ListStoreBase > info_store, const std::vector< SPObject * > &objects)
Glib::RefPtr< Gio::File > choose_file(Glib::ustring title, Gtk::Window *parent, Glib::ustring mime_type, Glib::ustring file_name)
bool filter_element< SPGradient >(SPGradient &object)
void save_gimp_palette(std::string fname, const std::vector< int > &colors, const char *name)
std::vector< T * > collect_items(SPObject *object, bool(*filter)(T &)=filter_element< T >)
std::size_t get_resource_count(details::Statistics const &stats, Resources const rsrc)
void collect_used_fonts(SPObject &object, std::set< std::string > &fonts)
std::unordered_map< std::string, size_t > collect_styles(SPObject *root)
void add_metadata(Glib::RefPtr< Gio::ListStoreBase > info_store, SPDocument *document, const boost::ptr_vector< Inkscape::UI::Widget::EntityEntry > &rdf_list)
void apply_visitor(SPObject &object, V &&visitor)
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
void trim(Glib::ustring &input, Glib::ustring const &also_remove="")
Modifies a string in place, removing leading and trailing whitespace characters.
Definition trim.h:34
Cairo::RefPtr< Cairo::Surface > add_background_to_image(Cairo::RefPtr< Cairo::Surface > image, uint32_t rgb, double margin, double radius, int device_scale, std::optional< uint32_t > border=std::optional< uint32_t >())
static void append(std::vector< T > &target, std::vector< T > &&source)
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
Glib::RefPtr< Gio::File > choose_file_save(Glib::ustring const &title, Gtk::Window *parent, Glib::RefPtr< Gio::ListStore< Gtk::FileFilter > > const &filters_model, std::string const &file_name, std::string &current_folder)
Synchronously run a Gtk::FileDialog to select a file for saving data.
bool extract_image(Gtk::Window *parent, SPImage *image)
Ocnode * child[8]
Definition quantize.cpp:33
RGB rgb
Definition quantize.cpp:36
struct rdf_work_entity_t rdf_work_entities[]
Definition rdf.cpp:240
struct rdf_license_t * rdf_get_license(SPDocument *document, bool read_only)
Attempts to match and retrieve a known RDF/License from the document XML.
Definition rdf.cpp:1047
headers for RDF types
@ RDF_EDIT_GENERIC
Definition rdf.h:63
GList * items
size_t N
Document level base class for all SVG filter primitives.
SVG <filter> element.
SVG <image> implementation.
TODO: insert short description here.
SVG <pattern> implementation.
SPRoot: SVG <svg> implementation.
char const * name
Definition rdf.h:72
SPStyle - a style object for SPItem objects.
SPDesktop * desktop
double height
double width
Gtk <themes> helper code.
std::unique_ptr< Toolbar >(* create)()
Definition toolbars.cpp:56
Glib::ustring name
Definition toolbars.cpp:55
Inkscape::Util::trim - trim whitespace and other characters.
Glib::RefPtr< Gdk::Texture > to_texture(Cairo::RefPtr< Cairo::Surface > const &surface)
Convert an image surface in ARGB32 format to a texture.
Definition util.cpp:510