diff options
Diffstat (limited to 'src/desktop.h')
-rw-r--r-- | src/desktop.h | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/src/desktop.h b/src/desktop.h new file mode 100644 index 0000000..0c62dc0 --- /dev/null +++ b/src/desktop.h @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef SEEN_SP_DESKTOP_H +#define SEEN_SP_DESKTOP_H + +/* + * Author: + * Lauris Kaplinski <lauris@kaplinski.com> + * Frank Felfe <innerspace@iname.com> + * bulia byak <buliabyak@users.sf.net> + * Ralf Stephan <ralf@ark.in-berlin.de> + * John Bintz <jcoswell@coswellproductions.org> + * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Jon A. Cruz <jon@joncruz.org>get + * Abhishek Sharma + * + * Copyright (C) 2007 Johan Engelen + * Copyright (C) 2006 John Bintz + * Copyright (C) 1999-2005 authors + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + * + */ + +#include <cstddef> +#include <sigc++/sigc++.h> +#include <glibmm/ustring.h> + +#include <2geom/affine.h> +#include <2geom/transforms.h> +#include <2geom/rect.h> + +#include "preferences.h" + +#include "display/rendermode.h" + +#include "object/sp-gradient.h" // TODO refactor enums out to their own .h file + +#include "ui/dialog/print.h" +#include "ui/view/view.h" + +class SPCSSAttr; +struct SPCanvas; +struct SPCanvasItem; +struct SPCanvasGroup; +struct DesktopPrefObserver; + +namespace Inkscape { +namespace UI { +namespace Tools { + +class ToolBase; + +} +} +} + +class SPItem; +class SPNamedView; +class SPObject; +class SPStyle; +typedef struct _DocumentInterface DocumentInterface;//struct DocumentInterface; + +class InkscapeWindow; + +namespace Gtk +{ + class Toolbar; + class Window; +} + +typedef int sp_verb_t; + +struct _GdkEventAny; +typedef struct _GdkEventAny GdkEventAny; +struct _GdkEventWindowState; +typedef struct _GdkEventWindowState GdkEventWindowState; + +struct InkscapeApplication; + +namespace Inkscape { + class LayerModel; + class MessageContext; + class Selection; + class LayerManager; + class EventLog; + namespace UI { + namespace Dialog { + class DialogManager; + } + + namespace Widget { + class Dock; + } + + namespace View { + struct EditWidgetInterface; + } + } + namespace Whiteboard { + class SessionManager; + } + namespace Display { + class TemporaryItemList; + class TemporaryItem; + class SnapIndicator; + } +} + +#define SP_DESKTOP_ZOOM_MAX 256.0 +#define SP_DESKTOP_ZOOM_MIN 0.01 + +/** + * SPDesktop is a subclass of View, implementing an editable document + * canvas. It is extensively used by many UI controls that need certain + * visual representations of their own. + * + * SPDesktop provides a certain set of SPCanvasItems, serving as GUI + * layers of different control objects. The one containing the whole + * document is the drawing layer. In addition to it, there are grid, + * guide, sketch and control layers. The sketch layer is used for + * temporary drawing objects, before the real objects in document are + * created. The control layer contains editing knots, rubberband and + * similar non-document UI objects. + * + * Each SPDesktop is associated with a SPNamedView node of the document + * tree. Currently, all desktops are created from a single main named + * view, but in the future there may be support for different ones. + * SPNamedView serves as an in-document container for desktop-related + * data, like grid and guideline placement, snapping options and so on. + * + * Associated with each SPDesktop are the two most important editing + * related objects - SPSelection and ToolBase. + * + * Sodipodi keeps track of the active desktop and invokes notification + * signals whenever it changes. UI elements can use these to update their + * display to the selection of the currently active editing window. + * (Lauris Kaplinski) + * + * @see \ref desktop-handles.h for desktop macros. + */ +class SPDesktop : public Inkscape::UI::View::View +{ +public: + Inkscape::UI::Dialog::DialogManager *_dlg_mgr; + SPNamedView *namedview; + SPCanvas *canvas; + Inkscape::LayerModel *layers; + /// current selection; will never generally be NULL + Inkscape::Selection *selection; + Inkscape::UI::Tools::ToolBase *event_context; + Inkscape::LayerManager *layer_manager; + Inkscape::EventLog *event_log; + DocumentInterface *dbus_document_interface; + Inkscape::Display::TemporaryItemList *temporary_item_list; + Inkscape::Display::SnapIndicator *snapindicator; + /// Stored settings for print dialogue + Inkscape::UI::Dialog::PrinterSettings printer_settings; + + Inkscape::UI::Tools::ToolBase* getEventContext() const; + Inkscape::Selection* getSelection() const; + SPDocument* getDocument() const; + SPCanvas* getCanvas() const; + SPCanvasItem* getAcetate() const; + SPCanvasGroup* getMain() const; + SPCanvasGroup* getGridGroup() const; + SPCanvasGroup* getGuides() const; + SPCanvasItem* getDrawing() const; + SPCanvasGroup* getSketch() const; + SPCanvasGroup* getControls() const; + SPCanvasGroup* getTempGroup() const; + Inkscape::MessageStack* getMessageStack() const; + SPNamedView* getNamedView() const; + + SPCanvasItem *acetate; + SPCanvasGroup *main; + SPCanvasGroup *gridgroup; + SPCanvasGroup *guides; + SPCanvasItem *drawing; + SPCanvasGroup *sketch; + SPCanvasGroup *controls; + SPCanvasGroup *tempgroup; ///< contains temporary canvas items + SPCanvasItem *page; ///< page background + SPCanvasItem *page_border; ///< page border + SPCanvasItem *canvas_rotate; ///< quickly show canvas rotation + SPCanvasItem *canvas_debug; ///< shows tiling + SPCSSAttr *current; ///< current style + bool _focusMode; ///< Whether we're focused working or general working + + unsigned int dkey; + unsigned int number; + guint window_state; + unsigned int interaction_disabled_counter; + bool waiting_cursor; + bool showing_dialogs; + bool rotation_locked; + /// \todo fixme: This has to be implemented in different way */ + guint guides_active : 1; + + // storage for selected dragger used by GrDrag as it's + // created and deleted by tools + SPItem *gr_item; + GrPointType gr_point_type; + guint gr_point_i; + Inkscape::PaintTarget gr_fill_or_stroke; + + Glib::ustring _reconstruction_old_layer_id; + + sigc::signal<void, sp_verb_t> _tool_changed; + sigc::signal<void, unsigned int, bool> _menu_update; + sigc::signal<void, SPObject *> _layer_changed_signal; + sigc::signal<bool, const SPCSSAttr *>::accumulated<StopOnTrue> _set_style_signal; + sigc::signal<int, SPStyle *, int>::accumulated<StopOnNonZero> _query_style_signal; + + /// Emitted when the zoom factor changes (not emitted when scrolling). + /// The parameter is the new zoom factor + sigc::signal<void, double> signal_zoom_changed; + + sigc::connection connectDestroy(const sigc::slot<void, SPDesktop*> &slot) + { + return _destroy_signal.connect(slot); + } + + sigc::connection connectDocumentReplaced (const sigc::slot<void,SPDesktop*,SPDocument*> & slot) + { + return _document_replaced_signal.connect (slot); + } + + sigc::connection connectEventContextChanged (const sigc::slot<void,SPDesktop*,Inkscape::UI::Tools::ToolBase*> & slot) + { + return _event_context_changed_signal.connect (slot); + } + sigc::connection connectSetStyle (const sigc::slot<bool, const SPCSSAttr *> & slot) + { + return _set_style_signal.connect (slot); + } + sigc::connection connectQueryStyle (const sigc::slot<int, SPStyle *, int> & slot) + { + return _query_style_signal.connect (slot); + } + // subselection is some sort of selection which is specific to the tool, such as a handle in gradient tool, or a text selection + sigc::connection connectToolSubselectionChanged(const sigc::slot<void, gpointer> & slot) { + return _tool_subselection_changed.connect(slot); + } + void emitToolSubselectionChanged(gpointer data); + sigc::connection connectCurrentLayerChanged(const sigc::slot<void, SPObject *> & slot) { + return _layer_changed_signal.connect(slot); + } + + /** + * Return new desktop object. + * \pre namedview != NULL. + * \pre canvas != NULL. + */ + SPDesktop(); + + void init (SPNamedView* nv, SPCanvas* canvas, Inkscape::UI::View::EditWidgetInterface *widget); + ~SPDesktop() override; + void destroy(); + + Inkscape::MessageContext *guidesMessageContext() const { + return _guides_message_context.get(); + } + + Inkscape::Display::TemporaryItem * add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom = true); + void remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem); + + void redrawDesktop(); + + void _setDisplayMode(Inkscape::RenderMode mode); + bool setDisplayModeNormal() + { + _setDisplayMode(Inkscape::RENDERMODE_NORMAL); + return true; + } + bool setDisplayModeNoFilters() + { + _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS); + return true; + } + bool setDisplayModeOutline() + { + _setDisplayMode(Inkscape::RENDERMODE_OUTLINE); + return true; + } + bool setDisplayModeVisibleHairlines() + { + _setDisplayMode(Inkscape::RENDERMODE_VISIBLE_HAIRLINES); + return true; + } + bool displayModeToggle(); + Inkscape::RenderMode _display_mode; + Inkscape::RenderMode getMode() const { return _display_mode; } + + void _setDisplayColorMode(Inkscape::ColorMode mode); + bool setDisplayColorModeNormal() + { + _setDisplayColorMode(Inkscape::COLORMODE_NORMAL); + return true; + } + bool setDisplayColorModeGrayscale() + { + _setDisplayColorMode(Inkscape::COLORMODE_GRAYSCALE); + return true; + } +// void setDisplayColorModePrintColorsPreview() { +// _setDisplayColorMode(Inkscape::COLORMODE_PRINT_COLORS_PREVIEW); +// } + bool displayColorModeToggle(); + Inkscape::ColorMode _display_color_mode; + Inkscape::ColorMode getColorMode() const { return _display_color_mode; } + + Inkscape::UI::Widget::Dock* getDock(); + + void set_active (bool new_active); + + // Could make all callers use this->layers instead of passing calls through? + SPObject *currentRoot() const; + SPObject *currentLayer() const; + void setCurrentLayer(SPObject *object); + void toggleLayerSolo(SPObject *object); + void toggleHideAllLayers(bool hide); + void toggleLockAllLayers(bool lock); + void toggleLockOtherLayers(SPObject *object); + bool isLayer(SPObject *object) const; + + bool isWithinViewport(SPItem *item) const; + bool itemIsHidden(SPItem const *item) const; + + void activate_guides (bool activate); + void change_document (SPDocument *document); + + + void setEventContext(const std::string& toolName); + + void set_coordinate_status (Geom::Point p); + SPItem *getItemFromListAtPointBottom(const std::vector<SPItem*> &list, Geom::Point const &p) const; + SPItem *getItemAtPoint(Geom::Point const &p, bool into_groups, SPItem *upto = nullptr) const; + SPItem *getGroupAtPoint(Geom::Point const &p) const; + Geom::Point point(bool outside_canvas = false) const; + + void prev_transform(); + void next_transform(); + void clear_transform_history(); + + void set_display_area (bool log = true); + void set_display_area (Geom::Point const &c, Geom::Point const &w, bool log = true); + void set_display_area (Geom::Rect const &a, Geom::Coord border, bool log = true); + Geom::Rect get_display_area(bool use_integer_viewbox = false) const; + + void zoom_absolute_keep_point (Geom::Point const &c, double const zoom); + void zoom_relative_keep_point (Geom::Point const &c, double const zoom); + void zoom_absolute_center_point (Geom::Point const &c, double const zoom); + void zoom_relative_center_point (Geom::Point const &c, double const zoom); + + void zoom_page(); + void zoom_page_width(); + void zoom_drawing(); + void zoom_selection(); + void zoom_center_page(); + + double current_zoom() const { return _current_affine.getZoom(); } + + void zoom_quick(bool enable = true); + /** \brief Returns whether the desktop is in quick zoom mode or not */ + bool quick_zoomed() { return _quick_zoom_enabled; } + + void toggle_rotation_lock() { rotation_locked = !rotation_locked; } + bool get_rotation_lock() const { return rotation_locked; } + + void zoom_grab_focus(); + + void rotate_absolute_keep_point (Geom::Point const &c, double const rotate); + void rotate_relative_keep_point (Geom::Point const &c, double const rotate); + void rotate_absolute_center_point (Geom::Point const &c, double const rotate); + void rotate_relative_center_point (Geom::Point const &c, double const rotate); + + enum CanvasFlip { + FLIP_NONE = 0, + FLIP_HORIZONTAL = 1, + FLIP_VERTICAL = 2 + }; + void flip_absolute_keep_point (Geom::Point const &c, CanvasFlip flip); + void flip_relative_keep_point (Geom::Point const &c, CanvasFlip flip); + void flip_absolute_center_point (Geom::Point const &c, CanvasFlip flip); + void flip_relative_center_point (Geom::Point const &c, CanvasFlip flip); + bool is_flipped (CanvasFlip flip); + + double current_rotation() const { return _current_affine.getRotation(); } + + void scroll_absolute (Geom::Point const &point, bool is_scrolling = false); + void scroll_relative (Geom::Point const &delta, bool is_scrolling = false); + void scroll_relative_in_svg_coords (double dx, double dy, bool is_scrolling = false); + bool scroll_to_point (Geom::Point const &s_dt, gdouble autoscrollspeed = 0); + + void getWindowGeometry (gint &x, gint &y, gint &w, gint &h); + void setWindowPosition (Geom::Point p); + void setWindowSize (gint w, gint h); + void setWindowTransient (void* p, int transient_policy=1); + Gtk::Window* getToplevel(); // To be removed in favor of getInkscapeWindow + InkscapeWindow* getInkscapeWindow(); + void presentWindow(); + bool showInfoDialog( Glib::ustring const &message ); + bool warnDialog (Glib::ustring const &text); + void toggleRulers(); + void toggleScrollbars(); + void layoutWidget(); + void destroyWidget(); + void setToolboxFocusTo (gchar const* label); + Gtk::Toolbar* get_toolbar_by_name(const Glib::ustring& name); + void setToolboxAdjustmentValue (gchar const* id, double val); + bool isToolboxButtonActive (gchar const *id); + void updateNow(); + void updateCanvasNow(); + void storeDesktopPosition(); + + void enableInteraction(); + void disableInteraction(); + + void setWaitingCursor(); + void clearWaitingCursor(); + bool isWaitingCursor() const { return waiting_cursor; }; + + void toggleGuidesLock(); + + void toggleColorProfAdjust(); + bool colorProfAdjustEnabled(); + + void toggleGrids(); + void toggleSplitMode(); + void toggleXRay(); + bool splitMode() const { return _split_canvas; }; + bool xrayMode() const { return _xray; }; + void toggleSnapGlobal(); + bool gridsEnabled() const { return grids_visible; }; + void showGrids(bool show, bool dirty_document = true); + void toggleToolbar(gchar const *toolbar_name, unsigned int verbenum); + + bool is_iconified(); + bool is_darktheme(); + bool is_maximized(); + bool is_fullscreen(); + bool is_focusMode(); + + void iconify(); + void maximize(); + void fullscreen(); + void focusMode(bool mode = true); + /** + * Reopen any dialogs that were open when inkscape last shutdown + */ + void show_dialogs(); + + // TODO return const ref instead of copy + Geom::Affine w2d() const; //transformation from window to desktop coordinates (zoom/rotate). + Geom::Point w2d(Geom::Point const &p) const; + /// Transformation from desktop to window coordinates + Geom::Affine d2w() const { return _current_affine.d2w(); } + Geom::Point d2w(Geom::Point const &p) const; + const Geom::Affine& doc2dt() const; + Geom::Affine dt2doc() const; + Geom::Point doc2dt(Geom::Point const &p) const; + Geom::Point dt2doc(Geom::Point const &p) const; + + bool is_yaxisdown() const { return doc2dt()[3] > 0; } + double yaxisdir() const { return doc2dt()[3]; } + + void setDocument (SPDocument* doc) override; + bool shutdown() override; + + virtual bool onDeleteUI (GdkEventAny*); + virtual bool onWindowStateEvent (GdkEventWindowState* event); + + void applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text); + +private: + GtkGesture *zoomgesture = nullptr; + + Inkscape::UI::View::EditWidgetInterface *_widget; + std::unique_ptr<Inkscape::MessageContext> _guides_message_context; + bool _active; + + // This simple class ensures that _w2d is always in sync with _rotation and _scale + // We keep rotation and scale separate to avoid having to extract them from the affine. + // With offset, this describes fully how to map the drawing to the window. + // Future: merge offset as a translation in w2d. + class DesktopAffine { + public: + Geom::Affine w2d() const { return _w2d; }; + Geom::Affine d2w() const { return _d2w; }; + + void setScale( Geom::Scale scale ) { + _scale = scale; + _update(); + } + void addScale( Geom::Scale scale) { + _scale *= scale; + _update(); + } + + void setRotate( Geom::Rotate rotate ) { + _rotate = rotate; + _update(); + } + void setRotate( double rotate ) { + _rotate = Geom::Rotate( rotate ); + _update(); + } + void addRotate( Geom::Rotate rotate ) { + _rotate *= rotate; + _update(); + } + void addRotate( double rotate ) { + _rotate *= Geom::Rotate( rotate ); + _update(); + } + + void setFlip( CanvasFlip flip ) { + _flip = Geom::Scale(); + addFlip( flip ); + } + + bool isFlipped( CanvasFlip flip ) { + if ((flip & FLIP_HORIZONTAL) && Geom::are_near(_flip[0], -1)) { + return true; + } + if ((flip & FLIP_VERTICAL) && Geom::are_near(_flip[1], -1)) { + return true; + } + return false; + } + + void addFlip( CanvasFlip flip ) { + if (flip & FLIP_HORIZONTAL) { + _flip *= Geom::Scale(-1.0, 1.0); + } + if (flip & FLIP_VERTICAL) { + _flip *= Geom::Scale(1.0, -1.0); + } + _update(); + } + + double getZoom() const { + return _d2w.descrim(); + } + + double getRotation() const { + return _rotate.angle(); + } + + void setOffset( Geom::Point offset ) { + _offset = offset; + } + void addOffset( Geom::Point offset ) { + _offset += offset; + } + Geom::Point getOffset() { + return _offset; + } + + private: + void _update() { + _d2w = _scale * _rotate * _flip; + _w2d = _d2w.inverse(); + } + Geom::Affine _w2d; // Window to desktop + Geom::Affine _d2w; // Desktop to window + Geom::Rotate _rotate; // Rotate part of _w2d + Geom::Scale _scale; // Scale part of _w2d, holds y-axis direction + Geom::Scale _flip; // Flip part of _w2d + Geom::Point _offset; // Point on canvas to align to (0,0) of window + }; + + DesktopAffine _current_affine; + std::list<DesktopAffine> transforms_past; + std::list<DesktopAffine> transforms_future; + bool _split_canvas; + bool _xray; + bool _quick_zoom_enabled; ///< Signifies that currently we're in quick zoom mode + DesktopAffine _quick_zoom_affine; ///< The transform of the screen before quick zoom + + /* + * Allow redrawing or refreshing if preferences change + */ + class DesktopPrefObserver : public Inkscape::Preferences::Observer { + public: + DesktopPrefObserver(SPDesktop *desktop, Glib::ustring const &path) + : Inkscape::Preferences::Observer(path) + , _desktop(desktop) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->addObserver(*this); + } + private: + void notify(Inkscape::Preferences::Entry const &) override { + _desktop->redrawDesktop(); + } + SPDesktop *_desktop; + }; + + DesktopPrefObserver _image_render_observer; + + bool grids_visible; /* don't set this variable directly, use the method below */ + void set_grids_visible(bool visible); + + sigc::signal<void, SPDesktop*> _destroy_signal; + sigc::signal<void,SPDesktop*,SPDocument*> _document_replaced_signal; + sigc::signal<void> _activate_signal; + sigc::signal<void> _deactivate_signal; + sigc::signal<void,SPDesktop*,Inkscape::UI::Tools::ToolBase*> _event_context_changed_signal; + sigc::signal<void, gpointer> _tool_subselection_changed; + + sigc::connection _activate_connection; + sigc::connection _deactivate_connection; + sigc::connection _sel_modified_connection; + sigc::connection _sel_changed_connection; + sigc::connection _reconstruction_start_connection; + sigc::connection _reconstruction_finish_connection; + sigc::connection _commit_connection; + sigc::connection _modified_connection; + + void onResized (double, double) override; + void onRedrawRequested() override; + void onStatusMessage (Inkscape::MessageType type, gchar const *message) override; + void onDocumentURISet (gchar const* uri) override; + void onDocumentResized (double, double) override; + + static void _onActivate (SPDesktop* dt); + static void _onDeactivate (SPDesktop* dt); + static void _onSelectionModified (Inkscape::Selection *selection, guint flags, SPDesktop *dt); +}; + +#endif // SEEN_SP_DESKTOP_H + +/* + 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 : |