From 1602477dfd4371a17639b0b6e4f59f5a5ea9565a Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Fri, 15 Mar 2019 15:21:30 +0100 Subject: Make InkscapeApplication responsible for managing documents and windows. Any change from previous behavior is a bug. --- src/document.cpp | 25 +- src/document.h | 1 + src/extension/dbus/document-interface.cpp | 15 +- src/file-update.cpp | 45 +-- src/file.cpp | 155 ++------- src/file.h | 11 - src/help.cpp | 7 +- src/inkscape-application.cpp | 508 +++++++++++++++++++++++++----- src/inkscape-application.h | 33 +- src/inkscape-window.cpp | 7 +- src/io/file.cpp | 105 +++--- src/io/file.h | 2 +- src/io/resource-manager.cpp | 7 +- src/ui/desktop/menubar.cpp | 9 +- src/ui/interface.cpp | 53 ++-- src/ui/view/view.cpp | 4 +- 16 files changed, 632 insertions(+), 355 deletions(-) (limited to 'src') diff --git a/src/document.cpp b/src/document.cpp index a6ad44c9d..1fc32d8a0 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -216,7 +216,6 @@ SPDocument::~SPDocument() { // This is at the end of the destructor, because preceding code adds new orphans to the queue collectOrphans(); - } sigc::connection SPDocument::connectDestroy(sigc::signal::slot_type slot) @@ -460,6 +459,10 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, )); document->oldSignalsConnected = true; + + // ************* Fix Document ************** + // Move to separate function? + /** Fix baseline spacing (pre-92 files) **/ if ( (!sp_no_convert_text_baseline_spacing) && sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) { @@ -471,11 +474,17 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, sp_file_convert_font_name(document); } - /** Fix dpi (pre-92 files) **/ + /** Fix dpi (pre-92 files). With GUI fixed in Inkscape::Application::fix_document. **/ if ( !(INKSCAPE.use_gui()) && sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) { sp_file_convert_dpi(document); } + // Update LPE's See: Related bug:#1769679 #18 + SPDefs * defs = document->getDefs(); + if (defs) { + defs->emitModified(SP_OBJECT_MODIFIED_CASCADE); + } + return document; } @@ -513,7 +522,6 @@ SPDocument *SPDocument::createChildDoc(std::string const &document_uri) } else { path = document_uri; } - std::cout << "Added document_base: '" << path << std::endl; document = createNewDoc(path.c_str(), false, false, this); } return document; @@ -782,6 +790,17 @@ Geom::Rect SPDocument::getViewBox() const return viewBox; } +/** + * Set default viewbox calculated from document properties. + */ +void SPDocument::setViewBox() +{ + setViewBox(Geom::Rect(0, + 0, + getWidth().value(getDisplayUnit()), + getHeight().value(getDisplayUnit()))); +} + void SPDocument::setViewBox(const Geom::Rect &viewBox) { root->viewBox_set = true; diff --git a/src/document.h b/src/document.h index 2c8a8f5ce..b3fa7da4d 100644 --- a/src/document.h +++ b/src/document.h @@ -284,6 +284,7 @@ public: void setWidthAndHeight(const Inkscape::Util::Quantity &width, const Inkscape::Util::Quantity &height, bool changeSize=true); void setWidth(const Inkscape::Util::Quantity &width, bool changeSize=true); void setHeight(const Inkscape::Util::Quantity &height, bool changeSize=true); + void setViewBox(); void setViewBox(const Geom::Rect &viewBox); void requestModified(); int ensureUpToDate(); diff --git a/src/extension/dbus/document-interface.cpp b/src/extension/dbus/document-interface.cpp index bb888d4e2..a22585cb4 100644 --- a/src/extension/dbus/document-interface.cpp +++ b/src/extension/dbus/document-interface.cpp @@ -19,10 +19,10 @@ #include #include -#include #include //#include "2geom/svg-path-parser.h" //get_node_coordinates +#include "inkscape-application.h" // create_window() #include "application-interface.h" #include "desktop-style.h" //sp_desktop_get_style @@ -930,12 +930,21 @@ document_interface_save (DocumentInterface *doc_interface, GError **error) gboolean document_interface_load(DocumentInterface *doc_interface, gchar *filename, GError ** /*error*/) { + if (!filename) { + return false; + } + SPDesktop *desk = doc_interface->target.getDesktop(); if (desk) { desktop_ensure_active(desk); } - const Glib::ustring file(filename); - sp_file_open(file, NULL, TRUE, TRUE); + + Glib::RefPtr file = Gio::File::create_for_path(filename); + + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + + app->create_window(file); + if (doc_interface->updates) { Inkscape::DocumentUndo::done(doc_interface->target.getDocument(), SP_VERB_FILE_OPEN, "Opened File"); } diff --git a/src/file-update.cpp b/src/file-update.cpp index f35107e2d..5489ee499 100644 --- a/src/file-update.cpp +++ b/src/file-update.cpp @@ -492,51 +492,8 @@ void sp_file_convert_dpi(SPDocument *doc) prefs->setBool("/options/transform/gradient", transform_gradient); did_scaling = true; -/* - -// There used to be 2 "experimental" scaling methods in trunk. Considering that the method in .92.x appear to work, so I'm commenting those. - - // Save preferences - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); - bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); - - prefs->setBool("/options/kbselection/onlysensitive", false); - prefs->setBool("/options/kbselection/onlyvisible", false); - - Inkscape::Selection *selection = desktop->getSelection(); - Inkscape::SelectionHelper::selectAllInAll( desktop ); - -//method 1 ... - selection->group(); - SPItem * group = selection->singleItem(); - if (group) { - group->setAttribute("transform","scale(1.06666667,1.06666667)"); - } else { - std::cerr << "sp_file_open: Failed to get group!" << std::endl; - } - selection->clear(); - selection->add( group ); - selection->ungroup(); - -// OR method 2... - - double height = root->height.computed; - selection->setScaleRelative( Geom::Point(0,height), Geom::Scale(96.0/90.0,96.0/90.0) ); - -... end method 2 - - selection->clear(); - - prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); - prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); - - did_scaling = true; -*/ - - - } + } else { // FILE_DPI_UNCHANGED if (need_fix_units) need_fix_grid_mm = true; diff --git a/src/file.cpp b/src/file.cpp index b23ed2185..74bf9c162 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -35,6 +35,7 @@ #include #include "file.h" +#include "inkscape-application.h" #include "inkscape-window.h" #include "desktop.h" @@ -123,38 +124,24 @@ static void sp_file_add_recent(gchar const *uri) /** * Create a blank document and add it to the desktop + * Input: empty string or template file name. */ SPDesktop *sp_file_new(const std::string &templ) { - SPDocument *doc = SPDocument::createNewDoc( !templ.empty() ? templ.c_str() : nullptr , TRUE, true ); - g_return_val_if_fail(doc != nullptr, NULL); + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); - // Remove all the template info from xml tree - Inkscape::XML::Node *myRoot = doc->getReprRoot(); - Inkscape::XML::Node *nodeToRemove = sp_repr_lookup_name(myRoot, "inkscape:_templateinfo"); - if (nodeToRemove != nullptr){ - DocumentUndo::setUndoSensitive(doc, false); - sp_repr_unparent(nodeToRemove); - delete nodeToRemove; - DocumentUndo::setUndoSensitive(doc, true); + SPDocument* doc = app->document_new (templ); + if (!doc) { + std::cerr << "sp_file_new: failed to open document: " << templ << std::endl; } + InkscapeWindow* win = app->window_open (doc); - SPDesktop *olddesktop = SP_ACTIVE_DESKTOP; - if (olddesktop) - olddesktop->setWaitingCursor(); - - InkscapeWindow* win = new InkscapeWindow(doc); SPDesktop* desktop = win->get_desktop(); #ifdef WITH_DBUS Inkscape::Extension::Dbus::dbus_init_desktop_interface(desktop); #endif - if (olddesktop) - olddesktop->clearWaitingCursor(); - if (desktop) - desktop->clearWaitingCursor(); - return desktop; } @@ -194,105 +181,6 @@ sp_file_exit() -/*###################### -## O P E N -######################*/ - -/** - * Open a file, add the document to the desktop - * - * \param replace_empty if true, and the current desktop is empty, this document - * will replace the empty one. - */ -bool sp_file_open(const Glib::ustring &uri, - Inkscape::Extension::Extension *key, - bool add_to_recent, - bool replace_empty) -{ - if (!INKSCAPE.use_gui()) { - std::cerr << "sp_file_open requires GUI, use ink_file_open() instead." << std::endl; - return false; - } - - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop) { - desktop->setWaitingCursor(); - } - - bool cancelled = false; - Glib::RefPtr file = Gio::File::create_for_path(uri); - SPDocument* doc = ink_file_open(file, cancelled); - - if (desktop) { - desktop->clearWaitingCursor(); - } - - if (doc) { - - SPDocument *existing = desktop ? desktop->getDocument() : nullptr; - - if (existing && existing->virgin && replace_empty) { - - // If the current desktop is empty, open the document there - doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc. - desktop->change_document(doc); - doc->emitResizedSignal(doc->getWidth().value("px"), doc->getHeight().value("px")); - } else { - - InkscapeWindow* win = new InkscapeWindow(doc); - desktop = win->get_desktop(); - } - - doc->virgin = FALSE; - - if (add_to_recent) { - sp_file_add_recent( doc->getDocumentURI() ); - } - - // --------------- Fix up document ---------------- - - // Fix DPI (90->96) - if (sp_version_inside_range(doc->getRoot()->version.inkscape, 0, 1, 0, 92)) { - sp_file_convert_dpi(doc); - } - - // Perform a fixup pass for hrefs. - if ( Inkscape::ResourceManager::getManager().fixupBrokenLinks(doc) ) { - Glib::ustring msg = _("Broken links have been changed to point to existing files."); - desktop->showInfoDialog(msg); - } - - // Check for font substitutions - Inkscape::UI::Dialog::FontSubstitution::getInstance().checkFontSubstitutions(doc); - - // Update LPE's See: Related bug:#1769679 #18 - SPDefs * defs = dynamic_cast(doc->getDefs()); - if (defs && !existing) { - defs->emitModified(SP_OBJECT_MODIFIED_CASCADE); - } - - // ------------------ Window options --------------- - - // Lock Guides - SPNamedView *nv = desktop->namedview; - if (nv->lockguides) { - nv->lockGuides(); - } - - return true; - - } else if (!cancelled) { - gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str()); - gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri); - sp_ui_error_dialog(text); - g_free(text); - g_free(safeUri); - return false; - } - - return false; -} - /** * Handle prompting user for "do you want to revert"? Revert on "OK" */ @@ -322,22 +210,10 @@ void sp_file_revert_dialog() } } - bool reverted; + bool reverted = false; if (do_revert) { - // Allow overwriting of current document. - doc->virgin = TRUE; - - // remember current zoom and view - double zoom = desktop->current_zoom(); - Geom::Point c = desktop->get_display_area().midpoint(); - - reverted = sp_file_open(uri,nullptr); - if (reverted) { - // restore zoom and view - desktop->zoom_absolute_center_point(c, zoom); - } - } else { - reverted = false; + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + reverted = app->document_revert (doc); } if (reverted) { @@ -507,6 +383,8 @@ sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*d return; } + // FIXME: This is silly to have separate code paths for opening one vs many files! + //# User selected something. Get name and type Glib::ustring fileName = openDialogInstance->getFilename(); @@ -520,6 +398,8 @@ sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*d delete openDialogInstance; openDialogInstance = nullptr; + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + //# Iterate through filenames if more than 1 if (flist.size() > 1) { @@ -536,7 +416,9 @@ sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*d #ifdef INK_DUMP_FILENAME_CONV g_message("Opening File %s\n", fileName.c_str()); #endif - sp_file_open(fileName, fileType); + + Glib::RefPtr file = Gio::File::create_for_path(fileName); + app->create_window (file); } return; @@ -556,7 +438,8 @@ sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*d open_path.append(G_DIR_SEPARATOR_S); prefs->setString("/dialogs/open/path", open_path); - sp_file_open(fileName, fileType); + Glib::RefPtr file = Gio::File::create_for_path(fileName); + app->create_window (file); } return; diff --git a/src/file.h b/src/file.h index 183233a1b..fc0925b78 100644 --- a/src/file.h +++ b/src/file.h @@ -61,17 +61,6 @@ void sp_file_exit (); ## O P E N ######################*/ -/** - * Opens a new file and window from the given URI - */ - -bool sp_file_open( - const Glib::ustring &uri, - Inkscape::Extension::Extension *key, - bool add_to_recent = true, - bool replace_empty = true - ); - /** * Displays a file open dialog. Calls sp_file_open on * an OK. diff --git a/src/help.cpp b/src/help.cpp index a32f8529a..5bfa282a9 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -15,7 +15,8 @@ #include #include -#include "file.h" +#include "inkscape-application.h" + #include "help.h" #include "io/resource.h" #include "io/sys.h" @@ -38,7 +39,9 @@ void sp_help_open_tutorial(Glib::ustring name) filename = get_filename(TUTORIALS, filename.c_str(), true); if (!filename.empty()) { - sp_file_open(filename.c_str(), nullptr, false, false); + Glib::RefPtr file = Gio::File::create_for_path(filename); + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + app->create_window(file, false, false); } else { sp_ui_error_dialog(_("The tutorial files are not installed.\nFor Linux, you may need to install " "'inkscape-tutorials'; for Windows, please re-run the setup and select 'Tutorials'.\nThe " diff --git a/src/inkscape-application.cpp b/src/inkscape-application.cpp index 6c6bd4ba4..e88dba707 100644 --- a/src/inkscape-application.cpp +++ b/src/inkscape-application.cpp @@ -16,11 +16,22 @@ #include "inkscape-window.h" #include "inkscape.h" // Inkscape::Application +#include "desktop.h" // Access to window +#include "file.h" // sp_file_convert_dpi + #include "inkgc/gc-core.h" // Garbage Collecting init -#include "ui/widget/panel.h" // Panel prep -#include "file.h" // File open and window creation. + #include "io/file.h" // File open (command line). -#include "desktop.h" // Access to window +#include "io/resource.h" // TEMPLATE +#include "io/resource-manager.h" // Fix up references. + +#include "object/sp-root.h" // Inkscape version. + +#include "ui/dialog/font-substitution.h" // Warn user about font substitution. +#include "ui/widget/panel.h" // Panel prep +#include "widgets/desktop-widget.h" // Close without saving dialog + +#include "util/units.h" // Redimension window #include "actions/actions-base.h" // Actions #include "actions/actions-output.h" // Actions @@ -53,61 +64,254 @@ InkscapeApplication::InkscapeApplication() , _active_view(nullptr) {} - // Open a document, add it to app. +// Add document to app. +void +InkscapeApplication::document_add(SPDocument* document) +{ + if (document) { + auto it = _documents.find(document); + if (it == _documents.end()) { + _documents[document] = std::vector(); + } else { + // Should never happen. + std::cerr << "InkscapeApplication::add_document: Document already opened!" << std::endl; + } + } else { + // Should never happen! + std::cerr << "InkscapeApplication::add_document: No document!" << std::endl; + } +} + +// New document, add it to app. TODO: This should really be open_document with option to strip template data. +SPDocument* +InkscapeApplication::document_new(const std::string &Template) +{ + // Open file + SPDocument *document = ink_file_new(Template); + if (document) { + document_add(document); + + // Set viewBox if it doesn't exist. + if (!document->getRoot()->viewBox_set) { + document->setViewBox(); + } + + } else { + std::cerr << "InkscapeApplication::new_document: failed to open new document!" << std::endl; + } + + return document; +} + + +// Open a document, add it to app. SPDocument* -InkscapeApplication::open_document(const Glib::RefPtr& file) +InkscapeApplication::document_open(const Glib::RefPtr& file) { // Open file bool cancelled = false; SPDocument *document = ink_file_open(file, cancelled); if (document) { - auto it = _documents.find(document); - if (it == _documents.end()) { - _documents[document] = std::vector(); + document->virgin = false; // Prevents replacing document in same window during file open. + + document_add (document); + } else { + std::cerr << "InkscapeApplication::open_document: Failed to open: " << file->get_parse_name() << std::endl; + } + + return document; +} + + +/** Swap out one document for another in a window... maybe this should disappear. + * Does not delete old document! + */ +bool +InkscapeApplication::document_swap(InkscapeWindow* window, SPDocument* document) +{ + if (!document || !window) { + std::cerr << "InkscapeAppliation::swap_document: Missing window or document!" << std::endl; + return false; + } + + SPDesktop* desktop = window->get_desktop(); + SPDocument* old_document = window->get_document(); + desktop->change_document(document); + document->emitResizedSignal(document->getWidth().value("px"), document->getHeight().value("px")); + + // We need to move window from the old document to the new document. + + // Find old document + auto it = _documents.find(old_document); + if (it != _documents.end()) { + + // Remove window from document map. + auto it2 = std::find(it->second.begin(), it->second.end(), window); + if (it2 != it->second.end()) { + it->second.erase(it2); } else { - std::cerr << "ConcreteInkscapeApplication::open_document: Document already opened: " << file->get_parse_name() << std::endl; + std::cerr << "InkscapeApplication::swap_document: Window not found!" << std::endl; } + } else { - std::cerr << "ConcreteInkscapeApplication::open_document: Failed to open: " << file->get_parse_name() << std::endl; + std::cerr << "InkscapeApplication::swap_document: Document not in map!" << std::endl; } - return document; + // Find new document + it = _documents.find(document); + if (it != _documents.end()) { + it->second.push_back(window); + } else { + std::cerr << "InkscapeApplication::swap_document: Document not in map!" << std::endl; + } + + // To be removed (add/delete once per window)! + INKSCAPE.add_document(document); + INKSCAPE.remove_document(old_document); + + // ActionContext should be removed once verbs are gone but we use it for now. + Inkscape::ActionContext context = INKSCAPE.action_context_for_document(document); + _active_document = document; + _active_selection = context.getSelection(); + _active_view = context.getView(); + + return true; +} + +/** Revert document: open saved document and swap it for each window. + */ +bool +InkscapeApplication::document_revert(SPDocument* document) +{ + // Find saved document. + gchar const *path = document->getDocumentURI(); + if (!path) { + std::cerr << "InkscapeApplication::revert_document: Document never saved, cannot revert." << std::endl; + return false; + } + + // Open saved document. + Glib::RefPtr file = Gio::File::create_for_path(document->getDocumentURI()); + SPDocument* new_document = document_open (file); + if (!new_document) { + std::cerr << "InkscapeApplication::revert_document: Cannot open saved document!" << std::endl; + return false; + } + + // Allow overwriting current document. + document->virgin = true; + + auto it = _documents.find(document); + if (it != _documents.end()) { + + // Swap reverted document in all windows. + for (auto it2 : it->second) { + + SPDesktop* desktop = it2->get_desktop(); + + // Remember current zoom and view. + double zoom = desktop->current_zoom(); + Geom::Point c = desktop->get_display_area().midpoint(); + + bool reverted = document_swap (it2, new_document); + + if (reverted) { + desktop->zoom_absolute_center_point (c, zoom); + } else { + std::cerr << "InkscapeApplication::revert_document: Revert failed!" << std::endl; + } + } + + document_close (document); + + } else { + std::cerr << "InkscapeApplication::revert_document: Document not found!" << std::endl; + return false; + } + + return true; } -// Close a document, remove from app. No checking is done on modified status, etc. + + +/** Close a document, remove from app. No checking is done on modified status, etc. + */ void -InkscapeApplication::close_document(SPDocument* document) +InkscapeApplication::document_close(SPDocument* document) { if (document) { + auto it = _documents.find(document); if (it != _documents.end()) { if (it->second.size() != 0) { - std::cerr << "ConcreteInkscapeApplication::close_document: Window vector not empty!" << std::endl; + std::cerr << "InkscapeApplication::close_document: Window vector not empty!" << std::endl; } _documents.erase(it); } else { - std::cerr << "ConcreteInkscapeApplication::close_document: Document not registered with application." << std::endl; + std::cerr << "InkscapeApplication::close_document: Document not registered with application." << std::endl; } delete document; } else { - std::cerr << "ConcreteInkscapeApplication::close_document: No document!" << std::endl; + std::cerr << "InkscapeApplication::close_document: No document!" << std::endl; + } +} + + +/** Return number of windows with document. + */ +unsigned +InkscapeApplication::document_window_count(SPDocument* document) +{ + unsigned count = 0; + + auto it = _documents.find(document); + if (it != _documents.end()) { + count = it->second.size(); + } else { + std::cerr << "InkscapeApplication::document_window_count: Document not in map!" << std::endl; } + + return count; } -// Fix up a document if necessary. +/** Fix up a document if necessary (Only fixes that require GUI). + */ void -InkscapeApplication::fix_document(SPDocument* document) +InkscapeApplication::document_fix(InkscapeWindow* window) { - // TODO + // Most fixes are handled when document is opened in SPDocument::createDoc(). + // But some require the GUI to be present. These are handled here. + + if (_with_gui) { + + SPDocument* document = window->get_document(); + + // Perform a fixup pass for hrefs. + if ( Inkscape::ResourceManager::getManager().fixupBrokenLinks(document) ) { + Glib::ustring msg = _("Broken links have been changed to point to existing files."); + SPDesktop* desktop = window->get_desktop(); + if (desktop != nullptr) { + desktop->showInfoDialog(msg); + } + } + + // Fix dpi (pre-92 files). + if ( sp_version_inside_range( document->getRoot()->version.inkscape, 0, 1, 0, 92 ) ) { + sp_file_convert_dpi(document); + } + + // Check for font substitutions, requires text to have been rendered. + Inkscape::UI::Dialog::FontSubstitution::getInstance().checkFontSubstitutions(document); + } } + // Take an already open document and create a new window, adding window to document map. -// Document fix-up should already be done. InkscapeWindow* -InkscapeApplication::open_window(SPDocument* document) +InkscapeApplication::window_open(SPDocument* document) { InkscapeWindow* window = new InkscapeWindow(document); // TODO Add window to application. (Instead of in InkscapeWindow constructor.) @@ -127,33 +331,41 @@ InkscapeApplication::open_window(SPDocument* document) if (it != _documents.end()) { it->second.push_back(window); } else { - std::cerr << "ConcreteInkscapeApplication::add_window: document not in map!" << std::endl; + std::cerr << "InkscapeApplication::open_window: Document not in map!" << std::endl; } + document_fix(window); // May need flag to prevent this from being called more than once. + return window; } +// Close a window. Does not delete document. void -InkscapeApplication::close_window(InkscapeWindow* window) +InkscapeApplication::window_close(InkscapeWindow* window) { + // std::cout << "InkscapeApplication::close_window" << std::endl; + // dump(); + if (window) { SPDocument* document = window->get_document(); if (document) { - // To be removed! - INKSCAPE.remove_document(document); + // To be removed (remove once per window)! + bool last = INKSCAPE.remove_document(document); _active_selection = nullptr; _active_view = nullptr; _active_document = nullptr; + // Remove window from document map. auto it = _documents.find(document); if (it != _documents.end()) { auto it2 = std::find(it->second.begin(), it->second.end(), window); if (it2 != it->second.end()) { it->second.erase(it2); + delete window; // Results in call to SPDesktop::destroy() } else { std::cerr << "ConcreteInkscapeApplication::close_window: window not found!" << std::endl; } @@ -164,11 +376,11 @@ InkscapeApplication::close_window(InkscapeWindow* window) std::cerr << "ConcreteInkscapeApplication::close_window: No document!" << std::endl; } - // TODO Remove window from application. - } else { std::cerr << "ConcreteInkscapeApplication::close_window: No window!" << std::endl; } + + // dump(); } @@ -178,20 +390,36 @@ InkscapeApplication::close_window(InkscapeWindow* window) * - Selection change */ void -InkscapeApplication::update_windows(SPDocument* document) +InkscapeApplication::windows_update(SPDocument* document) { // Find windows: auto it = _documents.find( document ); if (it != _documents.end()) { std::vector windows = it->second; - std::cout << "InkscapeApplication::update_windows: windows: " << windows.size() << std::endl; + // std::cout << "InkscapeApplication::update_windows: windows size: " << windows.size() << std::endl; // Loop over InkscapeWindows. // Loop over DialogWindows. TBD } else { - std::cout << "InkscapeApplication::update_windows: no windows found" << std::endl; + // std::cout << "InkscapeApplication::update_windows: no windows found" << std::endl; } } +/** Debug function + */ +void +InkscapeApplication::dump() +{ + std::cout << "InkscapeApplication::dump()" << std::endl; + std::cout << " Documents: " << _documents.size() << std::endl; + for (auto i : _documents) { + std::cout << " Document: " << (i.first->getDocumentName()?i.first->getDocumentName():"unnamed") << std::endl; + for (auto j : i.second) { + std::cout << " Window: " << j->get_title() << std::endl; + } + } +} + + template ConcreteInkscapeApplication& ConcreteInkscapeApplication::get_instance() @@ -369,6 +597,177 @@ ConcreteInkscapeApplication::on_startup2() } } +/** We should not create a window if T is Gio::Applicaton. +*/ +template +SPDesktop* +ConcreteInkscapeApplication::create_window(const Glib::RefPtr& file, + bool add_to_recent, + bool replace_empty) +{ + std::cerr << "ConcreteInkscapeApplication::create_window: Should not be called!"; + return nullptr; +} + + +/** Create a window given a Gio::File. This is what most functions should call. + The booleans are only false when opening a help file. +*/ +template<> +SPDesktop* +ConcreteInkscapeApplication::create_window(const Glib::RefPtr& file, + bool add_to_recent, + bool replace_empty) +{ + SPDesktop* desktop = nullptr; + + if (file) { + SPDocument* document = document_open (file); + if (document) { + + if (add_to_recent) { + + auto recentmanager = Gtk::RecentManager::get_default(); + Gtk::RecentManager::Data data; + data.app_name = g_get_prgname(); // Must match Gtk::RecentFilter in menubar.cpp + data.mime_type = "image"; // We don't know if we opened an SVG! (image/svg+xml). + recentmanager->add_item (file->get_uri(), data); + } + + // TODO Remove this code... handle document replacement elsewhere. + SPDocument* old_document = _active_document; + if (replace_empty && old_document && old_document->virgin) { + // virgin == true => an empty document (template). + + // Is there a better place for this? It requires GUI. + document->ensureUpToDate(); // TODO this will trigger broken line warnings, etc. + + InkscapeWindow* window = dynamic_cast(get_active_window()); + if (window) { + document_swap (window, document); + + // Delete old document if no longer attached to any window. + auto it = _documents.find (old_document); + if (it != _documents.end()) { + if (it->second.size() == 0) { + document_close (old_document); + } + } + + document->emitResizedSignal(document->getWidth().value("px"), document->getHeight().value("px")); + desktop = window->get_desktop(); + } else { + std::cerr << "ConcreteInkscapeApplication::create_window: Failed to find active window!" << std::endl; + } + } else { + InkscapeWindow* window = window_open (document); + desktop = window->get_desktop(); + } + + + } else { + std::cerr << "ConcreteInkscapeApplication::create_window: Failed to load: " + << file->get_parse_name() << std::endl; + } + + } else { + std::string Template = + Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::TEMPLATES, "default.svg", true); + SPDocument* document = document_new (Template); + if (document) { + InkscapeWindow* window = window_open (document); + desktop = window->get_desktop(); + } else { + std::cerr << "ConcreteInkscapeApplication::create_window: Failed to open default template! " << Template << std::endl; + } + } + + if (desktop) { + _active_document = desktop->getDocument(); +#ifdef WITH_DBUS + Inkscape::Extension::Dbus::dbus_init_desktop_interface(desktop); +#endif + } else { + std::cerr << "ConcreteInkscapeApplication::create_window: Failed to create desktop!" << std::endl; + } + + return (desktop); // Temp: Need to track desktop for shell mode. +} + +/** No need to destroy window if T is Gio::Application. + */ +template +bool +ConcreteInkscapeApplication::destroy_window(InkscapeWindow* window) +{ + std::cerr << "ConcreteInkscapeApplication::destroy_window: Should not be called!"; + return false; +} + +/** Destroy a window. Aborts if document needs saving. + * Returns true if window destroyed. + */ +template<> +bool +ConcreteInkscapeApplication::destroy_window(InkscapeWindow* window) +{ + SPDocument* document = window->get_document(); + + // Remove document if no windows left. + if (document) { + auto it = _documents.find(document); + if (it != _documents.end()) { + + // If only one window for document: + if (it->second.size() == 1) { + // Check if document needs saving. + bool abort = window->get_desktop_widget()->shutdown(); + if (abort) { + return false; + } + } + + window_close(window); + + if (it->second.size() == 0) { + document_close (document); + } + + } else { + std::cerr << "ConcreteInkscapeApplication::destroy_window: Could not find document!" << std::endl; + } + } + + // Debug + // auto windows = get_windows(); + // std::cout << "destroy_windows: app windows size: " << windows.size() << std::endl; + + return true; +} + +/* Close all windows and exit. +**/ +template +void +ConcreteInkscapeApplication::destroy_all() +{ + std::cerr << "ConcreteInkscapeApplication::destroy_all: Should not be called!"; +} + +template<> +void +ConcreteInkscapeApplication::destroy_all() +{ + while (_documents.size() != 0) { + auto it = _documents.begin(); + while (it->second.size() != 0) { + auto it2 = it->second.begin(); + if (!destroy_window (*it2)) return; // If destroy aborted, we need to stop exit. + } + } +} + + // Open document window with default document. Either this or on_open() is called. template void @@ -401,7 +800,7 @@ ConcreteInkscapeApplication::on_open(const Gio::Application::type_vec_files& for (auto file : files) { // Open file - SPDocument *document = open_document(file); + SPDocument *document = document_open (file); if (!document) continue; // Add to Inkscape::Application... @@ -433,7 +832,7 @@ ConcreteInkscapeApplication::on_open(const Gio::Application::type_vec_files& // Close file INKSCAPE.remove_document(document); - close_document(document); + document_close (document); } } @@ -466,7 +865,7 @@ ConcreteInkscapeApplication::on_open(const Gio::Application::t } else { // Open file - SPDocument *document = open_document(file); + SPDocument *document = document_open (file); if (!document) continue; // Add to Inkscape::Application... @@ -498,52 +897,11 @@ ConcreteInkscapeApplication::on_open(const Gio::Application::t // Close file INKSCAPE.remove_document(document); - close_document(document); + document_close (document); } } } - - -template -SPDesktop* -ConcreteInkscapeApplication::create_window(const Glib::RefPtr& file) -{ - SPDesktop* desktop = nullptr; - if (file) { - sp_file_open(file->get_parse_name(), nullptr, false, true); - desktop = SP_ACTIVE_DESKTOP; - } else { - desktop = sp_file_new_default(); - } - - _active_document = desktop->getDocument(); - // _documents.push_back(desktop->getDocument()); - - return (desktop); // Temp: Need to track desktop for shell mode. -} - - -// This is identical to the function above. We comment it out for now (otherwise it must be moved before on_open(). -// template<> -// SPDesktop* -// ConcreteInkscapeApplication::create_window(const Glib::RefPtr& file) -// { -// SPDesktop* desktop = nullptr; -// if (file) { -// sp_file_open(file->get_parse_name(), nullptr, false, true); -// desktop = SP_ACTIVE_DESKTOP; -// } else { -// desktop = sp_file_new_default(); -// } - -// _active_document = desktop->getDocument(); -// // _documents.push_back(desktop->getDocument()); - -// return (desktop); // Temp: Need to track desktop for shell mode. -// } - - template void ConcreteInkscapeApplication::parse_actions(const Glib::ustring& input, action_vector_t& action_vector) diff --git a/src/inkscape-application.h b/src/inkscape-application.h index 7c53b2795..05fbafb89 100644 --- a/src/inkscape-application.h +++ b/src/inkscape-application.h @@ -59,22 +59,34 @@ public: Inkscape::UI::View::View* get_active_view() { return _active_view; } void set_active_view(Inkscape::UI::View::View* view) { _active_view = view; } - SPDocument* open_document(const Glib::RefPtr& file); - void close_document(SPDocument* document); + /****** Document ******/ + /* Except for document_fix(), these should not require a GUI! */ + void document_add(SPDocument* document); - void fix_document(SPDocument* document); + SPDocument* document_new(const std::string &Template); + SPDocument* document_open(const Glib::RefPtr& file); + bool document_swap(InkscapeWindow* window, SPDocument* document); + bool document_revert(SPDocument* document); + void document_close(SPDocument* document); + unsigned document_window_count(SPDocument* document); - InkscapeWindow* open_window(SPDocument* document); - void close_window(InkscapeWindow* window); + void document_fix(InkscapeWindow* window); + + /******* Window *******/ + InkscapeWindow* window_open(SPDocument* document); + void window_close(InkscapeWindow* window); // Update all windows connected to a document. - void update_windows(SPDocument* document); + void windows_update(SPDocument* document); + + /******* Debug ********/ + void dump(); // These are needed to cast Glib::RefPtr to Glib::RefPtr, // Presumably, Gtk/Gio::Application takes care of ref counting in ConcreteInkscapeApplication // so we just provide dummies (and there is only one application in the application!). - void reference() { /*printf("reference()\n" );*/ } - void unreference() { /*printf("unreference()\n");*/ } + // void reference() { /*printf("reference()\n" );*/ } + // void unreference() { /*printf("unreference()\n");*/ } protected: bool _with_gui; @@ -111,13 +123,16 @@ private: public: InkFileExportCmd* file_export() override { return &_file_export; } + SPDesktop* create_window(const Glib::RefPtr& file = Glib::RefPtr(), + bool add_to_recent = true, bool replace_empty = true); + bool destroy_window(InkscapeWindow* window); + void destroy_all(); protected: void on_startup() override; void on_startup2() override; void on_activate() override; void on_open(const Gio::Application::type_vec_files& files, const Glib::ustring& hint) override; - SPDesktop* create_window(const Glib::RefPtr& file = Glib::RefPtr()); void parse_actions(const Glib::ustring& input, action_vector_t& action_vector); private: diff --git a/src/inkscape-window.cpp b/src/inkscape-window.cpp index bcf10cedd..eb11199db 100644 --- a/src/inkscape-window.cpp +++ b/src/inkscape-window.cpp @@ -99,6 +99,11 @@ InkscapeWindow::InkscapeWindow(SPDocument* document) sp_namedview_zoom_and_view_from_document(_desktop); sp_namedview_update_layers_from_document(_desktop); + SPNamedView *nv = _desktop->namedview; + if (nv && nv->lockguides) { + nv->lockGuides(); + } + } // Change a document, leaving desktop/view the same. (Eventually move all code here.) @@ -133,7 +138,7 @@ InkscapeWindow::on_focus_in_event(GdkEventFocus* event) _app->set_active_document(_document); _app->set_active_view(_desktop); _app->set_active_selection(_desktop->selection); - // _app->update_windows(_document); + _app->windows_update(_document); } else { std::cerr << "Inkscapewindow::on_focus_in_event: app is nullptr!" << std::endl; } diff --git a/src/io/file.cpp b/src/io/file.cpp index 5877e2727..294707464 100644 --- a/src/io/file.cpp +++ b/src/io/file.cpp @@ -2,7 +2,7 @@ /* * File operations (independent of GUI) * - * Copyright (C) 2018 Tavmjong Bah + * Copyright (C) 2018, 2019 Tavmjong Bah * * The contents of this file may be used under the GNU General Public License Version 2 or later. * @@ -14,6 +14,7 @@ #include "file.h" #include "document.h" +#include "document-undo.h" #include "extension/system.h" // Extension::open() #include "extension/extension.h" @@ -23,58 +24,86 @@ #include "object/sp-root.h" -// SPDocument* -// ink_file_new(const std::string &Template) -// { -// SPDocument *doc = SPDocument::createNewDoc( Template, true, true ); -// return doc; -// } +/** + * Create a blank document, remove any template data. + * Input: Empty string or template file name. + */ +SPDocument* +ink_file_new(const std::string &Template) +{ + SPDocument *doc = SPDocument::createNewDoc ((Template.empty() ? nullptr : Template.c_str()), true, true ); + + if (doc) { + // Remove all the template info from xml tree + Inkscape::XML::Node *myRoot = doc->getReprRoot(); + Inkscape::XML::Node *nodeToRemove = sp_repr_lookup_name(myRoot, "inkscape:_templateinfo"); + if (nodeToRemove != nullptr) { + Inkscape::DocumentUndo::ScopedInsensitive no_undo(doc); + sp_repr_unparent(nodeToRemove); + delete nodeToRemove; + } + } else { + std::cout << "ink_file_new: Did not create new document!" << std::endl; + } + + return doc; +} SPDocument* ink_file_open(const Glib::RefPtr& file, bool &cancelled) { - cancelled = false; + cancelled = false; - SPDocument *doc = nullptr; + SPDocument *doc = nullptr; - std::string path = file->get_path(); + std::string path = file->get_path(); - try { - doc = Inkscape::Extension::open(nullptr, path.c_str()); - } catch (Inkscape::Extension::Input::no_extension_found &e) { - doc = nullptr; - } catch (Inkscape::Extension::Input::open_failed &e) { - doc = nullptr; - } catch (Inkscape::Extension::Input::open_cancelled &e) { - cancelled = true; - doc = nullptr; - } - - // Try to open explicitly as SVG. - if (doc == nullptr && !cancelled) { try { - doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), path.c_str()); + doc = Inkscape::Extension::open(nullptr, path.c_str()); } catch (Inkscape::Extension::Input::no_extension_found &e) { - doc = nullptr; + doc = nullptr; } catch (Inkscape::Extension::Input::open_failed &e) { - doc = nullptr; + doc = nullptr; } catch (Inkscape::Extension::Input::open_cancelled &e) { - cancelled = true; - doc = nullptr; + cancelled = true; + doc = nullptr; } - } - if (doc == nullptr) { - std::cerr << "ink_file_open: '" << path << "' cannot be opened!" << std::endl; - } else { + // Try to open explicitly as SVG. + if (doc == nullptr && !cancelled) { + try { + doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), path.c_str()); + } catch (Inkscape::Extension::Input::no_extension_found &e) { + doc = nullptr; + } catch (Inkscape::Extension::Input::open_failed &e) { + doc = nullptr; + } catch (Inkscape::Extension::Input::open_cancelled &e) { + cancelled = true; + doc = nullptr; + } + } - // This is the only place original values should be set. - SPRoot *root = doc->getRoot(); - root->original.inkscape = root->version.inkscape; - root->original.svg = root->version.svg; - } + if (doc == nullptr) { + std::cerr << "ink_file_open: '" << path << "' cannot be opened!" << std::endl; + } else { - return doc; + // This is the only place original values should be set. + SPRoot *root = doc->getRoot(); + root->original.inkscape = root->version.inkscape; + root->original.svg = root->version.svg; + } + + return doc; } +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/io/file.h b/src/io/file.h index b911563b7..3e1c8ca65 100644 --- a/src/io/file.h +++ b/src/io/file.h @@ -13,7 +13,7 @@ class SPDocument; -//SPDocument* ink_file_new(const std::string &template = nullptr); +SPDocument* ink_file_new(const std::string &Template = nullptr); SPDocument* ink_file_open(const Glib::RefPtr& file, bool &cancelled); // To do: diff --git a/src/io/resource-manager.cpp b/src/io/resource-manager.cpp index fc742cb80..308aca8a9 100644 --- a/src/io/resource-manager.cpp +++ b/src/io/resource-manager.cpp @@ -338,7 +338,12 @@ bool ResourceManagerImpl::fixupBrokenLinks(SPDocument *doc) } } - std::map mapping = locateLinks(doc->getDocumentBase(), brokenHrefs); + Glib::ustring base; + if (doc->getDocumentBase()) { + base = doc->getDocumentBase(); + } + + std::map mapping = locateLinks(base, brokenHrefs); for ( std::map::iterator it = mapping.begin(); it != mapping.end(); ++it ) { // TODO debug g_message(" [%s] ==> {%s}", it->first.c_str(), it->second.c_str()); diff --git a/src/ui/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp index e9ef9e897..7300e8c3b 100644 --- a/src/ui/desktop/menubar.cpp +++ b/src/ui/desktop/menubar.cpp @@ -24,7 +24,8 @@ #include #include "inkscape.h" -#include "file.h" // sp_file_open +#include "inkscape-application.h" // Open recent + #include "message-context.h" #include "shortcuts.h" @@ -322,9 +323,9 @@ sp_recent_open(Gtk::RecentChooser* recentchooser) Glib::RefPtr file = Gio::File::create_for_uri(uri); - // To do: change sp_file_open to use Gio::File. - // To do: get rid of sp_file_open - sp_file_open(file->get_parse_name(), nullptr); + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + + app->create_window(file); } // =================== Main Menu ================ diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index 2e7d44bf8..1b977999e 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -63,7 +63,9 @@ sp_ui_new_view() document = SP_ACTIVE_DOCUMENT; if (!document) return; - auto win = new InkscapeWindow(document); + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + + InkscapeWindow* win = app->window_open (document); } void @@ -79,45 +81,46 @@ sp_ui_close_view(GtkWidget */*widget*/) return; // Shutdown operation has been canceled, so do nothing } + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + + InkscapeWindow* window = SP_ACTIVE_DESKTOP->getInkscapeWindow(); + // If closing the last document, open a new document so Inkscape doesn't quit. std::list desktops; INKSCAPE.get_all_desktops(desktops); if (desktops.size() == 1) { - Glib::ustring templateUri = sp_file_default_template_uri(); - SPDocument *doc = SPDocument::createNewDoc( templateUri.empty() ? nullptr : templateUri.c_str(), TRUE, true ); - // Set viewBox if it doesn't exist - if (!doc->getRoot()->viewBox_set) { - doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDisplayUnit()), doc->getHeight().value(doc->getDisplayUnit()))); + + SPDocument* old_document = window->get_document(); + + Glib::ustring template_path = sp_file_default_template_uri(); + SPDocument *doc = app->document_new (template_path); + + app->document_swap (window, doc); + + if (app->document_window_count(old_document) == 0) { + app->document_close(old_document); } - dt->change_document(doc); + + // Are these necessary? sp_namedview_window_from_document(dt); sp_namedview_update_layers_from_document(dt); - return; - } - // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP, - // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad) - dt->destroyWidget(); + } else { + + app->destroy_window (window); + } } unsigned int sp_ui_close_all() { - /* Iterate through all the windows, destroying each in the order they - become active */ - while (SP_ACTIVE_DESKTOP) { - SPDesktop *dt = SP_ACTIVE_DESKTOP; - if (dt->shutdown()) { - /* The user canceled the operation, so end doing the close */ - return FALSE; - } - // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP, - // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad) - dt->destroyWidget(); - } - return TRUE; + ConcreteInkscapeApplication* app = &(ConcreteInkscapeApplication::get_instance()); + + app->destroy_all(); + + return true; } diff --git a/src/ui/view/view.cpp b/src/ui/view/view.cpp index 4395357e0..3b2b7f229 100644 --- a/src/ui/view/view.cpp +++ b/src/ui/view/view.cpp @@ -84,7 +84,7 @@ void View::_close() { _document_resized_connection.disconnect(); if (INKSCAPE.remove_document(_doc)) { // this was the last view of this document, so delete it - delete _doc; + // delete _doc; Delete now handled in Inkscape::Application } _doc = nullptr; } @@ -110,7 +110,7 @@ void View::setDocument(SPDocument *doc) { _document_resized_connection.disconnect(); if (INKSCAPE.remove_document(_doc)) { // this was the last view of this document, so delete it - delete _doc; + // delete _doc; Delete now handled in Inkscape::Application } } -- cgit v1.2.3