File indexing completed on 2024-05-12 04:21:25

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_FLOW_BASE 0
0030 
0031 #include "kpToolFlowBase.h"
0032 
0033 #include <cstdlib>
0034 
0035 
0036 #include "kpLogCategories.h"
0037 #include <KLocalizedString>
0038 
0039 #include "imagelib/kpColor.h"
0040 #include "commands/kpCommandHistory.h"
0041 #include "cursors/kpCursorProvider.h"
0042 #include "kpDefs.h"
0043 #include "document/kpDocument.h"
0044 #include "imagelib/kpImage.h"
0045 #include "imagelib/kpPainter.h"
0046 #include "pixmapfx/kpPixmapFX.h"
0047 #include "environments/tools/kpToolEnvironment.h"
0048 #include "commands/tools/flow/kpToolFlowCommand.h"
0049 #include "widgets/toolbars/kpToolToolBar.h"
0050 #include "widgets/toolbars/options/kpToolWidgetBrush.h"
0051 #include "widgets/toolbars/options/kpToolWidgetEraserSize.h"
0052 #include "views/manager/kpViewManager.h"
0053 
0054 //---------------------------------------------------------------------
0055 
0056 struct kpToolFlowBasePrivate
0057 {
0058     kpToolWidgetBrush *toolWidgetBrush{};
0059     kpToolWidgetEraserSize *toolWidgetEraserSize{};
0060 
0061 
0062     //
0063     // Cursor and Brush Data
0064     // (must be zero if unused)
0065     //
0066 
0067         kpTempImage::UserFunctionType brushDrawFunc{}, cursorDrawFunc{};
0068 
0069         // Can't use union since package types contain fields requiring
0070         // constructors.
0071         kpToolWidgetBrush::DrawPackage brushDrawPackageForMouseButton [2];
0072         kpToolWidgetEraserSize::DrawPackage eraserDrawPackageForMouseButton [2];
0073 
0074         // Each element points to one of the above (both elements from the same
0075         // array).
0076         void *drawPackageForMouseButton [2]{};
0077 
0078         int brushWidth{}, brushHeight{};
0079         int cursorWidth{}, cursorHeight{};
0080 
0081         bool brushIsDiagonalLine{};
0082 
0083 
0084     kpToolFlowCommand *currentCommand{};
0085 };
0086 
0087 //---------------------------------------------------------------------
0088 
0089 kpToolFlowBase::kpToolFlowBase (const QString &text, const QString &description,
0090         int key,
0091         kpToolEnvironment *environ, QObject *parent, const QString &name)
0092 
0093     : kpTool (text, description, key, environ, parent, name),
0094       d (new kpToolFlowBasePrivate ())
0095 {
0096     d->toolWidgetBrush = nullptr;
0097     d->toolWidgetEraserSize = nullptr;
0098 
0099     clearBrushCursorData ();
0100 
0101     d->currentCommand = nullptr;
0102 }
0103 
0104 //---------------------------------------------------------------------
0105 
0106 kpToolFlowBase::~kpToolFlowBase ()
0107 {
0108     delete d;
0109 }
0110 
0111 //---------------------------------------------------------------------
0112 
0113 // private
0114 void kpToolFlowBase::clearBrushCursorData ()
0115 {
0116     d->brushDrawFunc = d->cursorDrawFunc = nullptr;
0117 
0118     memset (&d->brushDrawPackageForMouseButton,
0119         0,
0120         sizeof (d->brushDrawPackageForMouseButton));
0121     memset (&d->eraserDrawPackageForMouseButton,
0122         0,
0123         sizeof (d->eraserDrawPackageForMouseButton));
0124 
0125     memset (&d->drawPackageForMouseButton,
0126         0,
0127         sizeof (d->drawPackageForMouseButton));
0128 
0129     d->brushWidth = d->brushHeight = 0;
0130     d->cursorWidth = d->cursorHeight = 0;
0131 
0132     d->brushIsDiagonalLine = false;
0133 }
0134 
0135 //---------------------------------------------------------------------
0136 
0137 // virtual
0138 void kpToolFlowBase::begin ()
0139 {
0140     kpToolToolBar *tb = toolToolBar ();
0141     Q_ASSERT (tb);
0142 
0143     // TODO: Bad smell.  Mutually exclusive.  Use inheritance.
0144     if (haveSquareBrushes ())
0145     {
0146         d->toolWidgetEraserSize = tb->toolWidgetEraserSize ();
0147         connect (d->toolWidgetEraserSize, &kpToolWidgetEraserSize::eraserSizeChanged,
0148                  this, &kpToolFlowBase::updateBrushAndCursor);
0149         d->toolWidgetEraserSize->show ();
0150 
0151         updateBrushAndCursor ();
0152 
0153         viewManager ()->setCursor (kpCursorProvider::lightCross ());
0154     }
0155     else if (haveDiverseBrushes ())
0156     {
0157         d->toolWidgetBrush = tb->toolWidgetBrush ();
0158         connect (d->toolWidgetBrush, &kpToolWidgetBrush::brushChanged,
0159                  this, &kpToolFlowBase::updateBrushAndCursor);
0160         d->toolWidgetBrush->show ();
0161 
0162         updateBrushAndCursor ();
0163 
0164         viewManager ()->setCursor (kpCursorProvider::lightCross ());
0165     }
0166 
0167     setUserMessage (haventBegunDrawUserMessage ());
0168 }
0169 
0170 //---------------------------------------------------------------------
0171 
0172 // virtual
0173 void kpToolFlowBase::end ()
0174 {
0175     if (d->toolWidgetEraserSize)
0176     {
0177         disconnect (d->toolWidgetEraserSize, &kpToolWidgetEraserSize::eraserSizeChanged,
0178                     this, &kpToolFlowBase::updateBrushAndCursor);
0179         d->toolWidgetEraserSize = nullptr;
0180     }
0181     else if (d->toolWidgetBrush)
0182     {
0183         disconnect (d->toolWidgetBrush, &kpToolWidgetBrush::brushChanged,
0184                     this, &kpToolFlowBase::updateBrushAndCursor);
0185         d->toolWidgetBrush = nullptr;
0186     }
0187 
0188     kpViewManager *vm = viewManager ();
0189     Q_ASSERT (vm);
0190 
0191     if (vm->tempImage () && vm->tempImage ()->isBrush ()) {
0192         vm->invalidateTempImage ();
0193     }
0194 
0195     if (haveAnyBrushes ()) {
0196         vm->unsetCursor ();
0197     }
0198 
0199     clearBrushCursorData ();
0200 }
0201 
0202 //---------------------------------------------------------------------
0203 
0204 // virtual
0205 void kpToolFlowBase::beginDraw ()
0206 {
0207     d->currentCommand = new kpToolFlowCommand (text (), environ ()->commandEnvironment ());
0208 
0209     // We normally show the brush cursor in the foreground colour but if the
0210     // user starts drawing in the background color, we don't want to leave
0211     // the brush cursor in the foreground colour -- just hide it in all cases
0212     // to avoid confusion.
0213     viewManager ()->invalidateTempImage ();
0214 
0215     setUserMessage (cancelUserMessage ());
0216 }
0217 
0218 //---------------------------------------------------------------------
0219 
0220 // virtual
0221 void kpToolFlowBase::hover (const QPoint &point)
0222 {
0223 #if DEBUG_KP_TOOL_FLOW_BASE && 0
0224     qCDebug(kpLogTools) << "kpToolFlowBase::hover(" << point << ")"
0225                << " hasBegun=" << hasBegun ()
0226                << " hasBegunDraw=" << hasBegunDraw ()
0227                << " cursorPixmap.isNull=" << m_cursorPixmap.isNull ()
0228                << endl;
0229 #endif
0230     if (point != KP_INVALID_POINT && d->cursorDrawFunc)
0231     {
0232         viewManager ()->setFastUpdates ();
0233 
0234         viewManager ()->setTempImage (
0235             kpTempImage (true/*brush*/,
0236                 hotRect ().topLeft (),
0237                 d->cursorDrawFunc, d->drawPackageForMouseButton [0/*left button*/],
0238                 d->cursorWidth, d->cursorHeight));
0239 
0240         viewManager ()->restoreFastUpdates ();
0241     }
0242 
0243     setUserShapePoints (point);
0244 }
0245 
0246 //---------------------------------------------------------------------
0247 
0248 // virtual
0249 QRect kpToolFlowBase::drawPoint (const QPoint &point)
0250 {
0251     return drawLine (point, point);
0252 }
0253 
0254 //---------------------------------------------------------------------
0255 
0256 // virtual
0257 void kpToolFlowBase::draw (const QPoint &thisPoint, const QPoint &lastPoint, const QRect &normalizedRect)
0258 {
0259     if (!/*virtual*/drawShouldProceed (thisPoint, lastPoint, normalizedRect)) {
0260         return;
0261     }
0262 
0263     // sync: remember to restoreFastUpdates() in all exit paths
0264     viewManager ()->setFastUpdates ();
0265 
0266     QRect dirtyRect;
0267 
0268     // TODO: I'm beginning to wonder this drawPoint() "optimization" actually
0269     //       optimises.  Is it worth the complexity?  Hence drawPoint() impl above.
0270     if (d->brushIsDiagonalLine ?
0271             currentPointCardinallyNextToLast () :
0272             currentPointNextToLast ())
0273     {
0274         dirtyRect = drawPoint (thisPoint);
0275     }
0276     // in reality, the system is too slow to give us all the MouseMove events
0277     // so we "interpolate" the missing points :)
0278     else
0279     {
0280         dirtyRect = drawLine (thisPoint, lastPoint);
0281     }
0282 
0283     d->currentCommand->updateBoundingRect (dirtyRect);
0284 
0285     viewManager ()->restoreFastUpdates ();
0286     setUserShapePoints (thisPoint);
0287 }
0288 
0289 //---------------------------------------------------------------------
0290 
0291 // virtual
0292 void kpToolFlowBase::cancelShape ()
0293 {
0294     d->currentCommand->finalize ();
0295     d->currentCommand->cancel ();
0296 
0297     delete d->currentCommand;
0298     d->currentCommand = nullptr;
0299 
0300     updateBrushAndCursor ();
0301 
0302     setUserMessage (i18n ("Let go of all the mouse buttons."));
0303 }
0304 
0305 //---------------------------------------------------------------------
0306 
0307 void kpToolFlowBase::releasedAllButtons ()
0308 {
0309     setUserMessage (haventBegunDrawUserMessage ());
0310 }
0311 
0312 //---------------------------------------------------------------------
0313 
0314 // virtual
0315 void kpToolFlowBase::endDraw (const QPoint &, const QRect &)
0316 {
0317     d->currentCommand->finalize ();
0318     environ ()->commandHistory ()->addCommand (d->currentCommand,
0319         false/*don't exec*/);
0320 
0321     // don't delete - it's up to the commandHistory
0322     d->currentCommand = nullptr;
0323 
0324     updateBrushAndCursor ();
0325 
0326     setUserMessage (haventBegunDrawUserMessage ());
0327 }
0328 
0329 //---------------------------------------------------------------------
0330 
0331 // TODO: maybe the base should be virtual?
0332 kpColor kpToolFlowBase::color (int which)
0333 {
0334 #if DEBUG_KP_TOOL_FLOW_BASE && 0
0335     qCDebug(kpLogTools) << "kpToolFlowBase::color (" << which << ")";
0336 #endif
0337 
0338     // Pen & Brush
0339     if (!colorsAreSwapped ()) {
0340         return kpTool::color (which);
0341     }
0342     // only the (Color) Eraser uses the opposite color
0343     return kpTool::color (which ? 0 : 1);  // don't trust !0 == 1
0344 }
0345 
0346 //---------------------------------------------------------------------
0347 
0348 // protected
0349 kpTempImage::UserFunctionType kpToolFlowBase::brushDrawFunction () const
0350 {
0351     return d->brushDrawFunc;
0352 }
0353 
0354 //---------------------------------------------------------------------
0355 
0356 // protected
0357 void *kpToolFlowBase::brushDrawFunctionData () const
0358 {
0359     return d->drawPackageForMouseButton [mouseButton ()];
0360 }
0361 
0362 
0363 // protected
0364 int kpToolFlowBase::brushWidth () const
0365 {
0366     return d->brushWidth;
0367 }
0368 
0369 // protected
0370 int kpToolFlowBase::brushHeight () const
0371 {
0372     return d->brushHeight;
0373 }
0374 
0375 // protected
0376 bool kpToolFlowBase::brushIsDiagonalLine () const
0377 {
0378     return d->brushIsDiagonalLine;
0379 }
0380 
0381 
0382 // protected
0383 kpToolFlowCommand *kpToolFlowBase::currentCommand () const
0384 {
0385     return d->currentCommand;
0386 }
0387 
0388 //---------------------------------------------------------------------
0389 
0390 // protected slot
0391 void kpToolFlowBase::updateBrushAndCursor ()
0392 {
0393 #if DEBUG_KP_TOOL_FLOW_BASE && 1
0394     qCDebug(kpLogTools) << "kpToolFlowBase::updateBrushAndCursor()";
0395 #endif
0396 
0397     if (haveSquareBrushes ())
0398     {
0399         d->brushDrawFunc = d->toolWidgetEraserSize->drawFunction ();
0400         d->cursorDrawFunc = d->toolWidgetEraserSize->drawCursorFunction ();
0401 
0402         for (int i = 0; i < 2; i++)
0403         {
0404             d->drawPackageForMouseButton [i] =
0405                 &(d->eraserDrawPackageForMouseButton [i] =
0406                     d->toolWidgetEraserSize->drawFunctionData (color (i)));
0407         }
0408 
0409         d->brushWidth = d->brushHeight =
0410             d->cursorWidth = d->cursorHeight =
0411                 d->toolWidgetEraserSize->eraserSize ();
0412 
0413         d->brushIsDiagonalLine = false;
0414     }
0415     else if (haveDiverseBrushes ())
0416     {
0417         d->brushDrawFunc = d->cursorDrawFunc = d->toolWidgetBrush->drawFunction ();
0418 
0419         for (int i = 0; i < 2; i++)
0420         {
0421             d->drawPackageForMouseButton [i] =
0422                 &(d->brushDrawPackageForMouseButton [i] =
0423                     d->toolWidgetBrush->drawFunctionData (color (i)));
0424         }
0425 
0426         d->brushWidth = d->brushHeight =
0427             d->cursorWidth = d->cursorHeight =
0428                 d->toolWidgetBrush->brushSize ();
0429 
0430         d->brushIsDiagonalLine = d->toolWidgetBrush->brushIsDiagonalLine ();
0431     }
0432 
0433     hover (hasBegun () ? currentPoint () : calculateCurrentPoint ());
0434 }
0435 
0436 //---------------------------------------------------------------------
0437 
0438 // virtual private slot
0439 void kpToolFlowBase::slotForegroundColorChanged (const kpColor & /*col*/)
0440 {
0441 #if DEBUG_KP_TOOL_FLOW_BASE
0442     qCDebug(kpLogTools) << "kpToolFlowBase::slotForegroundColorChanged()";
0443 #endif
0444 
0445     updateBrushAndCursor ();
0446 }
0447 
0448 //---------------------------------------------------------------------
0449 
0450 // virtual private slot
0451 void kpToolFlowBase::slotBackgroundColorChanged (const kpColor & /*col*/)
0452 {
0453 #if DEBUG_KP_TOOL_FLOW_BASE
0454     qCDebug(kpLogTools) << "kpToolFlowBase::slotBackgroundColorChanged()";
0455 #endif
0456 
0457     updateBrushAndCursor ();
0458 }
0459 
0460 //---------------------------------------------------------------------
0461 
0462 // public static
0463 QRect kpToolFlowBase::hotRectForMousePointAndBrushWidthHeight (
0464         const QPoint &mousePoint,
0465         int brushWidth, int brushHeight)
0466 {
0467     /*
0468      * e.g.
0469      *    Width 5:
0470      *    0 1 2 3 4
0471      *        ^
0472      *        |
0473      *      Center
0474      */
0475     return  {mousePoint.x () - brushWidth / 2,
0476                 mousePoint.y () - brushHeight / 2,
0477                 brushWidth, brushHeight};
0478 }
0479 
0480 //---------------------------------------------------------------------
0481 
0482 // protected
0483 QRect kpToolFlowBase::hotRect () const
0484 {
0485     return hotRectForMousePointAndBrushWidthHeight (currentPoint (),
0486         d->brushWidth, d->brushHeight);
0487 }
0488 
0489 //---------------------------------------------------------------------
0490 
0491 #include "moc_kpToolFlowBase.cpp"