15#include <gdk/gdkkeysyms.h>
16#include <glibmm/i18n.h>
39template <
typename Tv,
typename Tk>
40static auto INDEX_OF(Tv
const &v, Tk
const &k) {
return std::distance(v.begin(), std::find(v.begin(), v.end(), k)); }
55 for (
int i = 0; i < 4; i++) {
87 drag_group->set_name(
"CanvasItemGroup:PagesDragShapes");
99 selectionChanged(desktop->getDocument(), nullptr);
158 rect =
page->getDesktopRect();
161 rect = *(document->preferredBounds()) * document->doc2dt();
173 if (point !=
start) {
196 if (!(state & GDK_SHIFT_MASK)) {
229 if (
auto page = pm.getSelected()) {
230 Geom::Point point = *ppointer * document->dt2doc();
241 auto delta = (point -
page->getDocumentRect().corner(side))[axis];
242 auto value = std::max(0.0, (side + 1) & 2 ? -
delta :
delta);
243 auto scale = document->getDocumentScale()[axis];
246 page->setMarginSide(side, value /
scale, confine);
251 g_warning(
"Can't add margin, pages not enabled correctly!");
268 if (event.num_press == 1 && event.button == 1) {
269 mouse_is_pressed = true;
270 drag_origin_w = event.pos;
271 drag_origin_dt = _desktop->w2d(drag_origin_w);
273 if (auto page = pageUnder(drag_origin_dt, false)) {
275 _desktop->getDocument()->getPageManager().selectPage(page);
276 set_cursor(
"page-dragging.svg");
283 }
else if (event.button == 3) {
284 menu_popup(event, page_manager.getSelected());
289 auto point_w =
event.pos;
290 auto point_dt = _desktop->w2d(point_w);
291 bool snap = !(
event.modifiers & GDK_SHIFT_MASK);
293 if (event.modifiers & GDK_BUTTON1_MASK) {
294 if (!mouse_is_pressed) {
296 drag_origin_w = point_w;
297 drag_origin_dt = point_dt;
298 mouse_is_pressed =
true;
301 if (dragging_item || dragging_viewbox) {
307 addDragShapes(dragging_item, tr);
308 _desktop->getCanvas()->enable_autoscroll();
309 }
else if (on_screen_rect) {
311 point_dt = getSnappedResizePoint(point_dt, event.modifiers, drag_origin_dt);
312 on_screen_rect =
Geom::Rect(drag_origin_dt, point_dt);
313 }
else if (
Geom::distance(drag_origin_w, point_w) < drag_tolerance) {
316 }
else if (
auto page = pageUnder(drag_origin_dt)) {
319 dragging_item =
page;
320 page_manager.selectPage(
page);
323 }
else if (viewboxUnder(drag_origin_dt)) {
325 dragging_viewbox =
true;
328 dragging_item =
nullptr;
329 on_screen_rect =
Geom::Rect(drag_origin_dt, drag_origin_dt);
330 set_cursor(
"page-draw.svg");
333 mouse_is_pressed =
false;
334 drag_origin_dt = point_dt;
337 [&] (ButtonReleaseEvent
const &event) {
338 if (event.button != 1) {
341 auto point_w =
event.pos;
342 auto point_dt = _desktop->w2d(point_w);
343 bool snap = !(
event.modifiers & GDK_SHIFT_MASK);
344 auto document = _desktop->getDocument();
346 if (dragging_viewbox || dragging_item) {
347 if (dragging_viewbox || dragging_item->isViewportPage()) {
349 auto page_items = page_manager.getOverlappingItems(_desktop, dragging_item);
350 auto rect = document->preferredBounds();
351 auto affine = moveTo(point_dt, snap);
352 document->fitToRect(*rect * affine * document->dt2doc(),
false);
355 dragging_item->movePage(affine,
false);
356 dragging_item->setDesktopRect(*rect);
359 if (page_manager.move_objects()) {
364 dragging_item->movePage(moveTo(point_dt, snap), page_manager.move_objects());
367 }
else if (on_screen_rect) {
369 page_manager.selectPage(page_manager.newDesktopPage(*on_screen_rect));
372 mouse_is_pressed =
false;
373 drag_origin_dt = point_dt;
377 _desktop->getSnapIndicator()->remove_snaptarget();
379 [&] (KeyPressEvent
const &event) {
380 if (event.keyval == GDK_KEY_Escape) {
381 mouse_is_pressed =
false;
384 if (event.keyval == GDK_KEY_Delete) {
385 page_manager.deletePage(page_manager.move_objects());
391 [&] (CanvasEvent
const &event) {}
395 if (!mouse_is_pressed && (dragging_item || on_screen_rect || dragging_viewbox)) {
396 dragging_viewbox =
false;
397 dragging_item =
nullptr;
400 visual_box->set_visible(
false);
402 }
else if (on_screen_rect) {
403 visual_box->set_visible(
true);
404 visual_box->set_rect(*on_screen_rect);
407 if (!mouse_is_pressed) {
408 if (pageUnder(drag_origin_dt) || viewboxUnder(drag_origin_dt)) {
410 set_cursor(
"page-mouseover.svg");
412 set_cursor(
"page-draw.svg");
421 auto &page_manager = _desktop->getDocument()->getPageManager();
425 drag_origin_dt = _desktop->w2d(event.pos);
426 page = pageUnder(drag_origin_dt);
431 ToolBase::menu_popup(event,
page);
434void PagesTool::switching_away(std::string
const &)
436 if (_selection_state) {
437 _desktop->getSelection()->setState(*_selection_state);
438 _selection_state.reset();
447 _bbox_points.clear();
461 snap_manager.
setup(_desktop,
true, dragging_item);
493 auto doc = _desktop->getDocument();
498 auto doc_rect = doc->preferredBounds();
502 for (
auto &
item : doc->getPageManager().getOverlappingItems(_desktop,
page)) {
504 addDragShape(
item, tr);
526 shape->set_stroke(0x00ff007f);
528 drag_shapes.push_back(shape);
534void PagesTool::clearDragShapes()
536 for (
auto &shape : drag_shapes) {
547 auto &pm = _desktop->getDocument()->getPageManager();
550 if (
auto selected = pm.getSelected()) {
551 if (retain_selected && selected->getSensitiveRect().contains(pt)) {
556 return pm.findPageAt(pt);
565 if (
auto document = _desktop->getDocument()) {
566 auto rect = document->preferredBounds();
567 rect->expandBy(-0.1);
568 return !document->getPageManager().hasPages() && rect.contains(pt);
575 _selector_changed_connection.disconnect();
578 _selector_changed_connection =
580 selectionChanged(doc,
page);
582 selectionChanged(doc, page_manager.getSelected());
584 selectionChanged(doc,
nullptr);
590 if (_page_modified_connection) {
591 _page_modified_connection.disconnect();
592 for (
auto knot : resize_knots) {
595 for (
auto knot : margin_knots) {
602 for (
auto &possible : _desktop->getDocument()->getPageManager().getPages()) {
603 if (highlight_item == possible) {
604 highlight_item->setSelected(
false);
607 highlight_item =
page;
610 _page_modified_connection =
page->connectModified(sigc::mem_fun(*
this, &PagesTool::pageModified));
611 page->setSelected(
true);
612 pageModified(
page, 0);
625void PagesTool::pageModified(
SPObject *
object, guint )
627 if (
auto page = cast<SPPage>(
object)) {
628 resizeKnotSet(
page->getDesktopRect());
629 marginKnotSet(
page->getDocumentMargin());
3x3 matrix representing an affine transformation.
void setMin(CPoint const &p)
Set the upper left point of the rectangle.
void setMax(CPoint const &p)
Set the lower right point of the rectangle.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Sequence of contiguous curves, aka spline.
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void maybeDone(SPDocument *document, const gchar *keyconst, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
A class to represent ways functionality is driven by shift modifiers.
bool active(int button_state) const
Test if this modifier is currently active.
static Modifier * get(Type index)
A function to turn an enum index into a modifier object.
void clear()
Unselects all selected objects.
sigc::connection connectPageSelected(const sigc::slot< void(SPPage *)> &slot)
void enablePages()
Enables multi page support by turning the document viewBox into the first page.
SPPage * getSelected() const
Preference storage class.
static Preferences * get()
Access the singleton Preferences object.
int getIntLimited(Glib::ustring const &pref_path, int def=0, int min=INT_MIN, int max=INT_MAX)
Retrieve a limited integer.
Geom::Point getTranslationSnapped()
SelectionState getState()
Returns the current selection state including selected objects and nodes.
Class to store data for points which are snap candidates, either as a source or as a target.
void addOrigin(Geom::Point pt)
void clearTargetMask(int enabled=-1)
Clear the target mask, this should be done in a four step process.
void setTargetMask(Inkscape::SnapTargetType const target, int enabled=1)
Set a target mask, which will turn off all other targets except the masked ones.
Class describing the result of an attempt to snap.
Geom::Point getPoint() const
bool marginKnotMoved(SPKnot *knot, Geom::Point *point, guint state)
Geom::Point drag_origin_dt
void resizeKnotFinished(SPKnot *knot, guint state)
void resizeKnotSet(Geom::Rect rect)
bool root_handler(CanvasEvent const &event) override
Geom::Point getSnappedResizePoint(Geom::Point point, guint state, Geom::Point origin, SPObject *target=nullptr)
Resize snapping allows knot and tool point snapping consistency.
CanvasItemPtr< CanvasItemRect > visual_box
bool viewboxUnder(Geom::Point pt)
Returns true if the document contains no pages AND the point is within the document viewbox.
std::optional< Geom::Rect > on_screen_rect
On-screen rectangle, in desktop coordinates.
PagesTool(SPDesktop *desktop)
void marginKnotSet(Geom::Rect margin_rect)
static Geom::Point middleOfSide(int side, const Geom::Rect &rect)
void resizeKnotMoved(SPKnot *knot, Geom::Point const &ppointer, guint state)
void menu_popup(CanvasEvent const &event, SPObject *obj=nullptr) override
Create popup menu and tell Gtk to show it.
std::unique_ptr< Inkscape::SelectionState > _selection_state
std::vector< Inkscape::CanvasItemBpath * > drag_shapes
std::vector< SPKnot * > margin_knots
sigc::connection _zoom_connection
void connectDocument(SPDocument *doc)
std::vector< SPKnot * > resize_knots
sigc::connection _doc_replaced_connection
CanvasItemPtr< CanvasItemGroup > drag_group
void marginKnotFinished(SPKnot *knot, guint state)
To do: update description of desktop.
Inkscape::UI::Widget::Canvas * getCanvas() const
Inkscape::CanvasItemGroup * getCanvasControls() const
sigc::connection connectDocumentReplaced(F &&slot)
SPDocument * getDocument() const
Inkscape::CanvasItemGroup * getCanvasTemp() const
SPNamedView * getNamedView() const
Inkscape::Selection * getSelection() const
sigc::signal< void(double)> signal_zoom_changed
Emitted when the zoom factor changes (not emitted when scrolling).
Geom::Affine const & doc2dt() const
Typed SVG document implementation.
Geom::OptRect preferredBounds() const
sigc::connection connectModified(ModifiedSignal::slot_type slot)
Inkscape::PageManager & getPageManager()
Base class for visual SVG elements.
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Desktop-bound visual control object.
Geom::Point drag_origin
Origin of drag.
void setPosition(Geom::Point const &p, unsigned int state)
Move knot to new position and emits "moved" signal.
Geom::Point position() const
Returns position of knot.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Geom::Rect getDesktopRect() const
Get the rectangle of the page, in desktop units.
static void moveItems(Geom::Affine translate, std::vector< SPItem * > const &objects)
Move the given items by the given translation in document units.
Class to coordinate snapping operations.
void setup(SPDesktop const *desktop, bool snapindicator=true, SPObject const *item_to_ignore=nullptr, std::vector< Inkscape::SnapCandidatePoint > *unselected_nodes=nullptr)
Convenience shortcut when there is only one item to ignore.
SPNamedView const * getNamedView() const
void snapTransformed(std::vector< Inkscape::SnapCandidatePoint > const &points, Geom::Point const &pointer, Inkscape::PureTransform &transform)
Method for snapping sets of points while they are being transformed.
Inkscape::SnapPreferences & snapprefs
Inkscape::SnappedPoint freeSnap(Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap=Geom::OptRect(), bool to_path_only=false) const
Try to snap a point to grids, guides or objects.
Editable view implementation.
TODO: insert short description here.
Macro for icon names used in Inkscape.
@ SP_KNOT_STATE_MOUSEOVER
Declarations for SPKnot: Desktop-bound visual control object.
Angle distance(Angle const &a, Angle const &b)
Point middle_point(LineSegment const &_segment)
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
@ SNAPTARGET_ALIGNMENT_PAGE_EDGE_CENTER
@ SNAPTARGET_GRID_INTERSECTION
@ SNAPTARGET_ALIGNMENT_CATEGORY
@ SNAPTARGET_PAGE_EDGE_CENTER
@ SNAPTARGET_PAGE_EDGE_CORNER
@ SNAPTARGET_GUIDE_INTERSECTION
@ SNAPTARGET_ALIGNMENT_PAGE_EDGE_CORNER
@ CANVAS_ITEM_CTRL_TYPE_MARGIN
@ CANVAS_ITEM_CTRL_TYPE_SIZER
void getBBoxPoints(Geom::OptRect const bbox, std::vector< SnapCandidatePoint > *points, bool const isTarget, bool const corners, bool const edges, bool const midpoint)
Default version of the getBBoxPoints with default corner source types.
static auto INDEX_OF(Tv const &v, Tk const &k)
Geom::PathVector * item_to_outline(SPItem const *item, bool exclude_markers)
Returns a pathvector that is the outline of the stroked item, with markers.
Two related object to path operations:
Provides a class that shows a temporary indicator on the canvas of where the snap was,...
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
Movement of the mouse pointer.