/** * @file * Symbols dialog. */ /* Authors: * Copyright (C) 2012 Tavmjong Bah * * Released under GNU GPL, read the file 'COPYING' for more information */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include "path-prefix.h" #include "io/sys.h" #include "io/resource.h" #include "ui/cache/svg_preview_cache.h" #include "ui/clipboard.h" #include "ui/icon-names.h" #include "symbols.h" #include "selection.h" #include "desktop.h" #include "document.h" #include "inkscape.h" #include "sp-root.h" #include "sp-use.h" #include "sp-defs.h" #include "sp-symbol.h" #ifdef WITH_LIBVISIO #include // TODO: Drop this check when librevenge is widespread. #if WITH_LIBVISIO01 #include using librevenge::RVNGFileStream; using librevenge::RVNGString; using librevenge::RVNGStringVector; using librevenge::RVNGPropertyList; using librevenge::RVNGSVGDrawingGenerator; #else #include typedef WPXFileStream RVNGFileStream; typedef libvisio::VSDStringVector RVNGStringVector; #endif #endif #include "verbs.h" #include "helper/action.h" #include 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 symbol_id; Gtk::TreeModelColumn symbol_title; Gtk::TreeModelColumn symbol_doc_title; Gtk::TreeModelColumn< Glib::RefPtr > 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 ) : UI::Widget::Panel("", prefsPath, SP_VERB_DIALOG_SYMBOLS), store(Gtk::ListStore::create(*getColumns())), icon_view(0), current_desktop(0), desk_track(), current_document(0), preview_document(0), instanceConns() { /******************** Table *************************/ table = new Gtk::Grid(); table->set_margin_left(3); table->set_margin_right(3); table->set_margin_top(4); // panel is a cloked Gtk::VBox _getContents()->pack_start(*Gtk::manage(table), Gtk::PACK_EXPAND_WIDGET); guint row = 0; /******************** Symbol Sets *************************/ Gtk::Label* labelSet = new Gtk::Label(_("Symbol set: ")); table->attach(*Gtk::manage(labelSet),0,row,1,1); symbol_set = new Gtk::ComboBoxText(); // Fill in later symbol_set->append(_("Current Document")); symbol_set->append(_("Search all sets")); symbol_set->set_active_text(_("Current Document")); symbol_set->set_hexpand(); table->attach(*Gtk::manage(symbol_set),1,row,1,1); sigc::connection connSet = symbol_set->signal_changed().connect( sigc::mem_fun(*this, &SymbolsDialog::rebuild)); instanceConns.push_back(connSet); ++row; /******************** Progress *******************************/ progress = new Gtk::HBox(); 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(8); progress->set_margin_bottom(8); ++row; /******************** Search *************************/ search = Gtk::manage(new Gtk::SearchEntry()); // Search search->set_tooltip_text(_("Return to start search.")); search->signal_key_press_event().connect_notify(sigc::mem_fun(*this, &SymbolsDialog::beforeAddSymbols)); search->set_margin_left(10); search->set_margin_right(10); search->signal_search_changed().connect(sigc::mem_fun(*this, &SymbolsDialog::clearSearch)); table->attach(*Gtk::manage(search),0,row,2,1); ++row; /********************* Icon View **************************/ SymbolColumns* columns = getColumns(); icon_view = new Gtk::IconView(static_cast >(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, 200 ); std::vector< Gtk::TargetEntry > targets; targets.push_back(Gtk::TargetEntry( "application/x-inkscape-paste")); icon_view->enable_model_drag_source (targets, Gdk::BUTTON1_MASK, Gdk::ACTION_COPY); icon_view->signal_drag_data_get().connect( sigc::mem_fun(*this, &SymbolsDialog::iconDragDataGet)); sigc::connection connIconChanged; connIconChanged = icon_view->signal_selection_changed().connect( sigc::mem_fun(*this, &SymbolsDialog::iconChanged)); instanceConns.push_back(connIconChanged); scroller = new Gtk::ScrolledWindow(); scroller->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS); scroller->add(*Gtk::manage(icon_view)); scroller->set_hexpand(); scroller->set_vexpand(); table->attach(*Gtk::manage(scroller),0,row,2,1); ++row; /******************** Tools *******************************/ Gtk::Button* button; Gtk::HBox* tools = new Gtk::HBox(); //tools->set_layout( Gtk::BUTTONBOX_END ); scroller->set_hexpand(); table->attach(*Gtk::manage(tools),0,row,2,1); auto add_symbol_image = Gtk::manage(new Gtk::Image()); add_symbol_image->set_from_icon_name("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_activate().connect(sigc::mem_fun(*this, &SymbolsDialog::insertSymbol)); tools->pack_start(* add_symbol, Gtk::PACK_SHRINK); auto remove_symbolImage = Gtk::manage(new Gtk::Image()); remove_symbolImage->set_from_icon_name("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(new Gtk::Image()); packMoreImage->set_from_icon_name("pack-more", Gtk::ICON_SIZE_SMALL_TOOLBAR); button = Gtk::manage(new Gtk::Button()); button->add(*packMoreImage); button->set_tooltip_text(_("Display more icons in row.")); button->set_relief( Gtk::RELIEF_NONE ); button->set_focus_on_click( false ); button->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::packmore)); tools->pack_start(* button, Gtk::PACK_SHRINK); auto packLessImage = Gtk::manage(new Gtk::Image()); packLessImage->set_from_icon_name("pack-less", Gtk::ICON_SIZE_SMALL_TOOLBAR); button = Gtk::manage(new Gtk::Button()); button->add(*packLessImage); button->set_tooltip_text(_("Display fewer icons in row.")); button->set_relief( Gtk::RELIEF_NONE ); button->set_focus_on_click( false ); button->signal_clicked().connect(sigc::mem_fun(*this, &SymbolsDialog::packless)); tools->pack_start(* button, Gtk::PACK_SHRINK); // Toggle scale to fit on/off auto fit_symbolImage = Gtk::manage(new Gtk::Image()); fit_symbolImage->set_from_icon_name("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(new Gtk::Image()); zoom_outImage->set_from_icon_name("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(new Gtk::Image()); zoom_inImage->set_from_icon_name("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; processed = false; search_str = ""; current_desktop = SP_ACTIVE_DESKTOP; current_document = current_desktop->getDocument(); 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 )); // This might need to be a global variable so setTargetDesktop can modify it SPDefs *defs = current_document->getDefs(); sigc::connection defsModifiedConn = defs->connectModified(sigc::mem_fun(*this, &SymbolsDialog::defsModified)); instanceConns.push_back(defsModifiedConn); sigc::connection selectionChangedConn = current_desktop->selection->connectChanged( sigc::mem_fun(*this, &SymbolsDialog::selectionChanged)); instanceConns.push_back(selectionChangedConn); sigc::connection documentReplacedConn = current_desktop->connectDocumentReplaced( sigc::mem_fun(*this, &SymbolsDialog::documentReplaced)); instanceConns.push_back(documentReplacedConn); getSymbolsFilename(); addSymbolsInDoc(current_document); /* Defaults to current document */ sigc::connection desktopChangeConn = desk_track.connectDesktopChanged( sigc::mem_fun(*this, &SymbolsDialog::setTargetDesktop) ); instanceConns.push_back( desktopChangeConn ); desk_track.connect(GTK_WIDGET(gobj())); } SymbolsDialog::~SymbolsDialog() { for (std::vector::iterator it = instanceConns.begin(); it != instanceConns.end(); ++it) { it->disconnect(); } instanceConns.clear(); desk_track.disconnect(); } 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(true); if( !symbol_document ) { // Symbol must be from Current Document (this method of // checking should be language independent). symbol_document = current_document; add_symbol->set_sensitive( true ); remove_symbol->set_sensitive( true ); } else { add_symbol->set_sensitive( false ); remove_symbol->set_sensitive( false ); } if (symbol_set->get_active_text() == _("Search all sets")) { store->clear(); if (search_str != "" && processed) { addSymbols(); } } else { search->set_text(""); addSymbolsInDoc(symbol_document); } } void SymbolsDialog::insertSymbol() { Inkscape::Verb *verb = Inkscape::Verb::get( SP_VERB_EDIT_SYMBOL ); SPAction *action = verb->get_action(Inkscape::ActionContext( (Inkscape::UI::View::View *) this->current_desktop) ); sp_action_perform (action, NULL); } void SymbolsDialog::revertSymbol() { Inkscape::Verb *verb = Inkscape::Verb::get( SP_VERB_EDIT_UNSYMBOL ); SPAction *action = verb->get_action(Inkscape::ActionContext( (Inkscape::UI::View::View *) this->current_desktop ) ); sp_action_perform (action, NULL); } void SymbolsDialog::iconDragDataGet(const Glib::RefPtr& /*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*/) { if ( !symbol_sets[symbol_set->get_active_text()] ) { rebuild(); } } void SymbolsDialog::selectionChanged(Inkscape::Selection *selection) { Glib::ustring symbol_id = selectedSymbolId(); SPDocument* symbol_document = selectedSymbols(); SPObject* symbol = symbol_document->getObjectById(symbol_id); if(symbol && !selection->includes(symbol)) { icon_view->unselect_all(); } } void SymbolsDialog::documentReplaced(SPDesktop *desktop, SPDocument *document) { current_desktop = desktop; current_document = document; rebuild(); } SPDocument* SymbolsDialog::selectedSymbols(bool ignorecurrent) { /* OK, we know symbol name... now we need to copy it to clipboard, bon chance! */ Glib::ustring doc_title = selectedSymbolDocTitle(); if (doc_title.empty()) { doc_title = symbol_set->get_active_text(); } 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 ) { if (ignorecurrent) { return NULL; } return current_document; } } 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) { Glib::ustring title = ""; if (symbol_doc) { SPRoot * root = symbol_doc->getRoot(); if (root->title()) { title = Glib::ustring(root->title()); } return title; } return title; } void SymbolsDialog::iconChanged() { Glib::ustring symbol_id = selectedSymbolId(); SPDocument* symbol_document = selectedSymbols(); SPObject* symbol = symbol_document->getObjectById(symbol_id); if( symbol ) { // Find style for use in // First look for default style stored in gchar const* style = symbol->getAttribute("inkscape:symbol-style"); if( !style ) { // If no default style in , look in documents. if( symbol_document == current_document ) { style = styleFromUse( symbol_id.c_str(), current_document ); } else { style = symbol_document->getReprRoot()->attribute("style"); } } ClipboardManager *cm = ClipboardManager::get(); cm->copySymbol(symbol->getRepr(), style, symbol_document == current_document); } } #ifdef WITH_LIBVISIO #if WITH_LIBVISIO01 // 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) { RVNGSVGDrawingGenerator::startPage(propList); if (propList["draw:name"]) { _titles.append(propList["draw:name"]->getStr()); } else { _titles.append(""); } } private: RVNGStringVector &_titles; }; #endif // 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 filename.copy(fullname, filename.length()); #endif RVNGFileStream input(fullname); g_free(fullname); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } RVNGStringVector output; RVNGStringVector titles; #if WITH_LIBVISIO01 RVNGSVGDrawingGenerator_WithTitle generator(output, titles, "svg"); if (!libvisio::VisioDocument::parseStencils(&input, &generator)) { #else if (!libvisio::VisioDocument::generateSVGStencils(&input, output)) { #endif return NULL; } if (output.empty()) { return NULL; } // 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 regex1 = Glib::Regex::create("[^a-zA-Z0-9_-]"); Glib::ustring id = regex1->replace(name, 0, "_", Glib::REGEX_MATCH_PARTIAL); Glib::ustring tmpSVGOutput; tmpSVGOutput += "\n"; tmpSVGOutput += "\n"; tmpSVGOutput += " "; tmpSVGOutput += title; tmpSVGOutput += "\n"; tmpSVGOutput += " \n"; // Each "symbol" is in its own SVG file, we wrap with and merge into one file. for (unsigned i=0; ireplace(titles[i].cstr(), 0, "_", Glib::REGEX_MATCH_PARTIAL); } else { ss << id << "_" << i; } tmpSVGOutput += " \n"; #if WITH_LIBVISIO01 if (titles.size() == output.size() && titles[i] != "") { tmpSVGOutput += " " + Glib::ustring(RVNGString::escapeXML(titles[i].cstr()).cstr()) + "\n"; } #endif 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 += " \n"; } tmpSVGOutput += " \n"; tmpSVGOutput += "\n"; return SPDocument::createNewDocFromMem( tmpSVGOutput.c_str(), strlen( tmpSVGOutput.c_str()), 0 ); } #endif /* Hunts preference directories for symbol files */ void SymbolsDialog::getSymbolsFilename() { using namespace Inkscape::IO::Resource; Glib::ustring title; number_docs = 0; for(auto &filename: get_filenames(SYMBOLS, {".svg", ".vss"})) { if(Glib::str_has_suffix(filename, ".svg")) { //TODO: find a way to get real title without loading all SPDocument 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]= NULL; symbol_set->append(title); ++number_docs; } #ifdef WITH_LIBVISIO 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]= NULL; symbol_set->append(title); ++number_docs; } #endif } } /* Hunts preference directories for symbol files */ std::pair SymbolsDialog::getSymbolsSet(Glib::ustring title) { if (symbol_sets[title]) { return std::make_pair(title, symbol_sets[title]); } using namespace Inkscape::IO::Resource; SPDocument* symbol_doc = NULL; Glib::ustring new_title; size_t i = 0; for(auto &filename: get_filenames(SYMBOLS, {".svg", ".vss"})) { std::size_t pos = filename.find_last_of("/\\"); if (pos != std::string::npos) { Glib::ustring filename_short = filename.substr(pos+1); if(filename_short == title + ".svg") { symbol_doc = SPDocument::createNewDoc(filename.c_str(), FALSE); if(symbol_doc) { new_title = symbol_doc->getRoot()->title(); if(new_title.empty()) { new_title = _("Unnamed Symbols"); } } } if(Glib::str_has_suffix(filename, ".svg")) { i++; } #ifdef WITH_LIBVISIO if(filename_short == title + ".vss") { symbol_doc = read_vss(filename, title); if(symbol_doc) { new_title = symbol_doc->getRoot()->title(); if(new_title.empty()) { new_title = _("Unnamed Symbols"); } } } if(Glib::str_has_suffix(filename, ".vss")) { i++; } #endif if(symbol_doc) { symbol_sets.erase(title); symbol_sets[new_title]= symbol_doc; sensitive = false; symbol_set->remove_text(i+1); symbol_set->insert (i+1, new_title); symbol_set->set_active(i+1); symbol_set->set_active_text(new_title); sensitive = true; break; } } } return std::make_pair(new_title, symbol_doc); } void SymbolsDialog::symbolsInDocRecursive (SPObject *r, std::vector &l) { if(!r) return; // Stop multiple counting of same symbol if ( dynamic_cast(r) ) { return; } if ( dynamic_cast(r) ) { l.push_back(dynamic_cast(r)); } for (auto& child: r->children) { symbolsInDocRecursive( &child, l ); } } std::vector SymbolsDialog::symbolsInDoc( SPDocument* symbol_document) { std::vector l; symbolsInDocRecursive (symbol_document->getRoot(), l ); return l; } void SymbolsDialog::useInDoc (SPObject *r, std::vector &l) { if ( dynamic_cast(r) ) { l.push_back(dynamic_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 = 0; std::vector 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()) { search_str = ""; store->clear(); Glib::ustring current = symbol_set->get_active_text(); if (current != _("Search all sets")) { addSymbolsInDoc(symbol_sets[current]); } } } void SymbolsDialog::beforeAddSymbols(GdkEventKey* evt) { search_str = search->get_text().lowercase(); if (evt->keyval != GDK_KEY_Return) { return; } progress_bar->set_fraction(0.0); search->set_text(_("Searching...")); search->set_sensitive(false); Glib::ustring current = symbol_set->get_active_text(); if (current != _("Search all sets")) { bool icons_found = false; Glib::ustring doc_title = symbol_set->get_active_text(); if (!doc_title.empty()) { SPDocument* symbol_document = symbol_sets[doc_title]; if(symbol_document) { store->clear(); icons_found = addSymbolsInDoc(symbol_document); } } if (!icons_found) { //show overlay } search->set_text(search_str); search->set_sensitive(true); } else if(processed) { Glib::signal_idle().connect( sigc::mem_fun(*this, &SymbolsDialog::callbackGetSymbols) ); addSymbols(); } else { Glib::signal_idle().connect( sigc::mem_fun(*this, &SymbolsDialog::callbackGetSymbolsSets) ); } } bool SymbolsDialog::callbackGetSymbolsSets(){ Glib::ustring current = symbol_set->get_active_text(); if (search->get_text() != _("Searching...") || current != _("Search all sets")) { return true; } size_t counter = 0; for(auto const &symbol_document_map : symbol_sets) { ++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(_("Search all sets")); if (!symbol_document) { continue; } progress_bar->set_fraction(((100.0/number_docs) * counter)/100.0); return true; } addSymbols(); processed = true; progress_bar->set_fraction(1.0); return false; } bool SymbolsDialog::callbackGetSymbols() { if (loading && processed) { progress_bar->pulse(); return true; } return false; } bool SymbolsDialog::addSymbolsInDoc(SPDocument* symbol_document) { bool icons_found = false; if (!symbol_document) { return false; //Search all } Glib::ustring doc_title = documentTitle(symbol_document); if (doc_title.empty()) { return false; } std::vector l = symbolsInDoc(symbol_document); loading = false; for(auto symbol:l) { gchar const *symbol_title_char = symbol->title(); gchar const *symbol_desc_char = symbol->description(); if (symbol_title_char) { bool found = false; Glib::ustring symbol_title = Glib::ustring(symbol_title_char).lowercase(); auto pos = symbol_title.rfind(search_str); if (pos != 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); if (pos != std::string::npos) { found = true; } } if (symbol && (search_str.empty() || found)) { addSymbol( symbol, doc_title); icons_found = true; } } } loading = false; return icons_found; } void SymbolsDialog::addSymbols() { bool icons_found = false; store->clear(); for(auto const &symbol_document_map : symbol_sets) { SPDocument* symbol_document = symbol_document_map.second; if (!symbol_document) { continue; } if(addSymbolsInDoc(symbol_document) && !icons_found) { icons_found = true; } } if (!icons_found) { //Show overlay } search->set_text(search_str); search->set_sensitive(true); } void SymbolsDialog::addSymbol( SPObject* symbol, Glib::ustring doc_title) { SymbolColumns* columns = getColumns(); gchar const *id = symbol->getRepr()->attribute("id"); gchar const *title = symbol->title(); // From title element if( !title ) { title = id; } Glib::ustring symbol_title = Glib::Markup::escape_text(Glib::ustring( g_dpgettext2(NULL, "Symbol", title) )); if (doc_title.empty()) { symbol_title = symbol_title + Glib::Markup::escape_text(Glib::ustring(g_dpgettext2(NULL, "Symbol", (Glib::ustring(" (") +_("Current Document") + Glib::ustring(")")).c_str()))); } else { symbol_title = symbol_title + Glib::Markup::escape_text(Glib::ustring(g_dpgettext2(NULL, "Symbol", (Glib::ustring(" (") + doc_title + Glib::ustring(")")).c_str()))); } Glib::RefPtr pixbuf = drawSymbol( symbol ); if( pixbuf ) { Gtk::ListStore::iterator row = store->append(); (*row)[columns->symbol_id] = Glib::ustring( id ); (*row)[columns->symbol_title] = 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 * element. A temporary document is created with a dummy * element and a element that references the symbol * element. Each real symbol is swapped in for the dummy symbol and * the temporary document is rendered. */ Glib::RefPtr 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); } // 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 == current_document ) { gchar const *id = symbol->getRepr()->attribute("id"); style = styleFromUse( id, symbol->document ); } else { style = symbol->document->getReprRoot()->attribute("style"); } } // Last ditch effort to provide some default styling if( !style ) style = "fill:#bbbbbb;stroke:#808080"; // This is for display in Symbols dialog only if( style ) repr->setAttribute( "style", style ); // BUG: Symbols don't work if defined outside of . Causes Inkscape // crash when trying to read in such a file. root->appendChild(repr); //defsrepr->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" ); preview_document->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); preview_document->ensureUpToDate(); SPItem *item = dynamic_cast(object_temp); g_assert(item != NULL); unsigned psize = SYMBOL_ICON_SIZES[pack_size]; Glib::RefPtr pixbuf(NULL); // 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 / 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 element is * provided. */ SPDocument* SymbolsDialog::symbolsPreviewDoc() { // BUG: must be inside gchar const *buffer = "" " " " " " " " " ""; return SPDocument::createNewDocFromMem( buffer, strlen(buffer), FALSE ); } void SymbolsDialog::setTargetDesktop(SPDesktop *desktop) { if (this->current_desktop != desktop) { this->current_desktop = desktop; if( !symbol_sets[symbol_set->get_active_text()] ) { // Symbol set is from Current document, update rebuild(); } } } } //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 :