diff options
Diffstat (limited to 'src/desktop.cpp')
| -rw-r--r-- | src/desktop.cpp | 682 |
1 files changed, 373 insertions, 309 deletions
diff --git a/src/desktop.cpp b/src/desktop.cpp index d482d0d7f..c56c42267 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -23,12 +23,11 @@ #ifdef HAVE_CONFIG_H -# include "config.h" +#include <config.h> #endif #include "ui/dialog/dialog-manager.h" #include <glibmm/i18n.h> -#include <sigc++/functors/mem_fun.h> #include <2geom/transforms.h> #include <2geom/rect.h> @@ -41,39 +40,30 @@ #include "desktop-style.h" #include "device-manager.h" #include "display/canvas-arena.h" +#include "display/canvas-debug.h" #include "display/canvas-grid.h" +#include "display/canvas-rotate.h" #include "display/canvas-temporary-item-list.h" #include "display/drawing-group.h" #include "display/gnome-canvas-acetate.h" -#include "display/drawing.h" #include "display/snap-indicator.h" #include "display/sodipodi-ctrlrect.h" #include "display/sp-canvas-group.h" -#include "display/sp-canvas.h" #include "display/sp-canvas-util.h" -#include "document.h" #include "document-undo.h" #include "event-log.h" #include "helper/action-context.h" #include "ui/interface.h" #include "layer-fns.h" #include "layer-manager.h" -#include "layer-model.h" -#include "macros.h" #include "message-context.h" #include "message-stack.h" -#include "preferences.h" #include "resource-manager.h" #include "ui/tools/select-tool.h" -#include "selection.h" -#include "sp-item-group.h" -#include "sp-item-group.h" #include "sp-namedview.h" #include "sp-root.h" -#include "sp-defs.h" #include "ui/tool-factory.h" #include "widgets/desktop-widget.h" -#include "xml/repr.h" #include "helper/action.h" //sp_action_perform // TODO those includes are only for node tool quick zoom. Remove them after fixing it. @@ -132,15 +122,10 @@ SPDesktop::SPDesktop() : _widget( NULL ), _guides_message_context( NULL ), _active( false ), - _w2d(), - _d2w(), _doc2dt( Geom::Scale(1, -1) ), _image_render_observer(this, "/options/rendering/imageinoutlinemode"), grids_visible( false ) { - _d2w.setIdentity(); - _w2d.setIdentity(); - layers = new Inkscape::LayerModel(); layers->_layer_activated_signal.connect(sigc::bind(sigc::ptr_fun(_layer_activated), this)); layers->_layer_deactivated_signal.connect(sigc::bind(sigc::ptr_fun(_layer_deactivated), this)); @@ -237,10 +222,10 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWid // being selected? or does it intercept some of the events that should have gone to the // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142) gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); - guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); - sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); + guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); + sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); - controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); + controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL); // Set the select tool as the active tool. set_event_context2("/tools/select"); @@ -268,7 +253,8 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWid _doc2dt[5] = document->getHeight().value("px"); sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt); - _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this)); + _modified_connection = + namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this)); Inkscape::DrawingItem *ai = document->getRoot()->invoke_show( SP_CANVAS_ARENA (drawing)->drawing, @@ -330,6 +316,10 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWid temporary_item_list = new Inkscape::Display::TemporaryItemList( this ); snapindicator = new Inkscape::Display::SnapIndicator ( this ); + + canvas_rotate = sp_canvas_item_new (root, SP_TYPE_CANVAS_ROTATE, NULL); + sp_canvas_item_hide( canvas_rotate ); + // canvas_debug = sp_canvas_item_new (main, SP_TYPE_CANVAS_DEBUG, NULL); } @@ -491,7 +481,7 @@ SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempi } void SPDesktop::redrawDesktop() { - sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw + sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _current_affine.d2w()); // redraw } void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) { @@ -745,164 +735,299 @@ Geom::Point SPDesktop::point() const { Geom::Point p = _widget->getPointer(); Geom::Point pw = sp_canvas_window_to_world (canvas, p); - p = w2d(pw); - Geom::Rect const r = canvas->getViewbox(); - Geom::Point r0 = w2d(r.min()); - Geom::Point r1 = w2d(r.max()); - - if (p[Geom::X] >= r0[Geom::X] && - p[Geom::X] <= r1[Geom::X] && - p[Geom::Y] >= r1[Geom::Y] && - p[Geom::Y] <= r0[Geom::Y]) - { + if (r.interiorContains(pw)) { + p = w2d(pw); return p; - } else { - return (r0 + r1) / 2; } + + // Shouldn't happen + std::cerr << "SPDesktop::point(): point outside of canvas!" << std::endl; + Geom::Point r0 = w2d(r.min()); + Geom::Point r1 = w2d(r.max()); + return (r0 + r1) / 2.0; } + /** - * Put current zoom data in history list. + * Revert back to previous transform if possible. Note: current transform is + * always at front of stack. */ void -SPDesktop::push_current_zoom (std::list<Geom::Rect> &history) +SPDesktop::prev_transform() { - Geom::Rect area = get_display_area(); + if (transforms_past.empty()) { + std::cerr << "SPDesktop::prev_transform: current transform missing!" << std::endl; + return; + } - if (history.empty() || history.front() != area) { - history.push_front(area); + if (transforms_past.size() == 1) { + messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous transform.")); + return; } + + // Push current transform into future transforms list. + transforms_future.push_front( _current_affine ); + + // Remove the current transform from the past transforms list. + transforms_past.pop_front(); + + // restore previous transform + _current_affine = transforms_past.front(); + set_display_area (false); + } + /** - * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels). + * Set transform to next in list. */ -void -SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log) +void SPDesktop::next_transform() { - g_assert(_widget); - bool zoomChanged = false; - - // save the zoom - if (log) { - push_current_zoom(zooms_past); - // if we do a logged zoom, our zoom-forward list is invalidated, so delete it - zooms_future.clear(); + if (transforms_future.empty()) { + this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next transform.")); + return; } - double const cx = 0.5 * (x0 + x1); - double const cy = 0.5 * (y0 + y1); + // restore next transform + _current_affine = transforms_future.front(); + set_display_area (false); - // FIXME: This 2geom idiom doesn't allow us to declare dbox const - Geom::Rect viewbox = canvas->getViewbox(); - viewbox.expandBy(-border); + // remove the just-used transform from the future transforms list + transforms_future.pop_front(); - double scale = _d2w.descrim(); - double newscale; - if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) { - newscale = viewbox.dimensions()[Geom::X] / (x1 - x0); - } else { - newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0); - } + // push current transform into past transforms list + transforms_past.push_front( _current_affine ); +} - newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels' - int clear = FALSE; - if (!Geom::are_near(newscale, scale, Geom::EPSILON * scale)) { - // zoom changed - set new zoom factors - _d2w = Geom::Scale(newscale, -newscale); - _w2d = Geom::Scale(1/newscale, 1/-newscale); - redrawDesktop(); - clear = TRUE; - zoomChanged = true; +/** + * Clear transform lists. + */ +void +SPDesktop::clear_transform_history() +{ + transforms_past.clear(); + transforms_future.clear(); +} + + +/** + * Does all the dirty work in setting the display area. + * _current_affine must already be full updated (including offset). + * log: if true, save transform in transform stack for reuse. + */ +void +SPDesktop::set_display_area (bool log) +{ + // Save the transform + if (log) { + transforms_past.push_front( _current_affine ); + // if we do a logged transform, our transform-forward list is invalidated, so delete it + transforms_future.clear(); } - /* Calculate top left corner (in document pixels) */ - x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale; - y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale; + redrawDesktop(); // Scroll - canvas->scrollTo(x0 * newscale - border, y1 * -newscale - border, clear); + Geom::Point offset = _current_affine.getOffset(); + canvas->scrollTo(offset, true); + // To do: if transform unchanged call with 'false' (redraw only newly exposed areas). - /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ - //sp_box3d_context_update_lines(event_context); + /* Update perspective lines if we are in the 3D box tool (so that infinite ones are shown + * correctly) */ if (SP_IS_BOX3D_CONTEXT(event_context)) { SP_BOX3D_CONTEXT(event_context)->_vpdrag->updateLines(); } _widget->updateRulers(); - _widget->updateScrollbars(_d2w.descrim()); + _widget->updateScrollbars(_current_affine.getZoom()); _widget->updateZoom(); + _widget->updateRotation(); - if ( zoomChanged ) { - signal_zoom_changed.emit(_d2w.descrim()); - } + signal_zoom_changed.emit(_current_affine.getZoom()); } -void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log) + +/** + * Map the drawing to the window so that 'c' lies at 'w' where where 'c' + * is a point on the canvas and 'w' is position in window in screen pixels. + */ +void +SPDesktop::set_display_area (Geom::Point const &c, Geom::Point const &w, bool log) { - set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log); + // The relative offset needed to keep c at w. + Geom::Point offset = d2w(c) - w; + _current_affine.addOffset( offset ); + set_display_area( log ); +} + + +/** + * Map the center of rectangle 'r' (which specifies a non-rotated region of the + * drawing) to lie at the center of the window. The zoom factor is calculated such that + * the edges of 'r' closest to 'w' are 'border' length inside of the window (if + * there is no rotation). 'r' is in document pixel units, 'border' is in screen pixels. + */ +void +SPDesktop::set_display_area( Geom::Rect const &r, double border, bool log) +{ + // Create a rectangle the size of the window aligned with origin. + Geom::Rect w( Geom::Point(), canvas->getViewbox().dimensions() ); // Not the SVG 'viewBox'. + + // Shrink window to account for border padding. + w.expandBy( -border ); + + double zoom = 1.0; + // Determine which direction limits scale: + // if (r.width/w.width > r.height/w.height) then zoom using width. + // Avoiding division in test: + if ( r.width()*w.height() > r.height()*w.width() ) { + zoom = w.width() / r.width(); + } else { + zoom = w.height() / r.height(); + } + _current_affine.setScale( zoom ); + + // Zero offset, actual offset calculated later. + _current_affine.setOffset( Geom::Point( 0, 0 ) ); + + set_display_area( r.midpoint(), w.midpoint(), log ); } + /** - * Return viewbox dimensions. + * Return viewbox dimensions. FixMe: Doesn't handle rotation. FixMe InvertedY */ Geom::Rect SPDesktop::get_display_area() const { Geom::Rect const viewbox = canvas->getViewbox(); - - double const scale = _d2w[0]; + double const scale = _current_affine.getZoom(); /// @fixme hardcoded desktop transform return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale), Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale)); } + +/** + * Zoom keeping the point 'c' fixed in the desktop window. + */ +void +SPDesktop::zoom_absolute_keep_point (Geom::Point const &c, double zoom) +{ + zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); + Geom::Point w = d2w( c ); // Must be before zoom changed. + _current_affine.setScale( zoom ); + set_display_area( c, w ); +} + + +void +SPDesktop::zoom_relative_keep_point (Geom::Point const &c, double zoom) +{ + double new_zoom = _current_affine.getZoom() * zoom; + zoom_absolute_keep_point( c, new_zoom ); +} + + +/** + * Zoom aligning the point 'c' to the center of desktop window. + */ +void +SPDesktop::zoom_absolute_center_point (Geom::Point const &c, double zoom) +{ + zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); + _current_affine.setScale( zoom ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); +} + + +void +SPDesktop::zoom_relative_center_point (Geom::Point const &c, double zoom) +{ + double new_zoom = _current_affine.getZoom() * zoom; + zoom_absolute_center_point( c, new_zoom ); +} + + /** - * Revert back to previous zoom if possible. + * Set display area to origin and current document dimensions. */ void -SPDesktop::prev_zoom() +SPDesktop::zoom_page() { - if (zooms_past.empty()) { - messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom.")); + Geom::Rect d(Geom::Point(0, 0), + Geom::Point(doc()->getWidth().value("px"), doc()->getHeight().value("px"))); + + if (d.minExtent() < 1.0) { return; } - // push current zoom into forward zooms list - push_current_zoom (zooms_future); + set_display_area(d, 10); +} + +/** + * Set display area to current document width. + */ +void +SPDesktop::zoom_page_width() +{ + Geom::Rect const a = get_display_area(); - // restore previous zoom - Geom::Rect past = zooms_past.front(); - set_display_area (past.left(), past.top(), past.right(), past.bottom(), 0, false); + if (doc()->getWidth().value("px") < 1.0) { + return; + } - // remove the just-added zoom from the past zooms list - zooms_past.pop_front(); + Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]), + Geom::Point(doc()->getWidth().value("px"), a.midpoint()[Geom::Y])); + + set_display_area(d, 10); } + /** - * Set zoom to next in list. + * Zoom to whole drawing. */ -void SPDesktop::next_zoom() +void +SPDesktop::zoom_drawing() { - if (zooms_future.empty()) { - this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom.")); + g_return_if_fail (doc() != NULL); + SPItem *docitem = doc()->getRoot(); + g_return_if_fail (docitem != NULL); + + docitem->bbox_valid = FALSE; + Geom::OptRect d = docitem->desktopVisualBounds(); + + /* Note that the second condition here indicates that + ** there are no items in the drawing. + */ + if ( !d || d->minExtent() < 0.1 ) { return; } - // push current zoom into past zooms list - push_current_zoom (zooms_past); + set_display_area(*d, 10); +} + + +/** + * Zoom to selection. + */ +void +SPDesktop::zoom_selection() +{ + Geom::OptRect const d = selection->visualBounds(); - // restore next zoom - Geom::Rect future = zooms_future.front(); - set_display_area (future.left(), future.top(), future.right(), future.bottom(), 0, false); + if ( !d || d->minExtent() < 0.1 ) { + return; + } - // remove the just-used zoom from the zooms_future list - zooms_future.pop_front(); + set_display_area(*d, 10); } + /** * Performs a quick zoom into what the user is working on. * @@ -914,8 +1039,8 @@ void SPDesktop::zoom_quick(bool enable) return; } - if (enable == true) { - _quick_zoom_stored_area = get_display_area(); + if (enable) { + _quick_zoom_affine = _current_affine; bool zoomed = false; // TODO This needs to migrate into the node tool, but currently the design @@ -927,7 +1052,7 @@ void SPDesktop::zoom_quick(bool enable) double area = nodes.area(); // do not zoom if a single cusp node is selected aand the bounds // have zero area. - if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) { + if (!Geom::are_near(area, 0)) { set_display_area(nodes, true); zoomed = true; } @@ -936,274 +1061,190 @@ void SPDesktop::zoom_quick(bool enable) if (!zoomed) { Geom::OptRect const d = selection->visualBounds(); - if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) { + if (d) { set_display_area(*d, true); zoomed = true; } } if (!zoomed) { - zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0); + Geom::Rect const d_canvas = canvas->getViewbox(); // Not SVG 'viewBox' + Geom::Point midpoint = w2d(d_canvas.midpoint()); // Midpoint of drawing on canvas. + zoom_relative_center_point(midpoint, 2.0); } } else { - set_display_area(_quick_zoom_stored_area, false); + _current_affine = _quick_zoom_affine; + set_display_area( false ); } _quick_zoom_enabled = enable; return; } + /** - * Zoom to point with absolute zoom factor. + * Tell widget to let zoom widget grab keyboard focus. */ void -SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom) +SPDesktop::zoom_grab_focus() { - zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); - - // maximum or minimum zoom reached, but there's no exact equality because of rounding errors; - // this check prevents "sliding" when trying to zoom in at maximum zoom; - /// \todo someone please fix calculations properly and remove this hack - if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001)) - return; - - Geom::Rect const viewbox = canvas->getViewbox(); - - double const width2 = viewbox.dimensions()[Geom::X] / zoom; - double const height2 = viewbox.dimensions()[Geom::Y] / zoom; - - set_display_area(cx - px * width2, - cy - py * height2, - cx + (1 - px) * width2, - cy + (1 - py) * height2, - 0.0); + _widget->letZoomGrabFocus(); } -/** - * Apply the desktop's current style or the tool style to the object. - */ -void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text) -{ - SPCSSAttr *css_current = sp_desktop_get_style(this, with_text); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - if (prefs->getBool(tool_path + "/usecurrent") && css_current) { - obj->setCSS(css_current,"style"); - } else { - SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style"); - obj->setCSS(css,"style"); - sp_repr_css_attr_unref(css); - } - if (css_current) { - sp_repr_css_attr_unref(css_current); - } -} /** - * Zoom to center with absolute zoom factor. + * Rotate keeping the point 'c' fixed in the desktop window. */ void -SPDesktop::zoom_absolute (double cx, double cy, double zoom) +SPDesktop::rotate_absolute_keep_point (Geom::Point const &c, double rotate) +{ + Geom::Point w = d2w( c ); // Must be before rotate changed. + _current_affine.setRotate( rotate ); + set_display_area( c, w ); +} + + +void +SPDesktop::rotate_relative_keep_point (Geom::Point const &c, double rotate) { - zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom); + Geom::Point w = d2w( c ); // Must be before rotate changed. + _current_affine.addRotate( rotate ); + set_display_area( c, w ); } + /** - * Zoom to point with relative zoom factor. + * Rotate aligning the point 'c' to the center of desktop window. */ -void -SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom) +void +SPDesktop::rotate_absolute_center_point (Geom::Point const &c, double rotate) { - Geom::Rect const area = get_display_area(); - - if (cx < area.min()[Geom::X]) { - cx = area.min()[Geom::X]; - } - if (cx > area.max()[Geom::X]) { - cx = area.max()[Geom::X]; - } - if (cy < area.min()[Geom::Y]) { - cy = area.min()[Geom::Y]; - } - if (cy > area.max()[Geom::Y]) { - cy = area.max()[Geom::Y]; - } + _current_affine.setRotate( rotate ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); +} - gdouble const scale = _d2w.descrim() * zoom; - double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X]; - double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y]; - zoom_absolute_keep_point(cx, cy, px, py, scale); +void +SPDesktop::rotate_relative_center_point (Geom::Point const &c, double rotate) +{ + _current_affine.addRotate( rotate ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); } + /** - * Zoom to center with relative zoom factor. + * Flip keeping the point 'c' fixed in the desktop window. */ void -SPDesktop::zoom_relative (double cx, double cy, double zoom) +SPDesktop::flip_absolute_keep_point (Geom::Point const &c, CanvasFlip flip) { - gdouble scale = _d2w.descrim() * zoom; - zoom_absolute (cx, cy, scale); + Geom::Point w = d2w( c ); // Must be before flip. + _current_affine.setFlip( flip ); + set_display_area( c, w ); } -/** - * Set display area to origin and current document dimensions. - */ + void -SPDesktop::zoom_page() +SPDesktop::flip_relative_keep_point (Geom::Point const &c, CanvasFlip flip) { - Geom::Rect d(Geom::Point(0, 0), - Geom::Point(doc()->getWidth().value("px"), doc()->getHeight().value("px"))); - - if (d.minExtent() < 1.0) { - return; - } - - set_display_area(d, 10); + Geom::Point w = d2w( c ); // Must be before flip. + _current_affine.addFlip( flip ); + set_display_area( c, w ); } + /** - * Set display area to current document width. + * Flip aligning the point 'c' to the center of desktop window. */ -void -SPDesktop::zoom_page_width() +void +SPDesktop::flip_absolute_center_point (Geom::Point const &c, CanvasFlip flip) { - Geom::Rect const a = get_display_area(); - - if (doc()->getWidth().value("px") < 1.0) { - return; - } + _current_affine.setFlip( flip ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); +} - Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]), - Geom::Point(doc()->getWidth().value("px"), a.midpoint()[Geom::Y])); - set_display_area(d, 10); +void +SPDesktop::flip_relative_center_point (Geom::Point const &c, CanvasFlip flip) +{ + _current_affine.addFlip( flip ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); } + /** - * Zoom to selection. + * Scroll canvas by to a particular point (window coordinates). */ void -SPDesktop::zoom_selection() +SPDesktop::scroll_absolute (Geom::Point const &point, bool is_scrolling) { - Geom::OptRect const d = selection->visualBounds(); + canvas->scrollTo(point, FALSE, is_scrolling); + _current_affine.setOffset( point ); - if ( !d || d->minExtent() < 0.1 ) { - return; - } + /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ + //sp_box3d_context_update_lines(event_context); + if (SP_IS_BOX3D_CONTEXT(event_context)) { + SP_BOX3D_CONTEXT(event_context)->_vpdrag->updateLines(); + } - set_display_area(*d, 10); + _widget->updateRulers(); + _widget->updateScrollbars(_current_affine.getZoom()); } -/** - * Tell widget to let zoom widget grab keyboard focus. - */ -void -SPDesktop::zoom_grab_focus() -{ - _widget->letZoomGrabFocus(); -} /** - * Zoom to whole drawing. + * Scroll canvas by specific coordinate amount (window coordinates). */ void -SPDesktop::zoom_drawing() +SPDesktop::scroll_relative (Geom::Point const &delta, bool is_scrolling) { - g_return_if_fail (doc() != NULL); - SPItem *docitem = doc()->getRoot(); - g_return_if_fail (docitem != NULL); - - docitem->bbox_valid = FALSE; - Geom::OptRect d = docitem->desktopVisualBounds(); - - /* Note that the second condition here indicates that - ** there are no items in the drawing. - */ - if ( !d || d->minExtent() < 0.1 ) { - return; - } - - set_display_area(*d, 10); + Geom::Rect const viewbox = canvas->getViewbox(); + scroll_absolute( viewbox.min() - delta, is_scrolling ); } + /** * Scroll canvas by specific coordinate amount in svg coordinates. */ void -SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling) +SPDesktop::scroll_relative_in_svg_coords (double dx, double dy, bool is_scrolling) { - double scale = _d2w.descrim(); - scroll_world(dx*scale, dy*scale, is_scrolling); + double scale = _current_affine.getZoom(); + scroll_relative(Geom::Point(dx*scale, dy*scale), is_scrolling); } + /** - * Scroll canvas by specific coordinate amount. + * Scroll screen so as to keep point 'p' visible in window. + * (Used, for example, when a node is being dragged.) + * 'p': The point in desktop coordinates. + * 'autoscrollspeed': The scroll speed (or zero to use preferences' value). */ -void -SPDesktop::scroll_world (double dx, double dy, bool is_scrolling) -{ - g_assert(_widget); - - Geom::Rect const viewbox = canvas->getViewbox(); - - canvas->scrollTo(viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling); - - /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ - //sp_box3d_context_update_lines(event_context); - if (SP_IS_BOX3D_CONTEXT(event_context)) { - SP_BOX3D_CONTEXT(event_context)->_vpdrag->updateLines(); - } - - _widget->updateRulers(); - _widget->updateScrollbars(_d2w.descrim()); -} - bool SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed) { - using Geom::X; - using Geom::Y; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + // autoscrolldistance is in screen pixels. gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000); - // autoscrolldistance is in screen pixels, but the display area is in document units - autoscrolldistance /= _d2w.descrim(); - // FIXME: This 2geom idiom doesn't allow us to declare dbox const - Geom::Rect dbox = get_display_area(); - dbox.expandBy(-autoscrolldistance); - - if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) || - !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) { - - Geom::Point const s_w( p * (Geom::Affine)_d2w ); - - gdouble x_to; - if (p[X] < dbox.min()[X]) - x_to = dbox.min()[X]; - else if (p[X] > dbox.max()[X]) - x_to = dbox.max()[X]; - else - x_to = p[X]; - - gdouble y_to; - if (p[Y] < dbox.min()[Y]) - y_to = dbox.min()[Y]; - else if (p[Y] > dbox.max()[Y]) - y_to = dbox.max()[Y]; - else - y_to = p[Y]; - - Geom::Point const d_dt(x_to, y_to); - Geom::Point const d_w( d_dt * _d2w ); - Geom::Point const moved_w( d_w - s_w ); + Geom::Rect w = canvas->getViewbox(); // Window in screen coordinates. + w.expandBy(-autoscrolldistance); // Shrink window + + Geom::Point c = d2w(p); // Point 'p' in screen coordinates. + if (!w.contains(c)) { + + Geom::Point c2 = w.clamp(c); // Constrain c to window. if (autoscrollspeed == 0) autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10); if (autoscrollspeed != 0) - scroll_world (autoscrollspeed * moved_w); + scroll_relative (autoscrollspeed * (c2 - c) ); return true; } @@ -1392,6 +1433,28 @@ SPDesktop::onWindowStateEvent (GdkEventWindowState* event) return false; } + +/** + * Apply the desktop's current style or the tool style to the object. + */ +void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text) +{ + SPCSSAttr *css_current = sp_desktop_get_style(this, with_text); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + if (prefs->getBool(tool_path + "/usecurrent") && css_current) { + obj->setCSS(css_current,"style"); + } else { + SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style"); + obj->setCSS(css,"style"); + sp_repr_css_attr_unref(css); + } + if (css_current) { + sp_repr_css_attr_unref(css_current); + } +} + + void SPDesktop::setToolboxFocusTo (gchar const *label) { @@ -1444,11 +1507,7 @@ void SPDesktop::setWaitingCursor() GdkDisplay *display = gdk_display_get_default(); GdkCursor *waiting = gdk_cursor_new_for_display(display, GDK_WATCH); gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(getCanvas())), waiting); -#if GTK_CHECK_VERSION(3,0,0) g_object_unref(waiting); -#else - gdk_cursor_unref(waiting); -#endif // GDK needs the flush for the cursor change to take effect gdk_flush(); waiting_cursor = true; @@ -1545,13 +1604,18 @@ SPDesktop::setDocument (SPDocument *doc) } layers->setDocument(doc); - - // remove old EventLog if it exists (see also: bug #1071082) - if (event_log) { - doc->removeUndoObserver(*event_log); - delete event_log; - event_log = 0; - } + selection->setDocument(doc); + + if (event_log) { + // Remove it from the replaced document. This prevents Inkscape from + // crashing since we access it in the replaced document's destructor + // which results in an undefined behavior. (See also: bug #1670688) + if (this->doc()) { + this->doc()->removeUndoObserver(*event_log); + } + delete event_log; + event_log = 0; + } /* setup EventLog */ event_log = new Inkscape::EventLog(doc); @@ -1638,7 +1702,7 @@ SPDesktop::_onSelectionModified (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt) { if (!dt->_widget) return; - dt->_widget->updateScrollbars (dt->_d2w.descrim()); + dt->_widget->updateScrollbars (dt->_current_affine.getZoom()); } static void @@ -1780,17 +1844,17 @@ static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop) Geom::Affine SPDesktop::w2d() const { - return _w2d; + return _current_affine.w2d(); } Geom::Point SPDesktop::w2d(Geom::Point const &p) const { - return p * _w2d; + return p * _current_affine.w2d(); } Geom::Point SPDesktop::d2w(Geom::Point const &p) const { - return p * _d2w; + return p * _current_affine.d2w(); } Geom::Affine SPDesktop::doc2dt() const |
