mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2025-09-13 20:23:04 +02:00
git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@4900 bfdf4180-ca20-0410-9c96-a3a8aa849046
664 lines
20 KiB
C++
664 lines
20 KiB
C++
/*
|
|
Copyright 2006-2017 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "elementprimitivedecorator.h"
|
|
#include "elementscene.h"
|
|
#include "customelementpart.h"
|
|
#include "editorcommands.h"
|
|
#include "qet.h"
|
|
#include <QPainter>
|
|
#include <QGraphicsSceneHoverEvent>
|
|
#include <QStyleOptionGraphicsItem>
|
|
#include <QGraphicsScene>
|
|
|
|
/**
|
|
Constructor
|
|
@param parent Parent QGraphicsItem
|
|
*/
|
|
ElementPrimitiveDecorator::ElementPrimitiveDecorator(QGraphicsItem *parent):
|
|
QGraphicsObject(parent),
|
|
m_handler(10)
|
|
{
|
|
init();
|
|
m_handler.setOuterColor(Qt::darkGreen);
|
|
}
|
|
|
|
/**
|
|
Destructor
|
|
*/
|
|
ElementPrimitiveDecorator::~ElementPrimitiveDecorator() {
|
|
}
|
|
|
|
/**
|
|
@return the internal bouding rect, i.e. the smallest rectangle containing
|
|
the bounding rectangle of every selected item.
|
|
*/
|
|
QRectF ElementPrimitiveDecorator::internalBoundingRect() const {
|
|
if (!decorated_items_.count() || !scene()) return(QRectF());
|
|
|
|
//if @decorated_items_ contain one item and if this item is a vertical or horizontal partline, apply a specific methode
|
|
if ((decorated_items_.count() == 1) && (decorated_items_.first() -> xmlName() == "line")) {
|
|
QRectF horto = decorated_items_.first() -> sceneGeometricRect();
|
|
if (!horto.width() || !horto.height()) {
|
|
return (getSceneBoundingRect(decorated_items_.first() -> toItem()));
|
|
}
|
|
}
|
|
QRectF rect = decorated_items_.first() -> sceneGeometricRect();
|
|
for (CustomElementPart *item: decorated_items_) {
|
|
rect = rect.united(item -> sceneGeometricRect());
|
|
}
|
|
return(rect);
|
|
}
|
|
/**
|
|
@return the outer bounds of the decorator as a rectangle.
|
|
*/
|
|
QRectF ElementPrimitiveDecorator::boundingRect() const
|
|
{
|
|
QVector<QRectF> rect_vector = m_handler.handlerRect(getResizingsPoints());
|
|
|
|
QRectF rect = effective_bounding_rect_;
|
|
rect |= rect_vector.first();
|
|
rect |= rect_vector.last();
|
|
return(rect);
|
|
}
|
|
|
|
/**
|
|
Paint the contents of an item in local coordinates, using \a painter, with
|
|
respect to \a option and
|
|
@param option The option parameter provides style options for the item, such
|
|
as its state, exposed area and its level-of-detail hints.
|
|
@param The widget argument is optional. If provided, it points to the
|
|
widget that is being painted on; otherwise, it is 0. For cached painting,
|
|
widget is always 0.
|
|
*/
|
|
void ElementPrimitiveDecorator::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
{
|
|
Q_UNUSED(option)
|
|
Q_UNUSED(widget)
|
|
painter -> save();
|
|
|
|
// paint the original bounding rect
|
|
QPen pen(Qt::DashLine);
|
|
pen.setCosmetic(true);
|
|
painter -> setPen(pen);
|
|
painter -> drawRect(modified_bounding_rect_);
|
|
|
|
//Draw the handlers
|
|
m_handler.drawHandler(painter, getResizingsPoints());
|
|
|
|
// uncomment to draw the real bouding rect (=adjusted internal bounding rect)
|
|
// painter -> setBrush(QBrush(QColor(240, 0, 0, 127)));
|
|
// painter -> drawRect(boundingRect());
|
|
painter -> restore();
|
|
}
|
|
|
|
/**
|
|
@param items the new list of items this decorator is suposed to manipulate.
|
|
*/
|
|
void ElementPrimitiveDecorator::setItems(const QList<CustomElementPart *> &items) {
|
|
if (CustomElementPart *single_item = singleItem()) {
|
|
if (items.count() == 1 && items.first() == single_item) {
|
|
// no actual change
|
|
goto end_setItems;
|
|
}
|
|
|
|
// break any connection between the former single selected item (if any) and
|
|
// the decorator
|
|
single_item -> setDecorator(0);
|
|
if (QGraphicsObject *single_object = dynamic_cast<QGraphicsObject *>(single_item)) {
|
|
disconnect(single_object, 0, this, 0);
|
|
}
|
|
}
|
|
|
|
decorated_items_ = items;
|
|
|
|
// when only a single primitive is selected, the decorator behaves specially
|
|
// to enable extra features, such as text edition, internal points movements,
|
|
// etc.
|
|
if (CustomElementPart *single_item = singleItem()) {
|
|
single_item -> setDecorator(this);
|
|
}
|
|
|
|
end_setItems:
|
|
adjust();
|
|
show();
|
|
if (focusItem() != this) {
|
|
setFocus();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@param items the new list of items this decorator is suposed to manipulate.
|
|
*/
|
|
void ElementPrimitiveDecorator::setItems(const QList<QGraphicsItem *> &items) {
|
|
QList<CustomElementPart *> primitives;
|
|
for (QGraphicsItem *item: items) {
|
|
if (CustomElementPart *part_item = dynamic_cast<CustomElementPart *>(item)) {
|
|
primitives << part_item;
|
|
}
|
|
}
|
|
setItems(primitives);
|
|
}
|
|
|
|
/**
|
|
@return the list of items this decorator is supposed to manipulate
|
|
*/
|
|
QList<CustomElementPart *> ElementPrimitiveDecorator::items() const {
|
|
return(decorated_items_);
|
|
}
|
|
|
|
/**
|
|
@return the list of items this decorator is supposed to manipulate
|
|
*/
|
|
QList<QGraphicsItem *> ElementPrimitiveDecorator::graphicsItems() const {
|
|
QList<QGraphicsItem *> list;
|
|
for (CustomElementPart *part_item: decorated_items_) {
|
|
if (QGraphicsItem *item = dynamic_cast<QGraphicsItem *>(part_item)) {
|
|
list << item;
|
|
}
|
|
}
|
|
return(list);
|
|
}
|
|
|
|
/**
|
|
Adjust the visual decorator according to the currently assigned items.
|
|
It is notably called by setItems().
|
|
*/
|
|
void ElementPrimitiveDecorator::adjust() {
|
|
saveOriginalBoundingRect();
|
|
modified_bounding_rect_ = original_bounding_rect_;
|
|
adjustEffectiveBoundingRect();
|
|
}
|
|
|
|
/**
|
|
Handle events generated when the mouse hovers over the decorator.
|
|
@param event Object describing the hover event.
|
|
*/
|
|
void ElementPrimitiveDecorator::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
|
|
{
|
|
int p = m_handler.pointIsHoverHandler(event->pos(), getResizingsPoints());
|
|
|
|
if (p == 0 || p == 7)
|
|
setCursor(Qt::SizeFDiagCursor);
|
|
else if (p == 2 || p == 5)
|
|
setCursor(Qt::SizeBDiagCursor);
|
|
else if (p == 1 || p ==6)
|
|
setCursor(Qt::SizeVerCursor);
|
|
else if (p == 3 || p == 4)
|
|
setCursor(Qt::SizeHorCursor);
|
|
else if (p == -1 && modified_bounding_rect_.normalized().contains(event->pos()))
|
|
setCursor(Qt::SizeAllCursor);
|
|
else
|
|
setCursor(Qt::ArrowCursor);
|
|
}
|
|
|
|
/**
|
|
Handle event generated when mouse buttons are pressed.
|
|
@param event Object describing the mouse event
|
|
*/
|
|
void ElementPrimitiveDecorator::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
QPointF pos = event -> pos();
|
|
QVector <QPointF> points = getResizingsPoints();
|
|
|
|
current_operation_square_ = m_handler.pointIsHoverHandler(pos, points);
|
|
bool accept = false;
|
|
|
|
if (current_operation_square_ != QET::NoOperation)
|
|
accept = true;
|
|
else
|
|
{
|
|
if (internalBoundingRect().contains(pos))
|
|
{
|
|
if (CustomElementPart *single_item = singleItem())
|
|
{
|
|
bool event_accepted = single_item -> singleItemPressEvent(this, event);
|
|
if (event_accepted)
|
|
{
|
|
event -> ignore();
|
|
return;
|
|
}
|
|
}
|
|
current_operation_square_ = QET::MoveArea;
|
|
accept = true;
|
|
}
|
|
}
|
|
|
|
if (accept)
|
|
{
|
|
if (current_operation_square_ > QET::NoOperation)
|
|
first_pos_ = latest_pos_ = mapToScene(points.at(current_operation_square_));
|
|
else
|
|
{
|
|
first_pos_ = decorated_items_.at(0) -> toItem() -> scenePos();
|
|
latest_pos_ = event -> scenePos();
|
|
mouse_offset_ = event -> scenePos() - first_pos_;
|
|
}
|
|
startMovement();
|
|
event -> accept();
|
|
}
|
|
else
|
|
event -> ignore();
|
|
}
|
|
|
|
/**
|
|
Handle events generated when mouse buttons are double clicked.
|
|
@param event Object describing the mouse event
|
|
*/
|
|
void ElementPrimitiveDecorator::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
|
|
//QGraphicsObject::mouseDoubleClickEvent(event);
|
|
if (CustomElementPart *single_item = singleItem()) {
|
|
bool event_accepted = single_item -> singleItemDoubleClickEvent(this, event);
|
|
if (event_accepted) {
|
|
event -> ignore();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Handle event generated when the mouse is moved and the decorator is the mouse grabber item.
|
|
@param event Object describing the mouse event
|
|
@see QGraphicsScene::mouseGrabberItem()
|
|
*/
|
|
void ElementPrimitiveDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
QPointF scene_pos = event -> scenePos();
|
|
QPointF movement = scene_pos - latest_pos_;
|
|
|
|
if (current_operation_square_ > QET::NoOperation) {
|
|
// This is a scaling operation.
|
|
|
|
// For convenience purposes, we may need to adjust mouse movements.
|
|
QET::ScalingMethod scaling_method = scalingMethod(event);
|
|
if (scaling_method > QET::FreeScaling) {
|
|
// real, non-rounded movement from the mouse press event
|
|
QPointF global_movement = scene_pos - first_pos_;
|
|
|
|
QPointF rounded_global_movement;
|
|
if (scaling_method == QET::SnapScalingPointToGrid) {
|
|
// real, rounded movement from the mouse press event
|
|
rounded_global_movement = snapConstPointToGrid(global_movement);
|
|
}
|
|
else {
|
|
QRectF new_bounding_rect = original_bounding_rect_;
|
|
applyMovementToRect(current_operation_square_, global_movement, new_bounding_rect);
|
|
|
|
const qreal scale_epsilon = 20.0; // rounds to 0.05
|
|
QPointF delta = deltaForRoundScaling(original_bounding_rect_, new_bounding_rect, scale_epsilon);
|
|
|
|
// real, rounded movement from the mouse press event
|
|
rounded_global_movement = global_movement + delta;
|
|
}
|
|
|
|
// rounded position of the current mouse move event
|
|
QPointF rounded_scene_pos = first_pos_ + rounded_global_movement;
|
|
|
|
// when scaling the selection, consider the center of the currently dragged resizing rectangle
|
|
QPointF current_position = mapToScene(getResizingsPoints().at(current_operation_square_));
|
|
// determine the final, effective movement
|
|
movement = rounded_scene_pos - current_position;
|
|
}
|
|
}
|
|
else if (current_operation_square_ == QET::MoveArea) {
|
|
// When moving the selection, consider the position of the first selected item
|
|
QPointF current_position = scene_pos - mouse_offset_;
|
|
QPointF rounded_current_position = snapConstPointToGrid(current_position);
|
|
movement = rounded_current_position - decorated_items_.at(0) -> toItem() -> scenePos();
|
|
}
|
|
else {
|
|
// Neither a movement nor a scaling operation -- perhaps the underlying item
|
|
// is interested in the mouse event for custom operations?
|
|
if (CustomElementPart *single_item = singleItem()) {
|
|
bool event_accepted = single_item -> singleItemMoveEvent(this, event);
|
|
if (event_accepted) {
|
|
event -> ignore();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
QRectF bounding_rect = modified_bounding_rect_;
|
|
applyMovementToRect(current_operation_square_, movement, modified_bounding_rect_);
|
|
if (modified_bounding_rect_ != bounding_rect) {
|
|
adjustEffectiveBoundingRect();
|
|
}
|
|
latest_pos_ = event -> scenePos();
|
|
|
|
if (current_operation_square_ == QET::MoveArea) {
|
|
translateItems(movement);
|
|
} else {
|
|
scaleItems(original_bounding_rect_, modified_bounding_rect_);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Handle event generated when a mouse buttons are releaseis moved and the
|
|
decorator is the mouse grabber item.
|
|
@param event Object describing the mouse event
|
|
@see QGraphicsScene::mouseGrabberItem()
|
|
*/
|
|
void ElementPrimitiveDecorator::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
|
|
Q_UNUSED(event)
|
|
|
|
ElementEditionCommand *command = 0;
|
|
if (current_operation_square_ > QET::NoOperation) {
|
|
ScalePartsCommand *scale_command = new ScalePartsCommand();
|
|
scale_command -> setScaledPrimitives(items());
|
|
scale_command -> setTransformation(
|
|
mapToScene(original_bounding_rect_).boundingRect(),
|
|
mapToScene(modified_bounding_rect_).boundingRect()
|
|
);
|
|
command = scale_command;
|
|
} else if (current_operation_square_ == QET::MoveArea) {
|
|
QPointF movement = mapToScene(modified_bounding_rect_.topLeft()) - mapToScene(original_bounding_rect_.topLeft());
|
|
if (!movement.isNull()) {
|
|
MovePartsCommand *move_command = new MovePartsCommand(movement, 0, graphicsItems());
|
|
command = move_command;
|
|
}
|
|
} else {
|
|
if (CustomElementPart *single_item = singleItem()) {
|
|
bool event_accepted = single_item -> singleItemReleaseEvent(this, event);
|
|
if (event_accepted) {
|
|
event -> ignore();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (command) {
|
|
emit(actionFinished(command));
|
|
}
|
|
|
|
if (current_operation_square_ != QET::NoOperation) {
|
|
adjust();
|
|
}
|
|
|
|
current_operation_square_ = QET::NoOperation;
|
|
}
|
|
|
|
/**
|
|
@reimp QGraphicsItem::keyPressEvent
|
|
*/
|
|
void ElementPrimitiveDecorator::keyPressEvent(QKeyEvent *e) {
|
|
const qreal movement_length = 1.0;
|
|
QPointF movement;
|
|
switch(e -> key()) {
|
|
case Qt::Key_Left: movement = QPointF(-movement_length, 0.0); break;
|
|
case Qt::Key_Right: movement = QPointF(+movement_length, 0.0); break;
|
|
case Qt::Key_Up: movement = QPointF(0.0, -movement_length); break;
|
|
case Qt::Key_Down: movement = QPointF(0.0, +movement_length); break;
|
|
}
|
|
if (!movement.isNull() && focusItem() == this) {
|
|
if (!moving_by_keys_) {
|
|
moving_by_keys_ = true;
|
|
keys_movement_ = movement;
|
|
} else {
|
|
keys_movement_ += movement;
|
|
}
|
|
for (QGraphicsItem *qgi: graphicsItems()) {
|
|
qgi -> setPos(qgi -> pos() + movement);
|
|
adjust();
|
|
}
|
|
}
|
|
|
|
QGraphicsObject::keyPressEvent(e);
|
|
}
|
|
|
|
/**
|
|
@reimp QGraphicsItem::keyReleaseEvent
|
|
*/
|
|
void ElementPrimitiveDecorator::keyReleaseEvent(QKeyEvent *e) {
|
|
// detecte le relachement d'une touche de direction ( = deplacement de parties)
|
|
if (
|
|
(e -> key() == Qt::Key_Left || e -> key() == Qt::Key_Right ||\
|
|
e -> key() == Qt::Key_Up || e -> key() == Qt::Key_Down) &&\
|
|
moving_by_keys_ && !e -> isAutoRepeat()
|
|
) {
|
|
// cree un objet d'annulation pour le mouvement qui vient de se finir
|
|
emit(actionFinished(new MovePartsCommand(keys_movement_, 0, graphicsItems())));
|
|
keys_movement_ = QPointF();
|
|
moving_by_keys_ = false;
|
|
}
|
|
QGraphicsObject::keyPressEvent(e);
|
|
}
|
|
|
|
/**
|
|
Initialize an ElementPrimitiveDecorator
|
|
*/
|
|
void ElementPrimitiveDecorator::init() {
|
|
setFlag(QGraphicsItem::ItemIsFocusable, true);
|
|
grid_step_x_ = grid_step_y_ = 1;
|
|
setAcceptHoverEvents(true);
|
|
}
|
|
|
|
/**
|
|
Save the original bounding rectangle.
|
|
*/
|
|
void ElementPrimitiveDecorator::saveOriginalBoundingRect() {
|
|
original_bounding_rect_ = internalBoundingRect();
|
|
}
|
|
|
|
/**
|
|
Adjust the effective bounding rect. This method should be called after the
|
|
modified_bouding_rect_ attribute was modified.
|
|
*/
|
|
void ElementPrimitiveDecorator::adjustEffectiveBoundingRect() {
|
|
prepareGeometryChange();
|
|
effective_bounding_rect_ = modified_bounding_rect_ | effective_bounding_rect_;
|
|
update();
|
|
}
|
|
|
|
/**
|
|
Start a movement (i.e. either a move or scaling operation)
|
|
*/
|
|
void ElementPrimitiveDecorator::startMovement() {
|
|
adjust();
|
|
|
|
for (CustomElementPart *item: decorated_items_) {
|
|
item -> startUserTransformation(mapToScene(original_bounding_rect_).boundingRect());
|
|
}
|
|
}
|
|
|
|
/**
|
|
Apply the movement described by \a movement_type and \a movement to \a rect.
|
|
*/
|
|
void ElementPrimitiveDecorator::applyMovementToRect(int movement_type, const QPointF &movement, QRectF &rect) {
|
|
qreal new_value;
|
|
QPointF new_point;
|
|
|
|
switch (movement_type) {
|
|
case QET::MoveArea:
|
|
rect.translate(movement.x(), movement.y());
|
|
break;
|
|
case QET::ResizeFromTopLeftCorner:
|
|
new_point = rect.topLeft() + movement;
|
|
rect.setTopLeft(new_point);
|
|
break;
|
|
case QET::ResizeFromTopCenterCorner:
|
|
new_value = rect.top() + movement.y();
|
|
rect.setTop(new_value);
|
|
break;
|
|
case QET::ResizeFromTopRightCorner:
|
|
new_point = rect.topRight() + movement;
|
|
rect.setTopRight(new_point);
|
|
break;
|
|
case QET::ResizeFromMiddleLeftCorner:
|
|
new_value = rect.left() + movement.x();
|
|
rect.setLeft(new_value);
|
|
break;
|
|
case QET::ResizeFromMiddleRightCorner:
|
|
new_value = rect.right() + movement.x();
|
|
rect.setRight(new_value);
|
|
break;
|
|
case QET::ResizeFromBottomLeftCorner:
|
|
new_point = rect.bottomLeft() + movement;
|
|
rect.setBottomLeft(new_point);
|
|
break;
|
|
case QET::ResizeFromBottomCenterCorner:
|
|
new_value = rect.bottom() + movement.y();
|
|
rect.setBottom(new_value);
|
|
break;
|
|
case QET::ResizeFromBottomRightCorner:
|
|
new_point = rect.bottomRight() + movement;
|
|
rect.setBottomRight(new_point);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CustomElementPart *ElementPrimitiveDecorator::singleItem() const {
|
|
if (decorated_items_.count() == 1) {
|
|
return(decorated_items_.first());
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
Translated the managed items by the \a movement
|
|
*/
|
|
void ElementPrimitiveDecorator::translateItems(const QPointF &movement) {
|
|
if (!decorated_items_.count()) return;
|
|
|
|
for (QGraphicsItem *qgi: graphicsItems()) {
|
|
// this is a naive, proof-of-concept implementation; we actually need to take
|
|
// the grid into account and create a command object in mouseReleaseEvent()
|
|
qgi -> moveBy(movement.x(), movement.y());
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Scale the managed items, provided they originally fit within \a
|
|
original_rect and they should now fit \a new_rect
|
|
*/
|
|
void ElementPrimitiveDecorator::scaleItems(const QRectF &original_rect, const QRectF &new_rect) {
|
|
if (!decorated_items_.count()) return;
|
|
if (original_rect == new_rect) return;
|
|
if (!original_rect.width() || !original_rect.height()) return; // cowardly flee division by zero FIXME?
|
|
|
|
QRectF scene_original_rect = mapToScene(original_rect).boundingRect();
|
|
QRectF scene_new_rect = mapToScene(new_rect).boundingRect();
|
|
|
|
for (CustomElementPart *item: decorated_items_) {
|
|
item -> handleUserTransformation(scene_original_rect, scene_new_rect);
|
|
}
|
|
}
|
|
|
|
/**
|
|
@return the bounding rectangle of \a item, in scene coordinates
|
|
*/
|
|
QRectF ElementPrimitiveDecorator::getSceneBoundingRect(QGraphicsItem *item) const {
|
|
if (!item) return(QRectF());
|
|
return(item -> mapRectToScene(item -> boundingRect()));
|
|
}
|
|
|
|
QVector<QPointF> ElementPrimitiveDecorator::getResizingsPoints() const
|
|
{
|
|
QRectF primitive_rect = modified_bounding_rect_;
|
|
QVector <QPointF> vector;
|
|
QPointF half;
|
|
|
|
vector << primitive_rect.topLeft(); //top left
|
|
half = primitive_rect.center();
|
|
half.setY(primitive_rect.top());
|
|
vector << half; //middle top
|
|
vector << primitive_rect.topRight(); //top right
|
|
half = primitive_rect.center();
|
|
half.setX(primitive_rect.left());
|
|
vector << half; //middle left
|
|
half = primitive_rect.center();
|
|
half.setX(primitive_rect.right());
|
|
vector << half; //middle right
|
|
vector << primitive_rect.bottomLeft(); //bottom left
|
|
half = primitive_rect.center();
|
|
half.setY(primitive_rect.bottom());
|
|
vector << half; //middle bottom
|
|
vector << primitive_rect.bottomRight(); //bottom right
|
|
|
|
return vector;
|
|
}
|
|
|
|
/**
|
|
Receive two rects, assuming they share a common corner and current is a \a
|
|
scaled version of \a original.
|
|
Calculate the scale ratios implied by this assumption, round them to the
|
|
nearest multiple of \a epsilon, then return the horizontal and vertical
|
|
offsets to be applied in order to pass from \a current to \a original scaled
|
|
by the rounded factors.
|
|
This method can be used to adjust a mouse movement so that it inputs a
|
|
round scaling operation.
|
|
*/
|
|
QPointF ElementPrimitiveDecorator::deltaForRoundScaling(const QRectF &original, const QRectF ¤t, qreal epsilon) {
|
|
qreal sx = current.width() / original.width();
|
|
qreal sy = current.height() / original.height();
|
|
|
|
qreal sx_rounded = QET::round(sx, epsilon);
|
|
qreal sy_rounded = QET::round(sy, epsilon);
|
|
|
|
QPointF delta(
|
|
original.width() * (sx_rounded - sx),
|
|
original.height() * (sy_rounded - sy)
|
|
);
|
|
return(delta);
|
|
}
|
|
|
|
/**
|
|
Round the coordinates of \a point so it is snapped to the grid defined by the
|
|
grid_step_x_ and grid_step_y_ attributes.
|
|
*/
|
|
QPointF ElementPrimitiveDecorator::snapConstPointToGrid(const QPointF &point) const {
|
|
return(
|
|
QPointF(
|
|
qRound(point.x() / grid_step_x_) * grid_step_x_,
|
|
qRound(point.y() / grid_step_y_) * grid_step_y_
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
Round the coordinates of \a point so it is snapped to the grid defined by the
|
|
grid_step_x_ and grid_step_y_ attributes.
|
|
*/
|
|
void ElementPrimitiveDecorator::snapPointToGrid(QPointF &point) const {
|
|
point.rx() = qRound(point.x() / grid_step_x_) * grid_step_x_;
|
|
point.ry() = qRound(point.y() / grid_step_y_) * grid_step_y_;
|
|
}
|
|
|
|
/**
|
|
@return whether the current operation should take the grid into account
|
|
according to the state of the provided \a event
|
|
*/
|
|
bool ElementPrimitiveDecorator::mustSnapToGrid(QGraphicsSceneMouseEvent *event) {
|
|
return(!(event -> modifiers() & Qt::ControlModifier));
|
|
}
|
|
|
|
/**
|
|
@param event Mouse event during the scale operations -- simply passed to mustSnapToGrid()
|
|
@return the scaling method to be used for the currently decorated items.
|
|
@see QET::ScalingMethod
|
|
@see mustSnapToGrid()
|
|
*/
|
|
QET::ScalingMethod ElementPrimitiveDecorator::scalingMethod(QGraphicsSceneMouseEvent *event) {
|
|
if (event && !mustSnapToGrid(event)) {
|
|
return(QET::FreeScaling);
|
|
}
|
|
if (CustomElementPart *single_item = singleItem()) {
|
|
return single_item -> preferredScalingMethod();
|
|
}
|
|
return QET::RoundScaleRatios;
|
|
}
|