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"