diff --git a/po/POTFILES.src.in b/po/POTFILES.src.in
index fbcba2319c7f83f01ef093fd002a1093697a7237..5b4210d2c7701bf848ac0244ce64924e93163427 100644
--- a/po/POTFILES.src.in
+++ b/po/POTFILES.src.in
@@ -267,6 +267,7 @@ ${_build_dir}/share/templates/templates.h
../src/rdf.cpp
../src/selection-chemistry.cpp
../src/selection-describer.cpp
+../src/selection.cpp
../src/seltrans-handles.cpp
../src/seltrans.cpp
../src/text-chemistry.cpp
diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml
index a411da5f3d1e73395b8b9d7eef7c4a22af36de00..00fdbfaedb96142a2bf35a8e0159826b00e99079 100644
--- a/share/keys/inkscape.xml
+++ b/share/keys/inkscape.xml
@@ -284,8 +284,6 @@ override) the bindings in the main default.xml.
-
-
@@ -302,12 +300,20 @@ override) the bindings in the main default.xml.
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/share/keys/macromedia-freehand-mx.xml b/share/keys/macromedia-freehand-mx.xml
index 53b1a57233205a77583af90e1e0f9d33a44bfb54..b6610201bb09532dbdf4c17864ac5185840ae92e 100644
--- a/share/keys/macromedia-freehand-mx.xml
+++ b/share/keys/macromedia-freehand-mx.xml
@@ -184,8 +184,6 @@ File, Edit, View, Modify, Text, Xtras, Window, Help.
-
-
diff --git a/share/keys/right-handed-illustration.xml b/share/keys/right-handed-illustration.xml
index 2f9a8d9bf51a2f93064629be5aba011c3f7a6a9b..ce438b1e1e474a56a9519e7d2931d1665fa96262 100644
--- a/share/keys/right-handed-illustration.xml
+++ b/share/keys/right-handed-illustration.xml
@@ -164,9 +164,6 @@ Future improvements:
-
-
-
diff --git a/share/keys/xara.xml b/share/keys/xara.xml
index ee8224821e321d688e3a72ca861bfae399adfd01..95311dcf3e66e85042b5d6f0a6b05a27eb1df3ca 100644
--- a/share/keys/xara.xml
+++ b/share/keys/xara.xml
@@ -171,8 +171,6 @@ Hom/end keys-select minimum or maximum feather values
-
-
diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp
index 51911e319663e0e810362067d930f6f7961b8371..b42729b23be82133d4e1a0f2b02cb27c4a4d038d 100644
--- a/src/actions/actions-object.cpp
+++ b/src/actions/actions-object.cpp
@@ -262,7 +262,7 @@ object_rotate_90_cw(InkscapeApplication *app)
// Object Rotate 90
auto desktop = selection->desktop();
- selection->rotate((!desktop || desktop->is_yaxisdown()) ? 90 : -90);
+ selection->rotateAnchored((!desktop || desktop->is_yaxisdown()) ? 90 : -90);
}
void
@@ -272,7 +272,7 @@ object_rotate_90_ccw(InkscapeApplication *app)
// Object Rotate 90 CCW
auto desktop = selection->desktop();
- selection->rotate((!desktop || desktop->is_yaxisdown()) ? -90 : 90);
+ selection->rotateAnchored((!desktop || desktop->is_yaxisdown()) ? -90 : 90);
}
void
@@ -294,7 +294,7 @@ object_flip_horizontal(InkscapeApplication *app)
}
// Object Flip Horizontal
- selection->setScaleRelative(center, Geom::Scale(-1.0, 1.0));
+ selection->scaleRelative(center, Geom::Scale(-1.0, 1.0));
Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip horizontally"), INKSCAPE_ICON("object-flip-horizontal"));
}
@@ -317,7 +317,7 @@ object_flip_vertical(InkscapeApplication *app)
}
// Object Flip Vertical
- selection->setScaleRelative(center, Geom::Scale(1.0, -1.0));
+ selection->scaleRelative(center, Geom::Scale(1.0, -1.0));
Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip vertically"), INKSCAPE_ICON("object-flip-vertical"));
}
@@ -436,6 +436,7 @@ add_actions_object(InkscapeApplication* app)
gapp->add_action( "object-set-inverse-mask", sigc::bind(sigc::ptr_fun(&object_mask_set_inverse), app));
gapp->add_action( "object-release-mask", sigc::bind(sigc::ptr_fun(&object_mask_release), app));
+ // Depricated, see app.transform-rotate(90)
gapp->add_action( "object-rotate-90-cw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app));
gapp->add_action( "object-rotate-90-ccw", sigc::bind(sigc::ptr_fun(&object_rotate_90_ccw), app));
gapp->add_action( "object-flip-horizontal", sigc::bind(sigc::ptr_fun(&object_flip_horizontal), app));
diff --git a/src/actions/actions-transform.cpp b/src/actions/actions-transform.cpp
index 3f0ca733ad1c49155706f5c95205d32121288bd5..9b02b2db2791d5989eddaba395499a90b931a26b 100644
--- a/src/actions/actions-transform.cpp
+++ b/src/actions/actions-transform.cpp
@@ -14,8 +14,10 @@
#include
#include "actions-helper.h"
+#include "desktop.h"
#include "document-undo.h"
#include "inkscape-application.h"
+#include "inkscape-window.h"
#include "preferences.h"
#include "selection.h" // Selection
#include "page-manager.h"
@@ -50,53 +52,62 @@ transform_translate(const Glib::VariantBase& value, InkscapeApplication *app)
}
void
-transform_rotate(const Glib::VariantBase& value, InkscapeApplication *app)
+transform_scale(const Glib::VariantBase& value, InkscapeApplication *app)
{
- Glib::Variant d = Glib::VariantBase::cast_dynamic >(value);
- auto selection = app->get_active_selection();
-
- selection->rotate(d.get());
+ auto scale = (Glib::VariantBase::cast_dynamic>(value)).get();
+ app->get_active_selection()->scaleAnchored(scale, false);
+}
- // Needed to update repr (is this the best way?).
- Inkscape::DocumentUndo::done(app->get_active_document(), "ActionTransformRotate", "");
+void
+transform_grow(const Glib::VariantBase& value, InkscapeApplication *app)
+{
+ auto scale = (Glib::VariantBase::cast_dynamic>(value)).get();
+ app->get_active_selection()->scaleAnchored(scale);
}
void
-transform_scale(const Glib::VariantBase& value, InkscapeApplication *app)
+transform_grow_step(const Glib::VariantBase& value, InkscapeApplication *app)
{
- Glib::Variant d = Glib::VariantBase::cast_dynamic >(value);
- auto selection = app->get_active_selection();
- selection->scale(d.get());
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- // Needed to update repr (is this the best way?).
- Inkscape::DocumentUndo::done(app->get_active_document(), "ActionTransformScale", "");
+ auto scale = (Glib::VariantBase::cast_dynamic>(value)).get();
+ app->get_active_selection()->scaleAnchored(scale * prefs->getDoubleLimited("/options/defaultscale/value", 2, 0, 1000));
}
void
-transform_grow(const Glib::VariantBase& value, InkscapeApplication *app)
+transform_grow_screen(const Glib::VariantBase& value, InkscapeWindow *win)
{
- Glib::Variant d = Glib::VariantBase::cast_dynamic >(value);
- auto selection = app->get_active_selection();
- selection->scaleGrow(d.get());
+ auto scale = (Glib::VariantBase::cast_dynamic>(value)).get();
+ auto desktop = win->get_desktop();
+ desktop->getSelection()->scaleAnchored(scale / desktop->current_zoom());
}
void
-transform_grow_step(const Glib::VariantBase& value, InkscapeApplication *app)
+transform_rotate(const Glib::VariantBase& value, InkscapeApplication *app)
+{
+ auto angle = (Glib::VariantBase::cast_dynamic>(value)).get();
+ app->get_active_selection()->rotateAnchored(angle);
+}
+
+void
+transform_rotate_step(const Glib::VariantBase& value, InkscapeApplication *app)
{
- Glib::Variant d = Glib::VariantBase::cast_dynamic >(value);
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- auto selection = app->get_active_selection();
- selection->scaleGrow(d.get() * prefs->getDoubleLimited("/options/defaultscale/value", 2, 0, 1000));
+
+ auto angle = (Glib::VariantBase::cast_dynamic>(value)).get();
+ app->get_active_selection()->rotateAnchored(angle / prefs->getInt("/options/rotationsnapsperpi/value", 12));
}
void
-transform_grow_screen(const Glib::VariantBase& value, InkscapeApplication *app)
+transform_rotate_screen(const Glib::VariantBase& value, InkscapeWindow *win)
{
- Glib::Variant d = Glib::VariantBase::cast_dynamic >(value);
- auto selection = app->get_active_selection();
- selection->scaleScreen(d.get());
+ auto angle = (Glib::VariantBase::cast_dynamic>(value)).get();
+ auto desktop = win->get_desktop();
+
+ desktop->getSelection()->rotateAnchored(angle, desktop->current_zoom());
}
+
void
transform_remove(InkscapeApplication *app)
{
@@ -123,7 +134,7 @@ void page_rotate(const Glib::VariantBase& value, InkscapeApplication *app)
Inkscape::DocumentUndo::done(document, "Rotate Page", INKSCAPE_ICON("tool-pages"));
}
-// SHOULD REALLY BE DOC LEVEL ACTIONS
+// SHOULD REALLY BE SELECTION LEVEL ACTIONS
std::vector> raw_data_transform = {
// clang-format off
{"app.transform-translate", N_("Translate"), "Transform", N_("Translate selected objects (dx,dy)")},
@@ -132,6 +143,10 @@ std::vector> raw_data_transform = {
{"app.transform-grow", N_("Grow/Shrink"), "Transform", N_("Grow/shrink selected objects")},
{"app.transform-grow-step", N_("Grow/Shrink Step"), "Transform", N_("Grow/shrink selected objects by multiple of step value")},
{"app.transform-grow-screen", N_("Grow/Shrink Screen"), "Transform", N_("Grow/shrink selected objects relative to zoom level")},
+ {"app.transform-rotate", N_("Rotate"), "Transform", N_("Rotate selected objects")},
+ {"app.transform-rotate-step", N_("Rotate Step"), "Transform", N_("Rotate selected objects by multiple of step value")},
+ {"app.transform-rotate-screen", N_("Rotate Screen"), "Transform", N_("Rotate selected objects relative to zoom level")},
+
{"app.transform-remove", N_("Remove Transforms"), "Transform", N_("Remove any transforms from selected objects")},
{"app.transform-reapply", N_("Reapply Transforms"), "Transform", N_("Reapply the last transformation to the selection")},
{"app.page-rotate", N_("Rotate Page 90°"), "Transform", N_("Rotate page by 90-degree rotation steps")},
@@ -154,6 +169,7 @@ std::vector> hint_data_transform =
void
add_actions_transform(InkscapeApplication* app)
{
+ // If these ever get moved to the Inkscape::Selection object, the screen and app based ones can be combined again.
Glib::VariantType Bool( Glib::VARIANT_TYPE_BOOL);
Glib::VariantType Int( Glib::VARIANT_TYPE_INT32);
Glib::VariantType Double(Glib::VARIANT_TYPE_DOUBLE);
@@ -167,7 +183,8 @@ add_actions_transform(InkscapeApplication* app)
gapp->add_action_with_parameter( "transform-scale", Double, sigc::bind(sigc::ptr_fun(&transform_scale), app));
gapp->add_action_with_parameter( "transform-grow", Double, sigc::bind(sigc::ptr_fun(&transform_grow), app));
gapp->add_action_with_parameter( "transform-grow-step", Double, sigc::bind(sigc::ptr_fun(&transform_grow_step), app));
- gapp->add_action_with_parameter( "transform-grow-screen", Double, sigc::bind(sigc::ptr_fun(&transform_grow_screen), app));
+ gapp->add_action_with_parameter( "transform-rotate", Double, sigc::bind(sigc::ptr_fun(&transform_rotate), app));
+ gapp->add_action_with_parameter( "transform-rotate-step", Double, sigc::bind(sigc::ptr_fun(&transform_rotate_step), app));
gapp->add_action( "transform-remove", sigc::bind(sigc::ptr_fun(&transform_remove), app));
gapp->add_action( "transform-reapply", sigc::bind(sigc::ptr_fun(&transform_reapply), app));
gapp->add_action_with_parameter( "page-rotate", Int, sigc::bind(sigc::ptr_fun(&page_rotate), app));
@@ -177,6 +194,16 @@ add_actions_transform(InkscapeApplication* app)
app->get_action_hint_data().add_data(hint_data_transform);
}
+void
+add_actions_transform(InkscapeWindow* win)
+{
+ Glib::VariantType Double(Glib::VARIANT_TYPE_DOUBLE);
+
+ win->add_action_with_parameter( "transform-grow-screen", Double, sigc::bind(sigc::ptr_fun(&transform_grow_screen), win));
+ win->add_action_with_parameter( "transform-rotate-screen", Double, sigc::bind(sigc::ptr_fun(&transform_rotate_screen), win));
+
+ // action data already added above by app actions.
+}
/*
Local Variables:
diff --git a/src/actions/actions-transform.h b/src/actions/actions-transform.h
index 9b1b0dc3da38a3fefdfde4585846eccb3a19d001..7f26228d1d94acbd10eddb70be7e23f32eaa692d 100644
--- a/src/actions/actions-transform.h
+++ b/src/actions/actions-transform.h
@@ -12,8 +12,10 @@
#define INK_ACTIONS_TRANSFORM_H
class InkscapeApplication;
+class InkscapeWindow;
void add_actions_transform(InkscapeApplication* app);
+void add_actions_transform(InkscapeWindow* win);
#endif // INK_ACTIONS_TRANSFORM_H
diff --git a/src/inkscape-window.cpp b/src/inkscape-window.cpp
index 4532ee1d3197646363ad40223c91836c2942a5e0..b2fb1e8fc72afdb2be1c4857d098aa581e5e7a98 100644
--- a/src/inkscape-window.cpp
+++ b/src/inkscape-window.cpp
@@ -41,6 +41,7 @@
#include "actions/actions-paths.h" // TEMP
#include "actions/actions-selection-window.h"
#include "actions/actions-tools.h"
+#include "actions/actions-transform.h"
#include "actions/actions-view-mode.h"
#include "actions/actions-view-window.h"
#include "inkscape.h"
@@ -83,12 +84,13 @@ InkscapeWindow::InkscapeWindow(SPDesktop *desktop)
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_page_tools(this); // Actions specific to pages tool and toolbar
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_transform(this); // Actions for transforming against the screen zoom
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_actions_page_tools(this); // Actions specific to pages tool and toolbar
// Add document action group to window and export to DBus.
add_document_actions();
diff --git a/src/object/object-set.h b/src/object/object-set.h
index 0b7c7b7a0476e612f13bcf8c52b620a52c41aa23..c869e5fd4cbd919ccf6f6c76e18b36a6d0717a16 100644
--- a/src/object/object-set.h
+++ b/src/object/object-set.h
@@ -477,16 +477,11 @@ public:
void removePathTransforms();
void removeTransform();
void setScaleAbsolute(double, double, double, double);
- void setScaleRelative(const Geom::Point&, const Geom::Scale&);
+ void scaleRelative(const Geom::Point&, const Geom::Scale&);
void rotateRelative(const Geom::Point&, double);
void skewRelative(const Geom::Point&, double, double);
void moveRelative(const Geom::Point &move, bool compensate = true);
void moveRelative(double dx, double dy);
- void rotate(double);
- void rotateScreen(double);
- void scaleGrow(double);
- void scaleScreen(double);
- void scale(double);
void move(double dx, double dy);
void moveScreen(double dx, double dy);
void move(double dx, double dy, bool rotated);
diff --git a/src/object/sp-marker.cpp b/src/object/sp-marker.cpp
index f38502ce28529a8cdd64501729b82cd31a2cc30f..2a28c079163d01a671243c842002fd5a799da075 100644
--- a/src/object/sp-marker.cpp
+++ b/src/object/sp-marker.cpp
@@ -635,7 +635,7 @@ void sp_marker_flip_horizontally(SPMarker* marker) {
set.addList(marker->item_list());
Geom::OptRect bbox = set.visualBounds();
if (bbox) {
- set.setScaleRelative(bbox->midpoint(), Geom::Scale(-1.0, 1.0));
+ set.scaleRelative(bbox->midpoint(), Geom::Scale(-1.0, 1.0));
if (marker->document) {
DocumentUndo::maybeDone(marker->document, "marker", _("Flip marker horizontally"), INKSCAPE_ICON("dialog-fill-and-stroke"));
}
diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp
index 8564130862f90287bb89b151e30f78121b20cf5d..cf33e11b4d380fbcdad721916782ef0ffa9a3097 100644
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -1880,7 +1880,7 @@ void ObjectSet::setScaleAbsolute(double x0, double x1,double y0, double y1)
applyAffine(final);
}
-void ObjectSet::setScaleRelative(Geom::Point const &align, Geom::Scale const &scale)
+void ObjectSet::scaleRelative(Geom::Point const &align, Geom::Scale const &scale)
{
if (isEmpty())
return;
@@ -1934,30 +1934,6 @@ void ObjectSet::moveRelative(double dx, double dy)
applyAffine(Geom::Affine(Geom::Translate(dx, dy)));
}
-void ObjectSet::rotate(gdouble const angle_degrees)
-{
- if (isEmpty())
- return;
-
- std::optional center_ = center();
- if (!center_) {
- return;
- }
- rotateRelative(*center_, angle_degrees);
-
- if (document()) {
- if (angle_degrees == 90.0) {
- DocumentUndo::done(document(), _("Rotate 90\xc2\xb0 CW"), INKSCAPE_ICON("object-rotate-right"));
- } else if (angle_degrees == -90.0) {
- DocumentUndo::done(document(), _("Rotate 90\xc2\xb0 CCW"), INKSCAPE_ICON("object-rotate-left"));
- } else {
- DocumentUndo::maybeDone(document(),
- ( ( angle_degrees > 0 )? "selector:rotate:ccw": "selector:rotate:cw" ),
- _("Rotate"), INKSCAPE_ICON("tool-pointer"));
- }
- }
-}
-
/*
* Selects all the visible items with the same fill and/or stroke color/style as the items in the current selection
*
@@ -2275,100 +2251,6 @@ std::vector sp_get_same_style(SPItem *sel, std::vector &src, S
return matches;
}
-// helper function:
-static
-Geom::Point
-cornerFarthestFrom(Geom::Rect const &r, Geom::Point const &p){
- Geom::Point m = r.midpoint();
- unsigned i = 0;
- if (p[X] < m[X]) {
- i = 1;
- }
- if (p[Y] < m[Y]) {
- i = 3 - i;
- }
- return r.corner(i);
-}
-
-/**
-\param angle the angle in "angular pixels", i.e. how many visible pixels must move the outermost point of the rotated object
-*/
-void ObjectSet::rotateScreen(double angle)
-{
- if (isEmpty()||!desktop())
- return;
-
- Geom::OptRect bbox = visualBounds();
- std::optional center_ = center();
-
- if ( !bbox || !center_ ) {
- return;
- }
-
- gdouble const zoom = desktop()->current_zoom();
- gdouble const zmove = angle / zoom;
- gdouble const r = Geom::L2(cornerFarthestFrom(*bbox, *center_) - *center_);
-
- gdouble const zangle = 180 * atan2(zmove, r) / M_PI;
-
- rotateRelative(*center_, zangle);
-
- DocumentUndo::maybeDone(document(),
- ( (angle > 0) ? "selector:rotate:ccw": "selector:rotate:cw" ),
- _("Rotate by pixels"), INKSCAPE_ICON("tool-pointer"));
-}
-
-void ObjectSet::scaleGrow(double grow)
-{
- if (isEmpty())
- return;
-
- Geom::OptRect bbox = visualBounds();
- if (!bbox) {
- return;
- }
-
- Geom::Point const center_(bbox->midpoint());
-
- // you can't scale "do nizhe pola" (below zero)
- double const max_len = bbox->maxExtent();
- if ( max_len + grow <= 1e-3 ) {
- return;
- }
-
- double const times = 1.0 + grow / max_len;
- setScaleRelative(center_, Geom::Scale(times, times));
-
- if (document()) {
- DocumentUndo::maybeDone(document(),
- ((grow > 0) ? "selector:grow:larger" : "selector:grow:smaller" ),
- ((grow > 0) ? _("Grow") : _("Shrink")), INKSCAPE_ICON("tool-pointer"));
- }
-}
-
-void ObjectSet::scaleScreen(double grow_pixels)
-{
- if(!desktop())
- return;
- scaleGrow(grow_pixels / desktop()->current_zoom());
-}
-
-void ObjectSet::scale(double times)
-{
- if (isEmpty())
- return;
-
- Geom::OptRect sel_bbox = visualBounds();
-
- if (!sel_bbox) {
- return;
- }
-
- Geom::Point const center_(sel_bbox->midpoint());
- setScaleRelative(center_, Geom::Scale(times, times));
- DocumentUndo::done(document(), _("Scale by whole factor"), INKSCAPE_ICON("tool-pointer"));
-}
-
void ObjectSet::move(double dx, double dy)
{
if (isEmpty()) {
diff --git a/src/selection.cpp b/src/selection.cpp
index 09730f29a3e073dd336624ff0fe76c291d931261..5db44de6b27ef926fb89959214dbe9373dca3fd4 100644
--- a/src/selection.cpp
+++ b/src/selection.cpp
@@ -22,6 +22,7 @@
#include "selection.h"
#include
+#include
#include "desktop.h"
#include "document-undo.h"
@@ -33,6 +34,7 @@
#include "object/sp-defs.h"
#include "object/sp-page.h"
#include "object/sp-shape.h"
+#include "ui/icon-names.h"
#include "ui/tool/control-point-selection.h"
#include "ui/tool/path-manipulator.h"
#include "ui/tools/node-tool.h"
@@ -193,6 +195,66 @@ void Selection::setAnchor(double x, double y, bool set)
}
}
+void Selection::scaleAnchored(double amount, bool fixed)
+{
+ if (Geom::OptRect bbox = visualBounds()) {
+ // Scale the amount by the size to get the final scale amount
+ if (fixed) {
+ double const max_len = bbox->maxExtent();
+ if (max_len + amount <= 1e-3) {
+ return;
+ }
+ amount = 1.0 + amount / max_len;
+ }
+
+ auto center = has_anchor ? bbox->min() + bbox->dimensions() * Geom::Scale(anchor) : bbox->midpoint();
+ scaleRelative(center, Geom::Scale(amount, amount));
+
+ DocumentUndo::maybeDone(document(),
+ ((amount > 0) ? "selector:grow:larger" : "selector:grow:smaller" ),
+ ((amount > 0) ? _("Grow") : _("Shrink")), INKSCAPE_ICON("tool-pointer"));
+ }
+}
+
+void Selection::rotateAnchored(double angle_degrees, double zoom)
+{
+ if (Geom::OptRect bbox = visualBounds()) {
+ auto mid = center() ? *center() : bbox->midpoint();
+ auto center = has_anchor ? bbox->min() + bbox->dimensions() * Geom::Scale(anchor) : mid;
+
+ if (auto d = desktop()) {
+ angle_degrees = d->yaxisdir();
+ }
+
+ if (zoom != 1.0) {
+ Geom::Point m = bbox->midpoint();
+ unsigned i = 0;
+ if (center[Geom::X] < m[Geom::X]) {
+ i = 1;
+ }
+ if (center[Geom::Y] < m[Geom::Y]) {
+ i = 3 - i;
+ }
+
+ double const r = Geom::L2(bbox->corner(i) - center);
+ angle_degrees = 180 * atan2(angle_degrees / zoom, r) / M_PI;
+ }
+
+ rotateRelative(center, angle_degrees);
+
+ if (angle_degrees == 90.0) {
+ DocumentUndo::done(document(), _("Rotate 90\xc2\xb0 CW"), INKSCAPE_ICON("object-rotate-right"));
+ } else if (angle_degrees == -90.0) {
+ DocumentUndo::done(document(), _("Rotate 90\xc2\xb0 CCW"), INKSCAPE_ICON("object-rotate-left"));
+ } else {
+ DocumentUndo::maybeDone(document(),
+ ( ( angle_degrees > 0 )? "selector:rotate:ccw": "selector:rotate:cw" ),
+ _("Rotate"), INKSCAPE_ICON("tool-pointer"));
+ }
+ }
+}
+
+
SPObject *Selection::_objectForXMLNode(Inkscape::XML::Node *repr) const {
g_return_val_if_fail(repr != nullptr, NULL);
auto object = _document->getObjectByRepr(repr);
diff --git a/src/selection.h b/src/selection.h
index 3f201ae5448f30cdee94ff05ec6723d8e349a245..28e1b92c8cffd1db784734d58f011e723ef988a3 100644
--- a/src/selection.h
+++ b/src/selection.h
@@ -175,6 +175,23 @@ public:
bool has_anchor = false;
Geom::Point anchor;
+ /**
+ * Scale the selection, anchoring it against the center, or a selected anchor
+ *
+ * @param amount - The amount to scale by, in a fixed or related amount.
+ * @param fixed - If true (default) scales by fixed document units instead of by
+ * factors of the size of the object.
+ */
+ void scaleAnchored(double amount, bool fixed = true);
+
+ /**
+ * Rotate the selection, anchoring it against the center, or a selected anchor
+ *
+ * @param angle_degrees - The amount to rotate by in degrees.
+ * @param zoom - The zoom amount for screen based rotation amount.
+ */
+ void rotateAnchored(double angle_degrees, double zoom = 1.0);
+
/**
* Connects a slot to be notified of selected object modifications.
*
diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp
index 0de8fef7dfa9bda17c42c90a5b8f0ad8afe9513e..1bbbafd6b91fc701f7857d90f4fa125430b7c14f 100644
--- a/src/ui/clipboard.cpp
+++ b/src/ui/clipboard.cpp
@@ -884,7 +884,7 @@ bool ClipboardManagerImpl::pasteSize(ObjectSet *set, bool separately, bool apply
// resize the selection as a whole
Geom::OptRect sel_size = set->preferredBounds();
if (sel_size) {
- set->setScaleRelative(sel_size->midpoint(),
+ set->scaleRelative(sel_size->midpoint(),
_getScale(set->desktop(), min, max, *sel_size, apply_x, apply_y));
}
}
diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp
index ee591a87a4e50f33655ed06b96522c65b77001a2..de5b05e6ee80bf8af9a857e98bfff447831e48c2 100644
--- a/src/ui/tools/select-tool.cpp
+++ b/src/ui/tools/select-tool.cpp
@@ -828,7 +828,6 @@ bool SelectTool::root_handler(CanvasEvent const &event)
}
gdouble const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000, "px"); // in px
- int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
auto const y_dir = _desktop->yaxisdir();
bool const rotated = prefs->getBool("/options/moverotated/value", true);
@@ -913,30 +912,6 @@ bool SelectTool::root_handler(CanvasEvent const &event)
}
break;
- case GDK_KEY_bracketleft:
- if (mod_alt(event)) {
- selection->rotateScreen(-mul * y_dir);
- } else if (mod_ctrl(event)) {
- selection->rotate(-90 * y_dir);
- } else if (snaps) {
- selection->rotate(-180.0/snaps * y_dir);
- }
-
- ret = true;
- break;
-
- case GDK_KEY_bracketright:
- if (mod_alt(event)) {
- selection->rotateScreen(mul * y_dir);
- } else if (mod_ctrl(event)) {
- selection->rotate(90 * y_dir);
- } else if (snaps) {
- selection->rotate(180.0/snaps * y_dir);
- }
-
- ret = true;
- break;
-
case GDK_KEY_Return:
if (mod_ctrl_only(event)) {
if (selection->singleItem()) {
diff --git a/testfiles/src/object-set-test.cpp b/testfiles/src/object-set-test.cpp
index 878e6ef6c4176339b1781ce1fc9f0c894bfff725..51fd1387b0767911500f89266645e95fabd84b0e 100644
--- a/testfiles/src/object-set-test.cpp
+++ b/testfiles/src/object-set-test.cpp
@@ -628,14 +628,14 @@ TEST_F(ObjectSetTest, Moves) {
EXPECT_EQ(15,r1->x.value);
Geom::Point p(20,20);
Geom::Scale s(2);
- set->setScaleRelative(p,s);
+ set->scaleRelative(p,s);
EXPECT_EQ(10,r1->x.value);
EXPECT_EQ(20,r1->width.value);
set->toCurves();
r1.release();
auto x = set->singleItem();
EXPECT_EQ(20,(*(x->documentVisualBounds()))[0].extent());
- set->rotate(180);
+ set->rotateRelative(*set->center(), 180);
EXPECT_EQ(20,(*(x->documentVisualBounds()))[0].extent());
set->deleteItems();
}