// SPDX-License-Identifier: GPL-2.0-or-later /** * A class for previewing a canvas rotation. */ /* * Author: * Tavmjong Bah * * Copyright (C) 2020 Tavmjong Bah * * Rewrite of SPCanvasRotate. * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include #include "canvas-item-rotate.h" #include "color.h" // SP_RGBA_x_F #include "desktop.h" #include "inkscape.h" #include "display/cairo-utils.h" // Copy Cairo surface. #include "ui/widget/canvas.h" namespace Inkscape { /** * Create an null control rotate. */ CanvasItemRotate::CanvasItemRotate(CanvasItemGroup *group) : CanvasItem(group) { _name = "CanvasItemRotate"; _pickable = true; // We need the events! _bounds = Geom::Rect(-Geom::infinity(), -Geom::infinity(), Geom::infinity(), Geom::infinity()); } /** * Returns distance between point in canvas units and nearest point on rotate. */ double CanvasItemRotate::closest_distance_to(Geom::Point const &p) { double d = Geom::infinity(); std::cerr << "CanvasItemRotate::closest_distance_to: Not implemented!" << std::endl; return d; } /** * Returns true if point p (in canvas units) is within tolerance (canvas units) distance of rotate. */ bool CanvasItemRotate::contains(Geom::Point const &p, double tolerance) { return true; // We're always picked! } /** * Update and redraw control rotate. */ void CanvasItemRotate::update(Geom::Affine const &affine) { _affine = affine; request_redraw(); } /** * Render rotate to screen via Cairo. */ void CanvasItemRotate::render(Inkscape::CanvasItemBuffer *buf) { return; // We do no proper rendering! } /** * Start */ void CanvasItemRotate::start(SPDesktop *desktop) { _desktop = desktop; _current_angle = 0.0; _surface_copy = ink_cairo_surface_copy(_canvas->get_backing_store()); } /** * Render to widget. */ void CanvasItemRotate::paint() { auto background = _canvas->get_backing_store(); if (!background) { std::cerr << "CanvasItemRotate::paint(): No background!" << std::endl; return; } double width = background->get_width(); double height = background->get_height(); // Draw rotated canvas. auto context = Cairo::Context::create(background); context->set_operator(Cairo::OPERATOR_SOURCE); context->translate(width/2.0, height/2.0); context->rotate(Geom::rad_from_deg(-_current_angle)); context->translate(-width/2.0, -height/2.0); context->set_source(_surface_copy, 0, 0); context->paint(); _canvas->queue_draw(); } /** * Handle events. */ bool CanvasItemRotate::handle_event(GdkEvent *event) { // Get geometry Geom::Rect viewbox = _canvas->get_area_world(); _center = viewbox.midpoint(); switch (event->type) { case GDK_MOTION_NOTIFY: { Geom::Point cursor( event->motion.x, event->motion.y ); // Both cursor and center are in window coordinates Geom::Point rcursor( cursor - _center ); double angle = Geom::deg_from_rad( Geom::atan2(rcursor) ); // Set start angle if (_start_angle < -360) { _start_angle = angle; } const double rotation_snap = 15; double delta_angle = _start_angle - angle; if (event->motion.state & GDK_SHIFT_MASK && event->motion.state & GDK_CONTROL_MASK) { delta_angle = 0; } else if (event->motion.state & GDK_SHIFT_MASK) { delta_angle = round(delta_angle/rotation_snap) * rotation_snap; } else if (event->motion.state & GDK_CONTROL_MASK) { // ? } else if (event->motion.state & GDK_MOD1_MASK) { // Decimal raw angle } else { delta_angle = floor(delta_angle); } _current_angle = delta_angle; // Correct line for snapping of angle double distance = rcursor.length(); _cursor = Geom::Point::polar( Geom::rad_from_deg(angle), distance ); // Update screen paint(); break; } case GDK_BUTTON_RELEASE: { // Rotate the actual canvas SPDesktop *desktop = SP_ACTIVE_DESKTOP; // FIXME.. desktop->rotate_relative_center_point (desktop->w2d(_center), (desktop->w2d().det() > 0 ? -1 : 1) * Geom::rad_from_deg(_current_angle) ); // We're done ungrab(); hide(); _start_angle = -1000; break; } case GDK_KEY_PRESS: // std::cout << " Key press: " << std::endl; break; case GDK_KEY_RELEASE: // std::cout << " Key release: " << std::endl; break; default: // ui_dump_event (event, "sp_canvas_rotate_event: unwanted event: "); break; } if (event->type == GDK_KEY_PRESS) return false; // Why? // Don't emit event signal! return true; } } // namespace Inkscape /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :