File indexing completed on 2024-05-12 04:21:28
0001 0002 /* 0003 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org> 0004 All rights reserved. 0005 0006 Redistribution and use in source and binary forms, with or without 0007 modification, are permitted provided that the following conditions 0008 are met: 0009 0010 1. Redistributions of source code must retain the above copyright 0011 notice, this list of conditions and the following disclaimer. 0012 2. Redistributions in binary form must reproduce the above copyright 0013 notice, this list of conditions and the following disclaimer in the 0014 documentation and/or other materials provided with the distribution. 0015 0016 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 0017 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 0018 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 0019 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 0020 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 0021 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 0022 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 0023 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 0025 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0026 */ 0027 0028 0029 #define DEBUG_KP_TOOL_SELECTION 0 0030 0031 0032 #include "kpAbstractSelectionTool.h" 0033 #include "kpAbstractSelectionToolPrivate.h" 0034 #include "kpLogCategories.h" 0035 0036 #include <KLocalizedString> 0037 0038 #include "layers/selections/image/kpAbstractImageSelection.h" 0039 #include "layers/selections/kpAbstractSelection.h" 0040 #include "commands/kpCommandHistory.h" 0041 #include "kpDefs.h" 0042 #include "document/kpDocument.h" 0043 #include "commands/kpMacroCommand.h" 0044 #include "commands/tools/selection/kpToolSelectionCreateCommand.h" 0045 #include "commands/tools/selection/kpToolSelectionDestroyCommand.h" 0046 #include "environments/tools/selection/kpToolSelectionEnvironment.h" 0047 #include "commands/tools/selection/kpToolSelectionMoveCommand.h" 0048 #include "commands/tools/selection/kpToolSelectionResizeScaleCommand.h" 0049 #include "commands/tools/selection/kpToolImageSelectionTransparencyCommand.h" 0050 #include "widgets/toolbars/kpToolToolBar.h" 0051 #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" 0052 #include "views/kpView.h" 0053 #include "views/manager/kpViewManager.h" 0054 0055 0056 // private 0057 int kpAbstractSelectionTool::onSelectionResizeHandle () const 0058 { 0059 kpView *v = viewManager ()->viewUnderCursor (); 0060 if (!v) { 0061 return 0; 0062 } 0063 0064 return v->mouseOnSelectionResizeHandle (currentViewPoint ()); 0065 } 0066 0067 //--------------------------------------------------------------------- 0068 0069 // private 0070 void kpAbstractSelectionTool::initResizeScale () 0071 { 0072 d->currentResizeScaleCommand = nullptr; 0073 0074 // d->resizeScaleType 0075 } 0076 0077 // private 0078 void kpAbstractSelectionTool::uninitResizeScale () 0079 { 0080 // (state must be after construction, or after some time after endResizeScale()) 0081 Q_ASSERT (!d->currentResizeScaleCommand); 0082 0083 // d->resizeScaleType 0084 } 0085 0086 0087 // private 0088 void kpAbstractSelectionTool::beginResizeScale () 0089 { 0090 // (state must be after construction, or after some time after endResizeScale()) 0091 Q_ASSERT (!d->currentResizeScaleCommand); 0092 0093 // d->resizeScaleType 0094 } 0095 0096 // private 0097 void kpAbstractSelectionTool::endResizeScale () 0098 { 0099 // (should have been killed by cancelResizeScale() or endResizeScale()) 0100 Q_ASSERT (!d->currentResizeScaleCommand); 0101 0102 // d->resizeScaleType 0103 } 0104 0105 //--------------------------------------------------------------------- 0106 0107 // private 0108 void kpAbstractSelectionTool::setCursorResizeScale () 0109 { 0110 #if DEBUG_KP_TOOL_SELECTION && 0 0111 qCDebug(kpLogTools) << "\tonSelectionResizeHandle=" 0112 << onSelectionResizeHandle (); 0113 #endif 0114 Qt::CursorShape shape = Qt::ArrowCursor; 0115 0116 switch (onSelectionResizeHandle ()) 0117 { 0118 case (kpView::Top | kpView::Left): 0119 case (kpView::Bottom | kpView::Right): 0120 shape = Qt::SizeFDiagCursor; 0121 break; 0122 0123 case (kpView::Bottom | kpView::Left): 0124 case (kpView::Top | kpView::Right): 0125 shape = Qt::SizeBDiagCursor; 0126 break; 0127 0128 case kpView::Top: 0129 case kpView::Bottom: 0130 shape = Qt::SizeVerCursor; 0131 break; 0132 0133 case kpView::Left: 0134 case kpView::Right: 0135 shape = Qt::SizeHorCursor; 0136 break; 0137 } 0138 0139 viewManager ()->setCursor (shape); 0140 } 0141 0142 //--------------------------------------------------------------------- 0143 0144 // protected virtual 0145 void kpAbstractSelectionTool::setSelectionBorderForBeginDrawResizeScale () 0146 { 0147 viewManager ()->setQueueUpdates (); 0148 { 0149 viewManager ()->setSelectionBorderVisible (true); 0150 viewManager ()->setSelectionBorderFinished (true); 0151 } 0152 viewManager ()->restoreQueueUpdates (); 0153 } 0154 0155 //--------------------------------------------------------------------- 0156 0157 // private 0158 void kpAbstractSelectionTool::beginDrawResizeScale () 0159 { 0160 d->resizeScaleType = onSelectionResizeHandle (); 0161 0162 /*virtual*/setSelectionBorderForBeginDrawResizeScale (); 0163 0164 setUserMessage (cancelUserMessage ()); 0165 } 0166 0167 //--------------------------------------------------------------------- 0168 0169 0170 // private 0171 void kpAbstractSelectionTool::drawResizeScaleTryKeepAspect ( 0172 int newWidth, int newHeight, 0173 bool horizontalGripDragged, bool verticalGripDragged, 0174 const kpAbstractSelection &originalSelection, 0175 int *newWidthOut, int *newHeightOut) 0176 { 0177 const int oldWidth = originalSelection.width (), 0178 oldHeight = originalSelection.height (); 0179 0180 // Width changed more than height? At equality, favor width. 0181 // Fix width, change height. 0182 // 0183 // We use <horizontalGripDragged> and <verticalGripDragged> to prevent 0184 // e.g. the situation where we've dragged such that newWidth < oldWidth but 0185 // we're not dragging a vertical grip. We certainly don't want this 0186 // code to modify the width - we want to fix the width and change the 0187 // height. 0188 if ((horizontalGripDragged ? double (newWidth) / oldWidth : 0) >= 0189 (verticalGripDragged ? double (newHeight) / oldHeight : 0)) 0190 { 0191 *newHeightOut = newWidth * oldHeight / oldWidth; 0192 *newHeightOut = qMax (originalSelection.minimumHeight (), *newHeightOut); 0193 } 0194 // Height changed more than width? 0195 // Fix height, change width. 0196 else 0197 { 0198 *newWidthOut = newHeight * oldWidth / oldHeight; 0199 *newWidthOut = qMax (originalSelection.minimumWidth (), *newWidthOut); 0200 } 0201 } 0202 0203 //--------------------------------------------------------------------- 0204 0205 // private 0206 void kpAbstractSelectionTool::drawResizeScaleCalculateNewSelectionPosSize ( 0207 const kpAbstractSelection &originalSelection, 0208 int *newX, int *newY, 0209 int *newWidth, int *newHeight) 0210 { 0211 // 0212 // Determine new width. 0213 // 0214 0215 // Dragging left or right grip? 0216 // If left, positive X drags decrease width. 0217 // If right, positive X drags increase width. 0218 int userXSign = 0; 0219 if (d->resizeScaleType & kpView::Left) { 0220 userXSign = -1; 0221 } 0222 else if (d->resizeScaleType & kpView::Right) { 0223 userXSign = +1; 0224 } 0225 0226 // Calculate new width. 0227 *newWidth = originalSelection.width () + 0228 userXSign * (currentPoint ().x () - startPoint ().x ()); 0229 0230 // Don't allow new width to be less than that kind of selection type's 0231 // minimum. 0232 *newWidth = qMax (originalSelection.minimumWidth (), *newWidth); 0233 0234 0235 // 0236 // Determine new height. 0237 // 0238 0239 // Dragging top or bottom grip? 0240 // If top, positive Y drags decrease height. 0241 // If bottom, positive Y drags increase height. 0242 int userYSign = 0; 0243 if (d->resizeScaleType & kpView::Top) { 0244 userYSign = -1; 0245 } 0246 else if (d->resizeScaleType & kpView::Bottom) { 0247 userYSign = +1; 0248 } 0249 0250 // Calculate new height. 0251 *newHeight = originalSelection.height () + 0252 userYSign * (currentPoint ().y () - startPoint ().y ()); 0253 0254 // Don't allow new height to be less than that kind of selection type's 0255 // minimum. 0256 *newHeight = qMax (originalSelection.minimumHeight (), *newHeight); 0257 0258 0259 // Keep aspect ratio? 0260 if (shiftPressed ()) 0261 { 0262 drawResizeScaleTryKeepAspect (*newWidth, *newHeight, 0263 (userXSign != 0)/*X or XY grip dragged*/, 0264 (userYSign != 0)/*Y or XY grip dragged*/, 0265 originalSelection, 0266 newWidth/*ptr*/, newHeight/*ptr*/); 0267 } 0268 0269 0270 *newX = originalSelection.x (); 0271 *newY = originalSelection.y (); 0272 0273 0274 // 0275 // Adjust x/y to new width/height for left/top resizes. 0276 // 0277 0278 if (d->resizeScaleType & kpView::Left) 0279 { 0280 *newX -= (*newWidth - originalSelection.width ()); 0281 } 0282 0283 if (d->resizeScaleType & kpView::Top) 0284 { 0285 *newY -= (*newHeight - originalSelection.height ()); 0286 } 0287 0288 #if DEBUG_KP_TOOL_SELECTION && 1 0289 qCDebug(kpLogTools) << "\t\tnewX=" << *newX 0290 << " newY=" << *newY 0291 << " newWidth=" << *newWidth 0292 << " newHeight=" << *newHeight; 0293 #endif 0294 } 0295 0296 //--------------------------------------------------------------------- 0297 0298 // private 0299 void kpAbstractSelectionTool::drawResizeScale ( 0300 const QPoint &thisPoint, 0301 const QRect &/*normalizedRect*/) 0302 { 0303 #if DEBUG_KP_TOOL_SELECTION && 1 0304 qCDebug(kpLogTools) << "\tresize/scale"; 0305 #endif 0306 0307 kpAbstractSelection *sel = document ()->selection (); 0308 0309 if (!d->dragAccepted && thisPoint == startPoint ()) 0310 { 0311 #if DEBUG_KP_TOOL_SELECTION && 1 0312 qCDebug(kpLogTools) << "\t\tnop"; 0313 #endif 0314 0315 setUserShapePoints (QPoint (sel->width (), sel->height ())); 0316 return; 0317 } 0318 0319 0320 giveContentIfNeeded (); 0321 0322 0323 if (!d->currentResizeScaleCommand) 0324 { 0325 d->currentResizeScaleCommand 0326 = new kpToolSelectionResizeScaleCommand (environ ()->commandEnvironment ()); 0327 } 0328 0329 0330 const kpAbstractSelection *originalSelection = 0331 d->currentResizeScaleCommand->originalSelection (); 0332 0333 0334 // There is nothing illegal about position (-1,-1) but why not. 0335 int newX = -1, newY = -1, 0336 newWidth = 0, newHeight = 0; 0337 0338 // This should change all of the above values. 0339 drawResizeScaleCalculateNewSelectionPosSize ( 0340 *originalSelection, 0341 &newX, &newY, 0342 &newWidth, &newHeight); 0343 0344 0345 viewManager ()->setFastUpdates (); 0346 { 0347 d->currentResizeScaleCommand->resizeAndMoveTo ( 0348 newWidth, newHeight, 0349 QPoint (newX, newY), 0350 true/*smooth scale delayed*/); 0351 } 0352 viewManager ()->restoreFastUpdates (); 0353 0354 setUserShapePoints (QPoint (originalSelection->width (), 0355 originalSelection->height ()), 0356 QPoint (newWidth, 0357 newHeight), 0358 false/*don't set size*/); 0359 setUserShapeSize (newWidth - originalSelection->width (), 0360 newHeight - originalSelection->height ()); 0361 0362 0363 d->dragAccepted = true; 0364 } 0365 0366 //--------------------------------------------------------------------- 0367 0368 0369 // private 0370 void kpAbstractSelectionTool::cancelResizeScale () 0371 { 0372 #if DEBUG_KP_TOOL_SELECTION 0373 qCDebug(kpLogTools) << "\twas resize/scale sel - kill"; 0374 #endif 0375 0376 // NOP drag? 0377 if (!d->currentResizeScaleCommand) { 0378 return; 0379 } 0380 0381 #if DEBUG_KP_TOOL_SELECTION 0382 qCDebug(kpLogTools) << "\t\tundo currentResizeScaleCommand"; 0383 #endif 0384 d->currentResizeScaleCommand->finalize (); // (unneeded but let's be safe) 0385 d->currentResizeScaleCommand->unexecute (); 0386 delete d->currentResizeScaleCommand; 0387 d->currentResizeScaleCommand = nullptr; 0388 } 0389 0390 //--------------------------------------------------------------------- 0391 0392 // private 0393 void kpAbstractSelectionTool::endDrawResizeScale () 0394 { 0395 // NOP drag? 0396 if (!d->currentResizeScaleCommand) { 0397 return; 0398 } 0399 0400 d->currentResizeScaleCommand->finalize (); 0401 0402 addNeedingContentCommand (d->currentResizeScaleCommand); 0403 d->currentResizeScaleCommand = nullptr; 0404 } 0405 0406 //--------------------------------------------------------------------- 0407 0408 // private 0409 QVariant kpAbstractSelectionTool::operationResizeScale (Operation op, 0410 const QVariant &data1, const QVariant &data2) 0411 { 0412 (void) data1; 0413 (void) data2; 0414 0415 0416 switch (op) 0417 { 0418 case HaventBegunDrawUserMessage: 0419 return /*virtual*/haventBegunDrawUserMessageResizeScale (); 0420 0421 case SetCursor: 0422 setCursorResizeScale (); 0423 break; 0424 0425 case BeginDraw: 0426 beginDrawResizeScale (); 0427 break; 0428 0429 case Draw: 0430 drawResizeScale (currentPoint (), normalizedRect ()); 0431 break; 0432 0433 case Cancel: 0434 cancelResizeScale (); 0435 break; 0436 0437 case EndDraw: 0438 endDrawResizeScale (); 0439 break; 0440 0441 default: 0442 Q_ASSERT (!"Unhandled operation"); 0443 break; 0444 } 0445 0446 0447 return {}; 0448 } 0449 0450 //---------------------------------------------------------------------