File indexing completed on 2024-05-12 04:21:27
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 0035 #include <QCursor> 0036 #include <QMenu> 0037 #include <QTimer> 0038 0039 #include "kpLogCategories.h" 0040 #include "layers/selections/kpAbstractSelection.h" 0041 #include "commands/tools/selection/kpAbstractSelectionContentCommand.h" 0042 #include "commands/kpCommandHistory.h" 0043 #include "kpDefs.h" 0044 #include "document/kpDocument.h" 0045 #include "commands/kpMacroCommand.h" 0046 #include "commands/tools/selection/kpToolSelectionCreateCommand.h" 0047 #include "environments/tools/selection/kpToolSelectionEnvironment.h" 0048 #include "widgets/toolbars/kpToolToolBar.h" 0049 #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" 0050 #include "views/kpView.h" 0051 #include "views/manager/kpViewManager.h" 0052 #include "imagelib/kpPainter.h" 0053 0054 #include <KLocalizedString> 0055 0056 //--------------------------------------------------------------------- 0057 0058 // For either of these timers, they are only active during the "drawing" phase 0059 // of kpTool. 0060 static void AssertAllTimersInactive (struct kpAbstractSelectionToolPrivate *d) 0061 { 0062 Q_ASSERT (!d->createNOPTimer->isActive ()); 0063 Q_ASSERT (!d->RMBMoveUpdateGUITimer->isActive ()); 0064 } 0065 0066 //--------------------------------------------------------------------- 0067 0068 kpAbstractSelectionTool::kpAbstractSelectionTool ( 0069 const QString &text, 0070 const QString &description, 0071 int key, 0072 kpToolSelectionEnvironment *environ, QObject *parent, 0073 const QString &name) 0074 0075 : kpTool (text, description, key, environ, parent, name), 0076 d (new kpAbstractSelectionToolPrivate ()) 0077 { 0078 d->drawType = None; 0079 d->currentSelContentCommand = nullptr; 0080 0081 // d->dragAccepted 0082 // d->hadSelectionBeforeDrag 0083 0084 // d->cancelledShapeButStillHoldingButtons 0085 0086 d->toolWidgetOpaqueOrTransparent = nullptr; 0087 0088 0089 initCreate (); 0090 initMove (); 0091 initResizeScale (); 0092 0093 // It would be bad practice to have timers ticking even when this tool 0094 // is not in use. 0095 ::AssertAllTimersInactive (d); 0096 } 0097 0098 //--------------------------------------------------------------------- 0099 0100 kpAbstractSelectionTool::~kpAbstractSelectionTool () 0101 { 0102 uninitCreate (); 0103 uninitMove (); 0104 uninitResizeScale (); 0105 0106 0107 // (state must be after construction, or after some time after end()) 0108 Q_ASSERT (d->drawType == None); 0109 Q_ASSERT (!d->currentSelContentCommand); 0110 0111 // d->dragAccepted 0112 // d->hadSelectionBeforeDraw 0113 0114 // d->cancelledShapeButStillHoldingButtons 0115 0116 // d->toolWidgetOpaqueOrTransparent 0117 0118 0119 delete d; 0120 } 0121 0122 //--------------------------------------------------------------------- 0123 0124 // protected 0125 kpAbstractSelectionTool::DrawType kpAbstractSelectionTool::drawType () const 0126 { 0127 return d->drawType; 0128 } 0129 0130 //--------------------------------------------------------------------- 0131 0132 // protected 0133 bool kpAbstractSelectionTool::hadSelectionBeforeDraw () const 0134 { 0135 return d->hadSelectionBeforeDraw; 0136 } 0137 0138 //--------------------------------------------------------------------- 0139 0140 // protected overrides [base kpTool] 0141 kpToolSelectionEnvironment *kpAbstractSelectionTool::environ () const 0142 { 0143 kpToolEnvironment *e = kpTool::environ (); 0144 Q_ASSERT (dynamic_cast <kpToolSelectionEnvironment *> (e)); 0145 return dynamic_cast <kpToolSelectionEnvironment *> (e); 0146 } 0147 0148 //--------------------------------------------------------------------- 0149 0150 // protected 0151 bool kpAbstractSelectionTool::controlOrShiftPressed () const 0152 { 0153 return (controlPressed () || shiftPressed ()); 0154 } 0155 0156 //--------------------------------------------------------------------- 0157 0158 // protected 0159 void kpAbstractSelectionTool::pushOntoDocument () 0160 { 0161 #if DEBUG_KP_TOOL_SELECTION && 1 0162 qCDebug(kpLogTools) << "kpAbstractSelectionTool::pushOntoDocument() selection=" 0163 << document ()->selection (); 0164 #endif 0165 Q_ASSERT (document ()->selection ()); 0166 environ ()->deselectSelection (); 0167 } 0168 0169 //--------------------------------------------------------------------- 0170 0171 // protected 0172 void kpAbstractSelectionTool::giveContentIfNeeded () 0173 { 0174 kpAbstractSelection *sel = document ()->selection (); 0175 Q_ASSERT (sel); 0176 0177 if (sel->hasContent ()) { 0178 return; 0179 } 0180 0181 if (d->currentSelContentCommand) { 0182 return; 0183 } 0184 0185 d->currentSelContentCommand = /*virtual*/newGiveContentCommand (); 0186 d->currentSelContentCommand->execute (); 0187 } 0188 0189 //--------------------------------------------------------------------- 0190 0191 // protected 0192 // REFACTOR: sync: Code dup with kpMainWindow::addImageOrSelectionCommand (). 0193 void kpAbstractSelectionTool::addNeedingContentCommand (kpCommand *cmd) 0194 { 0195 Q_ASSERT (cmd); 0196 0197 // Did we fill the selection with content? 0198 if (d->currentSelContentCommand) 0199 { 0200 // Make the border creation a command. 0201 #if DEBUG_KP_TOOL_SELECTION 0202 qCDebug(kpLogTools) << "\thave currentSelContentCommand"; 0203 #endif 0204 commandHistory ()->addCreateSelectionCommand ( 0205 new kpToolSelectionCreateCommand ( 0206 /*virtual*/nameOfCreateCommand (), 0207 *d->currentSelContentCommand->originalSelection (), 0208 environ ()->commandEnvironment ()), 0209 false/*no exec - user already dragged out sel*/); 0210 } 0211 0212 // Do we have a content setting command we need to commit? 0213 // (yes, this is the same check as the previous "if") 0214 if (d->currentSelContentCommand) 0215 { 0216 // Put the content command + given command (e.g. movement) together 0217 // as a macro command, in the command history. 0218 kpMacroCommand *macroCmd = new kpMacroCommand ( 0219 cmd->name (), environ ()->commandEnvironment ()); 0220 0221 macroCmd->addCommand (d->currentSelContentCommand); 0222 d->currentSelContentCommand = nullptr; 0223 0224 macroCmd->addCommand (cmd); 0225 0226 commandHistory ()->addCommand (macroCmd, false/*no exec*/); 0227 } 0228 else 0229 { 0230 // Put the given command into the command history. 0231 commandHistory ()->addCommand (cmd, false/*no exec*/); 0232 } 0233 } 0234 0235 //--------------------------------------------------------------------- 0236 0237 0238 // protected virtual 0239 void kpAbstractSelectionTool::setSelectionBorderForHaventBegunDraw () 0240 { 0241 viewManager ()->setQueueUpdates (); 0242 { 0243 viewManager ()->setSelectionBorderVisible (true); 0244 viewManager ()->setSelectionBorderFinished (true); 0245 } 0246 viewManager ()->restoreQueueUpdates (); 0247 } 0248 0249 //--------------------------------------------------------------------- 0250 0251 // private 0252 QString kpAbstractSelectionTool::haventBegunDrawUserMessage () 0253 { 0254 #if DEBUG_KP_TOOL_SELECTION && 0 0255 qCDebug(kpLogTools) << "kpAbstractSelectionTool::haventBegunDrawUserMessage()" 0256 " cancelledShapeButStillHoldingButtons=" 0257 << d->cancelledShapeButStillHoldingButtons; 0258 #endif 0259 0260 if (d->cancelledShapeButStillHoldingButtons) { 0261 return i18n ("Let go of all the mouse buttons."); 0262 } 0263 0264 return operation (calculateDrawType (), HaventBegunDrawUserMessage).toString (); 0265 } 0266 0267 //--------------------------------------------------------------------- 0268 0269 // public virtual [base kpTool] 0270 void kpAbstractSelectionTool::begin () 0271 { 0272 #if DEBUG_KP_TOOL_SELECTION 0273 qCDebug(kpLogTools) << "kpAbstractSelectionTool<" << objectName () << ">::begin()"; 0274 #endif 0275 0276 ::AssertAllTimersInactive (d); 0277 0278 // (state must be after construction, or after some time after end()) 0279 Q_ASSERT (d->drawType == None); 0280 Q_ASSERT (!d->currentSelContentCommand); 0281 0282 d->dragAccepted = false; 0283 // d->hadSelectionBeforeDraw 0284 0285 d->cancelledShapeButStillHoldingButtons = false; 0286 0287 0288 kpToolToolBar *tb = toolToolBar (); 0289 Q_ASSERT (tb); 0290 0291 d->toolWidgetOpaqueOrTransparent = tb->toolWidgetOpaqueOrTransparent (); 0292 Q_ASSERT (d->toolWidgetOpaqueOrTransparent); 0293 connect (d->toolWidgetOpaqueOrTransparent, 0294 &kpToolWidgetOpaqueOrTransparent::isOpaqueChanged, 0295 this, &kpAbstractSelectionTool::slotIsOpaqueChanged); 0296 d->toolWidgetOpaqueOrTransparent->show (); 0297 0298 /*virtual*/setSelectionBorderForHaventBegunDraw (); 0299 0300 0301 beginCreate (); 0302 beginMove (); 0303 beginResizeScale (); 0304 0305 0306 setUserMessage (haventBegunDrawUserMessage ()); 0307 } 0308 0309 //--------------------------------------------------------------------- 0310 0311 // public virtual [base kpTool] 0312 void kpAbstractSelectionTool::end () 0313 { 0314 #if DEBUG_KP_TOOL_SELECTION 0315 qCDebug(kpLogTools) << "kpAbstractSelectionTool<" << objectName () << ">::end()"; 0316 #endif 0317 0318 if (document ()->selection ()) { 0319 pushOntoDocument (); 0320 } 0321 0322 0323 endCreate (); 0324 endMove (); 0325 endResizeScale (); 0326 0327 0328 // (should have been killed by cancelShape() or endDraw()) 0329 Q_ASSERT (d->drawType == None); 0330 Q_ASSERT (!d->currentSelContentCommand); 0331 0332 // d->dragAccepted 0333 // d->hadSelectionBeforeDraw 0334 0335 // d->cancelledShapeButStillHoldingButtons 0336 0337 0338 Q_ASSERT (d->toolWidgetOpaqueOrTransparent); 0339 disconnect (d->toolWidgetOpaqueOrTransparent, 0340 &kpToolWidgetOpaqueOrTransparent::isOpaqueChanged, 0341 this, &kpAbstractSelectionTool::slotIsOpaqueChanged); 0342 d->toolWidgetOpaqueOrTransparent = nullptr; 0343 0344 0345 viewManager ()->unsetCursor (); 0346 0347 ::AssertAllTimersInactive (d); 0348 } 0349 0350 //--------------------------------------------------------------------- 0351 0352 // public virtual [base kpTool] 0353 void kpAbstractSelectionTool::reselect () 0354 { 0355 #if DEBUG_KP_TOOL_SELECTION 0356 qCDebug(kpLogTools) << "kpAbstractSelectionTool::reselect()"; 0357 #endif 0358 0359 if (document ()->selection ()) { 0360 pushOntoDocument (); 0361 } 0362 } 0363 0364 //--------------------------------------------------------------------- 0365 0366 // protected virtual 0367 kpAbstractSelectionTool::DrawType kpAbstractSelectionTool::calculateDrawTypeInsideSelection () const 0368 { 0369 #if DEBUG_KP_TOOL_SELECTION 0370 qCDebug(kpLogTools) << "\t\tis move"; 0371 #endif 0372 return kpAbstractSelectionTool::Move; 0373 } 0374 0375 //--------------------------------------------------------------------- 0376 0377 // protected virtual 0378 kpAbstractSelectionTool::DrawType kpAbstractSelectionTool::calculateDrawType () const 0379 { 0380 kpAbstractSelection *sel = document ()->selection (); 0381 if (!sel) { 0382 return Create; 0383 } 0384 #if DEBUG_KP_TOOL_SELECTION 0385 qCDebug(kpLogTools) << "\thas sel region rect=" << sel->boundingRect (); 0386 #endif 0387 0388 if (onSelectionResizeHandle () && !controlOrShiftPressed ()) { 0389 return ResizeScale; 0390 } 0391 0392 if (sel->contains (currentPoint ())) { 0393 return /*virtual*/calculateDrawTypeInsideSelection (); 0394 } 0395 0396 return Create; 0397 } 0398 0399 //--------------------------------------------------------------------- 0400 0401 // public virtual [base kpTool] 0402 void kpAbstractSelectionTool::beginDraw () 0403 { 0404 #if DEBUG_KP_TOOL_SELECTION 0405 qCDebug(kpLogTools) << "kpAbstractSelectionTool::beginDraw() startPoint ()=" 0406 << startPoint () 0407 << " QCursor::pos() view startPoint=" 0408 << viewUnderStartPoint ()->mapFromGlobal (QCursor::pos ()); 0409 #endif 0410 0411 // endDraw() and cancelShape() should have taken care of these. 0412 ::AssertAllTimersInactive (d); 0413 0414 // In case the cursor was wrong to start with 0415 // (forgot to call kpTool::somethingBelowTheCursorChanged()), 0416 // make sure it is correct during this operation. 0417 hover (currentPoint ()); 0418 0419 // Currently used only to end the current text 0420 if (hasBegunShape ()) 0421 { 0422 endShape(currentPoint(), 0423 kpPainter::normalizedRect(startPoint()/* TODO: wrong */, currentPoint())); 0424 } 0425 0426 d->drawType = calculateDrawType (); 0427 d->dragAccepted = false; 0428 0429 kpAbstractSelection *sel = document ()->selection (); 0430 d->hadSelectionBeforeDraw = bool (sel); 0431 0432 operation (d->drawType, BeginDraw); 0433 } 0434 0435 //--------------------------------------------------------------------- 0436 0437 0438 // public virtual [base kpTool] 0439 void kpAbstractSelectionTool::hover (const QPoint &point) 0440 { 0441 #if DEBUG_KP_TOOL_SELECTION && 1 0442 qCDebug(kpLogTools) << "kpAbstractSelectionTool::hover" << point; 0443 #endif 0444 0445 operation (calculateDrawType (), SetCursor); 0446 0447 setUserShapePoints (point, KP_INVALID_POINT, false/*don't set size*/); 0448 if (document () && document ()->selection ()) 0449 { 0450 setUserShapeSize (document ()->selection ()->width (), 0451 document ()->selection ()->height ()); 0452 } 0453 else 0454 { 0455 setUserShapeSize (KP_INVALID_SIZE); 0456 } 0457 0458 QString mess = haventBegunDrawUserMessage (); 0459 if (mess != userMessage ()) { 0460 setUserMessage (mess); 0461 } 0462 } 0463 0464 //--------------------------------------------------------------------- 0465 0466 0467 // public virtual [base kpTool] 0468 void kpAbstractSelectionTool::draw (const QPoint &thisPoint, const QPoint & /*lastPoint*/, 0469 const QRect &normalizedRect) 0470 { 0471 #if DEBUG_KP_TOOL_SELECTION && 1 0472 qCDebug(kpLogTools) << "kpAbstractSelectionTool::draw (" << thisPoint 0473 << ",startPoint=" << startPoint () 0474 << ",normalizedRect=" << normalizedRect << ")"; 0475 #else 0476 Q_UNUSED (thisPoint); 0477 Q_UNUSED (normalizedRect); 0478 #endif 0479 0480 0481 // OPT: return when thisPoint == lastPoint () so that e.g. when creating 0482 // Points sel, press modifiers doesn't add multiple points in same 0483 // place 0484 0485 0486 operation (d->drawType, Draw); 0487 } 0488 0489 //--------------------------------------------------------------------- 0490 0491 0492 // public virtual [base kpTool] 0493 void kpAbstractSelectionTool::cancelShape () 0494 { 0495 #if DEBUG_KP_TOOL_SELECTION 0496 qCDebug(kpLogTools) << "kpAbstractSelectionTool::cancelShape() mouseButton=" 0497 << mouseButton (); 0498 #endif 0499 0500 const DrawType oldDrawType = d->drawType; 0501 // kpTool::hasBegunDraw() returns false in this method so be consistent 0502 // and clear "drawType" before dispatching the operation() below. 0503 d->drawType = None; 0504 0505 0506 viewManager ()->setQueueUpdates (); 0507 { 0508 operation (oldDrawType, Cancel); 0509 0510 0511 if (d->currentSelContentCommand) 0512 { 0513 #if DEBUG_KP_TOOL_SELECTION 0514 qCDebug(kpLogTools) << "\t\tundo sel content"; 0515 #endif 0516 d->currentSelContentCommand->unexecute (); 0517 delete d->currentSelContentCommand; 0518 d->currentSelContentCommand = nullptr; 0519 } 0520 0521 0522 /*virtual*/setSelectionBorderForHaventBegunDraw (); 0523 } 0524 viewManager ()->restoreQueueUpdates (); 0525 0526 0527 d->cancelledShapeButStillHoldingButtons = true; 0528 setUserMessage (i18n ("Let go of all the mouse buttons.")); 0529 0530 0531 ::AssertAllTimersInactive (d); 0532 } 0533 0534 //--------------------------------------------------------------------- 0535 0536 // public virtual [base kpTool] 0537 void kpAbstractSelectionTool::releasedAllButtons () 0538 { 0539 d->cancelledShapeButStillHoldingButtons = false; 0540 setUserMessage (haventBegunDrawUserMessage ()); 0541 } 0542 0543 //--------------------------------------------------------------------- 0544 0545 // protected 0546 void kpAbstractSelectionTool::popupRMBMenu () 0547 { 0548 #if DEBUG_KP_TOOL_SELECTION 0549 qCDebug(kpLogTools) << "CALL - exec'ing menu"; 0550 #endif 0551 0552 QMenu *pop = environ ()->selectionToolRMBMenu (); 0553 Q_ASSERT (pop); 0554 0555 // Blocks until the menu closes. 0556 // WARNING: Enters event loop - may re-enter view/tool event handlers. 0557 pop->exec (QCursor::pos ()); 0558 #if DEBUG_KP_TOOL_SELECTION 0559 qCDebug(kpLogTools) << "calling somethingBelowTheCursorChanged()"; 0560 #endif 0561 0562 // Cursor may have moved while the menu was up, triggering QMouseMoveEvents 0563 // for the menu -- but not the view -- so we may have missed cursor moves. 0564 // Update cursor position now. 0565 somethingBelowTheCursorChanged (); 0566 #if DEBUG_KP_TOOL_SELECTION 0567 qCDebug(kpLogTools) << "DONE"; 0568 #endif 0569 } 0570 0571 //--------------------------------------------------------------------- 0572 0573 // public virtual [base kpTool] 0574 void kpAbstractSelectionTool::endDraw (const QPoint & /*thisPoint*/, 0575 const QRect & /*normalizedRect*/) 0576 { 0577 #if DEBUG_KP_TOOL_SELECTION 0578 qCDebug(kpLogTools) << "kpAbstractSelectionTool::endDraw()"; 0579 #endif 0580 0581 const DrawType oldDrawType = d->drawType; 0582 // kpTool::hasBegunDraw() returns false in this method so be consistent 0583 // and clear "drawType" before dispatching the operation() below. 0584 d->drawType = None; 0585 0586 0587 viewManager ()->setQueueUpdates (); 0588 { 0589 operation (oldDrawType, EndDraw); 0590 0591 /*virtual*/setSelectionBorderForHaventBegunDraw (); 0592 } 0593 viewManager ()->restoreQueueUpdates (); 0594 0595 0596 setUserMessage (haventBegunDrawUserMessage ()); 0597 0598 0599 ::AssertAllTimersInactive (d); 0600 0601 0602 if (mouseButton () == 1/*right*/) { 0603 popupRMBMenu (); 0604 } 0605 0606 0607 // WARNING: Do not place any code after the popupRMBMenu() call 0608 // (see the popupRMBMenu() API). 0609 } 0610 0611 //--------------------------------------------------------------------- 0612 0613 // protected virtual 0614 QVariant kpAbstractSelectionTool::operation (DrawType drawType, Operation op, 0615 const QVariant &data1, const QVariant &data2) 0616 { 0617 switch (drawType) 0618 { 0619 case None: 0620 // NOP. 0621 return {}; 0622 0623 case Create: 0624 return operationCreate (op, data1, data2); 0625 0626 case Move: 0627 return operationMove (op, data1, data2); 0628 0629 case ResizeScale: 0630 return operationResizeScale (op, data1, data2); 0631 0632 default: 0633 Q_ASSERT (!"Unhandled draw type"); 0634 return {}; 0635 } 0636 } 0637 0638 //--------------------------------------------------------------------- 0639 0640 #include "moc_kpAbstractSelectionTool.cpp"