summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/symbols.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/symbols.cpp')
-rw-r--r--src/ui/dialog/symbols.cpp1354
1 files changed, 1354 insertions, 0 deletions
diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp
new file mode 100644
index 0000000..147958b
--- /dev/null
+++ b/src/ui/dialog/symbols.cpp
@@ -0,0 +1,1354 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Symbols dialog.
+ */
+/* Authors:
+ * Copyright (C) 2012 Tavmjong Bah
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h" // only include where actually required!
+#endif
+
+#include "symbols.h"
+
+#include <iostream>
+#include <algorithm>
+#include <locale>
+#include <sstream>
+#include <fstream>
+#include <regex>
+
+#include <glibmm/i18n.h>
+#include <glibmm/markup.h>
+#include <glibmm/regex.h>
+#include <glibmm/stringutils.h>
+
+#include "document.h"
+#include "inkscape.h"
+#include "path-prefix.h"
+#include "selection.h"
+
+#include "display/cairo-utils.h"
+#include "include/gtkmm_version.h"
+#include "io/resource.h"
+#include "io/sys.h"
+#include "object/sp-defs.h"
+#include "object/sp-root.h"
+#include "object/sp-symbol.h"
+#include "object/sp-use.h"
+#include "ui/cache/svg_preview_cache.h"
+#include "ui/clipboard.h"
+#include "ui/icon-loader.h"
+#include "ui/icon-names.h"
+#include "ui/widget/scrollprotected.h"
+
+#ifdef WITH_LIBVISIO
+ #include <libvisio/libvisio.h>
+
+ #include <librevenge-stream/librevenge-stream.h>
+
+ using librevenge::RVNGFileStream;
+ using librevenge::RVNGString;
+ using librevenge::RVNGStringVector;
+ using librevenge::RVNGPropertyList;
+ using librevenge::RVNGSVGDrawingGenerator;
+#endif
+
+
+namespace Inkscape {
+namespace UI {
+
+namespace Dialog {
+
+// See: http://developer.gnome.org/gtkmm/stable/classGtk_1_1TreeModelColumnRecord.html
+class SymbolColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+
+ Gtk::TreeModelColumn<Glib::ustring> symbol_id;
+ Gtk::TreeModelColumn<Glib::ustring> symbol_title;
+ Gtk::TreeModelColumn<Glib::ustring> symbol_doc_title;
+ Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Pixbuf> > symbol_image;
+
+
+ SymbolColumns() {
+ add(symbol_id);
+ add(symbol_title);
+ add(symbol_doc_title);
+ add(symbol_image);
+ }
+};
+
+SymbolColumns* SymbolsDialog::getColumns()
+{
+ SymbolColumns* columns = new SymbolColumns();
+ return columns;
+}
+
+/**
+ * Constructor
+ */
+SymbolsDialog::SymbolsDialog(gchar const *prefsPath)
+ : DialogBase(prefsPath, "Symbols")
+ , store(Gtk::ListStore::create(*getColumns()))
+ , all_docs_processed(false)
+ , icon_view(nullptr)
+ , preview_document(nullptr)
+ , gtk_connections()
+ , CURRENTDOC(_("Current document"))
+ , ALLDOCS(_("All symbol sets"))
+{
+ /******************** Table *************************/
+ auto table = new Gtk::Grid();
+
+ table->set_margin_start(3);
+ table->set_margin_end(3);
+ table->set_margin_top(4);
+ // panel is a locked Gtk::VBox
+ pack_start(*Gtk::manage(table), Gtk::PACK_EXPAND_WIDGET);
+ guint row = 0;
+
+ /******************** Symbol Sets *************************/
+ Gtk::Label* label_set = new Gtk::Label(Glib::ustring(_("Symbol set")) + ": ");
+ table->attach(*Gtk::manage(label_set),0,row,1,1);
+ symbol_set = new Inkscape::UI::Widget::ScrollProtected<Gtk::ComboBoxText>(); // Fill in later
+ symbol_set->append(CURRENTDOC);
+ symbol_set->append(ALLDOCS);
+ symbol_set->set_active_text(CURRENTDOC);
+ symbol_set->set_hexpand();
+ gtk_connections.emplace_back(
+ symbol_set->signal_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild)));
+
+ table->attach(*Gtk::manage(symbol_set),1,row,1,1);
+ ++row;
+
+ /******************** Separator *************************/
+
+
+ Gtk::Separator* separator = Gtk::manage(new Gtk::Separator()); // Search
+ separator->set_margin_top(10);
+ separator->set_margin_bottom(10);
+ table->attach(*Gtk::manage(separator),0,row,2,1);
+
+ ++row;
+
+ /******************** Search *************************/
+
+ search = Gtk::manage(new Gtk::SearchEntry()); // Search
+ search->set_tooltip_text(_("Press 'Return' to start search."));
+ search->signal_key_press_event().connect_notify( sigc::mem_fun(*this, &SymbolsDialog::beforeSearch));
+ search->signal_key_release_event().connect_notify(sigc::mem_fun(*this, &SymbolsDialog::unsensitive));
+
+ search->set_margin_bottom(6);
+ search->signal_search_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::clearSearch));
+ table->attach(*Gtk::manage(search),0,row,2,1);
+ search_str = "";
+
+ ++row;
+
+
+ /********************* Icon View **************************/
+ SymbolColumns* columns = getColumns();
+
+ icon_view = new Gtk::IconView(static_cast<Glib::RefPtr<Gtk::TreeModel> >(store));
+ //icon_view->set_text_column( columns->symbol_id );
+ icon_view->set_tooltip_column( 1 );
+ icon_view->set_pixbuf_column( columns->symbol_image );
+ // Giving the iconview a small minimum size will help users understand
+ // What the dialog does.
+ icon_view->set_size_request( 100, 250 );
+ icon_view->set_vexpand(true);
+ std::vector< Gtk::TargetEntry > targets;
+ targets.emplace_back( "application/x-inkscape-paste");
+
+ 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)));
+
+ scroller = new Gtk::ScrolledWindow();
+ scroller->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+ scroller->add(*Gtk::manage(icon_view));
+ scroller->set_hexpand();
+ scroller->set_vexpand();
+ scroller->set_overlay_scrolling(false);
+ // here we fix scoller to allow pass the scroll to parent scroll when reach upper or lower limit
+ // this must be added to al scrolleing window in dialogs. We dont do auto because dialogs can be recreated
+ // in the dialog code so think is safer call inside
+ fix_inner_scroll(scroller);
+ overlay = new Gtk::Overlay();
+ overlay->set_hexpand();
+ overlay->set_vexpand();
+ overlay->add(* scroller);
+ overlay->get_style_context()->add_class("forcebright");
+ scroller->set_size_request(100, -1);
+ table->attach(*Gtk::manage(overlay), 0, row, 2, 1);
+
+ /*************************Overlays******************************/
+ overlay_opacity = new Gtk::Image();
+ overlay_opacity->set_halign(Gtk::ALIGN_START);
+ overlay_opacity->set_valign(Gtk::ALIGN_START);
+ overlay_opacity->get_style_context()->add_class("rawstyle");
+ overlay_opacity->set_no_show_all(true);
+ // No results
+ overlay_icon = sp_get_icon_image("searching", Gtk::ICON_SIZE_DIALOG);
+ overlay_icon->set_pixel_size(110);
+ overlay_icon->set_halign(Gtk::ALIGN_CENTER);
+ overlay_icon->set_valign(Gtk::ALIGN_START);
+
+ overlay_icon->set_margin_top(25);
+ overlay_icon->set_no_show_all(true);
+
+ overlay_title = new Gtk::Label();
+ overlay_title->set_halign(Gtk::ALIGN_CENTER );
+ overlay_title->set_valign(Gtk::ALIGN_START );
+ overlay_title->set_justify(Gtk::JUSTIFY_CENTER);
+ overlay_title->set_margin_top(135);
+ overlay_title->set_no_show_all(true);
+
+ overlay_desc = new Gtk::Label();
+ overlay_desc->set_halign(Gtk::ALIGN_CENTER);
+ overlay_desc->set_valign(Gtk::ALIGN_START);
+ overlay_desc->set_margin_top(160);
+ overlay_desc->set_justify(Gtk::JUSTIFY_CENTER);
+ overlay_desc->set_no_show_all(true);
+
+ overlay->add_overlay(*overlay_opacity);
+ overlay->add_overlay(*overlay_icon);
+ overlay->add_overlay(*overlay_title);
+ overlay->add_overlay(*overlay_desc);
+
+ previous_height = 0;
+ previous_width = 0;
+ ++row;
+
+ /******************** Progress *******************************/
+ progress = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL);
+ progress_bar = Gtk::manage(new Gtk::ProgressBar());
+ table->attach(*Gtk::manage(progress),0,row, 2, 1);
+ progress->pack_start(* progress_bar, Gtk::PACK_EXPAND_WIDGET);
+ progress->set_margin_top(15);
+ progress->set_margin_bottom(15);
+ progress->set_margin_start(20);
+ progress->set_margin_end(20);
+
+ ++row;
+
+ /******************** Tools *******************************/
+ tools = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL);
+
+ //tools->set_layout( Gtk::BUTTONBOX_END );
+ scroller->set_hexpand();
+ table->attach(*Gtk::manage(tools),0,row,2,1);
+
+ auto add_symbol_image = Gtk::manage(sp_get_icon_image("symbol-add", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ add_symbol = Gtk::manage(new Gtk::Button());
+ add_symbol->add(*add_symbol_image);
+ add_symbol->set_tooltip_text(_("Add Symbol from the current document."));
+ add_symbol->set_relief( Gtk::RELIEF_NONE );
+ add_symbol->set_focus_on_click( false );
+ add_symbol->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::insertSymbol));
+ tools->pack_start(* add_symbol, Gtk::PACK_SHRINK);
+
+ auto remove_symbolImage = Gtk::manage(sp_get_icon_image("symbol-remove", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ remove_symbol = Gtk::manage(new Gtk::Button());
+ remove_symbol->add(*remove_symbolImage);
+ remove_symbol->set_tooltip_text(_("Remove Symbol from the current document."));
+ remove_symbol->set_relief( Gtk::RELIEF_NONE );
+ remove_symbol->set_focus_on_click( false );
+ remove_symbol->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::revertSymbol));
+ tools->pack_start(* remove_symbol, Gtk::PACK_SHRINK);
+
+ Gtk::Label* spacer = Gtk::manage(new Gtk::Label(""));
+ tools->pack_start(* Gtk::manage(spacer));
+
+ // Pack size (controls display area)
+ pack_size = 2; // Default 32px
+
+ auto packMoreImage = Gtk::manage(sp_get_icon_image("pack-more", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ more = Gtk::manage(new Gtk::Button());
+ more->add(*packMoreImage);
+ more->set_tooltip_text(_("Display more icons in row."));
+ more->set_relief( Gtk::RELIEF_NONE );
+ more->set_focus_on_click( false );
+ more->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::packmore));
+ tools->pack_start(* more, Gtk::PACK_SHRINK);
+
+ auto packLessImage = Gtk::manage(sp_get_icon_image("pack-less", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ fewer = Gtk::manage(new Gtk::Button());
+ fewer->add(*packLessImage);
+ fewer->set_tooltip_text(_("Display fewer icons in row."));
+ fewer->set_relief( Gtk::RELIEF_NONE );
+ fewer->set_focus_on_click( false );
+ fewer->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::packless));
+ tools->pack_start(* fewer, Gtk::PACK_SHRINK);
+
+ // Toggle scale to fit on/off
+ auto fit_symbolImage = Gtk::manage(sp_get_icon_image("symbol-fit", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ fit_symbol = Gtk::manage(new Gtk::ToggleButton());
+ fit_symbol->add(*fit_symbolImage);
+ fit_symbol->set_tooltip_text(_("Toggle 'fit' symbols in icon space."));
+ fit_symbol->set_relief( Gtk::RELIEF_NONE );
+ fit_symbol->set_focus_on_click( false );
+ fit_symbol->set_active( true );
+ fit_symbol->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::rebuild));
+ tools->pack_start(* fit_symbol, Gtk::PACK_SHRINK);
+
+ // Render size (scales symbols within display area)
+ scale_factor = 0; // Default 1:1 * pack_size/pack_size default
+ auto zoom_outImage = Gtk::manage(sp_get_icon_image("symbol-smaller", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ zoom_out = Gtk::manage(new Gtk::Button());
+ zoom_out->add(*zoom_outImage);
+ zoom_out->set_tooltip_text(_("Make symbols smaller by zooming out."));
+ zoom_out->set_relief( Gtk::RELIEF_NONE );
+ zoom_out->set_focus_on_click( false );
+ zoom_out->set_sensitive( false );
+ zoom_out->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::zoomout));
+ tools->pack_start(* zoom_out, Gtk::PACK_SHRINK);
+
+ auto zoom_inImage = Gtk::manage(sp_get_icon_image("symbol-bigger", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ zoom_in = Gtk::manage(new Gtk::Button());
+ zoom_in->add(*zoom_inImage);
+ zoom_in->set_tooltip_text(_("Make symbols bigger by zooming in."));
+ zoom_in->set_relief( Gtk::RELIEF_NONE );
+ zoom_in->set_focus_on_click( false );
+ zoom_in->set_sensitive( false );
+ zoom_in->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::zoomin));
+ tools->pack_start(* zoom_in, Gtk::PACK_SHRINK);
+
+ ++row;
+
+ sensitive = true;
+
+ preview_document = symbolsPreviewDoc(); /* Template to render symbols in */
+ preview_document->ensureUpToDate(); /* Necessary? */
+ key = SPItem::display_key_new(1);
+ renderDrawing.setRoot(preview_document->getRoot()->invoke_show(renderDrawing, key, SP_ITEM_SHOW_DISPLAY ));
+
+ getSymbolsTitle();
+ icons_found = false;
+}
+
+SymbolsDialog::~SymbolsDialog()
+{
+ for (auto &connection : gtk_connections) {
+ connection.disconnect();
+ }
+ gtk_connections.clear();
+ idleconn.disconnect();
+
+ Inkscape::GC::release(preview_document);
+ assert(preview_document->_anchored_refcount() == 0);
+ delete preview_document;
+}
+
+SymbolsDialog& SymbolsDialog::getInstance()
+{
+ return *new SymbolsDialog();
+}
+
+void SymbolsDialog::packless() {
+ if(pack_size < 4) {
+ pack_size++;
+ rebuild();
+ }
+}
+
+void SymbolsDialog::packmore() {
+ if(pack_size > 0) {
+ pack_size--;
+ rebuild();
+ }
+}
+
+void SymbolsDialog::zoomin() {
+ if(scale_factor < 4) {
+ scale_factor++;
+ rebuild();
+ }
+}
+
+void SymbolsDialog::zoomout() {
+ if(scale_factor > -8) {
+ scale_factor--;
+ rebuild();
+ }
+}
+
+void SymbolsDialog::rebuild() {
+
+ if (!sensitive) {
+ return;
+ }
+
+ if( fit_symbol->get_active() ) {
+ zoom_in->set_sensitive( false );
+ zoom_out->set_sensitive( false );
+ } else {
+ zoom_in->set_sensitive( true);
+ zoom_out->set_sensitive( true );
+ }
+ store->clear();
+ SPDocument* symbol_document = selectedSymbols();
+ icons_found = false;
+ //We are not in search all docs
+ if (search->get_text() != _("Searching...") && search->get_text() != _("Loading all symbols...")) {
+ Glib::ustring current = Glib::Markup::escape_text(symbol_set->get_active_text());
+ if (current == ALLDOCS && search->get_text() != "") {
+ searchsymbols();
+ return;
+ }
+ }
+ if (symbol_document) {
+ addSymbolsInDoc(symbol_document);
+ } else {
+ showOverlay();
+ }
+}
+void SymbolsDialog::showOverlay() {
+ Glib::ustring current = Glib::Markup::escape_text(symbol_set->get_active_text());
+ if (current == ALLDOCS && !l.size())
+ {
+ overlay_icon->hide();
+ if (!all_docs_processed ) {
+ overlay_icon->show();
+ overlay_title->set_markup(Glib::ustring("<span size=\"large\">") +
+ Glib::ustring(_("Search in all symbol sets...")) + Glib::ustring("</span>"));
+ overlay_desc->set_markup(Glib::ustring("<span size=\"small\">") +
+ Glib::ustring(_("The first search can be slow.")) + Glib::ustring("</span>"));
+ } else if (!icons_found && !search_str.empty()) {
+ overlay_title->set_markup(Glib::ustring("<span size=\"large\">") + Glib::ustring(_("No symbols found.")) +
+ Glib::ustring("</span>"));
+ overlay_desc->set_markup(Glib::ustring("<span size=\"small\">") +
+ Glib::ustring(_("Try a different search term.")) + Glib::ustring("</span>"));
+ } else {
+ overlay_icon->show();
+ overlay_title->set_markup(Glib::ustring("<spansize=\"large\">") +
+ Glib::ustring(_("Search in all symbol sets...")) + Glib::ustring("</span>"));
+ overlay_desc->set_markup(Glib::ustring("<span size=\"small\">") + Glib::ustring("</span>"));
+ }
+ } else if (!number_symbols && (current != CURRENTDOC || !search_str.empty())) {
+ overlay_title->set_markup(Glib::ustring("<span size=\"large\">") + Glib::ustring(_("No symbols found.")) +
+ Glib::ustring("</span>"));
+ overlay_desc->set_markup(Glib::ustring("<span size=\"small\">") +
+ Glib::ustring(_("Try a different search term,\nor switch to a different symbol set.")) +
+ Glib::ustring("</span>"));
+ } else if (!number_symbols && current == CURRENTDOC) {
+ overlay_title->set_markup(Glib::ustring("<span size=\"large\">") + Glib::ustring(_("No symbols found.")) +
+ Glib::ustring("</span>"));
+ overlay_desc->set_markup(
+ Glib::ustring("<span size=\"small\">") +
+ Glib::ustring(_("No symbols in current document.\nChoose a different symbol set\nor add a new symbol.")) +
+ Glib::ustring("</span>"));
+ } else if (!icons_found && !search_str.empty()) {
+ overlay_title->set_markup(Glib::ustring("<span size=\"large\">") + Glib::ustring(_("No symbols found.")) +
+ Glib::ustring("</span>"));
+ overlay_desc->set_markup(Glib::ustring("<span size=\"small\">") +
+ Glib::ustring(_("Try a different search term,\nor switch to a different symbol set.")) +
+ Glib::ustring("</span>"));
+ }
+ gint width = scroller->get_allocated_width();
+ gint height = scroller->get_allocated_height();
+ if (previous_height != height || previous_width != width) {
+ previous_height = height;
+ previous_width = width;
+ overlay_opacity->set_size_request(width, height);
+ overlay_opacity->set(getOverlay(width, height));
+ }
+ overlay_opacity->hide();
+ overlay_icon->show();
+ overlay_title->show();
+ overlay_desc->show();
+ if (l.size()) {
+ overlay_opacity->show();
+ overlay_icon->hide();
+ overlay_title->hide();
+ overlay_desc->hide();
+ }
+}
+
+void SymbolsDialog::hideOverlay() {
+ overlay_opacity->hide();
+ overlay_icon->hide();
+ overlay_title->hide();
+ overlay_desc->hide();
+}
+
+void SymbolsDialog::insertSymbol() {
+ getDesktop()->selection->toSymbol();
+}
+
+void SymbolsDialog::revertSymbol() {
+ if (auto document = getDocument()) {
+ if (auto symbol = dynamic_cast<SPSymbol*> (document->getObjectById(selectedSymbolId()))) {
+ symbol->unSymbol();
+ }
+ Inkscape::DocumentUndo::done(document, _("Group from symbol"), "");
+ }
+}
+
+void SymbolsDialog::iconDragDataGet(const Glib::RefPtr<Gdk::DragContext>& /*context*/, Gtk::SelectionData& data, guint /*info*/, guint /*time*/)
+{
+ auto iconArray = icon_view->get_selected_items();
+
+ if( iconArray.empty() ) {
+ //std::cout << " iconArray empty: huh? " << std::endl;
+ } else {
+ Gtk::TreeModel::Path const & path = *iconArray.begin();
+ Gtk::ListStore::iterator row = store->get_iter(path);
+ Glib::ustring symbol_id = (*row)[getColumns()->symbol_id];
+ GdkAtom dataAtom = gdk_atom_intern( "application/x-inkscape-paste", FALSE );
+ gtk_selection_data_set( data.gobj(), dataAtom, 9, (guchar*)symbol_id.c_str(), symbol_id.length() );
+ }
+
+}
+
+void SymbolsDialog::defsModified(SPObject * /*object*/, guint /*flags*/)
+{
+ Glib::ustring doc_title = symbol_set->get_active_text();
+ if (doc_title != ALLDOCS && !symbol_sets[doc_title] ) {
+ rebuild();
+ }
+}
+
+void SymbolsDialog::selectionChanged(Inkscape::Selection *selection) {
+ Glib::ustring symbol_id = selectedSymbolId();
+ Glib::ustring doc_title = selectedSymbolDocTitle();
+ if (!doc_title.empty()) {
+ SPDocument* symbol_document = symbol_sets[doc_title];
+ if (!symbol_document) {
+ //we are in global search so get the original symbol document by title
+ symbol_document = selectedSymbols();
+ }
+ if (symbol_document) {
+ SPObject* symbol = symbol_document->getObjectById(symbol_id);
+ if(symbol && !selection->includes(symbol)) {
+ icon_view->unselect_all();
+ }
+ }
+ }
+}
+
+void SymbolsDialog::documentReplaced()
+{
+ defs_modified.disconnect();
+ if (auto document = getDocument()) {
+ defs_modified = document->getDefs()->connectModified(sigc::mem_fun(*this, &SymbolsDialog::defsModified));
+ if (!symbol_sets[symbol_set->get_active_text()]) {
+ // Symbol set is from Current document, need to rebuild
+ rebuild();
+ }
+ }
+}
+
+SPDocument* SymbolsDialog::selectedSymbols() {
+ /* OK, we know symbol name... now we need to copy it to clipboard, bon chance! */
+ Glib::ustring doc_title = symbol_set->get_active_text();
+ if (doc_title == ALLDOCS) {
+ return nullptr;
+ }
+ SPDocument* symbol_document = symbol_sets[doc_title];
+ if( !symbol_document ) {
+ symbol_document = getSymbolsSet(doc_title).second;
+ // Symbol must be from Current Document (this method of checking should be language independent).
+ if( !symbol_document ) {
+ // Symbol must be from Current Document (this method of
+ // checking should be language independent).
+ symbol_document = getDocument();
+ add_symbol->set_sensitive( true );
+ remove_symbol->set_sensitive( true );
+ } else {
+ add_symbol->set_sensitive( false );
+ remove_symbol->set_sensitive( false );
+ }
+ }
+ return symbol_document;
+}
+
+Glib::ustring SymbolsDialog::selectedSymbolId() {
+
+ auto iconArray = icon_view->get_selected_items();
+
+ if( !iconArray.empty() ) {
+ Gtk::TreeModel::Path const & path = *iconArray.begin();
+ Gtk::ListStore::iterator row = store->get_iter(path);
+ return (*row)[getColumns()->symbol_id];
+ }
+ return Glib::ustring("");
+}
+
+Glib::ustring SymbolsDialog::selectedSymbolDocTitle() {
+
+ auto iconArray = icon_view->get_selected_items();
+
+ if( !iconArray.empty() ) {
+ Gtk::TreeModel::Path const & path = *iconArray.begin();
+ Gtk::ListStore::iterator row = store->get_iter(path);
+ return (*row)[getColumns()->symbol_doc_title];
+ }
+ return Glib::ustring("");
+}
+
+Glib::ustring SymbolsDialog::documentTitle(SPDocument* symbol_doc) {
+ if (symbol_doc) {
+ SPRoot * root = symbol_doc->getRoot();
+ gchar * title = root->title();
+ if (title) {
+ return ellipsize(Glib::ustring(title), 33);
+ }
+ g_free(title);
+ }
+ Glib::ustring current = symbol_set->get_active_text();
+ if (current == CURRENTDOC) {
+ return current;
+ }
+ return _("Untitled document");
+}
+
+void SymbolsDialog::iconChanged() {
+
+ Glib::ustring symbol_id = selectedSymbolId();
+ SPDocument* symbol_document = selectedSymbols();
+ if (!symbol_document) {
+ //we are in global search so get the original symbol document by title
+ Glib::ustring doc_title = selectedSymbolDocTitle();
+ if (!doc_title.empty()) {
+ symbol_document = symbol_sets[doc_title];
+ }
+ }
+ if (symbol_document) {
+ SPObject* symbol = symbol_document->getObjectById(symbol_id);
+
+ if( symbol ) {
+ // Find style for use in <use>
+ // First look for default style stored in <symbol>
+ gchar const* style = symbol->getAttribute("inkscape:symbol-style");
+ if( !style ) {
+ // If no default style in <symbol>, look in documents.
+ if(symbol_document == getDocument()) {
+ style = styleFromUse(symbol_id.c_str(), symbol_document);
+ } else {
+ style = symbol_document->getReprRoot()->attribute("style");
+ }
+ }
+
+ ClipboardManager *cm = ClipboardManager::get();
+ cm->copySymbol(symbol->getRepr(), style, symbol_document);
+ }
+ }
+}
+
+#ifdef WITH_LIBVISIO
+
+// Extend libvisio's native RVNGSVGDrawingGenerator with support for extracting stencil names (to be used as ID/title)
+class REVENGE_API RVNGSVGDrawingGenerator_WithTitle : public RVNGSVGDrawingGenerator {
+ public:
+ RVNGSVGDrawingGenerator_WithTitle(RVNGStringVector &output, RVNGStringVector &titles, const RVNGString &nmSpace)
+ : RVNGSVGDrawingGenerator(output, nmSpace)
+ , _titles(titles)
+ {}
+
+ void startPage(const RVNGPropertyList &propList) override
+ {
+ RVNGSVGDrawingGenerator::startPage(propList);
+ if (propList["draw:name"]) {
+ _titles.append(propList["draw:name"]->getStr());
+ } else {
+ _titles.append("");
+ }
+ }
+
+ private:
+ RVNGStringVector &_titles;
+};
+
+// Read Visio stencil files
+SPDocument* read_vss(Glib::ustring filename, Glib::ustring name ) {
+ gchar *fullname;
+ #ifdef _WIN32
+ // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows
+ // therefore attempt to convert uri to the system codepage
+ // even if this is not possible the alternate short (8.3) file name will be used if available
+ fullname = g_win32_locale_filename_from_utf8(filename.c_str());
+ #else
+ fullname = strdup(filename.c_str());
+ #endif
+
+ RVNGFileStream input(fullname);
+ g_free(fullname);
+
+ if (!libvisio::VisioDocument::isSupported(&input)) {
+ return nullptr;
+ }
+ RVNGStringVector output;
+ RVNGStringVector titles;
+ RVNGSVGDrawingGenerator_WithTitle generator(output, titles, "svg");
+
+ if (!libvisio::VisioDocument::parseStencils(&input, &generator)) {
+ return nullptr;
+ }
+ if (output.empty()) {
+ return nullptr;
+ }
+
+ // prepare a valid title for the symbol file
+ Glib::ustring title = Glib::Markup::escape_text(name);
+ // prepare a valid id prefix for symbols libvisio doesn't give us a name for
+ Glib::RefPtr<Glib::Regex> regex1 = Glib::Regex::create("[^a-zA-Z0-9_-]");
+ Glib::ustring id = regex1->replace(name, 0, "_", Glib::REGEX_MATCH_PARTIAL);
+
+ Glib::ustring tmpSVGOutput;
+ tmpSVGOutput += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
+ tmpSVGOutput += "<svg\n";
+ tmpSVGOutput += " xmlns=\"http://www.w3.org/2000/svg\"\n";
+ tmpSVGOutput += " xmlns:svg=\"http://www.w3.org/2000/svg\"\n";
+ tmpSVGOutput += " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n";
+ tmpSVGOutput += " version=\"1.1\"\n";
+ tmpSVGOutput += " style=\"fill:none;stroke:#000000;stroke-width:2\">\n";
+ tmpSVGOutput += " <title>";
+ tmpSVGOutput += title;
+ tmpSVGOutput += "</title>\n";
+ tmpSVGOutput += " <defs>\n";
+
+ // Each "symbol" is in its own SVG file, we wrap with <symbol> and merge into one file.
+ for (unsigned i=0; i<output.size(); ++i) {
+
+ std::stringstream ss;
+ if (titles.size() == output.size() && titles[i] != "") {
+ // TODO: Do we need to check for duplicated titles?
+ ss << regex1->replace(titles[i].cstr(), 0, "_", Glib::REGEX_MATCH_PARTIAL);
+ } else {
+ ss << id << "_" << i;
+ }
+
+ tmpSVGOutput += " <symbol id=\"" + ss.str() + "\">\n";
+
+ if (titles.size() == output.size() && titles[i] != "") {
+ tmpSVGOutput += " <title>" + Glib::ustring(RVNGString::escapeXML(titles[i].cstr()).cstr()) + "</title>\n";
+ }
+
+ std::istringstream iss( output[i].cstr() );
+ std::string line;
+ while( std::getline( iss, line ) ) {
+ if( line.find( "svg:svg" ) == std::string::npos ) {
+ tmpSVGOutput += " " + line + "\n";
+ }
+ }
+
+ tmpSVGOutput += " </symbol>\n";
+ }
+
+ tmpSVGOutput += " </defs>\n";
+ tmpSVGOutput += "</svg>\n";
+ return SPDocument::createNewDocFromMem( tmpSVGOutput.c_str(), strlen( tmpSVGOutput.c_str()), false );
+
+}
+#endif
+
+/* Hunts preference directories for symbol files */
+void SymbolsDialog::getSymbolsTitle() {
+
+ using namespace Inkscape::IO::Resource;
+ Glib::ustring title;
+ number_docs = 0;
+ std::regex matchtitle (".*?<title.*?>(.*?)<(/| /)");
+ for(auto &filename: get_filenames(SYMBOLS, {".svg", ".vss"})) {
+ if(Glib::str_has_suffix(filename, ".vss")) {
+ std::size_t found = filename.find_last_of("/\\");
+ filename = filename.substr(found+1);
+ title = filename.erase(filename.rfind('.'));
+ if(title.empty()) {
+ title = _("Unnamed Symbols");
+ }
+ symbol_sets[title]= nullptr;
+ ++number_docs;
+ } else {
+ std::ifstream infile(filename);
+ std::string line;
+ while (std::getline(infile, line)) {
+ std::string title_res = std::regex_replace (line, matchtitle,"$1",std::regex_constants::format_no_copy);
+ if (!title_res.empty()) {
+ title_res = g_dpgettext2(nullptr, "Symbol", title_res.c_str());
+ symbol_sets[ellipsize(Glib::ustring(title_res), 33)]= nullptr;
+ ++number_docs;
+ break;
+ }
+ std::string::size_type position_exit = line.find ("<defs");
+ if (position_exit != std::string::npos) {
+ std::size_t found = filename.find_last_of("/\\");
+ filename = filename.substr(found+1);
+ title = filename.erase(filename.rfind('.'));
+ if(title.empty()) {
+ title = _("Unnamed Symbols");
+ }
+ symbol_sets[title]= nullptr;
+ ++number_docs;
+ break;
+ }
+ }
+ }
+ }
+ for(auto const &symbol_document_map : symbol_sets) {
+ symbol_set->append(symbol_document_map.first);
+ }
+}
+
+/* Hunts preference directories for symbol files */
+std::pair<Glib::ustring, SPDocument*>
+SymbolsDialog::getSymbolsSet(Glib::ustring title)
+{
+ SPDocument* symbol_doc = nullptr;
+ Glib::ustring current = symbol_set->get_active_text();
+ if (current == CURRENTDOC) {
+ return std::make_pair(CURRENTDOC, symbol_doc);
+ }
+ if (symbol_sets[title]) {
+ sensitive = false;
+ symbol_set->remove_all();
+ symbol_set->append(CURRENTDOC);
+ symbol_set->append(ALLDOCS);
+ for(auto const &symbol_document_map : symbol_sets) {
+ if (CURRENTDOC != symbol_document_map.first) {
+ symbol_set->append(symbol_document_map.first);
+ }
+ }
+ symbol_set->set_active_text(title);
+ sensitive = true;
+ return std::make_pair(title, symbol_sets[title]);
+ }
+ using namespace Inkscape::IO::Resource;
+ Glib::ustring new_title;
+
+ std::regex matchtitle (".*?<title.*?>(.*?)<(/| /)");
+ for(auto &filename: get_filenames(SYMBOLS, {".svg", ".vss"})) {
+ if(Glib::str_has_suffix(filename, ".vss")) {
+#ifdef WITH_LIBVISIO
+ std::size_t pos = filename.find_last_of("/\\");
+ Glib::ustring filename_short = "";
+ if (pos != std::string::npos) {
+ filename_short = filename.substr(pos+1);
+ }
+ if (filename_short == title + ".vss") {
+ new_title = title;
+ symbol_doc = read_vss(Glib::ustring(filename), title);
+ }
+#endif
+ } else {
+ std::ifstream infile(filename);
+ std::string line;
+ while (std::getline(infile, line)) {
+ std::string title_res = std::regex_replace (line, matchtitle,"$1",std::regex_constants::format_no_copy);
+ if (!title_res.empty()) {
+ title_res = g_dpgettext2(nullptr, "Symbol", title_res.c_str());
+ new_title = ellipsize(Glib::ustring(title_res), 33);
+ }
+ std::size_t pos = filename.find_last_of("/\\");
+ Glib::ustring filename_short = "";
+ if (pos != std::string::npos) {
+ filename_short = filename.substr(pos+1);
+ }
+ if (title == new_title || filename_short == title + ".svg") {
+ new_title = title;
+ if(Glib::str_has_suffix(filename, ".svg")) {
+ symbol_doc = SPDocument::createNewDoc(filename.c_str(), FALSE);
+ }
+ }
+ if (symbol_doc) {
+ break;
+ }
+ std::string::size_type position_exit = line.find ("<defs");
+ if (position_exit != std::string::npos) {
+ break;
+ }
+ }
+ }
+ if (symbol_doc) {
+ break;
+ }
+ }
+ if(symbol_doc) {
+ symbol_sets.erase(title);
+ symbol_sets[new_title] = symbol_doc;
+ sensitive = false;
+ symbol_set->remove_all();
+ symbol_set->append(CURRENTDOC);
+ symbol_set->append(ALLDOCS);
+ for(auto const &symbol_document_map : symbol_sets) {
+ if (CURRENTDOC != symbol_document_map.first) {
+ symbol_set->append(symbol_document_map.first);
+ }
+ }
+ symbol_set->set_active_text(new_title);
+ sensitive = true;
+ }
+ return std::make_pair(new_title, symbol_doc);
+}
+
+void SymbolsDialog::symbolsInDocRecursive (SPObject *r, std::map<Glib::ustring, std::pair<Glib::ustring, SPSymbol*> > &l, Glib::ustring doc_title)
+{
+ if(!r) return;
+
+ // Stop multiple counting of same symbol
+ if ( dynamic_cast<SPUse *>(r) ) {
+ return;
+ }
+
+ if ( dynamic_cast<SPSymbol *>(r)) {
+ Glib::ustring id = r->getAttribute("id");
+ gchar * title = r->title();
+ if(title) {
+ l[doc_title + title + id] = std::make_pair(doc_title,dynamic_cast<SPSymbol *>(r));
+ } else {
+ l[Glib::ustring(_("notitle_")) + id] = std::make_pair(doc_title,dynamic_cast<SPSymbol *>(r));
+ }
+ g_free(title);
+ }
+ for (auto& child: r->children) {
+ symbolsInDocRecursive(&child, l, doc_title);
+ }
+}
+
+std::map<Glib::ustring, std::pair<Glib::ustring, SPSymbol*> >
+SymbolsDialog::symbolsInDoc( SPDocument* symbol_document, Glib::ustring doc_title)
+{
+
+ std::map<Glib::ustring, std::pair<Glib::ustring, SPSymbol*> > l;
+ if (symbol_document) {
+ symbolsInDocRecursive (symbol_document->getRoot(), l , doc_title);
+ }
+ return l;
+}
+
+void SymbolsDialog::useInDoc (SPObject *r, std::vector<SPUse*> &l)
+{
+
+ if ( dynamic_cast<SPUse *>(r) ) {
+ l.push_back(dynamic_cast<SPUse *>(r));
+ }
+
+ for (auto& child: r->children) {
+ useInDoc( &child, l );
+ }
+}
+
+std::vector<SPUse*> SymbolsDialog::useInDoc( SPDocument* useDocument) {
+ std::vector<SPUse*> l;
+ useInDoc (useDocument->getRoot(), l);
+ return l;
+}
+
+// Returns style from first <use> 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<SPUse*> l = useInDoc( document );
+ for( auto use:l ) {
+ if ( use ) {
+ gchar const *href = use->getRepr()->attribute("xlink:href");
+ 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) {
+ enableWidgets(false);
+ search_str = "";
+ store->clear();
+ SPDocument* symbol_document = selectedSymbols();
+ if (symbol_document) {
+ //We are not in search all docs
+ icons_found = false;
+ addSymbolsInDoc(symbol_document);
+ } else {
+ showOverlay();
+ enableWidgets(true);
+ }
+ }
+}
+
+void SymbolsDialog::enableWidgets(bool enable)
+{
+ symbol_set->set_sensitive(enable);
+ search->set_sensitive(enable);
+ tools ->set_sensitive(enable);
+}
+
+void SymbolsDialog::beforeSearch(GdkEventKey* evt)
+{
+ sensitive = false;
+ search_str = search->get_text().lowercase();
+ if (evt->keyval != GDK_KEY_Return) {
+ return;
+ }
+ searchsymbols();
+}
+
+void SymbolsDialog::searchsymbols()
+{
+ progress_bar->set_fraction(0.0);
+ enableWidgets(false);
+ SPDocument *symbol_document = selectedSymbols();
+ if (symbol_document) {
+ // We are not in search all docs
+ search->set_text(_("Searching..."));
+ store->clear();
+ icons_found = false;
+ addSymbolsInDoc(symbol_document);
+ } else {
+ idleconn.disconnect();
+ idleconn = Glib::signal_idle().connect(sigc::mem_fun(*this, &SymbolsDialog::callbackAllSymbols));
+ search->set_text(_("Loading all symbols..."));
+ }
+}
+
+void SymbolsDialog::unsensitive(GdkEventKey* evt)
+{
+ sensitive = true;
+}
+
+bool SymbolsDialog::callbackSymbols(){
+ 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);
+ 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;
+ }
+
+ progress_bar->set_fraction(((100.0/number_symbols) * counter_symbols)/100.0);
+ symbol_data = l.erase(l.begin());
+ //to get more items and best performance
+ int modulus = number_symbols > 200 ? 50 : (number_symbols/4);
+ g_free(symbol_title_char);
+ g_free(symbol_desc_char);
+ if (modulus && counter_symbols % modulus == 0 && !l.empty()) {
+ return true;
+ }
+ }
+ if (!icons_found && !search_str.empty()) {
+ showOverlay();
+ } else {
+ hideOverlay();
+ }
+ progress_bar->set_fraction(0);
+ sensitive = false;
+ search->set_text(search_str);
+ sensitive = true;
+ enableWidgets(true);
+ return false;
+ }
+ return true;
+}
+
+bool SymbolsDialog::callbackAllSymbols(){
+ Glib::ustring current = symbol_set->get_active_text();
+ if (current == ALLDOCS && search->get_text() == _("Loading all symbols...")) {
+ size_t counter = 0;
+ std::map<Glib::ustring, SPDocument*> symbol_sets_tmp = symbol_sets;
+ for(auto const &symbol_document_map : symbol_sets_tmp) {
+ ++counter;
+ SPDocument* symbol_document = symbol_document_map.second;
+ if (symbol_document) {
+ continue;
+ }
+ symbol_document = getSymbolsSet(symbol_document_map.first).second;
+ symbol_set->set_active_text(ALLDOCS);
+ if (!symbol_document) {
+ continue;
+ }
+ progress_bar->set_fraction(((100.0/number_docs) * counter)/100.0);
+ return true;
+ }
+ symbol_sets_tmp.clear();
+ hideOverlay();
+ all_docs_processed = true;
+ addSymbols();
+ progress_bar->set_fraction(0);
+ search->set_text("Searching...");
+ return false;
+ }
+ return true;
+}
+
+Glib::ustring SymbolsDialog::ellipsize(Glib::ustring data, size_t limit) {
+ if (data.length() > limit) {
+ data = data.substr(0, limit-3);
+ return data + "...";
+ }
+ return data;
+}
+
+void SymbolsDialog::addSymbolsInDoc(SPDocument* symbol_document) {
+
+ if (!symbol_document) {
+ return; //Search all
+ }
+ Glib::ustring doc_title = documentTitle(symbol_document);
+ progress_bar->set_fraction(0.0);
+ counter_symbols = 0;
+ l = symbolsInDoc(symbol_document, doc_title);
+ number_symbols = l.size();
+ if (!number_symbols) {
+ sensitive = false;
+ search->set_text(search_str);
+ sensitive = true;
+ enableWidgets(true);
+ idleconn.disconnect();
+ showOverlay();
+ } else {
+ idleconn.disconnect();
+ idleconn = Glib::signal_idle().connect( sigc::mem_fun(*this, &SymbolsDialog::callbackSymbols));
+ }
+}
+
+void SymbolsDialog::addSymbols() {
+ store->clear();
+ icons_found = false;
+ for(auto const &symbol_document_map : symbol_sets) {
+ SPDocument* symbol_document = symbol_document_map.second;
+ if (!symbol_document) {
+ continue;
+ }
+ Glib::ustring doc_title = documentTitle(symbol_document);
+ std::map<Glib::ustring, std::pair<Glib::ustring, SPSymbol*> > l_tmp = symbolsInDoc(symbol_document, doc_title);
+ for(auto &p : l_tmp ) {
+ l[p.first] = p.second;
+ }
+ l_tmp.clear();
+ }
+ counter_symbols = 0;
+ progress_bar->set_fraction(0.0);
+ number_symbols = l.size();
+ if (!number_symbols) {
+ showOverlay();
+ idleconn.disconnect();
+ sensitive = false;
+ search->set_text(search_str);
+ sensitive = true;
+ enableWidgets(true);
+ } else {
+ idleconn.disconnect();
+ idleconn = Glib::signal_idle().connect( sigc::mem_fun(*this, &SymbolsDialog::callbackSymbols));
+ }
+}
+
+void SymbolsDialog::addSymbol( SPObject* symbol, Glib::ustring doc_title)
+{
+ gchar const *id = symbol->getRepr()->attribute("id");
+
+ if (doc_title.empty()) {
+ doc_title = CURRENTDOC;
+ } else {
+ doc_title = g_dpgettext2(nullptr, "Symbol", doc_title.c_str());
+ }
+
+ Glib::ustring symbol_title;
+ gchar *title = symbol->title(); // From title element
+ if (title) {
+ symbol_title = Glib::ustring::compose("%1 (%2)", g_dpgettext2(nullptr, "Symbol", title), doc_title.c_str());
+ } else {
+ symbol_title = Glib::ustring::compose("%1 %2 (%3)", _("Symbol without title"), Glib::ustring(id), doc_title);
+ }
+ g_free(title);
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf = drawSymbol( symbol );
+ if( pixbuf ) {
+ Gtk::ListStore::iterator row = store->append();
+ SymbolColumns* columns = getColumns();
+ (*row)[columns->symbol_id] = Glib::ustring( id );
+ (*row)[columns->symbol_title] = Glib::Markup::escape_text(symbol_title);
+ (*row)[columns->symbol_doc_title] = Glib::Markup::escape_text(doc_title);
+ (*row)[columns->symbol_image] = pixbuf;
+ delete columns;
+ }
+}
+
+/*
+ * Returns image of symbol.
+ *
+ * Symbols normally are not visible. They must be referenced by a
+ * <use> element. A temporary document is created with a dummy
+ * <symbol> element and a <use> element that references the symbol
+ * element. Each real symbol is swapped in for the dummy symbol and
+ * the temporary document is rendered.
+ */
+Glib::RefPtr<Gdk::Pixbuf>
+SymbolsDialog::drawSymbol(SPObject *symbol)
+{
+ // Create a copy repr of the symbol with id="the_symbol"
+ Inkscape::XML::Document *xml_doc = preview_document->getReprDoc();
+ Inkscape::XML::Node *repr = symbol->getRepr()->duplicate(xml_doc);
+ repr->setAttribute("id", "the_symbol");
+
+ // Replace old "the_symbol" in preview_document by new.
+ Inkscape::XML::Node *root = preview_document->getReprRoot();
+ SPObject *symbol_old = preview_document->getObjectById("the_symbol");
+ if (symbol_old) {
+ symbol_old->deleteObject(false);
+ }
+
+ SPDocument::install_reference_document scoped(preview_document, getDocument());
+
+ // First look for default style stored in <symbol>
+ gchar const* style = repr->attribute("inkscape:symbol-style");
+ if(!style) {
+ // If no default style in <symbol>, 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");
+ }
+ }
+
+ // This is for display in Symbols dialog only
+ if( style ) repr->setAttribute( "style", style );
+
+ root->appendChild(repr);
+ Inkscape::GC::release(repr);
+
+ // 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->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ preview_document->ensureUpToDate();
+
+ // Make sure we have symbol in preview_document
+ SPObject *object_temp = preview_document->getObjectById( "the_use" );
+
+ SPItem *item = dynamic_cast<SPItem *>(object_temp);
+ g_assert(item != nullptr);
+ unsigned psize = SYMBOL_ICON_SIZES[pack_size];
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf(nullptr);
+ // 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
+
+ // Find object's bbox in document.
+ // Note symbols can have own viewport... ignore for now.
+ //Geom::OptRect dbox = item->geometricBounds();
+ Geom::OptRect dbox = item->documentVisualBounds();
+
+ if (dbox) {
+ /* Scale symbols to fit */
+ double scale = 1.0;
+ double width = dbox->width();
+ double height = dbox->height();
+
+ if( width == 0.0 ) width = 1.0;
+ if( height == 0.0 ) height = 1.0;
+
+ if( fit_symbol->get_active() )
+ scale = psize / ceil(std::max(width, height));
+ else
+ scale = pow( 2.0, scale_factor/2.0 ) * psize / 32.0;
+
+ pixbuf = Glib::wrap(render_pixbuf(renderDrawing, scale, *dbox, psize));
+ }
+
+ return pixbuf;
+}
+
+/*
+ * Return empty doc to render symbols in.
+ * Symbols are by default not rendered so a <use> element is
+ * provided.
+ */
+SPDocument* SymbolsDialog::symbolsPreviewDoc()
+{
+ // BUG: <symbol> must be inside <defs>
+ gchar const *buffer =
+"<svg xmlns=\"http://www.w3.org/2000/svg\""
+" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\""
+" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\""
+" xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
+" <defs id=\"defs\">"
+" <symbol id=\"the_symbol\"/>"
+" </defs>"
+" <use id=\"the_use\" xlink:href=\"#the_symbol\"/>"
+"</svg>";
+ return SPDocument::createNewDocFromMem( buffer, strlen(buffer), FALSE );
+}
+
+/*
+ * Update image widgets
+ */
+Glib::RefPtr<Gdk::Pixbuf>
+SymbolsDialog::getOverlay(gint width, gint height)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create (surface);
+ cairo_set_source_rgba(cr, 1, 1, 1, 0.75);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_fill (cr);
+ GdkPixbuf* pixbuf = ink_pixbuf_create_from_cairo_surface(surface);
+ cairo_destroy (cr);
+ return Glib::wrap(pixbuf);
+}
+
+} //namespace Dialogs
+} //namespace UI
+} //namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-basic-offset:2
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2:fileencoding=utf-8:textwidth=99 :