summaryrefslogtreecommitdiffstats
path: root/src/inkscape-window.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /src/inkscape-window.cpp
parentInitial commit. (diff)
downloadinkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz
inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/inkscape-window.cpp')
-rw-r--r--src/inkscape-window.cpp367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/inkscape-window.cpp b/src/inkscape-window.cpp
new file mode 100644
index 0000000..8306d03
--- /dev/null
+++ b/src/inkscape-window.cpp
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Inkscape - An SVG editor.
+ */
+/*
+ * Authors:
+ * Tavmjong Bah
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * The contents of this file may be used under the GNU General Public License Version 2 or later.
+ * Read the file 'COPYING' for more information.
+ *
+ */
+
+
+#include "inkscape-window.h"
+#include "inkscape.h" // SP_ACTIVE_DESKTOP
+#include "desktop-events.h" // Handle key events
+#include "enums.h" // PREFS_WINDOW_GEOMETRY_NONE
+
+#include "inkscape-application.h"
+
+#include "actions/actions-canvas-mode.h"
+#include "actions/actions-canvas-snapping.h"
+#include "actions/actions-canvas-transform.h"
+#include "actions/actions-dialogs.h"
+#include "actions/actions-edit-window.h"
+#include "actions/actions-file-window.h"
+#include "actions/actions-help-url.h"
+#include "actions/actions-layer.h"
+#include "actions/actions-node-align.h" // Node alignment.
+#include "actions/actions-paths.h" // TEMP
+#include "actions/actions-selection-window.h"
+#include "actions/actions-tools.h"
+#include "actions/actions-view-mode.h"
+#include "actions/actions-view-window.h"
+
+#include "object/sp-namedview.h" // TODO Remove need for this!
+
+#include "ui/dialog/dialog-container.h"
+#include "ui/dialog/dialog-manager.h"
+#include "ui/dialog/dialog-window.h"
+#include "ui/drag-and-drop.h" // Move to canvas?
+#include "ui/interface.h" // main menu, sp_ui_close_view()
+
+#include "ui/monitor.h" // get_monitor_geometry_at_point()
+
+#include "ui/desktop/menubar.h"
+#include "ui/desktop/menu-icon-shift.h"
+
+#include "ui/drag-and-drop.h"
+
+#include "ui/event-debug.h"
+#include "ui/shortcuts.h"
+
+#include "widgets/desktop-widget.h"
+#include "ui/util.h"
+#include "ui/widget/canvas.h"
+
+using Inkscape::UI::Dialog::DialogManager;
+using Inkscape::UI::Dialog::DialogContainer;
+using Inkscape::UI::Dialog::DialogWindow;
+
+static gboolean _resize_children(Gtk::Window *win)
+{
+ Inkscape::UI::resize_widget_children(win);
+ return false;
+}
+
+
+InkscapeWindow::InkscapeWindow(SPDocument* document)
+ : _document(document)
+ , _app(nullptr)
+{
+ if (!_document) {
+ std::cerr << "InkscapeWindow::InkscapeWindow: null document!" << std::endl;
+ return;
+ }
+
+ _app = InkscapeApplication::instance();
+ _app->gtk_app()->add_window(*this);
+
+ set_resizable(true);
+
+ // =============== Build interface ===============
+
+ // Main box
+ _mainbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
+ _mainbox->set_name("DesktopMainBox");
+ _mainbox->show();
+ add(*_mainbox);
+
+ // Desktop widget (=> MultiPaned)
+ _desktop_widget = new SPDesktopWidget(this, _document);
+ _desktop_widget->window = this;
+ _desktop_widget->show();
+ _desktop = _desktop_widget->desktop;
+
+ // =================== Actions ===================
+
+ // After canvas has been constructed.. move to canvas proper.
+ add_actions_canvas_mode(this); // Actions to change canvas display mode.
+ add_actions_canvas_snapping(this); // Actions to toggle on/off snapping modes.
+ add_actions_canvas_transform(this); // Actions to transform canvas view.
+ add_actions_dialogs(this); // Actions to open dialogs.
+ add_actions_edit_window(this); // Actions to edit.
+ add_actions_file_window(this); // Actions for file actions which are desktop dependent.
+ add_actions_help_url(this); // Actions to help url.
+ add_actions_layer(this); // Actions for layer.
+ add_actions_node_align(this); // Actions to align and distribute nodes (requiring Node tool).
+ add_actions_path(this); // Actions for paths. TEMP
+ add_actions_select_window(this); // Actions with desktop selection
+ add_actions_tools(this); // Actions to switch between tools.
+ add_actions_view_mode(this); // Actions to change how Inkscape canvas is displayed.
+ add_actions_view_window(this); // Actions to add/change window of Inkscape
+
+ // Add document action group to window and export to DBus.
+ insert_action_group("doc", document->getActionGroup());
+
+ auto connection = _app->gio_app()->get_dbus_connection();
+ if (connection) {
+ std::string document_action_group_name = _app->gio_app()->get_dbus_object_path() + "/document/" + std::to_string(get_id());
+ connection->export_action_group(document_action_group_name, document->getActionGroup());
+ }
+
+ // This is called here (rather than in InkscapeApplication) solely to add win level action
+ // tooltips to the menu label-to-tooltip map.
+ build_menu();
+
+ // ========== Drag and Drop of Documents =========
+ ink_drag_setup(_desktop_widget);
+
+ // Pallet
+
+ // Status bar
+
+ // The main section
+ _mainbox->pack_start(*Gtk::manage(_desktop_widget), true, true);
+
+ // ================== Callbacks ==================
+ signal_window_state_event().connect(sigc::mem_fun(*_desktop, &SPDesktop::onWindowStateEvent));
+ signal_focus_in_event().connect( sigc::mem_fun(*_desktop_widget, &SPDesktopWidget::onFocusInEvent));
+
+
+ // ================ Window Options ===============
+ setup_view();
+
+ // Show dialogs after the main window, otherwise dialogs may be associated as the main window of the program.
+ // Restore short-lived floating dialogs state if this is the first window being opened
+ bool include_short_lived = _app->get_number_of_windows() == 0;
+ DialogManager::singleton().restore_dialogs_state(_desktop->getContainer(), include_short_lived);
+
+ // This pokes the window to request the right size for the dialogs once loaded.
+ g_idle_add(GSourceFunc(&_resize_children), this);
+
+ // ================= Shift Icons =================
+ // Note: The menu is defined at the app level but shifting icons requires actual widgets and
+ // must be done on the window level.
+ for (auto child : get_children()) {
+ auto menubar = dynamic_cast<Gtk::MenuBar *>(child);
+ if (menubar) {
+ shift_icons_recursive(menubar);
+ }
+ }
+
+ // ========= Update text for Accellerators =======
+ Inkscape::Shortcuts::getInstance().update_gui_text_recursive(this);
+}
+
+InkscapeWindow::~InkscapeWindow()
+{
+ g_idle_remove_by_data(this);
+}
+
+// Change a document, leaving desktop/view the same. (Eventually move all code here.)
+void
+InkscapeWindow::change_document(SPDocument* document)
+{
+ if (!_app) {
+ std::cerr << "Inkscapewindow::change_document: app is nullptr!" << std::endl;
+ return;
+ }
+
+ _document = document;
+ _app->set_active_document(_document);
+ remove_action_group("doc");
+ insert_action_group("doc", document->getActionGroup());
+
+ setup_view();
+ update_dialogs();
+}
+
+// Sets up the window and view according to user preferences and <namedview> of the just loaded document
+void
+InkscapeWindow::setup_view()
+{
+ // Make sure the GdkWindow is fully initialized before resizing/moving
+ // (ensures the monitor it'll be shown on is known)
+ realize();
+
+ // Resize the window to match the document properties
+ sp_namedview_window_from_document(_desktop); // This should probably be a member function here.
+
+ // Must show before setting zoom and view! (crashes otherwise)
+ //
+ // Showing after resizing/moving allows the window manager to correct an invalid size/position of the window
+ // TODO: This does *not* work when called from 'change_document()', i.e. when the window is already visible.
+ // This can result in off-screen windows! We previously worked around this by hiding and re-showing
+ // the window, but a call to hide() causes Inkscape to just exit since the migration to Gtk::Application
+ show();
+
+ sp_namedview_zoom_and_view_from_document(_desktop);
+ sp_namedview_update_layers_from_document(_desktop);
+
+ SPNamedView *nv = _desktop->namedview;
+ if (nv && nv->lockguides) {
+ nv->setLockGuides(true);
+ }
+}
+
+bool
+InkscapeWindow::on_key_press_event(GdkEventKey* event)
+{
+#ifdef EVENT_DEBUG
+ ui_dump_event(reinterpret_cast<GdkEvent *>(event), "\nInkscapeWindow::on_key_press_event");
+#endif
+
+ // Key press and release events are normally sent first to Gtk::Window for processing as
+ // accelerators and menomics before bubbling up from the "grab" or "focus" widget (unlike other
+ // events which always bubble up). This would means that key combinations used for accelerators
+ // won't reach the focus widget (and our tool event handlers). As we use single keys for
+ // accelerators, we wouldn't even be able to type text! We can get around this by sending key
+ // events first to the focus widget.
+ //
+ // See https://developer.gnome.org/gtk3/stable/chap-input-handling.html (Event Propagation)
+
+ auto focus = get_focus();
+ if (focus) {
+ if (focus->event(reinterpret_cast<GdkEvent *>(event))) {
+ return true;
+ }
+ }
+
+ if (Gtk::Window::on_key_press_event(event)) {
+ return true;
+ }
+
+ // Not handled
+ return false;
+}
+
+/**
+ * If "dialogs on top" is activated in the preferences, set `parent` as the
+ * new transient parent for all DialogWindow windows of the application.
+ */
+static void retransientize_dialogs(Gtk::Window &parent)
+{
+ assert(!dynamic_cast<DialogWindow *>(&parent));
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool window_above =
+ prefs->getInt("/options/transientpolicy/value", PREFS_DIALOGS_WINDOWS_NORMAL) != PREFS_DIALOGS_WINDOWS_NONE;
+
+ for (auto const &window : parent.get_application()->get_windows()) {
+ if (auto dialog_window = dynamic_cast<DialogWindow *>(window)) {
+ if (window_above) {
+ dialog_window->set_transient_for(parent);
+ } else {
+ dialog_window->unset_transient_for();
+ }
+ }
+ }
+}
+
+bool
+InkscapeWindow::on_focus_in_event(GdkEventFocus* event)
+{
+ if (_app) {
+ _app->set_active_window(this);
+ _app->set_active_document(_document);
+ _app->set_active_view(_desktop);
+ _app->set_active_selection(_desktop->selection);
+ _app->windows_update(_document);
+ update_dialogs();
+ retransientize_dialogs(*this);
+ } else {
+ std::cerr << "Inkscapewindow::on_focus_in_event: app is nullptr!" << std::endl;
+ }
+
+ return Gtk::ApplicationWindow::on_focus_in_event(event);
+}
+
+// Called when a window is closed via the 'X' in the window bar.
+bool
+InkscapeWindow::on_delete_event(GdkEventAny* event)
+{
+ if (_app) {
+ _app->destroy_window(this);
+ }
+ return true;
+};
+
+/**
+ * Configure is called when the widget's size, position or stack changes.
+ */
+bool InkscapeWindow::on_configure_event(GdkEventConfigure *event)
+{
+ bool ret = Gtk::ApplicationWindow::on_configure_event(event);
+ // Store the desktop widget size on resize.
+ if (!_desktop || !get_realized())
+ return ret;
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool maxed = _desktop->is_maximized();
+ bool full = _desktop->is_fullscreen();
+ prefs->setBool("/desktop/geometry/fullscreen", full);
+ prefs->setBool("/desktop/geometry/maximized", maxed);
+
+ // Don't save geom for maximized, fullscreen or iconified windows.
+ // It just tells you the current maximized size, which is not
+ // as useful as whatever value it had previously.
+ if (!_desktop->is_iconified() && !maxed && !full) {
+
+ // Get size is more accurate than frame extends for window size.
+ int w,h = 0;
+ get_size(w, h);
+ prefs->setInt("/desktop/geometry/width", w);
+ prefs->setInt("/desktop/geometry/height", h);
+
+ // Frame extends returns real positions, unlike get_position()
+ if (Glib::RefPtr<Gdk::Window> gdkw = get_window()) {
+ Gdk::Rectangle rect;
+ gdkw->get_frame_extents(rect);
+ prefs->setInt("/desktop/geometry/x", rect.get_x());
+ prefs->setInt("/desktop/geometry/y", rect.get_y());
+ }
+ }
+ return ret;
+}
+
+void InkscapeWindow::update_dialogs()
+{
+ std::vector<Gtk::Window *> windows = _app->gtk_app()->get_windows();
+ for (auto const &window : windows) {
+ DialogWindow *dialog_window = dynamic_cast<DialogWindow *>(window);
+ if (dialog_window) {
+ // Update the floating dialogs, reset them to the new desktop.
+ dialog_window->set_inkscape_window(this);
+ }
+ }
+
+ // Update the docked dialogs in this InkscapeWindow
+ _desktop->updateDialogs();
+}
+
+/*
+ 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 :