File indexing completed on 2024-06-16 04:17:54

0001 /*
0002  *  kis_tool_crop.cc -- part of Krita
0003  *
0004  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
0005  *  SPDX-FileCopyrightText: 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
0006  *  SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
0007  *  SPDX-FileCopyrightText: 2007 Adrian Page <adrian@pagenet.plus.com>
0008  *
0009  *  SPDX-License-Identifier: GPL-2.0-or-later
0010  */
0011 
0012 #include "kis_tool_crop.h"
0013 
0014 
0015 #include <QCheckBox>
0016 #include <QComboBox>
0017 #include <QObject>
0018 #include <QPainter>
0019 #include <QPen>
0020 #include <QPushButton>
0021 #include <QRect>
0022 #include <QVector>
0023 #include <QMenu>
0024 
0025 #include <kis_debug.h>
0026 #include <klocalizedstring.h>
0027 #include <ksharedconfig.h>
0028 
0029 #include <KoCanvasBase.h>
0030 #include <kis_global.h>
0031 #include <kis_painter.h>
0032 #include <kis_cursor.h>
0033 #include <kis_image.h>
0034 #include <kis_undo_adapter.h>
0035 #include <KoPointerEvent.h>
0036 #include <kis_selection.h>
0037 #include <kis_layer.h>
0038 #include <kis_canvas2.h>
0039 #include <KisViewManager.h>
0040 #include <kis_floating_message.h>
0041 #include <kis_group_layer.h>
0042 #include <kis_resources_snapshot.h>
0043 
0044 #include <kundo2command.h>
0045 #include <kis_crop_saved_extra_data.h>
0046 
0047 
0048 struct DecorationLine
0049 {
0050     QPointF start;
0051     QPointF end;
0052     enum Relation
0053     {
0054         Width,
0055         Height,
0056         Smallest,
0057         Largest
0058     };
0059     Relation startXRelation;
0060     Relation startYRelation;
0061     Relation endXRelation;
0062     Relation endYRelation;
0063 };
0064 
0065 DecorationLine decors[20] =
0066 {
0067     //thirds
0068     {QPointF(0.0, 0.3333),QPointF(1.0, 0.3333), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0069     {QPointF(0.0, 0.6666),QPointF(1.0, 0.6666), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0070     {QPointF(0.3333, 0.0),QPointF(0.3333, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0071     {QPointF(0.6666, 0.0),QPointF(0.6666, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0072 
0073     //fifths
0074     {QPointF(0.0, 0.2),QPointF(1.0, 0.2), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0075     {QPointF(0.0, 0.4),QPointF(1.0, 0.4), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0076     {QPointF(0.0, 0.6),QPointF(1.0, 0.6), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0077     {QPointF(0.0, 0.8),QPointF(1.0, 0.8), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0078     {QPointF(0.2, 0.0),QPointF(0.2, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0079     {QPointF(0.4, 0.0),QPointF(0.4, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0080     {QPointF(0.6, 0.0),QPointF(0.6, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0081     {QPointF(0.8, 0.0),QPointF(0.8, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0082 
0083     // Passport photo
0084     {QPointF(0.0, 0.45/0.35),QPointF(1.0, 0.45/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
0085     {QPointF(0.2, 0.05/0.35),QPointF(0.8, 0.05/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
0086     {QPointF(0.2, 0.40/0.35),QPointF(0.8, 0.40/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
0087     {QPointF(0.25, 0.07/0.35),QPointF(0.75, 0.07/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
0088     {QPointF(0.25, 0.38/0.35),QPointF(0.75, 0.38/0.35), DecorationLine::Width, DecorationLine::Width, DecorationLine::Width, DecorationLine::Width},
0089     {QPointF(0.35/0.45, 0.0),QPointF(0.35/0.45, 1.0), DecorationLine::Height, DecorationLine::Height, DecorationLine::Height, DecorationLine::Height},
0090 
0091     //Crosshair
0092     {QPointF(0.0, 0.5),QPointF(1.0, 0.5), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height},
0093     {QPointF(0.5, 0.0),QPointF(0.5, 1.0), DecorationLine::Width, DecorationLine::Height, DecorationLine::Width, DecorationLine::Height}
0094 };
0095 
0096 #define DECORATION_COUNT 5
0097 const int decorsIndex[DECORATION_COUNT] = {0,4,12,18,20};
0098 
0099 KisToolCrop::KisToolCrop(KoCanvasBase * canvas)
0100         : KisTool(canvas, KisCursor::load("tool_crop_cursor.png", 6, 6))
0101 {
0102     setObjectName("tool_crop");
0103     m_handleSize = 13;
0104     m_haveCropSelection = false;
0105     m_cropTypeSelectable = false;
0106     m_cropType = ImageCropType;
0107     m_decoration = 1;
0108 
0109     connect(&m_finalRect, SIGNAL(sigValuesChanged()), SLOT(slotRectChanged()));
0110     connect(&m_finalRect, SIGNAL(sigLockValuesChanged()), SLOT(slotRectChanged()));
0111 
0112     // context menu options (mirrors tool options)
0113     m_contextMenu.reset(new QMenu());
0114     applyCrop = new KisAction(i18n("Crop"));
0115 
0116     centerToggleOption = new KisAction(i18n("Center"));
0117     centerToggleOption->setCheckable(true);
0118     
0119     growToggleOption = new KisAction(i18nc("Grow as in crop tool", "Grow"));
0120     growToggleOption->setCheckable(true);
0121     
0122     lockWidthToggleOption = new KisAction(i18n("Lock Width"));
0123     lockWidthToggleOption->setCheckable(true);
0124     
0125     lockHeightToggleOption = new KisAction(i18n("Lock Height"));
0126     lockHeightToggleOption->setCheckable(true);
0127     
0128     lockRatioToggleOption = new KisAction(i18n("Lock Ratio"));
0129     lockRatioToggleOption->setCheckable(true);
0130 }
0131 
0132 KisToolCrop::~KisToolCrop()
0133 {
0134     delete applyCrop;
0135     delete centerToggleOption;
0136     delete growToggleOption;
0137     delete lockWidthToggleOption;
0138     delete lockHeightToggleOption;
0139     delete lockRatioToggleOption;
0140 }
0141 
0142 void KisToolCrop::activate(const QSet<KoShape*> &shapes)
0143 {
0144 
0145     KisTool::activate(shapes);
0146     configGroup =  KSharedConfig::openConfig()->group(toolId()); // save settings to kritarc
0147 
0148     KisResourcesSnapshotSP resources =
0149         new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
0150 
0151 
0152     // load settings from configuration
0153     setGrowCenter(configGroup.readEntry("growCenter", false));
0154     setAllowGrow(configGroup.readEntry("allowGrow", true));
0155 
0156     // Default: thirds decoration
0157     setDecoration(configGroup.readEntry("decoration", 1));
0158 
0159     // Default: crop the entire image
0160     setCropType(CropToolType(configGroup.readEntry("cropType", 0)));
0161 
0162     m_finalRect.setCropRect(image()->bounds());
0163 
0164     KisSelectionSP sel = resources->activeSelection();
0165     if (sel) {
0166         m_haveCropSelection = true;
0167         m_finalRect.setRectInitial(sel->selectedExactRect());
0168     }
0169     useCursor(cursor());
0170 
0171     //pixel layer
0172     if(resources->currentNode() && resources->currentNode()->paintDevice()) {
0173         setCropTypeSelectable(true);
0174     }
0175     //vector layer
0176     else {
0177         if (m_cropType != ImageCropType && m_cropType != CanvasCropType) {
0178             setCropType(ImageCropType);
0179         }
0180         setCropTypeSelectable(false);
0181     }
0182     connect(&m_finalRect, SIGNAL(sigValuesChanged()), SLOT(showSizeOnCanvas()));
0183 }
0184 
0185 void KisToolCrop::cancelStroke()
0186 {
0187     m_haveCropSelection = false;
0188     useCursor(cursor());
0189     doCanvasUpdate(image()->bounds());
0190 }
0191 
0192 void KisToolCrop::deactivate()
0193 {
0194     cancelStroke();
0195     KisTool::deactivate();
0196 }
0197 
0198 void KisToolCrop::requestStrokeEnd()
0199 {
0200     if (m_haveCropSelection) crop();
0201 }
0202 
0203 void KisToolCrop::requestStrokeCancellation()
0204 {
0205     cancelStroke();
0206 }
0207 
0208 void KisToolCrop::requestUndoDuringStroke()
0209 {
0210     cancelStroke();
0211 }
0212 
0213 void KisToolCrop::requestRedoDuringStroke()
0214 {
0215     cancelStroke();
0216 }
0217 
0218 void KisToolCrop::canvasResourceChanged(int key, const QVariant &res)
0219 {
0220     KisTool::canvasResourceChanged(key, res);
0221 
0222     //pixel layer
0223     if(currentNode() && currentNode()->paintDevice()) {
0224         setCropTypeSelectable(true);
0225     }
0226     //vector layer
0227     else {
0228         if (m_cropType != ImageCropType && m_cropType != CanvasCropType) {
0229             setCropType(ImageCropType);
0230         }
0231         setCropTypeSelectable(false);
0232     }
0233 }
0234 
0235 void KisToolCrop::paint(QPainter &painter, const KoViewConverter &converter)
0236 {
0237     Q_UNUSED(converter);
0238     paintOutlineWithHandles(painter);
0239 }
0240 
0241 QMenu *KisToolCrop::popupActionsMenu()
0242 {
0243     if (m_contextMenu) {
0244         // Sync state of context menu toggles with state of Tool Options toggles
0245         centerToggleOption->setChecked(growCenter());
0246         growToggleOption->setChecked(allowGrow());
0247         lockWidthToggleOption->setChecked(lockWidth());
0248         lockHeightToggleOption->setChecked(lockHeight());
0249         lockRatioToggleOption->setChecked(lockRatio());
0250       
0251         m_contextMenu->clear();
0252 
0253         m_contextMenu->addSection(i18n("Crop Tool Actions"));
0254         m_contextMenu->addSeparator();
0255 
0256         if (m_haveCropSelection) {         // can't crop if there is no selection
0257             m_contextMenu->addAction(applyCrop);
0258             m_contextMenu->addSeparator();
0259         }
0260 
0261         m_contextMenu->addAction(centerToggleOption);
0262         m_contextMenu->addAction(growToggleOption);
0263         
0264         m_contextMenu->addSeparator();
0265         
0266         m_contextMenu->addAction(lockWidthToggleOption);
0267         m_contextMenu->addAction(lockHeightToggleOption);
0268         m_contextMenu->addAction(lockRatioToggleOption);
0269     }
0270 
0271     return m_contextMenu.data();
0272 }
0273 
0274 void KisToolCrop::beginPrimaryAction(KoPointerEvent *event)
0275 {
0276     m_finalRect.setCropRect(image()->bounds());
0277     setMode(KisTool::PAINT_MODE);
0278 
0279     const QPointF imagePoint = convertToPixelCoord(event);
0280     m_mouseOnHandleType = mouseOnHandle(pixelToView(imagePoint));
0281 
0282     if (m_mouseOnHandleType != KisConstrainedRect::None) {
0283         QPointF snapPoint = m_finalRect.handleSnapPoint(KisConstrainedRect::HandleType(m_mouseOnHandleType), imagePoint);
0284         QPointF snapDocPoint = image()->pixelToDocument(snapPoint);
0285         m_dragOffsetDoc = snapDocPoint - event->point;
0286     } else {
0287         m_dragOffsetDoc = QPointF();
0288     }
0289 
0290     QPointF snappedPoint = convertToPixelCoordAndSnap(event, m_dragOffsetDoc);
0291 
0292     m_dragStart = snappedPoint.toPoint();
0293     m_resettingStroke = false;
0294 
0295     if (!m_haveCropSelection || m_mouseOnHandleType == None) {
0296         m_lastCanvasUpdateRect = image()->bounds();
0297         const int initialWidth = m_finalRect.widthLocked() ? m_finalRect.rect().width() : 1;
0298         const int initialHeight = m_finalRect.heightLocked() ? m_finalRect.rect().height() : 1;
0299         const QRect initialRect = QRect(m_dragStart, QSize(initialWidth, initialHeight));
0300         m_finalRect.setRectInitial(initialRect);
0301         m_initialDragRect = initialRect;
0302         m_mouseOnHandleType = KisConstrainedRect::Creation;
0303         m_resettingStroke = true;
0304     } else {
0305         m_initialDragRect = m_finalRect.rect();
0306     }
0307 }
0308 
0309 void KisToolCrop::continuePrimaryAction(KoPointerEvent *event)
0310 {
0311     CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
0312 
0313     const QPointF pos = convertToPixelCoordAndSnap(event, m_dragOffsetDoc);
0314     const QPoint drag = pos.toPoint() - m_dragStart;
0315 
0316     m_finalRect.moveHandle(KisConstrainedRect::HandleType(m_mouseOnHandleType), drag, m_initialDragRect);
0317 }
0318 
0319 bool KisToolCrop::tryContinueLastCropAction()
0320 {
0321     bool result = false;
0322 
0323     const KUndo2Command *lastCommand = image()->undoAdapter()->presentCommand();
0324     const KisCropSavedExtraData *data = 0;
0325 
0326     if ((lastCommand = image()->undoAdapter()->presentCommand()) &&
0327         (data = dynamic_cast<const KisCropSavedExtraData*>(lastCommand->extraData()))) {
0328 
0329         bool cropImageConsistent =
0330             m_cropType == ImageCropType &&
0331             (data->type() == KisCropSavedExtraData::CROP_IMAGE ||
0332              data->type() == KisCropSavedExtraData::RESIZE_IMAGE);
0333 
0334         bool cropLayerConsistent =
0335             m_cropType == LayerCropType &&
0336             data->type() == KisCropSavedExtraData::CROP_LAYER &&
0337             currentNode() == data->cropNode();
0338 
0339 
0340         if (cropImageConsistent || cropLayerConsistent) {
0341             image()->undoAdapter()->undoLastCommand();
0342             image()->waitForDone();
0343 
0344             m_finalRect.setRectInitial(data->cropRect());
0345             m_haveCropSelection = true;
0346 
0347             result = true;
0348         }
0349     }
0350 
0351     return result;
0352 }
0353 
0354 void KisToolCrop::endPrimaryAction(KoPointerEvent *event)
0355 {
0356     CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
0357     setMode(KisTool::HOVER_MODE);
0358 
0359     QRectF viewCropRect = pixelToView(m_finalRect.rect());
0360     const bool haveValidRect =
0361         viewCropRect.width() > m_handleSize &&
0362         viewCropRect.height() > m_handleSize;
0363 
0364 
0365     if (!m_haveCropSelection && !haveValidRect) {
0366         if (!tryContinueLastCropAction()) {
0367             m_finalRect.setRectInitial(image()->bounds());
0368             m_haveCropSelection = true;
0369         }
0370     } else if (m_resettingStroke && !haveValidRect) {
0371         m_lastCanvasUpdateRect = image()->bounds();
0372         m_haveCropSelection = false;
0373     } else {
0374         m_haveCropSelection = true;
0375     }
0376 
0377     m_finalRect.normalize();
0378 
0379     qint32 type = mouseOnHandle(pixelToView(convertToPixelCoordAndSnap(event, m_dragOffsetDoc)));
0380     setMoveResizeCursor(type);
0381 }
0382 
0383 void KisToolCrop::mouseMoveEvent(KoPointerEvent *event)
0384 {
0385     QPointF pos = convertToPixelCoordAndSnap(event);
0386 
0387     if (m_haveCropSelection) {  //if the crop selection is set
0388         //set resize cursor if we are on one of the handles
0389         if(mode() == KisTool::PAINT_MODE) {
0390             //keep the same cursor as the one we clicked with
0391             setMoveResizeCursor(m_mouseOnHandleType);
0392         }else{
0393             //hovering
0394             qint32 type = mouseOnHandle(pixelToView(pos));
0395             setMoveResizeCursor(type);
0396         }
0397     }
0398 }
0399 
0400 void KisToolCrop::beginPrimaryDoubleClickAction(KoPointerEvent *event)
0401 {
0402     if (m_haveCropSelection) crop();
0403 
0404     // this action will have no continuation
0405     event->ignore();
0406 }
0407 
0408 
0409 #define BORDER_LINE_WIDTH 0
0410 #define HALF_BORDER_LINE_WIDTH 0
0411 #define HANDLE_BORDER_LINE_WIDTH 1
0412 
0413 QRectF KisToolCrop::borderLineRect()
0414 {
0415     QRectF borderRect = pixelToView(m_finalRect.rect());
0416 
0417     // Draw the border line right next to the crop rectangle perimeter.
0418     borderRect.adjust(-HALF_BORDER_LINE_WIDTH, -HALF_BORDER_LINE_WIDTH, HALF_BORDER_LINE_WIDTH, HALF_BORDER_LINE_WIDTH);
0419 
0420     return borderRect;
0421 }
0422 
0423 #define OUTSIDE_CROP_ALPHA 200
0424 
0425 void KisToolCrop::paintOutlineWithHandles(QPainter& gc)
0426 {
0427     if (canvas() && (mode() == KisTool::PAINT_MODE || m_haveCropSelection)) {
0428         gc.save();
0429 
0430         QRectF wholeImageRect = pixelToView(image()->bounds());
0431         QRectF borderRect = borderLineRect();
0432 
0433         QPainterPath path;
0434 
0435         path.addRect(wholeImageRect);
0436         path.addRect(borderRect);
0437         gc.setPen(Qt::NoPen);
0438         gc.setBrush(QColor(0, 0, 0, OUTSIDE_CROP_ALPHA));
0439         gc.drawPath(path);
0440 
0441         // Handles
0442         QPen pen(Qt::SolidLine);
0443         pen.setWidth(HANDLE_BORDER_LINE_WIDTH * decorationThickness());
0444         pen.setColor(Qt::black);
0445         pen.setCosmetic(true);
0446         gc.setPen(pen);
0447         gc.setBrush(QColor(200, 200, 200, OUTSIDE_CROP_ALPHA));
0448         gc.drawPath(handlesPath());
0449 
0450         gc.setClipRect(borderRect, Qt::IntersectClip);
0451 
0452         if (m_decoration > 0) {
0453             for (int i = decorsIndex[m_decoration-1]; i<decorsIndex[m_decoration]; i++) {
0454                 drawDecorationLine(&gc, &(decors[i]), borderRect);
0455             }
0456         }
0457         gc.restore();
0458     }
0459 }
0460 
0461 void KisToolCrop::crop()
0462 {
0463     KIS_ASSERT_RECOVER_RETURN(currentImage());
0464     if (m_finalRect.rect().isEmpty()) return;
0465 
0466     const bool imageCrop = m_cropType == ImageCropType || m_cropType == CanvasCropType;
0467 
0468     if (!imageCrop) {
0469         //Cropping layer
0470         if (!nodeEditable()) {
0471             return;
0472         }
0473     }
0474 
0475     m_haveCropSelection = false;
0476     useCursor(cursor());
0477 
0478     QRect cropRect = m_finalRect.rect();
0479 
0480     // The visitor adds the undo steps to the macro
0481     if (imageCrop || !currentNode()->paintDevice()) {
0482         if (m_cropType == CanvasCropType) {
0483             currentImage()->resizeImage(cropRect);
0484         } else {
0485             currentImage()->cropImage(cropRect);
0486         }
0487     } else {
0488         currentImage()->cropNode(currentNode(), cropRect, m_cropType == FrameCropType);
0489     }
0490 }
0491 
0492 void KisToolCrop::setCropTypeLegacy(int cropType)
0493 {
0494     setCropType(static_cast<KisToolCrop::CropToolType>(cropType));
0495 }
0496 
0497 void KisToolCrop::setCropType(KisToolCrop::CropToolType cropType)
0498 {
0499     if(m_cropType == cropType)
0500         return;
0501     m_cropType = cropType;
0502 
0503     configGroup.writeEntry("cropType", static_cast<int>(cropType));
0504 
0505     emit cropTypeChanged(m_cropType);
0506 }
0507 
0508 KisToolCrop::CropToolType KisToolCrop::cropType() const
0509 {
0510     return m_cropType;
0511 }
0512 
0513 void KisToolCrop::setCropTypeSelectable(bool selectable)
0514 {
0515     if(selectable == m_cropTypeSelectable)
0516         return;
0517     m_cropTypeSelectable = selectable;
0518     emit cropTypeSelectableChanged();
0519 }
0520 
0521 bool KisToolCrop::cropTypeSelectable() const
0522 {
0523     return m_cropTypeSelectable;
0524 }
0525 
0526 int KisToolCrop::decoration() const
0527 {
0528     return m_decoration;
0529 }
0530 
0531 void KisToolCrop::setDecoration(int i)
0532 {
0533     // This shouldn't happen, but safety first
0534     if(i < 0 || i > DECORATION_COUNT)
0535         return;
0536     m_decoration = i;
0537     emit decorationChanged(decoration());
0538     updateCanvasViewRect(boundingRect());
0539 
0540     configGroup.writeEntry("decoration", i);
0541 }
0542 
0543 void KisToolCrop::doCanvasUpdate(const QRect &updateRect)
0544 {
0545     updateCanvasViewRect(updateRect | m_lastCanvasUpdateRect);
0546     m_lastCanvasUpdateRect = updateRect;
0547 }
0548 
0549 void KisToolCrop::slotRectChanged()
0550 {
0551     emit cropHeightChanged(cropHeight());
0552     emit cropWidthChanged(cropWidth());
0553     emit cropXChanged(cropX());
0554     emit cropYChanged(cropY());
0555     emit ratioChanged(ratio());
0556     emit lockHeightChanged(lockHeight());
0557     emit lockWidthChanged(lockWidth());
0558     emit lockRatioChanged(lockRatio());
0559 
0560     emit canGrowChanged(allowGrow());
0561     emit isCenteredChanged(growCenter());
0562 
0563     doCanvasUpdate(boundingRect().toAlignedRect());
0564 }
0565 
0566 void KisToolCrop::setCropX(int x)
0567 {
0568     if(x == m_finalRect.rect().x())
0569         return;
0570 
0571     if (!m_haveCropSelection) {
0572         m_haveCropSelection = true;
0573         m_finalRect.setRectInitial(image()->bounds());
0574     }
0575 
0576     QPoint offset = m_finalRect.rect().topLeft();
0577     offset.setX(x);
0578     m_finalRect.setOffset(offset);
0579 }
0580 
0581 int KisToolCrop::cropX() const
0582 {
0583     return m_finalRect.rect().x();
0584 }
0585 
0586 void KisToolCrop::setCropY(int y)
0587 {
0588     if(y == m_finalRect.rect().y())
0589         return;
0590 
0591     if (!m_haveCropSelection) {
0592         m_haveCropSelection = true;
0593         m_finalRect.setRectInitial(image()->bounds());
0594     }
0595 
0596     QPoint offset = m_finalRect.rect().topLeft();
0597     offset.setY(y);
0598     m_finalRect.setOffset(offset);
0599 }
0600 
0601 int KisToolCrop::cropY() const
0602 {
0603     return m_finalRect.rect().y();
0604 }
0605 
0606 void KisToolCrop::setCropWidth(int w)
0607 {
0608     if(w == m_finalRect.rect().width())
0609         return;
0610 
0611     if (!m_haveCropSelection) {
0612         m_haveCropSelection = true;
0613         m_finalRect.setRectInitial(image()->bounds());
0614     }
0615 
0616     m_finalRect.setWidth(w);
0617 }
0618 
0619 int KisToolCrop::cropWidth() const
0620 {
0621     return m_finalRect.rect().width();
0622 }
0623 
0624 void KisToolCrop::setLockWidth(bool lock)
0625 {
0626     m_finalRect.setWidthLocked(lock);
0627 }
0628 
0629 bool KisToolCrop::lockWidth() const
0630 {
0631     return m_finalRect.widthLocked();
0632 }
0633 
0634 void KisToolCrop::setCropHeight(int h)
0635 {
0636     if(h == m_finalRect.rect().height())
0637         return;
0638 
0639     if (!m_haveCropSelection) {
0640         m_haveCropSelection = true;
0641         m_finalRect.setRectInitial(image()->bounds());
0642     }
0643 
0644     m_finalRect.setHeight(h);
0645 }
0646 
0647 int KisToolCrop::cropHeight() const
0648 {
0649     return m_finalRect.rect().height();
0650 }
0651 
0652 void KisToolCrop::setLockHeight(bool lock)
0653 {
0654     m_finalRect.setHeightLocked(lock);
0655 }
0656 
0657 bool KisToolCrop::lockHeight() const
0658 {
0659     return m_finalRect.heightLocked();
0660 }
0661 
0662 void KisToolCrop::setAllowGrow(bool g)
0663 {
0664     m_finalRect.setCanGrow(g);
0665     m_finalRect.setCropRect(image()->bounds());
0666     configGroup.writeEntry("allowGrow", g);
0667 
0668     emit canGrowChanged(g);
0669 }
0670 
0671 bool KisToolCrop::allowGrow() const
0672 {
0673     return m_finalRect.canGrow();
0674 }
0675 
0676 void KisToolCrop::setGrowCenter(bool value)
0677 {
0678     m_finalRect.setCentered(value);
0679 
0680 
0681     configGroup.writeEntry("growCenter", value);
0682 
0683     emit isCenteredChanged(value);
0684 }
0685 
0686 bool KisToolCrop::growCenter() const
0687 {
0688     return m_finalRect.centered();
0689 }
0690 
0691 void KisToolCrop::setRatio(double ratio)
0692 {
0693     if(ratio == m_finalRect.ratio())
0694         return;
0695 
0696     if (!m_haveCropSelection) {
0697         m_haveCropSelection = true;
0698         m_finalRect.setRectInitial(image()->bounds());
0699     }
0700 
0701     m_finalRect.setRatio(ratio);
0702 }
0703 
0704 double KisToolCrop::ratio() const
0705 {
0706     return m_finalRect.ratio();
0707 }
0708 
0709 void KisToolCrop::setLockRatio(bool lock)
0710 {
0711     m_finalRect.setRatioLocked(lock);
0712 }
0713 
0714 bool KisToolCrop::lockRatio() const
0715 {
0716     return m_finalRect.ratioLocked();
0717 }
0718 
0719 void KisToolCrop::showSizeOnCanvas()
0720 {
0721     KisCanvas2 *kisCanvas =dynamic_cast<KisCanvas2*>(canvas());
0722     Q_ASSERT(kisCanvas);
0723     if(m_mouseOnHandleType == 9) {
0724         kisCanvas->viewManager()->showFloatingMessage(i18n("X: %1\nY: %2"
0725                                                        , optionsWidget->intX->text(), optionsWidget->intY->text())
0726                                                        , QIcon(), 1000, KisFloatingMessage::High, Qt::AlignLeft | Qt::TextWordWrap | Qt::AlignVCenter);
0727     }
0728     else {
0729         kisCanvas->viewManager()->showFloatingMessage(i18n("Width: %1\nHeight: %2"
0730                                                    , optionsWidget->intWidth->text(), optionsWidget->intHeight->text())
0731                                                    , QIcon(), 1000, KisFloatingMessage::High, Qt::AlignLeft | Qt::TextWordWrap | Qt::AlignVCenter);
0732     }
0733 }
0734 
0735 QWidget* KisToolCrop::createOptionWidget()
0736 {
0737     optionsWidget = new KisToolCropConfigWidget(0, this);
0738     // See https://bugs.kde.org/show_bug.cgi?id=316896
0739     QWidget *specialSpacer = new QWidget(optionsWidget);
0740     specialSpacer->setObjectName("SpecialSpacer");
0741     specialSpacer->setFixedSize(0, 0);
0742     optionsWidget->layout()->addWidget(specialSpacer);
0743 
0744     Q_CHECK_PTR(optionsWidget);
0745     optionsWidget->setObjectName(toolId() + " option widget");
0746 
0747     connect(optionsWidget->bnCrop, SIGNAL(clicked()), this, SLOT(crop()));
0748 
0749     connect(optionsWidget, SIGNAL(cropTypeChanged(int)), this, SLOT(setCropTypeLegacy(int)));
0750     connect(optionsWidget, SIGNAL(cropXChanged(int)), this, SLOT(setCropX(int)));
0751     connect(optionsWidget, SIGNAL(cropYChanged(int)), this, SLOT(setCropY(int)));
0752     connect(optionsWidget, SIGNAL(cropHeightChanged(int)), this, SLOT(setCropHeight(int)));
0753     connect(optionsWidget, SIGNAL(lockHeightChanged(bool)), this, SLOT(setLockHeight(bool)));
0754     connect(optionsWidget, SIGNAL(cropWidthChanged(int)), this, SLOT(setCropWidth(int)));
0755     connect(optionsWidget, SIGNAL(lockWidthChanged(bool)), this, SLOT(setLockWidth(bool)));
0756     connect(optionsWidget, SIGNAL(ratioChanged(double)), this, SLOT(setRatio(double)));
0757     connect(optionsWidget, SIGNAL(lockRatioChanged(bool)), this, SLOT(setLockRatio(bool)));
0758     connect(optionsWidget, SIGNAL(decorationChanged(int)), this, SLOT(setDecoration(int)));
0759     connect(optionsWidget, SIGNAL(allowGrowChanged(bool)), this, SLOT(setAllowGrow(bool)));
0760     connect(optionsWidget, SIGNAL(growCenterChanged(bool)), this, SLOT(setGrowCenter(bool)));
0761 
0762     optionsWidget->setFixedHeight(optionsWidget->sizeHint().height());
0763 
0764     connect(applyCrop, SIGNAL(triggered(bool)), this, SLOT(crop()));
0765     connect(centerToggleOption, SIGNAL(triggered(bool)), this, SLOT(setGrowCenter(bool)));
0766     connect(growToggleOption, SIGNAL(triggered(bool)), this, SLOT(setAllowGrow(bool)));
0767     connect(lockWidthToggleOption, SIGNAL(triggered(bool)), this, SLOT(setLockWidth(bool)));
0768     connect(lockHeightToggleOption, SIGNAL(triggered(bool)), this, SLOT(setLockHeight(bool)));
0769     connect(lockRatioToggleOption, SIGNAL(triggered(bool)), this, SLOT(setLockRatio(bool)));
0770 
0771     return optionsWidget;
0772 }
0773 
0774 QRectF KisToolCrop::lowerRightHandleRect(QRectF cropBorderRect)
0775 {
0776     return QRectF(cropBorderRect.right() - m_handleSize / 2.0, cropBorderRect.bottom() - m_handleSize / 2.0, m_handleSize, m_handleSize);
0777 }
0778 
0779 QRectF KisToolCrop::upperRightHandleRect(QRectF cropBorderRect)
0780 {
0781     return QRectF(cropBorderRect.right() - m_handleSize / 2.0 , cropBorderRect.top() - m_handleSize / 2.0, m_handleSize, m_handleSize);
0782 }
0783 
0784 QRectF KisToolCrop::lowerLeftHandleRect(QRectF cropBorderRect)
0785 {
0786     return QRectF(cropBorderRect.left() - m_handleSize / 2.0 , cropBorderRect.bottom() - m_handleSize / 2.0, m_handleSize, m_handleSize);
0787 }
0788 
0789 QRectF KisToolCrop::upperLeftHandleRect(QRectF cropBorderRect)
0790 {
0791     return QRectF(cropBorderRect.left() - m_handleSize / 2.0, cropBorderRect.top() - m_handleSize / 2.0, m_handleSize, m_handleSize);
0792 }
0793 
0794 QRectF KisToolCrop::lowerHandleRect(QRectF cropBorderRect)
0795 {
0796     return QRectF(cropBorderRect.left() + (cropBorderRect.width() - m_handleSize) / 2.0 , cropBorderRect.bottom() - m_handleSize / 2.0, m_handleSize, m_handleSize);
0797 }
0798 
0799 QRectF KisToolCrop::rightHandleRect(QRectF cropBorderRect)
0800 {
0801     return QRectF(cropBorderRect.right() - m_handleSize / 2.0 , cropBorderRect.top() + (cropBorderRect.height() - m_handleSize) / 2.0, m_handleSize, m_handleSize);
0802 }
0803 
0804 QRectF KisToolCrop::upperHandleRect(QRectF cropBorderRect)
0805 {
0806     return QRectF(cropBorderRect.left() + (cropBorderRect.width() - m_handleSize) / 2.0 , cropBorderRect.top() - m_handleSize / 2.0, m_handleSize, m_handleSize);
0807 }
0808 
0809 QRectF KisToolCrop::leftHandleRect(QRectF cropBorderRect)
0810 {
0811     return QRectF(cropBorderRect.left() - m_handleSize / 2.0, cropBorderRect.top() + (cropBorderRect.height() - m_handleSize) / 2.0, m_handleSize, m_handleSize);
0812 }
0813 
0814 QPainterPath KisToolCrop::handlesPath()
0815 {
0816     QRectF cropBorderRect = borderLineRect();
0817     QPainterPath path;
0818 
0819     path.addRect(upperLeftHandleRect(cropBorderRect));
0820     path.addRect(upperRightHandleRect(cropBorderRect));
0821     path.addRect(lowerLeftHandleRect(cropBorderRect));
0822     path.addRect(lowerRightHandleRect(cropBorderRect));
0823     path.addRect(upperHandleRect(cropBorderRect));
0824     path.addRect(lowerHandleRect(cropBorderRect));
0825     path.addRect(leftHandleRect(cropBorderRect));
0826     path.addRect(rightHandleRect(cropBorderRect));
0827 
0828     return path;
0829 }
0830 
0831 qint32 KisToolCrop::mouseOnHandle(QPointF currentViewPoint)
0832 {
0833     QRectF borderRect = borderLineRect();
0834     qint32 handleType = None;
0835 
0836     if (!m_haveCropSelection) {
0837         return None;
0838     }
0839 
0840     if (upperLeftHandleRect(borderRect).contains(currentViewPoint)) {
0841         handleType = UpperLeft;
0842     } else if (lowerLeftHandleRect(borderRect).contains(currentViewPoint)) {
0843         handleType = LowerLeft;
0844     } else if (upperRightHandleRect(borderRect).contains(currentViewPoint)) {
0845         handleType = UpperRight;
0846     } else if (lowerRightHandleRect(borderRect).contains(currentViewPoint)) {
0847         handleType = LowerRight;
0848     } else if (upperHandleRect(borderRect).contains(currentViewPoint)) {
0849         handleType = Upper;
0850     } else if (lowerHandleRect(borderRect).contains(currentViewPoint)) {
0851         handleType = Lower;
0852     } else if (leftHandleRect(borderRect).contains(currentViewPoint)) {
0853         handleType = Left;
0854     } else if (rightHandleRect(borderRect).contains(currentViewPoint)) {
0855         handleType = Right;
0856     } else if (borderRect.contains(currentViewPoint)) {
0857         handleType = Inside;
0858     }
0859 
0860     return handleType;
0861 }
0862 
0863 void KisToolCrop::setMoveResizeCursor(qint32 handle)
0864 {
0865     QCursor cursorType;
0866 
0867     switch (handle) {
0868     case(UpperLeft):
0869     case(LowerRight):
0870         cursorType = KisCursor::sizeFDiagCursor();
0871         break;
0872     case(LowerLeft):
0873     case(UpperRight):
0874         cursorType = KisCursor::sizeBDiagCursor();
0875         break;
0876     case(Upper):
0877     case(Lower):
0878         cursorType = KisCursor::sizeVerCursor();
0879         break;
0880     case(Left):
0881     case(Right):
0882         cursorType = KisCursor::sizeHorCursor();
0883         break;
0884     case(Inside):
0885         cursorType = KisCursor::sizeAllCursor();
0886         break;
0887     default:
0888         if (m_haveCropSelection) {
0889             cursorType = KisCursor::arrowCursor();
0890         } else {
0891             cursorType = cursor();
0892         }
0893         break;
0894     }
0895     useCursor(cursorType);
0896 }
0897 
0898 QRectF KisToolCrop::boundingRect()
0899 {
0900     QRectF rect = handlesPath().boundingRect();
0901     rect.adjust(-HANDLE_BORDER_LINE_WIDTH, -HANDLE_BORDER_LINE_WIDTH, HANDLE_BORDER_LINE_WIDTH, HANDLE_BORDER_LINE_WIDTH);
0902     return rect;
0903 }
0904 
0905 void KisToolCrop::drawDecorationLine(QPainter *p, DecorationLine *decorLine, const QRectF rect)
0906 {
0907     QPointF start = rect.topLeft();
0908     QPointF end = rect.topLeft();
0909     qreal small = qMin(rect.width(), rect.height());
0910     qreal large = qMax(rect.width(), rect.height());
0911 
0912     switch (decorLine->startXRelation) {
0913     case DecorationLine::Width:
0914         start.setX(start.x() + decorLine->start.x() * rect.width());
0915         break;
0916     case DecorationLine::Height:
0917         start.setX(start.x() + decorLine->start.x() * rect.height());
0918         break;
0919     case DecorationLine::Smallest:
0920         start.setX(start.x() + decorLine->start.x() * small);
0921         break;
0922     case DecorationLine::Largest:
0923         start.setX(start.x() + decorLine->start.x() * large);
0924         break;
0925     }
0926 
0927     switch (decorLine->startYRelation) {
0928     case DecorationLine::Width:
0929         start.setY(start.y() + decorLine->start.y() * rect.width());
0930         break;
0931     case DecorationLine::Height:
0932         start.setY(start.y() + decorLine->start.y() * rect.height());
0933         break;
0934     case DecorationLine::Smallest:
0935         start.setY(start.y() + decorLine->start.y() * small);
0936         break;
0937     case DecorationLine::Largest:
0938         start.setY(start.y() + decorLine->start.y() * large);
0939         break;
0940     }
0941 
0942     switch (decorLine->endXRelation) {
0943     case DecorationLine::Width:
0944         end.setX(end.x() + decorLine->end.x() * rect.width());
0945         break;
0946     case DecorationLine::Height:
0947         end.setX(end.x() + decorLine->end.x() * rect.height());
0948         break;
0949     case DecorationLine::Smallest:
0950         end.setX(end.x() + decorLine->end.x() * small);
0951         break;
0952     case DecorationLine::Largest:
0953         end.setX(end.x() + decorLine->end.x() * large);
0954         break;
0955     }
0956 
0957     switch (decorLine->endYRelation) {
0958     case DecorationLine::Width:
0959         end.setY(end.y() + decorLine->end.y() * rect.width());
0960         break;
0961     case DecorationLine::Height:
0962         end.setY(end.y() + decorLine->end.y() * rect.height());
0963         break;
0964     case DecorationLine::Smallest:
0965         end.setY(end.y() + decorLine->end.y() * small);
0966         break;
0967     case DecorationLine::Largest:
0968         end.setY(end.y() + decorLine->end.y() * large);
0969         break;
0970     }
0971 
0972     p->drawLine(start, end);
0973 }