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

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_RECTANGULAR_BASE 0
0030 
0031 
0032 #include "tools/rectangular/kpToolRectangularBase.h"
0033 
0034 #include <QCursor>
0035 
0036 #include "kpLogCategories.h"
0037 #include <KLocalizedString>
0038 
0039 #include "imagelib/kpColor.h"
0040 #include "commands/kpCommandHistory.h"
0041 #include "kpDefs.h"
0042 #include "document/kpDocument.h"
0043 #include "imagelib/kpPainter.h"
0044 #include "pixmapfx/kpPixmapFX.h"
0045 #include "layers/tempImage/kpTempImage.h"
0046 #include "environments/tools/kpToolEnvironment.h"
0047 #include "commands/tools/rectangular/kpToolRectangularCommand.h"
0048 #include "widgets/toolbars/kpToolToolBar.h"
0049 #include "widgets/toolbars/options/kpToolWidgetFillStyle.h"
0050 #include "widgets/toolbars/options/kpToolWidgetLineWidth.h"
0051 #include "views/kpView.h"
0052 #include "views/manager/kpViewManager.h"
0053 
0054 
0055 //---------------------------------------------------------------------
0056 
0057 struct kpToolRectangularBasePrivate
0058 {
0059     kpToolRectangularBase::DrawShapeFunc drawShapeFunc{};
0060 
0061     kpToolWidgetLineWidth *toolWidgetLineWidth{};
0062     kpToolWidgetFillStyle *toolWidgetFillStyle{};
0063 
0064     QRect toolRectangleRect;
0065 };
0066 
0067 //---------------------------------------------------------------------
0068 
0069 kpToolRectangularBase::kpToolRectangularBase (
0070         const QString &text,
0071         const QString &description,
0072         DrawShapeFunc drawShapeFunc,
0073         int key,
0074         kpToolEnvironment *environ, QObject *parent,
0075         const QString &name)
0076 
0077     : kpTool (text, description, key, environ, parent, name),
0078       d (new kpToolRectangularBasePrivate ())
0079 {
0080     d->drawShapeFunc = drawShapeFunc;
0081 
0082     d->toolWidgetLineWidth = nullptr;
0083     d->toolWidgetFillStyle = nullptr;
0084 }
0085 
0086 //---------------------------------------------------------------------
0087 
0088 kpToolRectangularBase::~kpToolRectangularBase ()
0089 {
0090     delete d;
0091 }
0092 
0093 //---------------------------------------------------------------------
0094 
0095 
0096 // private slot virtual
0097 void kpToolRectangularBase::slotLineWidthChanged ()
0098 {
0099     if (hasBegunDraw ()) {
0100         updateShape ();
0101     }
0102 }
0103 
0104 //---------------------------------------------------------------------
0105 
0106 // private slot virtual
0107 void kpToolRectangularBase::slotFillStyleChanged ()
0108 {
0109     if (hasBegunDraw ()) {
0110         updateShape ();
0111     }
0112 }
0113 
0114 //---------------------------------------------------------------------
0115 
0116 // private
0117 QString kpToolRectangularBase::haventBegunDrawUserMessage () const
0118 {
0119     return i18n ("Drag to draw.");
0120 }
0121 
0122 //---------------------------------------------------------------------
0123 
0124 // virtual
0125 void kpToolRectangularBase::begin ()
0126 {
0127 #if DEBUG_KP_TOOL_RECTANGULAR_BASE
0128     qCDebug(kpLogTools) << "kpToolRectangularBase::begin ()";
0129 #endif
0130 
0131     kpToolToolBar *tb = toolToolBar ();
0132     Q_ASSERT (tb);
0133 
0134 #if DEBUG_KP_TOOL_RECTANGULAR_BASE
0135     qCDebug(kpLogTools) << "\ttoolToolBar=" << tb;
0136 #endif
0137 
0138     d->toolWidgetLineWidth = tb->toolWidgetLineWidth ();
0139     connect (d->toolWidgetLineWidth, &kpToolWidgetLineWidth::lineWidthChanged,
0140              this, &kpToolRectangularBase::slotLineWidthChanged);
0141     d->toolWidgetLineWidth->show ();
0142 
0143     d->toolWidgetFillStyle = tb->toolWidgetFillStyle ();
0144     connect (d->toolWidgetFillStyle, &kpToolWidgetFillStyle::fillStyleChanged,
0145              this, &kpToolRectangularBase::slotFillStyleChanged);
0146     d->toolWidgetFillStyle->show ();
0147 
0148     viewManager ()->setCursor (QCursor (Qt::ArrowCursor));
0149 
0150     setUserMessage (haventBegunDrawUserMessage ());
0151 }
0152 
0153 //---------------------------------------------------------------------
0154 
0155 // virtual
0156 void kpToolRectangularBase::end ()
0157 {
0158 #if DEBUG_KP_TOOL_RECTANGULAR_BASE
0159     qCDebug(kpLogTools) << "kpToolRectangularBase::end ()";
0160 #endif
0161 
0162     if (d->toolWidgetLineWidth)
0163     {
0164         disconnect (d->toolWidgetLineWidth, &kpToolWidgetLineWidth::lineWidthChanged,
0165                  this, &kpToolRectangularBase::slotLineWidthChanged);
0166         d->toolWidgetLineWidth = nullptr;
0167     }
0168 
0169     if (d->toolWidgetFillStyle)
0170     {
0171         disconnect (d->toolWidgetFillStyle, &kpToolWidgetFillStyle::fillStyleChanged,
0172                  this, &kpToolRectangularBase::slotFillStyleChanged);
0173         d->toolWidgetFillStyle = nullptr;
0174     }
0175 
0176     viewManager ()->unsetCursor ();
0177 }
0178 
0179 //---------------------------------------------------------------------
0180 
0181 void kpToolRectangularBase::applyModifiers ()
0182 {
0183     QRect rect = normalizedRect ();
0184 
0185 #if DEBUG_KP_TOOL_RECTANGULAR_BASE
0186     qCDebug(kpLogTools) << "kpToolRectangularBase::applyModifiers(" << rect
0187                << ") shift=" << shiftPressed ()
0188                << " ctrl=" << controlPressed ()
0189                << endl;
0190 #endif
0191 
0192     // user wants to startPoint () == center
0193     if (controlPressed ())
0194     {
0195         int xdiff = qAbs (startPoint ().x () - currentPoint ().x ());
0196         int ydiff = qAbs (startPoint ().y () - currentPoint ().y ());
0197         rect = QRect (startPoint ().x () - xdiff, startPoint ().y () - ydiff,
0198                       xdiff * 2 + 1, ydiff * 2 + 1);
0199     }
0200 
0201     // user wants major axis == minor axis:
0202     //   rectangle --> square
0203     //   rounded rectangle --> rounded square
0204     //   ellipse --> circle
0205     if (shiftPressed ())
0206     {
0207         if (!controlPressed ())
0208         {
0209             if (rect.width () < rect.height ())
0210             {
0211                 if (startPoint ().y () == rect.y ()) {
0212                     rect.setHeight (rect.width ());
0213                 }
0214                 else {
0215                     rect.setY (rect.bottom () - rect.width () + 1);
0216                 }
0217             }
0218             else
0219             {
0220                 if (startPoint ().x () == rect.x ()) {
0221                     rect.setWidth (rect.height ());
0222                 }
0223                 else {
0224                     rect.setX (rect.right () - rect.height () + 1);
0225                 }
0226             }
0227         }
0228         // have to maintain the center
0229         else
0230         {
0231             if (rect.width () < rect.height ())
0232             {
0233                 QPoint center = rect.center ();
0234                 rect.setHeight (rect.width ());
0235                 rect.moveCenter (center);
0236             }
0237             else
0238             {
0239                 QPoint center = rect.center ();
0240                 rect.setWidth (rect.height ());
0241                 rect.moveCenter (center);
0242             }
0243         }
0244     }
0245 
0246     d->toolRectangleRect = rect;
0247 }
0248 
0249 //---------------------------------------------------------------------
0250 
0251 void kpToolRectangularBase::beginDraw ()
0252 {
0253     setUserMessage (cancelUserMessage ());
0254 }
0255 
0256 //---------------------------------------------------------------------
0257 
0258 
0259 // private
0260 kpColor kpToolRectangularBase::drawingForegroundColor () const
0261 {
0262     return color (mouseButton ());
0263 }
0264 
0265 //---------------------------------------------------------------------
0266 
0267 // private
0268 kpColor kpToolRectangularBase::drawingBackgroundColor () const
0269 {
0270     const kpColor foregroundColor = color (mouseButton ());
0271     const kpColor backgroundColor = color (1 - mouseButton ());
0272 
0273     return d->toolWidgetFillStyle->drawingBackgroundColor (
0274         foregroundColor, backgroundColor);
0275 }
0276 
0277 //---------------------------------------------------------------------
0278 
0279 // private
0280 void kpToolRectangularBase::updateShape ()
0281 {
0282     kpImage image = document ()->getImageAt (d->toolRectangleRect);
0283 
0284     // Invoke shape drawing function passed in ctor.
0285     (*d->drawShapeFunc) (&image,
0286         0, 0, d->toolRectangleRect.width (), d->toolRectangleRect.height (),
0287         drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (),
0288         drawingBackgroundColor ());
0289 
0290     kpTempImage newTempImage (false/*always display*/,
0291                                 kpTempImage::SetImage/*render mode*/,
0292                                 d->toolRectangleRect.topLeft (),
0293                                 image);
0294 
0295     viewManager ()->setFastUpdates ();
0296     viewManager ()->setTempImage (newTempImage);
0297     viewManager ()->restoreFastUpdates ();
0298 }
0299 
0300 //---------------------------------------------------------------------
0301 
0302 void kpToolRectangularBase::draw (const QPoint &, const QPoint &, const QRect &)
0303 {
0304     applyModifiers ();
0305 
0306 
0307     updateShape ();
0308 
0309 
0310     // Recover the start and end points from the transformed & normalized d->toolRectangleRect
0311 
0312     // S. or S or SC or S == C
0313     // .C    C
0314     if (currentPoint ().x () >= startPoint ().x () &&
0315         currentPoint ().y () >= startPoint ().y ())
0316     {
0317         setUserShapePoints (d->toolRectangleRect.topLeft (),
0318                             d->toolRectangleRect.bottomRight ());
0319     }
0320     // .C or C
0321     // S.    S
0322     else if (currentPoint ().x () >= startPoint ().x () &&
0323              currentPoint ().y () < startPoint ().y ())
0324     {
0325         setUserShapePoints (d->toolRectangleRect.bottomLeft (),
0326                             d->toolRectangleRect.topRight ());
0327     }
0328     // .S or CS
0329     // C.
0330     else if (currentPoint ().x () < startPoint ().x () &&
0331              currentPoint ().y () >= startPoint ().y ())
0332     {
0333         setUserShapePoints (d->toolRectangleRect.topRight (),
0334                             d->toolRectangleRect.bottomLeft ());
0335     }
0336     // C.
0337     // .S
0338     else
0339     {
0340         setUserShapePoints (d->toolRectangleRect.bottomRight (),
0341                             d->toolRectangleRect.topLeft ());
0342     }
0343 }
0344 
0345 //---------------------------------------------------------------------
0346 
0347 void kpToolRectangularBase::cancelShape ()
0348 {
0349     viewManager ()->invalidateTempImage ();
0350 
0351     setUserMessage (i18n ("Let go of all the mouse buttons."));
0352 }
0353 
0354 //---------------------------------------------------------------------
0355 
0356 void kpToolRectangularBase::releasedAllButtons ()
0357 {
0358     setUserMessage (haventBegunDrawUserMessage ());
0359 }
0360 
0361 //---------------------------------------------------------------------
0362 
0363 void kpToolRectangularBase::endDraw (const QPoint &, const QRect &)
0364 {
0365     applyModifiers ();
0366 
0367     // TODO: flicker
0368     // Later: So why can't we use kpViewManager::setQueueUpdates()?  Check SVN
0369     //        log to see if this method was not available at the time of the
0370     //        TODO, hence justifying the TODO.
0371     // Later2: kpToolPolygonalBase, and perhaps, other shapes will have the
0372     //         same problem.
0373     viewManager ()->invalidateTempImage ();
0374 
0375     environ ()->commandHistory ()->addCommand (
0376         new kpToolRectangularCommand (
0377             text (),
0378             d->drawShapeFunc, d->toolRectangleRect,
0379             drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (),
0380             drawingBackgroundColor (),
0381             environ ()->commandEnvironment ()));
0382 
0383     setUserMessage (haventBegunDrawUserMessage ());
0384 }
0385 
0386 //---------------------------------------------------------------------
0387 
0388 #include "moc_kpToolRectangularBase.cpp"