/* * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ #include "UBGraphicsDelegateFrame.h" #include <QtGui> #include <QtSvg> #include "core/UBApplication.h" #include "core/UBSettings.h" #include "domain/UBGraphicsItemDelegate.h" #include "domain/UBGraphicsScene.h" #include "domain/UBGraphicsProxyWidget.h" #include "gui/UBResources.h" #include "core/memcheck.h" qreal const UBGraphicsDelegateFrame::mAngleTolerance = 6; UBGraphicsDelegateFrame::UBGraphicsDelegateFrame(UBGraphicsItemDelegate* pDelegate, QRectF pRect, qreal pFrameWidth, bool respectRatio) : QGraphicsRectItem(), QObject(pDelegate) , mCurrentTool(None) , mDelegate(pDelegate) , mVisible(true) , mFrameWidth(pFrameWidth) , mNominalFrameWidth(pFrameWidth) , mRespectRatio(respectRatio) , mAngle(0) , mAngleOffset(0) , mTotalScaleX(-1) , mTotalScaleY(-1) , mTranslateX(0) , mTranslateY(0) , mTotalTranslateX(0) , mTotalTranslateY(0) , mOperationMode(Scaling) { setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); setAcceptedMouseButtons(Qt::LeftButton); setRect(pRect.adjusted(mFrameWidth, mFrameWidth, mFrameWidth * -1, mFrameWidth * -1)); setBrush(QBrush(UBSettings::paletteColor)); setPen(Qt::NoPen); setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control)); mBottomRightResizeGripSvgItem = new QGraphicsSvgItem(":/images/resize.svg", this); mBottomResizeGripSvgItem = new QGraphicsSvgItem(":/images/resizeBottom.svg", this); mLeftResizeGripSvgItem = new QGraphicsSvgItem(":/images/resizeLeft.svg", this); mRightResizeGripSvgItem = new QGraphicsSvgItem(":/images/resizeRight.svg", this); mTopResizeGripSvgItem = new QGraphicsSvgItem(":/images/resizeTop.svg", this); mBottomRightResizeGrip = new QGraphicsRectItem(this); mBottomRightResizeGrip->setPen(Qt::NoPen); mBottomResizeGrip = new QGraphicsRectItem(this); mBottomResizeGrip->setPen(Qt::NoPen); mLeftResizeGrip = new QGraphicsRectItem(this); mLeftResizeGrip->setPen(Qt::NoPen); mRightResizeGrip = new QGraphicsRectItem(this); mRightResizeGrip->setPen(Qt::NoPen); mTopResizeGrip = new QGraphicsRectItem(this); mTopResizeGrip->setPen(Qt::NoPen); mRotateButton = new QGraphicsSvgItem(":/images/rotate.svg", this); mRotateButton->setCursor(UBResources::resources()->rotateCursor); mRotateButton->setVisible(mDelegate->canRotate()); updateResizeCursors(); setAntiScale(1.0); positionHandles(); } UBGraphicsDelegateFrame::~UBGraphicsDelegateFrame() { // NOOP } void UBGraphicsDelegateFrame::setAntiScale(qreal pAntiScale) { mFrameWidth = mNominalFrameWidth * pAntiScale; QTransform tr; tr.scale(pAntiScale, pAntiScale); mBottomRightResizeGripSvgItem->setTransform(tr); mBottomResizeGripSvgItem->setTransform(tr); mLeftResizeGripSvgItem->setTransform(tr); mRightResizeGripSvgItem->setTransform(tr); mTopResizeGripSvgItem->setTransform(tr); mRotateButton->setTransform(tr); } void UBGraphicsDelegateFrame::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); QPainterPath path; path.addRoundedRect(rect(), mFrameWidth / 2, mFrameWidth / 2); if (rect().width() > 1 && rect().height() > 1) { QPainterPath extruded; extruded.addRect(rect().adjusted(mFrameWidth, mFrameWidth, (mFrameWidth * -1), (mFrameWidth * -1))); path = path.subtracted(extruded); } painter->fillPath(path, brush()); } QPainterPath UBGraphicsDelegateFrame::shape() const { QPainterPath path; //We do not use the rounded rect here because we want the bottom right corner //to be included in the frame (for resize grip handling : #702) path.addRect(rect()); if (rect().width() > 0 && rect().height() > 0) { QPainterPath extruded; extruded.addRect(rect().adjusted(mFrameWidth, mFrameWidth, mFrameWidth * -1, mFrameWidth * -1)); path = path.subtracted(extruded); } return path; } void UBGraphicsDelegateFrame::initializeTransform() { QTransform itemTransform = delegated()->sceneTransform(); QRectF itemRect = delegated()->boundingRect(); QPointF topLeft = itemTransform.map(itemRect.topLeft()); QPointF topRight = itemTransform.map(itemRect.topRight()); QPointF bottomLeft = itemTransform.map(itemRect.bottomLeft()); QLineF topLine(topLeft, topRight); QLineF leftLine(topLeft, bottomLeft); qreal width = topLine.length(); qreal height = leftLine.length(); mAngle = topLine.angle(); mTotalScaleX = width / itemRect.width(); mTotalScaleY = height / itemRect.height(); QTransform tr; QPointF center = delegated()->boundingRect().center(); tr.translate(center.x() * mTotalScaleX, center.y() * mTotalScaleY); tr.rotate(-mAngle); tr.translate(-center.x() * mTotalScaleX, -center.y() * mTotalScaleY); tr.scale(mTotalScaleX, mTotalScaleY); mTotalTranslateX = delegated()->transform().dx() - tr.dx(); mTotalTranslateY = delegated()->transform().dy() - tr.dy(); } void UBGraphicsDelegateFrame::mousePressEvent(QGraphicsSceneMouseEvent *event) { mDelegate->startUndoStep(); mStartingPoint = event->scenePos(); initializeTransform(); mScaleX = 1; mScaleY = 1; mTranslateX = 0; mTranslateY = 0; mAngleOffset = 0; mInitialTransform = buildTransform(); mCurrentTool = toolFromPos(event->pos()); event->accept(); } void UBGraphicsDelegateFrame::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { QLineF move(mStartingPoint, event->scenePos()); qreal moveX = move.length() * cos((move.angle() - mAngle) * PI / 180); qreal moveY = -move.length() * sin((move.angle() - mAngle) * PI / 180); qreal width = delegated()->boundingRect().width() * mTotalScaleX; qreal height = delegated()->boundingRect().height() * mTotalScaleY; if(mOperationMode == Scaling) { if (resizingBottomRight()) { qreal scaleX = (width + moveX) / width; qreal scaleY = (height + moveY) / height; qreal scaleFactor = (scaleX + scaleY) / 2; // Do not allow resizing of image size under frame size if (scaleFactor > 1 || ((width * scaleFactor) > 2 * mFrameWidth && (height * scaleFactor) > 2 * mFrameWidth)) { if (mRespectRatio) { mScaleX = scaleFactor; mScaleY = scaleFactor; } else { mScaleX = scaleX; mScaleY = scaleY; } } } else if (resizingLeft()) { qreal scaleX = (width - moveX) / width; if (scaleX > 1 || (width * scaleX) > 2 * mFrameWidth) { mScaleX = scaleX; mTranslateX = moveX; } } else if (resizingRight()) { qreal scaleX = (width + moveX) / width; if (scaleX > 1 || (width * scaleX) > 2 * mFrameWidth) { mScaleX = scaleX; } } else if (resizingTop()) { qreal scaleY = (height - moveY) / height; if (scaleY > 1 || (height * scaleY) > 2 * mFrameWidth) { mScaleY = scaleY; mTranslateY = moveY; } } else if (resizingBottom()) { qreal scaleY = (height + moveY) / height; if (scaleY > 1 || (height * scaleY) > 2 * mFrameWidth) { mScaleY = scaleY; } } } else if (mOperationMode == Resizing) { UBResizableGraphicsItem* resizableItem = dynamic_cast<UBResizableGraphicsItem*>(delegated()); if (resizableItem) { QLineF mousePosDelta(delegated()->mapFromScene(event->lastScenePos()) , delegated()->mapFromScene(event->scenePos())); QSizeF incVector(0, 0); if (resizingBottomRight()) { incVector = QSizeF(mousePosDelta.dx(), mousePosDelta.dy()); } else if (resizingRight()) { incVector = QSizeF(mousePosDelta.dx(), 0); } else if (resizingBottom()) { incVector = QSizeF(0, mousePosDelta.dy()); } else if (resizingLeft()) { incVector = QSizeF(- mousePosDelta.dx(), 0); } else if (resizingTop()) { incVector = QSizeF(0, - mousePosDelta.dy()); } QSizeF newSize = resizableItem->size() + incVector; if (newSize.width() < 0 || newSize.height() < 0) return; resizableItem->resize(newSize); } } if (rotating()) { QLineF startLine(sceneBoundingRect().center(), event->lastScenePos()); QLineF currentLine(sceneBoundingRect().center(), event->scenePos()); mAngle += startLine.angleTo(currentLine); if ((int)mAngle % 45 >= 45 - mAngleTolerance || (int)mAngle % 45 <= mAngleTolerance) { mAngle = qRound(mAngle / 45) * 45; mAngleOffset += startLine.angleTo(currentLine); if ((int)mAngleOffset % 360 > mAngleTolerance && (int)mAngleOffset % 360 < 360 - mAngleTolerance) { mAngle += mAngleOffset; mAngleOffset = 0; } } } else if (moving()) { mTranslateX = move.dx(); mTranslateY = move.dy(); } QTransform tr = buildTransform(); //TODO UB 4.x: Could find a better solution ? if (resizingRight() || resizingBottom() || resizingBottomRight()) { QPointF topLeft = tr.map(delegated()->boundingRect().topLeft()); QPointF fixedPoint = mInitialTransform.map(delegated()->boundingRect().topLeft()); mTranslateX += fixedPoint.x() - topLeft.x(); mTranslateY += fixedPoint.y() - topLeft.y(); tr = buildTransform(); } else if (resizingTop() || resizingLeft()) { if (mOperationMode == Scaling) { QPointF bottomRight = tr.map(delegated()->boundingRect().bottomRight()); QPointF fixedPoint = mInitialTransform.map(delegated()->boundingRect().bottomRight()); mTranslateX += fixedPoint.x() - bottomRight.x(); mTranslateY += fixedPoint.y() - bottomRight.y(); } else { QLineF vector; if (resizingLeft()) { QPointF topRight1 = mInitialTransform.map(QPointF(delegated()->boundingRect().width() - moveX, 0)); QPointF topRight2 = mInitialTransform.map(QPointF(delegated()->boundingRect().width(), 0)); vector.setPoints(topRight1, topRight2); } else { QPointF bottomLeft1 = mInitialTransform.map(QPointF(0, delegated()->boundingRect().height() - moveY)); QPointF bottomLeft2 = mInitialTransform.map(QPointF(0, delegated()->boundingRect().height())); vector.setPoints(bottomLeft1, bottomLeft2); } mTranslateX = vector.dx(); mTranslateY = vector.dy(); } tr = buildTransform(); } delegated()->setTransform(tr); event->accept(); } QTransform UBGraphicsDelegateFrame::buildTransform() { QTransform tr; QPointF center = delegated()->boundingRect().center(); tr.translate(mTotalTranslateX + mTranslateX, mTotalTranslateY + mTranslateY); tr.translate(center.x() * mTotalScaleX * mScaleX, center.y() * mTotalScaleY * mScaleY); tr.rotate(-mAngle); tr.translate(-center.x() * mTotalScaleX * mScaleX, -center.y() * mTotalScaleY * mScaleY); tr.scale(mTotalScaleX * mScaleX, mTotalScaleY * mScaleY); return tr; } void UBGraphicsDelegateFrame::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { updateResizeCursors(); mDelegate->commitUndoStep(); mTotalScaleX *= mScaleX; mTotalScaleY *= mScaleY; mTotalTranslateX += mTranslateX; mTotalTranslateY += mTranslateY; event->accept(); mCurrentTool = None; QGraphicsRectItem::mouseReleaseEvent(event); } void UBGraphicsDelegateFrame::updateResizeCursors() { QPixmap pix(":/images/cursors/resize.png"); QTransform tr; tr.rotate(-mAngle); QCursor resizeCursor = QCursor(pix.transformed(tr, Qt::SmoothTransformation), pix.width() / 2, pix.height() / 2); mLeftResizeGrip->setCursor(resizeCursor); mRightResizeGrip->setCursor(resizeCursor); tr.rotate(-90); resizeCursor = QCursor(pix.transformed(tr, Qt::SmoothTransformation), pix.width() / 2, pix.height() / 2); mBottomResizeGrip->setCursor(resizeCursor); mTopResizeGrip->setCursor(resizeCursor); tr.rotate(-45); resizeCursor = QCursor(pix.transformed(tr, Qt::SmoothTransformation), pix.width() / 2, pix.height() / 2); mBottomRightResizeGrip->setCursor(resizeCursor); } void UBGraphicsDelegateFrame::setVisible(bool visible) { mVisible = visible; if (mVisible) setBrush(QBrush(UBSettings::paletteColor)); else setBrush(Qt::NoBrush); } void UBGraphicsDelegateFrame::positionHandles() { QRectF itemRect = delegated()->boundingRect(); QTransform itemTransform = delegated()->sceneTransform(); QPointF topLeft = itemTransform.map(itemRect.topLeft()); QPointF topRight = itemTransform.map(itemRect.topRight()); QPointF bottomLeft = itemTransform.map(itemRect.bottomLeft()); QPointF center = itemTransform.map(itemRect.center()); QLineF topLine(topLeft, topRight); qreal angle = topLine.angle(); qreal width = topLine.length(); QLineF leftLine(topLeft, bottomLeft); qreal height = leftLine.length(); if (mVisible) { setRect(center.x() - mFrameWidth - width / 2, center.y() - mFrameWidth - height / 2, width + 2 * mFrameWidth, height + 2 * mFrameWidth); } else { setRect(center.x() - width / 2, center.y() - height / 2, width, height); } resetTransform(); translate(center.x(), center.y()); rotate(-angle); translate(-center.x(), -center.y()); mBottomRightResizeGripSvgItem->setParentItem(this); mBottomResizeGripSvgItem->setParentItem(this); mLeftResizeGripSvgItem->setParentItem(this); mRightResizeGripSvgItem->setParentItem(this); mTopResizeGripSvgItem->setParentItem(this); mRotateButton->setParentItem(this); mBottomRightResizeGrip->setParentItem(this); mBottomResizeGrip->setParentItem(this); mLeftResizeGrip->setParentItem(this); mRightResizeGrip->setParentItem(this); mTopResizeGrip->setParentItem(this); QRectF brRect = mBottomRightResizeGripSvgItem->mapRectToParent(mBottomRightResizeGripSvgItem->boundingRect()); mBottomRightResizeGripSvgItem->setPos(rect().right() - brRect.width() , rect().bottom() - brRect.height()); QRectF bRect = mBottomResizeGripSvgItem->mapRectToParent(mBottomResizeGripSvgItem->boundingRect()); mBottomResizeGripSvgItem->setPos(rect().center().x() - bRect.width() / 2 , rect().bottom() - bRect.height()); QRectF lRect = mLeftResizeGripSvgItem->mapRectToParent(mLeftResizeGripSvgItem->boundingRect()); mLeftResizeGripSvgItem->setPos(rect().left() , rect().center().y() - lRect.height() / 2); QRectF rRect = mRightResizeGripSvgItem->mapRectToParent(mRightResizeGripSvgItem->boundingRect()); mRightResizeGripSvgItem->setPos(rect().right() - rRect.width() , rect().center().y() - rRect.height() / 2); QRectF trRect = mTopResizeGripSvgItem->mapRectToParent(mTopResizeGripSvgItem->boundingRect()); mTopResizeGripSvgItem->setPos(rect().center().x() - trRect.width() / 2 , rect().y()); mRotateButton->setPos(rect().right() - mFrameWidth - 5, rect().top() + 5); mBottomRightResizeGrip->setRect(bottomRightResizeGripRect()); mBottomResizeGrip->setRect(bottomResizeGripRect()); mLeftResizeGrip->setRect(leftResizeGripRect()); mRightResizeGrip->setRect(rightResizeGripRect()); mTopResizeGrip->setRect(topResizeGripRect()); QVariant vLocked = delegated()->data(UBGraphicsItemData::ItemLocked); bool isLocked = (vLocked.isValid() && vLocked.toBool()); mBottomRightResizeGripSvgItem->setVisible(!isLocked); mBottomResizeGripSvgItem->setVisible(!isLocked); mLeftResizeGripSvgItem->setVisible(!isLocked); mRightResizeGripSvgItem->setVisible(!isLocked); mTopResizeGripSvgItem->setVisible(!isLocked); mRotateButton->setVisible(mDelegate->canRotate() && !isLocked); mBottomRightResizeGrip->setVisible(!isLocked); mBottomResizeGrip->setVisible(!isLocked); mLeftResizeGrip->setVisible(!isLocked); mRightResizeGrip->setVisible(!isLocked); mTopResizeGrip->setVisible(!isLocked); if (isLocked) { QColor baseColor = UBSettings::paletteColor; baseColor.setAlphaF(baseColor.alphaF() / 3); setBrush(QBrush(baseColor)); } else { setBrush(QBrush(UBSettings::paletteColor)); } } QGraphicsItem* UBGraphicsDelegateFrame::delegated() { return mDelegate->delegated(); } UBGraphicsDelegateFrame::FrameTool UBGraphicsDelegateFrame::toolFromPos(QPointF pos) { if(mDelegate->isLocked()) return None; else if (bottomRightResizeGripRect().contains(pos)) return ResizeBottomRight; else if (bottomResizeGripRect().contains(pos)) return ResizeBottom; else if (leftResizeGripRect().contains(pos)) return ResizeLeft; else if (rightResizeGripRect().contains(pos)) return ResizeRight; else if (topResizeGripRect().contains(pos)) return ResizeTop; else if (rotateButtonBounds().contains(pos) && mDelegate && mDelegate->canRotate()) return Rotate; else return Move; } QRectF UBGraphicsDelegateFrame::bottomRightResizeGripRect() const { return QRectF(rect().right() - mFrameWidth, rect().bottom() - mFrameWidth, mFrameWidth, mFrameWidth); } QRectF UBGraphicsDelegateFrame::bottomResizeGripRect() const { return QRectF(rect().center().x() - mFrameWidth / 2, rect().bottom() - mFrameWidth, mFrameWidth, mFrameWidth); } QRectF UBGraphicsDelegateFrame::leftResizeGripRect() const { return QRectF(rect().left(), rect().center().y() - mFrameWidth / 2, mFrameWidth, mFrameWidth); } QRectF UBGraphicsDelegateFrame::rightResizeGripRect() const { return QRectF(rect().right() - mFrameWidth, rect().center().y() - mFrameWidth / 2, mFrameWidth, mFrameWidth); } QRectF UBGraphicsDelegateFrame::topResizeGripRect() const { return QRectF(rect().center().x() - mFrameWidth / 2, rect().top(), mFrameWidth, mFrameWidth); } QRectF UBGraphicsDelegateFrame::rotateButtonBounds() const { return QRectF(rect().right()- mFrameWidth, rect().top(), mFrameWidth, mFrameWidth); }