summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2017-01-24 18:11:26 +0000
committerjabiertxof <info@marker.es>2017-01-24 18:11:26 +0000
commit176b68d76f6b684b4bf0797e1d132e8377bddab0 (patch)
treeb1918157cb8d5ada5bb76a0fd98b2433d2dd476d /src
parentFix a translation bug in rotate copies LPE (diff)
parentPut namespace as constant (diff)
downloadinkscape-176b68d76f6b684b4bf0797e1d132e8377bddab0.tar.gz
inkscape-176b68d76f6b684b4bf0797e1d132e8377bddab0.zip
merge lp:~inkscape.dev/inkscape/doc_rotate
(bzr r15444)
Diffstat (limited to 'src')
-rw-r--r--src/attributes.cpp1
-rw-r--r--src/attributes.h1
-rw-r--r--src/desktop-events.cpp4
-rw-r--r--src/display/canvas-temporary-item-list.h1
-rw-r--r--src/display/sp-canvas.cpp295
-rw-r--r--src/display/sp-canvas.h5
-rw-r--r--src/document-undo.cpp53
-rw-r--r--src/document.cpp6
-rw-r--r--src/extension/internal/cairo-png-out.cpp4
-rw-r--r--src/extension/internal/cairo-ps-out.cpp7
-rw-r--r--src/extension/internal/cairo-renderer-pdf-out.cpp4
-rw-r--r--src/extension/internal/emf-inout.cpp5
-rw-r--r--src/extension/internal/javafx-out.cpp9
-rw-r--r--src/extension/internal/latex-pstricks-out.cpp2
-rw-r--r--src/extension/internal/odf.cpp9
-rw-r--r--src/extension/internal/pov-out.cpp13
-rw-r--r--src/extension/internal/wmf-inout.cpp7
-rw-r--r--src/file.cpp6
-rw-r--r--src/print.cpp2
-rw-r--r--src/sp-namedview.cpp98
-rw-r--r--src/sp-namedview.h11
-rw-r--r--src/ui/dialog/export.cpp9
-rw-r--r--src/ui/tools/tool-base.cpp862
-rw-r--r--src/ui/tools/tool-base.h1
-rw-r--r--src/viewbox.cpp62
-rw-r--r--src/viewbox.h7
-rw-r--r--src/widgets/desktop-widget.cpp208
-rw-r--r--src/widgets/desktop-widget.h4
-rw-r--r--src/widgets/widget-sizes.h1
29 files changed, 1243 insertions, 454 deletions
diff --git a/src/attributes.cpp b/src/attributes.cpp
index 02b9450b0..8154bfbbe 100644
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
@@ -89,6 +89,7 @@ static SPStyleProp const props[] = {
{SP_ATTR_INKSCAPE_ZOOM, "inkscape:zoom"},
{SP_ATTR_INKSCAPE_CX, "inkscape:cx"},
{SP_ATTR_INKSCAPE_CY, "inkscape:cy"},
+ {SP_ATTR_INKSCAPE_DOCUMENT_ROTATION, "inkscape:document-rotation"},
{SP_ATTR_INKSCAPE_WINDOW_WIDTH, "inkscape:window-width"},
{SP_ATTR_INKSCAPE_WINDOW_HEIGHT, "inkscape:window-height"},
{SP_ATTR_INKSCAPE_WINDOW_X, "inkscape:window-x"},
diff --git a/src/attributes.h b/src/attributes.h
index 12adcf8e2..e21851bcf 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -97,6 +97,7 @@ enum SPAttributeEnum {
SP_ATTR_INKSCAPE_ZOOM,
SP_ATTR_INKSCAPE_CX,
SP_ATTR_INKSCAPE_CY,
+ SP_ATTR_INKSCAPE_DOCUMENT_ROTATION,
SP_ATTR_INKSCAPE_WINDOW_WIDTH,
SP_ATTR_INKSCAPE_WINDOW_HEIGHT,
SP_ATTR_INKSCAPE_WINDOW_X,
diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp
index 5fef8cbfc..cb3e1f7c8 100644
--- a/src/desktop-events.cpp
+++ b/src/desktop-events.cpp
@@ -155,6 +155,10 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge
}
}
+ SPNamedView *namedview = desktop->namedview;
+ if (namedview && namedview->document_rotation) {
+ normal *= Geom::Rotate(Geom::rad_from_deg(namedview->document_rotation * -1));
+ }
guide = sp_guideline_new(desktop->guides, NULL, event_dt, normal);
sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor);
diff --git a/src/display/canvas-temporary-item-list.h b/src/display/canvas-temporary-item-list.h
index 471bb99b9..6b0e5796b 100644
--- a/src/display/canvas-temporary-item-list.h
+++ b/src/display/canvas-temporary-item-list.h
@@ -14,6 +14,7 @@
struct SPCanvasItem;
class SPDesktop;
+class SPViewBox;
namespace Inkscape {
namespace Display {
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index 416c2c804..68eae0a65 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -29,18 +29,24 @@
#include "helper/sp-marshal.h"
#include <2geom/rect.h>
#include <2geom/affine.h>
-#include "display/cairo-utils.h"
#include "display/sp-canvas.h"
#include "display/sp-canvas-group.h"
+#include "display/rendermode.h"
+#include "display/cairo-utils.h"
+#include "display/cairo-templates.h"
+#include "display/drawing-context.h"
+#include "display/drawing-item.h"
+#include "display/nr-filter-colormatrix.h"
+#include "display/canvas-arena.h"
#include "preferences.h"
#include "inkscape.h"
#include "sodipodi-ctrlrect.h"
#include "cms-system.h"
-#include "display/rendermode.h"
-#include "display/cairo-utils.h"
#include "debug/gdk-event-latency-tracker.h"
#include "desktop.h"
#include "color.h"
+#include <iomanip>
+#include <glibmm/i18n.h>
using Inkscape::Debug::GdkEventLatencyTracker;
@@ -1888,6 +1894,19 @@ SPCanvasGroup *SPCanvas::getRoot()
return SP_CANVAS_GROUP(_root);
}
+gdouble grayscale_value_matrix[20] = {
+ 0.21, 0.72, 0.072, 0, 0,
+ 0.21, 0.72, 0.072, 0, 0,
+ 0.21, 0.72, 0.072, 0, 0,
+ 0 , 0 , 0 , 1, 0
+ };
+cairo_surface_t *surface_rotated;
+cairo_surface_t *surface_origin;
+cairo_surface_t *surface_measure;
+double start_angle = 0;
+bool started = false;
+bool rotated = false;
+
void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling)
{
GtkAllocation allocation;
@@ -1910,9 +1929,14 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
// Paint the background
cairo_translate(cr, -ix, -iy);
+ if (rotated) {
+ cairo_translate(cr, dx, dy);
+ rotated = false;
+ }
cairo_set_source(cr, _background);
cairo_paint(cr);
// Copy the old backing store contents
+
cairo_set_source_surface(cr, _backing_store, _x0, _y0);
cairo_rectangle(cr, _x0, _y0, allocation.width, allocation.height);
cairo_clip(cr);
@@ -1949,6 +1973,271 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll
addIdle();
}
+void SPCanvas::startRotateTo(double angle)
+{
+ if (!_backing_store || started) {
+ return;
+ }
+ start_angle = angle;
+ started = true;
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(&_widget, &allocation);
+ int half_w = allocation.width/2;
+ int half_h = allocation.height/2;
+ int half_min = std::min(half_w,half_h);
+
+ cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cairo_t *cr = cairo_create(new_backing_store);
+ cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI);
+ cairo_fill(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_IN);
+ cairo_set_source_surface(cr, _backing_store, 0, 0);
+ cairo_paint(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_arc(cr, half_w, half_h, half_min-16, 0, 2*M_PI);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ surface_rotated = new_backing_store;
+
+ cairo_surface_t *new_backing_store_measure = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cr = cairo_create(new_backing_store_measure);
+ cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
+ cairo_fill(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 10.0);
+ for (gint x = 0; x < 360 ; x++){
+ gint ang = 360 - x ;//+ 90;
+ if (ang > 180) {
+ ang -= 360;
+ }
+ double rot = (-180.0 + x)*(M_PI/180.);
+ double dist = half_min-9;
+ gint inverse = 1;
+ if((x) < 91 || (x) > 270) {
+ inverse = -1;
+ }
+ if(x%10 == 0) {
+ cairo_rotate(cr, -rot);
+ cairo_text_extents_t extents;
+ std::string s = std::to_string(ang) + "º";
+ cairo_text_extents(cr, s.c_str(), &extents);
+ //std::cout << extents.width/2 << "extents.x_bearing\n";
+ cairo_translate(cr, (extents.width/2) * inverse * -1, (dist + ((extents.height/2)* inverse)));
+ if((x) < 91 || (x) > 270) {
+ cairo_rotate(cr, 180*(M_PI/180.0));
+ }
+ cairo_text_path(cr, s.c_str());
+ if((x) < 91 || (x) > 270) {
+ cairo_rotate(cr, -180*(M_PI/180.0));
+ }
+ cairo_translate(cr, (extents.width/2) * inverse , (dist + ((extents.height/2)* inverse)) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_fill(cr);
+ cairo_rotate(cr, rot);
+ }
+ cairo_rotate(cr, x*(M_PI/180.));
+ if(x%5 == 0) {
+ cairo_move_to(cr, 0, half_min-30);
+ cairo_line_to(cr, 0, half_min-17);
+ } else {
+ cairo_move_to(cr, 0, half_min-20);
+ cairo_line_to(cr, 0, half_min-15);
+ }
+ cairo_line_to(cr, 0, half_min-15);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.4);
+ cairo_set_line_width (cr,1);
+ cairo_stroke(cr);
+ cairo_rotate(cr, -x*(M_PI/180.));
+ }
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_translate(cr, -half_w, -half_h);
+ cairo_arc(cr, half_w, half_h, half_min-30, 0, 2*M_PI);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_fill(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_rotate(cr, start_angle*(M_PI/180.));
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width (cr,5);
+ cairo_stroke(cr);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.9);
+ cairo_set_line_width (cr,1);
+ cairo_stroke(cr);
+ cairo_rotate(cr, -start_angle*(M_PI/180.));
+ cairo_destroy(cr);
+ surface_measure = new_backing_store_measure;
+
+ cairo_surface_t *new_backing_store_grey = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cr = cairo_create(new_backing_store_grey);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_surface(cr, _backing_store, 0, 0);
+ cairo_paint(cr);
+ Inkscape::Filters::FilterColorMatrix::ColorMatrixMatrix _grayscale_colormatrix = std::vector<gdouble> (grayscale_value_matrix, grayscale_value_matrix + 20);
+ cairo_surface_t *out = ink_cairo_surface_create_identical(new_backing_store_grey);
+ ink_cairo_surface_filter(new_backing_store_grey, out, _grayscale_colormatrix);
+ cairo_set_source_surface(cr, out, 0, 0);
+ cairo_surface_destroy(out);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ surface_origin = new_backing_store_grey;
+}
+
+bool SPCanvas::endRotateTo()
+{
+ if (!_backing_store || !started) {
+ return false;
+ }
+ started = false;
+ surface_rotated = NULL;
+ surface_origin = NULL;
+ gtk_widget_queue_draw(GTK_WIDGET(this));
+ dirtyAll();
+ addIdle();
+ rotated = true;
+ return true;
+}
+
+void SPCanvas::clearRotateTo()
+{
+ if (!started) {
+ return;
+ }
+ gtk_widget_queue_draw(GTK_WIDGET(this));
+ dirtyAll();
+ addIdle();
+}
+
+void SPCanvas::rotateTo(double angle)
+{
+ if (!_backing_store || !started) {
+ return;
+ }
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(&_widget, &allocation);
+ int half_w = allocation.width/2;
+ int half_h = allocation.height/2;
+ int half_min = std::min(half_w,half_h);
+ cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cairo_t *cr = cairo_create(new_backing_store);
+ cairo_set_source_surface(cr, surface_origin, 0, 0);
+ cairo_paint(cr);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_paint(cr);
+ cairo_pattern_t *source_pattern;
+ cairo_matrix_t matrix;
+ source_pattern = cairo_pattern_create_for_surface (surface_rotated);
+ cairo_matrix_init_identity (&matrix);
+ cairo_matrix_translate (&matrix, allocation.width/2.0, allocation.height/2.0);
+ cairo_matrix_rotate (&matrix, Geom::rad_from_deg(angle - start_angle) * -1);
+ cairo_matrix_translate (&matrix, -allocation.width/2.0, -allocation.height/2.0);
+ cairo_pattern_set_matrix (source_pattern, &matrix);
+ cairo_set_source(cr, source_pattern);
+ cairo_paint(cr);
+ cairo_set_source_surface(cr, surface_measure, 0, 0);
+ cairo_paint(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_rotate(cr, angle*(M_PI/180.));
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width (cr,5);
+ cairo_stroke(cr);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.9);
+ cairo_set_line_width (cr,1);
+ cairo_stroke(cr);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.9);
+ const double dashed[] = {6.0, 3.0};
+ int len = sizeof(dashed) / sizeof(dashed[0]);
+ cairo_set_dash(cr, dashed, len, 1);
+ cairo_stroke(cr);
+ cairo_translate(cr, -half_w, -half_h);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
+ cairo_arc(cr, half_w, half_h, 7, 0, 2*M_PI);
+ cairo_fill(cr);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.7);
+ cairo_arc(cr, half_w, half_h, 5, 0, 2*M_PI);
+ cairo_fill(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_rotate(cr, -angle*(M_PI/180.));
+ cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 15.0);
+ cairo_text_extents_t extents;
+ std::ostringstream s;
+ s << _("Original angle ") << std::fixed << std::setprecision(2) << start_angle << "º";
+ cairo_text_extents(cr, s.str().c_str(), &extents);
+ cairo_translate(cr, half_w - extents.width -15 ,-half_h + 25);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 25) *-1);
+ s.str("");
+ s << _("New angle ") << std::fixed << std::setprecision(2) << angle << "º";
+ cairo_text_extents(cr, s.str().c_str(), &extents);
+ cairo_translate(cr, half_w - extents.width -15 ,-half_h + 45);
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 45) *-1);
+ s.str("");
+ s << _("Gap ") << std::fixed << std::setprecision(2) << std::abs(start_angle-angle) << "º";
+ cairo_text_extents(cr, s.str().c_str(), &extents);
+ cairo_translate(cr, half_w - extents.width -15 ,-half_h + 65);
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 65) *-1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 25);
+ s.str("");
+ cairo_set_font_size(cr, 12.0);
+ s << _("Normal mode, 1º round step");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w +10) * -1 ,(-half_h + 25) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 40);
+ s.str("");
+ s << _("+ALT, Fractional degrees");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 40) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 55);
+ s.str("");
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ s << _("+CTRL, ") << 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12) << _("º round step");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 55) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 70);
+ s.str("");
+ s << _("+SHIFT, Reset");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 70) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 85);
+ s.str("");
+ s << _("+CTRL+SHIFT, 0º");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ //cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 60) * -1);
+ cairo_destroy(cr);
+ cairo_surface_destroy(_backing_store);
+ _backing_store = new_backing_store;
+ cairo_pattern_destroy (source_pattern);
+ gtk_widget_queue_draw(GTK_WIDGET(this));
+ addIdle();
+}
+
void SPCanvas::updateNow()
{
if (_need_update) {
diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h
index 78d96d728..21b6760f2 100644
--- a/src/display/sp-canvas.h
+++ b/src/display/sp-canvas.h
@@ -72,7 +72,10 @@ GType sp_canvas_get_type() G_GNUC_CONST;
struct SPCanvas {
/// Scrolls canvas to specific position (cx and cy are measured in screen pixels).
void scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling = false);
-
+ void startRotateTo(double angle);
+ void rotateTo(double angle);
+ bool endRotateTo();
+ void clearRotateTo();
/// Synchronously updates the canvas if necessary.
void updateNow();
diff --git a/src/document-undo.cpp b/src/document-undo.cpp
index 113d09d66..ae90c916e 100644
--- a/src/document-undo.cpp
+++ b/src/document-undo.cpp
@@ -240,49 +240,52 @@ static void perform_document_update(SPDocument &doc) {
gboolean Inkscape::DocumentUndo::undo(SPDocument *doc)
{
- using Inkscape::Debug::EventTracker;
- using Inkscape::Debug::SimpleEvent;
+ using Inkscape::Debug::EventTracker;
+ using Inkscape::Debug::SimpleEvent;
- gboolean ret;
+ gboolean ret;
- EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo");
+ EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo");
- g_assert (doc != NULL);
- g_assert (doc->priv != NULL);
- g_assert (doc->priv->sensitive);
+ g_assert (doc != NULL);
+ g_assert (doc->priv != NULL);
+ g_assert (doc->priv->sensitive);
- doc->priv->sensitive = FALSE;
+ doc->priv->sensitive = FALSE;
doc->priv->seeking = true;
- doc->actionkey.clear();
+ doc->actionkey.clear();
- finish_incomplete_transaction(*doc);
+ finish_incomplete_transaction(*doc);
- if (! doc->priv->undo.empty()) {
- Inkscape::Event *log = doc->priv->undo.back();
- doc->priv->undo.pop_back();
- sp_repr_undo_log (log->event);
- perform_document_update(*doc);
+ if (! doc->priv->undo.empty()) {
+ Inkscape::Event *log = doc->priv->undo.back();
+ doc->priv->undo.pop_back();
+ sp_repr_undo_log (log->event);
+ perform_document_update(*doc);
- doc->priv->redo.push_back(log);
+ doc->priv->redo.push_back(log);
doc->setModifiedSinceSave();
doc->priv->undoStackObservers.notifyUndoEvent(log);
- ret = TRUE;
- } else {
- ret = FALSE;
- }
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ }
- sp_repr_begin_transaction (doc->rdoc);
+ sp_repr_begin_transaction (doc->rdoc);
- doc->priv->sensitive = TRUE;
+ doc->priv->sensitive = TRUE;
doc->priv->seeking = false;
- if (ret)
- INKSCAPE.external_change();
+ if (ret) INKSCAPE.external_change();
- return ret;
+ SPObject *updated = doc->getRoot();
+ if (updated) {
+ updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+ return ret;
}
gboolean Inkscape::DocumentUndo::redo(SPDocument *doc)
diff --git a/src/document.cpp b/src/document.cpp
index 0c77a8f48..8072ec53d 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -386,6 +386,7 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc,
if (!bordercolor.empty()) {
rnew->setAttribute("bordercolor", bordercolor.data());
}
+ sp_repr_set_svg_double(rnew, "inkscape:document-rotation", 0.);
sp_repr_set_svg_double(rnew, "borderopacity",
prefs->getDouble("/template/base/borderopacity", 1.0));
sp_repr_set_svg_double(rnew, "objecttolerance",
@@ -407,6 +408,11 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc,
rroot->addChild(rnew, NULL);
// clean up
Inkscape::GC::release(rnew);
+ } else {
+ Inkscape::XML::Node *nv_repr = sp_item_group_get_child_by_name(document->root, NULL, "sodipodi:namedview")->getRepr();
+ if (!nv_repr->attribute("inkscape:document-rotation")) {
+ sp_repr_set_svg_double(nv_repr, "inkscape:document-rotation", 0.);
+ }
}
// Defs
diff --git a/src/extension/internal/cairo-png-out.cpp b/src/extension/internal/cairo-png-out.cpp
index 956fcce9a..5859a82da 100644
--- a/src/extension/internal/cairo-png-out.cpp
+++ b/src/extension/internal/cairo-png-out.cpp
@@ -53,11 +53,10 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename)
{
CairoRenderer *renderer;
CairoRenderContext *ctx;
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
/* Start */
-
SPItem *base = doc->getRoot();
Inkscape::Drawing drawing;
unsigned dkey = SPItem::display_key_new(1);
@@ -77,6 +76,7 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename)
renderer->destroyContext(ctx);
base->invoke_hide(dkey);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
/* end */
delete renderer;
diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp
index e8f47e79e..809125266 100644
--- a/src/extension/internal/cairo-ps-out.cpp
+++ b/src/extension/internal/cairo-ps-out.cpp
@@ -68,6 +68,7 @@ static bool
ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, bool texttopath, bool omittext,
bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px, bool eps = false)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
SPItem *base = NULL;
@@ -84,9 +85,10 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l
pageBoundingBox = !exportDrawing;
}
- if (!base)
+ if (!base) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
-
+ }
Inkscape::Drawing drawing;
unsigned dkey = SPItem::display_key_new(1);
base->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY);
@@ -115,6 +117,7 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l
renderer->destroyContext(ctx);
delete renderer;
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return ret;
}
diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp
index 5576676b2..5558fe1ad 100644
--- a/src/extension/internal/cairo-renderer-pdf-out.cpp
+++ b/src/extension/internal/cairo-renderer-pdf-out.cpp
@@ -61,6 +61,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int
bool texttopath, bool omittext, bool filtertobitmap, int resolution,
const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
/* Start */
@@ -80,6 +81,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int
}
if (!base) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
@@ -112,7 +114,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int
renderer->destroyContext(ctx);
delete renderer;
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return ret;
}
diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp
index 12751c5ec..198c18ff2 100644
--- a/src/extension/internal/emf-inout.cpp
+++ b/src/extension/internal/emf-inout.cpp
@@ -94,7 +94,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename)
const gchar *oldconst;
gchar *oldoutput;
unsigned int ret;
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
mod = Inkscape::Extension::get_print(PRINT_EMF);
@@ -114,6 +114,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename)
/* Print document */
ret = mod->begin(doc);
if (ret) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
g_free(oldoutput);
throw Inkscape::Extension::Output::save_failed();
}
@@ -127,7 +128,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename)
mod->set_param_string("destination", oldoutput);
g_free(oldoutput);
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp
index d7ad7e6f7..c95434939 100644
--- a/src/extension/internal/javafx-out.cpp
+++ b/src/extension/internal/javafx-out.cpp
@@ -843,7 +843,8 @@ void JavaFXOutput::reset()
bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
{
reset();
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
name = Glib::path_get_basename(filename_utf8);
int pos = name.find('.');
@@ -856,12 +857,14 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
//# Lets do the curves first, to get the stats
if (!doTree(doc)) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
String curveBuf = outbuf;
outbuf.clear();
if (!doHeader()) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
@@ -875,6 +878,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
doBody(doc, doc->getRoot());
if (!doTail()) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
@@ -884,6 +888,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
if (!f)
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could open JavaFX file '%s' for writing", filename_utf8);
return false;
}
@@ -894,7 +899,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
}
fclose(f);
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return true;
}
diff --git a/src/extension/internal/latex-pstricks-out.cpp b/src/extension/internal/latex-pstricks-out.cpp
index 3ce2c5531..aa6ea6963 100644
--- a/src/extension/internal/latex-pstricks-out.cpp
+++ b/src/extension/internal/latex-pstricks-out.cpp
@@ -49,6 +49,7 @@ bool LatexOutput::check(Inkscape::Extension::Extension * /*module*/)
void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc, gchar const *filename)
{
SPPrintContext context;
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
Inkscape::Extension::Print *mod = Inkscape::Extension::get_print(SP_MODULE_KEY_PRINT_LATEX);
@@ -76,6 +77,7 @@ void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc,
mod->set_param_string("destination", oldoutput);
g_free(oldoutput);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
#include "clear-n_.h"
diff --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp
index f885ef5e5..66d370357 100644
--- a/src/extension/internal/odf.cpp
+++ b/src/extension/internal/odf.cpp
@@ -72,6 +72,7 @@
#include "sp-path.h"
#include "sp-text.h"
#include "sp-flowtext.h"
+#include "sp-root.h"
#include "svg/svg.h"
#include "text-editing.h"
#include "util/units.h"
@@ -2095,7 +2096,8 @@ void OdfOutput::reset()
void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename)
{
reset();
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
documentUri = Inkscape::URI(filename);
ZipFile zf;
@@ -2104,25 +2106,30 @@ void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gcha
if (!writeManifest(zf))
{
g_warning("Failed to write manifest");
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
if (!writeContent(zf, doc->rroot))
{
g_warning("Failed to write content");
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
if (!writeMeta(zf))
{
g_warning("Failed to write metafile");
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
if (!zf.writeFile(filename))
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp
index 8df883069..03a2ecd62 100644
--- a/src/extension/internal/pov-out.cpp
+++ b/src/extension/internal/pov-out.cpp
@@ -616,11 +616,13 @@ void PovOutput::reset()
void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
{
reset();
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
//###### SAVE IN POV FORMAT TO BUFFER
//# Lets do the curves first, to get the stats
if (!doTree(doc))
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could not output curves for %s", filename_utf8);
return;
}
@@ -630,6 +632,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
if (!doHeader())
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could not write header for %s", filename_utf8);
return;
}
@@ -638,6 +641,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
if (!doTail())
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could not write footer for %s", filename_utf8);
return;
}
@@ -648,9 +652,11 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
//###### WRITE TO FILE
Inkscape::IO::dump_fopen_call(filename_utf8, "L");
FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
- if (!f)
+ if (!f){
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
-
+ }
+
for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); ++iter)
{
int ch = *iter;
@@ -658,6 +664,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
}
fclose(f);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp
index c7226a58a..a79af5ec1 100644
--- a/src/extension/internal/wmf-inout.cpp
+++ b/src/extension/internal/wmf-inout.cpp
@@ -95,7 +95,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
SPPrintContext context;
const gchar *oldconst;
gchar *oldoutput;
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
mod = Inkscape::Extension::get_print(PRINT_WMF);
@@ -115,6 +115,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
/* Print document */
if (mod->begin(doc)) {
g_free(oldoutput);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
throw Inkscape::Extension::Output::save_failed();
}
mod->base->invoke_print(&context);
@@ -127,7 +128,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
mod->set_param_string("destination", oldoutput);
g_free(oldoutput);
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -135,6 +136,8 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
void
Wmf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
Inkscape::Extension::Extension * ext;
ext = Inkscape::Extension::db.get(PRINT_WMF);
diff --git a/src/file.cpp b/src/file.cpp
index e8248bb8e..6613b1e65 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -293,10 +293,12 @@ bool sp_file_open(const Glib::ustring &uri,
bool replace_empty)
{
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ Inkscape::Display::TemporaryItem *page_border_rotated = NULL;
if (desktop) {
desktop->setWaitingCursor();
+ page_border_rotated = sp_document_namedview(desktop->getDocument(), NULL)->page_border_rotated;
}
-
+
SPDocument *doc = NULL;
bool cancelled = false;
try {
@@ -315,7 +317,6 @@ bool sp_file_open(const Glib::ustring &uri,
}
if (doc) {
-
SPDocument *existing = desktop ? desktop->getDocument() : NULL;
if (existing && existing->virgin && replace_empty) {
@@ -323,6 +324,7 @@ bool sp_file_open(const Glib::ustring &uri,
doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc.
desktop->change_document(doc);
doc->emitResizedSignal(doc->getWidth().value("px"), doc->getHeight().value("px"));
+ desktop->remove_temporary_canvasitem(page_border_rotated);
} else {
// create a whole new desktop and window
SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc.
diff --git a/src/print.cpp b/src/print.cpp
index 7cd05ac93..479401995 100644
--- a/src/print.cpp
+++ b/src/print.cpp
@@ -79,6 +79,7 @@ unsigned int sp_print_text(SPPrintContext *ctx, char const *text, Geom::Point p,
void
sp_print_document(Gtk::Window& parentWindow, SPDocument *doc)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
// Build arena
@@ -88,6 +89,7 @@ sp_print_document(Gtk::Window& parentWindow, SPDocument *doc)
Inkscape::UI::Dialog::Print printop(doc,base);
Gtk::PrintOperationResult res = printop.run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, parentWindow);
(void)res; // TODO handle this
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
void sp_print_document_to_file(SPDocument *doc, gchar const *filename)
diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp
index b9526433f..c9e71955f 100644
--- a/src/sp-namedview.cpp
+++ b/src/sp-namedview.cpp
@@ -19,7 +19,12 @@
#include "event-log.h"
#include <2geom/transforms.h>
+#include "display/sp-canvas-group.h"
+#include "display/canvas-bpath.h"
+#include "display/canvas-temporary-item.h"
+#include "display/canvas-temporary-item-list.h"
#include "display/canvas-grid.h"
+#include "display/curve.h"
#include "util/units.h"
#include "svg/svg-color.h"
#include "xml/repr.h"
@@ -29,12 +34,15 @@
#include "desktop-events.h"
#include "sp-guide.h"
+#include "sp-root.h"
#include "sp-item-group.h"
#include "sp-namedview.h"
#include "preferences.h"
#include "desktop.h"
+#include "selection.h"
+#include "object-set.h"
+#include "inkscape.h"
#include "conn-avoid-ref.h" // for defaultConnSpacing.
-#include "sp-root.h"
#include <gtkmm/window.h>
using Inkscape::DocumentUndo;
@@ -72,6 +80,7 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) {
this->pagecolor = 0;
this->cx = 0;
this->pageshadow = 0;
+ this->document_rotation = 0;
this->window_width = 0;
this->window_height = 0;
this->window_maximized = 0;
@@ -92,9 +101,13 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) {
this->default_layer_id = 0;
this->connector_spacing = defaultConnSpacing;
+ this->page_border_rotated = NULL;
}
SPNamedView::~SPNamedView() {
+ if(!this->getViewList().empty()) { // >0 Desktops
+ this->getViewList()[0]->remove_temporary_canvasitem(this->page_border_rotated);
+ }
}
static void sp_namedview_generate_old_grid(SPNamedView * /*nv*/, SPDocument *document, Inkscape::XML::Node *repr) {
@@ -212,6 +225,7 @@ void SPNamedView::build(SPDocument *document, Inkscape::XML::Node *repr) {
this->readAttr( "inkscape:zoom" );
this->readAttr( "inkscape:cx" );
this->readAttr( "inkscape:cy" );
+ this->readAttr( "inkscape:document-rotation" );
this->readAttr( "inkscape:window-width" );
this->readAttr( "inkscape:window-height" );
this->readAttr( "inkscape:window-x" );
@@ -409,6 +423,11 @@ void SPNamedView::set(unsigned int key, const gchar* value) {
this->cy = value ? g_ascii_strtod(value, NULL) : HUGE_VAL; // HUGE_VAL means not set
this->requestModified(SP_OBJECT_MODIFIED_FLAG);
break;
+ case SP_ATTR_INKSCAPE_DOCUMENT_ROTATION:
+ this->document_rotation = value ? g_ascii_strtod(value, NULL) : 0;
+ sp_namedview_set_document_rotation(this);
+ this->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
case SP_ATTR_INKSCAPE_WINDOW_WIDTH:
this->window_width = value? atoi(value) : -1; // -1 means not set
this->requestModified(SP_OBJECT_MODIFIED_FLAG);
@@ -939,6 +958,81 @@ static void sp_namedview_lock_guides(SPNamedView *nv)
}
}
+void sp_namedview_doc_rotate_guides(SPNamedView *nv)
+{
+ bool saved = DocumentUndo::getUndoSensitive(nv->document);
+ DocumentUndo::setUndoSensitive(nv->document, false);
+ SPRoot * root = nv->document->getRoot();
+ Geom::Point page_center = root->viewBox.midpoint() * root->vbt;
+ Geom::Affine rot = Geom::identity();
+ rot *= Geom::Translate(page_center).inverse();
+ rot *= Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1));
+ rot *= Geom::Translate(page_center);
+ for(std::vector<SPGuide *>::iterator it=nv->guides.begin();it!=nv->guides.end();++it ) {
+ Geom::Point const on_line = (*it)->getPoint() * rot ;
+ (*it)->moveto(on_line, true);
+ Geom::Affine rot_normal_affine = Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1));
+ Geom::Point const rot_normal = (*it)->getNormal() * rot_normal_affine;
+ (*it)->set_normal(rot_normal, true);
+ }
+ DocumentUndo::setUndoSensitive(nv->document, saved);
+ nv->document->setModifiedSinceSave();
+}
+
+void sp_namedview_set_document_rotation(SPNamedView *nv)
+{
+ if ( nv->document->getRoot()->get_rotation() == nv->document_rotation) return;
+ if(!nv->getViewList().empty()) { // >0 Desktops
+ SPDesktop *desktop = nv->getViewList()[0];
+ desktop->remove_temporary_canvasitem(nv->page_border_rotated);
+ SPRoot * root = nv->document->getRoot();
+ SPCurve *c = new SPCurve();
+ c->moveto(root->viewBox.min());
+ c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.min()[Geom::Y]));
+ c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.max()[Geom::Y]));
+ c->lineto(Geom::Point(root->viewBox.min()[Geom::X],root->viewBox.max()[Geom::Y]));
+ c->closepath();
+ Geom::Point page_center = root->viewBox.midpoint();
+ Geom::PathVector const box = c->get_pathvector();
+ Geom::Affine rot = Geom::identity();
+ rot *= Geom::Translate(page_center).inverse();
+ rot *= Geom::Rotate(Geom::rad_from_deg(nv->document_rotation * -1));
+ rot *= Geom::Translate(page_center);
+ if (nv->document_rotation) {
+ SPCanvasItem *canvas_border = sp_canvas_bpath_new(desktop->getTempGroup(), c, true);
+ sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvas_border), 0xFF00009A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+ sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvas_border), 0, SP_WIND_RULE_NONZERO);
+ sp_canvas_item_affine_absolute(canvas_border, rot * root->vbt);
+ nv->page_border_rotated = desktop->add_temporary_canvasitem(canvas_border, 0);
+ }
+ sp_namedview_doc_rotate_guides(nv);
+ nv->document->getRoot()->set_rotation(nv->document_rotation);
+ c->unref();
+ }
+ if (nv->document_rotation) {
+ nv->showborder = FALSE;
+ } else {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ nv->showborder = prefs->getBool("/template/base/showborder", 1.0);
+ }
+
+ SPDesktop * desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+//TODO: Remove knots of shapes on selected items
+// Inkscape::Selection * sel = desktop->getSelection();
+// std::vector<SPItem*> il(sel->items().begin(), sel->items().end());
+// for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){
+// SPItem *item = *l;
+// sel->remove(item->getRepr());
+// sel->add(item->getRepr());
+// }
+ SPObject *updated = desktop->getDocument()->getRoot();
+ if (updated) {
+ updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+ }
+}
+
static void sp_namedview_show_single_guide(SPGuide* guide, bool show)
{
if (show) {
@@ -953,6 +1047,8 @@ static void sp_namedview_lock_single_guide(SPGuide* guide, bool locked)
guide->set_locked(locked, true);
}
+
+
void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr)
{
unsigned int v;
diff --git a/src/sp-namedview.h b/src/sp-namedview.h
index d8ac1a77e..9d11c13b7 100644
--- a/src/sp-namedview.h
+++ b/src/sp-namedview.h
@@ -21,6 +21,7 @@
#include "snap.h"
#include "document.h"
#include "util/units.h"
+#include "display/sp-canvas.h"
#include <vector>
namespace Inkscape {
@@ -28,6 +29,9 @@ namespace Inkscape {
namespace Util {
class Unit;
}
+ namespace Display {
+ class TemporaryItem;
+ }
}
typedef unsigned int guint32;
@@ -38,7 +42,7 @@ enum {
SP_BORDER_LAYER_TOP
};
-class SPNamedView : public SPObjectGroup {
+class SPNamedView : public SPObjectGroup{
public:
SPNamedView();
virtual ~SPNamedView();
@@ -54,6 +58,7 @@ public:
double zoom;
double cx;
double cy;
+ double document_rotation;
int window_width;
int window_height;
int window_x;
@@ -66,7 +71,7 @@ public:
Inkscape::Util::Unit const *display_units; // Units used for the UI (*not* the same as units of SVG coordinates)
Inkscape::Util::Unit const *page_size_units; // Only used in "Custom size" part of Document Properties dialog
-
+ Inkscape::Display::TemporaryItem *page_border_rotated;
GQuark default_layer_id;
double connector_spacing;
@@ -121,7 +126,7 @@ SPNamedView const *sp_document_namedview(SPDocument const *document, char const
void sp_namedview_window_from_document(SPDesktop *desktop);
void sp_namedview_document_from_window(SPDesktop *desktop);
void sp_namedview_update_layers_from_document (SPDesktop *desktop);
-
+void sp_namedview_set_document_rotation(SPNamedView *nv);
void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr);
void sp_namedview_guides_toggle_lock(SPDocument *doc, Inkscape::XML::Node *repr);
void sp_namedview_show_grids(SPNamedView *namedview, bool show, bool dirty_document);
diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp
index 1bb952de4..b7207fd50 100644
--- a/src/ui/dialog/export.cpp
+++ b/src/ui/dialog/export.cpp
@@ -978,7 +978,9 @@ void Export::onExport ()
SPNamedView *nv = desktop->getNamedView();
SPDocument *doc = desktop->getDocument();
-
+ Geom::Affine rot = doc->getRoot()->c2p;
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
bool exportSuccessful = false;
bool hide = hide_export.get_active ();
@@ -1003,6 +1005,7 @@ void Export::onExport ()
if (num < 1) {
desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No items selected."));
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1094,6 +1097,7 @@ void Export::onExport ()
if (filename.empty()) {
desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("You have to enter a filename."));
sp_ui_error_dialog(_("You have to enter a filename"));
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1110,6 +1114,7 @@ void Export::onExport ()
if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The chosen area to be exported is invalid."));
sp_ui_error_dialog(_("The chosen area to be exported is invalid"));
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1132,6 +1137,7 @@ void Export::onExport ()
g_free(safeDir);
g_free(error);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1281,6 +1287,7 @@ void Export::onExport ()
}
}
}
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
} // end of sp_export_export_clicked()
/// Called when Browse button is clicked
diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp
index 8a35882b9..ca700f652 100644
--- a/src/ui/tools/tool-base.cpp
+++ b/src/ui/tools/tool-base.cpp
@@ -94,6 +94,7 @@ ToolBase::ToolBase(gchar const *const *cursor_shape, gint hot_x, gint hot_y, boo
, _grdrag(NULL)
, shape_editor(NULL)
, space_panning(false)
+ , rotating_mode(false)
, _delayed_snap_event(NULL)
, _dse_callback_in_process(false)
, desktop(NULL)
@@ -327,99 +328,130 @@ bool ToolBase::root_handler(GdkEvent* event) {
static unsigned int panning = 0;
static unsigned int panning_cursor = 0;
static unsigned int zoom_rb = 0;
+ static double angle = 0;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
/// @todo REmove redundant /value in preference keys
tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
bool allow_panning = prefs->getBool("/options/spacebarpans/value");
+ int rotation_snap = 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12);
gint ret = FALSE;
switch (event->type) {
- case GDK_2BUTTON_PRESS:
- if (panning) {
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
- ret = TRUE;
- } else {
- /* sp_desktop_dialog(); */
- }
- break;
-
- case GDK_BUTTON_PRESS:
- // save drag origin
- xp = (gint) event->button.x;
- yp = (gint) event->button.y;
- within_tolerance = true;
-
- button_w = Geom::Point(event->button.x, event->button.y);
-
- switch (event->button.button) {
- case 1:
- if (this->space_panning) {
- // When starting panning, make sure there are no snap events pending because these might disable the panning again
- if (_uses_snap) {
- sp_event_context_discard_delayed_snap_event(this);
- }
- panning = 1;
-
- sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
- GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK
- | GDK_POINTER_MOTION_MASK
- | GDK_POINTER_MOTION_HINT_MASK, NULL,
- event->button.time - 1);
-
+ case GDK_2BUTTON_PRESS:
+ if (panning) {
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
ret = TRUE;
+ } else {
+ /* sp_desktop_dialog(); */
}
break;
- case 2:
- if (event->button.state & GDK_SHIFT_MASK) {
- zoom_rb = 2;
- } else {
- // When starting panning, make sure there are no snap events pending because these might disable the panning again
- if (_uses_snap) {
- sp_event_context_discard_delayed_snap_event(this);
- }
- panning = 2;
+ case GDK_BUTTON_PRESS:
+ // save drag origin
+ xp = (gint) event->button.x;
+ yp = (gint) event->button.y;
+ within_tolerance = true;
- sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
- GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
- | GDK_POINTER_MOTION_HINT_MASK, NULL,
- event->button.time - 1);
+ button_w = Geom::Point(event->button.x, event->button.y);
+ switch (event->button.button) {
+ case 1:
+ if (this->space_panning) {
+ // When starting panning, make sure there are no snap events pending because these might disable the panning again
+ if (_uses_snap) {
+ sp_event_context_discard_delayed_snap_event(this);
+ }
+ panning = 1;
- }
+ sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+ GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK, NULL,
+ event->button.time - 1);
- ret = TRUE;
- break;
+ ret = TRUE;
+ }
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ break;
- case 3:
- if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) {
- // When starting panning, make sure there are no snap events pending because these might disable the panning again
- if (_uses_snap) {
- sp_event_context_discard_delayed_snap_event(this);
+ case 2:
+ if (event->button.state & GDK_CONTROL_MASK) {
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
+ desktop->canvas->startRotateTo(desktop->namedview->document_rotation);
+ this->rotating_mode = true;
+ this->message_context->set(Inkscape::INFORMATION_MESSAGE,
+ _("<b>MMB + mouse move</b> to rotate canvas, use modifiers on screen to change snaps"));
+ } else {
+ if (event->button.state & GDK_SHIFT_MASK) {
+ zoom_rb = 2;
+ } else {
+ // When starting panning, make sure there are no snap events pending because these might disable the panning again
+ if (_uses_snap) {
+ sp_event_context_discard_delayed_snap_event(this);
+ }
+ panning = 2;
+
+ sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK, NULL,
+ event->button.time - 1);
+ }
+ ret = TRUE;
}
- panning = 3;
+ ret = TRUE;
+ break;
- sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
- GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
- | GDK_POINTER_MOTION_HINT_MASK, NULL,
- event->button.time);
+ case 3:
+ if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) {
+ // When starting panning, make sure there are no snap events pending because these might disable the panning again
+ if (_uses_snap) {
+ sp_event_context_discard_delayed_snap_event(this);
+ }
+ panning = 3;
+ sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK, NULL,
+ event->button.time);
+
+ ret = TRUE;
+ } else if( !this->space_panning) {
+ sp_event_root_menu_popup(desktop, NULL, event);
+ }
ret = TRUE;
- } else {
- sp_event_root_menu_popup(desktop, NULL, event);
- }
- break;
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ break;
- default:
+ default:
+ break;
+ }
break;
- }
- break;
- case GDK_MOTION_NOTIFY:
- if (panning) {
- if (panning == 4 && !xp && !yp ) {
+ case GDK_MOTION_NOTIFY:
+ if (this->rotating_mode) {
+ button_w = Geom::Point(event->motion.x, event->motion.y);
+ Geom::Point const motion_dt(desktop->doc2dt(desktop->w2d(button_w)));
+ Geom::Rect view = desktop->get_display_area();
+ Geom::Point view_center = desktop->doc2dt(view.midpoint());
+ Geom::Ray center_ray(motion_dt, view_center);
+ angle = Geom::deg_from_rad(center_ray.angle()) - 90;
+ if (event->motion.state & GDK_SHIFT_MASK && event->motion.state & GDK_CONTROL_MASK) {
+ angle = 0;
+ } else if(event->motion.state & GDK_CONTROL_MASK) {
+ angle = floor(angle/rotation_snap) * rotation_snap;
+ } else if (event->motion.state & GDK_SHIFT_MASK) {
+ angle = desktop->namedview->document_rotation;
+ } else if (event->motion.state & GDK_MOD1_MASK) {
+ //Decimal raw angle
+ } else {
+ angle = floor(angle);
+ }
+ desktop->canvas->rotateTo(angle);
+ } else if (panning == 4 && !xp && !yp ) {
// <Space> + mouse panning started, save location and grab canvas
xp = event->motion.x;
yp = event->motion.y;
@@ -431,401 +463,465 @@ bool ToolBase::root_handler(GdkEvent* event) {
| GDK_POINTER_MOTION_HINT_MASK, NULL,
event->motion.time - 1);
}
+ if (panning && !this->rotating_mode) {
+ if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK))
+ || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK))
+ || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) {
+ /* Gdk seems to lose button release for us sometimes :-( */
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
+ ret = TRUE;
+ } else {
+ if (within_tolerance && (abs((gint) event->motion.x - xp)
+ < tolerance) && (abs((gint) event->motion.y - yp)
+ < tolerance)) {
+ // do not drag if we're within tolerance from origin
+ break;
+ }
+
+ // Once the user has moved farther than tolerance from
+ // the original location (indicating they intend to move
+ // the object, not click), then always process the motion
+ // notify coordinates as given (no snapping back to origin)
+ within_tolerance = false;
+
+ // gobble subsequent motion events to prevent "sticking"
+ // when scrolling is slow
+ gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning
+ == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK));
+
+ if (panning_cursor == 0) {
+ panning_cursor = 1;
+ this->sp_event_context_set_cursor(GDK_FLEUR);
+ }
+
+ Geom::Point const motion_w(event->motion.x, event->motion.y);
+ Geom::Point const moved_w(motion_w - button_w);
+ this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw
+ ret = TRUE;
+ }
+ } else if (zoom_rb) {
+ Geom::Point const motion_w(event->motion.x, event->motion.y);
+ Geom::Point const motion_dt(desktop->w2d(motion_w));
- if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK))
- || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK))
- || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) {
- /* Gdk seems to lose button release for us sometimes :-( */
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
- ret = TRUE;
- } else {
if (within_tolerance && (abs((gint) event->motion.x - xp)
< tolerance) && (abs((gint) event->motion.y - yp)
< tolerance)) {
- // do not drag if we're within tolerance from origin
- break;
+ break; // do not drag if we're within tolerance from origin
}
- // Once the user has moved farther than tolerance from
- // the original location (indicating they intend to move
- // the object, not click), then always process the motion
- // notify coordinates as given (no snapping back to origin)
+ // Once the user has moved farther than tolerance from the original location
+ // (indicating they intend to move the object, not click), then always process the
+ // motion notify coordinates as given (no snapping back to origin)
within_tolerance = false;
- // gobble subsequent motion events to prevent "sticking"
- // when scrolling is slow
- gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning
- == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK));
-
- if (panning_cursor == 0) {
- panning_cursor = 1;
- this->sp_event_context_set_cursor(GDK_FLEUR);
+ if (Inkscape::Rubberband::get(desktop)->is_started()) {
+ Inkscape::Rubberband::get(desktop)->move(motion_dt);
+ } else {
+ Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt);
}
- Geom::Point const motion_w(event->motion.x, event->motion.y);
- Geom::Point const moved_w(motion_w - button_w);
- this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw
- ret = TRUE;
- }
- } else if (zoom_rb) {
- Geom::Point const motion_w(event->motion.x, event->motion.y);
- Geom::Point const motion_dt(desktop->w2d(motion_w));
-
- if (within_tolerance && (abs((gint) event->motion.x - xp)
- < tolerance) && (abs((gint) event->motion.y - yp)
- < tolerance)) {
- break; // do not drag if we're within tolerance from origin
+ if (zoom_rb == 2) {
+ gobble_motion_events(GDK_BUTTON2_MASK);
+ }
}
+ break;
- // Once the user has moved farther than tolerance from the original location
- // (indicating they intend to move the object, not click), then always process the
- // motion notify coordinates as given (no snapping back to origin)
- within_tolerance = false;
-
- if (Inkscape::Rubberband::get(desktop)->is_started()) {
- Inkscape::Rubberband::get(desktop)->move(motion_dt);
+ case GDK_BUTTON_RELEASE:
+ if (this->rotating_mode && event->button.button == 2) {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ if (desktop->canvas->endRotateTo()) {
+ sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle);
+ }
} else {
- Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt);
- }
+ xp = yp = 0;
+ if (panning_cursor == 1) {
+ panning_cursor = 0;
+ GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
+ gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
+ }
- if (zoom_rb == 2) {
- gobble_motion_events(GDK_BUTTON2_MASK);
- }
- }
- break;
+ if (within_tolerance && (panning || zoom_rb)) {
+ zoom_rb = 0;
- case GDK_BUTTON_RELEASE:
- xp = yp = 0;
+ if (panning) {
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+ event->button.time);
+ }
- if (panning_cursor == 1) {
- panning_cursor = 0;
- GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
- gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
- }
+ Geom::Point const event_w(event->button.x, event->button.y);
+ Geom::Point const event_dt(desktop->w2d(event_w));
- if (within_tolerance && (panning || zoom_rb)) {
- zoom_rb = 0;
+ double const zoom_inc = prefs->getDoubleLimited(
+ "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
- if (panning) {
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
- event->button.time);
- }
+ desktop->zoom_relative_keep_point(event_dt, (event->button.state
+ & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc);
- Geom::Point const event_w(event->button.x, event->button.y);
- Geom::Point const event_dt(desktop->w2d(event_w));
+ desktop->updateNow();
+ ret = TRUE;
+ } else if (panning == event->button.button) {
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+ event->button.time);
- double const zoom_inc = prefs->getDoubleLimited(
- "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
+ // in slow complex drawings, some of the motion events are lost;
+ // to make up for this, we scroll it once again to the button-up event coordinates
+ // (i.e. canvas will always get scrolled all the way to the mouse release point,
+ // even if few intermediate steps were visible)
+ Geom::Point const motion_w(event->button.x, event->button.y);
+ Geom::Point const moved_w(motion_w - button_w);
- desktop->zoom_relative_keep_point(event_dt, (event->button.state
- & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc);
+ this->desktop->scroll_world(moved_w);
+ desktop->updateNow();
+ ret = TRUE;
+ } else if (zoom_rb == event->button.button) {
+ zoom_rb = 0;
- desktop->updateNow();
- ret = TRUE;
- } else if (panning == event->button.button) {
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
- event->button.time);
-
- // in slow complex drawings, some of the motion events are lost;
- // to make up for this, we scroll it once again to the button-up event coordinates
- // (i.e. canvas will always get scrolled all the way to the mouse release point,
- // even if few intermediate steps were visible)
- Geom::Point const motion_w(event->button.x, event->button.y);
- Geom::Point const moved_w(motion_w - button_w);
-
- this->desktop->scroll_world(moved_w);
- desktop->updateNow();
- ret = TRUE;
- } else if (zoom_rb == event->button.button) {
- zoom_rb = 0;
+ Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
+ Inkscape::Rubberband::get(desktop)->stop();
- Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
- Inkscape::Rubberband::get(desktop)->stop();
+ if (b && !within_tolerance) {
+ desktop->set_display_area(*b, 10);
+ }
- if (b && !within_tolerance) {
- desktop->set_display_area(*b, 10);
+ ret = TRUE;
+ }
+ if (this->rotating_mode && event->button.button != 3) {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ desktop->canvas->endRotateTo();
+ }
}
+ break;
- ret = TRUE;
- }
- break;
-
- case GDK_KEY_PRESS: {
- double const acceleration = prefs->getDoubleLimited(
- "/options/scrollingacceleration/value", 0, 0, 6);
- int const key_scroll = prefs->getIntLimited("/options/keyscroll/value",
- 10, 0, 1000);
-
- switch (get_group0_keyval(&event->key)) {
- // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets
- // in the editing window). So we resteal them back and run our regular shortcut
- // invoker on them.
- unsigned int shortcut;
- case GDK_KEY_Tab:
- case GDK_KEY_ISO_Left_Tab:
- case GDK_KEY_F1:
- shortcut = get_group0_keyval(&event->key);
-
- if (event->key.state & GDK_SHIFT_MASK) {
- shortcut |= SP_SHORTCUT_SHIFT_MASK;
+ case GDK_KEY_PRESS: {
+ double const acceleration = prefs->getDoubleLimited(
+ "/options/scrollingacceleration/value", 0, 0, 6);
+ int const key_scroll = prefs->getIntLimited("/options/keyscroll/value",
+ 10, 0, 1000);
+
+ if (this->rotating_mode &&
+ get_group0_keyval(&event->key) != GDK_KEY_space &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_R )
+ {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ desktop->canvas->endRotateTo();
+ break;
}
- if (event->key.state & GDK_CONTROL_MASK) {
- shortcut |= SP_SHORTCUT_CONTROL_MASK;
- }
+ switch (get_group0_keyval(&event->key)) {
+ // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets
+ // in the editing window). So we resteal them back and run our regular shortcut
+ // invoker on them.
+ unsigned int shortcut;
+ case GDK_KEY_Tab:
+ case GDK_KEY_ISO_Left_Tab:
+ case GDK_KEY_F1:
+ shortcut = get_group0_keyval(&event->key);
+
+ if (event->key.state & GDK_SHIFT_MASK) {
+ shortcut |= SP_SHORTCUT_SHIFT_MASK;
+ }
- if (event->key.state & GDK_MOD1_MASK) {
- shortcut |= SP_SHORTCUT_ALT_MASK;
- }
+ if (event->key.state & GDK_CONTROL_MASK) {
+ shortcut |= SP_SHORTCUT_CONTROL_MASK;
+ }
- ret = sp_shortcut_invoke(shortcut, desktop);
- break;
+ if (event->key.state & GDK_MOD1_MASK) {
+ shortcut |= SP_SHORTCUT_ALT_MASK;
+ }
- case GDK_KEY_Q:
- case GDK_KEY_q:
- if (desktop->quick_zoomed()) {
- ret = TRUE;
- }
- if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) {
- desktop->zoom_quick(true);
- ret = TRUE;
- }
- break;
+ ret = sp_shortcut_invoke(shortcut, desktop);
+ break;
- case GDK_KEY_W:
- case GDK_KEY_w:
- case GDK_KEY_F4:
- /* Close view */
- if (MOD__CTRL_ONLY(event)) {
- sp_ui_close_view(NULL);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Q:
+ case GDK_KEY_q:
+ if (desktop->quick_zoomed()) {
+ ret = TRUE;
+ }
+ if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) {
+ desktop->zoom_quick(true);
+ ret = TRUE;
+ }
+ break;
- case GDK_KEY_Left: // Ctrl Left
- case GDK_KEY_KP_Left:
- case GDK_KEY_KP_4:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ case GDK_KEY_W:
+ case GDK_KEY_w:
+ case GDK_KEY_F4:
+ /* Close view */
+ if (MOD__CTRL_ONLY(event)) {
+ sp_ui_close_view(NULL);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(i, 0);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Left: // Ctrl Left
+ case GDK_KEY_KP_Left:
+ case GDK_KEY_KP_4:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Up: // Ctrl Up
- case GDK_KEY_KP_Up:
- case GDK_KEY_KP_8:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(i, 0);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(0, i);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Up: // Ctrl Up
+ case GDK_KEY_KP_Up:
+ case GDK_KEY_KP_8:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Right: // Ctrl Right
- case GDK_KEY_KP_Right:
- case GDK_KEY_KP_6:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(0, i);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(-i, 0);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Right: // Ctrl Right
+ case GDK_KEY_KP_Right:
+ case GDK_KEY_KP_6:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Down: // Ctrl Down
- case GDK_KEY_KP_Down:
- case GDK_KEY_KP_2:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(-i, 0);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(0, -i);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Down: // Ctrl Down
+ case GDK_KEY_KP_Down:
+ case GDK_KEY_KP_2:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Menu:
- sp_event_root_menu_popup(desktop, NULL, event);
- ret = TRUE;
- break;
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(0, -i);
+ ret = TRUE;
+ }
+ break;
- case GDK_KEY_F10:
- if (MOD__SHIFT_ONLY(event)) {
+ case GDK_KEY_Menu:
sp_event_root_menu_popup(desktop, NULL, event);
ret = TRUE;
- }
- break;
-
- case GDK_KEY_space:
- within_tolerance = true;
- xp = yp = 0;
- if (!allow_panning) break;
- panning = 4;
- this->space_panning = true;
- this->message_context->set(Inkscape::INFORMATION_MESSAGE,
- _("<b>Space+mouse move</b> to pan canvas"));
+ break;
- ret = TRUE;
- break;
+ case GDK_KEY_F10:
+ if (MOD__SHIFT_ONLY(event)) {
+ sp_event_root_menu_popup(desktop, NULL, event);
+ ret = TRUE;
+ }
+ break;
- case GDK_KEY_z:
- case GDK_KEY_Z:
- if (MOD__ALT_ONLY(event)) {
- desktop->zoom_grab_focus();
+ case GDK_KEY_space:
+ if (event->key.state & GDK_CONTROL_MASK) {
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
+ desktop->canvas->startRotateTo(desktop->namedview->document_rotation);
+ this->rotating_mode = true;
+ this->message_context->set(Inkscape::INFORMATION_MESSAGE,
+ _("<b>Space+mouse move</b> to rotate canvas, use modifiers on screen to change snaps"));
+ } else {
+ within_tolerance = true;
+ xp = yp = 0;
+ if (!allow_panning) break;
+ panning = 4;
+ this->space_panning = true;
+ this->message_context->set(Inkscape::INFORMATION_MESSAGE,
+ _("<b>Space+mouse move</b> to pan canvas"));
+ }
ret = TRUE;
+ break;
+
+ case GDK_KEY_z:
+ case GDK_KEY_Z:
+ if (MOD__ALT_ONLY(event)) {
+ desktop->zoom_grab_focus();
+ ret = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
}
break;
- default:
- break;
+ case GDK_KEY_RELEASE:
+ if (this->rotating_mode &&
+ get_group0_keyval(&event->key) != GDK_KEY_space &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_R )
+ {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ desktop->canvas->endRotateTo();
+ break;
}
- }
- break;
- case GDK_KEY_RELEASE:
- // Stop panning on any key release
- if (this->space_panning) {
- this->space_panning = false;
- this->message_context->clear();
- }
+ // Stop panning on any key release
+ if (this->space_panning) {
+ this->space_panning = false;
+ this->message_context->clear();
+ }
- if (panning) {
- panning = 0;
- xp = yp = 0;
+ if (panning) {
+ panning = 0;
+ xp = yp = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
- event->key.time);
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+ event->key.time);
- desktop->updateNow();
- }
+ desktop->updateNow();
+ }
- if (panning_cursor == 1) {
- panning_cursor = 0;
- GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
- gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
- }
+ if (panning_cursor == 1) {
+ panning_cursor = 0;
+ GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
+ gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
+ }
- switch (get_group0_keyval(&event->key)) {
- case GDK_KEY_space:
- if (within_tolerance) {
- // Space was pressed, but not panned
- sp_toggle_selector(desktop);
+ switch (get_group0_keyval(&event->key)) {
+ case GDK_KEY_space:
+ if (this->rotating_mode) {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ if (desktop->canvas->endRotateTo()) {
+ sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle);
+ }
+ }
+ if (within_tolerance) {
+ // Space was pressed, but not panned
+ sp_toggle_selector(desktop);
- // Be careful, sp_toggle_selector will delete ourselves.
- // Thus, make sure we return immediately.
- return true;
- }
+ // Be careful, sp_toggle_selector will delete ourselves.
+ // Thus, make sure we return immediately.
+ return true;
+ }
+ break;
- break;
+ case GDK_KEY_Q:
+ case GDK_KEY_q:
+ if (desktop->quick_zoomed()) {
+ desktop->zoom_quick(false);
+ ret = TRUE;
+ }
+ break;
- case GDK_KEY_Q:
- case GDK_KEY_q:
- if (desktop->quick_zoomed()) {
- desktop->zoom_quick(false);
- ret = TRUE;
+ default:
+ break;
}
break;
- default:
- break;
- }
- break;
-
- case GDK_SCROLL: {
- bool ctrl = (event->scroll.state & GDK_CONTROL_MASK);
- bool wheelzooms = prefs->getBool("/options/wheelzooms/value");
+ case GDK_SCROLL: {
+ if (this->rotating_mode) {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ desktop->canvas->endRotateTo();
+ }
+ bool ctrl = (event->scroll.state & GDK_CONTROL_MASK);
+ bool wheelzooms = prefs->getBool("/options/wheelzooms/value");
- int const wheel_scroll = prefs->getIntLimited(
- "/options/wheelscroll/value", 40, 0, 1000);
+ int const wheel_scroll = prefs->getIntLimited(
+ "/options/wheelscroll/value", 40, 0, 1000);
- // Size of smooth-scrolls (only used in GTK+ 3)
- gdouble delta_x = 0;
- gdouble delta_y = 0;
+ // Size of smooth-scrolls (only used in GTK+ 3)
+ gdouble delta_x = 0;
+ gdouble delta_y = 0;
- /* shift + wheel, pan left--right */
- if (event->scroll.state & GDK_SHIFT_MASK) {
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- desktop->scroll_world(wheel_scroll, 0);
- break;
+ /* shift + wheel, pan left--right */
+ if (event->scroll.state & GDK_SHIFT_MASK) {
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ desktop->scroll_world(wheel_scroll, 0);
+ break;
- case GDK_SCROLL_DOWN:
- desktop->scroll_world(-wheel_scroll, 0);
- break;
+ case GDK_SCROLL_DOWN:
+ desktop->scroll_world(-wheel_scroll, 0);
+ break;
- default:
- break;
- }
+ default:
+ break;
+ }
- /* ctrl + wheel, zoom in--out */
- } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) {
- double rel_zoom;
- double const zoom_inc = prefs->getDoubleLimited(
- "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
+ /* ctrl + wheel, zoom in--out */
+ } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) {
+ double rel_zoom;
+ double const zoom_inc = prefs->getDoubleLimited(
+ "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- rel_zoom = zoom_inc;
- break;
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ rel_zoom = zoom_inc;
+ break;
- case GDK_SCROLL_DOWN:
- rel_zoom = 1 / zoom_inc;
- break;
+ case GDK_SCROLL_DOWN:
+ rel_zoom = 1 / zoom_inc;
+ break;
- default:
- rel_zoom = 0.0;
- break;
- }
+ default:
+ rel_zoom = 0.0;
+ break;
+ }
- if (rel_zoom != 0.0) {
- Geom::Point const scroll_dt = desktop->point();
- desktop->zoom_relative_keep_point(scroll_dt, rel_zoom);
- }
+ if (rel_zoom != 0.0) {
+ Geom::Point const scroll_dt = desktop->point();
+ desktop->zoom_relative_keep_point(scroll_dt, rel_zoom);
+ }
- /* no modifier, pan up--down (left--right on multiwheel mice?) */
- } else {
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- desktop->scroll_world(0, wheel_scroll);
- break;
+ /* no modifier, pan up--down (left--right on multiwheel mice?) */
+ } else {
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ desktop->scroll_world(0, wheel_scroll);
+ break;
- case GDK_SCROLL_DOWN:
- desktop->scroll_world(0, -wheel_scroll);
- break;
+ case GDK_SCROLL_DOWN:
+ desktop->scroll_world(0, -wheel_scroll);
+ break;
- case GDK_SCROLL_LEFT:
- desktop->scroll_world(wheel_scroll, 0);
- break;
+ case GDK_SCROLL_LEFT:
+ desktop->scroll_world(wheel_scroll, 0);
+ break;
- case GDK_SCROLL_RIGHT:
- desktop->scroll_world(-wheel_scroll, 0);
- break;
+ case GDK_SCROLL_RIGHT:
+ desktop->scroll_world(-wheel_scroll, 0);
+ break;
- case GDK_SCROLL_SMOOTH:
- gdk_event_get_scroll_deltas(event, &delta_x, &delta_y);
- desktop->scroll_world(delta_x, delta_y);
- break;
+ case GDK_SCROLL_SMOOTH:
+ gdk_event_get_scroll_deltas(event, &delta_x, &delta_y);
+ desktop->scroll_world(delta_x, delta_y);
+ break;
+ }
}
+ break;
}
- break;
- }
- default:
- break;
+ default:
+ break;
}
-
return ret;
}
diff --git a/src/ui/tools/tool-base.h b/src/ui/tools/tool-base.h
index 58eb6f88e..3d22fc66f 100644
--- a/src/ui/tools/tool-base.h
+++ b/src/ui/tools/tool-base.h
@@ -176,6 +176,7 @@ public:
ShapeEditor* shape_editor;
bool space_panning;
+ bool rotating_mode;
DelayedSnapEvent *_delayed_snap_event;
bool _dse_callback_in_process;
diff --git a/src/viewbox.cpp b/src/viewbox.cpp
index 1b50fe71c..ebfc04d8a 100644
--- a/src/viewbox.cpp
+++ b/src/viewbox.cpp
@@ -17,6 +17,8 @@
#include "viewbox.h"
#include "enums.h"
#include "sp-item.h"
+#include "inkscape.h"
+#include "desktop.h"
SPViewBox::SPViewBox()
: viewBox_set(false)
@@ -25,6 +27,11 @@ SPViewBox::SPViewBox()
, aspect_align(SP_ASPECT_XMID_YMID) // Default per spec
, aspect_clip(SP_ASPECT_MEET)
, c2p(Geom::identity())
+ , vbt(Geom::identity())
+ , rotation(Geom::identity())
+ , angle(0)
+ , previous_angle(0)
+ , rotated(false)
{
}
@@ -159,6 +166,16 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) {
}
}
+double SPViewBox::get_rotation() {
+ return this->angle;
+}
+
+void SPViewBox::set_rotation(double angle_val) {
+ this->previous_angle = this->angle;
+ this->angle = angle_val;
+ this->rotated = true;
+}
+
// Apply scaling from viewbox
void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) {
@@ -222,22 +239,41 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) {
break;
}
}
-
/* Viewbox transform from scale and position */
- Geom::Affine q;
- q[0] = scale_x;
- q[1] = 0.0;
- q[2] = 0.0;
- q[3] = scale_y;
- q[4] = x - scale_x * this->viewBox.left();
- q[5] = y - scale_y * this->viewBox.top();
-
- // std::cout << " q\n" << q << std::endl;
-
- /* Append viewbox transformation */
- this->c2p = q * this->c2p;
+ vbt = Geom::identity();
+ vbt[0] = scale_x;
+ vbt[1] = 0.0;
+ vbt[2] = 0.0;
+ vbt[3] = scale_y;
+ vbt[4] = x - scale_x * this->viewBox.left();
+ vbt[5] = y - scale_y * this->viewBox.top();
+ /* Append viewbox and turn transformation */
+ Geom::Point page_center = this->viewBox.midpoint();
+ SPDesktop * desktop = SP_ACTIVE_DESKTOP;
+ if (this->angle > 0.0 || this->angle < 0.0 ) { //!0
+ if (desktop) {
+ rotation = Geom::Translate(page_center).inverse() * Geom::Rotate(Geom::rad_from_deg(angle)) * Geom::Translate(page_center);
+ this->c2p = rotation * vbt * this->c2p;
+ } else {
+ this->c2p = vbt * this->c2p;
+ }
+ } else {
+ this->c2p = vbt * this->c2p;
+ }
+ if (desktop && this->rotated) {
+ Geom::Rect view = desktop->get_display_area();
+ Geom::Point view_center = desktop->doc2dt(view.midpoint());
+ Geom::Affine center_rotation = Geom::identity();
+ center_rotation *= Geom::Translate(page_center * vbt).inverse();
+ center_rotation *= Geom::Rotate(Geom::rad_from_deg(this->angle - this->previous_angle));
+ center_rotation *= Geom::Translate(page_center * vbt);
+ view_center = desktop->dt2doc(view_center * center_rotation);
+ desktop->zoom_relative(view_center[Geom::X], view_center[Geom::Y], 1.0);
+ this->rotated = false;
+ }
}
+
SPItemCtx SPViewBox::get_rctx(const SPItemCtx* ictx, double scale_none) {
/* Create copy of item context */
diff --git a/src/viewbox.h b/src/viewbox.h
index c71abb610..2f107132c 100644
--- a/src/viewbox.h
+++ b/src/viewbox.h
@@ -36,7 +36,14 @@ public:
/* Child to parent additional transform */
Geom::Affine c2p;
+ Geom::Affine vbt;
+ Geom::Affine rotation;
+ double angle;
+ double previous_angle;
+ bool rotated;
+ double get_rotation();
+ void set_rotation(double angle_val);
void set_viewBox(const gchar* value);
void set_preserveAspectRatio(const gchar* value);
diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp
index 21bd31299..8dddb40eb 100644
--- a/src/widgets/desktop-widget.cpp
+++ b/src/widgets/desktop-widget.cpp
@@ -54,7 +54,7 @@
#include "ui/uxmanager.h"
#include "util/ege-appear-time-tracker.h"
#include "sp-root.h"
-
+#include "attributes.h"
// We're in the "widgets" directory, so no need to explicitly prefix these:
#include "button.h"
#include "gimp/ruler.h"
@@ -62,11 +62,11 @@
#include "spw-utilities.h"
#include "toolbox.h"
#include "widget-sizes.h"
-
#include "verbs.h"
#include <gtkmm/cssprovider.h>
#include <gtkmm/paned.h>
#include <gtkmm/messagedialog.h>
+#include <iomanip>
#if defined (SOLARIS) && (SOLARIS == 8)
#include "round.h"
@@ -105,8 +105,20 @@ static void sp_update_guides_lock( GtkWidget *button, gpointer data );
static void cms_adjust_toggled( GtkWidget *button, gpointer data );
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
static void cms_adjust_set_sensitive( SPDesktopWidget *dtw, bool enabled );
+static void sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw);
static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw);
+static gint sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer data);
+static bool sp_dtw_rotation_output (GtkSpinButton *spin, gpointer data);
+static void sp_dtw_rotation_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data);
+static void sp_dtw_rotate_minus_180 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_minus_135 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_minus_90 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_minus_45 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_0 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_45 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_90 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_135 (GtkMenuItem *item, SPDesktopWidget * data);
static gdouble sp_dtw_zoom_value_to_display (gdouble value);
static gdouble sp_dtw_zoom_display_to_value (gdouble value);
static gint sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer data);
@@ -593,6 +605,34 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw )
g_signal_connect (G_OBJECT (dtw->zoom_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->zoom_status);
dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "value_changed", G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "populate_popup", G_CALLBACK (sp_dtw_zoom_populate_popup), dtw);
+ auto css_provider_spinbutton = Gtk::CssProvider::create();
+ css_provider_spinbutton->load_from_data("* { padding-left: 2; padding-right: 2; padding-top: 0; padding-bottom: 0;}");
+ auto zoomstat = Glib::wrap(dtw->zoom_status);
+ zoomstat->set_name("ZoomStatus");
+ auto context_zoom = zoomstat->get_style_context();
+ context_zoom->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ // Rotate status spinbutton
+ dtw->rotation_status = gtk_spin_button_new_with_range (-360.0,360.0, 1.0);
+ gtk_widget_set_tooltip_text (dtw->rotation_status, _("Rotation. Can be interactive with CTRL+SPACEBAR or CTRL+MMB"));
+ gtk_widget_set_size_request (dtw->rotation_status, STATUS_ROTATION_WIDTH, -1);
+ gtk_entry_set_width_chars (GTK_ENTRY (dtw->rotation_status), 7);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dtw->rotation_status), FALSE);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (dtw->rotation_status), 2);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dtw->rotation_status), GTK_UPDATE_ALWAYS);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "input", G_CALLBACK (sp_dtw_rotation_input), dtw);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "output", G_CALLBACK (sp_dtw_rotation_output), dtw);
+ g_object_set_data (G_OBJECT (dtw->rotation_status), "dtw", dtw->canvas);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "focus-in-event", G_CALLBACK (spinbutton_focus_in), dtw->rotation_status);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->rotation_status);
+ dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "value_changed", G_CALLBACK (sp_desktop_widget_rotate_document), dtw);
+ dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "populate_popup", G_CALLBACK (sp_dtw_rotation_populate_popup), dtw);
+
+ auto rotstat = Glib::wrap(dtw->rotation_status);
+ rotstat->set_name("RotationStatus");
+ auto context_rotation = rotstat->get_style_context();
+ context_rotation->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
// Cursor coordinates
dtw->coord_status = gtk_grid_new();
@@ -619,12 +659,16 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw )
auto label_z = gtk_label_new(_("Z:"));
gtk_widget_set_name(label_z, "ZLabel");
+ auto label_r = gtk_label_new(_("R:"));
+ gtk_widget_set_name(label_r, "RLabel");
gtk_widget_set_halign(dtw->coord_status_x, GTK_ALIGN_END);
gtk_widget_set_halign(dtw->coord_status_y, GTK_ALIGN_END);
gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_x, 2, 0, 1, 1);
gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_y, 2, 1, 1, 1);
gtk_grid_attach(GTK_GRID(dtw->coord_status), label_z, 3, 0, 1, 2);
+ gtk_grid_attach(GTK_GRID(dtw->coord_status), label_r, 5, 0, 1, 2);
gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->zoom_status, 4, 0, 1, 2);
+ gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->rotation_status, 6, 0, 1, 2);
sp_set_font_size_smaller (dtw->coord_status);
@@ -692,6 +736,11 @@ static void sp_desktop_widget_dispose(GObject *object)
g_signal_handlers_disconnect_matched (G_OBJECT (dtw->zoom_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->zoom_status);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_populate_popup), dtw);
+ g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_input), dtw);
+ g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_output), dtw);
+ g_signal_handlers_disconnect_matched (G_OBJECT (dtw->rotation_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->rotation_status);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_desktop_widget_rotate_document), dtw);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_dtw_rotation_populate_popup), dtw);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas), (gpointer) G_CALLBACK (sp_desktop_widget_event), dtw);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas_tbl), (gpointer) G_CALLBACK (canvas_tbl_size_allocate), dtw);
@@ -1423,7 +1472,7 @@ void SPDesktopWidget::layoutWidgets()
} else {
gtk_widget_show_all (dtw->menubar);
}
-
+
if (!prefs->getBool(pref_root + "commands/state", true)) {
gtk_widget_hide (dtw->commands_toolbox);
} else {
@@ -1629,10 +1678,11 @@ SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview)
dtw->menubar = sp_ui_main_menubar (dtw->desktop);
gtk_widget_set_name(dtw->menubar, "MenuBar");
gtk_widget_show_all (dtw->menubar);
- gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0);
-
+ SPNamedView *nv = dtw->desktop->namedview;
+ gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, TRUE, TRUE, 0);
dtw->layoutWidgets();
-
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON (dtw->rotation_status), namedview->document_rotation);
+ sp_namedview_set_document_rotation(namedview);
std::vector<GtkWidget *> toolboxes;
toolboxes.push_back(dtw->tool_toolbox);
toolboxes.push_back(dtw->aux_toolbox);
@@ -1672,6 +1722,8 @@ sp_desktop_widget_update_rulers (SPDesktopWidget *dtw)
void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags)
{
SPNamedView *nv=SP_NAMEDVIEW(obj);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(this->rotation_status), desktop->namedview->document_rotation);
+ sp_namedview_set_document_rotation(nv);
if (flags & SP_OBJECT_MODIFIED_FLAG) {
this->dt2r = 1. / nv->display_units->factor;
@@ -1723,6 +1775,18 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags)
}
static void
+sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw)
+{
+ SPNamedView *nv = dtw->desktop->namedview;
+ double value = gtk_spin_button_get_value (spin);
+ if (!dtw->desktop->getDocument()->getRoot()->rotated && value != nv->document_rotation) {
+ sp_repr_set_svg_double(nv->getRepr(), "inkscape:document-rotation", value);
+ }
+ spinbutton_defocus (GTK_WIDGET(spin));
+}
+
+
+static void
sp_desktop_widget_adjustment_value_changed (GtkAdjustment */*adj*/, SPDesktopWidget *dtw)
{
if (dtw->update)
@@ -1802,6 +1866,34 @@ sp_dtw_zoom_output (GtkSpinButton *spin, gpointer /*data*/)
return TRUE;
}
+static gint
+sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer /*data*/)
+{
+ gdouble new_scrolled = gtk_spin_button_get_value (spin);
+ const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin));
+ gdouble new_typed = atof (b);
+
+ if (new_scrolled == new_typed) { // the new value is set by scrolling
+ *new_val = new_scrolled;
+ } else { // the new value is typed in
+ *new_val = new_typed;
+ }
+
+ return TRUE;
+}
+
+static bool
+sp_dtw_rotation_output (GtkSpinButton *spin, gpointer /*data*/)
+{
+ gchar b[64];
+ double val = gtk_spin_button_get_value (spin);
+ std::ostringstream s;
+ s.imbue(std::locale(""));;
+ s << std::fixed << std::setprecision(2) << val << "º";
+ gtk_entry_set_text (GTK_ENTRY (spin), s.str().c_str());
+ return TRUE;
+}
+
static void
sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data)
{
@@ -1940,6 +2032,110 @@ sp_dtw_zoom_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data)
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
}
+
+static void
+sp_dtw_rotation_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data)
+{
+ GList *children, *iter;
+ GtkWidget *item;
+ SPDesktopWidget *dtw = static_cast<SPDesktopWidget*>(data);
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ for ( iter = children ; iter ; iter = g_list_next (iter)) {
+ gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (iter->data));
+ }
+ g_list_free (children);
+
+ item = gtk_menu_item_new_with_label ("-180º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_180), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("-135º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_135), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("-90º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_90), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("-45º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_45), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("0º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_0), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("45º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_45), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+
+ item = gtk_menu_item_new_with_label ("90º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_90), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+
+ item = gtk_menu_item_new_with_label ("135º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_135), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+}
+
+static void
+sp_dtw_rotate_minus_180 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status),-180);
+}
+
+static void
+sp_dtw_rotate_minus_135 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -135);
+}
+
+static void
+sp_dtw_rotate_minus_90 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -90);
+}
+
+static void
+sp_dtw_rotate_minus_45 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -45);
+}
+
+static void
+sp_dtw_rotate_0 (GtkMenuItem */*item*/,SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 0);
+}
+
+static void
+sp_dtw_rotate_45 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 45);
+}
+
+static void
+sp_dtw_rotate_90 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 90);
+}
+
+static void
+sp_dtw_rotate_135 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 135);
+}
+
static void
sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor)
{
diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h
index 08966ad5f..61c3b8b37 100644
--- a/src/widgets/desktop-widget.h
+++ b/src/widgets/desktop-widget.h
@@ -78,7 +78,7 @@ struct SPDesktopWidget {
GtkWidget *hbox;
- GtkWidget *menubar, *statusbar;
+ GtkWidget *menubar, *statusbar, *rotatebar;
Inkscape::UI::Dialogs::SwatchesPanel *panels;
@@ -97,7 +97,9 @@ struct SPDesktopWidget {
GtkWidget *select_status;
GtkWidget *select_status_eventbox;
GtkWidget *zoom_status;
+ GtkWidget *rotation_status;
gulong zoom_update;
+ gulong rotation_update;
Inkscape::UI::Widget::Dock *dock;
diff --git a/src/widgets/widget-sizes.h b/src/widgets/widget-sizes.h
index 87c7ca2e0..186cf4730 100644
--- a/src/widgets/widget-sizes.h
+++ b/src/widgets/widget-sizes.h
@@ -28,6 +28,7 @@
#define STATUS_BAR_FONT_SIZE 10000
#define STATUS_ZOOM_WIDTH 57
+#define STATUS_ROTATION_WIDTH 57
#define SELECTED_STYLE_SB_WIDTH 48
#define SELECTED_STYLE_WIDTH 190