diff --git a/share/icons/Tango/scalable/actions/symbols.svg b/share/icons/Tango/scalable/actions/symbols.svg new file mode 100644 index 0000000000000000000000000000000000000000..94782b5e781821c163059aefb7586ba004392e08 --- /dev/null +++ b/share/icons/Tango/scalable/actions/symbols.svg @@ -0,0 +1,166 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/symbols.svg b/share/icons/hicolor/scalable/actions/symbols.svg index f889a69a99817d4b0f4b20e700fe6228f96e8732..94782b5e781821c163059aefb7586ba004392e08 100644 --- a/share/icons/hicolor/scalable/actions/symbols.svg +++ b/share/icons/hicolor/scalable/actions/symbols.svg @@ -1,3 +1,166 @@ - - + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/symbols-symbolic.svg b/share/icons/hicolor/symbolic/actions/symbols-symbolic.svg index f889a69a99817d4b0f4b20e700fe6228f96e8732..c11090f4d312948db810fbdeb70583f95a003f36 100644 --- a/share/icons/hicolor/symbolic/actions/symbols-symbolic.svg +++ b/share/icons/hicolor/symbolic/actions/symbols-symbolic.svg @@ -1,3 +1,156 @@ - - + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/symbols-symbolic.svg b/share/icons/multicolor/symbolic/actions/symbols-symbolic.svg index f889a69a99817d4b0f4b20e700fe6228f96e8732..ee7d9b1b700f944a9239e31fbd8a013de961917e 100644 --- a/share/icons/multicolor/symbolic/actions/symbols-symbolic.svg +++ b/share/icons/multicolor/symbolic/actions/symbols-symbolic.svg @@ -1,3 +1,165 @@ - - + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/share/ui/style.css b/share/ui/style.css index 33b3af4e91bfe551b66c6c73eb566f0106bf790f..429d8ac206f0950022a460fb91dbc79d474a408a 100644 --- a/share/ui/style.css +++ b/share/ui/style.css @@ -195,14 +195,14 @@ notebook.blink > header > tabs > tab:checked { color: @success_color; } -iconview *:hover { +iconview *:hover ,iconview *:selected{ border-width: 1px; border-color: @theme_fg_color; border-style: solid; border-radius: 4px; } -.dark iconview *:hover { +.dark iconview *:hover, .dark iconview *:selected { border-color: @theme_bg_color; } diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp index 6df884c22632fb72277c1716530e201066784b82..a6c7fad386647f4fe2fab9c8514865fff9500104 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -254,8 +254,7 @@ object_to_path(InkscapeApplication *app) // We should not have to do this! auto document = app->get_active_document(); selection->setDocument(document); - - selection->toCurves(); // TODO: Rename toPaths() + selection->toCurves(false, Inkscape::Preferences::get()->getBool("/options/clonestocurvesjustunlink/value", true)); } diff --git a/src/document.cpp b/src/document.cpp index 9980bd11665849030989f7e911b0c96decc4be0e..3731de8981830ab8fa5a70bdac4fa6f210bc9ce9 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -54,6 +54,7 @@ #include "inkscape-window.h" #include "profile-manager.h" #include "rdf.h" +#include "style.h" #include "live_effects/effect.h" @@ -1953,33 +1954,29 @@ void SPDocument::_importDefsNode(SPDocument *source, Inkscape::XML::Node *defs, if (src && (s_gr || s_lpeobj)) { for (auto& trg: getDefs()->children) { auto t_gr = cast(&trg); + bool exists = false; if (src != &trg && s_gr && t_gr) { if (s_gr->isEquivalent(t_gr)) { - // Change object references to the existing equivalent gradient - Glib::ustring newid = trg.getId(); - if (newid != defid) { // id could be the same if it is a second paste into the same document - change_def_references(src, &trg); - } - gchar *longid = g_strdup_printf("%s_%9.9d", DuplicateDefString.c_str(), stagger++); - def->setAttribute("id", longid); - g_free(longid); - // do NOT break here, there could be more than 1 duplicate! + exists = true; } } auto t_lpeobj = cast(&trg); if (src != &trg && s_lpeobj && t_lpeobj) { if (t_lpeobj->is_similar(s_lpeobj)) { - // Change object references to the existing equivalent gradient - Glib::ustring newid = trg.getId(); - if (newid != defid) { // id could be the same if it is a second paste into the same document - change_def_references(src, &trg); - } - gchar *longid = g_strdup_printf("%s_%9.9d", DuplicateDefString.c_str(), stagger++); - def->setAttribute("id", longid); - g_free(longid); - // do NOT break here, there could be more than 1 duplicate! + exists = true; } } + if (exists) { + // Change object references to the existing equivalent gradient + Glib::ustring newid = trg.getId(); + if (newid != defid) { // id could be the same if it is a second paste into the same document + change_def_references(src, &trg); + } + gchar *longid = g_strdup_printf("%s_%9.9d", DuplicateDefString.c_str(), stagger++); + def->setAttribute("id", longid); + g_free(longid); + // do NOT break here, there could be more than 1 duplicate! + } } } } @@ -2057,15 +2054,13 @@ void SPDocument::_importDefsNode(SPDocument *source, Inkscape::XML::Node *defs, if (is(&trg) && src != &trg) { std::string id2 = trg.getRepr()->attribute("id"); - if( !id.compare( id2 ) ) { + if( !id.compare( id2 )) { duplicate = true; break; } } } - if ( !duplicate ) { - src->setAttribute("id", id); - } + rename_id(src, id); } } diff --git a/src/live_effects/lpeobject.cpp b/src/live_effects/lpeobject.cpp index 4cf4a97bc395b0951a3d687cb6f50ab4c37fa06e..5687a66a50ec3f87901bc69ee541cde9f7b49df6 100644 --- a/src/live_effects/lpeobject.cpp +++ b/src/live_effects/lpeobject.cpp @@ -144,7 +144,7 @@ bool LivePathEffectObject::is_similar(LivePathEffectObject *that) } Inkscape::LivePathEffect::Effect *thislpe = this->get_lpe(); Inkscape::LivePathEffect::Effect *thatlpe = that->get_lpe(); - if (thatlpe && thislpe && thislpe->getName() != thatlpe->getName()) { + if (!thatlpe || !thislpe || thislpe->getName() != thatlpe->getName()) { return false; } } diff --git a/src/object/object-set.h b/src/object/object-set.h index 76301b862deb9d64823495ffafb5c4c5d9a1bb04..875877f14cb58dda7ddaa5217bd6f7a78137b786 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -463,7 +463,7 @@ public: //in path-chemistry.cpp void combine(bool skip_undo = false, bool silent = false); void breakApart(bool skip_undo = false, bool overlapping = true, bool silent = false); - void toCurves(bool skip_undo = false); + void toCurves(bool skip_undo = false, bool clonesjustunlink = false); void toLPEItems(); void pathReverse(); diff --git a/src/object/sp-lpe-item.cpp b/src/object/sp-lpe-item.cpp index a0e8b57d4410c5c796d963ae2b28a9a2d020fec6..439af923369c71ad4d5502ae4875bdb4552020fd 100755 --- a/src/object/sp-lpe-item.cpp +++ b/src/object/sp-lpe-item.cpp @@ -44,6 +44,7 @@ #include "sp-path.h" #include "sp-rect.h" #include "sp-root.h" +#include "sp-symbol.h" #include "svg/svg.h" #include "ui/shape-editor.h" #include "uri.h" @@ -83,7 +84,7 @@ SPLPEItem::~SPLPEItem() = default; void SPLPEItem::build(SPDocument *document, Inkscape::XML::Node *repr) { this->readAttr(SPAttr::INKSCAPE_PATH_EFFECT); - + onsymbol = isOnSymbol(); SPItem::build(document, repr); } @@ -218,6 +219,10 @@ bool SPLPEItem::isOnClipboard() return clipnode != nullptr; } +bool SPLPEItem::isOnSymbol() const { + auto p = cast(parent); + return (p && p->onsymbol) || is(this); +} /** * returns true when LPE was successful. */ @@ -1749,7 +1754,7 @@ void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable) // Are the path effects enabled on this item ? bool SPLPEItem::pathEffectsEnabled() const { - return path_effects_enabled > 0; + return !onsymbol && path_effects_enabled > 0; } /* diff --git a/src/object/sp-lpe-item.h b/src/object/sp-lpe-item.h index 4cef73ebcfaed3d1d65ab34cb1915c5e377114ef..9b1b6017755978310551e05490e4b181e76b18f1 100644 --- a/src/object/sp-lpe-item.h +++ b/src/object/sp-lpe-item.h @@ -91,6 +91,8 @@ public: std::vector getPathEffects() const; std::vector get_satellites(bool force = true, bool recursive = false, bool onchilds = false); bool isOnClipboard(); + bool isOnSymbol() const; + bool onsymbol = false; bool hasBrokenPathEffect() const; bool lpe_initialized = false; PathEffectList getEffectList(); diff --git a/src/object/sp-use.cpp b/src/object/sp-use.cpp index 1fd55bc20197bb679dcf9b0285963f1f6dc4a684..afc0adb6981c628ba5fda9c7df6c6e5d1e14895a 100644 --- a/src/object/sp-use.cpp +++ b/src/object/sp-use.cpp @@ -741,7 +741,7 @@ SPItem *SPUse::unlink() { // Track the ultimate source of a chain of uses. SPItem *orig = this->root(); - + SPItem *origtrue = this->trueOriginal(); if (!orig) { return nullptr; } @@ -754,29 +754,44 @@ SPItem *SPUse::unlink() { if (auto symbol = cast(orig)) { // make a group, copy children copy = xml_doc->createElement("svg:g"); + copy->setAttribute("display","none"); for (Inkscape::XML::Node *child = orig->getRepr()->firstChild() ; child != nullptr; child = child->next()) { - Inkscape::XML::Node *newchild = child->duplicate(xml_doc); - copy->appendChild(newchild); + Inkscape::XML::Node *newchild = child->duplicate(xml_doc); + copy->appendChild(newchild); } // viewBox transformation t = symbol->c2p * t; } else { // just copy copy = orig->getRepr()->duplicate(xml_doc); + copy->setAttribute("display","none"); } - // Add the duplicate repr just after the existing one. parent->addChild(copy, repr); // Retrieve the SPItem of the resulting repr. SPObject *unlinked = document->getObjectByRepr(copy); - + if (origtrue) { + if (unlinked) { + origtrue->setTmpSuccessor(unlinked); + } + auto newLPEObj = cast(unlinked); + if (newLPEObj) { + // force always fork + newLPEObj->forkPathEffectsIfNecessary(1, true, true); + sp_lpe_item_update_patheffect(newLPEObj, false, true, true); + } + origtrue->fixTmpSuccessors(); + origtrue->unsetTmpSuccessor(); + } + // Merge style from the use. unlinked->style->merge( this->style ); unlinked->style->cascade( unlinked->parent->style ); unlinked->updateRepr(); - + unlinked->removeAttribute("display"); + // Hold onto our SPObject and repr for now. sp_object_ref(this); Inkscape::GC::anchor(repr); @@ -809,6 +824,7 @@ SPItem *SPUse::unlink() { // Advertise ourselves as not moving. item->doWriteTransform(t, &nomove); } + document->fix_lpe_data(); return item; } diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 7375105911c471ba53c6ba6dca14bd8cc4e2cacd..6d2b801aaab80913ff67e8a782f87b6a94af759c 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -301,7 +301,7 @@ ObjectSet::breakApart(bool skip_undo, bool overlapping, bool silent) } } -void ObjectSet::toCurves(bool skip_undo) +void ObjectSet::toCurves(bool skip_undo, bool clonesjustunlink) { if (isEmpty()) { if (desktop()) @@ -315,13 +315,17 @@ void ObjectSet::toCurves(bool skip_undo) // set "busy" cursor desktop()->setWaitingCursor(); } - unlinkRecursive(true, false, true); + if (!clonesjustunlink) { + unlinkRecursive(true, false, true); + } std::vector selected(items().begin(), items().end()); std::vector to_select; std::vector items(selected); did = sp_item_list_to_curves(items, selected, to_select); - + if (clonesjustunlink) { + unlinkRecursive(true, false, true); + } if (did) { setReprList(to_select); addList(selected); diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index 2a4fcfdec0ca00ae13e5fd803620b3bf48ea41f4..b9ffd213d05970515a4d0769035e706cadffd5ed 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -67,6 +67,8 @@ #include "object/sp-rect.h" #include "object/sp-root.h" #include "object/sp-shape.h" +#include "object/sp-style-elem.h" +#include "object/sp-symbol.h" #include "object/sp-textpath.h" #include "object/sp-use.h" #include "page-manager.h" @@ -341,7 +343,9 @@ void ClipboardManagerImpl::copySymbol(Inkscape::XML::Node* symbol, gchar const* _discardInternalClipboard(); _createInternalClipboard(); - + + auto original = cast(source->getObjectByRepr(symbol)); + _copyUsedDefs(original); // We add "_duplicate" to have a well defined symbol name that // bypasses the "prevent_id_classes" routine. We'll get rid of it // when we paste. @@ -351,51 +355,56 @@ void ClipboardManagerImpl::copySymbol(Inkscape::XML::Node* symbol, gchar const* symbol_name += "_inkscape_duplicate"; repr->setAttribute("id", symbol_name); _defs->appendChild(repr); - - auto scale = _clipboardSPDoc->getDocumentScale(); - if (auto group = cast(_clipboardSPDoc->getObjectByRepr(repr))) { + auto nsymbol = cast(_clipboardSPDoc->getObjectById(symbol_name)); + if (nsymbol) { + nsymbol->setAttribute("inkscape:symbol-style", style); + const gchar *stylewrap = source->getRoot()->getAttribute("style"); + source->getRoot()->setAttribute("style", nullptr); + _copyCompleteStyle(original, repr, true); + source->getRoot()->setAttribute("style", stylewrap); + auto scale = _clipboardSPDoc->getDocumentScale(); // Convert scale from source to clipboard user units - group->scaleChildItemsRec(scale, Geom::Point(0, 0), false); - } - - auto href = Glib::ustring("#") + symbol->attribute("id"); - Inkscape::XML::Node *use_repr = _doc->createElement("svg:use"); - use_repr->setAttribute("xlink:href", href); - - /** - * If the symbol has a viewBox but no width or height, then take width and - * height from the viewBox and set them on the use element. Otherwise, the - * use element will have 100% document width and height! - */ - { - auto widthAttr = symbol->attribute("width"); - auto heightAttr = symbol->attribute("height"); - auto viewBoxAttr = symbol->attribute("viewBox"); - - if (viewBoxAttr && !(heightAttr || widthAttr)) { - SPViewBox vb; - vb.set_viewBox(viewBoxAttr); - if (vb.viewBox_set) { - use_repr->setAttributeSvgDouble("width", vb.viewBox.width()); - use_repr->setAttributeSvgDouble("height", vb.viewBox.height()); + nsymbol->scaleChildItemsRec(scale, Geom::Point(0, 0), false); + if (!nsymbol->title()) { + nsymbol->setTitle(nsymbol->label() ? nsymbol->label() : nsymbol->getId()); + } + auto href = Glib::ustring("#") + symbol_name; + Inkscape::XML::Node *use_repr = _doc->createElement("svg:use"); + use_repr->setAttribute("xlink:href", href); + /** + * If the symbol has a viewBox but no width or height, then take width and + * height from the viewBox and set them on the use element. Otherwise, the + * use element will have 100% document width and height! + */ + { + auto widthAttr = symbol->attribute("width"); + auto heightAttr = symbol->attribute("height"); + auto viewBoxAttr = symbol->attribute("viewBox"); + if (viewBoxAttr && !(heightAttr || widthAttr)) { + SPViewBox vb; + vb.set_viewBox(viewBoxAttr); + if (vb.viewBox_set) { + use_repr->setAttributeSvgDouble("width", vb.viewBox.width()); + use_repr->setAttributeSvgDouble("height", vb.viewBox.height()); + } } } - } + // Set a default style in rather than so it can be changed. + use_repr->setAttribute("style", style); + _root->appendChild(use_repr); + // because a extrange reason on append use getObjectsByElement("symbol") return 2 elements, + // it not give errrost by the moment; + if (auto use = cast(_clipboardSPDoc->getObjectByRepr(use_repr))) { + Geom::Affine affine = source->getDocumentScale(); + use->doWriteTransform(affine, &affine, false); + } - // Set a default style in rather than so it can be changed. - use_repr->setAttribute("style", style); - _root->appendChild(use_repr); + // Set min and max offsets based on the bounding rectangle. + _clipnode->setAttributePoint("min", bbox.min()); + _clipnode->setAttributePoint("max", bbox.max()); - if (auto use = cast(_clipboardSPDoc->getObjectByRepr(use_repr))) { - Geom::Affine affine = source->getDocumentScale(); - use->doWriteTransform(affine, &affine, false); + fit_canvas_to_drawing(_clipboardSPDoc.get()); } - - // Set min and max offsets based on the bounding rectangle. - _clipnode->setAttributePoint("min", bbox.min()); - _clipnode->setAttributePoint("max", bbox.max()); - - fit_canvas_to_drawing(_clipboardSPDoc.get()); _setClipboardTargets(); } diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 6bb117ca0b400e992f3a78da90752726d47d3dc4..38fd578c542b59be4a2c926c13a4a9531d3a3343 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -2691,7 +2691,9 @@ void InkscapePreferences::initPageBehavior() _clone_to_curves.init ( _("Path operations unlink clones"), "/options/pathoperationsunlink/value", true); _page_clones.add_line(true, "", _clone_to_curves, "", _("The following path operations will unlink clones: Stroke to path, Object to path, Boolean operations, Combine, Break apart")); - + _clone_ignore_to_curves.init ( _("Convert to paths just unlink clones (keep LPE, and shapes)"), "/options/clonestocurvesjustunlink/value", true); + _page_clones.add_line(true, "", _clone_ignore_to_curves, "", + _("The following path operations just unlink clones on convert to paths action (keep LPE, and shapes)")); //TRANSLATORS: Heading for the Inkscape Preferences "Clones" Page this->AddPage(_page_clones, _("Clones"), iter_behavior, PREFS_PAGE_BEHAVIOR_CLONES); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index ebb614e83a79df446605030f79f068448b59c859..5fb9d9a515b22ad3a7b2415f7ce47fe6ac1d728f 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -327,6 +327,7 @@ protected: UI::Widget::PrefRadioButton _clone_option_delete; UI::Widget::PrefCheckButton _clone_relink_on_duplicate; UI::Widget::PrefCheckButton _clone_to_curves; + UI::Widget::PrefCheckButton _clone_ignore_to_curves; UI::Widget::PrefCheckButton _mask_mask_on_top; UI::Widget::PrefCheckButton _mask_mask_remove; diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp index ac49bbd60bd006de5d6a474f0ef4bcffb47a2457..c4a9f7f308497846e7090550a533c9fb1da3fba5 100644 --- a/src/ui/dialog/symbols.cpp +++ b/src/ui/dialog/symbols.cpp @@ -46,6 +46,7 @@ #include "ui/icon-names.h" #include "ui/widget/scrollprotected.h" #include "xml/href-attribute-helper.h" +#include "widgets/toolbox.h" #ifdef WITH_LIBVISIO #include @@ -65,6 +66,26 @@ namespace UI { namespace Dialog { static std::map > symbol_sets; + +/* + * Return empty doc to render symbols in. + * Symbols are by default not rendered so a element is + * provided. + */ +SPDocument* sp_symbols_preview_doc() +{ + // BUG: must be inside + gchar const *buffer = +"" +" " +" " +""; + return SPDocument::createNewDocFromMem( buffer, strlen(buffer), FALSE ); +} + /** * Constructor */ @@ -74,7 +95,6 @@ SymbolsDialog::SymbolsDialog(gchar const *prefsPath) , store(Gtk::ListStore::create(_columns)) , all_docs_processed(false) , icon_view(nullptr) - , preview_document(nullptr) , gtk_connections() , CURRENTDOC(_("Current document")) , ALLDOCS(_("All symbol sets")) @@ -151,8 +171,8 @@ SymbolsDialog::SymbolsDialog(gchar const *prefsPath) icon_view->enable_model_drag_source (targets, Gdk::BUTTON1_MASK, Gdk::ACTION_COPY); gtk_connections.emplace_back( icon_view->signal_drag_data_get().connect(sigc::mem_fun(*this, &SymbolsDialog::iconDragDataGet))); - gtk_connections.emplace_back( - icon_view->signal_selection_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::iconChanged))); + //gtk_connections.emplace_back( + // icon_view->signal_selection_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::iconChanged))); gtk_connections.emplace_back( icon_view->signal_drag_begin().connect( [=](Glib::RefPtr const &) { onDragStart(); })); @@ -313,10 +333,6 @@ SymbolsDialog::SymbolsDialog(gchar const *prefsPath) sensitive = true; - preview_document = symbolsPreviewDoc(); /* Template to render symbols in */ - key = SPItem::display_key_new(1); - renderDrawing.setRoot(preview_document->getRoot()->invoke_show(renderDrawing, key, SP_ITEM_SHOW_DISPLAY )); - getSymbolsTitle(); icons_found = false; } @@ -328,10 +344,47 @@ SymbolsDialog::~SymbolsDialog() } gtk_connections.clear(); idleconn.disconnect(); +} - Inkscape::GC::release(preview_document); - assert(preview_document->_anchored_refcount() == 0); - delete preview_document; +void useInDoc (SPObject *r, std::vector &l) +{ + + if (is(r) ) { + l.push_back(cast(r)); + } + + for (auto& child: r->children) { + useInDoc( &child, l ); + } +} + +std::vector useInDoc( SPDocument* useDocument) { + std::vector l; + useInDoc (useDocument->getRoot(), l); + return l; +} + +// Returns style from first element found that references id. +// This is a last ditch effort to find a style. +gchar const* styleFromUse( gchar const* id, SPDocument* document) { + + gchar const* style = nullptr; + std::vector l = useInDoc( document ); + for( auto use:l ) { + if ( use ) { + gchar const *href = Inkscape::getHrefAttribute(*use->getRepr()).second; + if( href ) { + Glib::ustring href2(href); + Glib::ustring id2(id); + id2 = "#" + id2; + if( !href2.compare(id2) ) { + style = use->getRepr()->attribute("style"); + break; + } + } + } + } + return style; } bool findString(const Glib::ustring & haystack, const Glib::ustring & needle) @@ -493,9 +546,14 @@ void SymbolsDialog::iconDragDataGet(const Glib::RefPtr& /*cont void SymbolsDialog::defsModified(SPObject * /*object*/, guint /*flags*/) { + static size_t nsymbols = 0; Glib::ustring doc_title = get_active_base_text(); - if (doc_title != ALLDOCS && !symbol_sets[doc_title].second ) { - rebuild(); + if (doc_title != ALLDOCS && !symbol_sets[doc_title].second) { + size_t nsyms = getDocument()->getObjectsByElement("symbol").size(); + if (nsymbols != nsyms) { + rebuild(); + nsymbols = nsyms; + } } } @@ -633,19 +691,25 @@ void SymbolsDialog::sendToClipboard(Gtk::TreeModel::Path const &symbol_path, Geo if (SPObject* symbol = symbol_document->getObjectById(symbol_id)) { // Find style for use in // First look for default style stored in - gchar const* style = symbol->getAttribute("inkscape:symbol-style"); + gchar const* style = styleFromUse(symbol_id.c_str(), symbol_document); if (!style) { - // If no default style in , look in documents. - if (symbol_document == getDocument()) { - style = styleFromUse(symbol_id.c_str(), symbol_document); - } else { - style = symbol_document->getReprRoot()->attribute("style"); - } + style = symbol_document->getReprRoot()->attribute("style"); } + auto symbolis = cast(symbol); + if (!symbolis) { + return; + } + // Update all GUI text + std::list< SPDesktop* > listbuf; + // Get list of all available desktop + INKSCAPE.get_all_desktops(listbuf); auto const dims = getSymbolDimensions(symbol_path); ClipboardManager *cm = ClipboardManager::get(); - cm->copySymbol(symbol->getRepr(), style, symbol_document, bbox); + cm->copySymbol(symbolis->getRepr(), style, symbol_document, bbox); } + sensitive = false; + icon_view->select_path(symbol_path); + sensitive = true; } void SymbolsDialog::iconChanged() @@ -655,52 +719,15 @@ void SymbolsDialog::iconChanged() sendToClipboard(*selected, Geom::Rect(-0.5 * dims, 0.5 * dims)); } } - -/** Handle the start of a drag on a symbol preview icon. */ void SymbolsDialog::onDragStart() { - auto selected = getSelected(); - if (!selected) { - return; + if (!sensitive) { + return; } - - // Get the rectangle of the cell where the drag started. - Gdk::Rectangle temprect; - icon_view->get_cell_rect(*selected, temprect); - auto cell_rect = Geom::IntRect::from_xywh({temprect.get_x(), temprect.get_y()}, - {temprect.get_width(), temprect.get_height()}); - - // Find the rectangle of the actual symbol preview - // (not the same as the cell rectangle, due to fitting and padding). - auto const dims = getSymbolDimensions(selected); - unsigned preview_size = SYMBOL_ICON_SIZES[pack_size]; - Geom::Dim2 larger_dim = dims[Geom::X] > dims[Geom::Y] ? Geom::X : Geom::Y; - Geom::Dim2 smaller_dim = (Geom::Dim2)(!larger_dim); - Geom::Rect preview_rect; ///< The actual rectangle taken up by the bbox of the rendered preview. - - Geom::Interval larger_interval = cell_rect[larger_dim]; - larger_interval.expandBy(0.5 * (preview_size - larger_interval.extent())); // Trim off the padding. - preview_rect[larger_dim] = larger_interval; - - double const proportionally_scaled_smaller = preview_size * dims[smaller_dim] / dims[larger_dim]; - double const smaller_trim = 0.5 * (cell_rect[smaller_dim].extent() - proportionally_scaled_smaller); - Geom::Interval smaller_interval = cell_rect[smaller_dim]; - smaller_interval.expandBy(-smaller_trim); // Trim off padding and the "letterboxes" for non-square bbox. - preview_rect[smaller_dim] = smaller_interval; - - // Map the last mousedown position to [0, 1] x [0, 1] coordinates in the preview rectangle. - Geom::Point normalized_position = _last_mousedown - preview_rect.min(); - normalized_position.x() = std::clamp(normalized_position.x() / preview_rect.width(), 0.0, 1.0); - normalized_position.y() /= preview_rect.height(); - if (auto desktop = getDesktop(); desktop && !desktop->is_yaxisdown()) { - normalized_position.y() = 1.0 - normalized_position.y(); + if (auto selected = getSelected()) { + auto const dims = getSymbolDimensions(selected); + sendToClipboard(*selected, Geom::Rect(-0.5 * dims, 0.5 * dims)); } - normalized_position.y() = std::clamp(normalized_position.y(), 0.0, 1.0); - - // Push the symbol into the private clipboard with the correct bounding box. This box has dimensions - // `dims` but is positioned in such a way that the origin point (0, 0) lies at `normalized_position`. - auto const box_position = -Geom::Point(normalized_position.x() * dims.x(), normalized_position.y() * dims.y()); - sendToClipboard(*selected, Geom::Rect::from_xywh(box_position, dims)); } #ifdef WITH_LIBVISIO @@ -845,12 +872,14 @@ void SymbolsDialog::getSymbolsTitle() { } else { std::ifstream infile(filename); std::string line; + std::string pline = ""; //stored titles can be in 2 lines while (std::getline(infile, line)) { - std::string title_res = std::regex_replace (line, matchtitle,"$1",std::regex_constants::format_no_copy); + std::string title_res = std::regex_replace (pline + line, matchtitle,"$1",std::regex_constants::format_no_copy); + pline = line; if (!title_res.empty()) { title_res = g_dpgettext2(nullptr, "Symbol", title_res.c_str()); if (!symbol_sets[filename].second) - symbol_sets[filename]= std::make_pair(Glib::ustring(title_res),nullptr); + symbol_sets[filename]= std::make_pair(title_res,nullptr); ++number_docs; break; } @@ -930,9 +959,9 @@ void SymbolsDialog::symbolsInDocRecursive (SPObject *r, std::mapgetAttribute("id"); gchar * title = r->title(); if(title) { - l[doc_title + title + id] = std::make_pair(doc_title,cast(r)); + l[Glib::ustring::format(l.size()) + "_" + doc_title + title + id] = std::make_pair(doc_title,cast(r)); } else { - l[Glib::ustring(_("notitle_")) + id] = std::make_pair(doc_title,cast(r)); + l[Glib::ustring::format(l.size()) + "_" + Glib::ustring(_("notitle_")) + id] = std::make_pair(doc_title,cast(r)); } g_free(title); } @@ -952,47 +981,6 @@ SymbolsDialog::symbolsInDoc( SPDocument* symbol_document, Glib::ustring doc_titl return l; } -void SymbolsDialog::useInDoc (SPObject *r, std::vector &l) -{ - - if (is(r) ) { - l.push_back(cast(r)); - } - - for (auto& child: r->children) { - useInDoc( &child, l ); - } -} - -std::vector SymbolsDialog::useInDoc( SPDocument* useDocument) { - std::vector l; - useInDoc (useDocument->getRoot(), l); - return l; -} - -// Returns style from first element found that references id. -// This is a last ditch effort to find a style. -gchar const* SymbolsDialog::styleFromUse( gchar const* id, SPDocument* document) { - - gchar const* style = nullptr; - std::vector l = useInDoc( document ); - for( auto use:l ) { - if ( use ) { - gchar const *href = Inkscape::getHrefAttribute(*use->getRepr()).second; - if( href ) { - Glib::ustring href2(href); - Glib::ustring id2(id); - id2 = "#" + id2; - if( !href2.compare(id2) ) { - style = use->getRepr()->attribute("style"); - break; - } - } - } - } - return style; -} - void SymbolsDialog::clearSearch() { if(search->get_text().empty() && sensitive) { @@ -1039,9 +1027,9 @@ void SymbolsDialog::searchsymbols() icons_found = false; addSymbolsInDoc(symbol_document); } else { + search->set_text(_("Loading all symbols...")); idleconn.disconnect(); idleconn = Glib::signal_idle().connect(sigc::mem_fun(*this, &SymbolsDialog::callbackAllSymbols)); - search->set_text(_("Loading all symbols...")); } } @@ -1052,54 +1040,51 @@ void SymbolsDialog::unsensitive(GdkEventKey* evt) bool SymbolsDialog::callbackSymbols(){ icon_view->hide(); - if (l.size()) { - showOverlay(); - for (auto symbol_data = l.begin(); symbol_data != l.end();) { - Glib::ustring doc_title = symbol_data->second.first; - SPSymbol * symbol = symbol_data->second.second; - counter_symbols ++; - gchar *symbol_title_char = symbol->title(); - gchar *symbol_desc_char = symbol->description(); - bool found = false; - if (symbol_title_char) { - Glib::ustring symbol_title = Glib::ustring(symbol_title_char).lowercase(); - auto pos = symbol_title.rfind(search_str); - auto pos_translated = Glib::ustring(g_dpgettext2(nullptr, "Symbol", symbol_title_char)).lowercase().rfind(search_str); + showOverlay(); + for (auto symbol_data = l.begin(); symbol_data != l.end();) { + Glib::ustring doc_title = symbol_data->second.first; + SPSymbol * symbol = symbol_data->second.second; + counter_symbols ++; + gchar *symbol_title_char = symbol->title(); + gchar *symbol_desc_char = symbol->description(); + bool found = false; + if (symbol_title_char) { + Glib::ustring symbol_title = Glib::ustring(symbol_title_char).lowercase(); + auto pos = symbol_title.rfind(search_str); + auto pos_translated = Glib::ustring(g_dpgettext2(nullptr, "Symbol", symbol_title_char)).lowercase().rfind(search_str); + if ((pos != std::string::npos) || (pos_translated != std::string::npos)) { + found = true; + } + if (!found && symbol_desc_char) { + Glib::ustring symbol_desc = Glib::ustring(symbol_desc_char).lowercase(); + auto pos = symbol_desc.rfind(search_str); + auto pos_translated = Glib::ustring(g_dpgettext2(nullptr, "Symbol", symbol_desc_char)).lowercase().rfind(search_str); if ((pos != std::string::npos) || (pos_translated != std::string::npos)) { found = true; } - if (!found && symbol_desc_char) { - Glib::ustring symbol_desc = Glib::ustring(symbol_desc_char).lowercase(); - auto pos = symbol_desc.rfind(search_str); - auto pos_translated = Glib::ustring(g_dpgettext2(nullptr, "Symbol", symbol_desc_char)).lowercase().rfind(search_str); - if ((pos != std::string::npos) || (pos_translated != std::string::npos)) { - found = true; - } - } } - if (symbol && (search_str.empty() || found)) { - addSymbol( symbol, doc_title); - icons_found = true; - } - symbol_data = l.erase(l.begin()); - //to get more items and best performance - g_free(symbol_title_char); - g_free(symbol_desc_char); } - if (!icons_found && !search_str.empty()) { - showOverlay(); - } else { - hideOverlay(); + if (symbol && (search_str.empty() || found)) { + addSymbol( symbol, doc_title); + icons_found = true; } - sensitive = false; - search->set_text(search_str); - sensitive = true; - enableWidgets(true); - icon_view->show(); - return false; + symbol_data = l.erase(l.begin()); + //to get more items and best performance + g_free(symbol_title_char); + g_free(symbol_desc_char); + } + if (!icons_found && !search_str.empty()) { + showOverlay(); + } else { + hideOverlay(); } + sensitive = false; + search->set_text(search_str); + sensitive = true; + enableWidgets(true); icon_view->show(); - return true; + Glib::ustring doc_title = get_active_base_text(); + return false; } Glib::ustring SymbolsDialog::get_active_base_text(Glib::ustring title) @@ -1117,32 +1102,24 @@ Glib::ustring SymbolsDialog::get_active_base_text(Glib::ustring title) bool SymbolsDialog::callbackAllSymbols(){ icon_view->hide(); Glib::ustring current = get_active_base_text(); - if (current == ALLDOCS && search->get_text() == _("Loading all symbols...")) { - size_t counter = 0; - std::map > symbol_sets_tmp = symbol_sets; - for(auto const &symbol_document_map : symbol_sets_tmp) { - ++counter; - Glib::ustring current = get_active_base_text(); - if (current == CURRENTDOC) { - return true; - } - SPDocument* symbol_document = symbol_document_map.second.second; - if (symbol_document) { - continue; - } - symbol_document = getSymbolsSet(symbol_document_map.first).second; - symbol_set->set_active_text(ALLDOCS); + size_t counter = 0; + std::map > symbol_sets_tmp = symbol_sets; + for(auto const &symbol_document_map : symbol_sets_tmp) { + ++counter; + SPDocument* symbol_document = symbol_document_map.second.second; + if (symbol_document) { + continue; } - symbol_sets_tmp.clear(); - hideOverlay(); - all_docs_processed = true; - addSymbols(); - search->set_text(search_str); - icon_view->show(); - return false; + symbol_document = getSymbolsSet(symbol_document_map.first).second; + symbol_set->set_active_text(ALLDOCS); } + symbol_sets_tmp.clear(); + hideOverlay(); + all_docs_processed = true; + addSymbols(); + search->set_text(search_str); icon_view->show(); - return true; + return false; } void SymbolsDialog::addSymbolsInDoc(SPDocument* symbol_document) { @@ -1244,34 +1221,45 @@ void SymbolsDialog::addSymbol(SPSymbol *symbol, Glib::ustring doc_title) Glib::RefPtr SymbolsDialog::drawSymbol(SPObject *symbol) { + return draw_symbol(symbol, SYMBOL_ICON_SIZES[pack_size], scale_factor, fit_symbol != nullptr ? fit_symbol->get_active() : true); +} +// set scale factor = 0 from external calls +Glib::RefPtr +SymbolsDialog::draw_symbol(SPObject *symbol, unsigned size, double scale_factor, bool fit_symbol) +{ + Glib::RefPtr pixbuf(nullptr); + if (!symbol) { + return pixbuf; + } + SPDocument *preview_document = sp_symbols_preview_doc(); + Inkscape::Drawing renderDrawing; + unsigned key = SPItem::display_key_new(1); // Create a copy repr of the symbol with id="the_symbol" Inkscape::XML::Node *repr = symbol->getRepr()->duplicate(preview_document->getReprDoc()); repr->setAttribute("id", "the_symbol"); - - // First look for default style stored in - gchar const* style = repr->attribute("inkscape:symbol-style"); - if(!style) { - // If no default style in , look in documents. - if(symbol->document == getDocument()) { - gchar const *id = symbol->getRepr()->attribute("id"); - style = styleFromUse( id, symbol->document ); - } else { - style = symbol->document->getReprRoot()->attribute("style"); + // look for default style stored in + gchar const* style = symbol->getAttribute("inkscape:symbol-style"); + if (!style) { + style = styleFromUse(symbol->getId(), symbol->document); + if (!style) { + style = symbol->document->getReprRoot()->attribute("style"); } } - // This is for display in Symbols dialog only if( style ) repr->setAttribute( "style", style ); - - SPDocument::install_reference_document scoped(preview_document, getDocument()); - preview_document->getDefs()->getRepr()->appendChild(repr); + const gchar *stylewrap = symbol->document->getRoot()->getAttribute("style"); + if (stylewrap) { + symbol->document->getRoot()->setAttribute("style", nullptr); + } + SPDocument::install_reference_document scoped(preview_document, symbol->document); + preview_document->getDefs()->getRepr()->appendChild(repr); Inkscape::GC::release(repr); - + renderDrawing.setRoot(preview_document->getRoot()->invoke_show(renderDrawing, key, SP_ITEM_SHOW_DISPLAY )); // Uncomment this to get the preview_document documents saved (useful for debugging) // FILE *fp = fopen (g_strconcat(id, ".svg", NULL), "w"); // sp_repr_save_stream(preview_document->getReprDoc(), fp); // fclose (fp); - + // Make sure preview_document is up-to-date. preview_document->ensureUpToDate(); @@ -1280,9 +1268,8 @@ SymbolsDialog::drawSymbol(SPObject *symbol) auto item = cast(object_temp); g_assert(item != nullptr); - unsigned psize = SYMBOL_ICON_SIZES[pack_size]; - - Glib::RefPtr pixbuf(nullptr); + unsigned psize = size; + // We could use cache here, but it doesn't really work with the structure // of this user interface and we've already cached the pixbuf in the gtklist @@ -1299,38 +1286,23 @@ SymbolsDialog::drawSymbol(SPObject *symbol) if( width == 0.0 ) width = 1.0; if( height == 0.0 ) height = 1.0; - - if( fit_symbol->get_active() ) + if (fit_symbol) { scale = psize / ceil(std::max(width, height)); - else + } else { scale = pow( 2.0, scale_factor/2.0 ) * psize / 32.0; - + } pixbuf = Glib::wrap(render_pixbuf(renderDrawing, scale, *dbox, psize)); } - - preview_document->getObjectByRepr(repr)->deleteObject(false); + if (stylewrap) { + symbol->document->getRoot()->setAttribute("style", stylewrap); + } + Inkscape::GC::release(preview_document); + assert(preview_document->_anchored_refcount() == 0); + delete preview_document; return pixbuf; } -/* - * Return empty doc to render symbols in. - * Symbols are by default not rendered so a element is - * provided. - */ -SPDocument* SymbolsDialog::symbolsPreviewDoc() -{ - // BUG: must be inside - gchar const *buffer = -"" -" " -""; - return SPDocument::createNewDocFromMem( buffer, strlen(buffer), FALSE ); -} - /* * Update image widgets */ diff --git a/src/ui/dialog/symbols.h b/src/ui/dialog/symbols.h index abbd40b781f6ad8f2804fb24f119cf93682c2de6..67c616211f909ffa973b0c4efd0657b0b38b80eb 100644 --- a/src/ui/dialog/symbols.h +++ b/src/ui/dialog/symbols.h @@ -76,7 +76,7 @@ class SymbolsDialog : public DialogBase public: SymbolsDialog(char const *prefsPath = "/dialogs/symbols"); ~SymbolsDialog() override; - + static Glib::RefPtr draw_symbol(SPObject *symbol, unsigned size, double scale_factor = 0, bool fit_symbol = true); private: void documentReplaced() override; void selectionChanged(Inkscape::Selection *selection) override; @@ -106,11 +106,8 @@ private: Glib::ustring documentTitle(SPDocument* doc); std::pair getSymbolsSet(std::string title); void addSymbol(SPSymbol *symbol, Glib::ustring doc_title); - SPDocument* symbolsPreviewDoc(); void symbolsInDocRecursive (SPObject *r, std::map > &l, Glib::ustring doc_title); std::map > symbolsInDoc( SPDocument* document, Glib::ustring doc_title); - void useInDoc(SPObject *r, std::vector &l); - std::vector useInDoc( SPDocument* document); void beforeSearch(GdkEventKey* evt); void unsensitive(GdkEventKey* evt); void searchsymbols(); @@ -123,7 +120,6 @@ private: bool callbackAllSymbols(); Glib::ustring get_active_base_text(Glib::ustring title = "selectedcombo"); void enableWidgets(bool enable); - gchar const* styleFromUse( gchar const* id, SPDocument* document); Glib::RefPtr drawSymbol(SPObject *symbol); Glib::RefPtr getOverlay(gint width, gint height); /* Keep track of all symbol template documents */ @@ -161,24 +157,15 @@ private: Gtk::ScrolledWindow *scroller; Gtk::ToggleButton* fit_symbol; Gtk::IconSize iconsize; - - SPDocument* preview_document; /* Document to render single symbol */ - sigc::connection idleconn; - /* For rendering the template drawing */ - unsigned key; - Inkscape::Drawing renderDrawing; - std::vector gtk_connections; Inkscape::auto_connection defs_modified; }; - -} //namespace Dialogs +} //namespace Dialog } //namespace UI } //namespace Inkscape - #endif // INKSCAPE_UI_DIALOG_SYMBOLS_H /*