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"