diff --git a/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp b/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp index 37bba0f4..7864b541 100644 --- a/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp +++ b/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp @@ -1,68 +1,66 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_BLUR_SHARPEN 1 - #include "kpEffectBlurSharpenCommand.h" #include //-------------------------------------------------------------------------------- kpEffectBlurSharpenCommand::kpEffectBlurSharpenCommand (kpEffectBlurSharpen::Type type, int strength, bool actOnSelection, kpCommandEnvironment *environ) : kpEffectCommandBase (kpEffectBlurSharpenCommand::nameForType (type), actOnSelection, environ), m_type (type), m_strength (strength) { } kpEffectBlurSharpenCommand::~kpEffectBlurSharpenCommand () = default; // public static QString kpEffectBlurSharpenCommand::nameForType (kpEffectBlurSharpen::Type type) { switch (type) { case kpEffectBlurSharpen::Blur: return i18n ("Soften"); case kpEffectBlurSharpen::Sharpen: return i18n ("Sharpen"); default: return {}; } } // protected virtual [base kpEffectCommandBase] kpImage kpEffectBlurSharpenCommand::applyEffect (const kpImage &image) { return kpEffectBlurSharpen::applyEffect (image, m_type, m_strength); } diff --git a/commands/imagelib/effects/kpEffectEmbossCommand.cpp b/commands/imagelib/effects/kpEffectEmbossCommand.cpp index 1d8aa8c9..8eebb6ad 100644 --- a/commands/imagelib/effects/kpEffectEmbossCommand.cpp +++ b/commands/imagelib/effects/kpEffectEmbossCommand.cpp @@ -1,55 +1,53 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_EMBOSS 1 - #include "kpEffectEmbossCommand.h" #include "imagelib/effects/kpEffectEmboss.h" #include "kpLogCategories.h" #include kpEffectEmbossCommand::kpEffectEmbossCommand (int strength, bool actOnSelection, kpCommandEnvironment *environ) : kpEffectCommandBase (i18n ("Emboss"), actOnSelection, environ), m_strength (strength) { } kpEffectEmbossCommand::~kpEffectEmbossCommand () = default; // protected virtual [base kpEffectCommandBase] kpImage kpEffectEmbossCommand::applyEffect (const kpImage &image) { return kpEffectEmboss::applyEffect (image, m_strength); } diff --git a/commands/imagelib/effects/kpEffectFlattenCommand.cpp b/commands/imagelib/effects/kpEffectFlattenCommand.cpp index aa3113ef..b50ace98 100644 --- a/commands/imagelib/effects/kpEffectFlattenCommand.cpp +++ b/commands/imagelib/effects/kpEffectFlattenCommand.cpp @@ -1,61 +1,59 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_FLATTEN 1 - #include "kpEffectFlattenCommand.h" #include "imagelib/effects/kpEffectFlatten.h" #include //-------------------------------------------------------------------------------- kpEffectFlattenCommand::kpEffectFlattenCommand (const QColor &color1, const QColor &color2, bool actOnSelection, kpCommandEnvironment *environ) : kpEffectCommandBase (i18n ("Flatten"), actOnSelection, environ), m_color1 (color1), m_color2 (color2) { } kpEffectFlattenCommand::~kpEffectFlattenCommand () = default; // // kpEffectFlattenCommand implements kpEffectCommandBase interface // // protected virtual [base kpEffectCommandBase] kpImage kpEffectFlattenCommand::applyEffect (const kpImage &image) { return kpEffectFlatten::applyEffect (image, m_color1, m_color2); } diff --git a/commands/imagelib/effects/kpEffectReduceColorsCommand.cpp b/commands/imagelib/effects/kpEffectReduceColorsCommand.cpp index 32f5012c..25c9f492 100644 --- a/commands/imagelib/effects/kpEffectReduceColorsCommand.cpp +++ b/commands/imagelib/effects/kpEffectReduceColorsCommand.cpp @@ -1,80 +1,79 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_REDUCE_COLORS 1 #include "kpEffectReduceColorsCommand.h" #include "imagelib/effects/kpEffectReduceColors.h" #include //--------------------------------------------------------------------- kpEffectReduceColorsCommand::kpEffectReduceColorsCommand (int depth, bool dither, bool actOnSelection, kpCommandEnvironment *environ) : kpEffectCommandBase (commandName (depth, dither), actOnSelection, environ), m_depth (depth), m_dither (dither) { } //--------------------------------------------------------------------- // public QString kpEffectReduceColorsCommand::commandName (int depth, int dither) const { switch (depth) { case 1: if (dither) { return i18n ("Reduce to Monochrome (Dithered)"); } return i18n ("Reduce to Monochrome"); case 8: if (dither) { return i18n ("Reduce to 256 Color (Dithered)"); } return i18n ("Reduce to 256 Color"); default: return {}; } } //--------------------------------------------------------------------- // // kpEffectReduceColorsCommand implements kpEffectCommandBase interface // // protected virtual [base kpEffectCommandBase] kpImage kpEffectReduceColorsCommand::applyEffect (const kpImage &image) { return kpEffectReduceColors::applyEffect (image, m_depth, m_dither); } //--------------------------------------------------------------------- diff --git a/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp b/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp index 2df9a55e..36568640 100644 --- a/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp +++ b/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp @@ -1,486 +1,477 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND 1 -#define DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG 1 - #include "kpTransformResizeScaleCommand.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/image/kpFreeFormImageSelection.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/selections/image/kpRectangularImageSelection.h" #include "layers/selections/text/kpTextSelection.h" #include #include #include #include #include #include #include "kpLogCategories.h" #include //-------------------------------------------------------------------------------- kpTransformResizeScaleCommand::kpTransformResizeScaleCommand (bool actOnSelection, int newWidth, int newHeight, Type type, kpCommandEnvironment *environ) : kpCommand (environ), m_actOnSelection (actOnSelection), m_type (type), m_backgroundColor (environ->backgroundColor ()), m_oldSelectionPtr (nullptr) { kpDocument *doc = document (); Q_ASSERT (doc); m_oldWidth = doc->width (m_actOnSelection); m_oldHeight = doc->height (m_actOnSelection); m_actOnTextSelection = (m_actOnSelection && doc->textSelection ()); resize (newWidth, newHeight); // If we have a selection _border_ (but not a floating selection), // then scale the selection with the document m_scaleSelectionWithImage = (!m_actOnSelection && (m_type == Scale || m_type == SmoothScale) && document ()->selection () && !document ()->selection ()->hasContent ()); } kpTransformResizeScaleCommand::~kpTransformResizeScaleCommand () { delete m_oldSelectionPtr; } // public virtual [base kpCommand] QString kpTransformResizeScaleCommand::name () const { if (m_actOnSelection) { if (m_actOnTextSelection) { if (m_type == Resize) { return i18n ("Text: Resize Box"); } } else { if (m_type == Scale) { return i18n ("Selection: Scale"); } if (m_type == SmoothScale) { return i18n ("Selection: Smooth Scale"); } } } else { switch (m_type) { case Resize: return i18n ("Resize"); case Scale: return i18n ("Scale"); case SmoothScale: return i18n ("Smooth Scale"); } } return {}; } // public virtual [base kpCommand] kpCommandSize::SizeType kpTransformResizeScaleCommand::size () const { return ImageSize (m_oldImage) + ImageSize (m_oldRightImage) + ImageSize (m_oldBottomImage) + SelectionSize (m_oldSelectionPtr); } // public int kpTransformResizeScaleCommand::newWidth () const { return m_newWidth; } // public void kpTransformResizeScaleCommand::setNewWidth (int width) { resize (width, newHeight ()); } // public int kpTransformResizeScaleCommand::newHeight () const { return m_newHeight; } // public void kpTransformResizeScaleCommand::setNewHeight (int height) { resize (newWidth (), height); } // public QSize kpTransformResizeScaleCommand::newSize () const { return {newWidth (), newHeight ()}; } // public virtual void kpTransformResizeScaleCommand::resize (int width, int height) { m_newWidth = width; m_newHeight = height; m_isLosslessScale = ((m_type == Scale) && (m_newWidth / m_oldWidth * m_oldWidth == m_newWidth) && (m_newHeight / m_oldHeight * m_oldHeight == m_newHeight)); } // public bool kpTransformResizeScaleCommand::scaleSelectionWithImage () const { return m_scaleSelectionWithImage; } // private void kpTransformResizeScaleCommand::scaleSelectionRegionWithDocument () { -#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND qCDebug(kpLogCommands) << "kpTransformResizeScaleCommand::scaleSelectionRegionWithDocument"; -#endif Q_ASSERT (m_oldSelectionPtr); Q_ASSERT (!m_oldSelectionPtr->hasContent ()); const double horizScale = double (m_newWidth) / double (m_oldWidth); const double vertScale = double (m_newHeight) / double (m_oldHeight); const int newX = static_cast (m_oldSelectionPtr->x () * horizScale); const int newY = static_cast (m_oldSelectionPtr->y () * vertScale); QPolygon currentPoints = m_oldSelectionPtr->calculatePoints (); currentPoints.translate (-currentPoints.boundingRect ().x (), -currentPoints.boundingRect ().y ()); // TODO: refactor into kpPixmapFX // TODO: Can we get to size 0x0 accidentally? QTransform scaleMatrix; scaleMatrix.scale (horizScale, vertScale); currentPoints = scaleMatrix.map (currentPoints); currentPoints.translate ( -currentPoints.boundingRect ().x () + newX, -currentPoints.boundingRect ().y () + newY); auto *imageSel = dynamic_cast (m_oldSelectionPtr); auto *textSel = dynamic_cast (m_oldSelectionPtr); if (imageSel) { document ()->setSelection ( kpFreeFormImageSelection (currentPoints, kpImage (), imageSel->transparency ())); } else if (textSel) { document ()->setSelection ( kpTextSelection (currentPoints.boundingRect (), textSel->textLines (), textSel->textStyle ())); } else { Q_ASSERT (!"Unknown selection type"); } environ ()->somethingBelowTheCursorChanged (); } // public virtual [base kpCommand] void kpTransformResizeScaleCommand::execute () { -#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND qCDebug(kpLogCommands) << "kpTransformResizeScaleCommand::execute() type=" << (int) m_type << " oldWidth=" << m_oldWidth << " oldHeight=" << m_oldHeight << " newWidth=" << m_newWidth << " newHeight=" << m_newHeight; -#endif if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight) return; if (m_type == Resize) { if (m_actOnSelection) { if (!m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::execute() resizing sel doesn't make sense"); } QApplication::setOverrideCursor (Qt::WaitCursor); kpTextSelection *textSel = textSelection (); Q_ASSERT (textSel); kpTextSelection *newSel = textSel->resized (m_newWidth, m_newHeight); document ()->setSelection (*newSel); delete newSel; environ ()->somethingBelowTheCursorChanged (); QApplication::restoreOverrideCursor (); } else { QApplication::setOverrideCursor (Qt::WaitCursor); if (m_newWidth < m_oldWidth) { m_oldRightImage = document ()->getImageAt ( QRect (m_newWidth, 0, m_oldWidth - m_newWidth, m_oldHeight)); } if (m_newHeight < m_oldHeight) { m_oldBottomImage = document ()->getImageAt ( QRect (0, m_newHeight, m_newWidth, m_oldHeight - m_newHeight)); } document ()->resize (m_newWidth, m_newHeight, m_backgroundColor); QApplication::restoreOverrideCursor (); } } // Scale else { QApplication::setOverrideCursor (Qt::WaitCursor); kpImage oldImage = document ()->image (m_actOnSelection); if (!m_isLosslessScale) { m_oldImage = oldImage; } kpImage newImage = kpPixmapFX::scale (oldImage, m_newWidth, m_newHeight, m_type == SmoothScale); if (!m_oldSelectionPtr && document ()->selection ()) { // Save sel border m_oldSelectionPtr = document ()->selection ()->clone (); m_oldSelectionPtr->deleteContent (); } if (m_actOnSelection) { if (m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::execute() scaling text sel doesn't make sense"); } Q_ASSERT (m_oldSelectionPtr); if ( !m_oldSelectionPtr ) { // make coverity happy return; } QRect newRect = QRect (m_oldSelectionPtr->x (), m_oldSelectionPtr->y (), newImage.width (), newImage.height ()); // Not possible to retain non-rectangular selection borders on scale // (think about e.g. a 45 deg line as part of the border & 2x scale) Q_ASSERT (dynamic_cast (m_oldSelectionPtr)); document ()->setSelection ( kpRectangularImageSelection (newRect, newImage, dynamic_cast (m_oldSelectionPtr) ->transparency ())); environ ()->somethingBelowTheCursorChanged (); } else { document ()->setImage (newImage); if (m_scaleSelectionWithImage) { scaleSelectionRegionWithDocument (); } } QApplication::restoreOverrideCursor (); } } // public virtual [base kpCommand] void kpTransformResizeScaleCommand::unexecute () { -#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND qCDebug(kpLogCommands) << "kpTransformResizeScaleCommand::unexecute() type=" << m_type; -#endif if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight) { return; } kpDocument *doc = document (); Q_ASSERT (doc); if (m_type == Resize) { if (m_actOnSelection) { if (!m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::unexecute() resizing sel doesn't make sense"); } QApplication::setOverrideCursor (Qt::WaitCursor); kpTextSelection *textSel = textSelection (); Q_ASSERT (textSel); kpTextSelection *newSel = textSel->resized (m_oldWidth, m_oldHeight); document ()->setSelection (*newSel); delete newSel; environ ()->somethingBelowTheCursorChanged (); QApplication::restoreOverrideCursor (); } else { QApplication::setOverrideCursor (Qt::WaitCursor); kpImage newImage (m_oldWidth, m_oldHeight, QImage::Format_ARGB32_Premultiplied); kpPixmapFX::setPixmapAt (&newImage, QPoint (0, 0), doc->image ()); if (m_newWidth < m_oldWidth) { kpPixmapFX::setPixmapAt (&newImage, QPoint (m_newWidth, 0), m_oldRightImage); } if (m_newHeight < m_oldHeight) { kpPixmapFX::setPixmapAt (&newImage, QPoint (0, m_newHeight), m_oldBottomImage); } doc->setImage (newImage); QApplication::restoreOverrideCursor (); } } // Scale else { QApplication::setOverrideCursor (Qt::WaitCursor); kpImage oldImage; if (!m_isLosslessScale) { oldImage = m_oldImage; } else { oldImage = kpPixmapFX::scale (doc->image (m_actOnSelection), m_oldWidth, m_oldHeight); } if (m_actOnSelection) { if (m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::unexecute() scaling text sel doesn't make sense"); } Q_ASSERT (dynamic_cast (m_oldSelectionPtr)); auto *oldImageSel = dynamic_cast (m_oldSelectionPtr); kpAbstractImageSelection *oldSelection = oldImageSel->clone (); oldSelection->setBaseImage (oldImage); doc->setSelection (*oldSelection); delete oldSelection; environ ()->somethingBelowTheCursorChanged (); } else { doc->setImage (oldImage); if (m_scaleSelectionWithImage) { doc->setSelection (*m_oldSelectionPtr); environ ()->somethingBelowTheCursorChanged (); } } QApplication::restoreOverrideCursor (); } } diff --git a/commands/imagelib/transforms/kpTransformRotateCommand.cpp b/commands/imagelib/transforms/kpTransformRotateCommand.cpp index 33eb3bd5..24f651ee 100644 --- a/commands/imagelib/transforms/kpTransformRotateCommand.cpp +++ b/commands/imagelib/transforms/kpTransformRotateCommand.cpp @@ -1,220 +1,217 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_ROTATE 1 #include "kpTransformRotateCommand.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/image/kpFreeFormImageSelection.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/selections/image/kpRectangularImageSelection.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include #include #include #include //-------------------------------------------------------------------------------- kpTransformRotateCommand::kpTransformRotateCommand (bool actOnSelection, double angle, kpCommandEnvironment *environ) : kpCommand (environ), m_actOnSelection (actOnSelection), m_angle (angle), m_backgroundColor (environ->backgroundColor (actOnSelection)), m_losslessRotation (kpPixmapFX::isLosslessRotation (angle)), m_oldSelectionPtr (nullptr) { } kpTransformRotateCommand::~kpTransformRotateCommand () { delete m_oldSelectionPtr; } // public virtual [base kpCommand] QString kpTransformRotateCommand::name () const { QString opName = i18n ("Rotate"); return (m_actOnSelection) ? i18n ("Selection: %1", opName) : opName; } // public virtual [base kpCommand] kpCommandSize::SizeType kpTransformRotateCommand::size () const { return ImageSize (m_oldImage) + SelectionSize (m_oldSelectionPtr); } // public virtual [base kpCommand] void kpTransformRotateCommand::execute () { kpDocument *doc = document (); Q_ASSERT (doc); QApplication::setOverrideCursor (Qt::WaitCursor); if (!m_losslessRotation) { m_oldImage = doc->image (m_actOnSelection); } kpImage newImage = kpPixmapFX::rotate (doc->image (m_actOnSelection), m_angle, m_backgroundColor); if (!m_actOnSelection) { doc->setImage (newImage); } else { kpAbstractImageSelection *sel = doc->imageSelection (); Q_ASSERT (sel); // Save old selection m_oldSelectionPtr = sel->clone (); // Conserve memmory: // // 1. If it's a lossless rotation, we don't need to the store old // image anywhere at all, as we can reconstruct it by rotating in // reverse. // 2. If it's not a lossless rotation, "m_oldImage" already holds // a copy of the old image. In this case, we actually save very // little with this line (just, the computed transparency mask) since // kpImage is copy-on-write. m_oldSelectionPtr->setBaseImage (kpImage ()); // Calculate new top left (so selection rotates about center) // (the Times2 trickery is used to reduce integer division error without // resorting to the troublesome world of floating point) QPoint oldCenterTimes2 (sel->x () * 2 + sel->width (), sel->y () * 2 + sel->height ()); QPoint newTopLeftTimes2 (oldCenterTimes2 - QPoint (newImage.width (), newImage.height ())); QPoint newTopLeft (newTopLeftTimes2.x () / 2, newTopLeftTimes2.y () / 2); // Calculate rotated points QPolygon currentPoints = sel->calculatePoints (); currentPoints.translate (-currentPoints.boundingRect ().x (), -currentPoints.boundingRect ().y ()); QTransform rotateMatrix = kpPixmapFX::rotateMatrix (doc->image (m_actOnSelection), m_angle); currentPoints = rotateMatrix.map (currentPoints); currentPoints.translate (-currentPoints.boundingRect ().x () + newTopLeft.x (), -currentPoints.boundingRect ().y () + newTopLeft.y ()); if (currentPoints.boundingRect ().width () == newImage.width () && currentPoints.boundingRect ().height () == newImage.height ()) { doc->setSelection ( kpFreeFormImageSelection ( currentPoints, newImage, m_oldSelectionPtr->transparency ())); } else { // TODO: fix the latter "victim of" problem in kpAbstractImageSelection by // allowing the border width & height != pixmap width & height // Or maybe autocrop? - #if DEBUG_KP_TOOL_ROTATE qCDebug(kpLogCommands) << "kpTransformRotateCommand::execute() currentPoints.boundingRect=" << currentPoints.boundingRect () << " newPixmap: w=" << newImage.width () << " h=" << newImage.height () << " (victim of rounding error and/or rotated-a-(rectangular)-pixmap-that-was-transparent-in-the-corners-making-sel-uselessly-bigger-than-needs-be)"; - #endif doc->setSelection ( kpRectangularImageSelection ( QRect (newTopLeft.x (), newTopLeft.y (), newImage.width (), newImage.height ()), newImage, m_oldSelectionPtr->transparency ())); } environ ()->somethingBelowTheCursorChanged (); } QApplication::restoreOverrideCursor (); } // public virtual [base kpCommand] void kpTransformRotateCommand::unexecute () { kpDocument *doc = document (); Q_ASSERT (doc); QApplication::setOverrideCursor (Qt::WaitCursor); kpImage oldImage; if (!m_losslessRotation) { oldImage = m_oldImage; m_oldImage = kpImage (); } else { oldImage = kpPixmapFX::rotate (doc->image (m_actOnSelection), 360 - m_angle, m_backgroundColor); } if (!m_actOnSelection) { doc->setImage (oldImage); } else { m_oldSelectionPtr->setBaseImage (oldImage); doc->setSelection (*m_oldSelectionPtr); delete m_oldSelectionPtr; m_oldSelectionPtr = nullptr; environ ()->somethingBelowTheCursorChanged (); } QApplication::restoreOverrideCursor (); } diff --git a/commands/imagelib/transforms/kpTransformSkewCommand.cpp b/commands/imagelib/transforms/kpTransformSkewCommand.cpp index 36e5b773..618b408c 100644 --- a/commands/imagelib/transforms/kpTransformSkewCommand.cpp +++ b/commands/imagelib/transforms/kpTransformSkewCommand.cpp @@ -1,195 +1,190 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SKEW 1 -#define DEBUG_KP_TOOL_SKEW_DIALOG 1 - #include "kpTransformSkewCommand.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/image/kpFreeFormImageSelection.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/selections/image/kpRectangularImageSelection.h" #include "kpLogCategories.h" #include #include #include // TODO: nasty, should avoid using GUI class in this command class #include "dialogs/imagelib/transforms/kpTransformSkewDialog.h" #include //-------------------------------------------------------------------------------- kpTransformSkewCommand::kpTransformSkewCommand (bool actOnSelection, int hangle, int vangle, kpCommandEnvironment *environ) : kpCommand (environ), m_actOnSelection (actOnSelection), m_hangle (hangle), m_vangle (vangle), m_backgroundColor (environ->backgroundColor (actOnSelection)), m_oldSelectionPtr (nullptr) { } kpTransformSkewCommand::~kpTransformSkewCommand () { delete m_oldSelectionPtr; } // public virtual [base kpCommand] QString kpTransformSkewCommand::name () const { QString opName = i18n ("Skew"); return (m_actOnSelection) ? i18n ("Selection: %1", opName) : opName; } // public virtual [base kpCommand] kpCommandSize::SizeType kpTransformSkewCommand::size () const { return ImageSize (m_oldImage) + SelectionSize (m_oldSelectionPtr); } // public virtual [base kpCommand] void kpTransformSkewCommand::execute () { kpDocument *doc = document (); Q_ASSERT (doc); QApplication::setOverrideCursor (Qt::WaitCursor); kpImage newImage = kpPixmapFX::skew (doc->image (m_actOnSelection), kpTransformSkewDialog::horizontalAngleForPixmapFX (m_hangle), kpTransformSkewDialog::verticalAngleForPixmapFX (m_vangle), m_backgroundColor); if (!m_actOnSelection) { m_oldImage = doc->image (m_actOnSelection); doc->setImage (newImage); } else { kpAbstractImageSelection *sel = doc->imageSelection (); Q_ASSERT (sel); // Save old selection m_oldSelectionPtr = sel->clone (); // Calculate skewed points QPolygon currentPoints = sel->calculatePoints (); currentPoints.translate (-currentPoints.boundingRect ().x (), -currentPoints.boundingRect ().y ()); QTransform skewMatrix = kpPixmapFX::skewMatrix ( doc->image (m_actOnSelection), kpTransformSkewDialog::horizontalAngleForPixmapFX (m_hangle), kpTransformSkewDialog::verticalAngleForPixmapFX (m_vangle)); currentPoints = skewMatrix.map (currentPoints); currentPoints.translate (-currentPoints.boundingRect ().x () + m_oldSelectionPtr->x (), -currentPoints.boundingRect ().y () + m_oldSelectionPtr->y ()); if (currentPoints.boundingRect ().width () == newImage.width () && currentPoints.boundingRect ().height () == newImage.height ()) { doc->setSelection ( kpFreeFormImageSelection ( currentPoints, newImage, m_oldSelectionPtr->transparency ())); } else { // TODO: fix the latter "victim of" problem in kpAbstractImageSelection by // allowing the border width & height != pixmap width & height // Or maybe autocrop? - #if DEBUG_KP_TOOL_SKEW qCDebug(kpLogCommands) << "kpTransformSkewCommand::execute() currentPoints.boundingRect=" << currentPoints.boundingRect () << " newPixmap: w=" << newImage.width () << " h=" << newImage.height () << " (victim of rounding error and/or skewed-a-(rectangular)-pixmap-that-was-transparent-in-the-corners-making-sel-uselessly-bigger-than-needs-be))"; - #endif doc->setSelection ( kpRectangularImageSelection ( QRect (currentPoints.boundingRect ().x (), currentPoints.boundingRect ().y (), newImage.width (), newImage.height ()), newImage, m_oldSelectionPtr->transparency ())); } environ ()->somethingBelowTheCursorChanged (); } QApplication::restoreOverrideCursor (); } // public virtual [base kpCommand] void kpTransformSkewCommand::unexecute () { kpDocument *doc = document (); Q_ASSERT (doc); QApplication::setOverrideCursor (Qt::WaitCursor); if (!m_actOnSelection) { doc->setImage (m_oldImage); m_oldImage = kpImage (); } else { doc->setSelection (*m_oldSelectionPtr); delete m_oldSelectionPtr; m_oldSelectionPtr = nullptr; environ ()->somethingBelowTheCursorChanged (); } QApplication::restoreOverrideCursor (); } diff --git a/commands/kpCommand.cpp b/commands/kpCommand.cpp index e0f01c4b..d7c5b25d 100644 --- a/commands/kpCommand.cpp +++ b/commands/kpCommand.cpp @@ -1,83 +1,81 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COMMAND_HISTORY 1 - #include "kpCommand.h" #include "environments/commands/kpCommandEnvironment.h" kpCommand::kpCommand (kpCommandEnvironment *environ) : m_environ (environ) { Q_ASSERT (environ); } kpCommand::~kpCommand () = default; kpCommandEnvironment *kpCommand::environ () const { return m_environ; } // protected kpDocument *kpCommand::document () const { return m_environ->document (); } // protected kpAbstractSelection *kpCommand::selection () const { return m_environ->selection (); } // protected kpAbstractImageSelection *kpCommand::imageSelection () const { return m_environ->imageSelection (); } // protected kpTextSelection *kpCommand::textSelection () const { return m_environ->textSelection (); } // protected kpViewManager *kpCommand::viewManager () const { return m_environ->viewManager (); } diff --git a/commands/kpCommandHistory.cpp b/commands/kpCommandHistory.cpp index 2b78d1fe..06ca9149 100644 --- a/commands/kpCommandHistory.cpp +++ b/commands/kpCommandHistory.cpp @@ -1,132 +1,128 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COMMAND_HISTORY 1 - #include "kpCommandHistory.h" #include "kpLogCategories.h" #include "layers/selections/kpAbstractSelection.h" #include "mainWindow/kpMainWindow.h" #include "tools/kpTool.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" kpCommandHistory::kpCommandHistory (bool doReadConfig, kpMainWindow *mainWindow) : kpCommandHistoryBase (doReadConfig, mainWindow->actionCollection ()), m_mainWindow (mainWindow) { } kpCommandHistory::~kpCommandHistory () = default; static bool NextUndoCommandIsCreateBorder (kpCommandHistory *commandHistory) { Q_ASSERT (commandHistory); kpCommand *cmd = commandHistory->nextUndoCommand (); if (!cmd) { return false; } auto *c = dynamic_cast (cmd); if (!c) { return false; } const kpAbstractSelection *sel = c->fromSelection (); Q_ASSERT (sel); return (!sel->hasContent ()); } // public void kpCommandHistory::addCreateSelectionCommand (kpToolSelectionCreateCommand *cmd, bool execute) { if (cmd->fromSelection ()->hasContent ()) { addCommand (cmd, execute); return; } if (::NextUndoCommandIsCreateBorder (this)) { setNextUndoCommand (cmd); if (execute) { cmd->execute (); } } else { addCommand (cmd, execute); } } //--------------------------------------------------------------------- // public slot virtual [base KCommandHistory] void kpCommandHistory::undo () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistory::undo() CALLED!"; -#endif + if (m_mainWindow && m_mainWindow->toolHasBegunShape ()) { - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\thas begun shape - cancel draw"; - #endif + m_mainWindow->tool ()->cancelShapeInternal (); } else { kpCommandHistoryBase::undo (); } } //--------------------------------------------------------------------- // public slot virtual [base KCommandHistory] void kpCommandHistory::redo () { if (m_mainWindow && m_mainWindow->toolHasBegunShape ()) { // Not completely obvious but what else can we do? // // Ignoring the request would not be intuitive for tools like // Polygon & Polyline (where it's not always apparent to the user // that s/he's still drawing a shape even though the mouse isn't // down). m_mainWindow->tool ()->cancelShapeInternal (); } else { kpCommandHistoryBase::redo (); } } diff --git a/commands/kpCommandHistoryBase.cpp b/commands/kpCommandHistoryBase.cpp index aa2f0e70..6bdb0cbf 100644 --- a/commands/kpCommandHistoryBase.cpp +++ b/commands/kpCommandHistoryBase.cpp @@ -1,729 +1,667 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COMMAND_HISTORY 1 - #include "kpCommandHistoryBase.h" #include +#include #include #include #include #include #include #include #include #include #include #include "kpCommand.h" #include "kpLogCategories.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "tools/kpTool.h" //--------------------------------------------------------------------- //template static void ClearPointerList (QLinkedList *listPtr) { if (!listPtr) return; qDeleteAll (listPtr->begin (), listPtr->end ()); listPtr->clear (); } struct kpCommandHistoryBasePrivate { }; kpCommandHistoryBase::kpCommandHistoryBase (bool doReadConfig, KActionCollection *ac) : d (new kpCommandHistoryBasePrivate ()) { m_actionUndo = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("edit-undo")), undoActionText (), this); ac->addAction (KStandardAction::name (KStandardAction::Undo), m_actionUndo); ac->setDefaultShortcuts (m_actionUndo, KStandardShortcut::shortcut (KStandardShortcut::Undo)); connect (m_actionUndo, &KToolBarPopupAction::triggered, this, &kpCommandHistoryBase::undo); m_actionRedo = new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("edit-redo")), redoActionText (), this); ac->addAction (KStandardAction::name (KStandardAction::Redo), m_actionRedo); ac->setDefaultShortcuts (m_actionRedo, KStandardShortcut::shortcut (KStandardShortcut::Redo)); connect (m_actionRedo, &KToolBarPopupAction::triggered, this, &kpCommandHistoryBase::redo ); m_actionUndo->setEnabled (false); m_actionRedo->setEnabled (false); connect (m_actionUndo->menu(), &QMenu::triggered, this, &kpCommandHistoryBase::undoUpToNumber); connect (m_actionRedo->menu(), &QMenu::triggered, this, &kpCommandHistoryBase::redoUpToNumber); m_undoMinLimit = 10; m_undoMaxLimit = 500; m_undoMaxLimitSizeLimit = 16 * 1048576; m_documentRestoredPosition = 0; if (doReadConfig) { readConfig (); } } kpCommandHistoryBase::~kpCommandHistoryBase () { ::ClearPointerList (&m_undoCommandList); ::ClearPointerList (&m_redoCommandList); delete d; } // public int kpCommandHistoryBase::undoLimit () const { return undoMinLimit (); } // public void kpCommandHistoryBase::setUndoLimit (int limit) { setUndoMinLimit (limit); } // public int kpCommandHistoryBase::undoMinLimit () const { return m_undoMinLimit; } // public void kpCommandHistoryBase::setUndoMinLimit (int limit) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setUndoMinLimit(" << limit << ")"; -#endif if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::setUndoMinLimit(" << limit << ")"; return; } if (limit == m_undoMinLimit) { return; } m_undoMinLimit = limit; trimCommandListsUpdateActions (); } // public int kpCommandHistoryBase::undoMaxLimit () const { return m_undoMaxLimit; } // public void kpCommandHistoryBase::setUndoMaxLimit (int limit) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimit(" << limit << ")"; -#endif if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimit(" << limit << ")"; return; } if (limit == m_undoMaxLimit) { return; } m_undoMaxLimit = limit; trimCommandListsUpdateActions (); } // public kpCommandSize::SizeType kpCommandHistoryBase::undoMaxLimitSizeLimit () const { return m_undoMaxLimitSizeLimit; } // public void kpCommandHistoryBase::setUndoMaxLimitSizeLimit (kpCommandSize::SizeType sizeLimit) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit(" << sizeLimit << ")"; -#endif if (sizeLimit < 0 || sizeLimit > (500 * 1048576)/*"ought to be enough for anybody"*/) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit(" << sizeLimit << ")"; return; } if (sizeLimit == m_undoMaxLimitSizeLimit) { return; } m_undoMaxLimitSizeLimit = sizeLimit; trimCommandListsUpdateActions (); } // public void kpCommandHistoryBase::readConfig () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::readConfig()"; -#endif + KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupUndoRedo); setUndoMinLimit (cfg.readEntry (kpSettingUndoMinLimit, undoMinLimit ())); setUndoMaxLimit (cfg.readEntry (kpSettingUndoMaxLimit, undoMaxLimit ())); setUndoMaxLimitSizeLimit ( cfg.readEntry (kpSettingUndoMaxLimitSizeLimit, undoMaxLimitSizeLimit ())); trimCommandListsUpdateActions (); } // public void kpCommandHistoryBase::writeConfig () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::writeConfig()"; -#endif + KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupUndoRedo); cfg.writeEntry (kpSettingUndoMinLimit, undoMinLimit ()); cfg.writeEntry (kpSettingUndoMaxLimit, undoMaxLimit ()); cfg.writeEntry ( kpSettingUndoMaxLimitSizeLimit, undoMaxLimitSizeLimit ()); cfg.sync (); } // public void kpCommandHistoryBase::addCommand (kpCommand *command, bool execute) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::addCommand(" << command << ",execute=" << execute << ")"; -#endif + if (execute) { command->execute (); } m_undoCommandList.push_front (command); ::ClearPointerList (&m_redoCommandList); -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition; -#endif + if (m_documentRestoredPosition != INT_MAX) { if (m_documentRestoredPosition > 0) { m_documentRestoredPosition = INT_MAX; } else { m_documentRestoredPosition--; } - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition; - #endif } trimCommandListsUpdateActions (); } // public void kpCommandHistoryBase::clear () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::clear()"; -#endif ::ClearPointerList (&m_undoCommandList); ::ClearPointerList (&m_redoCommandList); m_documentRestoredPosition = 0; updateActions (); } //--------------------------------------------------------------------- // protected slot void kpCommandHistoryBase::undoInternal () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::undoInternal()"; -#endif kpCommand *undoCommand = nextUndoCommand (); if (!undoCommand) { return; } undoCommand->unexecute (); m_undoCommandList.erase (m_undoCommandList.begin ()); m_redoCommandList.push_front (undoCommand); -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition; -#endif if (m_documentRestoredPosition != INT_MAX) { m_documentRestoredPosition++; if (m_documentRestoredPosition == 0) emit documentRestored (); - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition; - #endif } } //--------------------------------------------------------------------- // protected slot void kpCommandHistoryBase::redoInternal () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::redoInternal()"; -#endif kpCommand *redoCommand = nextRedoCommand (); if (!redoCommand) { return; } redoCommand->execute (); m_redoCommandList.erase (m_redoCommandList.begin ()); m_undoCommandList.push_front (redoCommand); -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition; -#endif + if (m_documentRestoredPosition != INT_MAX) { m_documentRestoredPosition--; if (m_documentRestoredPosition == 0) { emit documentRestored (); } - #if DEBUG_KP_COMMAND_HISTORY + qCDebug(kpLogCommands) << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition; - #endif } } //--------------------------------------------------------------------- // public slot virtual void kpCommandHistoryBase::undo () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::undo()"; -#endif undoInternal (); trimCommandListsUpdateActions (); } //--------------------------------------------------------------------- // public slot virtual void kpCommandHistoryBase::redo () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::redo()"; -#endif redoInternal (); trimCommandListsUpdateActions (); } //--------------------------------------------------------------------- // public slot virtual void kpCommandHistoryBase::undoUpToNumber (QAction *which) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::undoUpToNumber(" << which << ")"; -#endif for (int i = 0; i <= which->data().toInt() && !m_undoCommandList.isEmpty (); i++) { undoInternal (); } trimCommandListsUpdateActions (); } // public slot virtual void kpCommandHistoryBase::redoUpToNumber (QAction *which) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::redoUpToNumber(" << which << ")"; -#endif for (int i = 0; i <= which->data().toInt() && !m_redoCommandList.isEmpty (); i++) { redoInternal (); } trimCommandListsUpdateActions (); } // protected QString kpCommandHistoryBase::undoActionText () const { kpCommand *undoCommand = nextUndoCommand (); return (undoCommand) ? i18n ("&Undo: %1", undoCommand->name ()) : i18n ("&Undo"); } // protected QString kpCommandHistoryBase::redoActionText () const { kpCommand *redoCommand = nextRedoCommand (); return (redoCommand) ? i18n ("&Redo: %1", redoCommand->name ()) : i18n ("&Redo"); } // protected QString kpCommandHistoryBase::undoActionToolTip () const { kpCommand *undoCommand = nextUndoCommand (); return (undoCommand) ? i18n ("Undo: %1", undoCommand->name ()) : i18n ("Undo"); } // protected QString kpCommandHistoryBase::redoActionToolTip () const { kpCommand *redoCommand = nextRedoCommand (); return (redoCommand) ? i18n ("Redo: %1", redoCommand->name ()) : i18n ("Redo"); } // protected void kpCommandHistoryBase::trimCommandListsUpdateActions () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::trimCommandListsUpdateActions()"; -#endif trimCommandLists (); updateActions (); } // protected void kpCommandHistoryBase::trimCommandList (QLinkedList *commandList) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::trimCommandList()"; - QTime timer; timer.start (); -#endif + QElapsedTimer timer; timer.start (); if (!commandList) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::trimCommandList() passed 0 commandList"; return; } -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tsize=" << commandList->size () << " undoMinLimit=" << m_undoMinLimit << " undoMaxLimit=" << m_undoMaxLimit << " undoMaxLimitSizeLimit=" << m_undoMaxLimitSizeLimit; -#endif + if (static_cast (commandList->size ()) <= m_undoMinLimit) { - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tsize under undoMinLimit - done"; - #endif return; } -#if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\tsize over undoMinLimit - iterating thru cmds:"; -#endif QLinkedList ::iterator it = commandList->begin (); int upto = 0; kpCommandSize::SizeType sizeSoFar = 0; while (it != commandList->end ()) { bool advanceIt = true; if (sizeSoFar <= m_undoMaxLimitSizeLimit) { sizeSoFar += (*it)->size (); } - #if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\t\t" << upto << ":" << " name='" << (*it)->name () << "' size=" << (*it)->size () << " sizeSoFar=" << sizeSoFar; - #endif if (upto >= m_undoMinLimit) { if (upto >= m_undoMaxLimit || sizeSoFar > m_undoMaxLimitSizeLimit) { - #if DEBUG_KP_COMMAND_HISTORY && 0 - qCDebug(kpLogCommands) << "\t\t\tkill"; - #endif delete (*it); it = m_undoCommandList.erase (it); advanceIt = false; } } if (advanceIt) { it++; } upto++; } -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\ttook " << timer.elapsed () << "ms"; -#endif } // protected void kpCommandHistoryBase::trimCommandLists () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::trimCommandLists()"; -#endif trimCommandList (&m_undoCommandList); trimCommandList (&m_redoCommandList); -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition; -#endif + if (m_documentRestoredPosition != INT_MAX) { - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tundoCmdList.size=" << m_undoCommandList.size () << " redoCmdList.size=" << m_redoCommandList.size (); - #endif + if (m_documentRestoredPosition > static_cast (m_redoCommandList.size ()) || -m_documentRestoredPosition > static_cast (m_undoCommandList.size ())) { - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\t\tinvalidate documentRestoredPosition"; - #endif + m_documentRestoredPosition = INT_MAX; } } } static void populatePopupMenu (QMenu *popupMenu, const QString &undoOrRedo, const QLinkedList &commandList) { if (!popupMenu) { return; } popupMenu->clear (); QLinkedList ::const_iterator it = commandList.begin (); int i = 0; while (i < 10 && it != commandList.end ()) { QAction *action = new QAction(i18n ("%1: %2", undoOrRedo, (*it)->name ()), popupMenu); action->setData(i); popupMenu->addAction (action); i++; it++; } if (it != commandList.end ()) { // TODO: maybe have a scrollview show all the items instead, like KOffice in KDE 3 // LOCOMPAT: should be centered text. popupMenu->addSection (i18np ("%1 more item", "%1 more items", commandList.size () - i)); } } // protected void kpCommandHistoryBase::updateActions () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::updateActions()"; -#endif m_actionUndo->setEnabled (static_cast (nextUndoCommand ())); // Don't want to keep changing toolbar text. // TODO: As a bad side-effect, the menu doesn't have "Undo: " // anymore. In any case, the KDE4 KToolBarPopupAction // sucks in menus as it forces the clicking of a submenu. IMO, // there should be no submenu in the menu. //m_actionUndo->setText (undoActionText ()); // But in icon mode, a tooltip with context is useful. m_actionUndo->setToolTip (undoActionToolTip ()); -#if DEBUG_KP_COMMAND_HISTORY - QTime timer; timer.start (); -#endif + + QElapsedTimer timer; timer.start (); + populatePopupMenu (m_actionUndo->menu (), i18n ("Undo"), m_undoCommandList); -#if DEBUG_KP_COMMAND_HISTORY + qCDebug(kpLogCommands) << "\tpopuplatePopupMenu undo=" << timer.elapsed () << "ms"; -#endif m_actionRedo->setEnabled (static_cast (nextRedoCommand ())); // Don't want to keep changing toolbar text. // TODO: As a bad side-effect, the menu doesn't have "Undo: " // anymore. In any case, the KDE4 KToolBarPopupAction // sucks in menus as it forces the clicking of a submenu. IMO, // there should be no submenu in the menu. //m_actionRedo->setText (redoActionText ()); // But in icon mode, a tooltip with context is useful. m_actionRedo->setToolTip (redoActionToolTip ()); -#if DEBUG_KP_COMMAND_HISTORY + timer.restart (); -#endif + populatePopupMenu (m_actionRedo->menu (), i18n ("Redo"), m_redoCommandList); -#if DEBUG_KP_COMMAND_HISTORY + qCDebug(kpLogCommands) << "\tpopuplatePopupMenu redo=" << timer.elapsed () << "ms"; -#endif } // public kpCommand *kpCommandHistoryBase::nextUndoCommand () const { if (m_undoCommandList.isEmpty ()) { return nullptr; } return m_undoCommandList.first (); } // public kpCommand *kpCommandHistoryBase::nextRedoCommand () const { if (m_redoCommandList.isEmpty ()) { return nullptr; } return m_redoCommandList.first (); } // public void kpCommandHistoryBase::setNextUndoCommand (kpCommand *command) { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setNextUndoCommand("<< command << ")"; -#endif if (m_undoCommandList.isEmpty ()) { return; } delete *m_undoCommandList.begin (); *m_undoCommandList.begin () = command; trimCommandListsUpdateActions (); } // public slot virtual void kpCommandHistoryBase::documentSaved () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::documentSaved()"; -#endif m_documentRestoredPosition = 0; } diff --git a/commands/kpCommandSize.cpp b/commands/kpCommandSize.cpp index 2e1c1e36..0c80251b 100644 --- a/commands/kpCommandSize.cpp +++ b/commands/kpCommandSize.cpp @@ -1,154 +1,146 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COMMAND_SIZE 1 - #include "commands/kpCommandSize.h" #include "layers/selections/kpAbstractSelection.h" #include "kpLogCategories.h" #include #include #include // public static kpCommandSize::SizeType kpCommandSize::PixmapSize (const QImage &image) { return kpCommandSize::PixmapSize (image.width (), image.height (), image.depth ()); } // public static kpCommandSize::SizeType kpCommandSize::PixmapSize (const QImage *image) { return (image ? kpCommandSize::PixmapSize (*image) : 0); } // public static kpCommandSize::SizeType kpCommandSize::PixmapSize (int width, int height, int depth) { // handle 15bpp int roundedDepth = (depth > 8 ? (depth + 7) / 8 * 8 : depth); kpCommandSize::SizeType ret = static_cast (width) * height * roundedDepth / 8; -#if DEBUG_KP_COMMAND_SIZE && 0 qCDebug(kpLogCommands) << "kpCommandSize::PixmapSize() w=" << width << " h=" << height << " d=" << depth << " roundedDepth=" << roundedDepth << " ret=" << ret; -#endif + return ret; } // public static kpCommandSize::SizeType kpCommandSize::QImageSize (const QImage &image) { return kpCommandSize::QImageSize (image.width (), image.height (), image.depth ()); } // public static kpCommandSize::SizeType kpCommandSize::QImageSize (const QImage *image) { return (image ? kpCommandSize::QImageSize (*image) : 0); } // public static kpCommandSize::SizeType kpCommandSize::QImageSize (int width, int height, int depth) { // handle 15bpp int roundedDepth = (depth > 8 ? (depth + 7) / 8 * 8 : depth); kpCommandSize::SizeType ret = static_cast (width) * height * roundedDepth / 8; -#if DEBUG_KP_COMMAND_SIZE && 0 qCDebug(kpLogCommands) << "kpCommandSize::QImageSize() w=" << width << " h=" << height << " d=" << depth << " roundedDepth=" << roundedDepth << " ret=" << ret; -#endif return ret; } // public static kpCommandSize::SizeType kpCommandSize::ImageSize (const kpImage &image) { return kpCommandSize::PixmapSize (image); } // public static kpCommandSize::SizeType kpCommandSize::ImageSize (const kpImage *image) { return kpCommandSize::PixmapSize (image); } // public static kpCommandSize::SizeType kpCommandSize::SelectionSize (const kpAbstractSelection &sel) { return sel.size (); } // public static kpCommandSize::SizeType kpCommandSize::SelectionSize (const kpAbstractSelection *sel) { return (sel ? sel->size () : 0); } // public static kpCommandSize::SizeType kpCommandSize::StringSize (const QString &string) { -#if DEBUG_KP_COMMAND_SIZE && 1 qCDebug(kpLogCommands) << "kpCommandSize::StringSize(" << string << ")" << " len=" << string.length () << " sizeof(QChar)=" << sizeof (QChar); -#endif + return static_cast (static_cast (string.length ()) * sizeof (QChar)); } // public static kpCommandSize::SizeType kpCommandSize::PolygonSize (const QPolygon &points) { -#if DEBUG_KP_COMMAND_SIZE && 1 qCDebug(kpLogCommands) << "kpCommandSize::PolygonSize() points.size=" << points.size () << " sizeof(QPoint)=" << sizeof (QPoint); -#endif return static_cast (static_cast (points.size ()) * sizeof (QPoint)); } diff --git a/commands/kpMacroCommand.cpp b/commands/kpMacroCommand.cpp index 3df8be33..0701c4d0 100644 --- a/commands/kpMacroCommand.cpp +++ b/commands/kpMacroCommand.cpp @@ -1,149 +1,135 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COMMAND_HISTORY 1 - #include "commands/kpMacroCommand.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include #include //--------------------------------------------------------------------- struct kpMacroCommandPrivate { }; kpMacroCommand::kpMacroCommand (const QString &name, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), d (new kpMacroCommandPrivate ()) { } //--------------------------------------------------------------------- kpMacroCommand::~kpMacroCommand () { qDeleteAll (m_commandList.begin (), m_commandList.end ()); delete d; } //--------------------------------------------------------------------- // public virtual [base kpCommand] kpCommandSize::SizeType kpMacroCommand::size () const { -#if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "kpMacroCommand::size()"; -#endif + SizeType s = 0; -#if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\tcalculating:"; -#endif + foreach (kpCommand *cmd, m_commandList) { - #if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\t\tcurrentSize=" << s << " + " << cmd->name () << ".size=" << cmd->size (); - #endif s += cmd->size (); } -#if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\treturning " << s; -#endif return s; } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpMacroCommand::execute () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpMacroCommand::execute()"; -#endif viewManager()->setQueueUpdates(); for (QLinkedList ::const_iterator it = m_commandList.begin (); it != m_commandList.end (); ++it) { - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\texecuting " << (*it)->name (); - #endif + (*it)->execute (); } viewManager()->restoreQueueUpdates(); } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpMacroCommand::unexecute () { -#if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpMacroCommand::unexecute()"; -#endif viewManager()->setQueueUpdates(); QLinkedList ::const_iterator it = m_commandList.end (); it--; while (it != m_commandList.end ()) { - #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tunexecuting " << (*it)->name (); - #endif + (*it)->unexecute (); it--; } viewManager()->restoreQueueUpdates(); } //--------------------------------------------------------------------- // public void kpMacroCommand::addCommand (kpCommand *command) { m_commandList.push_back (command); } //--------------------------------------------------------------------- diff --git a/commands/tools/flow/kpToolFlowCommand.cpp b/commands/tools/flow/kpToolFlowCommand.cpp index ad75bbdc..22dcc773 100644 --- a/commands/tools/flow/kpToolFlowCommand.cpp +++ b/commands/tools/flow/kpToolFlowCommand.cpp @@ -1,140 +1,137 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_FLOW_COMMAND 1 - #include "kpToolFlowCommand.h" #include "document/kpDocument.h" #include "imagelib/kpImage.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "views/manager/kpViewManager.h" +#include "kpLogCategories.h" #include struct kpToolFlowCommandPrivate { kpImage image; QRect boundingRect; }; kpToolFlowCommand::kpToolFlowCommand (const QString &name, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), d (new kpToolFlowCommandPrivate ()) { d->image = document ()->image (); } kpToolFlowCommand::~kpToolFlowCommand () { delete d; } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolFlowCommand::size () const { return ImageSize (d->image); } // public virtual [base kpCommand] void kpToolFlowCommand::execute () { swapOldAndNew (); } // public virtual [base kpCommand] void kpToolFlowCommand::unexecute () { swapOldAndNew (); } // private void kpToolFlowCommand::swapOldAndNew () { if (d->boundingRect.isValid ()) { const kpImage oldImage = document ()->getImageAt (d->boundingRect); document ()->setImageAt (d->image, d->boundingRect.topLeft ()); d->image = oldImage; } } // public void kpToolFlowCommand::updateBoundingRect (const QPoint &point) { updateBoundingRect (QRect (point, point)); } // public void kpToolFlowCommand::updateBoundingRect (const QRect &rect) { -#if DEBUG_KP_TOOL_FLOW_COMMAND & 0 qCDebug(kpLogCommands) << "kpToolFlowCommand::updateBoundingRect() existing=" << d->boundingRect << " plus=" << rect; -#endif + d->boundingRect = d->boundingRect.united (rect); -#if DEBUG_KP_TOOL_FLOW_COMMAND & 0 + qCDebug(kpLogCommands) << "\tresult=" << d->boundingRect; -#endif } // public void kpToolFlowCommand::finalize () { if (d->boundingRect.isValid ()) { // Store only the needed part of doc image. d->image = kpTool::neededPixmap (d->image, d->boundingRect); } else { d->image = kpImage (); } } // public void kpToolFlowCommand::cancel () { if (d->boundingRect.isValid ()) { viewManager ()->setFastUpdates (); document ()->setImageAt (d->image, d->boundingRect.topLeft ()); viewManager ()->restoreFastUpdates (); } } diff --git a/commands/tools/kpToolColorPickerCommand.cpp b/commands/tools/kpToolColorPickerCommand.cpp index 25e9cdc3..afd62203 100644 --- a/commands/tools/kpToolColorPickerCommand.cpp +++ b/commands/tools/kpToolColorPickerCommand.cpp @@ -1,81 +1,79 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_COLOR_PICKER 1 - #include "kpToolColorPickerCommand.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include kpToolColorPickerCommand::kpToolColorPickerCommand ( int mouseButton, const kpColor &newColor, const kpColor &oldColor, kpCommandEnvironment *environ) : kpCommand (environ), m_mouseButton (mouseButton), m_newColor (newColor), m_oldColor (oldColor) { } kpToolColorPickerCommand::~kpToolColorPickerCommand () = default; // public virtual [base kpCommand] QString kpToolColorPickerCommand::name () const { return i18n ("Color Picker"); } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolColorPickerCommand::size () const { return 0; } // public virtual [base kpCommand] void kpToolColorPickerCommand::execute () { environ ()->setColor (m_mouseButton, m_newColor); } // public virtual [base kpCommand] void kpToolColorPickerCommand::unexecute () { environ ()->setColor (m_mouseButton, m_oldColor); } diff --git a/commands/tools/kpToolFloodFillCommand.cpp b/commands/tools/kpToolFloodFillCommand.cpp index ae5a88a4..e704235b 100644 --- a/commands/tools/kpToolFloodFillCommand.cpp +++ b/commands/tools/kpToolFloodFillCommand.cpp @@ -1,169 +1,161 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_FLOOD_FILL 1 - #include "kpToolFloodFillCommand.h" #include "imagelib/kpColor.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "imagelib/kpImage.h" #include "kpLogCategories.h" #include #include //--------------------------------------------------------------------- struct kpToolFloodFillCommandPrivate { kpImage oldImage; bool fillEntireImage{false}; }; //--------------------------------------------------------------------- kpToolFloodFillCommand::kpToolFloodFillCommand (int x, int y, const kpColor &color, int processedColorSimilarity, kpCommandEnvironment *environ) : kpCommand (environ), kpFloodFill (document ()->imagePointer (), x, y, color, processedColorSimilarity), d (new kpToolFloodFillCommandPrivate ()) { d->fillEntireImage = false; } //--------------------------------------------------------------------- kpToolFloodFillCommand::~kpToolFloodFillCommand () { delete d; } //--------------------------------------------------------------------- // public virtual [base kpCommand] QString kpToolFloodFillCommand::name () const { return i18n ("Flood Fill"); } //--------------------------------------------------------------------- // public virtual [base kpCommand] kpCommandSize::SizeType kpToolFloodFillCommand::size () const { return kpFloodFill::size () + ImageSize (d->oldImage); } //--------------------------------------------------------------------- // public void kpToolFloodFillCommand::setFillEntireImage (bool yes) { d->fillEntireImage = yes; } //--------------------------------------------------------------------- // protected virtual [base kpCommand] void kpToolFloodFillCommand::execute () { -#if DEBUG_KP_TOOL_FLOOD_FILL && 1 qCDebug(kpLogCommands) << "kpToolFloodFillCommand::execute() fillEntireImage=" << d->fillEntireImage; -#endif kpDocument *doc = document (); Q_ASSERT (doc); if (d->fillEntireImage) { doc->fill (kpFloodFill::color ()); } else { QRect rect = kpFloodFill::boundingRect (); if (rect.isValid ()) { QApplication::setOverrideCursor (Qt::WaitCursor); { d->oldImage = doc->getImageAt (rect); kpFloodFill::fill (); doc->slotContentsChanged (rect); } QApplication::restoreOverrideCursor (); } else { - #if DEBUG_KP_TOOL_FLOOD_FILL && 1 qCDebug(kpLogCommands) << "\tinvalid boundingRect - must be NOP case"; - #endif } } } //--------------------------------------------------------------------- // protected virtual [base kpCommand] void kpToolFloodFillCommand::unexecute () { -#if DEBUG_KP_TOOL_FLOOD_FILL && 1 qCDebug(kpLogCommands) << "kpToolFloodFillCommand::unexecute() fillEntireImage=" << d->fillEntireImage; -#endif kpDocument *doc = document (); Q_ASSERT (doc); if (d->fillEntireImage) { doc->fill (kpFloodFill::colorToChange ()); } else { QRect rect = kpFloodFill::boundingRect (); if (rect.isValid ()) { doc->setImageAt (d->oldImage, rect.topLeft ()); d->oldImage = kpImage (); doc->slotContentsChanged (rect); } } } //--------------------------------------------------------------------- diff --git a/commands/tools/polygonal/kpToolPolygonalCommand.cpp b/commands/tools/polygonal/kpToolPolygonalCommand.cpp index 88a19067..f55a916a 100644 --- a/commands/tools/polygonal/kpToolPolygonalCommand.cpp +++ b/commands/tools/polygonal/kpToolPolygonalCommand.cpp @@ -1,124 +1,123 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_POLYGON 1 #include "kpToolPolygonalCommand.h" #include "document/kpDocument.h" #include "kpDefs.h" #include "imagelib/kpImage.h" #include "tools/polygonal/kpToolPolygonalBase.h" struct kpToolPolygonalCommandPrivate { kpToolPolygonalBase::DrawShapeFunc drawShapeFunc{}; QPolygon points; QRect boundingRect; kpColor fcolor; int penWidth{}; kpColor bcolor; kpImage oldImage; }; kpToolPolygonalCommand::kpToolPolygonalCommand (const QString &name, kpToolPolygonalBase::DrawShapeFunc drawShapeFunc, const QPolygon &points, const QRect &boundingRect, const kpColor &fcolor, int penWidth, const kpColor &bcolor, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), d (new kpToolPolygonalCommandPrivate ()) { d->drawShapeFunc = drawShapeFunc; d->points = points; d->boundingRect = boundingRect; d->fcolor = fcolor; d->penWidth = penWidth; d->bcolor = bcolor; } kpToolPolygonalCommand::~kpToolPolygonalCommand () { delete d; } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolPolygonalCommand::size () const { return PolygonSize (d->points) + ImageSize (d->oldImage); } // public virtual [base kpCommand] void kpToolPolygonalCommand::execute () { kpDocument *doc = document (); Q_ASSERT (doc); // Store Undo info. Q_ASSERT (d->oldImage.isNull ()); d->oldImage = doc->getImageAt (d->boundingRect); // Invoke shape drawing function passed in ctor. kpImage image = d->oldImage; QPolygon pointsTranslated = d->points; pointsTranslated.translate (-d->boundingRect.x (), -d->boundingRect.y ()); (*d->drawShapeFunc) (&image, pointsTranslated, d->fcolor, d->penWidth, d->bcolor, true/*final shape*/); doc->setImageAt (image, d->boundingRect.topLeft ()); } // public virtual [base kpCommand] void kpToolPolygonalCommand::unexecute () { kpDocument *doc = document (); Q_ASSERT (doc); Q_ASSERT (!d->oldImage.isNull ()); doc->setImageAt (d->oldImage, d->boundingRect.topLeft ()); d->oldImage = kpImage (); } diff --git a/commands/tools/rectangular/kpToolRectangularCommand.cpp b/commands/tools/rectangular/kpToolRectangularCommand.cpp index 63f135dd..d1341dc4 100644 --- a/commands/tools/rectangular/kpToolRectangularCommand.cpp +++ b/commands/tools/rectangular/kpToolRectangularCommand.cpp @@ -1,126 +1,124 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_RECTANGULAR_COMMAND 1 - #include "kpToolRectangularCommand.h" #include "imagelib/kpColor.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/tempImage/kpTempImage.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetFillStyle.h" #include "widgets/toolbars/options/kpToolWidgetLineWidth.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" struct kpToolRectangularCommandPrivate { kpToolRectangularBase::DrawShapeFunc drawShapeFunc{}; QRect rect; kpColor fcolor; int penWidth{}; kpColor bcolor; kpImage oldImage; }; kpToolRectangularCommand::kpToolRectangularCommand (const QString &name, kpToolRectangularBase::DrawShapeFunc drawShapeFunc, const QRect &rect, const kpColor &fcolor, int penWidth, const kpColor &bcolor, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), d (new kpToolRectangularCommandPrivate ()) { d->drawShapeFunc = drawShapeFunc; d->rect = rect; d->fcolor = fcolor; d->penWidth = penWidth; d->bcolor = bcolor; } kpToolRectangularCommand::~kpToolRectangularCommand () { delete d; } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolRectangularCommand::size () const { return ImageSize (d->oldImage); } // public virtual [base kpCommand] void kpToolRectangularCommand::execute () { kpDocument *doc = document (); Q_ASSERT (doc); // Store Undo info. // OPT: For a pure rectangle, can do better if there is no bcolor, by only // saving 4 pixmaps corresponding to the pixels dirtied by the 4 edges. Q_ASSERT (d->oldImage.isNull ()); d->oldImage = doc->getImageAt (d->rect); // Invoke shape drawing function passed in ctor. kpImage image = d->oldImage; (*d->drawShapeFunc) (&image, 0, 0, d->rect.width (), d->rect.height (), d->fcolor, d->penWidth, d->bcolor); doc->setImageAt (image, d->rect.topLeft ()); } // public virtual [base kpCommand] void kpToolRectangularCommand::unexecute () { kpDocument *doc = document (); Q_ASSERT (doc); Q_ASSERT (!d->oldImage.isNull ()); doc->setImageAt (d->oldImage, d->rect.topLeft ()); d->oldImage = kpImage (); } diff --git a/commands/tools/selection/kpToolImageSelectionTransparencyCommand.cpp b/commands/tools/selection/kpToolImageSelectionTransparencyCommand.cpp index 1383aec8..b9328e4f 100644 --- a/commands/tools/selection/kpToolImageSelectionTransparencyCommand.cpp +++ b/commands/tools/selection/kpToolImageSelectionTransparencyCommand.cpp @@ -1,96 +1,90 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 - #include "kpToolImageSelectionTransparencyCommand.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "generic/kpSetOverrideCursorSaver.h" #include "kpLogCategories.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include //-------------------------------------------------------------------------------- kpToolImageSelectionTransparencyCommand::kpToolImageSelectionTransparencyCommand ( const QString &name, const kpImageSelectionTransparency &st, const kpImageSelectionTransparency &oldST, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_st (st), m_oldST (oldST) { } kpToolImageSelectionTransparencyCommand::~kpToolImageSelectionTransparencyCommand () = default; // public virtual [base kpCommand] kpCommandSize::SizeType kpToolImageSelectionTransparencyCommand::size () const { return 0; } // public virtual [base kpCommand] void kpToolImageSelectionTransparencyCommand::execute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolImageSelectionTransparencyCommand::execute()"; -#endif kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor); environ ()->setImageSelectionTransparency (m_st, true/*force colour change*/); if (imageSelection ()) { imageSelection ()->setTransparency (m_st); } } // public virtual [base kpCommand] void kpToolImageSelectionTransparencyCommand::unexecute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolImageSelectionTransparencyCommand::unexecute()"; -#endif kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor); environ ()->setImageSelectionTransparency (m_oldST, true/*force colour change*/); if (imageSelection ()) { imageSelection ()->setTransparency (m_oldST); } } diff --git a/commands/tools/selection/kpToolSelectionCreateCommand.cpp b/commands/tools/selection/kpToolSelectionCreateCommand.cpp index fbcb512b..abedc5f0 100644 --- a/commands/tools/selection/kpToolSelectionCreateCommand.cpp +++ b/commands/tools/selection/kpToolSelectionCreateCommand.cpp @@ -1,161 +1,156 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 - #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "layers/selections/kpAbstractSelection.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include #include #include #include #include #include #include #include #include kpToolSelectionCreateCommand::kpToolSelectionCreateCommand (const QString &name, const kpAbstractSelection &fromSelection, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_fromSelection (nullptr), m_textRow (0), m_textCol (0) { setFromSelection (fromSelection); } kpToolSelectionCreateCommand::~kpToolSelectionCreateCommand () { delete m_fromSelection; } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolSelectionCreateCommand::size () const { return SelectionSize (m_fromSelection); } // public const kpAbstractSelection *kpToolSelectionCreateCommand::fromSelection () const { return m_fromSelection; } // public void kpToolSelectionCreateCommand::setFromSelection (const kpAbstractSelection &fromSelection) { delete m_fromSelection; m_fromSelection = fromSelection.clone (); } // public virtual [base kpCommand] void kpToolSelectionCreateCommand::execute () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "kpToolSelectionCreateCommand::execute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); if (m_fromSelection) { - #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "\tusing fromSelection"; - qCDebug(kpLogCommands) << "\t\thave sel=" << doc->selection () << endl; - #endif + qCDebug(kpLogCommands) << "\t\thave sel=" << doc->selection (); + kpAbstractImageSelection *imageSel = dynamic_cast (m_fromSelection); kpTextSelection *textSel = dynamic_cast (m_fromSelection); if (imageSel) { if (imageSel->transparency () != environ ()->imageSelectionTransparency ()) { environ ()->setImageSelectionTransparency (imageSel->transparency ()); } } else if (textSel) { if (textSel->textStyle () != environ ()->textStyle ()) { environ ()->setTextStyle (textSel->textStyle ()); } } else { Q_ASSERT (!"Unknown selection type"); } viewManager ()->setTextCursorPosition (m_textRow, m_textCol); doc->setSelection (*m_fromSelection); environ ()->somethingBelowTheCursorChanged (); } } // public virtual [base kpCommand] void kpToolSelectionCreateCommand::unexecute () { kpDocument *doc = document (); Q_ASSERT (doc); if (!doc->selection ()) { // Was just a border that got deselected? if (m_fromSelection && !m_fromSelection->hasContent ()) { return; } Q_ASSERT (!"kpToolSelectionCreateCommand::unexecute() without sel region"); return; } m_textRow = viewManager ()->textCursorRow (); m_textCol = viewManager ()->textCursorCol (); doc->selectionDelete (); environ ()->somethingBelowTheCursorChanged (); } diff --git a/commands/tools/selection/kpToolSelectionDestroyCommand.cpp b/commands/tools/selection/kpToolSelectionDestroyCommand.cpp index e8037489..515975cf 100644 --- a/commands/tools/selection/kpToolSelectionDestroyCommand.cpp +++ b/commands/tools/selection/kpToolSelectionDestroyCommand.cpp @@ -1,176 +1,165 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 - #include "kpToolSelectionDestroyCommand.h" #include "kpLogCategories.h" #include "layers/selections/kpAbstractSelection.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "views/manager/kpViewManager.h" //--------------------------------------------------------------------- kpToolSelectionDestroyCommand::kpToolSelectionDestroyCommand (const QString &name, bool pushOntoDocument, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_pushOntoDocument (pushOntoDocument), m_oldSelectionPtr (nullptr), m_textRow(0), m_textCol(0) { } //--------------------------------------------------------------------- kpToolSelectionDestroyCommand::~kpToolSelectionDestroyCommand () { delete m_oldSelectionPtr; } //--------------------------------------------------------------------- // public virtual [base kpCommand] kpCommandSize::SizeType kpToolSelectionDestroyCommand::size () const { return ImageSize (m_oldDocImage) + SelectionSize (m_oldSelectionPtr); } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpToolSelectionDestroyCommand::execute () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "kpToolSelectionDestroyCommand::execute () CALLED"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); Q_ASSERT (doc->selection ()); m_textRow = viewManager ()->textCursorRow (); m_textCol = viewManager ()->textCursorCol (); Q_ASSERT (!m_oldSelectionPtr); m_oldSelectionPtr = doc->selection ()->clone (); if (m_pushOntoDocument) { m_oldDocImage = doc->getImageAt (doc->selection ()->boundingRect ()); doc->selectionPushOntoDocument (); } else { doc->selectionDelete (); } environ ()->somethingBelowTheCursorChanged (); } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpToolSelectionDestroyCommand::unexecute () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "kpToolSelectionDestroyCommand::unexecute () CALLED"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); if (doc->selection ()) { // not error because it's possible that the user dragged out a new // region (without pulling image), and then CTRL+Z - #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "kpToolSelectionDestroyCommand::unexecute() already has sel region"; - #endif if (doc->selection ()->hasContent ()) { Q_ASSERT (!"kpToolSelectionDestroyCommand::unexecute() already has sel content"); return; } } Q_ASSERT (m_oldSelectionPtr); if (m_pushOntoDocument) { - #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "\tunpush oldDocImage onto doc first"; - #endif doc->setImageAt (m_oldDocImage, m_oldSelectionPtr->topLeft ()); } -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "\tsetting selection to: rect=" << m_oldSelectionPtr->boundingRect () << " hasContent=" << m_oldSelectionPtr->hasContent (); -#endif + kpAbstractImageSelection *imageSel = dynamic_cast (m_oldSelectionPtr); kpTextSelection *textSel = dynamic_cast (m_oldSelectionPtr); if (imageSel) { if (imageSel->transparency () != environ ()->imageSelectionTransparency ()) { environ ()->setImageSelectionTransparency (imageSel->transparency ()); } if (dynamic_cast (doc->selection())) { doc->selectionPushOntoDocument(); } } else if (textSel) { if (textSel->textStyle () != environ ()->textStyle ()) { environ ()->setTextStyle (textSel->textStyle ()); } if (dynamic_cast (doc->selection())) { doc->selectionPushOntoDocument(); } } else { Q_ASSERT (!"Unknown selection type"); } viewManager ()->setTextCursorPosition (m_textRow, m_textCol); doc->setSelection (*m_oldSelectionPtr); environ ()->somethingBelowTheCursorChanged (); delete m_oldSelectionPtr; m_oldSelectionPtr = nullptr; } diff --git a/commands/tools/selection/kpToolSelectionMoveCommand.cpp b/commands/tools/selection/kpToolSelectionMoveCommand.cpp index 2b3cfe9a..fb4d0fd1 100644 --- a/commands/tools/selection/kpToolSelectionMoveCommand.cpp +++ b/commands/tools/selection/kpToolSelectionMoveCommand.cpp @@ -1,219 +1,208 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 - #include "kpToolSelectionMoveCommand.h" #include "layers/selections/kpAbstractSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "tools/kpTool.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" //-------------------------------------------------------------------------------- kpToolSelectionMoveCommand::kpToolSelectionMoveCommand (const QString &name, kpCommandEnvironment *environ) : kpNamedCommand (name, environ) { kpDocument *doc = document (); Q_ASSERT (doc); Q_ASSERT (doc->selection ()); m_startPoint = m_endPoint = doc->selection ()->topLeft (); } kpToolSelectionMoveCommand::~kpToolSelectionMoveCommand () = default; // public kpAbstractSelection *kpToolSelectionMoveCommand::originalSelectionClone () const { kpDocument *doc = document (); Q_ASSERT (doc); Q_ASSERT (doc->selection ()); kpAbstractSelection *selection = doc->selection ()->clone (); selection->moveTo (m_startPoint); return selection; } // public virtual [base kpComand] kpCommandSize::SizeType kpToolSelectionMoveCommand::size () const { return ImageSize (m_oldDocumentImage) + PolygonSize (m_copyOntoDocumentPoints); } // public virtual [base kpCommand] void kpToolSelectionMoveCommand::execute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolSelectionMoveCommand::execute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); kpAbstractSelection *sel = doc->selection (); // Must have content before it can be moved. Q_ASSERT (sel && sel->hasContent ()); kpViewManager *vm = viewManager (); Q_ASSERT (vm); vm->setQueueUpdates (); { for (const auto &p : m_copyOntoDocumentPoints) { sel->moveTo (p); doc->selectionCopyOntoDocument (); } sel->moveTo (m_endPoint); environ ()->somethingBelowTheCursorChanged (); } vm->restoreQueueUpdates (); } // public virtual [base kpCommand] void kpToolSelectionMoveCommand::unexecute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolSelectionMoveCommand::unexecute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); kpAbstractSelection *sel = doc->selection (); // Must have content before it can be un-moved. Q_ASSERT (sel && sel->hasContent ()); kpViewManager *vm = viewManager (); Q_ASSERT (vm); vm->setQueueUpdates (); if (!m_oldDocumentImage.isNull ()) { doc->setImageAt (m_oldDocumentImage, m_documentBoundingRect.topLeft ()); } -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "\tmove to startPoint=" << m_startPoint; -#endif + sel->moveTo (m_startPoint); environ ()->somethingBelowTheCursorChanged (); vm->restoreQueueUpdates (); } // public void kpToolSelectionMoveCommand::moveTo (const QPoint &point, bool moveLater) { -#if DEBUG_KP_TOOL_SELECTION && 0 qCDebug(kpLogCommands) << "kpToolSelectionMoveCommand::moveTo" << point << " moveLater=" << moveLater; -#endif if (!moveLater) { kpDocument *doc = document (); Q_ASSERT (doc); kpAbstractSelection *sel = doc->selection (); // Must have content before it can be moved. Q_ASSERT (sel && sel->hasContent ()); if (point == sel->topLeft ()) { return; } sel->moveTo (point); } m_endPoint = point; } // public void kpToolSelectionMoveCommand::moveTo (int x, int y, bool moveLater) { moveTo (QPoint (x, y), moveLater); } // public void kpToolSelectionMoveCommand::copyOntoDocument () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "kpToolSelectionMoveCommand::copyOntoDocument()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); kpAbstractSelection *sel = doc->selection (); // Must have content before we allow it be stamped onto the document, // to be consistent with the requirement on other selection operations. Q_ASSERT (sel && sel->hasContent ()); if (m_oldDocumentImage.isNull ()) { m_oldDocumentImage = doc->image (); } QRect selBoundingRect = sel->boundingRect (); m_documentBoundingRect = m_documentBoundingRect.united (selBoundingRect); doc->selectionCopyOntoDocument (); m_copyOntoDocumentPoints.putPoints (m_copyOntoDocumentPoints.count (), 1, selBoundingRect.x (), selBoundingRect.y ()); } // public void kpToolSelectionMoveCommand::finalize () { if (!m_oldDocumentImage.isNull () && !m_documentBoundingRect.isNull ()) { m_oldDocumentImage = kpTool::neededPixmap (m_oldDocumentImage, m_documentBoundingRect); } } diff --git a/commands/tools/selection/kpToolSelectionPullFromDocumentCommand.cpp b/commands/tools/selection/kpToolSelectionPullFromDocumentCommand.cpp index c165f28f..8b556752 100644 --- a/commands/tools/selection/kpToolSelectionPullFromDocumentCommand.cpp +++ b/commands/tools/selection/kpToolSelectionPullFromDocumentCommand.cpp @@ -1,140 +1,133 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 #include "kpToolSelectionPullFromDocumentCommand.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "document/kpDocument.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" kpToolSelectionPullFromDocumentCommand::kpToolSelectionPullFromDocumentCommand ( const kpAbstractImageSelection &originalSelBorder, const kpColor &backgroundColor, const QString &name, kpCommandEnvironment *environ) : kpAbstractSelectionContentCommand (originalSelBorder, name, environ), m_backgroundColor (backgroundColor) { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolSelectionPullFromDocumentCommand::() environ=" << environ; -#endif } kpToolSelectionPullFromDocumentCommand::~kpToolSelectionPullFromDocumentCommand () = default; // public virtual [base kpCommand] void kpToolSelectionPullFromDocumentCommand::execute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolSelectionPullFromDocumentCommand::execute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); kpViewManager *vm = viewManager (); Q_ASSERT (vm); vm->setQueueUpdates (); { // // Recreate border // // The previously executed command is required to have been a // kpToolSelectionCreateCommand, which must have been given an image // selection with no content. // // However, there is a tricky case. Suppose we are called for the first // time, where the above precondition holds. We would add content // to the selection as expected. But the user then undoes (CTRL+Z) the // operation, calling unexecute(). There is now no content again. // Since selection is only a border, the user can freely deselect it // and/or select another region without changing the command history // or document modified state. Therefore, if they now call us again // by redoing (CTRL+Shift+Z), there is potentially no selection at all // or it is at an arbitrary location. // // This assertion covers all 3 possibilities: // // 1. First call: image selection with no content // 2. Later calls: // a) no image selection (due to deselection) // b) image selection with no content, at an arbitrary location Q_ASSERT (!imageSelection () || !imageSelection ()->hasContent ()); const auto *originalImageSel = dynamic_cast (originalSelection ()); if (originalImageSel->transparency () != environ ()->imageSelectionTransparency ()) { environ ()->setImageSelectionTransparency (originalImageSel->transparency ()); } doc->setSelection (*originalSelection ()); // // Add content // doc->imageSelectionPullFromDocument (m_backgroundColor); } vm->restoreQueueUpdates (); } // public virtual [base kpCommand] void kpToolSelectionPullFromDocumentCommand::unexecute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolSelectionPullFromDocumentCommand::unexecute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); // Must have selection image content. Q_ASSERT (doc->imageSelection () && doc->imageSelection ()->hasContent ()); // We can have faith that this is the state of the selection after // execute(), rather than after the user tried to throw us off by // simply selecting another region as to do that, a destroy command // must have been used. doc->selectionCopyOntoDocument (false/*use opaque pixmap*/); doc->imageSelection ()->deleteContent (); } diff --git a/commands/tools/selection/kpToolSelectionResizeScaleCommand.cpp b/commands/tools/selection/kpToolSelectionResizeScaleCommand.cpp index 73dedda0..1bd7030e 100644 --- a/commands/tools/selection/kpToolSelectionResizeScaleCommand.cpp +++ b/commands/tools/selection/kpToolSelectionResizeScaleCommand.cpp @@ -1,257 +1,252 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 #include "kpToolSelectionResizeScaleCommand.h" #include "layers/selections/kpAbstractSelection.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/image/kpRectangularImageSelection.h" #include "layers/selections/text/kpTextSelection.h" #include "kpLogCategories.h" #include #include #include #include //-------------------------------------------------------------------------------- kpToolSelectionResizeScaleCommand::kpToolSelectionResizeScaleCommand ( kpCommandEnvironment *environ) : kpNamedCommand (environ->textSelection () ? i18n ("Text: Resize Box") : i18n ("Selection: Smooth Scale"), environ), m_smoothScaleTimer (new QTimer (this)) { m_originalSelectionPtr = selection ()->clone (); m_newTopLeft = selection ()->topLeft (); m_newWidth = selection ()->width (); m_newHeight = selection ()->height (); m_smoothScaleTimer->setSingleShot (true); connect (m_smoothScaleTimer, &QTimer::timeout, this, [this]{resizeScaleAndMove(false);}); } kpToolSelectionResizeScaleCommand::~kpToolSelectionResizeScaleCommand () { delete m_originalSelectionPtr; } // public virtual kpCommandSize::SizeType kpToolSelectionResizeScaleCommand::size () const { return SelectionSize (m_originalSelectionPtr); } // public const kpAbstractSelection *kpToolSelectionResizeScaleCommand::originalSelection () const { return m_originalSelectionPtr; } // public QPoint kpToolSelectionResizeScaleCommand::topLeft () const { return m_newTopLeft; } // public void kpToolSelectionResizeScaleCommand::moveTo (const QPoint &point) { if (point == m_newTopLeft) { return; } m_newTopLeft = point; selection ()->moveTo (m_newTopLeft); } // public int kpToolSelectionResizeScaleCommand::width () const { return m_newWidth; } // public int kpToolSelectionResizeScaleCommand::height () const { return m_newHeight; } // public void kpToolSelectionResizeScaleCommand::resize (int width, int height, bool delayed) { if (width == m_newWidth && height == m_newHeight) { return; } m_newWidth = width; m_newHeight = height; resizeScaleAndMove (delayed); } // public void kpToolSelectionResizeScaleCommand::resizeAndMoveTo (int width, int height, const QPoint &point, bool delayed) { if (width == m_newWidth && height == m_newHeight && point == m_newTopLeft) { return; } m_newWidth = width; m_newHeight = height; m_newTopLeft = point; resizeScaleAndMove (delayed); } // protected void kpToolSelectionResizeScaleCommand::killSmoothScaleTimer () { m_smoothScaleTimer->stop (); } // protected void kpToolSelectionResizeScaleCommand::resizeScaleAndMove (bool delayed) { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "kpToolSelectionResizeScaleCommand::resizeScaleAndMove(delayed=" << delayed << ")"; -#endif killSmoothScaleTimer (); kpAbstractSelection *newSelPtr = nullptr; if (textSelection ()) { Q_ASSERT (dynamic_cast (m_originalSelectionPtr)); auto *orgTextSel = dynamic_cast (m_originalSelectionPtr); newSelPtr = orgTextSel->resized (m_newWidth, m_newHeight); } else { Q_ASSERT (dynamic_cast (m_originalSelectionPtr)); auto *imageSel = dynamic_cast (m_originalSelectionPtr); newSelPtr = new kpRectangularImageSelection ( QRect (imageSel->x (), imageSel->y (), m_newWidth, m_newHeight), kpPixmapFX::scale (imageSel->baseImage (), m_newWidth, m_newHeight, !delayed/*if not delayed, smooth*/), imageSel->transparency ()); if (delayed) { // Call self (once) with delayed==false in 200ms m_smoothScaleTimer->start (200/*ms*/); } } Q_ASSERT (newSelPtr); newSelPtr->moveTo (m_newTopLeft); document ()->setSelection (*newSelPtr); delete newSelPtr; } // public void kpToolSelectionResizeScaleCommand::finalize () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogCommands) << "kpToolSelectionResizeScaleCommand::finalize()" << " smoothScaleTimer->isActive=" << m_smoothScaleTimer->isActive (); -#endif // Make sure the selection contains the final image and the timer won't // fire afterwards. if (m_smoothScaleTimer->isActive ()) { resizeScaleAndMove (); Q_ASSERT (!m_smoothScaleTimer->isActive ()); } } // public virtual [base kpToolResizeScaleCommand] void kpToolSelectionResizeScaleCommand::execute () { QApplication::setOverrideCursor (Qt::WaitCursor); killSmoothScaleTimer (); resizeScaleAndMove (); environ ()->somethingBelowTheCursorChanged (); QApplication::restoreOverrideCursor (); } // public virtual [base kpToolResizeScaleCommand] void kpToolSelectionResizeScaleCommand::unexecute () { QApplication::setOverrideCursor (Qt::WaitCursor); killSmoothScaleTimer (); document ()->setSelection (*m_originalSelectionPtr); environ ()->somethingBelowTheCursorChanged (); QApplication::restoreOverrideCursor (); } diff --git a/commands/tools/selection/text/kpToolTextBackspaceCommand.cpp b/commands/tools/selection/text/kpToolTextBackspaceCommand.cpp index a5ad238d..5691fe75 100644 --- a/commands/tools/selection/text/kpToolTextBackspaceCommand.cpp +++ b/commands/tools/selection/text/kpToolTextBackspaceCommand.cpp @@ -1,152 +1,150 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_TEXT 1 - #include "kpToolTextBackspaceCommand.h" #include "layers/selections/text/kpTextSelection.h" #include "views/manager/kpViewManager.h" #include kpToolTextBackspaceCommand::kpToolTextBackspaceCommand (const QString &name, int row, int col, Action action, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_row (row), m_col (col), m_numBackspaces (0) { viewManager ()->setTextCursorPosition (m_row, m_col); if (action == AddBackspaceNow) { addBackspace (); } } kpToolTextBackspaceCommand::~kpToolTextBackspaceCommand () = default; // public void kpToolTextBackspaceCommand::addBackspace () { QList textLines = textSelection ()->textLines (); if (m_col > 0) { m_deletedText.prepend (textLines [m_row][m_col - 1]); textLines [m_row] = textLines [m_row].left (m_col - 1) + textLines [m_row].mid (m_col); m_col--; } else { if (m_row > 0) { int newCursorRow = m_row - 1; int newCursorCol = textLines [newCursorRow].length (); m_deletedText.prepend ('\n'); textLines [newCursorRow] += textLines [m_row]; textLines.erase (textLines.begin () + m_row); m_row = newCursorRow; m_col = newCursorCol; } } textSelection ()->setTextLines (textLines); viewManager ()->setTextCursorPosition (m_row, m_col); m_numBackspaces++; } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolTextBackspaceCommand::size () const { return static_cast (static_cast (m_deletedText.length ()) * sizeof (QChar)); } // public virtual [base kpCommand] void kpToolTextBackspaceCommand::execute () { viewManager ()->setTextCursorPosition (m_row, m_col); m_deletedText.clear (); int oldNumBackspaces = m_numBackspaces; m_numBackspaces = 0; for (int i = 0; i < oldNumBackspaces; i++) { addBackspace (); } } // public virtual [base kpCommand] void kpToolTextBackspaceCommand::unexecute () { viewManager ()->setTextCursorPosition (m_row, m_col); QList textLines = textSelection ()->textLines (); for (auto && i : m_deletedText) { if (i == '\n') { const QString rightHalf = textLines [m_row].mid (m_col); textLines [m_row].truncate (m_col); textLines.insert (textLines.begin () + m_row + 1, rightHalf); m_row++; m_col = 0; } else { const QString leftHalf = textLines [m_row].left (m_col); const QString rightHalf = textLines [m_row].mid (m_col); textLines [m_row] = leftHalf + i + rightHalf; m_col++; } } m_deletedText.clear (); textSelection ()->setTextLines (textLines); viewManager ()->setTextCursorPosition (m_row, m_col); } diff --git a/commands/tools/selection/text/kpToolTextChangeStyleCommand.cpp b/commands/tools/selection/text/kpToolTextChangeStyleCommand.cpp index d259e6d2..79791c72 100644 --- a/commands/tools/selection/text/kpToolTextChangeStyleCommand.cpp +++ b/commands/tools/selection/text/kpToolTextChangeStyleCommand.cpp @@ -1,96 +1,90 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_TEXT 1 - #include "kpToolTextChangeStyleCommand.h" #include "environments/commands/kpCommandEnvironment.h" #include "layers/selections/text/kpTextSelection.h" #include "kpLogCategories.h" kpToolTextChangeStyleCommand::kpToolTextChangeStyleCommand (const QString &name, const kpTextStyle &newTextStyle, const kpTextStyle &oldTextStyle, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_newTextStyle (newTextStyle), m_oldTextStyle (oldTextStyle) { } kpToolTextChangeStyleCommand::~kpToolTextChangeStyleCommand () = default; // public virtual [base kpCommand] kpCommandSize::SizeType kpToolTextChangeStyleCommand::size () const { return 0; } // public virtual [base kpCommand] void kpToolTextChangeStyleCommand::execute () { -#if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogCommands) << "kpToolTextChangeStyleCommand::execute()" << " font=" << m_newTextStyle.fontFamily () << " fontSize=" << m_newTextStyle.fontSize () << " isBold=" << m_newTextStyle.isBold () << " isItalic=" << m_newTextStyle.isItalic () << " isUnderline=" << m_newTextStyle.isUnderline () << " isStrikeThru=" << m_newTextStyle.isStrikeThru (); -#endif environ ()->setTextStyle (m_newTextStyle); if (textSelection ()) { textSelection ()->setTextStyle (m_newTextStyle); } } // public virtual [base kpCommand] void kpToolTextChangeStyleCommand::unexecute () { -#if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogCommands) << "kpToolTextChangeStyleCommand::unexecute()" << " font=" << m_newTextStyle.fontFamily () << " fontSize=" << m_newTextStyle.fontSize () << " isBold=" << m_newTextStyle.isBold () << " isItalic=" << m_newTextStyle.isItalic () << " isUnderline=" << m_newTextStyle.isUnderline () << " isStrikeThru=" << m_newTextStyle.isStrikeThru (); -#endif environ ()->setTextStyle (m_oldTextStyle); if (textSelection ()) textSelection ()->setTextStyle (m_oldTextStyle); } diff --git a/commands/tools/selection/text/kpToolTextDeleteCommand.cpp b/commands/tools/selection/text/kpToolTextDeleteCommand.cpp index 985d04d7..9412108c 100644 --- a/commands/tools/selection/text/kpToolTextDeleteCommand.cpp +++ b/commands/tools/selection/text/kpToolTextDeleteCommand.cpp @@ -1,140 +1,138 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_TEXT 1 - #include "kpToolTextDeleteCommand.h" #include "layers/selections/text/kpTextSelection.h" #include "views/manager/kpViewManager.h" #include kpToolTextDeleteCommand::kpToolTextDeleteCommand (const QString &name, int row, int col, Action action, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_row (row), m_col (col), m_numDeletes (0) { viewManager ()->setTextCursorPosition (m_row, m_col); if (action == AddDeleteNow) { addDelete (); } } kpToolTextDeleteCommand::~kpToolTextDeleteCommand () = default; // public void kpToolTextDeleteCommand::addDelete () { QList textLines = textSelection ()->textLines (); if (m_col < static_cast (textLines [m_row].length ())) { m_deletedText.prepend (textLines [m_row][m_col]); textLines [m_row] = textLines [m_row].left (m_col) + textLines [m_row].mid (m_col + 1); } else { if (m_row < static_cast (textLines.size () - 1)) { m_deletedText.prepend ('\n'); textLines [m_row] += textLines [m_row + 1]; textLines.erase (textLines.begin () + m_row + 1); } } textSelection ()->setTextLines (textLines); viewManager ()->setTextCursorPosition (m_row, m_col); m_numDeletes++; } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolTextDeleteCommand::size () const { return static_cast (static_cast (m_deletedText.length ()) * sizeof (QChar)); } // public virtual [base kpCommand] void kpToolTextDeleteCommand::execute () { viewManager ()->setTextCursorPosition (m_row, m_col); m_deletedText.clear (); int oldNumDeletes = m_numDeletes; m_numDeletes = 0; for (int i = 0; i < oldNumDeletes; i++) { addDelete (); } } // public virtual [base kpCommand] void kpToolTextDeleteCommand::unexecute () { viewManager ()->setTextCursorPosition (m_row, m_col); QList textLines = textSelection ()->textLines (); for (auto && i : m_deletedText) { if (i == '\n') { const QString rightHalf = textLines [m_row].mid (m_col); textLines [m_row].truncate (m_col); textLines.insert (textLines.begin () + m_row + 1, rightHalf); } else { const QString leftHalf = textLines [m_row].left (m_col); const QString rightHalf = textLines [m_row].mid (m_col); textLines [m_row] = leftHalf + i + rightHalf; } } m_deletedText.clear (); textSelection ()->setTextLines (textLines); viewManager ()->setTextCursorPosition (m_row, m_col); } diff --git a/commands/tools/selection/text/kpToolTextEnterCommand.cpp b/commands/tools/selection/text/kpToolTextEnterCommand.cpp index 39b3cdbc..78d81f86 100644 --- a/commands/tools/selection/text/kpToolTextEnterCommand.cpp +++ b/commands/tools/selection/text/kpToolTextEnterCommand.cpp @@ -1,126 +1,124 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_TEXT 1 - #include "kpToolTextEnterCommand.h" #include "layers/selections/text/kpTextSelection.h" #include "views/manager/kpViewManager.h" #include kpToolTextEnterCommand::kpToolTextEnterCommand (const QString &name, int row, int col, Action action, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_row (row), m_col (col), m_numEnters (0) { viewManager ()->setTextCursorPosition (m_row, m_col); if (action == AddEnterNow) { addEnter (); } } kpToolTextEnterCommand::~kpToolTextEnterCommand () = default; // public void kpToolTextEnterCommand::addEnter () { QList textLines = textSelection ()->textLines (); const QString rightHalf = textLines [m_row].mid (m_col); textLines [m_row].truncate (m_col); textLines.insert (textLines.begin () + m_row + 1, rightHalf); textSelection ()->setTextLines (textLines); m_row++; m_col = 0; viewManager ()->setTextCursorPosition (m_row, m_col); m_numEnters++; } // public virtual [base kpCommand] kpCommandSize::SizeType kpToolTextEnterCommand::size () const { return 0; } // public virtual [base kpCommand] void kpToolTextEnterCommand::execute () { viewManager ()->setTextCursorPosition (m_row, m_col); int oldNumEnters = m_numEnters; m_numEnters = 0; for (int i = 0; i < oldNumEnters; i++) { addEnter (); } } // public virtual [base kpCommand] void kpToolTextEnterCommand::unexecute () { viewManager ()->setTextCursorPosition (m_row, m_col); QList textLines = textSelection ()->textLines (); for (int i = 0; i < m_numEnters; i++) { Q_ASSERT (m_col == 0); if (m_row <= 0) { break; } int newRow = m_row - 1; int newCol = textLines [newRow].length (); textLines [newRow] += textLines [m_row]; textLines.erase (textLines.begin () + m_row); m_row = newRow; m_col = newCol; } textSelection ()->setTextLines (textLines); viewManager ()->setTextCursorPosition (m_row, m_col); } diff --git a/commands/tools/selection/text/kpToolTextGiveContentCommand.cpp b/commands/tools/selection/text/kpToolTextGiveContentCommand.cpp index ec063c9e..58f522c4 100644 --- a/commands/tools/selection/text/kpToolTextGiveContentCommand.cpp +++ b/commands/tools/selection/text/kpToolTextGiveContentCommand.cpp @@ -1,151 +1,143 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 - #include "kpToolTextGiveContentCommand.h" #include "environments/commands/kpCommandEnvironment.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" kpToolTextGiveContentCommand::kpToolTextGiveContentCommand ( const kpTextSelection &originalSelBorder, const QString &name, kpCommandEnvironment *environ) : kpAbstractSelectionContentCommand (originalSelBorder, name, environ) { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolTextGiveContentCommand::() environ=" << environ; -#endif } kpToolTextGiveContentCommand::~kpToolTextGiveContentCommand () = default; // public virtual [base kpCommand] void kpToolTextGiveContentCommand::execute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolTextGiveContentCommand::execute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); kpViewManager *vm = viewManager (); Q_ASSERT (vm); // See API Doc for kpViewManager::textCursorRow() & textCursorCol(). Q_ASSERT (vm->textCursorRow () == 0 && vm->textCursorCol () == 0); vm->setQueueUpdates (); { // // Recreate border // // The previously executed command is required to have been a // kpToolSelectionCreateCommand, which must have been given a text // selection with no content. // // However, there is a tricky case. Suppose we are called for the first // time, where the above precondition holds. We would add content // to the selection as expected. But the user then undoes (CTRL+Z) the // operation, calling unexecute(). There is now no content again. // Since selection is only a border, the user can freely deselect it // and/or select another region without changing the command history // or document modified state. Therefore, if they now call us again // by redoing (CTRL+Shift+Z), there is potentially no selection at all // or it is at an arbitrary location. // // This assertion covers all 3 possibilities: // // 1. First call: text selection with no content // 2. Later calls: // a) no text selection (due to deselection) // b) text selection with no content, at an arbitrary location Q_ASSERT (!textSelection () || !textSelection ()->hasContent ()); const auto *originalTextSel = dynamic_cast (originalSelection ()); if (originalTextSel->textStyle () != environ ()->textStyle ()) { environ ()->setTextStyle (originalTextSel->textStyle ()); } doc->setSelection (*originalSelection ()); // // Add Content // QList listOfOneEmptyString; listOfOneEmptyString.append (QString ()); textSelection ()->setTextLines (listOfOneEmptyString); } vm->restoreQueueUpdates (); // This should not have changed from the start of the method. Q_ASSERT (vm->textCursorRow () == 0 && vm->textCursorCol () == 0); } // public virtual [base kpCommand] void kpToolTextGiveContentCommand::unexecute () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogCommands) << "kpToolTextGiveContentCommand::unexecute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); // Must have selection text content. Q_ASSERT (doc->textSelection () && doc->textSelection ()->hasContent ()); kpViewManager *vm = viewManager (); Q_ASSERT (vm); // All the commands after us have been unexecuted, so we must be back // to the state we were after our execute(). Q_ASSERT (vm->textCursorRow () == 0 && vm->textCursorCol () == 0); // We can have faith that this is the state of the selection after // execute(), rather than after the user tried to throw us off by // simply selecting another region as to do that, a destroy command // must have been used. doc->textSelection ()->deleteContent (); // This should not have changed from the start of the method. Q_ASSERT (vm->textCursorRow () == 0 && vm->textCursorCol () == 0); } diff --git a/commands/tools/selection/text/kpToolTextInsertCommand.cpp b/commands/tools/selection/text/kpToolTextInsertCommand.cpp index 8992723f..ce7e789a 100644 --- a/commands/tools/selection/text/kpToolTextInsertCommand.cpp +++ b/commands/tools/selection/text/kpToolTextInsertCommand.cpp @@ -1,110 +1,108 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_TEXT 1 - #include "kpToolTextInsertCommand.h" #include "layers/selections/text/kpTextSelection.h" #include "views/manager/kpViewManager.h" #include //--------------------------------------------------------------------- kpToolTextInsertCommand::kpToolTextInsertCommand (const QString &name, int row, int col, const QString& newText, kpCommandEnvironment *environ) : kpNamedCommand (name, environ), m_row (row), m_col (col) { viewManager ()->setTextCursorPosition (m_row, m_col); addText (newText); } //--------------------------------------------------------------------- // public void kpToolTextInsertCommand::addText (const QString &moreText) { if (moreText.isEmpty ()) { return; } QList textLines = textSelection ()->textLines (); const QString leftHalf = textLines [m_row].left (m_col); const QString rightHalf = textLines [m_row].mid (m_col); textLines [m_row] = leftHalf + moreText + rightHalf; textSelection ()->setTextLines (textLines); m_newText += moreText; m_col += moreText.length (); viewManager ()->setTextCursorPosition (m_row, m_col); } //--------------------------------------------------------------------- // public virtual [base kpCommand] kpCommandSize::SizeType kpToolTextInsertCommand::size () const { return static_cast (static_cast (m_newText.length ()) * sizeof (QChar)); } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpToolTextInsertCommand::execute () { viewManager ()->setTextCursorPosition (m_row, m_col); QString text = m_newText; m_newText.clear (); addText (text); } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpToolTextInsertCommand::unexecute () { viewManager ()->setTextCursorPosition (m_row, m_col); QList textLines = textSelection ()->textLines (); const QString leftHalf = textLines [m_row].left (m_col - m_newText.length ()); const QString rightHalf = textLines [m_row].mid (m_col); textLines [m_row] = leftHalf + rightHalf; textSelection ()->setTextLines (textLines); m_col -= m_newText.length (); viewManager ()->setTextCursorPosition (m_row, m_col); } //--------------------------------------------------------------------- diff --git a/cursors/kpCursorLightCross.cpp b/cursors/kpCursorLightCross.cpp index 02992617..a01c488e 100644 --- a/cursors/kpCursorLightCross.cpp +++ b/cursors/kpCursorLightCross.cpp @@ -1,131 +1,128 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_CURSOR_LIGHT_CROSS 1 #include "kpCursorLightCross.h" #include "kpLogCategories.h" #include #include enum PixelValue { White, Black, Transparent }; static void setPixel (unsigned char *colorBitmap, unsigned char *maskBitmap, int width, int y, int x, enum PixelValue pv) { const int ColorBlack = 1; const int ColorWhite = 0; const int MaskOpaque = 1; const int MaskTransparent = 0; int colorValue, maskValue; switch (pv) { case White: colorValue = ColorWhite; maskValue = MaskOpaque; break; case Black: colorValue = ColorBlack; maskValue = MaskOpaque; break; case Transparent: colorValue = ColorWhite; maskValue = MaskTransparent; break; } if (colorValue) { colorBitmap [y * (width / 8) + (x / 8)] |= (1 << (x % 8)); } if (maskValue) { maskBitmap [y * (width / 8) + (x / 8)] |= (1 << (x % 8)); } } const QCursor *kpCursorLightCrossCreate () { -#if DEBUG_KP_CURSOR_LIGHT_CROSS qCDebug(kpLogMisc) << "kpCursorLightCrossCreate() "; -#endif const int side = 24; const int byteSize = (side * side) / 8; auto *colorBitmap = new unsigned char [byteSize]; auto *maskBitmap = new unsigned char [byteSize]; memset (colorBitmap, 0, byteSize); memset (maskBitmap, 0, byteSize); const int oddSide = side - 1; const int strokeLen = oddSide * 3 / 8; for (int i = 0; i < strokeLen; i++) { const enum PixelValue pv = (i % 2) ? Black : White; #define X_(val) (val) #define Y_(val) (val) #define DRAW(y,x) setPixel (colorBitmap, maskBitmap, side, (y), (x), pv) // horizontal DRAW (Y_(side / 2), X_(1 + i)); DRAW (Y_(side / 2), X_(side - 1 - i)); // vertical DRAW (Y_(1 + i), X_(side / 2)); DRAW (Y_(side - 1 - i), X_(side / 2)); #undef DRAW #undef Y_ #undef X_ } const QSize size (side, side); QCursor *cursor = new QCursor ( QBitmap::fromData (size, colorBitmap, QImage::Format_MonoLSB), QBitmap::fromData (size, maskBitmap, QImage::Format_MonoLSB)); delete [] maskBitmap; delete [] colorBitmap; return cursor; } diff --git a/dialogs/imagelib/effects/kpEffectsDialog.cpp b/dialogs/imagelib/effects/kpEffectsDialog.cpp index 88e2ea2b..07f72644 100644 --- a/dialogs/imagelib/effects/kpEffectsDialog.cpp +++ b/dialogs/imagelib/effects/kpEffectsDialog.cpp @@ -1,372 +1,346 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECTS_DIALOG 1 - #include "kpEffectsDialog.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "widgets/imagelib/effects/kpEffectBalanceWidget.h" #include "widgets/imagelib/effects/kpEffectBlurSharpenWidget.h" #include "widgets/imagelib/effects/kpEffectEmbossWidget.h" #include "widgets/imagelib/effects/kpEffectFlattenWidget.h" #include "widgets/imagelib/effects/kpEffectHSVWidget.h" #include "widgets/imagelib/effects/kpEffectInvertWidget.h" #include "widgets/imagelib/effects/kpEffectReduceColorsWidget.h" #include "widgets/imagelib/effects/kpEffectToneEnhanceWidget.h" #include "pixmapfx/kpPixmapFX.h" #include "environments/dialogs/imagelib/transforms/kpTransformDialogEnvironment.h" #include #include "kpLogCategories.h" #include #include #include #include #include #include #include // protected static int kpEffectsDialog::s_lastWidth = 640; int kpEffectsDialog::s_lastHeight = 620; kpEffectsDialog::kpEffectsDialog (bool actOnSelection, kpTransformDialogEnvironment *_env, QWidget *parent, int defaultSelectedEffect) : kpTransformPreviewDialog (kpTransformPreviewDialog::Preview, true/*reserve top row*/, QString()/*caption*/, QString()/*afterActionText (no Dimensions Group Box)*/, actOnSelection, _env, parent), m_delayedUpdateTimer (new QTimer (this)), m_effectsComboBox (nullptr), m_settingsGroupBox (nullptr), m_settingsLayout (nullptr), m_effectWidget (nullptr) { -#if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "kpEffectsDialog::kpEffectsDialog()"; -#endif + const bool e = updatesEnabled (); setUpdatesEnabled (false); if (actOnSelection) { setWindowTitle (i18nc ("@title:window", "More Image Effects (Selection)")); } else { setWindowTitle (i18nc ("@title:window", "More Image Effects")); } m_delayedUpdateTimer->setSingleShot (true); connect (m_delayedUpdateTimer, &QTimer::timeout, this, &kpEffectsDialog::slotUpdateWithWaitCursor); QWidget *effectContainer = new QWidget (mainWidget ()); auto *containerLayout = new QHBoxLayout (effectContainer); containerLayout->setContentsMargins(0, 0, 0, 0); QLabel *label = new QLabel (i18n ("&Effect:"), effectContainer); m_effectsComboBox = new QComboBox (effectContainer); // Keep in alphabetical order. // TODO: What about translations? // sync: order in selectEffect(). m_effectsComboBox->addItem (i18n ("Balance")); m_effectsComboBox->addItem (i18n ("Emboss")); m_effectsComboBox->addItem (i18n ("Flatten")); m_effectsComboBox->addItem (i18n ("Histogram Equalizer")); m_effectsComboBox->addItem (i18n ("Hue, Saturation, Value")); m_effectsComboBox->addItem (i18n ("Invert")); m_effectsComboBox->addItem (i18n ("Reduce Colors")); m_effectsComboBox->addItem (i18n ("Soften & Sharpen")); containerLayout->addWidget (label); containerLayout->addWidget (m_effectsComboBox, 1); label->setBuddy (m_effectsComboBox); addCustomWidgetToFront (effectContainer); m_settingsGroupBox = new QGroupBox (mainWidget ()); m_settingsLayout = new QVBoxLayout ( m_settingsGroupBox ); addCustomWidgetToBack (m_settingsGroupBox); connect (m_effectsComboBox, static_cast(&QComboBox::activated), this, &kpEffectsDialog::selectEffect); selectEffect (defaultSelectedEffect); resize (s_lastWidth, s_lastHeight); -#if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "about to setUpdatesEnabled()"; -#endif + // OPT: The preview pixmap gets recalculated here and then possibly // again when QResizeEvent fires, when the dialog is shown. setUpdatesEnabled (e); -#if DEBUG_KP_EFFECTS_DIALOG + qCDebug(kpLogDialogs) << endl << endl << endl; -#endif } kpEffectsDialog::~kpEffectsDialog () { s_lastWidth = width (); s_lastHeight = height (); } // public virtual [base kpTransformPreviewDialog] bool kpEffectsDialog::isNoOp () const { if (!m_effectWidget) { return true; } return m_effectWidget->isNoOp (); } // public kpEffectCommandBase *kpEffectsDialog::createCommand () const { if (!m_effectWidget) { return nullptr; } return m_effectWidget->createCommand (m_environ->commandEnvironment ()); } // protected virtual [base kpTransformPreviewDialog] QSize kpEffectsDialog::newDimensions () const { kpDocument *doc = document (); if (!doc) { return {}; } return {doc->width (m_actOnSelection), doc->height (m_actOnSelection)}; } // protected virtual [base kpTransformPreviewDialog] QImage kpEffectsDialog::transformPixmap (const QImage &pixmap, int targetWidth, int targetHeight) const { QImage pixmapWithEffect; if (m_effectWidget && !m_effectWidget->isNoOp ()) { pixmapWithEffect = m_effectWidget->applyEffect (pixmap); } else { pixmapWithEffect = pixmap; } return kpPixmapFX::scale (pixmapWithEffect, targetWidth, targetHeight); } // public int kpEffectsDialog::selectedEffect () const { return m_effectsComboBox->currentIndex (); } // public slot void kpEffectsDialog::selectEffect (int which) { -#if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "kpEffectsDialog::selectEffect(" << which << ")"; -#endif if (which < 0 || which >= m_effectsComboBox->count ()) { return; } if (which != m_effectsComboBox->currentIndex ()) { m_effectsComboBox->setCurrentIndex (which); } delete m_effectWidget; m_effectWidget = nullptr; m_settingsGroupBox->setWindowTitle(QString()); #define CREATE_EFFECT_WIDGET(name) \ m_effectWidget = new name (m_actOnSelection, m_settingsGroupBox) // sync: order in constructor. switch (which) { case 0: CREATE_EFFECT_WIDGET (kpEffectBalanceWidget); break; case 1: CREATE_EFFECT_WIDGET (kpEffectEmbossWidget); break; case 2: CREATE_EFFECT_WIDGET (kpEffectFlattenWidget); break; case 3: CREATE_EFFECT_WIDGET (kpEffectToneEnhanceWidget); break; case 4: CREATE_EFFECT_WIDGET (kpEffectHSVWidget); break; case 5: CREATE_EFFECT_WIDGET (kpEffectInvertWidget); break; case 6: CREATE_EFFECT_WIDGET (kpEffectReduceColorsWidget); break; case 7: CREATE_EFFECT_WIDGET (kpEffectBlurSharpenWidget); break; } #undef CREATE_EFFECT_WIDGET if (m_effectWidget) { const bool e = updatesEnabled (); setUpdatesEnabled (false); - #if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "widget exists for effect #"; - #endif m_settingsGroupBox->setTitle (m_effectWidget->caption ()); // Show widget. // // Don't resize the whole dialog when doing this. // This seems to work magically without any extra code with Qt4. - #if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "addWidget"; - #endif m_settingsLayout->addWidget (m_effectWidget); - #if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "show widget"; - #endif + m_effectWidget->show (); connect (m_effectWidget, &kpEffectWidgetBase::settingsChangedNoWaitCursor, this, &kpEffectsDialog::slotUpdate); connect (m_effectWidget, &kpEffectWidgetBase::settingsChanged, this, &kpEffectsDialog::slotUpdateWithWaitCursor); connect (m_effectWidget, &kpEffectWidgetBase::settingsChangedDelayed, this, &kpEffectsDialog::slotDelayedUpdate); - #if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "about to setUpdatesEnabled()"; - #endif setUpdatesEnabled (e); } -#if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "done" - << endl - << endl << endl; -#endif } // protected slot virtual [base kpTransformPreviewDialog] void kpEffectsDialog::slotUpdate () { -#if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "kpEffectsDialog::slotUpdate()" - << " timerActive=" << m_delayedUpdateTimer->isActive () - << endl; -#endif + << " timerActive=" << m_delayedUpdateTimer->isActive (); m_delayedUpdateTimer->stop (); kpTransformPreviewDialog::slotUpdate (); } // protected slot virtual [base kpTransformPreviewDialog] void kpEffectsDialog::slotUpdateWithWaitCursor () { -#if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "kpEffectsDialog::slotUpdateWithWaitCursor()" - << " timerActive=" << m_delayedUpdateTimer->isActive () - << endl; -#endif + << " timerActive=" << m_delayedUpdateTimer->isActive (); m_delayedUpdateTimer->stop (); kpTransformPreviewDialog::slotUpdateWithWaitCursor (); } // protected slot void kpEffectsDialog::slotDelayedUpdate () { -#if DEBUG_KP_EFFECTS_DIALOG qCDebug(kpLogDialogs) << "kpEffectsDialog::slotDelayedUpdate()" - << " timerActive=" << m_delayedUpdateTimer->isActive () - << endl; -#endif + << " timerActive=" << m_delayedUpdateTimer->isActive (); + m_delayedUpdateTimer->stop (); // (single shot) m_delayedUpdateTimer->start (400/*ms*/); } diff --git a/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp b/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp index 41115c96..bb43ae58 100644 --- a/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp +++ b/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp @@ -1,753 +1,721 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT_META_INFO_DIALOG 1 - #include "kpDocumentMetaInfoDialog.h" #include "kpDefs.h" #include "imagelib/kpDocumentMetaInfo.h" #include #include #include "kpLogCategories.h" #include #include #include #include #include #include #include #include #include #include struct kpDocumentMetaInfoDialogPrivate { const kpDocumentMetaInfo *originalMetaInfoPtr; QDoubleSpinBox *horizDpiInput, *vertDpiInput; QSpinBox *horizOffsetInput, *vertOffsetInput; QTableWidget *fieldsTableWidget; QPushButton *fieldsAddRowButton, *fieldsDeleteRowButton, *fieldsResetButton; }; // (shared by all dialogs, across all main windows, in a KolourPaint instance) static int LastWidth = -1, LastHeight = -1; // sync: You must keep DpiMinStep = 10 ^ (-DpiPrecision). // // You can increase the precision to reduce the chance of inadvertently changing // the resolution when converting from kpDocumentMetaInfo's "dots per meter" // to our "dots per inch" and back. It would be bad if simply going into this // dialog and pressing OK changed the resolution (it's unlikely but I still think // it might happen with the current precision). // TODO: On a related note, for many particular resolutions, if the user enters // one of them into the UI, presses OK and then comes back to the dialog, // s/he is presented with a different resolution to the one typed in. // Maybe make DotsPerMeter[XY] be of type "double" instead of "int" to // solve this problem? // // Of course, if you increase the precision too much, the minimum step will // become so small that it will start experiencing floating point inaccuracies // esp. since we use it for the "DpiUnspecified" hack. static const int DpiPrecision = 3; static const double DpiMinStep = 0.001; static const double DpiLegalMin = kpDocumentMetaInfo::MinDotsPerMeter / KP_INCHES_PER_METER; // Out of range represents unspecified DPI. static const double DpiUnspecified = ::DpiLegalMin - ::DpiMinStep; static const double DpiInputMin = ::DpiUnspecified; static const double DpiInputMax = kpDocumentMetaInfo::MaxDotsPerMeter / KP_INCHES_PER_METER; // The increment the DPI spinboxes jump by when they're clicked. // // We make this relatively big since people don't usually just increase their // DPIs by 1 or so -- they are usually changing from say, 72, to 96. // // Obviously, making it equal to DpiMinStep is too slow a UI. Therefore, with // our big setting, the user will still have to manually change the value in // the spinbox, using the keyboard, after all their clicking to ensure it is // exactly the value they want. static const double DpiInputStep = 10; // TODO: Halve groupbox layout margins in every other file since it doesn't // seem to be need in Qt4. kpDocumentMetaInfoDialog::kpDocumentMetaInfoDialog ( const kpDocumentMetaInfo *docMetaInfo, QWidget *parent) : QDialog (parent), d (new kpDocumentMetaInfoDialogPrivate ()) { d->originalMetaInfoPtr = docMetaInfo; setWindowTitle (i18nc ("@title:window", "Document Properties")); auto * buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect (buttons, &QDialogButtonBox::accepted, this, &kpDocumentMetaInfoDialog::accept); connect (buttons, &QDialogButtonBox::rejected, this, &kpDocumentMetaInfoDialog::reject); auto *baseWidget = new QWidget (this); auto *dialogLayout = new QVBoxLayout (this); dialogLayout->addWidget (baseWidget); dialogLayout->addWidget (buttons); // // DPI Group Box // Q_ASSERT (::DpiInputMin < ::DpiInputMax); auto *dpiGroupBox = new QGroupBox(i18n("Dots &Per Inch (DPI)"), baseWidget); d->horizDpiInput = new QDoubleSpinBox(dpiGroupBox); d->horizDpiInput->setRange(::DpiInputMin, ::DpiInputMax); d->horizDpiInput->setValue(0.0); d->horizDpiInput->setSingleStep(::DpiInputStep); d->horizDpiInput->setDecimals(::DpiPrecision); d->horizDpiInput->setSpecialValueText(i18n("Unspecified")); auto *dpiXLabel = new QLabel ( i18nc ("Horizontal DPI 'x' Vertical DPI", " x "), dpiGroupBox); dpiXLabel->setAlignment (Qt::AlignCenter); d->vertDpiInput = new QDoubleSpinBox(dpiGroupBox); d->vertDpiInput->setRange(::DpiInputMin, ::DpiInputMax); d->vertDpiInput->setValue(0.0); d->vertDpiInput->setSingleStep(::DpiInputStep); d->vertDpiInput->setDecimals(::DpiPrecision); d->vertDpiInput->setSpecialValueText(i18n("Unspecified")); auto *dpiLay = new QGridLayout(dpiGroupBox); dpiLay->addWidget(new QLabel(i18n("Horizontal:")), 0, 0, Qt::AlignHCenter); dpiLay->addWidget(d->horizDpiInput, 1, 0); dpiLay->addWidget(dpiXLabel, 0, 1); dpiLay->addWidget(new QLabel(i18n("Vertical:")), 0, 2, Qt::AlignHCenter); dpiLay->addWidget(d->vertDpiInput, 1, 2); dpiLay->setRowStretch(2, 1); dpiGroupBox->setWhatsThis ( i18n ( "" "

Dots Per Inch (DPI) specifies the number of pixels" " of the image that should be printed inside one inch (2.54cm).

" "

The higher the image's DPI, the smaller the printed image." " Note that your printer is unlikely to produce high" " quality prints if you increase this to more than 300 or 600 DPI," " depending on the printer.

" "

If you would like to print the image so that it is the same" " size as it is displayed on the screen, set the image's DPI" " values to be the same as the screen's.

" // TODO: This is currently not true! // See "96dpi" TODO in kpMainWindow::sendPixmapToPrinter(). // This also why we don't try to report the current screen DPI // for the above paragraph. "

If either DPI value is Unspecified, the image will also" " be printed to be the same size as on the screen.

" "

Not all image formats support DPI values. If the format you" " save in does not support them, they will not be saved.

" "
" )); // // Offset Group Box // auto *offsetGroupBox = new QGroupBox(i18n ("O&ffset"), baseWidget); d->horizOffsetInput = new QSpinBox; d->horizOffsetInput->setRange(kpDocumentMetaInfo::MinOffset, kpDocumentMetaInfo::MaxOffset); d->vertOffsetInput = new QSpinBox; d->vertOffsetInput->setRange(kpDocumentMetaInfo::MinOffset, kpDocumentMetaInfo::MaxOffset); auto *offsetLay = new QGridLayout(offsetGroupBox); offsetLay->addWidget(new QLabel(i18n("Horizontal:")), 0, 0, Qt::AlignHCenter); offsetLay->addWidget(d->horizOffsetInput, 1, 0); offsetLay->addWidget(new QLabel(i18n("Vertical:")), 0, 1, Qt::AlignHCenter); offsetLay->addWidget(d->vertOffsetInput, 1, 1); offsetLay->setRowStretch (2, 1); offsetGroupBox->setWhatsThis ( i18n ( "" "

The Offset is the relative position where this image" " should be placed, compared to other images.

" "

Not all image formats support the Offset feature." " If the format you save in does not support it, the values" " specified here will not be saved.

" "
" )); // // Fields Group Box // auto *fieldsGroupBox = new QGroupBox (i18n ("&Text Fields"), baseWidget); d->fieldsTableWidget = new QTableWidget (fieldsGroupBox); d->fieldsTableWidget->setEditTriggers(QAbstractItemView::AllEditTriggers); connect (d->fieldsTableWidget, &QTableWidget::currentCellChanged, this, &kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged); connect (d->fieldsTableWidget, &QTableWidget::itemChanged, this, &kpDocumentMetaInfoDialog::slotFieldsItemChanged); d->fieldsAddRowButton = new QPushButton (i18n ("&Add Row"), fieldsGroupBox); connect (d->fieldsAddRowButton, &QPushButton::clicked, this, &kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked); d->fieldsDeleteRowButton = new QPushButton (i18n ("&Delete Row"), fieldsGroupBox); connect (d->fieldsDeleteRowButton, &QPushButton::clicked, this, &kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked); d->fieldsResetButton = new QPushButton (i18n ("&Reset"), fieldsGroupBox); connect (d->fieldsResetButton, &QPushButton::clicked, this, &kpDocumentMetaInfoDialog::setUIToOriginalMetaInfo); auto *fieldsButtonsLayout = new QHBoxLayout (); fieldsButtonsLayout->addWidget (d->fieldsAddRowButton); fieldsButtonsLayout->addWidget (d->fieldsDeleteRowButton); fieldsButtonsLayout->addStretch (); fieldsButtonsLayout->addWidget (d->fieldsResetButton); auto *fieldsLayout = new QVBoxLayout (fieldsGroupBox); fieldsLayout->addWidget (d->fieldsTableWidget); fieldsLayout->addLayout (fieldsButtonsLayout); fieldsGroupBox->setWhatsThis ( i18n ( "" "

Text Fields provide extra information about the image." " This is probably a comment area that you can freely write any text in.

" "

However, this is format-specific so the fields could theoretically be" " computer-interpreted data - that you should not modify -" " but this is unlikely.

" "

Not all image formats support Text Fields. If the format" " you save in does not support them, they will not be saved.

" "
" )); // // Global Layout // auto *baseLayout = new QGridLayout (baseWidget); baseLayout->setContentsMargins(0, 0, 0, 0); // Col 0 baseLayout->addWidget (dpiGroupBox, 0, 0); baseLayout->addWidget (offsetGroupBox, 1, 0); // Col 1 baseLayout->addWidget (fieldsGroupBox, 0, 1, 2/*row span*/, 1/*col span*/); baseLayout->setColumnStretch (1, 1/*stretch*/); // // Remaining UI Setup // setUIToOriginalMetaInfo (); if (::LastWidth > 0 && ::LastHeight > 0) { resize (::LastWidth, ::LastHeight); } } //--------------------------------------------------------------------- kpDocumentMetaInfoDialog::~kpDocumentMetaInfoDialog () { ::LastWidth = width (); ::LastHeight = height (); delete d; } //--------------------------------------------------------------------- // private void kpDocumentMetaInfoDialog::editCell (int r, int c) { d->fieldsTableWidget->setCurrentCell (r, c); d->fieldsTableWidget->editItem (d->fieldsTableWidget->item (r, c)); } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::setUIToOriginalMetaInfo () { // Set DPI spinboxes. d->horizDpiInput->setValue (d->originalMetaInfoPtr->dotsPerMeterX () / KP_INCHES_PER_METER); d->vertDpiInput->setValue (d->originalMetaInfoPtr->dotsPerMeterY () / KP_INCHES_PER_METER); // Set Offset spinboxes. d->horizOffsetInput->setValue (d->originalMetaInfoPtr->offset ().x ()); d->vertOffsetInput->setValue (d->originalMetaInfoPtr->offset ().y ()); // Set Text Fields. // // Block itemChanged() signal as slotFieldsItemChanged() should not get called // when rows are half-created. const bool b = d->fieldsTableWidget->blockSignals (true); { d->fieldsTableWidget->clear (); d->fieldsTableWidget->setRowCount (d->originalMetaInfoPtr->textKeys ().size ()); d->fieldsTableWidget->setColumnCount (2); QStringList fieldsHeader; fieldsHeader << i18n ("Key") << i18n ("Value"); d->fieldsTableWidget->setHorizontalHeaderLabels (fieldsHeader); int row = 0; for (const auto &key : d->originalMetaInfoPtr->textKeys ()) { d->fieldsTableWidget->setItem (row, 0/*1st col*/, new QTableWidgetItem (key)); d->fieldsTableWidget->setItem (row, 1/*2nd col*/, new QTableWidgetItem (d->originalMetaInfoPtr->text (key))); row++; } fieldsAppendEmptyRow (); } d->fieldsTableWidget->blockSignals (b); editCell (0/*row*/, 0/*col*/); enableFieldsDeleteRowButtonIfShould (); } //--------------------------------------------------------------------- // public bool kpDocumentMetaInfoDialog::isNoOp () const { return (metaInfo () == *d->originalMetaInfoPtr); } //--------------------------------------------------------------------- // public kpDocumentMetaInfo kpDocumentMetaInfoDialog::originalMetaInfo () const { return *d->originalMetaInfoPtr; } // public kpDocumentMetaInfo kpDocumentMetaInfoDialog::metaInfo ( QString *errorMessage) const { if (errorMessage) { // No errors to start with. *errorMessage = QString (); } kpDocumentMetaInfo ret; if (d->horizDpiInput->value () < ::DpiLegalMin) { ret.setDotsPerMeterX (0/*unspecified*/); } else { ret.setDotsPerMeterX (qRound (d->horizDpiInput->value () * KP_INCHES_PER_METER)); } if (d->vertDpiInput->value () < ::DpiLegalMin) { ret.setDotsPerMeterY (0/*unspecified*/); } else { ret.setDotsPerMeterY (qRound (d->vertDpiInput->value () * KP_INCHES_PER_METER)); } ret.setOffset (QPoint (d->horizOffsetInput->value (), d->vertOffsetInput->value ())); for (int r = 0; r < d->fieldsTableWidget->rowCount (); r++) { const QString key = d->fieldsTableWidget->item (r, 0)->text (); const QString value = d->fieldsTableWidget->item (r, 1)->text (); // Empty key? if (key.isEmpty ()) { // Empty value too? if (value.isEmpty ()) { // Ignore empty row. continue; } // Value without a key? if (errorMessage) { *errorMessage = ki18n ("The text value \"%1\" on line %2 requires a key.") .subs (value).subs (r + 1/*count from 1*/).toString (); // Print only 1 error message per method invocation. errorMessage = nullptr; } // Ignore. continue; } // Duplicate key? if (ret.textKeys ().contains (key)) { if (errorMessage) { int q; for (q = 0; q < r; q++) { if (d->fieldsTableWidget->item (q, 0)->text () == key) { break; } } Q_ASSERT (q != r); *errorMessage = ki18n ("All text keys must be unique. The text key \"%1\"" " on lines %2 and %3 are identical.") .subs (key) .subs (q + 1/*count from 1*/) .subs (r + 1/*count from 1*/) .toString (); // Print only 1 error message per method invocation. errorMessage = nullptr; } // Ignore this duplicate - keep the first value of the key. continue; } ret.setText (key, value); } // for (r = 0; r < table widget rows; r++) { return ret; } // private void kpDocumentMetaInfoDialog::fieldsUpdateVerticalHeader () { QStringList vertLabels; for (int r = 1; r <= d->fieldsTableWidget->rowCount (); r++) { vertLabels << QString::number (r); } d->fieldsTableWidget->setVerticalHeaderLabels (vertLabels); } // private void kpDocumentMetaInfoDialog::fieldsAddEmptyRow (int atRow) { // Block itemChanged() signal as slotFieldsItemChanged() should not get called // when rows are half-created. const bool b = d->fieldsTableWidget->blockSignals (true); { d->fieldsTableWidget->insertRow (atRow); d->fieldsTableWidget->setItem (atRow, 0, new QTableWidgetItem (QString ())); d->fieldsTableWidget->setItem (atRow, 1, new QTableWidgetItem (QString ())); } d->fieldsTableWidget->blockSignals (b); // Hack around Qt's failure to redraw these sometimes. fieldsUpdateVerticalHeader (); enableFieldsDeleteRowButtonIfShould (); } // private void kpDocumentMetaInfoDialog::fieldsAppendEmptyRow () { fieldsAddEmptyRow (d->fieldsTableWidget->rowCount ()); } // private bool kpDocumentMetaInfoDialog::isFieldsRowDeleteable (int row) const { // Can't delete no row and can't delete last (always blank) row, which // is used to make it easy for the user to add rows without pressing // the "Add" button explicitly. return (row >= 0 && row < d->fieldsTableWidget->rowCount () - 1); } // private void kpDocumentMetaInfoDialog::fieldsDeleteRow (int r) { -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::fieldsDeleteRow(" << "row=" << r << ")" << " currentRow=" << d->fieldsTableWidget->currentRow (); -#endif Q_ASSERT (isFieldsRowDeleteable (r)); if (r == d->fieldsTableWidget->currentRow ()) { // Assertion follows from previous assertion. const int newRow = r + 1; - #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tnewRow=" << newRow; - #endif Q_ASSERT (newRow < d->fieldsTableWidget->rowCount ()); int newCol = d->fieldsTableWidget->currentColumn (); - #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tnewCol=" << newCol; - #endif if (newCol != 0 && newCol != 1) { newCol = 0; - #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\t\tcorrecting to " << newCol; - #endif } // WARNING: You must call this _before_ deleting the row. Else, you'll // trigger a Qt bug where if the editor is active on the row to // be deleted, it might crash. To reproduce, move this line to // after removeRow() (and subtract 1 from newRow) and // press the "Delete Row" button several times in succession // very quickly. // // TODO: This usually results in a redraw error if the scrollbar scrolls // after deleting the 2nd last row. Qt bug. editCell (newRow, newCol); } d->fieldsTableWidget->removeRow (r); fieldsUpdateVerticalHeader (); enableFieldsDeleteRowButtonIfShould (); } // private void kpDocumentMetaInfoDialog::enableFieldsDeleteRowButtonIfShould () { -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::enableFieldsDeleteRowButtonIfShould()"; -#endif const int r = d->fieldsTableWidget->currentRow (); -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; -#endif d->fieldsDeleteRowButton->setEnabled (isFieldsRowDeleteable (r)); } // private slot void kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged (int row, int col, int oldRow, int oldCol) { -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged(" << "row=" << row << ",col=" << col << ",oldRow=" << oldRow << ",oldCol=" << oldCol << ")" << endl; -#endif (void) row; (void) col; (void) oldRow; (void) oldCol; enableFieldsDeleteRowButtonIfShould (); } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::slotFieldsItemChanged (QTableWidgetItem *it) { -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsItemChanged(" << "item=" << it << ") rows=" << d->fieldsTableWidget->rowCount (); -#endif const int r = d->fieldsTableWidget->row (it); -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; -#endif Q_ASSERT (r >= 0 && r < d->fieldsTableWidget->rowCount ()); const QString key = d->fieldsTableWidget->item (r, 0)->text (); -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << " key='" << key << "'"; -#endif const QString value = d->fieldsTableWidget->item (r, 1)->text (); -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << " value='" << value << "'"; -#endif // At the last row? if (r == d->fieldsTableWidget->rowCount () - 1) { // Typed some text? if (!key.isEmpty () || !value.isEmpty ()) { // LOTODO: If we're called due to the cell's text being finalized // as a result of the user pressing the "Add Row" button, // should this really append a row since // slotFieldsAddRowButtonClicked() button is going to add // another one? That's two rows when the user only clicked // that button once! fieldsAppendEmptyRow (); } } } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked () { -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked()"; -#endif const int r = d->fieldsTableWidget->currentRow (); -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; -#endif // (if no row is selected, r = -1) fieldsAddEmptyRow (r + 1); // Edit the key of this new row (column 0). // No one edits the value first (column 1). editCell ((r + 1)/*row*/, 0/*col*/); } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked () { -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked()"; -#endif const int r = d->fieldsTableWidget->currentRow (); -#if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; -#endif Q_ASSERT (isFieldsRowDeleteable (r)); fieldsDeleteRow (r); } // private slot virtual [base QDialog] void kpDocumentMetaInfoDialog::accept () { // Validate text fields. QString errorMessage; (void) metaInfo (&errorMessage); if (!errorMessage.isEmpty ()) { KMessageBox::sorry (this, errorMessage, i18nc ("@title:window", "Invalid Text Fields")); return; } QDialog::accept (); } diff --git a/dialogs/imagelib/transforms/kpTransformPreviewDialog.cpp b/dialogs/imagelib/transforms/kpTransformPreviewDialog.cpp index 6014bb2d..7a097b9e 100644 --- a/dialogs/imagelib/transforms/kpTransformPreviewDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformPreviewDialog.cpp @@ -1,468 +1,442 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TRANSFORM_PREVIEW_DIALOG 1 - - #include "dialogs/imagelib/transforms/kpTransformPreviewDialog.h" #include #include #include #include #include #include #include #include #include #include "kpLogCategories.h" #include #include "layers/selections/image/kpAbstractImageSelection.h" #include "imagelib/kpColor.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "generic/widgets/kpResizeSignallingLabel.h" #include "environments/dialogs/imagelib/transforms/kpTransformDialogEnvironment.h" kpTransformPreviewDialog::kpTransformPreviewDialog (Features features, bool reserveTopRow, const QString &caption, const QString &afterActionText, bool actOnSelection, kpTransformDialogEnvironment *_env, QWidget *parent) : QDialog (parent), m_afterActionText (afterActionText), m_actOnSelection (actOnSelection), m_dimensionsGroupBox (nullptr), m_afterTransformDimensionsLabel (nullptr), m_previewGroupBox (nullptr), m_previewPixmapLabel (nullptr), m_gridLayout (nullptr), m_environ (_env) { setWindowTitle (caption); QDialogButtonBox *buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect (buttons, &QDialogButtonBox::accepted, this, &kpTransformPreviewDialog::accept); connect (buttons, &QDialogButtonBox::rejected, this, &kpTransformPreviewDialog::reject); QWidget *baseWidget = new QWidget (this); m_mainWidget = baseWidget; auto *dialogLayout = new QVBoxLayout (this); dialogLayout->addWidget (baseWidget); dialogLayout->addWidget (buttons); if (document ()) { m_oldWidth = document ()->width (actOnSelection); m_oldHeight = document ()->height (actOnSelection); } else { m_oldWidth = m_oldHeight = 1; } if (features & Dimensions) { createDimensionsGroupBox (); } if (features & Preview) { createPreviewGroupBox (); } m_gridLayout = new QGridLayout (baseWidget ); m_gridLayout->setContentsMargins(0, 0, 0, 0); m_gridNumRows = reserveTopRow ? 1 : 0; if (m_dimensionsGroupBox || m_previewGroupBox) { if (m_dimensionsGroupBox && m_previewGroupBox) { m_gridLayout->addWidget (m_dimensionsGroupBox, m_gridNumRows, 0); m_gridLayout->addWidget (m_previewGroupBox, m_gridNumRows, 1); m_gridLayout->setColumnStretch (1, 1); } else if (m_dimensionsGroupBox) { m_gridLayout->addWidget (m_dimensionsGroupBox, m_gridNumRows, 0, 1, 2); } else if (m_previewGroupBox) { m_gridLayout->addWidget (m_previewGroupBox, m_gridNumRows, 0, 1, 2); } m_gridLayout->setRowStretch (m_gridNumRows, 1); m_gridNumRows++; } } kpTransformPreviewDialog::~kpTransformPreviewDialog () = default; // private void kpTransformPreviewDialog::createDimensionsGroupBox () { m_dimensionsGroupBox = new QGroupBox (i18n ("Dimensions"), mainWidget ()); auto *originalLabel = new QLabel (i18n ("Original:"), m_dimensionsGroupBox); QString originalDimensions; if (document ()) { originalDimensions = i18n ("%1 x %2", m_oldWidth, m_oldHeight); // Stop the Dimensions Group Box from resizing so often const QString minimumLengthString (QStringLiteral("100000 x 100000")); const int padLength = minimumLengthString.length (); for (int i = originalDimensions.length (); i < padLength; i++) { originalDimensions += ' '; } } auto *originalDimensionsLabel = new QLabel (originalDimensions, m_dimensionsGroupBox); auto *afterTransformLabel = new QLabel (m_afterActionText, m_dimensionsGroupBox); m_afterTransformDimensionsLabel = new QLabel (m_dimensionsGroupBox); auto *dimensionsLayout = new QGridLayout (m_dimensionsGroupBox ); dimensionsLayout->addWidget (originalLabel, 0, 0, Qt::AlignBottom); dimensionsLayout->addWidget (originalDimensionsLabel, 0, 1, Qt::AlignBottom); dimensionsLayout->addWidget (afterTransformLabel, 1, 0, Qt::AlignTop); dimensionsLayout->addWidget (m_afterTransformDimensionsLabel, 1, 1, Qt::AlignTop); } // private void kpTransformPreviewDialog::createPreviewGroupBox () { m_previewGroupBox = new QGroupBox (i18n ("Preview"), mainWidget ()); m_previewPixmapLabel = new kpResizeSignallingLabel (m_previewGroupBox); m_previewPixmapLabel->setMinimumSize (150, 110); connect (m_previewPixmapLabel, &kpResizeSignallingLabel::resized, this, &kpTransformPreviewDialog::updatePreview); QPushButton *updatePushButton = new QPushButton (i18n ("&Update"), m_previewGroupBox); connect (updatePushButton, &QPushButton::clicked, this, &kpTransformPreviewDialog::slotUpdateWithWaitCursor); auto *previewLayout = new QVBoxLayout (m_previewGroupBox); previewLayout->addWidget (m_previewPixmapLabel, 1/*stretch*/); previewLayout->addWidget (updatePushButton, 0/*stretch*/, Qt::AlignHCenter); } // protected kpDocument *kpTransformPreviewDialog::document () const { return m_environ->document (); } // protected QWidget *kpTransformPreviewDialog::mainWidget () const { return m_mainWidget; } // protected void kpTransformPreviewDialog::addCustomWidgetToFront (QWidget *w) { m_gridLayout->addWidget (w, 0, 0, 1, 2); } // protected void kpTransformPreviewDialog::addCustomWidget (QWidget *w) { m_gridLayout->addWidget (w, m_gridNumRows, 0, 1, 2); m_gridNumRows++; } // public override [base QWidget] void kpTransformPreviewDialog::setUpdatesEnabled (bool enable) { QDialog::setUpdatesEnabled (enable); if (enable) { slotUpdateWithWaitCursor (); } } // private void kpTransformPreviewDialog::updateDimensions () { if (!m_dimensionsGroupBox) { return; } kpDocument *doc = document (); if (!doc) { return; } if (!updatesEnabled ()) { - #if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "updates not enabled - aborting"; - #endif return; } QSize newDim = newDimensions (); -#if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "kpTransformPreviewDialog::updateDimensions(): newDim=" << newDim; -#endif QString newDimString = i18n ("%1 x %2", newDim.width (), newDim.height ()); m_afterTransformDimensionsLabel->setText (newDimString); } // public static double kpTransformPreviewDialog::aspectScale (int newWidth, int newHeight, int oldWidth, int oldHeight) { double widthScale = double (newWidth) / double (oldWidth); double heightScale = double (newHeight) / double (oldHeight); // Keeps aspect ratio return qMin (widthScale, heightScale); } // public static int kpTransformPreviewDialog::scaleDimension (int dimension, double scale, int min, int max) { return qMax (min, qMin (max, qRound (dimension * scale))); } // private void kpTransformPreviewDialog::updateShrunkenDocumentPixmap () { -#if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "kpTransformPreviewDialog::updateShrunkenDocumentPixmap()" << " shrunkenDocPixmap.size=" << m_shrunkenDocumentPixmap.size () << " previewPixmapLabelSizeWhenUpdatedPixmap=" << m_previewPixmapLabelSizeWhenUpdatedPixmap << " previewPixmapLabel.size=" - << m_previewPixmapLabel->size () - << endl; -#endif + << m_previewPixmapLabel->size (); if (!m_previewGroupBox) { return; } kpDocument *doc = document (); Q_ASSERT (doc && !doc->image ().isNull ()); if (m_shrunkenDocumentPixmap.isNull () || m_previewPixmapLabel->size () != m_previewPixmapLabelSizeWhenUpdatedPixmap) { - #if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "\tupdating shrunkenDocPixmap"; - #endif // TODO: Why the need to keep aspect ratio here? // Isn't scaling the skewed result maintaining aspect enough? double keepsAspectScale = aspectScale (m_previewPixmapLabel->width (), m_previewPixmapLabel->height (), m_oldWidth, m_oldHeight); kpImage image; if (m_actOnSelection) { kpAbstractImageSelection *sel = doc->imageSelection ()->clone (); if (!sel->hasContent ()) { sel->setBaseImage (doc->getSelectedBaseImage ()); } image = sel->transparentImage (); delete sel; } else { image = doc->image (); } m_shrunkenDocumentPixmap = kpPixmapFX::scale ( image, scaleDimension (m_oldWidth, keepsAspectScale, 1, m_previewPixmapLabel->width ()), scaleDimension (m_oldHeight, keepsAspectScale, 1, m_previewPixmapLabel->height ())); m_previewPixmapLabelSizeWhenUpdatedPixmap = m_previewPixmapLabel->size (); } } // private void kpTransformPreviewDialog::updatePreview () { -#if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "kpTransformPreviewDialog::updatePreview()"; -#endif if (!m_previewGroupBox) { return; } kpDocument *doc = document (); if (!doc) { return; } if (!updatesEnabled ()) { - #if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "updates not enabled - aborting"; - #endif return; } updateShrunkenDocumentPixmap (); if (!m_shrunkenDocumentPixmap.isNull ()) { QSize newDim = newDimensions (); double keepsAspectScale = aspectScale (m_previewPixmapLabel->width (), m_previewPixmapLabel->height (), newDim.width (), newDim.height ()); int targetWidth = scaleDimension (newDim.width (), keepsAspectScale, 1, // min m_previewPixmapLabel->width ()); // max int targetHeight = scaleDimension (newDim.height (), keepsAspectScale, 1, // min m_previewPixmapLabel->height ()); // max // TODO: Some effects work directly on QImage; so could cache the // QImage so that transformPixmap() is faster QImage transformedShrunkenDocumentPixmap = transformPixmap (m_shrunkenDocumentPixmap, targetWidth, targetHeight); QImage previewPixmap (m_previewPixmapLabel->width (), m_previewPixmapLabel->height (), QImage::Format_ARGB32_Premultiplied); previewPixmap.fill(QColor(Qt::transparent).rgba()); kpPixmapFX::setPixmapAt (&previewPixmap, (previewPixmap.width () - transformedShrunkenDocumentPixmap.width ()) / 2, (previewPixmap.height () - transformedShrunkenDocumentPixmap.height ()) / 2, transformedShrunkenDocumentPixmap); -#if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "kpTransformPreviewDialog::updatePreview ():" << " shrunkenDocumentPixmap: w=" << m_shrunkenDocumentPixmap.width () << " h=" << m_shrunkenDocumentPixmap.height () << " previewPixmapLabel: w=" << m_previewPixmapLabel->width () << " h=" << m_previewPixmapLabel->height () << " transformedShrunkenDocumentPixmap: w=" << transformedShrunkenDocumentPixmap.width () << " h=" << transformedShrunkenDocumentPixmap.height () << " previewPixmap: w=" << previewPixmap.width () << " h=" - << previewPixmap.height () - << endl; -#endif + << previewPixmap.height (); m_previewPixmapLabel->setPixmap (QPixmap::fromImage(previewPixmap)); // immediate update esp. for expensive previews m_previewPixmapLabel->repaint (); -#if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "\tafter QLabel::setPixmap() previewPixmapLabel: w=" << m_previewPixmapLabel->width () << " h=" - << m_previewPixmapLabel->height () - << endl; -#endif + << m_previewPixmapLabel->height (); } } // protected slot virtual void kpTransformPreviewDialog::slotUpdate () { -#if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "kpTransformPreviewDialog::slotUpdate()"; -#endif updateDimensions (); updatePreview (); } // protected slot virtual void kpTransformPreviewDialog::slotUpdateWithWaitCursor () { -#if DEBUG_KP_TRANSFORM_PREVIEW_DIALOG qCDebug(kpLogDialogs) << "kpTransformPreviewDialog::slotUpdateWithWaitCursor()"; -#endif QApplication::setOverrideCursor (Qt::WaitCursor); slotUpdate (); QApplication::restoreOverrideCursor (); } diff --git a/dialogs/imagelib/transforms/kpTransformResizeScaleDialog.cpp b/dialogs/imagelib/transforms/kpTransformResizeScaleDialog.cpp index 24410bf7..151eea15 100644 --- a/dialogs/imagelib/transforms/kpTransformResizeScaleDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformResizeScaleDialog.cpp @@ -1,840 +1,826 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2011 Martin Koller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG 1 - #include "kpTransformResizeScaleDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kpLogCategories.h" #include #include "layers/selections/kpAbstractSelection.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "tools/kpTool.h" #include "environments/dialogs/imagelib/transforms/kpTransformDialogEnvironment.h" //--------------------------------------------------------------------- #define kpSettingResizeScaleLastKeepAspect "Resize Scale - Last Keep Aspect" #define kpSettingResizeScaleScaleType "Resize Scale - ScaleType" //--------------------------------------------------------------------- #define SET_VALUE_WITHOUT_SIGNAL_EMISSION(knuminput_instance,value) \ { \ knuminput_instance->blockSignals (true); \ knuminput_instance->setValue (value); \ knuminput_instance->blockSignals (false); \ } #define IGNORE_KEEP_ASPECT_RATIO(cmd) \ { \ m_ignoreKeepAspectRatio++; \ cmd; \ m_ignoreKeepAspectRatio--; \ } //--------------------------------------------------------------------- kpTransformResizeScaleDialog::kpTransformResizeScaleDialog ( kpTransformDialogEnvironment *_env, QWidget *parent) : QDialog (parent), m_environ (_env), m_ignoreKeepAspectRatio (0), m_lastType(kpTransformResizeScaleCommand::Resize) { setWindowTitle (i18nc ("@title:window", "Resize / Scale")); QDialogButtonBox *buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect (buttons, &QDialogButtonBox::accepted, this, &kpTransformResizeScaleDialog::accept); connect (buttons, &QDialogButtonBox::rejected, this, &kpTransformResizeScaleDialog::reject); QWidget *baseWidget = new QWidget (this); auto *dialogLayout = new QVBoxLayout (this); dialogLayout->addWidget (baseWidget); dialogLayout->addWidget (buttons); QWidget *actOnBox = createActOnBox(baseWidget); QGroupBox *operationGroupBox = createOperationGroupBox(baseWidget); QGroupBox *dimensionsGroupBox = createDimensionsGroupBox(baseWidget); auto *baseLayout = new QVBoxLayout (baseWidget); baseLayout->setContentsMargins(0, 0, 0, 0); baseLayout->addWidget(actOnBox); baseLayout->addWidget(operationGroupBox); baseLayout->addWidget(dimensionsGroupBox); KConfigGroup cfg(KSharedConfig::openConfig(), kpSettingsGroupGeneral); setKeepAspectRatio(cfg.readEntry(kpSettingResizeScaleLastKeepAspect, false)); m_lastType = static_cast (cfg.readEntry(kpSettingResizeScaleScaleType, static_cast(kpTransformResizeScaleCommand::Resize))); slotActOnChanged (); m_newWidthInput->setFocus (); //enableButtonOk (!isNoOp ()); } //--------------------------------------------------------------------- // private kpDocument *kpTransformResizeScaleDialog::document () const { return m_environ->document (); } //--------------------------------------------------------------------- // private kpAbstractSelection *kpTransformResizeScaleDialog::selection () const { Q_ASSERT (document ()); return document ()->selection (); } //--------------------------------------------------------------------- // private kpTextSelection *kpTransformResizeScaleDialog::textSelection () const { Q_ASSERT (document ()); return document ()->textSelection (); } //--------------------------------------------------------------------- // private QWidget *kpTransformResizeScaleDialog::createActOnBox(QWidget *baseWidget) { QWidget *actOnBox = new QWidget (baseWidget); auto *actOnLabel = new QLabel (i18n ("Ac&t on:"), actOnBox); m_actOnCombo = new QComboBox (actOnBox); actOnLabel->setBuddy (m_actOnCombo); m_actOnCombo->insertItem (Image, i18n ("Entire Image")); if (selection ()) { QString selName = i18n ("Selection"); if (textSelection ()) { selName = i18n ("Text Box"); } m_actOnCombo->insertItem (Selection, selName); m_actOnCombo->setCurrentIndex (Selection); } else { actOnLabel->setEnabled (false); m_actOnCombo->setEnabled (false); } auto *lay = new QHBoxLayout (actOnBox); lay->setContentsMargins(0, 0, 0, 0); lay->addWidget (actOnLabel); lay->addWidget (m_actOnCombo, 1); connect (m_actOnCombo, static_cast(&QComboBox::activated), this, &kpTransformResizeScaleDialog::slotActOnChanged); return actOnBox; } //--------------------------------------------------------------------- static void toolButtonSetLook (QToolButton *button, const QString &iconName, const QString &name) { QPixmap icon; const QString qrcPath = QStringLiteral(":/icons/") + iconName; if (!icon.load (qrcPath)) { qWarning() << qrcPath << "not found"; } else { button->setIconSize (QSize (icon.width (), icon.height ())); button->setIcon (icon); } button->setToolButtonStyle (Qt::ToolButtonTextUnderIcon); button->setText (name); button->setFocusPolicy (Qt::StrongFocus); button->setCheckable (true); } //--------------------------------------------------------------------- // private QGroupBox *kpTransformResizeScaleDialog::createOperationGroupBox (QWidget *baseWidget) { QGroupBox *operationGroupBox = new QGroupBox (i18n ("Operation"), baseWidget); operationGroupBox->setWhatsThis( i18n ("" "
    " "
  • Resize: The size of the picture will be" " increased" " by creating new areas to the right and/or bottom" " (filled in with the background color) or" " decreased by cutting" " it at the right and/or bottom.
  • " "
  • Scale: The picture will be expanded" " by duplicating pixels or squashed by dropping pixels.
  • " "
  • Smooth Scale: This is the same as" " Scale except that it blends neighboring" " pixels to produce a smoother looking picture.
  • " "
" "
")); m_resizeButton = new QToolButton (operationGroupBox); toolButtonSetLook (m_resizeButton, QStringLiteral ("resize"), i18n ("&Resize")); m_scaleButton = new QToolButton (operationGroupBox); toolButtonSetLook (m_scaleButton, QStringLiteral ("scale"), i18n ("&Scale")); m_smoothScaleButton = new QToolButton (operationGroupBox); toolButtonSetLook (m_smoothScaleButton, QStringLiteral ("smooth_scale"), i18n ("S&mooth Scale")); auto *resizeScaleButtonGroup = new QButtonGroup (baseWidget); resizeScaleButtonGroup->addButton (m_resizeButton); resizeScaleButtonGroup->addButton (m_scaleButton); resizeScaleButtonGroup->addButton (m_smoothScaleButton); auto *operationLayout = new QGridLayout (operationGroupBox ); operationLayout->addWidget (m_resizeButton, 0, 0, Qt::AlignCenter); operationLayout->addWidget (m_scaleButton, 0, 1, Qt::AlignCenter); operationLayout->addWidget (m_smoothScaleButton, 0, 2, Qt::AlignCenter); connect (m_resizeButton, &QToolButton::toggled, this, &kpTransformResizeScaleDialog::slotTypeChanged); connect (m_scaleButton, &QToolButton::toggled, this, &kpTransformResizeScaleDialog::slotTypeChanged); connect (m_smoothScaleButton, &QToolButton::toggled, this, &kpTransformResizeScaleDialog::slotTypeChanged); return operationGroupBox; } //--------------------------------------------------------------------- // private QGroupBox *kpTransformResizeScaleDialog::createDimensionsGroupBox(QWidget *baseWidget) { QGroupBox *dimensionsGroupBox = new QGroupBox (i18n ("Dimensions"), baseWidget); auto *widthLabel = new QLabel (i18n ("Width:"), dimensionsGroupBox); widthLabel->setAlignment (widthLabel->alignment () | Qt::AlignHCenter); auto *heightLabel = new QLabel (i18n ("Height:"), dimensionsGroupBox); heightLabel->setAlignment (heightLabel->alignment () | Qt::AlignHCenter); auto *originalLabel = new QLabel (i18n ("Original:"), dimensionsGroupBox); m_originalWidthInput = new QSpinBox; m_originalWidthInput->setRange(1, INT_MAX); m_originalWidthInput->setValue(document()->width(static_cast (selection()))); auto *xLabel0 = new QLabel (i18n ("x"), dimensionsGroupBox); m_originalHeightInput = new QSpinBox; m_originalHeightInput->setRange(1, INT_MAX); m_originalHeightInput->setValue(document()->height(static_cast (selection()))); auto *newLabel = new QLabel (i18n ("&New:"), dimensionsGroupBox); m_newWidthInput = new QSpinBox; m_newWidthInput->setRange(1, INT_MAX); auto *xLabel1 = new QLabel (i18n ("x"), dimensionsGroupBox); m_newHeightInput = new QSpinBox; m_newHeightInput->setRange(1, INT_MAX); auto *percentLabel = new QLabel (i18n ("&Percent:"), dimensionsGroupBox); m_percentWidthInput = new QDoubleSpinBox; m_percentWidthInput->setRange(0.01, 1000000); m_percentWidthInput->setValue(100); m_percentWidthInput->setSingleStep(1); m_percentWidthInput->setDecimals(2); m_percentWidthInput->setSuffix(i18n("%")); auto *xLabel2 = new QLabel (i18n ("x"), dimensionsGroupBox); m_percentHeightInput = new QDoubleSpinBox; m_percentHeightInput->setRange(0.01, 1000000); m_percentHeightInput->setValue(100); m_percentHeightInput->setSingleStep(1); m_percentHeightInput->setDecimals(2); m_percentHeightInput->setSuffix(i18n("%")); m_keepAspectRatioCheckBox = new QCheckBox (i18n ("Keep &aspect ratio"), dimensionsGroupBox); m_originalWidthInput->setEnabled (false); m_originalHeightInput->setEnabled (false); originalLabel->setBuddy (m_originalWidthInput); newLabel->setBuddy (m_newWidthInput); m_percentWidthInput->setValue (100); m_percentHeightInput->setValue (100); percentLabel->setBuddy (m_percentWidthInput); auto *dimensionsLayout = new QGridLayout (dimensionsGroupBox); dimensionsLayout->setColumnStretch (1/*column*/, 1); dimensionsLayout->setColumnStretch (3/*column*/, 1); dimensionsLayout->addWidget (widthLabel, 0, 1); dimensionsLayout->addWidget (heightLabel, 0, 3); dimensionsLayout->addWidget (originalLabel, 1, 0); dimensionsLayout->addWidget (m_originalWidthInput, 1, 1); dimensionsLayout->addWidget (xLabel0, 1, 2); dimensionsLayout->addWidget (m_originalHeightInput, 1, 3); dimensionsLayout->addWidget (newLabel, 2, 0); dimensionsLayout->addWidget (m_newWidthInput, 2, 1); dimensionsLayout->addWidget (xLabel1, 2, 2); dimensionsLayout->addWidget (m_newHeightInput, 2, 3); dimensionsLayout->addWidget (percentLabel, 3, 0); dimensionsLayout->addWidget (m_percentWidthInput, 3, 1); dimensionsLayout->addWidget (xLabel2, 3, 2); dimensionsLayout->addWidget (m_percentHeightInput, 3, 3); dimensionsLayout->addWidget (m_keepAspectRatioCheckBox, 4, 0, 1, 4); dimensionsLayout->setRowStretch (4/*row*/, 1); dimensionsLayout->setRowMinimumHeight (4/*row*/, dimensionsLayout->rowMinimumHeight (4) * 2); connect (m_newWidthInput, static_cast(&QSpinBox::valueChanged), this, &kpTransformResizeScaleDialog::slotWidthChanged); connect (m_newHeightInput, static_cast(&QSpinBox::valueChanged), this, &kpTransformResizeScaleDialog::slotHeightChanged); // COMPAT: KDoubleNumInput only fires valueChanged(double) once per // edit. It should either fire: // // 1. At the end of the edit (triggered by clicking or tabbing // away), like with KDE 3. // // OR // // 2. Once per keystroke. // // Bug in KDoubleNumInput. connect (m_percentWidthInput, static_cast(&QDoubleSpinBox::valueChanged), this, &kpTransformResizeScaleDialog::slotPercentWidthChanged); connect (m_percentHeightInput, static_cast(&QDoubleSpinBox::valueChanged), this, &kpTransformResizeScaleDialog::slotPercentHeightChanged); connect (m_keepAspectRatioCheckBox, &QCheckBox::toggled, this, &kpTransformResizeScaleDialog::setKeepAspectRatio); return dimensionsGroupBox; } //--------------------------------------------------------------------- // private void kpTransformResizeScaleDialog::widthFitHeightToAspectRatio () { if (m_keepAspectRatioCheckBox->isChecked () && !m_ignoreKeepAspectRatio) { // width / height = oldWidth / oldHeight // height = width * oldHeight / oldWidth const int newHeight = qRound (double (imageWidth ()) * double (originalHeight ()) / double (originalWidth ())); IGNORE_KEEP_ASPECT_RATIO (m_newHeightInput->setValue (newHeight)); } } //--------------------------------------------------------------------- // private void kpTransformResizeScaleDialog::heightFitWidthToAspectRatio () { if (m_keepAspectRatioCheckBox->isChecked () && !m_ignoreKeepAspectRatio) { // width / height = oldWidth / oldHeight // width = height * oldWidth / oldHeight const int newWidth = qRound (double (imageHeight ()) * double (originalWidth ()) / double (originalHeight ())); IGNORE_KEEP_ASPECT_RATIO (m_newWidthInput->setValue (newWidth)); } } //--------------------------------------------------------------------- // private bool kpTransformResizeScaleDialog::resizeEnabled () const { return (!actOnSelection () || (actOnSelection () && textSelection ())); } //--------------------------------------------------------------------- // private bool kpTransformResizeScaleDialog::scaleEnabled () const { return (!(actOnSelection () && textSelection ())); } //--------------------------------------------------------------------- // private bool kpTransformResizeScaleDialog::smoothScaleEnabled () const { return scaleEnabled (); } //--------------------------------------------------------------------- // public slot void kpTransformResizeScaleDialog::slotActOnChanged () { -#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 qCDebug(kpLogDialogs) << "kpTransformResizeScaleDialog::slotActOnChanged()"; -#endif m_resizeButton->setEnabled (resizeEnabled ()); m_scaleButton->setEnabled (scaleEnabled ()); m_smoothScaleButton->setEnabled (smoothScaleEnabled ()); // TODO: somehow share logic with (resize|*scale)Enabled() if (actOnSelection ()) { if (textSelection ()) { m_resizeButton->setChecked (true); } else { if (m_lastType == kpTransformResizeScaleCommand::Scale) { m_scaleButton->setChecked (true); } else { m_smoothScaleButton->setChecked (true); } } } else { if (m_lastType == kpTransformResizeScaleCommand::Resize) { m_resizeButton->setChecked (true); } else if (m_lastType == kpTransformResizeScaleCommand::Scale) { m_scaleButton->setChecked (true); } else { m_smoothScaleButton->setChecked (true); } } m_originalWidthInput->setValue (originalWidth ()); m_originalHeightInput->setValue (originalHeight ()); m_newWidthInput->blockSignals (true); m_newHeightInput->blockSignals (true); m_newWidthInput->setMinimum (actOnSelection () ? selection ()->minimumWidth () : 1); m_newHeightInput->setMinimum (actOnSelection () ? selection ()->minimumHeight () : 1); m_newWidthInput->blockSignals (false); m_newHeightInput->blockSignals (false); IGNORE_KEEP_ASPECT_RATIO (slotPercentWidthChanged (m_percentWidthInput->value ())); IGNORE_KEEP_ASPECT_RATIO (slotPercentHeightChanged (m_percentHeightInput->value ())); setKeepAspectRatio (m_keepAspectRatioCheckBox->isChecked ()); } //--------------------------------------------------------------------- // public slot void kpTransformResizeScaleDialog::slotTypeChanged () { m_lastType = type (); } //--------------------------------------------------------------------- // public slot void kpTransformResizeScaleDialog::slotWidthChanged (int width) { -#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 qCDebug(kpLogDialogs) << "kpTransformResizeScaleDialog::slotWidthChanged(" - << width << ")" << endl; -#endif + << width << ")"; const double newPercentWidth = double (width) * 100 / double (originalWidth ()); SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_percentWidthInput,newPercentWidth); widthFitHeightToAspectRatio (); //enableButtonOk (!isNoOp ()); } //--------------------------------------------------------------------- // public slot void kpTransformResizeScaleDialog::slotHeightChanged (int height) { -#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 qCDebug(kpLogDialogs) << "kpTransformResizeScaleDialog::slotHeightChanged(" - << height << ")" << endl; -#endif + << height << ")"; const double newPercentHeight = double (height) * 100 / double (originalHeight ()); SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_percentHeightInput,newPercentHeight); heightFitWidthToAspectRatio (); //enableButtonOk (!isNoOp ()); } //--------------------------------------------------------------------- // public slot void kpTransformResizeScaleDialog::slotPercentWidthChanged (double percentWidth) { -#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 qCDebug(kpLogDialogs) << "kpTransformResizeScaleDialog::slotPercentWidthChanged(" << percentWidth << ")"; -#endif SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_newWidthInput, qRound (percentWidth * originalWidth () / 100.0)); widthFitHeightToAspectRatio (); //enableButtonOk (!isNoOp ()); } //--------------------------------------------------------------------- // public slot void kpTransformResizeScaleDialog::slotPercentHeightChanged (double percentHeight) { -#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 qCDebug(kpLogDialogs) << "kpTransformResizeScaleDialog::slotPercentHeightChanged(" << percentHeight << ")"; -#endif SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_newHeightInput, qRound (percentHeight * originalHeight () / 100.0)); heightFitWidthToAspectRatio (); //enableButtonOk (!isNoOp ()); } //--------------------------------------------------------------------- // public slot void kpTransformResizeScaleDialog::setKeepAspectRatio (bool on) { -#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1 qCDebug(kpLogDialogs) << "kpTransformResizeScaleDialog::setKeepAspectRatio(" << on << ")"; -#endif if (on != m_keepAspectRatioCheckBox->isChecked ()) { m_keepAspectRatioCheckBox->setChecked (on); } if (on) { widthFitHeightToAspectRatio (); } } //--------------------------------------------------------------------- #undef IGNORE_KEEP_ASPECT_RATIO #undef SET_VALUE_WITHOUT_SIGNAL_EMISSION //--------------------------------------------------------------------- // private int kpTransformResizeScaleDialog::originalWidth () const { return document ()->width (actOnSelection ()); } //--------------------------------------------------------------------- // private int kpTransformResizeScaleDialog::originalHeight () const { return document ()->height (actOnSelection ()); } //--------------------------------------------------------------------- // public int kpTransformResizeScaleDialog::imageWidth () const { return m_newWidthInput->value (); } //--------------------------------------------------------------------- // public int kpTransformResizeScaleDialog::imageHeight () const { return m_newHeightInput->value (); } //--------------------------------------------------------------------- // public bool kpTransformResizeScaleDialog::actOnSelection () const { return (m_actOnCombo->currentIndex () == Selection); } //--------------------------------------------------------------------- // public kpTransformResizeScaleCommand::Type kpTransformResizeScaleDialog::type () const { if (m_resizeButton->isChecked ()) { return kpTransformResizeScaleCommand::Resize; } if (m_scaleButton->isChecked ()) { return kpTransformResizeScaleCommand::Scale; } return kpTransformResizeScaleCommand::SmoothScale; } //--------------------------------------------------------------------- // public bool kpTransformResizeScaleDialog::isNoOp () const { return (imageWidth () == originalWidth () && imageHeight () == originalHeight ()); } //--------------------------------------------------------------------- // private slot virtual [base QDialog] void kpTransformResizeScaleDialog::accept () { enum { eText, eSelection, eImage } actionTarget = eText; if (actOnSelection ()) { if (textSelection ()) { actionTarget = eText; } else { actionTarget = eSelection; } } else { actionTarget = eImage; } KLocalizedString message; QString caption, continueButtonText; // Note: If eText, can't Scale nor SmoothScale. // If eSelection, can't Resize. switch (type ()) { default: case kpTransformResizeScaleCommand::Resize: if (actionTarget == eText) { message = ki18n ("

Resizing the text box to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to resize the text box?

"); caption = i18nc ("@title:window", "Resize Text Box?"); continueButtonText = i18n ("R&esize Text Box"); } else if (actionTarget == eImage) { message = ki18n ("

Resizing the image to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to resize the image?

"); caption = i18nc ("@title:window", "Resize Image?"); continueButtonText = i18n ("R&esize Image"); } break; case kpTransformResizeScaleCommand::Scale: if (actionTarget == eImage) { message = ki18n ("

Scaling the image to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to scale the image?

"); caption = i18nc ("@title:window", "Scale Image?"); continueButtonText = i18n ("Scal&e Image"); } else if (actionTarget == eSelection) { message = ki18n ("

Scaling the selection to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to scale the selection?

"); caption = i18nc ("@title:window", "Scale Selection?"); continueButtonText = i18n ("Scal&e Selection"); } break; case kpTransformResizeScaleCommand::SmoothScale: if (actionTarget == eImage) { message = ki18n ("

Smooth Scaling the image to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to smooth scale the image?

"); caption = i18nc ("@title:window", "Smooth Scale Image?"); continueButtonText = i18n ("Smooth Scal&e Image"); } else if (actionTarget == eSelection) { message = ki18n ("

Smooth Scaling the selection to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to smooth scale the selection?

"); caption = i18nc ("@title:window", "Smooth Scale Selection?"); continueButtonText = i18n ("Smooth Scal&e Selection"); } break; } if (kpTool::warnIfBigImageSize (originalWidth (), originalHeight (), imageWidth (), imageHeight (), message.subs (imageWidth ()).subs (imageHeight ()).toString (), caption, continueButtonText, this)) { QDialog::accept (); } // store settings KConfigGroup cfg(KSharedConfig::openConfig(), kpSettingsGroupGeneral); cfg.writeEntry(kpSettingResizeScaleLastKeepAspect, m_keepAspectRatioCheckBox->isChecked()); cfg.writeEntry(kpSettingResizeScaleScaleType, static_cast(m_lastType)); cfg.sync(); } //--------------------------------------------------------------------- diff --git a/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp b/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp index c41d31f5..fe7b449e 100644 --- a/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp @@ -1,321 +1,319 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_ROTATE 1 - #include "kpTransformRotateDialog.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "environments/dialogs/imagelib/transforms/kpTransformDialogEnvironment.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include #include #include #include #include #include #include #include #include #include #include // private static int kpTransformRotateDialog::s_lastWidth = -1, kpTransformRotateDialog::s_lastHeight = -1; // private static bool kpTransformRotateDialog::s_lastIsClockwise = true; int kpTransformRotateDialog::s_lastAngleCustom = 0; kpTransformRotateDialog::kpTransformRotateDialog (bool actOnSelection, kpTransformDialogEnvironment *_env, QWidget *parent) : kpTransformPreviewDialog (kpTransformPreviewDialog::AllFeatures, false/*don't reserve top row*/, actOnSelection ? i18nc ("@title:window", "Rotate Selection") : i18nc ("@title:window", "Rotate Image"), i18n ("After rotate:"), actOnSelection, _env, parent) { s_lastAngleCustom = 0; createDirectionGroupBox (); createAngleGroupBox (); if (s_lastWidth > 0 && s_lastHeight > 0) { resize (s_lastWidth, s_lastHeight); } slotAngleCustomRadioButtonToggled (m_angleCustomRadioButton->isChecked ()); slotUpdate (); } kpTransformRotateDialog::~kpTransformRotateDialog () { s_lastWidth = width (); s_lastHeight = height (); } // private void kpTransformRotateDialog::createDirectionGroupBox () { auto *directionGroupBox = new QGroupBox (i18n ("Direction"), mainWidget ()); addCustomWidget (directionGroupBox); auto *antiClockwisePixmapLabel = new QLabel (directionGroupBox); antiClockwisePixmapLabel->setPixmap (QStringLiteral(":/icons/image_rotate_anticlockwise")); auto *clockwisePixmapLabel = new QLabel (directionGroupBox); clockwisePixmapLabel->setPixmap (QStringLiteral(":/icons/image_rotate_clockwise")); m_antiClockwiseRadioButton = new QRadioButton (i18n ("Cou&nterclockwise"), directionGroupBox); m_clockwiseRadioButton = new QRadioButton (i18n ("C&lockwise"), directionGroupBox); m_antiClockwiseRadioButton->setChecked (!s_lastIsClockwise); m_clockwiseRadioButton->setChecked (s_lastIsClockwise); auto *directionLayout = new QGridLayout (directionGroupBox ); directionLayout->addWidget (antiClockwisePixmapLabel, 0, 0, Qt::AlignCenter); directionLayout->addWidget (clockwisePixmapLabel, 0, 1, Qt::AlignCenter); directionLayout->addWidget (m_antiClockwiseRadioButton, 1, 0, Qt::AlignCenter); directionLayout->addWidget (m_clockwiseRadioButton, 1, 1, Qt::AlignCenter); connect (m_antiClockwiseRadioButton, &QRadioButton::toggled, this, &kpTransformRotateDialog::slotUpdate); connect (m_clockwiseRadioButton, &QRadioButton::toggled, this, &kpTransformRotateDialog::slotUpdate); } // private void kpTransformRotateDialog::createAngleGroupBox () { auto *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ()); addCustomWidget (angleGroupBox); m_angle90RadioButton = new QRadioButton (i18n ("90 °rees"), angleGroupBox); m_angle180RadioButton = new QRadioButton (i18n ("180 d&egrees"), angleGroupBox); m_angle270RadioButton = new QRadioButton (i18n ("270 de&grees"), angleGroupBox); m_angleCustomRadioButton = new QRadioButton (i18n ("C&ustom:"), angleGroupBox); m_angleCustomInput = new QSpinBox; m_angleCustomInput->setMinimum(-359); m_angleCustomInput->setMaximum(+359); m_angleCustomInput->setValue(s_lastAngleCustom); auto *degreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); m_angleCustomRadioButton->setChecked (true); auto *angleLayout = new QGridLayout (angleGroupBox ); angleLayout->addWidget (m_angle90RadioButton, 0, 0, 1, 3); angleLayout->addWidget (m_angle180RadioButton, 1, 0, 1, 3); angleLayout->addWidget (m_angle270RadioButton, 2, 0, 1, 3); angleLayout->addWidget (m_angleCustomRadioButton, 3, 0); angleLayout->addWidget (m_angleCustomInput, 3, 1); angleLayout->addWidget (degreesLabel, 3, 2); angleLayout->setColumnStretch (1, 2); // Stretch Custom Angle Input connect (m_angle90RadioButton, &QRadioButton::toggled, this, &kpTransformRotateDialog::slotUpdate); connect (m_angle180RadioButton, &QRadioButton::toggled, this, &kpTransformRotateDialog::slotUpdate); connect (m_angle270RadioButton, &QRadioButton::toggled, this, &kpTransformRotateDialog::slotUpdate); connect (m_angleCustomRadioButton, &QRadioButton::toggled, this, &kpTransformRotateDialog::slotAngleCustomRadioButtonToggled); connect (m_angleCustomRadioButton, &QRadioButton::toggled, this, &kpTransformRotateDialog::slotUpdate); connect (m_angleCustomInput, static_cast(&QSpinBox::valueChanged), this, &kpTransformRotateDialog::slotUpdate); } // public virtual [base kpTransformPreviewDialog] bool kpTransformRotateDialog::isNoOp () const { return (angle () == 0); } // public int kpTransformRotateDialog::angle () const { int retAngle; if (m_angle90RadioButton->isChecked ()) { retAngle = 90; } else if (m_angle180RadioButton->isChecked ()) { retAngle = 180; } else if (m_angle270RadioButton->isChecked ()) { retAngle = 270; } else { // if (m_angleCustomRadioButton->isChecked ()) retAngle = m_angleCustomInput->value (); } if (m_antiClockwiseRadioButton->isChecked ()) { retAngle *= -1; } if (retAngle < 0) { retAngle += ((0 - retAngle) / 360 + 1) * 360; } if (retAngle >= 360) { retAngle -= ((retAngle - 360) / 360 + 1) * 360; } return retAngle; } // private virtual [base kpTransformPreviewDialog] QSize kpTransformRotateDialog::newDimensions () const { QTransform matrix = kpPixmapFX::rotateMatrix (m_oldWidth, m_oldHeight, angle ()); QRect rect = matrix.mapRect (QRect (0, 0, m_oldWidth, m_oldHeight)); return rect.size (); } // private virtual [base kpTransformPreviewDialog] QImage kpTransformRotateDialog::transformPixmap (const QImage &image, int targetWidth, int targetHeight) const { return kpPixmapFX::rotate (image, angle (), m_environ->backgroundColor (m_actOnSelection), targetWidth, targetHeight); } // private slot void kpTransformRotateDialog::slotAngleCustomRadioButtonToggled (bool isChecked) { m_angleCustomInput->setEnabled (isChecked); if (isChecked) { m_angleCustomInput->setFocus(); } } // private slot virtual [base kpTransformPreviewDialog] void kpTransformRotateDialog::slotUpdate () { s_lastIsClockwise = m_clockwiseRadioButton->isChecked (); s_lastAngleCustom = m_angleCustomInput->value (); kpTransformPreviewDialog::slotUpdate (); } // private slot virtual [base QDialog] void kpTransformRotateDialog::accept () { KLocalizedString message; QString caption, continueButtonText; if (document ()->selection ()) { if (!document ()->textSelection ()) { message = ki18n ("

Rotating the selection to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to rotate the selection?

"); caption = i18nc ("@title:window", "Rotate Selection?"); continueButtonText = i18n ("Rotat&e Selection"); } } else { message = ki18n ("

Rotating the image to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to rotate the image?

"); caption = i18nc ("@title:window", "Rotate Image?"); continueButtonText = i18n ("Rotat&e Image"); } const int newWidth = newDimensions ().width (); const int newHeight = newDimensions ().height (); if (kpTool::warnIfBigImageSize (m_oldWidth, m_oldHeight, newWidth, newHeight, message.subs (newWidth).subs (newHeight).toString (), caption, continueButtonText, this)) { QDialog::accept (); } } diff --git a/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp b/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp index 8544c67a..eae04792 100644 --- a/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp @@ -1,297 +1,294 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SKEW 1 -#define DEBUG_KP_TOOL_SKEW_DIALOG 1 - #include "dialogs/imagelib/transforms/kpTransformSkewDialog.h" #include #include #include #include #include #include #include #include #include #include "kpLogCategories.h" #include #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "environments/dialogs/imagelib/transforms/kpTransformDialogEnvironment.h" // private static int kpTransformSkewDialog::s_lastWidth = -1, kpTransformSkewDialog::s_lastHeight = -1; // private static int kpTransformSkewDialog::s_lastHorizontalAngle = 0, kpTransformSkewDialog::s_lastVerticalAngle = 0; kpTransformSkewDialog::kpTransformSkewDialog (bool actOnSelection, kpTransformDialogEnvironment *_env, QWidget *parent) : kpTransformPreviewDialog (kpTransformPreviewDialog::AllFeatures, false/*don't reserve top row*/, actOnSelection ? i18nc ("@title:window", "Skew Selection") : i18nc ("@title:window", "Skew Image"), i18n ("After skew:"), actOnSelection, _env, parent) { // Too confusing - disable for now s_lastHorizontalAngle = s_lastVerticalAngle = 0; createAngleGroupBox (); if (s_lastWidth > 0 && s_lastHeight > 0) { resize (s_lastWidth, s_lastHeight); } slotUpdate (); m_horizontalSkewInput->setFocus (); } kpTransformSkewDialog::~kpTransformSkewDialog () { s_lastWidth = width (); s_lastHeight = height (); } // private void kpTransformSkewDialog::createAngleGroupBox () { auto *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ()); addCustomWidget (angleGroupBox); auto *horizontalSkewPixmapLabel = new QLabel (angleGroupBox); horizontalSkewPixmapLabel->setPixmap (QStringLiteral(":/icons/image_skew_horizontal")); auto *horizontalSkewLabel = new QLabel (i18n ("&Horizontal:"), angleGroupBox); m_horizontalSkewInput = new QSpinBox; m_horizontalSkewInput->setValue(s_lastHorizontalAngle); m_horizontalSkewInput->setMinimum(-89); m_horizontalSkewInput->setMaximum(+89); auto *horizontalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); auto *verticalSkewPixmapLabel = new QLabel (angleGroupBox); verticalSkewPixmapLabel->setPixmap (QStringLiteral(":/icons/image_skew_vertical")); auto *verticalSkewLabel = new QLabel (i18n ("&Vertical:"), angleGroupBox); m_verticalSkewInput = new QSpinBox; m_verticalSkewInput->setValue(s_lastVerticalAngle); m_verticalSkewInput->setMinimum(-89); m_verticalSkewInput->setMaximum(+89); auto *verticalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); horizontalSkewLabel->setBuddy (m_horizontalSkewInput); verticalSkewLabel->setBuddy (m_verticalSkewInput); auto *angleLayout = new QGridLayout (angleGroupBox); angleLayout->addWidget (horizontalSkewPixmapLabel, 0, 0); angleLayout->addWidget (horizontalSkewLabel, 0, 1); angleLayout->addWidget (m_horizontalSkewInput, 0, 2, Qt::AlignVCenter); angleLayout->addWidget (horizontalSkewDegreesLabel, 0, 3); angleLayout->addWidget (verticalSkewPixmapLabel, 1, 0); angleLayout->addWidget (verticalSkewLabel, 1, 1); angleLayout->addWidget (m_verticalSkewInput, 1, 2, Qt::AlignVCenter); angleLayout->addWidget (verticalSkewDegreesLabel, 1, 3); connect (m_horizontalSkewInput, static_cast(&QSpinBox::valueChanged), this, &kpTransformSkewDialog::slotUpdate); connect (m_verticalSkewInput, static_cast(&QSpinBox::valueChanged), this, &kpTransformSkewDialog::slotUpdate); } // private virtual [base kpTransformPreviewDialog] QSize kpTransformSkewDialog::newDimensions () const { kpDocument *doc = document (); Q_ASSERT (doc); auto skewMatrix = kpPixmapFX::skewMatrix (doc->image (), horizontalAngleForPixmapFX (), verticalAngleForPixmapFX ()); auto skewRect = skewMatrix.mapRect (doc->rect (m_actOnSelection)); return {skewRect.width (), skewRect.height ()}; } // private virtual [base kpTransformPreviewDialog] QImage kpTransformSkewDialog::transformPixmap (const QImage &image, int targetWidth, int targetHeight) const { return kpPixmapFX::skew (image, horizontalAngleForPixmapFX (), verticalAngleForPixmapFX (), m_environ->backgroundColor (m_actOnSelection), targetWidth, targetHeight); } // private void kpTransformSkewDialog::updateLastAngles () { s_lastHorizontalAngle = horizontalAngle (); s_lastVerticalAngle = verticalAngle (); } // private slot virtual [base kpTransformPreviewDialog] void kpTransformSkewDialog::slotUpdate () { updateLastAngles (); kpTransformPreviewDialog::slotUpdate (); } // public int kpTransformSkewDialog::horizontalAngle () const { return m_horizontalSkewInput->value (); } // public int kpTransformSkewDialog::verticalAngle () const { return m_verticalSkewInput->value (); } // public static int kpTransformSkewDialog::horizontalAngleForPixmapFX (int hangle) { return -hangle; } // public static int kpTransformSkewDialog::verticalAngleForPixmapFX (int vangle) { return -vangle; } // public int kpTransformSkewDialog::horizontalAngleForPixmapFX () const { return kpTransformSkewDialog::horizontalAngleForPixmapFX (horizontalAngle ()); } // public int kpTransformSkewDialog::verticalAngleForPixmapFX () const { return kpTransformSkewDialog::verticalAngleForPixmapFX (verticalAngle ()); } // public virtual [base kpTransformPreviewDialog] bool kpTransformSkewDialog::isNoOp () const { return (horizontalAngle () == 0) && (verticalAngle () == 0); } // private slot virtual [base QDialog] void kpTransformSkewDialog::accept () { KLocalizedString message; QString caption, continueButtonText; if (document ()->selection ()) { if (!document ()->textSelection ()) { message = ki18n ("

Skewing the selection to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to skew the selection?

"); caption = i18nc ("@title:window", "Skew Selection?"); continueButtonText = i18n ("Sk&ew Selection"); } } else { message = ki18n ("

Skewing the image to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to skew the image?

"); caption = i18nc ("@title:window", "Skew Image?"); continueButtonText = i18n ("Sk&ew Image"); } const int newWidth = newDimensions ().width (); const int newHeight = newDimensions ().height (); if (kpTool::warnIfBigImageSize (m_oldWidth, m_oldHeight, newWidth, newHeight, message.subs (newWidth).subs (newHeight).toString (), caption, continueButtonText, this)) { QDialog::accept (); } } diff --git a/dialogs/kpDocumentSaveOptionsPreviewDialog.cpp b/dialogs/kpDocumentSaveOptionsPreviewDialog.cpp index 5f73dde0..21e88a9e 100644 --- a/dialogs/kpDocumentSaveOptionsPreviewDialog.cpp +++ b/dialogs/kpDocumentSaveOptionsPreviewDialog.cpp @@ -1,232 +1,218 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET 1 - #include "kpDocumentSaveOptionsPreviewDialog.h" #include #include #include #include #include #include #include "kpLogCategories.h" #include #include "commands/kpCommandSize.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "generic/widgets/kpResizeSignallingLabel.h" #include "dialogs/imagelib/transforms/kpTransformPreviewDialog.h" // protected static const QSize kpDocumentSaveOptionsPreviewDialog::s_pixmapLabelMinimumSize (25, 25); kpDocumentSaveOptionsPreviewDialog::kpDocumentSaveOptionsPreviewDialog ( QWidget *parent ) : kpSubWindow (parent), m_filePixmap (nullptr), m_fileSize (0) { setWindowTitle (i18nc ("@title:window", "Save Preview")); auto *baseWidget = this;//new QWidget (this); //setMainWidget (baseWidget); auto *lay = new QGridLayout ( baseWidget ); m_filePixmapLabel = new kpResizeSignallingLabel (baseWidget); m_fileSizeLabel = new QLabel (baseWidget); m_filePixmapLabel->setMinimumSize (s_pixmapLabelMinimumSize); lay->addWidget (m_filePixmapLabel, 0, 0); lay->addWidget (m_fileSizeLabel, 1, 0, Qt::AlignHCenter); lay->setRowStretch (0, 1); connect (m_filePixmapLabel, &kpResizeSignallingLabel::resized, this, &kpDocumentSaveOptionsPreviewDialog::updatePixmapPreview); } kpDocumentSaveOptionsPreviewDialog::~kpDocumentSaveOptionsPreviewDialog () { delete m_filePixmap; } // public QSize kpDocumentSaveOptionsPreviewDialog::preferredMinimumSize () const { const auto contentsWidth = 180; const auto totalMarginsWidth = fontMetrics ().height (); return {contentsWidth + totalMarginsWidth, contentsWidth * 3 / 4 + totalMarginsWidth}; } // public slot void kpDocumentSaveOptionsPreviewDialog::setFilePixmapAndSize (const QImage &pixmap, qint64 fileSize) { delete m_filePixmap; m_filePixmap = new QImage (pixmap); updatePixmapPreview (); m_fileSize = fileSize; const kpCommandSize::SizeType pixmapSize = kpCommandSize::PixmapSize (pixmap); // (int cast is safe as long as the file size is not more than 20 million // -- i.e. INT_MAX / 100 -- times the pixmap size) const int percent = pixmapSize ? qMax (1, static_cast (static_cast (fileSize * 100 / pixmapSize))) : 0; -#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogDialogs) << "kpDocumentSaveOptionsPreviewDialog::setFilePixmapAndSize()" << " pixmapSize=" << pixmapSize << " fileSize=" << fileSize << " raw fileSize/pixmapSize%=" << (pixmapSize ? (kpCommandSize::SizeType) fileSize * 100 / pixmapSize : 0); -#endif m_fileSizeLabel->setText (i18np ("1 byte (approx. %2%)", "%1 bytes (approx. %2%)", m_fileSize, percent)); } // public slot void kpDocumentSaveOptionsPreviewDialog::updatePixmapPreview () { -#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogDialogs) << "kpDocumentSaveOptionsPreviewDialog::updatePreviewPixmap()" << " filePixmapLabel.size=" << m_filePixmapLabel->size () << " filePixmap.size=" << m_filePixmap->size (); -#endif if (m_filePixmap) { int maxNewWidth = qMin (m_filePixmap->width (), m_filePixmapLabel->width ()), maxNewHeight = qMin (m_filePixmap->height (), m_filePixmapLabel->height ()); double keepsAspect = kpTransformPreviewDialog::aspectScale ( maxNewWidth, maxNewHeight, m_filePixmap->width (), m_filePixmap->height ()); - #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET + qCDebug(kpLogDialogs) << "\tmaxNewWidth=" << maxNewWidth << " maxNewHeight=" << maxNewHeight << " keepsAspect=" << keepsAspect; - #endif const int newWidth = kpTransformPreviewDialog::scaleDimension ( m_filePixmap->width (), keepsAspect, 1, maxNewWidth); const int newHeight = kpTransformPreviewDialog::scaleDimension ( m_filePixmap->height (), keepsAspect, 1, maxNewHeight); - #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET + qCDebug(kpLogDialogs) << "\tnewWidth=" << newWidth << " newHeight=" << newHeight; - #endif QImage transformedPixmap = kpPixmapFX::scale (*m_filePixmap, newWidth, newHeight); QImage labelPixmap (m_filePixmapLabel->width (), m_filePixmapLabel->height (), QImage::Format_ARGB32_Premultiplied); labelPixmap.fill(QColor(Qt::transparent).rgba()); kpPixmapFX::setPixmapAt (&labelPixmap, (labelPixmap.width () - transformedPixmap.width ()) / 2, (labelPixmap.height () - transformedPixmap.height ()) / 2, transformedPixmap); m_filePixmapLabel->setPixmap (QPixmap::fromImage(labelPixmap)); } else { m_filePixmapLabel->setPixmap (QPixmap ()); } } // protected virtual [base QWidget] void kpDocumentSaveOptionsPreviewDialog::closeEvent (QCloseEvent *e) { -#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogDialogs) << "kpDocumentSaveOptionsPreviewDialog::closeEvent()"; -#endif QWidget::closeEvent (e); emit finished (); } // protected virtual [base QWidget] void kpDocumentSaveOptionsPreviewDialog::moveEvent (QMoveEvent *e) { -#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogDialogs) << "kpDocumentSaveOptionsPreviewDialog::moveEvent()"; -#endif QWidget::moveEvent (e); emit moved (); } // protected virtual [base QWidget] void kpDocumentSaveOptionsPreviewDialog::resizeEvent (QResizeEvent *e) { -#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogDialogs) << "kpDocumentSaveOptionsPreviewDialog::resizeEvent()"; -#endif QWidget::resizeEvent (e); emit resized (); } diff --git a/document/kpDocument.cpp b/document/kpDocument.cpp index 521ad5ee..fa231976 100644 --- a/document/kpDocument.cpp +++ b/document/kpDocument.cpp @@ -1,459 +1,447 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT 1 - #include "kpDocument.h" #include "kpDocumentPrivate.h" #include "layers/selections/kpAbstractSelection.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "environments/document/kpDocumentEnvironment.h" #include "document/kpDocumentSaveOptions.h" #include "imagelib/kpDocumentMetaInfo.h" #include "imagelib/effects/kpEffectReduceColors.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "widgets/toolbars/kpToolToolBar.h" #include "lgpl/generic/kpUrlFormatter.h" #include "kpLogCategories.h" #include #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------- kpDocument::kpDocument (int w, int h, kpDocumentEnvironment *environ) : QObject (), m_constructorWidth (w), m_constructorHeight (h), m_isFromExistingURL (false), m_savedAtLeastOnceBefore (false), m_saveOptions (new kpDocumentSaveOptions ()), m_metaInfo (new kpDocumentMetaInfo ()), m_modified (false), m_selection (nullptr), m_oldWidth (-1), m_oldHeight (-1), d (new kpDocumentPrivate ()) { -#if DEBUG_KP_DOCUMENT && 0 qCDebug(kpLogDocument) << "kpDocument::kpDocument (" << w << "," << h << ")"; -#endif m_image = new kpImage(w, h, QImage::Format_ARGB32_Premultiplied); m_image->fill(QColor(Qt::white).rgb()); d->environ = environ; } //--------------------------------------------------------------------- kpDocument::~kpDocument () { delete d; delete m_image; delete m_saveOptions; delete m_metaInfo; delete m_selection; } //--------------------------------------------------------------------- // public kpDocumentEnvironment *kpDocument::environ () const { return d->environ; } //--------------------------------------------------------------------- // public void kpDocument::setEnviron (kpDocumentEnvironment *environ) { d->environ = environ; } //--------------------------------------------------------------------- // public bool kpDocument::savedAtLeastOnceBefore () const { return m_savedAtLeastOnceBefore; } //--------------------------------------------------------------------- // public QUrl kpDocument::url () const { return m_url; } //--------------------------------------------------------------------- // public void kpDocument::setURL (const QUrl &url, bool isFromExistingURL) { m_url = url; m_isFromExistingURL = isFromExistingURL; } //--------------------------------------------------------------------- // public bool kpDocument::isFromExistingURL () const { return m_isFromExistingURL; } //--------------------------------------------------------------------- // public bool kpDocument::urlExists (const QUrl &url) const { if (url.isEmpty()) { return false; } KIO::StatJob *job = KIO::stat (url, KIO::StatJob::SourceSide, 0); KJobWidgets::setWindow (job, d->environ->dialogParent ()); return job->exec(); } //--------------------------------------------------------------------- // public QString kpDocument::prettyUrl () const { return kpUrlFormatter::PrettyUrl (m_url); } //--------------------------------------------------------------------- // public QString kpDocument::prettyFilename () const { return kpUrlFormatter::PrettyFilename (m_url); } //--------------------------------------------------------------------- // public const kpDocumentSaveOptions *kpDocument::saveOptions () const { return m_saveOptions; } //--------------------------------------------------------------------- // public void kpDocument::setSaveOptions (const kpDocumentSaveOptions &saveOptions) { *m_saveOptions = saveOptions; } //--------------------------------------------------------------------- // public const kpDocumentMetaInfo *kpDocument::metaInfo () const { return m_metaInfo; } //--------------------------------------------------------------------- // public void kpDocument::setMetaInfo (const kpDocumentMetaInfo &metaInfo) { *m_metaInfo = metaInfo; } //--------------------------------------------------------------------- /* * Properties */ void kpDocument::setModified (bool yes) { if (yes == m_modified) { return; } m_modified = yes; if (yes) { emit documentModified (); } } //--------------------------------------------------------------------- bool kpDocument::isModified () const { return m_modified; } //--------------------------------------------------------------------- bool kpDocument::isEmpty () const { return url ().isEmpty () && !isModified (); } //--------------------------------------------------------------------- int kpDocument::constructorWidth () const { return m_constructorWidth; } //--------------------------------------------------------------------- int kpDocument::width (bool ofSelection) const { return (ofSelection && m_selection) ? m_selection->width() : m_image->width(); } //--------------------------------------------------------------------- int kpDocument::oldWidth () const { return m_oldWidth; } //--------------------------------------------------------------------- void kpDocument::setWidth (int w, const kpColor &backgroundColor) { resize (w, height (), backgroundColor); } //--------------------------------------------------------------------- int kpDocument::constructorHeight () const { return m_constructorHeight; } //--------------------------------------------------------------------- int kpDocument::height (bool ofSelection) const { return (ofSelection && m_selection) ? m_selection->height() : m_image->height(); } //--------------------------------------------------------------------- int kpDocument::oldHeight () const { return m_oldHeight; } //--------------------------------------------------------------------- void kpDocument::setHeight (int h, const kpColor &backgroundColor) { resize (width (), h, backgroundColor); } //--------------------------------------------------------------------- QRect kpDocument::rect (bool ofSelection) const { return (ofSelection && m_selection) ? m_selection->boundingRect() : m_image->rect(); } //--------------------------------------------------------------------- // public kpImage kpDocument::getImageAt (const QRect &rect) const { return kpPixmapFX::getPixmapAt (*m_image, rect); } //--------------------------------------------------------------------- // public void kpDocument::setImageAt (const kpImage &image, const QPoint &at) { -#if DEBUG_KP_DOCUMENT && 0 qCDebug(kpLogDocument) << "kpDocument::setImageAt (image (w=" << image.width () << ",h=" << image.height () << "), x=" << at.x () << ",y=" << at.y (); -#endif kpPixmapFX::setPixmapAt (m_image, at, image); slotContentsChanged (QRect (at.x (), at.y (), image.width (), image.height ())); } //--------------------------------------------------------------------- // public kpImage kpDocument::image (bool ofSelection) const { kpImage ret; if (ofSelection) { kpAbstractImageSelection *imageSel = imageSelection (); Q_ASSERT (imageSel); ret = imageSel->baseImage (); } else { ret = *m_image; } return ret; } //--------------------------------------------------------------------- // public kpImage *kpDocument::imagePointer () const { return m_image; } //--------------------------------------------------------------------- // public void kpDocument::setImage (const kpImage &image) { m_oldWidth = width (); m_oldHeight = height (); *m_image = image; if (m_oldWidth == width () && m_oldHeight == height ()) { slotContentsChanged (image.rect ()); } else { slotSizeChanged (QSize (width (), height ())); } } //--------------------------------------------------------------------- // public void kpDocument::setImage (bool ofSelection, const kpImage &image) { if (ofSelection) { kpAbstractImageSelection *imageSel = imageSelection (); // Have to have an image selection in order to set its pixmap. Q_ASSERT (imageSel); imageSel->setBaseImage (image); } else { setImage (image); } } //--------------------------------------------------------------------- void kpDocument::fill (const kpColor &color) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::fill ()"; -#endif m_image->fill(color.toQRgb()); slotContentsChanged (m_image->rect ()); } //--------------------------------------------------------------------- void kpDocument::resize (int w, int h, const kpColor &backgroundColor) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::resize (" << w << "," << h << ")"; -#endif m_oldWidth = width (); m_oldHeight = height (); -#if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "\toldWidth=" << m_oldWidth << " oldHeight=" << m_oldHeight; -#endif if (w == m_oldWidth && h == m_oldHeight) { return; } kpPixmapFX::resize (m_image, w, h, backgroundColor); slotSizeChanged (QSize (width (), height ())); } //--------------------------------------------------------------------- void kpDocument::slotContentsChanged (const QRect &rect) { setModified (); emit contentsChanged (rect); } //--------------------------------------------------------------------- void kpDocument::slotSizeChanged (const QSize &newSize) { setModified (); emit sizeChanged (newSize.width(), newSize.height()); emit sizeChanged (newSize); } //--------------------------------------------------------------------- diff --git a/document/kpDocumentSaveOptions.cpp b/document/kpDocumentSaveOptions.cpp index b941b22b..68fb1ecd 100644 --- a/document/kpDocumentSaveOptions.cpp +++ b/document/kpDocumentSaveOptions.cpp @@ -1,623 +1,617 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT_SAVE_OPTIONS 1 - #include "kpDocumentSaveOptions.h" #include "kpDefs.h" #include "pixmapfx/kpPixmapFX.h" #include #include "kpLogCategories.h" #include #include #include #include //--------------------------------------------------------------------- class kpDocumentSaveOptionsPrivate { public: QString m_mimeType; int m_colorDepth{}; bool m_dither{}; int m_quality{}; }; //--------------------------------------------------------------------- kpDocumentSaveOptions::kpDocumentSaveOptions () : d (new kpDocumentSaveOptionsPrivate ()) { d->m_mimeType = invalidMimeType (); d->m_colorDepth = invalidColorDepth (); d->m_dither = initialDither (); d->m_quality = invalidQuality (); } //--------------------------------------------------------------------- kpDocumentSaveOptions::kpDocumentSaveOptions (const kpDocumentSaveOptions &rhs) : d (new kpDocumentSaveOptionsPrivate ()) { d->m_mimeType = rhs.mimeType (); d->m_colorDepth = rhs.colorDepth (); d->m_dither = rhs.dither (); d->m_quality = rhs.quality (); } //--------------------------------------------------------------------- kpDocumentSaveOptions::kpDocumentSaveOptions (const QString &mimeType, int colorDepth, bool dither, int quality) : d (new kpDocumentSaveOptionsPrivate ()) { d->m_mimeType = mimeType; d->m_colorDepth = colorDepth; d->m_dither = dither; d->m_quality = quality; } //--------------------------------------------------------------------- kpDocumentSaveOptions::~kpDocumentSaveOptions () { delete d; } //--------------------------------------------------------------------- // public bool kpDocumentSaveOptions::operator== (const kpDocumentSaveOptions &rhs) const { return (mimeType () == rhs.mimeType () && colorDepth () == rhs.colorDepth () && dither () == rhs.dither () && quality () == rhs.quality ()); } //--------------------------------------------------------------------- // public bool kpDocumentSaveOptions::operator!= (const kpDocumentSaveOptions &rhs) const { return !(*this == rhs); } //--------------------------------------------------------------------- // public kpDocumentSaveOptions &kpDocumentSaveOptions::operator= (const kpDocumentSaveOptions &rhs) { setMimeType (rhs.mimeType ()); setColorDepth (rhs.colorDepth ()); setDither (rhs.dither ()); setQuality (rhs.quality ()); return *this; } //--------------------------------------------------------------------- // public void kpDocumentSaveOptions::printDebug (const QString &prefix) const { const QString usedPrefix = !prefix.isEmpty () ? prefix + QLatin1String (": ") : QString(); qCDebug(kpLogDocument) << usedPrefix << "mimeType=" << mimeType () << " colorDepth=" << colorDepth () << " dither=" << dither () << " quality=" << quality (); } //--------------------------------------------------------------------- // public QString kpDocumentSaveOptions::mimeType () const { return d->m_mimeType; } //--------------------------------------------------------------------- // public void kpDocumentSaveOptions::setMimeType (const QString &mimeType) { Q_ASSERT(mimeType.isEmpty () || mimeType.contains ('/')); d->m_mimeType = mimeType; } //--------------------------------------------------------------------- // public static QString kpDocumentSaveOptions::invalidMimeType () { return {}; } //--------------------------------------------------------------------- // public static bool kpDocumentSaveOptions::mimeTypeIsInvalid (const QString &mimeType) { return (mimeType == invalidMimeType ()); } //--------------------------------------------------------------------- // public bool kpDocumentSaveOptions::mimeTypeIsInvalid () const { return mimeTypeIsInvalid (mimeType ()); } //--------------------------------------------------------------------- // public int kpDocumentSaveOptions::colorDepth () const { return d->m_colorDepth; } // public void kpDocumentSaveOptions::setColorDepth (int depth) { d->m_colorDepth = depth; } // public static int kpDocumentSaveOptions::invalidColorDepth () { return -1; } // public static bool kpDocumentSaveOptions::colorDepthIsInvalid (int colorDepth) { return (colorDepth != 1 && colorDepth != 8 && colorDepth != 32); } // public bool kpDocumentSaveOptions::colorDepthIsInvalid () const { return colorDepthIsInvalid (colorDepth ()); } // public bool kpDocumentSaveOptions::dither () const { return d->m_dither; } // public void kpDocumentSaveOptions::setDither (bool dither) { d->m_dither = dither; } // public static int kpDocumentSaveOptions::initialDither () { return false; // to avoid accidental double dithering } // public int kpDocumentSaveOptions::quality () const { return d->m_quality; } // public void kpDocumentSaveOptions::setQuality (int quality) { d->m_quality = quality; } // public static int kpDocumentSaveOptions::invalidQuality () { return -2; } // public static bool kpDocumentSaveOptions::qualityIsInvalid (int quality) { return (quality < -1 || quality > 100); } // public bool kpDocumentSaveOptions::qualityIsInvalid () const { return qualityIsInvalid (quality ()); } // public static QString kpDocumentSaveOptions::defaultMimeType (const KConfigGroup &config) { return config.readEntry (kpSettingForcedMimeType, QStringLiteral ("image/png")); } // public static void kpDocumentSaveOptions::saveDefaultMimeType (KConfigGroup &config, const QString &mimeType) { config.writeEntry (kpSettingForcedMimeType, mimeType); } // public static int kpDocumentSaveOptions::defaultColorDepth (const KConfigGroup &config) { int colorDepth = config.readEntry (kpSettingForcedColorDepth, -1); if (colorDepthIsInvalid (colorDepth)) { // (not screen depth, in case of transparency) colorDepth = 32; } return colorDepth; } //--------------------------------------------------------------------- // public static void kpDocumentSaveOptions::saveDefaultColorDepth (KConfigGroup &config, int colorDepth) { config.writeEntry (kpSettingForcedColorDepth, colorDepth); } //--------------------------------------------------------------------- // public static int kpDocumentSaveOptions::defaultDither (const KConfigGroup &config) { return config.readEntry (kpSettingForcedDither, initialDither ()); } //--------------------------------------------------------------------- // public static void kpDocumentSaveOptions::saveDefaultDither (KConfigGroup &config, bool dither) { config.writeEntry (kpSettingForcedDither, dither); } //--------------------------------------------------------------------- // public static int kpDocumentSaveOptions::defaultQuality (const KConfigGroup &config) { int val = config.readEntry (kpSettingForcedQuality, -1); return qualityIsInvalid (val) ? -1 : val; } //--------------------------------------------------------------------- // public static void kpDocumentSaveOptions::saveDefaultQuality (KConfigGroup &config, int quality) { config.writeEntry (kpSettingForcedQuality, quality); } //--------------------------------------------------------------------- // public static kpDocumentSaveOptions kpDocumentSaveOptions::defaultDocumentSaveOptions (const KConfigGroup &config) { kpDocumentSaveOptions saveOptions; saveOptions.setMimeType (defaultMimeType (config)); saveOptions.setColorDepth (defaultColorDepth (config)); saveOptions.setDither (defaultDither (config)); saveOptions.setQuality (defaultQuality (config)); -#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS saveOptions.printDebug ("kpDocumentSaveOptions::defaultDocumentSaveOptions()"); -#endif return saveOptions; } //--------------------------------------------------------------------- // public static bool kpDocumentSaveOptions::saveDefaultDifferences (KConfigGroup &config, const kpDocumentSaveOptions &oldDocInfo, const kpDocumentSaveOptions &newDocInfo) { bool savedSomething = false; -#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS qCDebug(kpLogDocument) << "kpDocumentSaveOptions::saveDefaultDifferences()"; oldDocInfo.printDebug ("\told"); newDocInfo.printDebug ("\tnew"); -#endif if (newDocInfo.mimeType () != oldDocInfo.mimeType ()) { saveDefaultMimeType (config, newDocInfo.mimeType ()); savedSomething = true; } if (newDocInfo.colorDepth () != oldDocInfo.colorDepth ()) { saveDefaultColorDepth (config, newDocInfo.colorDepth ()); savedSomething = true; } if (newDocInfo.dither () != oldDocInfo.dither ()) { saveDefaultDither (config, newDocInfo.dither ()); savedSomething = true; } if (newDocInfo.quality () != oldDocInfo.quality ()) { saveDefaultQuality (config, newDocInfo.quality ()); savedSomething = true; } return savedSomething; } //--------------------------------------------------------------------- static QStringList mimeTypesSupportingProperty (const QString &property, const QStringList &defaultMimeTypesWithPropertyList) { QStringList mimeTypeList; KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupMimeTypeProperties); if (cfg.hasKey (property)) { mimeTypeList = cfg.readEntry (property, QStringList ()); } else { mimeTypeList = defaultMimeTypesWithPropertyList; cfg.writeEntry (property, mimeTypeList); cfg.sync (); } return mimeTypeList; } //--------------------------------------------------------------------- static bool mimeTypeSupportsProperty (const QString &mimeType, const QString &property, const QStringList &defaultMimeTypesWithPropertyList) { const auto mimeTypeList = mimeTypesSupportingProperty ( property, defaultMimeTypesWithPropertyList); return mimeTypeList.contains (mimeType); } //--------------------------------------------------------------------- // SYNC: update mime info // // Only care about writable mimetypes. // // Run: // // branches/kolourpaint/control/scripts/gen_mimetype_line.sh Write | // branches/kolourpaint/control/scripts/split_mimetype_line.pl // // in the version of kdelibs/kimgio/ (e.g. KDE 4.0) KolourPaint is shipped with, // to check for any new mimetypes to add info for. In the methods below, // you can specify this info (maximum color depth, whether it's lossy etc.). // // Update the below list and if you do change any of that info, bump up // "kpSettingsGroupMimeTypeProperties" in kpDefs.h. // // Currently, Depth and Quality settings are mutually exclusive with // Depth overriding Quality. I've currently favoured Quality with the // below mimetypes (i.e. all lossy mimetypes are only given Quality settings, // no Depth settings). // // Mimetypes done: // image/bmp // image/jpeg // image/jp2 // image/png // image/tiff // image/x-eps // image/x-pcx // image/x-portable-bitmap // image/x-portable-graymap // image/x-portable-pixmap // image/x-rgb // image/x-tga // image/x-xbitmap // image/x-xpixmap // video/x-mng [COULD NOT TEST] // // To test whether depth is configurable, write an image in the new // mimetype with all depths and read each one back. See what // kpDocument thinks the depth is when it gets QImage to read it. // public static int kpDocumentSaveOptions::mimeTypeMaximumColorDepth (const QString &mimeType) { QStringList defaultList; // SYNC: update mime info here // Grayscale actually (unenforced since depth not set to configurable) defaultList << QStringLiteral ("image/x-eps:32"); defaultList << QStringLiteral ("image/x-portable-bitmap:1"); // Grayscale actually (unenforced since depth not set to configurable) defaultList << QStringLiteral ("image/x-portable-graymap:8"); defaultList << QStringLiteral ("image/x-xbitmap:1"); const auto mimeTypeList = mimeTypesSupportingProperty ( kpSettingMimeTypeMaximumColorDepth, defaultList); const QString mimeTypeColon = mimeType + QLatin1String (":"); for (const auto & it : mimeTypeList) { if (it.startsWith (mimeTypeColon)) { int number = it.midRef (mimeTypeColon.length ()).toInt (); if (!colorDepthIsInvalid (number)) { return number; } } } return 32; } //--------------------------------------------------------------------- // public int kpDocumentSaveOptions::mimeTypeMaximumColorDepth () const { return mimeTypeMaximumColorDepth (mimeType ()); } //--------------------------------------------------------------------- // public static bool kpDocumentSaveOptions::mimeTypeHasConfigurableColorDepth (const QString &mimeType) { QStringList defaultMimeTypes; // SYNC: update mime info here defaultMimeTypes << QStringLiteral ("image/png"); defaultMimeTypes << QStringLiteral ("image/bmp"); defaultMimeTypes << QStringLiteral ("image/x-pcx"); // TODO: Only 1, 24 not 8; Qt only sees 32 but "file" cmd realizes // it's either 1 or 24. defaultMimeTypes << QStringLiteral ("image/x-rgb"); // TODO: Only 8 and 24 - no 1. defaultMimeTypes << QStringLiteral ("image/x-xpixmap"); return mimeTypeSupportsProperty (mimeType, kpSettingMimeTypeHasConfigurableColorDepth, defaultMimeTypes); } //--------------------------------------------------------------------- // public bool kpDocumentSaveOptions::mimeTypeHasConfigurableColorDepth () const { return mimeTypeHasConfigurableColorDepth (mimeType ()); } //--------------------------------------------------------------------- // public static bool kpDocumentSaveOptions::mimeTypeHasConfigurableQuality (const QString &mimeType) { QStringList defaultMimeTypes; // SYNC: update mime info here defaultMimeTypes << QStringLiteral ("image/jp2"); defaultMimeTypes << QStringLiteral ("image/jpeg"); defaultMimeTypes << QStringLiteral ("image/x-webp"); return mimeTypeSupportsProperty (mimeType, kpSettingMimeTypeHasConfigurableQuality, defaultMimeTypes); } //--------------------------------------------------------------------- // public bool kpDocumentSaveOptions::mimeTypeHasConfigurableQuality () const { return mimeTypeHasConfigurableQuality (mimeType ()); } //--------------------------------------------------------------------- // public int kpDocumentSaveOptions::isLossyForSaving (const QImage &image) const { auto ret = 0; if (mimeTypeMaximumColorDepth () < image.depth ()) { ret |= MimeTypeMaximumColorDepthLow; } if (mimeTypeHasConfigurableColorDepth () && !colorDepthIsInvalid () /*REFACTOR: guarantee it is valid*/ && ((colorDepth () < image.depth ()) || (colorDepth () < 32 && image.hasAlphaChannel()))) { ret |= ColorDepthLow; } if (mimeTypeHasConfigurableQuality () && !qualityIsInvalid ()) { ret |= Quality; } return ret; } //--------------------------------------------------------------------- diff --git a/document/kpDocument_Open.cpp b/document/kpDocument_Open.cpp index 4b82a346..a2e4d1b9 100644 --- a/document/kpDocument_Open.cpp +++ b/document/kpDocument_Open.cpp @@ -1,249 +1,237 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT 1 - #include "kpDocument.h" #include "kpDocumentPrivate.h" #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "environments/document/kpDocumentEnvironment.h" #include "document/kpDocumentSaveOptions.h" #include "imagelib/kpDocumentMetaInfo.h" #include "imagelib/effects/kpEffectReduceColors.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "lgpl/generic/kpUrlFormatter.h" #include "views/manager/kpViewManager.h" #include #include #include #include #include #include #include "kpLogCategories.h" #include #include #include //--------------------------------------------------------------------- void kpDocument::getDataFromImage(const QImage &image, kpDocumentSaveOptions &saveOptions, kpDocumentMetaInfo &metaInfo) { saveOptions.setColorDepth(image.depth()); saveOptions.setDither(false); // avoid double dithering when saving metaInfo.setDotsPerMeterX(image.dotsPerMeterX()); metaInfo.setDotsPerMeterY(image.dotsPerMeterY()); metaInfo.setOffset(image.offset()); QStringList keys = image.textKeys(); for (int i = 0; i < keys.count(); i++) { metaInfo.setText(keys[i], image.text(keys[i])); } } //--------------------------------------------------------------------- // public static QImage kpDocument::getPixmapFromFile(const QUrl &url, bool suppressDoesntExistDialog, QWidget *parent, kpDocumentSaveOptions *saveOptions, kpDocumentMetaInfo *metaInfo) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::getPixmapFromFile(" << url << "," << parent << ")"; -#endif if (saveOptions) { *saveOptions = kpDocumentSaveOptions (); } if (metaInfo) { *metaInfo = kpDocumentMetaInfo (); } if (url.isEmpty ()) { return {}; } KIO::StoredTransferJob *job = KIO::storedGet (url); KJobWidgets::setWindow(job, parent); if (!job->exec()) { if (!suppressDoesntExistDialog) { // TODO: Use "Cannot" instead of "Could not" in all dialogs in KolourPaint. // Or at least choose one consistently. // // TODO: Have captions for all dialogs in KolourPaint. KMessageBox::sorry (parent, i18n ("Could not open \"%1\".", kpUrlFormatter::PrettyFilename (url))); } return {}; } QByteArray data = job->data(); QMimeDatabase db; QMimeType mimeType = db.mimeTypeForFileNameAndData(url.fileName(), data); if (saveOptions) { saveOptions->setMimeType(mimeType.name()); } -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tmimetype=" << mimeType.name(); qCDebug(kpLogDocument) << "\tsrc=" << url.path (); -#endif QBuffer buffer(&data); buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer); reader.setAutoTransform(true); reader.setDecideFormatFromContent(true); QImage image = reader.read(); if (image.isNull ()) { KMessageBox::sorry (parent, i18n ("Could not open \"%1\" - unsupported image format.\n" "The file may be corrupt.", kpUrlFormatter::PrettyFilename (url))); return {}; } -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tpixmap: depth=" << image.depth () << " hasAlphaChannel=" << image.hasAlphaChannel (); -#endif if ( saveOptions && metaInfo ) { getDataFromImage(image, *saveOptions, *metaInfo); } // make sure we always have Format_ARGB32_Premultiplied as this is the fastest to draw on // and Qt can not draw onto Format_Indexed8 (Qt-4.7) if ( image.format() != QImage::Format_ARGB32_Premultiplied ) { image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); } return image; } //--------------------------------------------------------------------- void kpDocument::openNew (const QUrl &url) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::openNew (" << url << ")"; -#endif m_image->fill(QColor(Qt::white).rgb()); setURL (url, false/*not from url*/); *m_saveOptions = kpDocumentSaveOptions (); if ( !url.isEmpty() ) { // guess the mimetype from url's filename extension. // // That way "kolourpaint doesnotexist.bmp" automatically // selects the BMP file format when the save dialog comes up for // the first time. QMimeDatabase mimeDb; m_saveOptions->setMimeType(mimeDb.mimeTypeForUrl(url).name()); } *m_metaInfo = kpDocumentMetaInfo (); m_modified = false; emit documentOpened (); } //--------------------------------------------------------------------- bool kpDocument::open (const QUrl &url, bool newDocSameNameIfNotExist) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::open (" << url << ")"; -#endif kpDocumentSaveOptions newSaveOptions; kpDocumentMetaInfo newMetaInfo; QImage newPixmap = kpDocument::getPixmapFromFile (url, newDocSameNameIfNotExist/*suppress "doesn't exist" dialog*/, d->environ->dialogParent (), &newSaveOptions, &newMetaInfo); if (!newPixmap.isNull ()) { delete m_image; m_image = new kpImage (newPixmap); setURL (url, true/*is from url*/); *m_saveOptions = newSaveOptions; *m_metaInfo = newMetaInfo; m_modified = false; emit documentOpened (); return true; } if (newDocSameNameIfNotExist) { if (urlExists (url)) // not just a permission error? { openNew (url); } else { openNew (QUrl ()); } return true; } return false; } //--------------------------------------------------------------------- diff --git a/document/kpDocument_Save.cpp b/document/kpDocument_Save.cpp index 53041dd2..c406b95c 100644 --- a/document/kpDocument_Save.cpp +++ b/document/kpDocument_Save.cpp @@ -1,481 +1,448 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT 1 - #include "kpDocument.h" #include "kpDocumentPrivate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kpLogCategories.h" #include #include #include #include #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "environments/document/kpDocumentEnvironment.h" #include "document/kpDocumentSaveOptions.h" #include "imagelib/kpDocumentMetaInfo.h" #include "imagelib/effects/kpEffectReduceColors.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "widgets/toolbars/kpToolToolBar.h" #include "lgpl/generic/kpUrlFormatter.h" #include "views/manager/kpViewManager.h" bool kpDocument::save (bool lossyPrompt) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::save(" << ",lossyPrompt=" << lossyPrompt << ") url=" << m_url << " savedAtLeastOnceBefore=" << savedAtLeastOnceBefore (); -#endif // TODO: check feels weak if (m_url.isEmpty () || m_saveOptions->mimeType ().isEmpty ()) { KMessageBox::detailedError (d->environ->dialogParent (), i18n ("Could not save image - insufficient information."), i18n ("URL: %1\n" "Mimetype: %2", prettyUrl (), m_saveOptions->mimeType ().isEmpty () ? i18n ("") : m_saveOptions->mimeType ()), i18nc ("@title:window", "Internal Error")); return false; } return saveAs (m_url, *m_saveOptions, lossyPrompt); } //--------------------------------------------------------------------- // public static bool kpDocument::lossyPromptContinue (const QImage &pixmap, const kpDocumentSaveOptions &saveOptions, QWidget *parent) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::lossyPromptContinue()"; -#endif #define QUIT_IF_CANCEL(messageBoxCommand) \ { \ if (messageBoxCommand != KMessageBox::Continue) \ { \ return false; \ } \ } const int lossyType = saveOptions.isLossyForSaving (pixmap); if (lossyType & (kpDocumentSaveOptions::MimeTypeMaximumColorDepthLow | kpDocumentSaveOptions::Quality)) { QMimeDatabase db; QUIT_IF_CANCEL ( KMessageBox::warningContinueCancel (parent, i18n ("

The %1 format may not be able" " to preserve all of the image's color information.

" "

Are you sure you want to save in this format?

", db.mimeTypeForName(saveOptions.mimeType()).comment()), // TODO: caption misleading for lossless formats that have // low maximum colour depth i18nc ("@title:window", "Lossy File Format"), KStandardGuiItem::save (), KStandardGuiItem::cancel(), QLatin1String ("SaveInLossyMimeTypeDontAskAgain"))); } else if (lossyType & kpDocumentSaveOptions::ColorDepthLow) { QUIT_IF_CANCEL ( KMessageBox::warningContinueCancel (parent, i18n ("

Saving the image at the low color depth of %1-bit" " may result in the loss of color information." // TODO: It looks like 8-bit QImage's now support alpha. // Update kpDocumentSaveOptions::isLossyForSaving() // and change "might" to "will". " Any transparency might also be removed.

" "

Are you sure you want to save at this color depth?

", saveOptions.colorDepth ()), i18nc ("@title:window", "Low Color Depth"), KStandardGuiItem::save (), KStandardGuiItem::cancel(), QLatin1String ("SaveAtLowColorDepthDontAskAgain"))); } #undef QUIT_IF_CANCEL return true; } //--------------------------------------------------------------------- // public static bool kpDocument::savePixmapToDevice (const QImage &image, QIODevice *device, const kpDocumentSaveOptions &saveOptions, const kpDocumentMetaInfo &metaInfo, bool lossyPrompt, QWidget *parent, bool *userCancelled) { if (userCancelled) { *userCancelled = false; } QString type = QMimeDatabase().mimeTypeForName (saveOptions.mimeType ()).preferredSuffix (); -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tmimeType=" << saveOptions.mimeType () << " type=" << type; -#endif + if (type.isEmpty ()) { return false; } if (lossyPrompt && !lossyPromptContinue (image, saveOptions, parent)) { if (userCancelled) { *userCancelled = true; } - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\treturning false because of lossyPrompt"; - #endif + return false; } // TODO: fix dup with kpDocumentSaveOptions::isLossyForSaving() const bool useSaveOptionsColorDepth = (saveOptions.mimeTypeHasConfigurableColorDepth () && !saveOptions.colorDepthIsInvalid ()); const bool useSaveOptionsQuality = (saveOptions.mimeTypeHasConfigurableQuality () && !saveOptions.qualityIsInvalid ()); // // Reduce colors if required // -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tuseSaveOptionsColorDepth=" << useSaveOptionsColorDepth << "current image depth=" << image.depth () << "save options depth=" << saveOptions.colorDepth (); -#endif + QImage imageToSave(image); if (useSaveOptionsColorDepth && imageToSave.depth () != saveOptions.colorDepth ()) { // TODO: I think this erases the mask! // // I suspect this doesn't matter since this is only called to // reduce color depth and QImage's with depth < 32 don't // support masks anyway. // // Later: I think the mask is preserved for 8-bit since Qt4 // seems to support it for QImage. imageToSave = kpEffectReduceColors::convertImageDepth (imageToSave, saveOptions.colorDepth (), saveOptions.dither ()); } // // Write Meta Info // imageToSave.setDotsPerMeterX (metaInfo.dotsPerMeterX ()); imageToSave.setDotsPerMeterY (metaInfo.dotsPerMeterY ()); imageToSave.setOffset (metaInfo.offset ()); QList keyList = metaInfo.textKeys (); for (QList ::const_iterator it = keyList.constBegin (); it != keyList.constEnd (); ++it) { imageToSave.setText (*it, metaInfo.text (*it)); } // // Save at required quality // int quality = -1; // default if (useSaveOptionsQuality) { quality = saveOptions.quality (); } -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tsaving"; -#endif + if (!imageToSave.save (device, type.toLatin1 (), quality)) { - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tQImage::save() returned false"; - #endif return false; } -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tsave OK"; -#endif return true; } //--------------------------------------------------------------------- static void CouldNotCreateTemporaryFileDialog (QWidget *parent) { KMessageBox::error (parent, i18n ("Could not save image - unable to create temporary file.")); } //--------------------------------------------------------------------- static void CouldNotSaveDialog (const QUrl &url, QWidget *parent) { // TODO: use file.errorString() KMessageBox::error (parent, i18n ("Could not save as \"%1\".", kpUrlFormatter::PrettyFilename (url))); } //--------------------------------------------------------------------- // public static bool kpDocument::savePixmapToFile (const QImage &pixmap, const QUrl &url, const kpDocumentSaveOptions &saveOptions, const kpDocumentMetaInfo &metaInfo, bool lossyPrompt, QWidget *parent) { // TODO: Use KIO::NetAccess:mostLocalURL() for accessing home:/ (and other // such local URLs) for efficiency and because only local writes // are atomic. -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::savePixmapToFile (" << url << ",lossyPrompt=" << lossyPrompt << ")"; saveOptions.printDebug (QLatin1String ("\tsaveOptions")); metaInfo.printDebug (QLatin1String ("\tmetaInfo")); -#endif if (lossyPrompt && !lossyPromptContinue (pixmap, saveOptions, parent)) { - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\treturning false because of lossyPrompt"; - #endif return false; } // Local file? if (url.isLocalFile ()) { const QString filename = url.toLocalFile (); // sync: All failure exit paths _must_ call QSaveFile::cancelWriting() or // else, the QSaveFile destructor will overwrite the file, // , despite the failure. QSaveFile atomicFileWriter (filename); { if (!atomicFileWriter.open (QIODevice::WriteOnly)) { // We probably don't need this as has not been // opened. atomicFileWriter.cancelWriting (); - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\treturning false because could not open QSaveFile" - << " error=" << atomicFileWriter.error () << endl; - #endif + << " error=" << atomicFileWriter.error (); ::CouldNotCreateTemporaryFileDialog (parent); return false; } // Write to local temporary file. if (!savePixmapToDevice (pixmap, &atomicFileWriter, saveOptions, metaInfo, false/*no lossy prompt*/, parent)) { atomicFileWriter.cancelWriting (); - #if DEBUG_KP_DOCUMENT - qCDebug(kpLogDocument) << "\treturning false because could not save pixmap to device" - << endl; - #endif + qCDebug(kpLogDocument) << "\treturning false because could not save pixmap to device"; ::CouldNotSaveDialog (url, parent); return false; } // Atomically overwrite local file with the temporary file // we saved to. if (!atomicFileWriter.commit ()) { atomicFileWriter.cancelWriting (); - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\tcould not close QSaveFile"; - #endif + ::CouldNotSaveDialog (url, parent); return false; } } // sync QSaveFile.cancelWriting() } // Remote file? else { // Create temporary file that is deleted when the variable goes // out of scope. QTemporaryFile tempFile; if (!tempFile.open ()) { - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\treturning false because could not open tempFile"; - #endif + ::CouldNotCreateTemporaryFileDialog (parent); return false; } // Write to local temporary file. if (!savePixmapToDevice (pixmap, &tempFile, saveOptions, metaInfo, false/*no lossy prompt*/, parent)) { - #if DEBUG_KP_DOCUMENT - qCDebug(kpLogDocument) << "\treturning false because could not save pixmap to device" - << endl; - #endif + qCDebug(kpLogDocument) << "\treturning false because could not save pixmap to device"; ::CouldNotSaveDialog (url, parent); return false; } // Collect name of temporary file now, as QTemporaryFile::fileName() // stops working after close() is called. const QString tempFileName = tempFile.fileName (); - #if DEBUG_KP_DOCUMENT - qCDebug(kpLogDocument) << "\ttempFileName='" << tempFileName << "'"; - #endif + + qCDebug(kpLogDocument) << "\ttempFileName='" << tempFileName << "'"; + Q_ASSERT (!tempFileName.isEmpty ()); tempFile.close (); if (tempFile.error () != QFile::NoError) { - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\treturning false because could not close"; - #endif ::CouldNotSaveDialog (url, parent); return false; } // Copy local temporary file to overwrite remote. // It's the kioslave's job to make this atomic (write to .part, then rename .part file) KIO::FileCopyJob *job = KIO::file_copy (QUrl::fromLocalFile (tempFileName), url, -1, KIO::Overwrite); KJobWidgets::setWindow (job, parent); if (!job->exec ()) { - #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\treturning false because could not upload"; - #endif + KMessageBox::error (parent, i18n ("Could not save image - failed to upload.")); return false; } } return true; } //--------------------------------------------------------------------- bool kpDocument::saveAs (const QUrl &url, const kpDocumentSaveOptions &saveOptions, bool lossyPrompt) { -#if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::saveAs (" << url << "," - << saveOptions.mimeType () << ")" << endl; -#endif + << saveOptions.mimeType () << ")"; if (kpDocument::savePixmapToFile (imageWithSelection (), url, saveOptions, *metaInfo (), lossyPrompt, d->environ->dialogParent ())) { setURL (url, true/*is from url*/); *m_saveOptions = saveOptions; m_modified = false; m_savedAtLeastOnceBefore = true; emit documentSaved (); return true; } return false; } //--------------------------------------------------------------------- diff --git a/document/kpDocument_Selection.cpp b/document/kpDocument_Selection.cpp index 4370fcfe..e040590c 100644 --- a/document/kpDocument_Selection.cpp +++ b/document/kpDocument_Selection.cpp @@ -1,350 +1,336 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT 1 - #include "kpDocument.h" #include "kpDocumentPrivate.h" #include #include #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "kpDefs.h" #include "environments/document/kpDocumentEnvironment.h" #include "layers/selections/kpAbstractSelection.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "layers/selections/text/kpTextSelection.h" // public kpAbstractSelection *kpDocument::selection () const { return m_selection; } //--------------------------------------------------------------------- // public kpAbstractImageSelection *kpDocument::imageSelection () const { return dynamic_cast (m_selection); } //--------------------------------------------------------------------- // public kpTextSelection *kpDocument::textSelection () const { return dynamic_cast (m_selection); } //--------------------------------------------------------------------- // public void kpDocument::setSelection (const kpAbstractSelection &selection) { -#if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "kpDocument::setSelection() sel boundingRect=" << selection.boundingRect (); -#endif d->environ->setQueueViewUpdates (); { const bool hadSelection = static_cast (m_selection); auto *oldSelection = m_selection; // (must be called before giving the document a new selection, to // avoid a potential mess where switchToCompatibleTool() ends // the current selection tool, killing the new selection) bool isTextChanged = false; d->environ->switchToCompatibleTool (selection, &isTextChanged); Q_ASSERT (m_selection == oldSelection); m_selection = selection.clone (); // There's no need to uninitialize the old selection // (e.g. call disconnect()) since we delete it later. connect (m_selection, &kpAbstractSelection::changed, this, &kpDocument::slotContentsChanged); // // Now all kpDocument state has been set. // We can _only_ change the environment after that, as the environment // may access the document. Exception is above with // switchToCompatibleTool(). // d->environ->assertMatchingUIState (selection); // // Now all kpDocument and environment state has been set. // We can _only_ fire signals after that, as the signal receivers (the // "wider environment") may access the document and the environment. // - #if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "\tcheck sel " << (int *) m_selection << " boundingRect=" << m_selection->boundingRect (); - #endif if (oldSelection) { if (oldSelection->hasContent ()) { slotContentsChanged (oldSelection->boundingRect ()); } else { emit contentsChanged (oldSelection->boundingRect ()); } delete oldSelection; oldSelection = nullptr; } if (m_selection->hasContent ()) { slotContentsChanged (m_selection->boundingRect ()); } else { emit contentsChanged (m_selection->boundingRect ()); } if (!hadSelection) { emit selectionEnabled (true); } if (isTextChanged) { emit selectionIsTextChanged (textSelection ()); } } d->environ->restoreQueueViewUpdates (); -#if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "\tkpDocument::setSelection() ended"; -#endif } //--------------------------------------------------------------------- // public kpImage kpDocument::getSelectedBaseImage () const { auto *imageSel = imageSelection (); Q_ASSERT (imageSel); // Easy if we already have it :) const auto image = imageSel->baseImage (); if (!image.isNull ()) { return image; } const auto boundingRect = imageSel->boundingRect (); Q_ASSERT (boundingRect.isValid ()); // OPT: This is very slow. Image / More Effects ... calls us twice // unnecessarily. return imageSel->givenImageMaskedByShape (getImageAt (boundingRect)); } //--------------------------------------------------------------------- // public void kpDocument::imageSelectionPullFromDocument (const kpColor &backgroundColor) { auto *imageSel = imageSelection (); Q_ASSERT (imageSel); // Should not already have an image or we would not be pulling. Q_ASSERT (!imageSel->hasContent ()); const auto boundingRect = imageSel->boundingRect (); Q_ASSERT (boundingRect.isValid ()); // // Get selection image from document // auto selectedImage = getSelectedBaseImage (); d->environ->setQueueViewUpdates (); imageSel->setBaseImage (selectedImage); // // Fill opaque bits of the hole in the document // #if !defined (QT_NO_DEBUG) && !defined (NDEBUG) if (imageSel->transparency ().isTransparent ()) { Q_ASSERT (backgroundColor == imageSel->transparency ().transparentColor ()); } else { // If this method is begin called by a tool, the assert does not // have to hold since transparentColor() might not be defined in Opaque // Mode. // // If we were called by a tricky sequence of undo/redo commands, the assert // does not have to hold for additional reason, which is that // kpMainWindow::setImageSelectionTransparency() does not have to // set in Opaque Mode. // // In practice, it probably does hold but I wouldn't bet on it. } #endif kpImage eraseImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied); eraseImage.fill(backgroundColor.toQRgb()); // only paint the region of the shape of the selection QPainter painter(m_image); painter.setClipRegion(imageSel->shapeRegion()); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.drawImage(boundingRect.topLeft(), eraseImage); slotContentsChanged(boundingRect); d->environ->restoreQueueViewUpdates (); } //--------------------------------------------------------------------- // public void kpDocument::selectionDelete () { if ( !m_selection ) { return; } const auto boundingRect = m_selection->boundingRect (); Q_ASSERT (boundingRect.isValid ()); const auto selectionHadContent = m_selection->hasContent (); delete m_selection; m_selection = nullptr; // HACK to prevent document from being modified when // user cancels dragging out a new selection // REFACTOR: Extract this out into a method. if (selectionHadContent) { slotContentsChanged (boundingRect); } else { emit contentsChanged (boundingRect); } emit selectionEnabled (false); } //--------------------------------------------------------------------- // public void kpDocument::selectionCopyOntoDocument (bool applySelTransparency) { // Empty selection, just doing nothing if ( !m_selection || !m_selection->hasContent() ) { return; } const QRect boundingRect = m_selection->boundingRect (); Q_ASSERT (boundingRect.isValid ()); if (imageSelection ()) { if (applySelTransparency) { imageSelection ()->paint (m_image, rect ()); } else { imageSelection ()->paintWithBaseImage (m_image, rect ()); } } else { // (for antialiasing with background) m_selection->paint (m_image, rect ()); } slotContentsChanged (boundingRect); } //--------------------------------------------------------------------- // public void kpDocument::selectionPushOntoDocument (bool applySelTransparency) { selectionCopyOntoDocument (applySelTransparency); selectionDelete (); } //--------------------------------------------------------------------- // public kpImage kpDocument::imageWithSelection () const { -#if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "kpDocument::imageWithSelection()"; -#endif // Have selection? // // It need not have any content because e.g. a text box with an opaque // background, but no content, is still visually there. if (m_selection) { - #if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "\tselection @ " << m_selection->boundingRect (); - #endif kpImage output = *m_image; // (this is a NOP for image selections without content) m_selection->paint (&output, rect ()); return output; } else { - #if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "\tno selection"; - #endif return *m_image; } } //--------------------------------------------------------------------- diff --git a/environments/document/kpDocumentEnvironment.cpp b/environments/document/kpDocumentEnvironment.cpp index 950e0211..9c977dd8 100644 --- a/environments/document/kpDocumentEnvironment.cpp +++ b/environments/document/kpDocumentEnvironment.cpp @@ -1,211 +1,195 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_DOCUMENT_ENVIRONMENT 1 - #include "environments/document/kpDocumentEnvironment.h" #include "kpLogCategories.h" #include "mainWindow/kpMainWindow.h" #include "layers/selections/kpAbstractSelection.h" #include "document/kpDocument.h" #include "layers/selections/image/kpEllipticalImageSelection.h" #include "layers/selections/image/kpFreeFormImageSelection.h" #include "layers/selections/image/kpImageSelectionTransparency.h" #include "layers/selections/image/kpRectangularImageSelection.h" #include "layers/selections/text/kpTextSelection.h" #include "layers/selections/text/kpTextStyle.h" -#if DEBUG_KP_DOCUMENT_ENVIRONMENT - #include "tools/kpTool.h" -#endif +#include "tools/kpTool.h" #include "views/manager/kpViewManager.h" struct kpDocumentEnvironmentPrivate { }; kpDocumentEnvironment::kpDocumentEnvironment (kpMainWindow *mainWindow) : kpEnvironmentBase (mainWindow), d (new kpDocumentEnvironmentPrivate ()) { } kpDocumentEnvironment::~kpDocumentEnvironment () { delete d; } // public QWidget *kpDocumentEnvironment::dialogParent () const { return mainWindow (); } static kpViewManager *ViewManager (kpMainWindow *mw) { return mw->viewManager (); } // public void kpDocumentEnvironment::setQueueViewUpdates () const { ::ViewManager (mainWindow ())->setQueueUpdates (); } // public void kpDocumentEnvironment::restoreQueueViewUpdates () const { ::ViewManager (mainWindow ())->restoreQueueUpdates (); } //--------------------------------------------------------------------- // public void kpDocumentEnvironment::switchToCompatibleTool (const kpAbstractSelection &selection, bool *isTextChanged) const { -#if DEBUG_KP_DOCUMENT_ENVIRONMENT qCDebug(kpLogEnvironments) << "kpDocumentEnvironment::switchToCompatibleTool(" << &selection << ")" << " mainwindow.tool=" << (mainWindow ()->tool () ? mainWindow ()->tool ()->objectName () : nullptr) << " mainWindow.toolIsTextTool=" << mainWindow ()->toolIsTextTool () << " current selection=" << document ()->selection () << " new selection is text=" - << dynamic_cast (&selection); -#endif + << &selection; *isTextChanged = (mainWindow ()->toolIsTextTool () != (dynamic_cast (&selection) != nullptr)); // We don't change the Selection Tool if the new selection's // shape is merely different to the current tool's (e.g. rectangular // vs elliptical) because: // // 1. All image selection tools support editing selections of all the // different shapes anyway. // 2. Suppose the user is trying out different drags of selection borders // and then decides to paste a differently shaped selection before continuing // to try out different borders. If the pasting were to switch to // a differently shaped tool, the borders drawn after the paste would // be using a new shape rather than the shape before the paste. This // could get irritating so we don't do the switch. if (!mainWindow ()->toolIsASelectionTool () || *isTextChanged) { // See kpDocument::setSelection() APIDoc for this assumption. Q_ASSERT (!document ()->selection ()); // Switch to the appropriately shaped selection tool // _before_ we change the selection // (all selection tool's ::end() functions nuke the current selection) if (dynamic_cast (&selection)) { - #if DEBUG_KP_DOCUMENT_ENVIRONMENT qCDebug(kpLogEnvironments) << "\tswitch to rect selection tool"; - #endif mainWindow ()->slotToolRectSelection (); } else if (dynamic_cast (&selection)) { - #if DEBUG_KP_DOCUMENT_ENVIRONMENT qCDebug(kpLogEnvironments) << "\tswitch to elliptical selection tool"; - #endif mainWindow ()->slotToolEllipticalSelection (); } else if (dynamic_cast (&selection)) { - #if DEBUG_KP_DOCUMENT_ENVIRONMENT qCDebug(kpLogEnvironments) << "\tswitch to free form selection tool"; - #endif mainWindow ()->slotToolFreeFormSelection (); } else if (dynamic_cast (&selection)) { - #if DEBUG_KP_DOCUMENT_ENVIRONMENT qCDebug(kpLogEnvironments) << "\tswitch to text selection tool"; - #endif mainWindow ()->slotToolText (); } else { Q_ASSERT (!"Unknown selection type"); } } -#if DEBUG_KP_DOCUMENT_ENVIRONMENT qCDebug(kpLogEnvironments) << "kpDocumentEnvironment::switchToCompatibleTool(" << &selection << ") finished"; -#endif } //--------------------------------------------------------------------- // public void kpDocumentEnvironment::assertMatchingUIState (const kpAbstractSelection &selection) const { // Trap and try to recover from bugs. // TODO: See kpDocument::setSelection() API comment and determine best fix. const auto *imageSelection = dynamic_cast (&selection); const auto *textSelection = dynamic_cast (&selection); if (imageSelection) { if (imageSelection->transparency () != mainWindow ()->imageSelectionTransparency ()) { qCCritical(kpLogEnvironments) << "kpDocument::setSelection() sel's transparency differs " "from mainWindow's transparency - setting mainWindow's transparency " "to sel"; qCCritical(kpLogEnvironments) << "\tisOpaque: sel=" << imageSelection->transparency ().isOpaque () << " mainWindow=" << mainWindow ()->imageSelectionTransparency ().isOpaque (); mainWindow ()->setImageSelectionTransparency (imageSelection->transparency ()); } } else if (textSelection) { if (textSelection->textStyle () != mainWindow ()->textStyle ()) { qCCritical(kpLogEnvironments) << "kpDocument::setSelection() sel's textStyle differs " "from mainWindow's textStyle - setting mainWindow's textStyle " "to sel"; mainWindow ()->setTextStyle (textSelection->textStyle ()); } } else { Q_ASSERT (!"Unknown selection type"); } } diff --git a/generic/widgets/kpResizeSignallingLabel.cpp b/generic/widgets/kpResizeSignallingLabel.cpp index 66471cce..32b6822d 100644 --- a/generic/widgets/kpResizeSignallingLabel.cpp +++ b/generic/widgets/kpResizeSignallingLabel.cpp @@ -1,66 +1,63 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_RESIZE_SIGNALLING_LABEL 1 #include "generic/widgets/kpResizeSignallingLabel.h" #include #include #include "kpLogCategories.h" kpResizeSignallingLabel::kpResizeSignallingLabel (const QString &string, QWidget *parent ) : QLabel (string, parent) { } kpResizeSignallingLabel::kpResizeSignallingLabel (QWidget *parent ) : QLabel (parent) { } kpResizeSignallingLabel::~kpResizeSignallingLabel () = default; // protected virtual [base QLabel] void kpResizeSignallingLabel::resizeEvent (QResizeEvent *e) { -#if DEBUG_KP_RESIZE_SIGNALLING_LABEL qCDebug(kpLogMisc) << "kpResizeSignallingLabel::resizeEvent() newSize=" << e->size () << " oldSize=" << e->oldSize (); -#endif QLabel::resizeEvent (e); emit resized (); } diff --git a/imagelib/effects/kpEffectBalance.cpp b/imagelib/effects/kpEffectBalance.cpp index f3997ed3..8a66c37b 100644 --- a/imagelib/effects/kpEffectBalance.cpp +++ b/imagelib/effects/kpEffectBalance.cpp @@ -1,210 +1,200 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_BALANCE 1 - #include "kpEffectBalance.h" #include #include #include "kpLogCategories.h" #include "pixmapfx/kpPixmapFX.h" -#if DEBUG_KP_EFFECT_BALANCE - #include -#endif +#include static inline int between0And255 (int val) { if (val < 0) { return 0; } if (val > 255) { return 255; } return val; } static inline int brightness (int base, int strength) { return between0And255 (base + strength * 255 / 50); } static inline int contrast (int base, int strength) { return between0And255 ((base - 127) * (strength + 50) / 50 + 127); } static inline int gamma (int base, int strength) { return between0And255 (qRound (255.0 * std::pow (base / 255.0, 1.0 / std::pow (10., strength / 50.0)))); } static inline int brightnessContrastGamma (int base, int newBrightness, int newContrast, int newGamma) { return gamma (contrast (brightness (base, newBrightness), newContrast), newGamma); } static inline QRgb brightnessContrastGammaForRGB (QRgb rgb, int channels, int brightness, int contrast, int gamma) { int red = qRed (rgb); int green = qGreen (rgb); int blue = qBlue (rgb); if (channels & kpEffectBalance::Red) { red = brightnessContrastGamma (red, brightness, contrast, gamma); } if (channels & kpEffectBalance::Green) { green = brightnessContrastGamma (green, brightness, contrast, gamma); } if (channels & kpEffectBalance::Blue) { blue = brightnessContrastGamma (blue, brightness, contrast, gamma); } return qRgba (red, green, blue, qAlpha (rgb)); } // public static kpImage kpEffectBalance::applyEffect (const kpImage &image, int channels, int brightness, int contrast, int gamma) { -#if DEBUG_KP_EFFECT_BALANCE qCDebug(kpLogImagelib) << "kpEffectBalance::applyEffect(" << "channels=" << channels << ",brightness=" << brightness << ",contrast=" << contrast << ",gamma=" << gamma << ")"; - QTime timer; timer.start (); -#endif + QElapsedTimer timer; timer.start (); QImage qimage = image; -#if DEBUG_KP_EFFECT_BALANCE qCDebug(kpLogImagelib) << "\tconvertToImage=" << timer.restart (); -#endif quint8 transformRed [256], transformGreen [256], transformBlue [256]; for (int i = 0; i < 256; i++) { auto applied = static_cast (brightnessContrastGamma (i, brightness, contrast, gamma)); if (channels & kpEffectBalance::Red) { transformRed [i] = applied; } else { transformRed [i] = static_cast (i); } if (channels & kpEffectBalance::Green) { transformGreen [i] = applied; } else { transformGreen [i] = static_cast (i); } if (channels & kpEffectBalance::Blue) { transformBlue [i] = applied; } else { transformBlue [i] = static_cast (i); } } -#if DEBUG_KP_EFFECT_BALANCE qCDebug(kpLogImagelib) << "\tbuild lookup=" << timer.restart (); -#endif if (qimage.depth () > 8) { for (int y = 0; y < qimage.height (); y++) { for (int x = 0; x < qimage.width (); x++) { const QRgb rgb = qimage.pixel (x, y); const auto red = static_cast (qRed (rgb)); const auto green = static_cast (qGreen (rgb)); const auto blue = static_cast (qBlue (rgb)); const auto alpha = static_cast (qAlpha (rgb)); qimage.setPixel (x, y, qRgba (transformRed [red], transformGreen [green], transformBlue [blue], alpha)); } } } else { for (int i = 0; i < qimage.colorCount (); i++) { const QRgb rgb = qimage.color (i); const auto red = static_cast (qRed (rgb)); const auto green = static_cast (qGreen (rgb)); const auto blue = static_cast (qBlue (rgb)); const auto alpha = static_cast (qAlpha (rgb)); qimage.setColor (i, qRgba (transformRed [red], transformGreen [green], transformBlue [blue], alpha)); } } return qimage; } diff --git a/imagelib/effects/kpEffectBlurSharpen.cpp b/imagelib/effects/kpEffectBlurSharpen.cpp index 967547c5..5b3ac0a4 100644 --- a/imagelib/effects/kpEffectBlurSharpen.cpp +++ b/imagelib/effects/kpEffectBlurSharpen.cpp @@ -1,184 +1,173 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_BLUR_SHARPEN 1 - #include "kpEffectBlurSharpen.h" #include "blitz.h" #include "kpLogCategories.h" #include "pixmapfx/kpPixmapFX.h" -#if DEBUG_KP_EFFECT_BLUR_SHARPEN - #include -#endif +#include //--------------------------------------------------------------------- // // For info on "radius" and "sigma", see https://redskiesatnight.com/2005/04/06/sharpening-using-image-magick/ // // Daniel Duley says: // // // I don't think I can describe it any better than the article: The radius // controls many how pixels are taken into account when determining the value // of the center pixel. This controls the quality [and speed] of the result but not // necessarily the strength. The sigma controls how those neighboring pixels // are weighted depending on how far the are from the center one. This is // closer to strength, but not exactly >:) // // static QImage BlurQImage(const QImage &qimage, int strength) { if (strength == 0) { return qimage; } // The numbers that follow were picked by experimentation to try to get // an effect linearly proportional to and at the same time, // be fast enough. // // I still have no idea what "radius" means. const double RadiusMin = 1; const double RadiusMax = 10; const double radius = RadiusMin + (strength - 1) * (RadiusMax - RadiusMin) / (kpEffectBlurSharpen::MaxStrength - 1); -#if DEBUG_KP_EFFECT_BLUR_SHARPEN qCDebug(kpLogImagelib) << "kpEffectBlurSharpen.cpp:BlurQImage(strength=" << strength << ")" << " radius=" << radius; -#endif QImage img(qimage); return Blitz::blur(img, qRound(radius)); } //--------------------------------------------------------------------- static QImage SharpenQImage (const QImage &qimage_, int strength) { QImage qimage = qimage_; if (strength == 0) { return qimage; } // The numbers that follow were picked by experimentation to try to get // an effect linearly proportional to and at the same time, // be fast enough. // // I still have no idea what "radius" and "sigma" mean. const double RadiusMin = 0.1; const double RadiusMax = 2.5; const double radius = RadiusMin + (strength - 1) * (RadiusMax - RadiusMin) / (kpEffectBlurSharpen::MaxStrength - 1); const double SigmaMin = 0.5; const double SigmaMax = 3.0; const double sigma = SigmaMin + (strength - 1) * (SigmaMax - SigmaMin) / (kpEffectBlurSharpen::MaxStrength - 1); const double RepeatMin = 1; const double RepeatMax = 2; const double repeat = qRound (RepeatMin + (strength - 1) * (RepeatMax - RepeatMin) / (kpEffectBlurSharpen::MaxStrength - 1)); -#if DEBUG_KP_EFFECT_BLUR_SHARPEN qCDebug(kpLogImagelib) << "kpEffectBlurSharpen.cpp:SharpenQImage(strength=" << strength << ")" << " radius=" << radius << " sigma=" << sigma << " repeat=" << repeat; -#endif + QElapsedTimer timer; for (int i = 0; i < repeat; i++) { - #if DEBUG_KP_EFFECT_BLUR_SHARPEN - QTime timer; timer.start (); - #endif + timer.restart(); + qimage = Blitz::gaussianSharpen (qimage, static_cast (radius), static_cast (sigma)); - #if DEBUG_KP_EFFECT_BLUR_SHARPEN + qCDebug(kpLogImagelib) << "\titeration #" + QString::number (i) << ": " + QString::number (timer.elapsed ()) << "ms"; - #endif } return qimage; } //--------------------------------------------------------------------- // public static kpImage kpEffectBlurSharpen::applyEffect (const kpImage &image, Type type, int strength) { -#if DEBUG_KP_EFFECT_BLUR_SHARPEN qCDebug(kpLogImagelib) << "kpEffectBlurSharpen::applyEffect(image.rect=" << image.rect () << ",type=" << int (type) << ",strength=" << strength << ")"; -#endif Q_ASSERT (strength >= MinStrength && strength <= MaxStrength); if (type == Blur) { return ::BlurQImage (image, strength); } if (type == Sharpen) { return ::SharpenQImage (image, strength); } if (type == MakeConfidential) { QImage img(image); return Blitz::blur(img, qMin(20, img.width() / 2)); } return kpImage(); } //--------------------------------------------------------------------- diff --git a/imagelib/effects/kpEffectEmboss.cpp b/imagelib/effects/kpEffectEmboss.cpp index 35b981ac..c3567c28 100644 --- a/imagelib/effects/kpEffectEmboss.cpp +++ b/imagelib/effects/kpEffectEmboss.cpp @@ -1,81 +1,78 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_EMBOSS 1 #include "kpEffectEmboss.h" #include "blitz.h" #include "kpLogCategories.h" #include "pixmapfx/kpPixmapFX.h" static QImage EmbossQImage (const QImage &qimage_, int strength) { QImage qimage = qimage_; if (strength == 0) { return qimage; } // The numbers that follow were picked by experimentation to try to get // an effect linearly proportional to and at the same time, // be fast enough. // // I still have no idea what "radius" and "sigma" mean. const auto radius = 0.0; const auto sigma = 1.0; const auto repeat = 1; for (int i = 0; i < repeat; i++) { qimage = Blitz::emboss (qimage, radius, sigma); } return qimage; } // public static kpImage kpEffectEmboss::applyEffect (const kpImage &image, int strength) { -#if DEBUG_KP_EFFECT_EMBOSS qCDebug(kpLogImagelib) << "kpEffectEmboss::applyEffect(strength=" << strength << ")" << endl; -#endif Q_ASSERT (strength >= MinStrength && strength <= MaxStrength); return ::EmbossQImage (image, strength); } diff --git a/imagelib/effects/kpEffectGrayscale.cpp b/imagelib/effects/kpEffectGrayscale.cpp index e1dc4657..639926c1 100644 --- a/imagelib/effects/kpEffectGrayscale.cpp +++ b/imagelib/effects/kpEffectGrayscale.cpp @@ -1,75 +1,73 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_PIXMAP_FX 1 - #include "kpEffectGrayscale.h" #include "pixmapfx/kpPixmapFX.h" static QRgb toGray (QRgb rgb) { // naive way that doesn't preserve brightness // int gray = (qRed (rgb) + qGreen (rgb) + qBlue (rgb)) / 3; // over-exaggerates red & blue // int gray = qGray (rgb); int gray = (212671 * qRed (rgb) + 715160 * qGreen (rgb) + 72169 * qBlue (rgb)) / 1000000; return qRgba (gray, gray, gray, qAlpha (rgb)); } // public static kpImage kpEffectGrayscale::applyEffect (const kpImage &image) { kpImage qimage(image); // TODO: Why not just write to the kpImage directly? if (qimage.depth () > 8) { for (int y = 0; y < qimage.height (); y++) { for (int x = 0; x < qimage.width (); x++) { qimage.setPixel (x, y, toGray (qimage.pixel (x, y))); } } } else { // 1- & 8- bit images use a color table for (int i = 0; i < qimage.colorCount (); i++) { qimage.setColor (i, toGray (qimage.color (i))); } } return qimage; } diff --git a/imagelib/effects/kpEffectInvert.cpp b/imagelib/effects/kpEffectInvert.cpp index 537a291f..570e7d0e 100644 --- a/imagelib/effects/kpEffectInvert.cpp +++ b/imagelib/effects/kpEffectInvert.cpp @@ -1,89 +1,85 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_INVERT 1 - #include "kpEffectInvert.h" #include #include "kpLogCategories.h" #include "pixmapfx/kpPixmapFX.h" // public static void kpEffectInvert::applyEffect (QImage *destImagePtr, int channels) { if (channels == kpEffectInvert::RGB) { destImagePtr->invertPixels (); return; } QRgb mask = qRgba ((channels & Red) ? 0xFF : 0, (channels & Green) ? 0xFF : 0, (channels & Blue) ? 0xFF : 0, 0/*don't invert alpha*/); -#if DEBUG_KP_EFFECT_INVERT qCDebug(kpLogImagelib) << "kpEffectInvert::applyEffect(channels=" << channels << ") mask=" << (int *) mask; -#endif if (destImagePtr->depth () > 8) { // Above version works for Qt 3.2 at least. // But this version will always work (slower, though) and supports // inverting particular channels. for (int y = 0; y < destImagePtr->height (); y++) { for (int x = 0; x < destImagePtr->width (); x++) { destImagePtr->setPixel (x, y, destImagePtr->pixel (x, y) ^ mask); } } } else { for (int i = 0; i < destImagePtr->colorCount (); i++) { destImagePtr->setColor (i, destImagePtr->color (i) ^ mask); } } } // public static QImage kpEffectInvert::applyEffect (const QImage &img, int channels) { QImage retImage = img; applyEffect (&retImage, channels); return retImage; } diff --git a/imagelib/effects/kpEffectReduceColors.cpp b/imagelib/effects/kpEffectReduceColors.cpp index afce41cc..c661dc47 100644 --- a/imagelib/effects/kpEffectReduceColors.cpp +++ b/imagelib/effects/kpEffectReduceColors.cpp @@ -1,240 +1,222 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2011 Martin Koller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_REDUCE_COLORS 1 - #include "imagelib/effects/kpEffectReduceColors.h" #include "kpLogCategories.h" //--------------------------------------------------------------------- static QImage::Format DepthToFormat (int depth) { // These values are QImage's supported depths. switch (depth) { case 1: // (can be MSB instead, I suppose) return QImage::Format_MonoLSB; case 8: return QImage::Format_Indexed8; case 16: return QImage::Format_ARGB4444_Premultiplied; case 24: return QImage::Format_ARGB6666_Premultiplied; case 32: return QImage::Format_ARGB32_Premultiplied; default: Q_ASSERT (!"unknown depth"); return QImage::Format_Invalid; } } //--------------------------------------------------------------------- // public static QImage kpEffectReduceColors::convertImageDepth (const QImage &image, int depth, bool dither) { -#if DEBUG_KP_EFFECT_REDUCE_COLORS qCDebug(kpLogImagelib) << "kpeffectreducecolors.cpp:ConvertImageDepth() changing image (w=" << image.width () << ",h=" << image.height () << ") depth from " << image.depth () << " to " << depth << " (dither=" << dither << ")" << endl; -#endif if (image.isNull ()) { return image; } if (depth == image.depth ()) { return image; } -#if DEBUG_KP_EFFECT_REDUCE_COLORS && 0 for (int y = 0; y < image.height (); y++) { for (int x = 0; x < image.width (); x++) { fprintf (stderr, " %08X", image.pixel (x, y)); } fprintf (stderr, "\n"); } -#endif // Hack around Qt's braindead QImage::convertToFormat(QImage::Format_MonoLSB, ...) // (with dithering off) which produces pathetic results with an image that // only has 2 colors - sometimes it just gives a completely black // result (try yellow and white as input). Instead, we simply preserve // the 2 colours. // // One use case is resaving a "color monochrome" image (<= 2 colors but // not necessarily black & white). if (depth == 1 && !dither) { - #if DEBUG_KP_EFFECT_REDUCE_COLORS qCDebug(kpLogImagelib) << "\tinvoking convert-to-depth 1 hack"; - #endif QRgb color0 = 0, color1 = 0; bool color0Valid = false, color1Valid = false; bool moreThan2Colors = false; QImage monoImage (image.width (), image.height (), QImage::Format_MonoLSB); monoImage.setColorCount (2); - #if DEBUG_KP_EFFECT_REDUCE_COLORS qCDebug(kpLogImagelib) << "\t\tinitialising output image w=" << monoImage.width () << ",h=" << monoImage.height () << ",d=" << monoImage.depth (); - #endif for (int y = 0; y < image.height (); y++) { for (int x = 0; x < image.width (); x++) { // (this can be transparent) QRgb imagePixel = image.pixel (x, y); if (color0Valid && imagePixel == color0) { monoImage.setPixel (x, y, 0); } else if (color1Valid && imagePixel == color1) { monoImage.setPixel (x, y, 1); } else if (!color0Valid) { color0 = imagePixel; color0Valid = true; monoImage.setPixel (x, y, 0); - #if DEBUG_KP_EFFECT_REDUCE_COLORS qCDebug(kpLogImagelib) << "\t\t\tcolor0=" << (int *) color0 << " at x=" << x << ",y=" << y; - #endif } else if (!color1Valid) { color1 = imagePixel; color1Valid = true; monoImage.setPixel (x, y, 1); - #if DEBUG_KP_EFFECT_REDUCE_COLORS qCDebug(kpLogImagelib) << "\t\t\tcolor1=" << (int *) color1 << " at x=" << x << ",y=" << y; - #endif } else { - #if DEBUG_KP_EFFECT_REDUCE_COLORS qCDebug(kpLogImagelib) << "\t\t\timagePixel=" << (int *) imagePixel << " at x=" << x << ",y=" << y << " moreThan2Colors - abort hack"; - #endif moreThan2Colors = true; // Dijkstra, this is clearer than double break'ing or // a check in both loops goto exit_loop; } } } exit_loop: if (!moreThan2Colors) { monoImage.setColor (0, color0Valid ? color0 : 0xFFFFFF); monoImage.setColor (1, color1Valid ? color1 : 0x000000); return monoImage; } } QImage retImage = image.convertToFormat (::DepthToFormat (depth), Qt::AutoColor | (dither ? Qt::DiffuseDither : Qt::ThresholdDither) | Qt::ThresholdAlphaDither | (dither ? Qt::PreferDither : Qt::AvoidDither)); -#if DEBUG_KP_EFFECT_REDUCE_COLORS qCDebug(kpLogImagelib) << "\tformat: before=" << image.format () << "after=" << retImage.format (); -#endif -#if DEBUG_KP_EFFECT_REDUCE_COLORS && 0 qCDebug(kpLogImagelib) << "After colour reduction:"; - for (int y = 0; y < image.height (); y++) - { - for (int x = 0; x < image.width (); x++) + if (kpLogImagelib().isDebugEnabled()) { + for (int y = 0; y < image.height (); y++) { - fprintf (stderr, " %08X", image.pixel (x, y)); + for (int x = 0; x < image.width (); x++) + { + fprintf (stderr, " %08X", image.pixel (x, y)); + } + fprintf (stderr, "\n"); } - fprintf (stderr, "\n"); } -#endif return retImage; } //--------------------------------------------------------------------- // public static void kpEffectReduceColors::applyEffect (QImage *destPtr, int depth, bool dither) { if (!destPtr) { return; } // You can't "reduce" to 32-bit since it's the highest depth. if (depth != 1 && depth != 8) { return; } *destPtr = convertImageDepth(*destPtr, depth, dither); // internally we always use QImage::Format_ARGB32_Premultiplied and // this effect is just an "effect" in that it changes the image (the look) somehow // When one wants a different depth on the file, then he needs to save the image // in that depth *destPtr = destPtr->convertToFormat(QImage::Format_ARGB32_Premultiplied); } //--------------------------------------------------------------------- QImage kpEffectReduceColors::applyEffect (const QImage &pm, int depth, bool dither) { QImage ret = pm; applyEffect (&ret, depth, dither); return ret; } //--------------------------------------------------------------------- diff --git a/imagelib/kpColor.cpp b/imagelib/kpColor.cpp index a1e63829..00c61da8 100644 --- a/imagelib/kpColor.cpp +++ b/imagelib/kpColor.cpp @@ -1,317 +1,309 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COLOR 1 - #include "kpColor.h" #include #include "kpLogCategories.h" //--------------------------------------------------------------------- kpColor::kpColor() : m_rgbaIsValid(false), m_rgba(0), m_colorCacheIsValid(false) { } //--------------------------------------------------------------------- kpColor::kpColor (int red, int green, int blue, bool isTransparent) : m_rgba(0), m_colorCacheIsValid(false) { -#if DEBUG_KP_COLOR qCDebug(kpLogImagelib) << "kpColor::(r=" << red << ",g=" << green << ",b=" << blue << ",isTrans=" << isTransparent << ")"; -#endif if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { qCCritical(kpLogImagelib) << "kpColor::(r=" << red << ",g=" << green << ",b=" << blue << ",t=" << isTransparent << ") passed out of range values"; m_rgbaIsValid = false; return; } m_rgba = qRgba (red, green, blue, isTransparent ? 0 : 255/*opaque*/); m_rgbaIsValid = true; } //--------------------------------------------------------------------- kpColor::kpColor (const QRgb &rgba) : m_colorCacheIsValid (false) { -#if DEBUG_KP_COLOR qCDebug(kpLogImagelib) << "kpColor::(rgba=" << (int *) rgba << ")"; -#endif m_rgba = rgba; m_rgbaIsValid = true; } //--------------------------------------------------------------------- kpColor::kpColor (const kpColor &rhs) : m_rgbaIsValid (rhs.m_rgbaIsValid), m_rgba (rhs.m_rgba), m_colorCacheIsValid (rhs.m_colorCacheIsValid), m_colorCache (rhs.m_colorCache) { -#if DEBUG_KP_COLOR qCDebug(kpLogImagelib) << "kpColor::()"; -#endif } //--------------------------------------------------------------------- // friend QDataStream &operator<< (QDataStream &stream, const kpColor &color) { stream << int (color.m_rgbaIsValid) << int (color.m_rgba); return stream; } //--------------------------------------------------------------------- // friend QDataStream &operator>> (QDataStream &stream, kpColor &color) { int a, b; stream >> a >> b; color.m_rgbaIsValid = a; color.m_rgba = static_cast (b); color.m_colorCacheIsValid = false; return stream; } //--------------------------------------------------------------------- kpColor &kpColor::operator= (const kpColor &rhs) { // (as soon as you add a ptr, you won't be complaining to me that this // method was unnecessary :)) if (this == &rhs) { return *this; } m_rgbaIsValid = rhs.m_rgbaIsValid; m_rgba = rhs.m_rgba; m_colorCacheIsValid = rhs.m_colorCacheIsValid; m_colorCache = rhs.m_colorCache; return *this; } bool kpColor::operator== (const kpColor &rhs) const { return isSimilarTo (rhs, kpColor::Exact); } bool kpColor::operator!= (const kpColor &rhs) const { return !(*this == rhs); } //--------------------------------------------------------------------- template inline dtype square (dtype val) { return val * val; } //--------------------------------------------------------------------- // public static int kpColor::processSimilarity (double colorSimilarity) { // sqrt (dr ^ 2 + dg ^ 2 + db ^ 2) <= colorSimilarity * sqrt (255 ^ 2 * 3) // dr ^ 2 + dg ^ 2 + db ^ 2 <= (colorSimilarity ^ 2) * (255 ^ 2 * 3) return int (square (colorSimilarity) * (square (255) * 3)); } //--------------------------------------------------------------------- bool kpColor::isSimilarTo (const kpColor &rhs, int processedSimilarity) const { // Are we the same? if (this == &rhs) { return true; } // Do we dither in terms of validity? if (isValid () != rhs.isValid ()) { return false; } // Are both of us invalid? if (!isValid ()) { return true; } // --- both are now valid --- if (m_rgba == rhs.m_rgba) { return true; } if (processedSimilarity == kpColor::Exact) { return false; } return (square (qRed (m_rgba) - qRed (rhs.m_rgba)) + square (qGreen (m_rgba) - qGreen (rhs.m_rgba)) + square (qBlue (m_rgba) - qBlue (rhs.m_rgba)) <= processedSimilarity); } //--------------------------------------------------------------------- // public bool kpColor::isValid () const { return m_rgbaIsValid; } //--------------------------------------------------------------------- // public int kpColor::red () const { if (!m_rgbaIsValid) { qCCritical(kpLogImagelib) << "kpColor::red() called with invalid kpColor"; return 0; } return qRed (m_rgba); } //--------------------------------------------------------------------- // public int kpColor::green () const { if (!m_rgbaIsValid) { qCCritical(kpLogImagelib) << "kpColor::green() called with invalid kpColor"; return 0; } return qGreen (m_rgba); } //--------------------------------------------------------------------- // public int kpColor::blue () const { if (!m_rgbaIsValid) { qCCritical(kpLogImagelib) << "kpColor::blue() called with invalid kpColor"; return 0; } return qBlue (m_rgba); } //--------------------------------------------------------------------- // public int kpColor::alpha () const { if (!m_rgbaIsValid) { qCCritical(kpLogImagelib) << "kpColor::alpha() called with invalid kpColor"; return 0; } return qAlpha (m_rgba); } //--------------------------------------------------------------------- // public bool kpColor::isTransparent () const { return (alpha () == 0); } //--------------------------------------------------------------------- // public QRgb kpColor::toQRgb () const { if (!m_rgbaIsValid) { qCCritical(kpLogImagelib) << "kpColor::toQRgb() called with invalid kpColor"; return 0; } return m_rgba; } //--------------------------------------------------------------------- // public QColor kpColor::toQColor () const { if (!m_rgbaIsValid) { qCCritical(kpLogImagelib) << "kpColor::toQColor() called with invalid kpColor"; return Qt::black; } if (m_colorCacheIsValid) { return m_colorCache; } m_colorCache = QColor(qRed(m_rgba), qGreen(m_rgba), qBlue(m_rgba), qAlpha(m_rgba)); m_colorCacheIsValid = true; return m_colorCache; } //--------------------------------------------------------------------- diff --git a/imagelib/kpColor_Constants.cpp b/imagelib/kpColor_Constants.cpp index b518a758..9de4baa9 100644 --- a/imagelib/kpColor_Constants.cpp +++ b/imagelib/kpColor_Constants.cpp @@ -1,117 +1,115 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COLOR 1 - #include "kpColor.h" static inline int RoundUp2 (int val) { return val % 2 ? val + 1 : val; } static inline int Bound0_255 (int val) { return qBound (0, val, 255); } enum { BlendDark = 25, BlendNormal = 50, BlendLight = 75, BlendAdd = 100 }; // Adds the 2 given colors together and then multiplies by the given . static inline kpColor Blend (const kpColor &a, const kpColor &b, int percent = ::BlendNormal) { return kpColor (::Bound0_255 (::RoundUp2 (a.red () + b.red ()) * percent / 100), ::Bound0_255 (::RoundUp2 (a.green () + b.green ()) * percent / 100), ::Bound0_255 (::RoundUp2 (a.blue () + b.blue ()) * percent / 100)); } static inline kpColor Add (const kpColor &a, const kpColor &b) { return ::Blend (a, b, ::BlendAdd); } // (intentionally _not_ an HSV darkener) static inline kpColor Dark (const kpColor &color) { return ::Blend (color, kpColor::Black); } // public static const int kpColor::Exact = 0; // public static const kpColor kpColor::Invalid; // LOTODO: what's wrong with explicitly specifying () constructor? const kpColor kpColor::Transparent (0, 0, 0, true/*isTransparent*/); // // Make our own colors in case weird ones like "Qt::cyan" // (turquoise) get changed by Qt. // const kpColor kpColor::Red (255, 0, 0); const kpColor kpColor::Green (0, 255, 0); const kpColor kpColor::Blue (0, 0, 255); const kpColor kpColor::Black (0, 0, 0); const kpColor kpColor::White (255, 255, 255); const kpColor kpColor::Yellow = ::Add (kpColor::Red, kpColor::Green); const kpColor kpColor::Purple = ::Add (kpColor::Red, kpColor::Blue); const kpColor kpColor::Aqua = ::Add (kpColor::Green, kpColor::Blue); const kpColor kpColor::Gray = ::Blend (kpColor::Black, kpColor::White); const kpColor kpColor::LightGray = ::Blend (kpColor::Gray, kpColor::White); const kpColor kpColor::Orange = ::Blend (kpColor::Red, kpColor::Yellow); const kpColor kpColor::Pink = ::Blend (kpColor::Red, kpColor::White); const kpColor kpColor::LightGreen = ::Blend (kpColor::Green, kpColor::White); const kpColor kpColor::LightBlue = ::Blend (kpColor::Blue, kpColor::White); const kpColor kpColor::Tan = ::Blend (kpColor::Yellow, kpColor::White); const kpColor kpColor::DarkRed = ::Dark (kpColor::Red); const kpColor kpColor::DarkOrange = ::Dark (kpColor::Orange); const kpColor kpColor::Brown = kpColor::DarkOrange; const kpColor kpColor::DarkYellow = ::Dark (kpColor::Yellow); const kpColor kpColor::DarkGreen = ::Dark (kpColor::Green); const kpColor kpColor::DarkAqua = ::Dark (kpColor::Aqua); const kpColor kpColor::DarkBlue = ::Dark (kpColor::Blue); const kpColor kpColor::DarkPurple = ::Dark (kpColor::Purple); const kpColor kpColor::DarkGray = ::Dark (kpColor::Gray); diff --git a/imagelib/kpFloodFill.cpp b/imagelib/kpFloodFill.cpp index fbfa18f3..ca826747 100644 --- a/imagelib/kpFloodFill.cpp +++ b/imagelib/kpFloodFill.cpp @@ -1,430 +1,413 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_FLOOD_FILL 1 - #include "kpFloodFill.h" #include #include #include #include #include #include "kpLogCategories.h" #include "kpColor.h" #include "kpImage.h" #include "kpDefs.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" //--------------------------------------------------------------------- class kpFillLine { public: kpFillLine (int y = -1, int x1 = -1, int x2 = -1) : m_y (y), m_x1 (x1), m_x2 (x2) { } static kpCommandSize::SizeType size () { return sizeof (kpFillLine); } int m_y, m_x1, m_x2; }; //--------------------------------------------------------------------- static kpCommandSize::SizeType FillLinesListSize (const QLinkedList &fillLines) { return (fillLines.size () * kpFillLine::size ()); } //--------------------------------------------------------------------- struct kpFloodFillPrivate { // // Copy of whatever was passed to the constructor. // kpImage *imagePtr{}; int x{}, y{}; kpColor color; int processedColorSimilarity{}; // // Set by Step 1. // kpColor colorToChange; // // Set by Step 2. // QLinkedList fillLines; QList < QLinkedList > fillLinesCache; QRect boundingRect; bool prepared{}; }; //--------------------------------------------------------------------- kpFloodFill::kpFloodFill (kpImage *image, int x, int y, const kpColor &color, int processedColorSimilarity) : d (new kpFloodFillPrivate ()) { d->imagePtr = image; d->x = x; d->y = y; d->color = color; d->processedColorSimilarity = processedColorSimilarity; d->prepared = false; } //--------------------------------------------------------------------- kpFloodFill::~kpFloodFill () { delete d; } //--------------------------------------------------------------------- // public kpColor kpFloodFill::color () const { return d->color; } //--------------------------------------------------------------------- // public int kpFloodFill::processedColorSimilarity () const { return d->processedColorSimilarity; } //--------------------------------------------------------------------- // public kpCommandSize::SizeType kpFloodFill::size () const { kpCommandSize::SizeType fillLinesCacheSize = 0; for (const auto &linesList : d->fillLinesCache) { fillLinesCacheSize += ::FillLinesListSize (linesList); } return ::FillLinesListSize(d->fillLines) + kpCommandSize::QImageSize(d->imagePtr) + fillLinesCacheSize; } //--------------------------------------------------------------------- // public void kpFloodFill::prepareColorToChange () { if (d->colorToChange.isValid ()) { return; } -#if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "kpFloodFill::prepareColorToChange()"; -#endif d->colorToChange = kpPixmapFX::getColorAtPixel (*d->imagePtr, QPoint (d->x, d->y)); } //--------------------------------------------------------------------- // public kpColor kpFloodFill::colorToChange () { prepareColorToChange (); return d->colorToChange; } //--------------------------------------------------------------------- // Derived from the zSprite2 Graphics Engine // private kpColor kpFloodFill::pixelColor (int x, int y, bool *beenHere) const { if (beenHere) { *beenHere = false; } Q_ASSERT (y >= 0 && y < static_cast (d->fillLinesCache.count ())); for (const auto &line : d->fillLinesCache [y]) { if (x >= line.m_x1 && x <= line.m_x2) { if (beenHere) { *beenHere = true; } return d->color; } } return kpPixmapFX::getColorAtPixel (*(d->imagePtr), QPoint (x, y)); } //--------------------------------------------------------------------- // private bool kpFloodFill::shouldGoTo (int x, int y) const { bool beenThere; const kpColor col = pixelColor (x, y, &beenThere); return (!beenThere && col.isSimilarTo (d->colorToChange, d->processedColorSimilarity)); } //--------------------------------------------------------------------- // private int kpFloodFill::findMinX (int y, int x) const { for (;;) { if (x < 0) { return 0; } if (shouldGoTo (x, y)) { x--; } else { return x + 1; } } } //--------------------------------------------------------------------- // private int kpFloodFill::findMaxX (int y, int x) const { for (;;) { if (x > d->imagePtr->width () - 1) { return d->imagePtr->width () - 1; } if (shouldGoTo (x, y)) { x++; } else { return x - 1; } } } //--------------------------------------------------------------------- // private void kpFloodFill::addLine (int y, int x1, int x2) { -#if DEBUG_KP_FLOOD_FILL && 0 qCDebug(kpLogImagelib) << "kpFillCommand::fillAddLine (" - << y << "," << x1 << "," << x2 << ")" << endl; -#endif + << y << "," << x1 << "," << x2 << ")"; d->fillLines.append (kpFillLine (y, x1, x2)); d->fillLinesCache [y].append ( kpFillLine (y/*OPT: can determine from array index*/, x1, x2)); d->boundingRect = d->boundingRect.united (QRect (QPoint (x1, y), QPoint (x2, y))); } //--------------------------------------------------------------------- // private void kpFloodFill::findAndAddLines (const kpFillLine &fillLine, int dy) { // out of bounds? if (fillLine.m_y + dy < 0 || fillLine.m_y + dy >= d->imagePtr->height ()) { return; } for (int xnow = fillLine.m_x1; xnow <= fillLine.m_x2; xnow++) { // At current position, right colour? if (shouldGoTo (xnow, fillLine.m_y + dy)) { // Find minimum and maximum x values int minxnow = findMinX (fillLine.m_y + dy, xnow); int maxxnow = findMaxX (fillLine.m_y + dy, xnow); // Draw line addLine (fillLine.m_y + dy, minxnow, maxxnow); // Move x pointer xnow = maxxnow; } } } //--------------------------------------------------------------------- // public void kpFloodFill::prepare () { if (d->prepared) { return; } -#if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "kpFloodFill::prepare()"; -#endif prepareColorToChange (); d->boundingRect = QRect (); -#if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tperforming NOP check"; -#endif // get the color we need to replace if (d->processedColorSimilarity == 0 && d->color == d->colorToChange) { // need to do absolutely nothing (this is a significant optimization // for people who randomly click a lot over already-filled areas) d->prepared = true; // sync with all "return true"'s return; } -#if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tcreating fillLinesCache"; -#endif // ready cache for (int i = 0; i < d->imagePtr->height (); i++) { d->fillLinesCache.append (QLinkedList ()); } -#if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tcreating fill lines"; -#endif // draw initial line addLine (d->y, findMinX (d->y, d->x), findMaxX (d->y, d->x)); for (QLinkedList ::ConstIterator it = d->fillLines.begin (); it != d->fillLines.end (); ++it) { - #if DEBUG_KP_FLOOD_FILL && 0 + #if 0 qCDebug(kpLogImagelib) << "Expanding from y=" << (*it).m_y << " x1=" << (*it).m_x1 - << " x2=" << (*it).m_x2 - << endl; + << " x2=" << (*it).m_x2; #endif // // Make more lines above and below current line. // // WARNING: Adds to end of "fillLines" (the linked list we are iterating // through). Therefore, "fillLines" must remain a linked list // - you cannot change it into a vector. Also, do not use // "foreach" for this loop as that makes a copy of the linked // list at the start and won't see new lines. // findAndAddLines (*it, -1); findAndAddLines (*it, +1); } -#if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tfinalising memory usage"; -#endif // finalize memory usage d->fillLinesCache.clear (); d->prepared = true; // sync with all "return true"'s } //--------------------------------------------------------------------- // public QRect kpFloodFill::boundingRect () { prepare (); return d->boundingRect; } //--------------------------------------------------------------------- // public void kpFloodFill::fill() { prepare(); QApplication::setOverrideCursor(Qt::WaitCursor); QPainter painter(d->imagePtr); // by definition, flood fill with a fully transparent color erases the pixels // and sets them to be fully transparent if ( d->color.isTransparent() ) { painter.setCompositionMode(QPainter::CompositionMode_Clear); } painter.setPen(d->color.toQColor()); for (const auto &l : d->fillLines) { if ( l.m_x1 == l.m_x2 ) { painter.drawPoint(l.m_x1, l.m_y); } else { painter.drawLine(l.m_x1, l.m_y, l.m_x2, l.m_y); } } QApplication::restoreOverrideCursor(); } //--------------------------------------------------------------------- diff --git a/imagelib/kpPainter.cpp b/imagelib/kpPainter.cpp index f9497600..90b9559e 100644 --- a/imagelib/kpPainter.cpp +++ b/imagelib/kpPainter.cpp @@ -1,523 +1,497 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_PAINTER 1 - #include "kpPainter.h" #include "kpImage.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "tools/flow/kpToolFlowBase.h" #include +#include #include #include #include "kpLogCategories.h" #include //--------------------------------------------------------------------- // public static bool kpPainter::pointsAreCardinallyAdjacent (const QPoint &p, const QPoint &q) { int dx = qAbs (p.x () - q.x ()); int dy = qAbs (p.y () - q.y ()); return (dx + dy == 1); } //--------------------------------------------------------------------- // Returns a random integer from 0 to 999 inclusive. static int RandomNumberFrom0to999 () { return (KRandom::random () % 1000); } //--------------------------------------------------------------------- // public static QList kpPainter::interpolatePoints (const QPoint &startPoint, const QPoint &endPoint, bool cardinalAdjacency, double probability) { -#if DEBUG_KP_PAINTER qCDebug(kpLogImagelib) << "CALL(startPoint=" << startPoint << ",endPoint=" << endPoint << ")"; -#endif QList ret; Q_ASSERT (probability >= 0.0 && probability <= 1.0); const int probabilityTimes1000 = qRound (probability * 1000); #define SHOULD_DRAW() (probabilityTimes1000 == 1000/*avoid ::RandomNumberFrom0to999() call*/ || \ ::RandomNumberFrom0to999 () < probabilityTimes1000) // Derived from the zSprite2 Graphics Engine. // "MODIFIED" comment shows deviation from zSprite2 and Bresenham's line // algorithm. const int x1 = startPoint.x (), y1 = startPoint.y (), x2 = endPoint.x (), y2 = endPoint.y (); // Difference of x and y values const int dx = x2 - x1; const int dy = y2 - y1; // Absolute values of differences const int ix = qAbs (dx); const int iy = qAbs (dy); // Larger of the x and y differences const int inc = ix > iy ? ix : iy; // Plot location int plotx = x1; int ploty = y1; int x = 0; int y = 0; if (SHOULD_DRAW ()) { ret.append (QPoint (plotx, ploty)); } for (int i = 0; i <= inc; i++) { // oldplotx is equally as valid but would look different // (but nobody will notice which one it is) const int oldploty = ploty; int plot = 0; x += ix; y += iy; if (x > inc) { plot++; x -= inc; if (dx < 0) { plotx--; } else { plotx++; } } if (y > inc) { plot++; y -= inc; if (dy < 0) { ploty--; } else { ploty++; } } if (plot) { if (cardinalAdjacency && plot == 2) { // MODIFIED: Every point is // horizontally or vertically adjacent to another point (if there // is more than 1 point, of course). This is in contrast to the // ordinary line algorithm which can create diagonal adjacencies. if (SHOULD_DRAW ()) { ret.append (QPoint (plotx, oldploty)); } } if (SHOULD_DRAW ()) { ret.append (QPoint (plotx, ploty)); } } } #undef SHOULD_DRAW return ret; } //--------------------------------------------------------------------- // public static void kpPainter::fillRect (kpImage *image, int x, int y, int width, int height, const kpColor &color) { kpPixmapFX::fillRect (image, x, y, width, height, color); } //--------------------------------------------------------------------- // are operating on the original image // (the original image is not passed to this function). // // = subset of the original image containing all the pixels in // // = the rectangle, relative to the painters, whose pixels we // want to change static bool ReadableImageWashRect (QPainter *rgbPainter, const QImage &image, const kpColor &colorToReplace, const QRect &imageRect, const QRect &drawRect, int processedColorSimilarity) { bool didSomething = false; -#if DEBUG_KP_PAINTER && 0 qCDebug(kpLogImagelib) << "kppixmapfx.cpp:WashRect(imageRect=" << imageRect << ",drawRect=" << drawRect << ")" << endl; -#endif // If you're going to pass painter pointers, those painters had better be // active (i.e. QPainter::begin() has been called). Q_ASSERT (!rgbPainter || rgbPainter->isActive ()); // make use of scanline coherence #define FLUSH_LINE() \ { \ if (rgbPainter) { \ if (startDrawX == x - 1) \ rgbPainter->drawPoint (startDrawX + imageRect.x (), \ y + imageRect.y ()); \ else \ rgbPainter->drawLine (startDrawX + imageRect.x (), \ y + imageRect.y (), \ x - 1 + imageRect.x (), \ y + imageRect.y ()); \ } \ didSomething = true; \ startDrawX = -1; \ } const int maxY = drawRect.bottom () - imageRect.top (); const int minX = drawRect.left () - imageRect.left (); const int maxX = drawRect.right () - imageRect.left (); for (int y = drawRect.top () - imageRect.top (); y <= maxY; y++) { int startDrawX = -1; int x; // for FLUSH_LINE() for (x = minX; x <= maxX; x++) { - #if DEBUG_KP_PAINTER && 0 + #if 0 fprintf (stderr, "y=%i x=%i colorAtPixel=%08X colorToReplace=%08X ... ", y, x, kpPixmapFX::getColorAtPixel (image, QPoint (x, y)).toQRgb (), colorToReplace.toQRgb ()); #endif if (kpPixmapFX::getColorAtPixel (image, QPoint (x, y)).isSimilarTo (colorToReplace, processedColorSimilarity)) { - #if DEBUG_KP_PAINTER && 0 + #if 0 fprintf (stderr, "similar\n"); #endif if (startDrawX < 0) { startDrawX = x; } } else { - #if DEBUG_KP_PAINTER && 0 + #if 0 fprintf (stderr, "different\n"); #endif if (startDrawX >= 0) { FLUSH_LINE (); } } } if (startDrawX >= 0) { FLUSH_LINE (); } } #undef FLUSH_LINE return didSomething; } //--------------------------------------------------------------------- struct WashPack { QPoint startPoint, endPoint; kpColor color; int penWidth{}, penHeight{}; kpColor colorToReplace; int processedColorSimilarity{}; QRect readableImageRect; QImage readableImage; }; //--------------------------------------------------------------------- static QRect Wash (kpImage *image, const QPoint &startPoint, const QPoint &endPoint, const kpColor &color, int penWidth, int penHeight, const kpColor &colorToReplace, int processedColorSimilarity, QRect (*drawFunc) (QPainter * /*rgbPainter*/, void * /*data*/)) { WashPack pack; pack.startPoint = startPoint; pack.endPoint = endPoint; pack.color = color; pack.penWidth = penWidth; pack.penHeight = penHeight; pack.colorToReplace = colorToReplace; pack.processedColorSimilarity = processedColorSimilarity; // Get the rectangle that bounds the changes and the pixmap for that // rectangle. const QRect normalizedRect = kpPainter::normalizedRect(pack.startPoint, pack.endPoint); pack.readableImageRect = kpTool::neededRect (normalizedRect, qMax (pack.penWidth, pack.penHeight)); -#if DEBUG_KP_PAINTER qCDebug(kpLogImagelib) << "kppainter.cpp:Wash() startPoint=" << startPoint << " endPoint=" << endPoint << " --> normalizedRect=" << normalizedRect - << " readableImageRect=" << pack.readableImageRect - << endl; -#endif + << " readableImageRect=" << pack.readableImageRect; pack.readableImage = kpPixmapFX::getPixmapAt (*image, pack.readableImageRect); QPainter painter(image); return (*drawFunc)(&painter, &pack); } //--------------------------------------------------------------------- void WashHelperSetup (QPainter *rgbPainter, const WashPack *pack) { // Set the drawing colors for the painters. if (rgbPainter) { rgbPainter->setPen (pack->color.toQColor()); } } //--------------------------------------------------------------------- static QRect WashLineHelper (QPainter *rgbPainter, void *data) { -#if DEBUG_KP_PAINTER && 0 - qCDebug(kpLogImagelib) << "Washing pixmap (w=" << rect.width () - << ",h=" << rect.height () << ")" << endl; - QTime timer; - int convAndWashTime; -#endif + qCDebug(kpLogImagelib) << "Washing pixmap"; + QElapsedTimer timer; timer.start(); + int convAndWashTime = 0; auto *pack = static_cast (data); // Setup painters. ::WashHelperSetup (rgbPainter, pack); bool didSomething = false; QList points = kpPainter::interpolatePoints (pack->startPoint, pack->endPoint); for (QList ::const_iterator pit = points.constBegin (); pit != points.constEnd (); ++pit) { // OPT: This may be reading and possibly writing pixels that were // visited on a previous iteration, since the pen is usually // bigger than 1 pixel. Maybe we could use QRegion to determine // all the non-intersecting regions and only wash each region once. // // Profiling needs to be done as QRegion is known to be a CPU hog. if (::ReadableImageWashRect (rgbPainter, pack->readableImage, pack->colorToReplace, pack->readableImageRect, kpToolFlowBase::hotRectForMousePointAndBrushWidthHeight ( *pit, pack->penWidth, pack->penHeight), pack->processedColorSimilarity)) { didSomething = true; } } -#if DEBUG_KP_PAINTER && 0 int ms = timer.restart (); - qCDebug(kpLogImagelib) << "\ttried to wash: " << ms << "ms" - << " (" << (ms ? (rect.width () * rect.height () / ms) : -1234) - << " pixels/ms)" - << endl; + qCDebug(kpLogImagelib) << "\ttried to wash: " << ms << "ms"; convAndWashTime += ms; -#endif // TODO: Rectangle may be too big. Use QRect::united() incrementally? // Efficiency? return didSomething ? pack->readableImageRect : QRect (); } //--------------------------------------------------------------------- // public static QRect kpPainter::washLine (kpImage *image, int x1, int y1, int x2, int y2, const kpColor &color, int penWidth, int penHeight, const kpColor &colorToReplace, int processedColorSimilarity) { return ::Wash (image, QPoint (x1, y1), QPoint (x2, y2), color, penWidth, penHeight, colorToReplace, processedColorSimilarity, &::WashLineHelper); } //--------------------------------------------------------------------- static QRect WashRectHelper (QPainter *rgbPainter, void *data) { auto *pack = static_cast (data); -#if DEBUG_KP_PAINTER && 0 - qCDebug(kpLogImagelib) << "Washing pixmap (w=" << rect.width () - << ",h=" << rect.height () << ")" << endl; - QTime timer; - int convAndWashTime; -#endif + qCDebug(kpLogImagelib) << "Washing pixmap"; + QElapsedTimer timer; timer.start(); + int convAndWashTime = 0; // Setup painters. ::WashHelperSetup (rgbPainter, pack); const QRect drawRect (pack->startPoint, pack->endPoint); bool didSomething = false; if (::ReadableImageWashRect (rgbPainter, pack->readableImage, pack->colorToReplace, pack->readableImageRect, drawRect, pack->processedColorSimilarity)) { didSomething = true; } -#if DEBUG_KP_PAINTER && 0 int ms = timer.restart (); - qCDebug(kpLogImagelib) << "\ttried to wash: " << ms << "ms" - << " (" << (ms ? (rect.width () * rect.height () / ms) : -1234) - << " pixels/ms)" - << endl; + qCDebug(kpLogImagelib) << "\ttried to wash: " << ms << "ms"; convAndWashTime += ms; -#endif return didSomething ? drawRect : QRect (); } //--------------------------------------------------------------------- // public static QRect kpPainter::washRect (kpImage *image, int x, int y, int width, int height, const kpColor &color, const kpColor &colorToReplace, int processedColorSimilarity) { return ::Wash (image, QPoint (x, y), QPoint (x + width - 1, y + height - 1), color, 1/*pen width*/, 1/*pen height*/, colorToReplace, processedColorSimilarity, &::WashRectHelper); } //--------------------------------------------------------------------- // public static void kpPainter::sprayPoints (kpImage *image, const QList &points, const kpColor &color, int spraycanSize) { -#if DEBUG_KP_PAINTER qCDebug(kpLogImagelib) << "kpPainter::sprayPoints()"; -#endif Q_ASSERT (spraycanSize > 0); QPainter painter(image); const int radius = spraycanSize / 2; // Set the drawing colors for the painters. painter.setPen(color.toQColor()); for (const auto &p : points) { for (int i = 0; i < 10; i++) { const int dx = (KRandom::random () % spraycanSize) - radius; const int dy = (KRandom::random () % spraycanSize) - radius; // Make it look circular. // TODO: Can be done better by doing a random vector angle & length // but would sin and cos be too slow? if ((dx * dx) + (dy * dy) > (radius * radius)) { continue; } const QPoint p2 (p.x () + dx, p.y () + dy); painter.drawPoint(p2); } } } //--------------------------------------------------------------------- diff --git a/imagelib/transforms/kpTransformAutoCrop.cpp b/imagelib/transforms/kpTransformAutoCrop.cpp index 82e1edda..7aa4a13d 100644 --- a/imagelib/transforms/kpTransformAutoCrop.cpp +++ b/imagelib/transforms/kpTransformAutoCrop.cpp @@ -1,756 +1,732 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // TODO: Color Similarity is obviously useful in Autocrop but it isn't // obvious as to how to implement it. The current heuristic, // for each side, chooses an arbitrary reference color for which // all other candidate pixels in that side are tested against // for similarity. But if the reference color happens to be at // one extreme of the range of colors in that side, then pixels // at the other extreme would not be deemed similar enough. The // key is to find the median color as the reference but how do // you do this if you don't know which pixels to sample in the first // place (that's what you're trying to find)? Chicken and egg situation. // // The other heuristic that is in doubt is the use of the average // color in determining the similarity of sides (it is possible // to get vastly differently colors in both sides yet they will be // considered similar). -#define DEBUG_KP_TOOL_AUTO_CROP 1 - #include "kpTransformAutoCrop.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "widgets/toolbars/kpColorToolBar.h" #include "environments/commands/kpCommandEnvironment.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/selections/image/kpRectangularImageSelection.h" #include "generic/kpSetOverrideCursorSaver.h" #include "tools/kpTool.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include #include #include //--------------------------------------------------------------------- class kpTransformAutoCropBorder { public: // WARNING: Only call the with imagePtr = 0 if you are going to use // operator= to fill it in with a valid imagePtr immediately // afterwards. kpTransformAutoCropBorder (const kpImage *imagePtr = nullptr, int processedColorSimilarity = 0); kpCommandSize::SizeType size () const; const kpImage *image () const; int processedColorSimilarity () const; QRect rect () const; int left () const; int right () const; int top () const; int bottom () const; kpColor referenceColor () const; kpColor averageColor () const; bool isSingleColor () const; // (returns true on success (even if no rect) or false on error) bool calculate (int isX, int dir); bool fillsEntireImage () const; bool exists () const; void invalidate (); private: const kpImage *m_imagePtr; int m_processedColorSimilarity; QRect m_rect; kpColor m_referenceColor; int m_redSum, m_greenSum, m_blueSum; bool m_isSingleColor; }; kpTransformAutoCropBorder::kpTransformAutoCropBorder (const kpImage *imagePtr, int processedColorSimilarity) : m_imagePtr (imagePtr), m_processedColorSimilarity (processedColorSimilarity) { invalidate (); } // public kpCommandSize::SizeType kpTransformAutoCropBorder::size () const { return sizeof (kpTransformAutoCropBorder); } // public const kpImage *kpTransformAutoCropBorder::image () const { return m_imagePtr; } // public int kpTransformAutoCropBorder::processedColorSimilarity () const { return m_processedColorSimilarity; } // public QRect kpTransformAutoCropBorder::rect () const { return m_rect; } // public int kpTransformAutoCropBorder::left () const { return m_rect.left (); } // public int kpTransformAutoCropBorder::right () const { return m_rect.right (); } // public int kpTransformAutoCropBorder::top () const { return m_rect.top (); } // public int kpTransformAutoCropBorder::bottom () const { return m_rect.bottom (); } // public kpColor kpTransformAutoCropBorder::referenceColor () const { return m_referenceColor; } // public kpColor kpTransformAutoCropBorder::averageColor () const { if (!m_rect.isValid ()) return kpColor::Invalid; if (m_referenceColor.isTransparent ()) return kpColor::Transparent; if (m_processedColorSimilarity == 0) return m_referenceColor; int numPixels = (m_rect.width () * m_rect.height ()); Q_ASSERT (numPixels > 0); return kpColor (m_redSum / numPixels, m_greenSum / numPixels, m_blueSum / numPixels); } //--------------------------------------------------------------------- bool kpTransformAutoCropBorder::isSingleColor () const { return m_isSingleColor; } //--------------------------------------------------------------------- // public bool kpTransformAutoCropBorder::calculate (int isX, int dir) { -#if DEBUG_KP_TOOL_AUTO_CROP && 1 qCDebug(kpLogImagelib) << "kpTransformAutoCropBorder::calculate() CALLED!"; -#endif + int maxX = m_imagePtr->width () - 1; int maxY = m_imagePtr->height () - 1; QImage qimage = *m_imagePtr; Q_ASSERT (!qimage.isNull ()); // (sync both branches) if (isX) { int numCols = 0; int startX = (dir > 0) ? 0 : maxX; kpColor col = kpPixmapFX::getColorAtPixel (qimage, startX, 0); for (int x = startX; x >= 0 && x <= maxX; x += dir) { int y; for (y = 0; y <= maxY; y++) { if (!kpPixmapFX::getColorAtPixel (qimage, x, y).isSimilarTo (col, m_processedColorSimilarity)) break; } if (y <= maxY) break; else numCols++; } if (numCols) { m_rect = kpPainter::normalizedRect(QPoint(startX, 0), QPoint(startX + (numCols - 1) * dir, maxY)); m_referenceColor = col; } } else { int numRows = 0; int startY = (dir > 0) ? 0 : maxY; kpColor col = kpPixmapFX::getColorAtPixel (qimage, 0, startY); for (int y = startY; y >= 0 && y <= maxY; y += dir) { int x; for (x = 0; x <= maxX; x++) { if (!kpPixmapFX::getColorAtPixel (qimage, x, y).isSimilarTo (col, m_processedColorSimilarity)) break; } if (x <= maxX) break; else numRows++; } if (numRows) { m_rect = kpPainter::normalizedRect(QPoint(0, startY), QPoint(maxX, startY + (numRows - 1) * dir)); m_referenceColor = col; } } if (m_rect.isValid ()) { m_isSingleColor = true; if (m_processedColorSimilarity != 0) { for (int y = m_rect.top (); y <= m_rect.bottom (); y++) { for (int x = m_rect.left (); x <= m_rect.right (); x++) { kpColor colAtPixel = kpPixmapFX::getColorAtPixel (qimage, x, y); if (m_isSingleColor && colAtPixel != m_referenceColor) m_isSingleColor = false; m_redSum += colAtPixel.red (); m_greenSum += colAtPixel.green (); m_blueSum += colAtPixel.blue (); } } } } return true; } // public bool kpTransformAutoCropBorder::fillsEntireImage () const { return (m_rect == m_imagePtr->rect ()); } // public bool kpTransformAutoCropBorder::exists () const { // (will use in an addition so make sure returns 1 or 0) return (m_rect.isValid () ? 1 : 0); } // public void kpTransformAutoCropBorder::invalidate () { m_rect = QRect (); m_referenceColor = kpColor::Invalid; m_redSum = m_greenSum = m_blueSum = 0; m_isSingleColor = false; } struct kpTransformAutoCropCommandPrivate { bool actOnSelection{}; kpTransformAutoCropBorder leftBorder, rightBorder, topBorder, botBorder; kpImage *leftImage{}, *rightImage{}, *topImage{}, *botImage{}; QRect contentsRect; int oldWidth{}, oldHeight{}; kpAbstractImageSelection *oldSelectionPtr{}; }; // REFACTOR: Move to /commands/ kpTransformAutoCropCommand::kpTransformAutoCropCommand (bool actOnSelection, const kpTransformAutoCropBorder &leftBorder, const kpTransformAutoCropBorder &rightBorder, const kpTransformAutoCropBorder &topBorder, const kpTransformAutoCropBorder &botBorder, kpCommandEnvironment *environ) : kpNamedCommand(text(actOnSelection, DontShowAccel), environ), d (new kpTransformAutoCropCommandPrivate ()) { d->actOnSelection = actOnSelection; d->leftBorder = leftBorder; d->rightBorder = rightBorder; d->topBorder = topBorder; d->botBorder = botBorder; d->leftImage = nullptr; d->rightImage = nullptr; d->topImage = nullptr; d->botImage = nullptr; kpDocument *doc = document (); Q_ASSERT (doc); d->oldWidth = doc->width (d->actOnSelection); d->oldHeight = doc->height (d->actOnSelection); d->oldSelectionPtr = nullptr; } //--------------------------------------------------------------------- kpTransformAutoCropCommand::~kpTransformAutoCropCommand () { deleteUndoImages (); delete d->oldSelectionPtr; delete d; } //--------------------------------------------------------------------- // public static QString kpTransformAutoCropCommand::text(bool actOnSelection, int options) { if (actOnSelection) { if (options & kpTransformAutoCropCommand::ShowAccel) { return i18n ("Remove Internal B&order"); } return i18n ("Remove Internal Border"); } if (options & kpTransformAutoCropCommand::ShowAccel) return i18n ("Autocr&op"); return i18n ("Autocrop"); } //--------------------------------------------------------------------- // public virtual [base kpCommand] kpCommandSize::SizeType kpTransformAutoCropCommand::size () const { return d->leftBorder.size () + d->rightBorder.size () + d->topBorder.size () + d->botBorder.size () + ImageSize (d->leftImage) + ImageSize (d->rightImage) + ImageSize (d->topImage) + ImageSize (d->botImage) + SelectionSize (d->oldSelectionPtr); } //--------------------------------------------------------------------- // private void kpTransformAutoCropCommand::getUndoImage (const kpTransformAutoCropBorder &border, kpImage **image) { kpDocument *doc = document (); Q_ASSERT (doc); -#if DEBUG_KP_TOOL_AUTO_CROP && 1 qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::getUndoImage()"; qCDebug(kpLogImagelib) << "\timage=" << image << " border: rect=" << border.rect () << " isSingleColor=" << border.isSingleColor (); -#endif if (image && border.exists () && !border.isSingleColor ()) { if (*image) { - #if DEBUG_KP_TOOL_AUTO_CROP && 1 qCDebug(kpLogImagelib) << "\talready have *image - delete it"; - #endif delete *image; } *image = new kpImage ( kpPixmapFX::getPixmapAt (doc->image (d->actOnSelection), border.rect ())); } } // private void kpTransformAutoCropCommand::getUndoImages () { getUndoImage (d->leftBorder, &d->leftImage); getUndoImage (d->rightBorder, &d->rightImage); getUndoImage (d->topBorder, &d->topImage); getUndoImage (d->botBorder, &d->botImage); } // private void kpTransformAutoCropCommand::deleteUndoImages () { -#if DEBUG_KP_TOOL_AUTO_CROP && 1 qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::deleteUndoImages()"; -#endif delete d->leftImage; d->leftImage = nullptr; delete d->rightImage; d->rightImage = nullptr; delete d->topImage; d->topImage = nullptr; delete d->botImage; d->botImage = nullptr; } // public virtual [base kpCommand] void kpTransformAutoCropCommand::execute () { if (!d->contentsRect.isValid ()) { d->contentsRect = contentsRect (); } getUndoImages (); kpDocument *doc = document (); Q_ASSERT (doc); kpImage imageWithoutBorder = kpTool::neededPixmap (doc->image (d->actOnSelection), d->contentsRect); if (!d->actOnSelection) { doc->setImage (imageWithoutBorder); } else { d->oldSelectionPtr = doc->imageSelection ()->clone (); d->oldSelectionPtr->setBaseImage (kpImage ()); // d->contentsRect is relative to the top of the sel // while sel is relative to the top of the doc QRect rect = d->contentsRect; rect.translate (d->oldSelectionPtr->x (), d->oldSelectionPtr->y ()); kpRectangularImageSelection sel ( rect, imageWithoutBorder, d->oldSelectionPtr->transparency ()); doc->setSelection (sel); environ ()->somethingBelowTheCursorChanged (); } } // public virtual [base kpCommand] void kpTransformAutoCropCommand::unexecute () { -#if DEBUG_KP_TOOL_AUTO_CROP && 1 qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::unexecute()"; -#endif kpDocument *doc = document (); Q_ASSERT (doc); kpImage image (d->oldWidth, d->oldHeight, QImage::Format_ARGB32_Premultiplied); // restore the position of the center image kpPixmapFX::setPixmapAt (&image, d->contentsRect, doc->image (d->actOnSelection)); // draw the borders const kpTransformAutoCropBorder *borders [] = { &d->leftBorder, &d->rightBorder, &d->topBorder, &d->botBorder, nullptr }; const kpImage *images [] = { d->leftImage, d->rightImage, d->topImage, d->botImage, nullptr }; const kpImage **p = images; for (const kpTransformAutoCropBorder **b = borders; *b; b++, p++) { if (!(*b)->exists ()) { continue; } if ((*b)->isSingleColor ()) { kpColor col = (*b)->referenceColor (); - #if DEBUG_KP_TOOL_AUTO_CROP && 1 qCDebug(kpLogImagelib) << "\tdrawing border " << (*b)->rect () << " rgb=" << (int *) col.toQRgb () /* %X hack */; - #endif const QRect r = (*b)->rect (); kpPainter::fillRect (&image, r.x (), r.y (), r.width (), r.height (), col); } else { - #if DEBUG_KP_TOOL_AUTO_CROP && 1 qCDebug(kpLogImagelib) << "\trestoring border image " << (*b)->rect (); - #endif if (*p) { // REFACTOR: Add equivalent method to kpPainter and use. kpPixmapFX::setPixmapAt (&image, (*b)->rect (), **p); } } } if (!d->actOnSelection) { doc->setImage (image); } else { d->oldSelectionPtr->setBaseImage (image); doc->setSelection (*d->oldSelectionPtr); delete d->oldSelectionPtr; d->oldSelectionPtr = nullptr; environ ()->somethingBelowTheCursorChanged (); } deleteUndoImages (); } // private QRect kpTransformAutoCropCommand::contentsRect () const { const kpImage image = document ()->image (d->actOnSelection); QPoint topLeft (d->leftBorder.exists () ? d->leftBorder.rect ().right () + 1 : 0, d->topBorder.exists () ? d->topBorder.rect ().bottom () + 1 : 0); QPoint botRight (d->rightBorder.exists () ? d->rightBorder.rect ().left () - 1 : image.width () - 1, d->botBorder.exists () ? d->botBorder.rect ().top () - 1 : image.height () - 1); return {topLeft, botRight}; } static void ShowNothingToAutocropMessage (kpMainWindow *mainWindow, bool actOnSelection) { kpSetOverrideCursorSaver cursorSaver (Qt::ArrowCursor); if (actOnSelection) { KMessageBox::information (mainWindow, i18n ("KolourPaint cannot remove the selection's internal border as it" " could not be located."), i18nc ("@title:window", "Cannot Remove Internal Border"), QStringLiteral("NothingToAutoCrop")); } else { KMessageBox::information (mainWindow, i18n ("KolourPaint cannot automatically crop the image as its" " border could not be located."), i18nc ("@title:window", "Cannot Autocrop"), QStringLiteral("NothingToAutoCrop")); } } bool kpTransformAutoCrop (kpMainWindow *mainWindow) { -#if DEBUG_KP_TOOL_AUTO_CROP qCDebug(kpLogImagelib) << "kpTransformAutoCrop() CALLED!"; -#endif Q_ASSERT (mainWindow); kpDocument *doc = mainWindow->document (); Q_ASSERT (doc); // OPT: if already pulled selection image, no need to do it again here kpImage image = doc->selection () ? doc->getSelectedBaseImage () : doc->image (); Q_ASSERT (!image.isNull ()); kpViewManager *vm = mainWindow->viewManager (); Q_ASSERT (vm); int processedColorSimilarity = mainWindow->colorToolBar ()->processedColorSimilarity (); kpTransformAutoCropBorder leftBorder (&image, processedColorSimilarity), rightBorder (&image, processedColorSimilarity), topBorder (&image, processedColorSimilarity), botBorder (&image, processedColorSimilarity); kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor); mainWindow->colorToolBar ()->flashColorSimilarityToolBarItem (); // TODO: With Colour Similarity, a lot of weird (and wonderful) things can // happen resulting in a huge number of code paths. Needs refactoring // and regression testing. // // TODO: e.g. When the top fills entire rect but bot doesn't we could // invalidate top and continue autocrop. int numRegions = 0; if (!leftBorder.calculate (true/*x*/, +1/*going right*/) || leftBorder.fillsEntireImage () || !rightBorder.calculate (true/*x*/, -1/*going left*/) || rightBorder.fillsEntireImage () || !topBorder.calculate (false/*y*/, +1/*going down*/) || topBorder.fillsEntireImage () || !botBorder.calculate (false/*y*/, -1/*going up*/) || botBorder.fillsEntireImage () || ((numRegions = leftBorder.exists () + rightBorder.exists () + topBorder.exists () + botBorder.exists ()) == 0)) { - #if DEBUG_KP_TOOL_AUTO_CROP qCDebug(kpLogImagelib) << "\tcan't find border; leftBorder.rect=" << leftBorder.rect () << " rightBorder.rect=" << rightBorder.rect () << " topBorder.rect=" << topBorder.rect () << " botBorder.rect=" << botBorder.rect (); - #endif + ::ShowNothingToAutocropMessage (mainWindow, static_cast (doc->selection ())); return false; } -#if DEBUG_KP_TOOL_AUTO_CROP qCDebug(kpLogImagelib) << "\tnumRegions=" << numRegions; qCDebug(kpLogImagelib) << "\t\tleft=" << leftBorder.rect () << " refCol=" << (leftBorder.exists () ? (int *) leftBorder.referenceColor ().toQRgb () : nullptr) << " avgCol=" << (leftBorder.exists () ? (int *) leftBorder.averageColor ().toQRgb () : nullptr); qCDebug(kpLogImagelib) << "\t\tright=" << rightBorder.rect () << " refCol=" << (rightBorder.exists () ? (int *) rightBorder.referenceColor ().toQRgb () : nullptr) << " avgCol=" << (rightBorder.exists () ? (int *) rightBorder.averageColor ().toQRgb () : nullptr); qCDebug(kpLogImagelib) << "\t\ttop=" << topBorder.rect () << " refCol=" << (topBorder.exists () ? (int *) topBorder.referenceColor ().toQRgb () : nullptr) << " avgCol=" << (topBorder.exists () ? (int *) topBorder.averageColor ().toQRgb () : nullptr); qCDebug(kpLogImagelib) << "\t\tbot=" << botBorder.rect () << " refCol=" << (botBorder.exists () ? (int *) botBorder.referenceColor ().toQRgb () : nullptr) << " avgCol=" << (botBorder.exists () ? (int *) botBorder.averageColor ().toQRgb () : nullptr); -#endif // In case e.g. the user pastes a solid, coloured-in rectangle, // we favor killing the bottom and right regions // (these regions probably contain the unwanted whitespace due // to the doc being bigger than the pasted selection to start with). // // We also kill if they kiss or even overlap. if (leftBorder.exists () && rightBorder.exists ()) { const kpColor leftCol = leftBorder.averageColor (); const kpColor rightCol = rightBorder.averageColor (); if ((numRegions == 2 && !leftCol.isSimilarTo (rightCol, processedColorSimilarity)) || leftBorder.right () >= rightBorder.left () - 1) // kissing or overlapping { - #if DEBUG_KP_TOOL_AUTO_CROP qCDebug(kpLogImagelib) << "\tignoring left border"; - #endif leftBorder.invalidate (); } } if (topBorder.exists () && botBorder.exists ()) { const kpColor topCol = topBorder.averageColor (); const kpColor botCol = botBorder.averageColor (); if ((numRegions == 2 && !topCol.isSimilarTo (botCol, processedColorSimilarity)) || topBorder.bottom () >= botBorder.top () - 1) // kissing or overlapping { - #if DEBUG_KP_TOOL_AUTO_CROP qCDebug(kpLogImagelib) << "\tignoring top border"; - #endif topBorder.invalidate (); } } mainWindow->addImageOrSelectionCommand ( new kpTransformAutoCropCommand (static_cast (doc->selection ()), leftBorder, rightBorder, topBorder, botBorder, mainWindow->commandEnvironment ())); return true; } diff --git a/imagelib/transforms/kpTransformCrop.cpp b/imagelib/transforms/kpTransformCrop.cpp index ce636c0e..ff323e54 100644 --- a/imagelib/transforms/kpTransformCrop.cpp +++ b/imagelib/transforms/kpTransformCrop.cpp @@ -1,77 +1,75 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_CROP 1 - #include "kpTransformCrop.h" #include "kpTransformCropPrivate.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "layers/selections/kpAbstractSelection.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/imagelib/transforms/kpTransformResizeScaleCommand.h" #include void kpTransformCrop (kpMainWindow *mainWindow) { kpDocument *doc = mainWindow->document (); Q_ASSERT (doc); kpAbstractSelection *sel = doc->selection (); Q_ASSERT (sel); kpCommand *resizeDocCommand = new kpTransformResizeScaleCommand ( false/*act on doc, not sel*/, sel->width (), sel->height (), kpTransformResizeScaleCommand::Resize, mainWindow->commandEnvironment ()); auto *textSel = dynamic_cast (sel); auto *imageSel = dynamic_cast (sel); // It's either a text selection or an image selection, but cannot be // neither or both. Q_ASSERT (!!textSel != !!imageSel); if (textSel) { ::kpTransformCrop_TextSelection (mainWindow, i18n ("Set as Image"), resizeDocCommand); } else if (imageSel) { ::kpTransformCrop_ImageSelection (mainWindow, i18n ("Set as Image"), resizeDocCommand); } else { Q_ASSERT (!"unreachable"); } } diff --git a/imagelib/transforms/kpTransformCrop_ImageSelection.cpp b/imagelib/transforms/kpTransformCrop_ImageSelection.cpp index 30a2937a..787eb8d1 100644 --- a/imagelib/transforms/kpTransformCrop_ImageSelection.cpp +++ b/imagelib/transforms/kpTransformCrop_ImageSelection.cpp @@ -1,258 +1,245 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_CROP 1 - #include "kpTransformCrop.h" #include "kpTransformCropPrivate.h" #include "kpLogCategories.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "imagelib/kpImage.h" #include "commands/kpMacroCommand.h" #include "mainWindow/kpMainWindow.h" #include "pixmapfx/kpPixmapFX.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "views/manager/kpViewManager.h" // See the "image selection" part of the kpTransformCrop() API Doc. // // REFACTOR: Move into commands/ class SetDocumentToSelectionImageCommand : public kpCommand { public: SetDocumentToSelectionImageCommand (kpCommandEnvironment *environ); ~SetDocumentToSelectionImageCommand () override; /* (uninteresting child of macro cmd) */ QString name () const override { return {}; } kpCommandSize::SizeType size () const override { return ImageSize (m_oldImage) + SelectionSize (m_fromSelectionPtr) + ImageSize (m_imageIfFromSelectionDoesntHaveOne); } // ASSUMPTION: Document has been resized to be the same size as the // selection. void execute () override; void unexecute () override; protected: kpColor m_backgroundColor; kpImage m_oldImage; kpAbstractImageSelection *m_fromSelectionPtr; kpImage m_imageIfFromSelectionDoesntHaveOne; }; SetDocumentToSelectionImageCommand::SetDocumentToSelectionImageCommand (kpCommandEnvironment *environ) : kpCommand (environ), m_backgroundColor (environ->backgroundColor ()), m_fromSelectionPtr ( dynamic_cast ( environ->document ()->selection ()->clone ())) { Q_ASSERT (m_fromSelectionPtr); if ( m_fromSelectionPtr ) // make coverity happy { m_imageIfFromSelectionDoesntHaveOne = m_fromSelectionPtr->hasContent () ? kpImage () : document ()->getSelectedBaseImage (); } } //--------------------------------------------------------------------- SetDocumentToSelectionImageCommand::~SetDocumentToSelectionImageCommand () { delete m_fromSelectionPtr; } //--------------------------------------------------------------------- // public virtual [base kpCommand] void SetDocumentToSelectionImageCommand::execute () { -#if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "SetDocumentToSelectionImageCommand::execute()"; -#endif viewManager ()->setQueueUpdates (); { // kpTransformCrop_ImageSelection's has // executed, resizing the document to be the size of the selection // bounding rectangle. Q_ASSERT (document ()->width () == m_fromSelectionPtr->width ()); Q_ASSERT (document ()->height () == m_fromSelectionPtr->height ()); m_oldImage = document ()->image (); // // e.g. original elliptical selection: // // t/---\ T = original transparent selection pixel // | TT | t = outside the selection region // t\__/t [every other character] = original opaque selection pixel // // Afterwards, the _document_ image becomes: // // b/---\ T = [unchanged] // | TT | b = background color // b\__/b [every other character] = [unchanged] // // The selection is deleted. // // TODO: Do not introduce a mask if the result will not contain // any transparent pixels. // QImage newDocImage(document()->width(), document()->height(), QImage::Format_ARGB32_Premultiplied); newDocImage.fill(m_backgroundColor.toQRgb()); - #if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "\tsel: rect=" << m_fromSelectionPtr->boundingRect () << " pm=" << m_fromSelectionPtr->hasContent (); - #endif + QImage setTransparentImage; if (m_fromSelectionPtr->hasContent ()) { setTransparentImage = m_fromSelectionPtr->transparentImage (); - #if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "\thave pixmap; rect=" << setTransparentImage.rect (); - #endif } else { setTransparentImage = m_imageIfFromSelectionDoesntHaveOne; - #if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "\tno pixmap in sel - get it; rect=" << setTransparentImage.rect (); - #endif } kpPixmapFX::paintPixmapAt (&newDocImage, QPoint (0, 0), setTransparentImage); document ()->setImageAt (newDocImage, QPoint (0, 0)); document ()->selectionDelete (); environ ()->somethingBelowTheCursorChanged (); } viewManager ()->restoreQueueUpdates (); } //--------------------------------------------------------------------- // public virtual [base kpCommand] void SetDocumentToSelectionImageCommand::unexecute () { -#if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "SetDocumentToSelectionImageCommand::unexecute()"; -#endif viewManager ()->setQueueUpdates (); { document ()->setImageAt (m_oldImage, QPoint (0, 0)); m_oldImage = kpImage (); - #if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "\tsel: rect=" << m_fromSelectionPtr->boundingRect () << " pm=" << m_fromSelectionPtr->hasContent (); - #endif + document ()->setSelection (*m_fromSelectionPtr); environ ()->somethingBelowTheCursorChanged (); } viewManager ()->restoreQueueUpdates (); } //--------------------------------------------------------------------- void kpTransformCrop_ImageSelection (kpMainWindow *mainWindow, const QString &commandName, kpCommand *resizeDocCommand) { // Save starting selection, minus the border. auto *borderImageSel = dynamic_cast ( mainWindow->document ()->selection ()->clone ()); Q_ASSERT (borderImageSel); if ( !borderImageSel ) { // make coverity happy return; } // (only interested in border) borderImageSel->deleteContent (); borderImageSel->moveTo (QPoint (0, 0)); auto *environ = mainWindow->commandEnvironment (); auto *macroCmd = new kpMacroCommand (commandName, environ); // (must resize doc _before_ SetDocumentToSelectionImageCommand in case // doc needs to gets bigger - else selection image may not fit) macroCmd->addCommand (resizeDocCommand); -#if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "\tis pixmap sel"; qCDebug(kpLogImagelib) << "\tcreating SetImage cmd"; -#endif + macroCmd->addCommand (new SetDocumentToSelectionImageCommand (environ)); mainWindow->addImageOrSelectionCommand ( macroCmd, true/*add create cmd*/, false/*don't add pull cmd*/); // Add selection border back for convenience. mainWindow->commandHistory ()->addCommand ( new kpToolSelectionCreateCommand ( i18n ("Selection: Create"), *borderImageSel, mainWindow->commandEnvironment ())); delete borderImageSel; } diff --git a/imagelib/transforms/kpTransformCrop_TextSelection.cpp b/imagelib/transforms/kpTransformCrop_TextSelection.cpp index 78dd0665..291658fd 100644 --- a/imagelib/transforms/kpTransformCrop_TextSelection.cpp +++ b/imagelib/transforms/kpTransformCrop_TextSelection.cpp @@ -1,77 +1,74 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_CROP 1 #include "kpTransformCrop.h" #include "kpTransformCropPrivate.h" #include "kpLogCategories.h" #include "commands/imagelib/effects/kpEffectClearCommand.h" #include "commands/kpMacroCommand.h" #include "mainWindow/kpMainWindow.h" #include "commands/tools/selection/kpToolSelectionMoveCommand.h" void kpTransformCrop_TextSelection (kpMainWindow *mainWindow, const QString &commandName, kpCommand *resizeDocCommand) { kpCommandEnvironment *environ = mainWindow->commandEnvironment (); auto *macroCmd = new kpMacroCommand (commandName, environ); macroCmd->addCommand (resizeDocCommand); -#if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "\tisText"; qCDebug(kpLogImagelib) << "\tclearing doc with trans cmd"; -#endif + macroCmd->addCommand ( new kpEffectClearCommand ( false/*act on doc*/, kpColor::Transparent, environ)); -#if DEBUG_KP_TOOL_CROP qCDebug(kpLogImagelib) << "\tmoving sel to (0,0) cmd"; -#endif + kpToolSelectionMoveCommand *moveCmd = new kpToolSelectionMoveCommand ( QString()/*uninteresting child of macro cmd*/, environ); moveCmd->moveTo (QPoint (0, 0), true/*move on exec, not now*/); moveCmd->finalize (); macroCmd->addCommand (moveCmd); mainWindow->addImageOrSelectionCommand ( macroCmd, true/*add create cmd*/, true/*add create content cmd*/); } diff --git a/kpThumbnail.cpp b/kpThumbnail.cpp index 9f38ca24..8424f62b 100644 --- a/kpThumbnail.cpp +++ b/kpThumbnail.cpp @@ -1,183 +1,176 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_THUMBNAIL 1 #include "kpThumbnail.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "views/kpThumbnailView.h" #include "tools/kpTool.h" #include "kpLogCategories.h" #include #include #include struct kpThumbnailPrivate { kpMainWindow *mainWindow; kpThumbnailView *view; QHBoxLayout *lay; }; kpThumbnail::kpThumbnail (kpMainWindow *parent) : kpSubWindow (parent), d (new kpThumbnailPrivate ()) { Q_ASSERT (parent); d->mainWindow = parent; d->view = nullptr; d->lay = new QHBoxLayout (this); setMinimumSize (64, 64); updateCaption (); } kpThumbnail::~kpThumbnail () { delete d; } // public kpThumbnailView *kpThumbnail::view () const { return d->view; } // public void kpThumbnail::setView (kpThumbnailView *view) { -#if DEBUG_KP_THUMBNAIL qCDebug(kpLogMisc) << "kpThumbnail::setView(" << view << ")"; -#endif if (d->view == view) { return; } if (d->view) { disconnect (d->view, &kpThumbnailView::destroyed, this, &kpThumbnail::slotViewDestroyed); disconnect (d->view, &kpThumbnailView::zoomLevelChanged, this, &kpThumbnail::updateCaption); d->lay->removeWidget (d->view); } d->view = view; if (d->view) { connect (d->view, &kpThumbnailView::destroyed, this, &kpThumbnail::slotViewDestroyed); connect (d->view, &kpThumbnailView::zoomLevelChanged, this, &kpThumbnail::updateCaption); Q_ASSERT (d->view->parent () == this); d->lay->addWidget (d->view, Qt::AlignCenter); d->view->show (); } updateCaption (); } // public slot void kpThumbnail::updateCaption () { setWindowTitle (view () ? view ()->caption () : i18nc ("@title:window", "Thumbnail")); } // protected slot void kpThumbnail::slotViewDestroyed () { -#if DEBUG_KP_THUMBNAIL qCDebug(kpLogMisc) << "kpThumbnail::slotViewDestroyed()"; -#endif d->view = nullptr; updateCaption (); } // protected virtual [base QWidget] void kpThumbnail::resizeEvent (QResizeEvent *e) { -#if DEBUG_KP_THUMBNAIL qCDebug(kpLogMisc) << "kpThumbnail::resizeEvent(" << width () << "," << height () << ")"; -#endif QWidget::resizeEvent (e); // updateVariableZoom (); TODO: is below a good idea since this commented out? if (d->mainWindow) { d->mainWindow->notifyThumbnailGeometryChanged (); if (d->mainWindow->tool ()) { d->mainWindow->tool ()->somethingBelowTheCursorChanged (); } } } // protected virtual [base QWidget] void kpThumbnail::moveEvent (QMoveEvent * /*e*/) { if (d->mainWindow) { d->mainWindow->notifyThumbnailGeometryChanged (); } } // protected virtual [base QWidget] void kpThumbnail::closeEvent (QCloseEvent *e) { QWidget::closeEvent (e); emit windowClosed (); } diff --git a/kpViewScrollableContainer.cpp b/kpViewScrollableContainer.cpp index 41675ee1..043b5f76 100644 --- a/kpViewScrollableContainer.cpp +++ b/kpViewScrollableContainer.cpp @@ -1,1208 +1,1187 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2011 Martin Koller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_VIEW_SCROLLABLE_CONTAINER 1 - #include "kpViewScrollableContainer.h" #include #include #include #include #include #include #include #include #include "kpLogCategories.h" #include #include "kpDefs.h" #include "pixmapfx/kpPixmapFX.h" #include "views/kpView.h" #include "generic/kpWidgetMapper.h" //--------------------------------------------------------------------- // (Pulled from out of Thurston's hat) static const int DragScrollLeftTopMargin = 0; static const int DragScrollRightBottomMargin = 16; // scrollbarish static const int DragScrollInterval = 150; static const int DragScrollInitialInterval = DragScrollInterval * 2; static const int DragScrollNumPixels = 10; static const int DragDistanceFromRectMaxFor1stMultiplier = 50; static const int DragDistanceFromRectMaxFor2ndMultiplier = 100; //--------------------------------------------------------------------- //--------------------------------------------------------------------- // a transparent widget above all others in the viewport used only while resizing the document // to be able to show the resize lines above everything else class kpOverlay : public QWidget { public: kpOverlay(QWidget *parent, kpViewScrollableContainer *container) : QWidget(parent), m_container(container) { } void paintEvent(QPaintEvent *) override { m_container->drawResizeLines(); } private: kpViewScrollableContainer *m_container; }; //--------------------------------------------------------------------- const int kpGrip::Size = 5; //--------------------------------------------------------------------- kpGrip::kpGrip (GripType type, QWidget *parent) : QWidget(parent), m_type (type), m_startPoint (KP_INVALID_POINT), m_currentPoint (KP_INVALID_POINT), m_shouldReleaseMouseButtons (false) { setCursor(cursorForType(m_type)); setMouseTracking(true); // mouseMoveEvent's even when no mousebtn down setAutoFillBackground(true); setBackgroundRole(QPalette::Highlight); setFixedSize(kpGrip::Size, kpGrip::Size); } //--------------------------------------------------------------------- // public kpGrip::GripType kpGrip::type () const { return m_type; } //--------------------------------------------------------------------- // public static QCursor kpGrip::cursorForType (GripType type) { switch (type) { case kpGrip::Bottom: return Qt::SizeVerCursor; case kpGrip::Right: return Qt::SizeHorCursor; case kpGrip::BottomRight: return Qt::SizeFDiagCursor; } return Qt::ArrowCursor; } //--------------------------------------------------------------------- // public bool kpGrip::containsCursor() { return isVisible() && QRect(mapToGlobal(rect().topLeft()), mapToGlobal(rect().bottomRight())).contains(QCursor::pos()); } //--------------------------------------------------------------------- // public bool kpGrip::isDrawing () const { return (m_startPoint != KP_INVALID_POINT); } //--------------------------------------------------------------------- // public QString kpGrip::haventBegunDrawUserMessage () const { return i18n ("Left drag the handle to resize the image."); } //--------------------------------------------------------------------- // public QString kpGrip::userMessage () const { return m_userMessage; } //--------------------------------------------------------------------- // public void kpGrip::setUserMessage (const QString &message) { // Don't do NOP checking here since another grip might have changed // the message so an apparent NOP for this grip is not a NOP in the // global sense (kpViewScrollableContainer::slotGripStatusMessageChanged()). m_userMessage = message; emit statusMessageChanged (message); } //--------------------------------------------------------------------- // protected void kpGrip::cancel () { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpGrip::cancel()"; -#endif + if (m_currentPoint == KP_INVALID_POINT) { return; } m_startPoint = KP_INVALID_POINT; m_currentPoint = KP_INVALID_POINT; setUserMessage (i18n ("Resize Image: Let go of all the mouse buttons.")); setCursor (Qt::ArrowCursor); m_shouldReleaseMouseButtons = true; releaseKeyboard (); emit cancelledDraw (); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpGrip::keyReleaseEvent (QKeyEvent *e) { if (m_startPoint != KP_INVALID_POINT && e->key () == Qt::Key_Escape) { cancel (); } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpGrip::mousePressEvent (QMouseEvent *e) { if (m_startPoint == KP_INVALID_POINT && (e->buttons () & Qt::MouseButtonMask) == Qt::LeftButton) { m_startPoint = e->pos (); m_currentPoint = e->pos (); emit beganDraw (); grabKeyboard (); setUserMessage (i18n ("Resize Image: Right click to cancel.")); setCursor (cursorForType (m_type)); } else { if (m_startPoint != KP_INVALID_POINT) { cancel (); } } } //--------------------------------------------------------------------- // public QPoint kpGrip::viewDeltaPoint () const { if (m_startPoint == KP_INVALID_POINT) { return KP_INVALID_POINT; } const QPoint point = mapFromGlobal (QCursor::pos ()); // TODO: this is getting out of sync with m_currentPoint return {(m_type & kpGrip::Right) ? point.x () - m_startPoint.x () : 0, (m_type & kpGrip::Bottom) ? point.y () - m_startPoint.y () : 0}; } //--------------------------------------------------------------------- // public void kpGrip::mouseMovedTo (const QPoint &point, bool dueToDragScroll) { if (m_startPoint == KP_INVALID_POINT) { return; } m_currentPoint = point; emit continuedDraw (((m_type & kpGrip::Right) ? point.x () - m_startPoint.x () : 0), ((m_type & kpGrip::Bottom) ? point.y () - m_startPoint.y () : 0), dueToDragScroll); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpGrip::mouseMoveEvent (QMouseEvent *e) { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpGrip::mouseMoveEvent() m_startPoint=" << m_startPoint << " stateAfter: buttons=" << (int *) (int) e->buttons (); -#endif if (m_startPoint == KP_INVALID_POINT) { if ((e->buttons () & Qt::MouseButtonMask) == 0) { setUserMessage (haventBegunDrawUserMessage ()); } return; } mouseMovedTo (e->pos (), false/*not due to drag scroll*/); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpGrip::mouseReleaseEvent (QMouseEvent *e) { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpGrip::mouseReleaseEvent() m_startPoint=" << m_startPoint << " stateAfter: buttons=" << (int *) (int) e->buttons (); -#endif if (m_startPoint != KP_INVALID_POINT) { const int dx = m_currentPoint.x () - m_startPoint.x (), dy = m_currentPoint.y () - m_startPoint.y (); m_currentPoint = KP_INVALID_POINT; m_startPoint = KP_INVALID_POINT; releaseKeyboard (); emit endedDraw ((m_type & kpGrip::Right) ? dx : 0, (m_type & kpGrip::Bottom) ? dy : 0); } if ((e->buttons () & Qt::MouseButtonMask) == 0) { m_shouldReleaseMouseButtons = false; setUserMessage(QString()); setCursor (cursorForType (m_type)); releaseKeyboard (); emit releasedAllButtons (); } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpGrip::enterEvent (QEvent * /*e*/) { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpGrip::enterEvent()" << " m_startPoint=" << m_startPoint << " shouldReleaseMouseButtons=" << m_shouldReleaseMouseButtons; -#endif if (m_startPoint == KP_INVALID_POINT && !m_shouldReleaseMouseButtons) { - #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "\tsending message"; - #endif setUserMessage (haventBegunDrawUserMessage ()); } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpGrip::leaveEvent (QEvent * /*e*/) { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpGrip::leaveEvent()" << " m_startPoint=" << m_startPoint << " shouldReleaseMouseButtons=" << m_shouldReleaseMouseButtons; -#endif + if (m_startPoint == KP_INVALID_POINT && !m_shouldReleaseMouseButtons) { setUserMessage(QString()); } } //--------------------------------------------------------------------- //--------------------------------------------------------------------- //--------------------------------------------------------------------- // TODO: Are we checking for m_view == 0 often enough? Also an issue in KDE 3. kpViewScrollableContainer::kpViewScrollableContainer(QWidget *parent) : QScrollArea(parent), m_view(nullptr), m_overlay(new kpOverlay(viewport(), this)), m_docResizingGrip (nullptr), m_dragScrollTimer (new QTimer (this)), m_zoomLevel (100), m_scrollTimerRunOnce (false), m_resizeRoundedLastViewX (-1), m_resizeRoundedLastViewY (-1), m_resizeRoundedLastViewDX (0), m_resizeRoundedLastViewDY (0), m_haveMovedFromOriginalDocSize (false) { // the base widget holding the documents view plus the resize grips setWidget(new QWidget(viewport())); m_bottomGrip = new kpGrip(kpGrip::Bottom, widget()); m_rightGrip = new kpGrip(kpGrip::Right, widget()); m_bottomRightGrip = new kpGrip(kpGrip::BottomRight, widget()); m_bottomGrip->setObjectName(QStringLiteral("Bottom Grip")); m_rightGrip->setObjectName(QStringLiteral("Right Grip")); m_bottomRightGrip->setObjectName(QStringLiteral("BottomRight Grip")); m_bottomGrip->hide (); connectGripSignals (m_bottomGrip); m_rightGrip->hide (); connectGripSignals (m_rightGrip); m_bottomRightGrip->hide (); connectGripSignals (m_bottomRightGrip); connect (horizontalScrollBar(), &QScrollBar::valueChanged, this, &kpViewScrollableContainer::slotContentsMoved); connect (verticalScrollBar(), &QScrollBar::valueChanged, this, &kpViewScrollableContainer::slotContentsMoved); connect (m_dragScrollTimer, &QTimer::timeout, this, [this]{slotDragScroll();}); m_overlay->hide(); } //--------------------------------------------------------------------- // protected void kpViewScrollableContainer::connectGripSignals (kpGrip *grip) { connect (grip, &kpGrip::beganDraw, this, &kpViewScrollableContainer::slotGripBeganDraw); connect (grip, &kpGrip::continuedDraw, this, &kpViewScrollableContainer::slotGripContinuedDraw); connect (grip, &kpGrip::cancelledDraw, this, &kpViewScrollableContainer::slotGripCancelledDraw); connect (grip, &kpGrip::endedDraw, this, &kpViewScrollableContainer::slotGripEndedDraw); connect (grip, &kpGrip::statusMessageChanged, this, &kpViewScrollableContainer::slotGripStatusMessageChanged); connect (grip, &kpGrip::releasedAllButtons, this, &kpViewScrollableContainer::recalculateStatusMessage); } //--------------------------------------------------------------------- // public QSize kpViewScrollableContainer::newDocSize () const { return newDocSize (m_resizeRoundedLastViewDX, m_resizeRoundedLastViewDY); } //--------------------------------------------------------------------- // public bool kpViewScrollableContainer::haveMovedFromOriginalDocSize () const { return m_haveMovedFromOriginalDocSize; } //--------------------------------------------------------------------- // public QString kpViewScrollableContainer::statusMessage () const { return m_gripStatusMessage; } //--------------------------------------------------------------------- // public void kpViewScrollableContainer::clearStatusMessage () { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1 qCDebug(kpLogMisc) << "kpViewScrollableContainer::clearStatusMessage()"; -#endif + m_bottomRightGrip->setUserMessage(QString()); m_bottomGrip->setUserMessage(QString()); m_rightGrip->setUserMessage(QString()); } //--------------------------------------------------------------------- // protected QSize kpViewScrollableContainer::newDocSize (int viewDX, int viewDY) const { if (!m_view) { return {}; } if (!docResizingGrip ()) { return {}; } const int docX = static_cast (m_view->transformViewToDocX (m_view->width () + viewDX)); const int docY = static_cast (m_view->transformViewToDocY (m_view->height () + viewDY)); return {qMax (1, docX), qMax (1, docY)}; } //--------------------------------------------------------------------- // protected void kpViewScrollableContainer::calculateDocResizingGrip () { if (m_bottomRightGrip->isDrawing ()) { m_docResizingGrip = m_bottomRightGrip; } else if (m_bottomGrip->isDrawing ()) { m_docResizingGrip = m_bottomGrip; } else if (m_rightGrip->isDrawing ()) { m_docResizingGrip = m_rightGrip; } else { m_docResizingGrip = nullptr; } } //--------------------------------------------------------------------- // protected kpGrip *kpViewScrollableContainer::docResizingGrip () const { return m_docResizingGrip; } //--------------------------------------------------------------------- // protected int kpViewScrollableContainer::bottomResizeLineWidth () const { if (!docResizingGrip ()) { return -1; } if (!m_view) { return -1; } if (docResizingGrip ()->type () & kpGrip::Bottom) { return qMax (m_view->zoomLevelY () / 100, 1); } return 1; } //--------------------------------------------------------------------- // protected int kpViewScrollableContainer::rightResizeLineWidth () const { if (!docResizingGrip ()) { return -1; } if (!m_view) { return -1; } if (docResizingGrip ()->type () & kpGrip::Right) { return qMax (m_view->zoomLevelX () / 100, 1); } return 1; } //--------------------------------------------------------------------- // protected QRect kpViewScrollableContainer::bottomResizeLineRect () const { if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0) { return {}; } QRect visibleArea = QRect(QPoint(horizontalScrollBar()->value(),verticalScrollBar()->value()), viewport()->size()); return QRect (QPoint (0, m_resizeRoundedLastViewY), QPoint (m_resizeRoundedLastViewX - 1, m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)).intersected(visibleArea); } //--------------------------------------------------------------------- // protected QRect kpViewScrollableContainer::rightResizeLineRect () const { if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0) { return {}; } QRect visibleArea = QRect(QPoint(horizontalScrollBar()->value(),verticalScrollBar()->value()), viewport()->size()); return QRect (QPoint (m_resizeRoundedLastViewX, 0), QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1, m_resizeRoundedLastViewY - 1)).intersected(visibleArea); } //--------------------------------------------------------------------- // protected QRect kpViewScrollableContainer::bottomRightResizeLineRect () const { if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0) { return {}; } QRect visibleArea = QRect(QPoint(horizontalScrollBar()->value(),verticalScrollBar()->value()), viewport()->size()); return QRect (QPoint (m_resizeRoundedLastViewX, m_resizeRoundedLastViewY), QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1, m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)).intersected(visibleArea); } //--------------------------------------------------------------------- // private QRect kpViewScrollableContainer::mapViewToViewport (const QRect &viewRect) { if (!viewRect.isValid ()) { return {}; } QRect ret = viewRect; ret.translate (-horizontalScrollBar()->value() - viewport()->x(), -verticalScrollBar()->value() - viewport()->y()); return ret; } //--------------------------------------------------------------------- void kpViewScrollableContainer::drawResizeLines () { static const char *stipple[] = { "8 8 2 1", ". c #000000", "# c #ffffff", "....####", "....####", "....####", "....####", "####....", "####....", "####....", "####...." }; QPainter p(m_overlay); p.setBackground(QPixmap(stipple)); const QRect rightRect = rightResizeLineRect(); if ( rightRect.isValid() ) { QRect rect = mapViewToViewport(rightRect); p.setBrushOrigin(rect.x(), rect.y()); p.eraseRect(rect); } const QRect bottomRect = bottomResizeLineRect(); if ( bottomRect.isValid() ) { QRect rect = mapViewToViewport(bottomRect); p.setBrushOrigin(rect.x(), rect.y()); p.eraseRect(rect); } const QRect bottomRightRect = bottomRightResizeLineRect (); if ( bottomRightRect.isValid() ) { QRect rect = mapViewToViewport(bottomRightRect); p.setBrushOrigin(rect.x(), rect.y()); p.eraseRect(rect); } } //--------------------------------------------------------------------- // protected void kpViewScrollableContainer::updateResizeLines (int viewX, int viewY, int viewDX, int viewDY) { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0 qCDebug(kpLogMisc) << "kpViewScrollableContainer::updateResizeLines(" << viewX << "," << viewY << ")" << " oldViewX=" << m_resizeRoundedLastViewX << " oldViewY=" << m_resizeRoundedLastViewY << " viewDX=" << viewDX << " viewDY=" << viewDY; -#endif if (viewX >= 0 && viewY >= 0) { m_resizeRoundedLastViewX = static_cast (m_view->transformDocToViewX (m_view->transformViewToDocX (viewX))); m_resizeRoundedLastViewY = static_cast (m_view->transformDocToViewY (m_view->transformViewToDocY (viewY))); m_resizeRoundedLastViewDX = viewDX; m_resizeRoundedLastViewDY = viewDY; } else { m_resizeRoundedLastViewX = -1; m_resizeRoundedLastViewY = -1; m_resizeRoundedLastViewDX = 0; m_resizeRoundedLastViewDY = 0; } m_overlay->update(); } //--------------------------------------------------------------------- // protected slot void kpViewScrollableContainer::slotGripBeganDraw () { if (!m_view) { return; } m_overlay->resize(viewport()->size()); // make it cover whole viewport m_overlay->move(viewport()->pos()); m_overlay->show(); m_overlay->raise(); // make it top-most calculateDocResizingGrip (); m_haveMovedFromOriginalDocSize = false; updateResizeLines (m_view->width (), m_view->height (), 0/*viewDX*/, 0/*viewDY*/); emit beganDocResize (); } //--------------------------------------------------------------------- // protected slot void kpViewScrollableContainer::slotGripContinuedDraw (int inViewDX, int inViewDY, bool dueToDragScroll) { int viewDX = inViewDX, viewDY = inViewDY; -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpViewScrollableContainer::slotGripContinuedDraw(" << viewDX << "," << viewDY << ") size=" << newDocSize (viewDX, viewDY) << " dueToDragScroll=" << dueToDragScroll; -#endif if (!m_view) { return; } if (!dueToDragScroll && beginDragScroll(m_view->zoomLevelX ())) { const QPoint newViewDeltaPoint = docResizingGrip ()->viewDeltaPoint (); viewDX = newViewDeltaPoint.x (); viewDY = newViewDeltaPoint.y (); - #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + qCDebug(kpLogMisc) << "\tdrag scrolled - new view delta point=" << newViewDeltaPoint; - #endif } m_haveMovedFromOriginalDocSize = true; updateResizeLines (qMax (1, qMax (m_view->width () + viewDX, static_cast (m_view->transformDocToViewX (1)))), qMax (1, qMax (m_view->height () + viewDY, static_cast (m_view->transformDocToViewY (1)))), viewDX, viewDY); emit continuedDocResize (newDocSize ()); } //--------------------------------------------------------------------- // protected slot void kpViewScrollableContainer::slotGripCancelledDraw () { m_haveMovedFromOriginalDocSize = false; updateResizeLines (-1, -1, 0, 0); calculateDocResizingGrip (); emit cancelledDocResize (); endDragScroll (); m_overlay->hide(); } //--------------------------------------------------------------------- // protected slot void kpViewScrollableContainer::slotGripEndedDraw (int viewDX, int viewDY) { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpViewScrollableContainer::slotGripEndedDraw(" << viewDX << "," << viewDY << ") size=" << newDocSize (viewDX, viewDY); -#endif if (!m_view) { return; } const QSize newSize = newDocSize (viewDX, viewDY); m_haveMovedFromOriginalDocSize = false; // must erase lines before view size changes updateResizeLines (-1, -1, 0, 0); calculateDocResizingGrip (); emit endedDocResize (newSize); endDragScroll (); m_overlay->hide(); } //--------------------------------------------------------------------- // protected slot void kpViewScrollableContainer::slotGripStatusMessageChanged (const QString &string) { if (string == m_gripStatusMessage) { return; } m_gripStatusMessage = string; emit statusMessageChanged (string); } //--------------------------------------------------------------------- // public slot void kpViewScrollableContainer::recalculateStatusMessage () { -#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER qCDebug(kpLogMisc) << "kpViewScrollabelContainer::recalculateStatusMessage()"; qCDebug(kpLogMisc) << "\tQCursor::pos=" << QCursor::pos () << " global visibleRect=" << kpWidgetMapper::toGlobal (this, QRect(0, 0, viewport()->width(), viewport()->height())); -#endif + // HACK: After dragging to a new size, handles move so that they are now // under the mouse pointer but no mouseMoveEvent() is generated for // any grip. This also handles the case of canceling over any // grip. // if (kpWidgetMapper::toGlobal (this, QRect(0, 0, viewport()->width(), viewport()->height())) .contains (QCursor::pos ())) { if ( m_bottomRightGrip->containsCursor() ) { m_bottomRightGrip->setUserMessage (i18n ("Left drag the handle to resize the image.")); } else if ( m_bottomGrip->containsCursor() ) { m_bottomGrip->setUserMessage (i18n ("Left drag the handle to resize the image.")); } else if ( m_rightGrip->containsCursor() ) { m_rightGrip->setUserMessage (i18n ("Left drag the handle to resize the image.")); } else { clearStatusMessage (); } } else { clearStatusMessage (); } } //--------------------------------------------------------------------- // protected slot void kpViewScrollableContainer::slotContentsMoved () { kpGrip *grip = docResizingGrip (); if (grip) { grip->mouseMovedTo (grip->mapFromGlobal (QCursor::pos ()), true/*moved due to drag scroll*/); } m_overlay->move(viewport()->pos()); m_overlay->update(); emit contentsMoved(); } //--------------------------------------------------------------------- // protected void kpViewScrollableContainer::disconnectViewSignals () { disconnect (m_view, static_cast(&kpView::sizeChanged), this, &kpViewScrollableContainer::updateGrips); disconnect (m_view, &kpView::destroyed, this, &kpViewScrollableContainer::slotViewDestroyed); } //--------------------------------------------------------------------- // protected void kpViewScrollableContainer::connectViewSignals () { connect (m_view, static_cast(&kpView::sizeChanged), this, &kpViewScrollableContainer::updateGrips); connect (m_view, &kpView::destroyed, this, &kpViewScrollableContainer::slotViewDestroyed); } //--------------------------------------------------------------------- // public kpView *kpViewScrollableContainer::view () const { return m_view; } //--------------------------------------------------------------------- // public void kpViewScrollableContainer::setView (kpView *view) { if (m_view == view) { return; } if (m_view) { disconnectViewSignals (); } m_view = view; if ( m_view ) { m_view->setParent(widget()); m_view->show(); } updateGrips (); if (m_view) { connectViewSignals (); } } //--------------------------------------------------------------------- // public slot void kpViewScrollableContainer::updateGrips () { if (m_view) { widget()->resize(m_view->size() + m_bottomRightGrip->size()); // to make the grip more easily "touchable" make it as high as the view m_rightGrip->setFixedHeight(m_view->height()); m_rightGrip->move(m_view->width(), 0); // to make the grip more easily "touchable" make it as wide as the view m_bottomGrip->setFixedWidth(m_view->width()); m_bottomGrip->move(0, m_view->height ()); m_bottomRightGrip->move(m_view->width(), m_view->height()); } m_bottomGrip->setHidden (m_view == nullptr); m_rightGrip->setHidden (m_view == nullptr); m_bottomRightGrip->setHidden (m_view == nullptr); recalculateStatusMessage (); } //--------------------------------------------------------------------- // protected slot void kpViewScrollableContainer::slotViewDestroyed () { m_view = nullptr; updateGrips (); } //--------------------------------------------------------------------- // public slot bool kpViewScrollableContainer::beginDragScroll(int zoomLevel, bool *didSomething) { if (didSomething) { *didSomething = false; } m_zoomLevel = zoomLevel; const QPoint p = mapFromGlobal (QCursor::pos ()); bool stopDragScroll = true; bool scrolled = false; if (!noDragScrollRect ().contains (p)) { if (m_dragScrollTimer->isActive ()) { if (m_scrollTimerRunOnce) { scrolled = slotDragScroll (); } } else { m_scrollTimerRunOnce = false; m_dragScrollTimer->start (DragScrollInitialInterval); } stopDragScroll = false; } if (stopDragScroll) { m_dragScrollTimer->stop (); } if (didSomething) { *didSomething = scrolled; } return scrolled; } //--------------------------------------------------------------------- // public slot bool kpViewScrollableContainer::beginDragScroll(int zoomLevel) { return beginDragScroll(zoomLevel, nullptr/*don't want scrolled notification*/); } //--------------------------------------------------------------------- // public slot bool kpViewScrollableContainer::endDragScroll () { if (m_dragScrollTimer->isActive ()) { m_dragScrollTimer->stop (); return true; } return false; } //--------------------------------------------------------------------- static int distanceFromRectToMultiplier (int dist) { if (dist < 0) { return 0; } if (dist < DragDistanceFromRectMaxFor1stMultiplier) { return 1; } if (dist < DragDistanceFromRectMaxFor2ndMultiplier) { return 2; } return 4; } //--------------------------------------------------------------------- // protected slot bool kpViewScrollableContainer::slotDragScroll (bool *didSomething) { bool scrolled = false; if (didSomething) { *didSomething = false; } const QRect rect = noDragScrollRect (); const QPoint pos = mapFromGlobal (QCursor::pos ()); int dx = 0, dy = 0; int dxMultiplier = 0, dyMultiplier = 0; if (pos.x () < rect.left ()) { dx = -DragScrollNumPixels; dxMultiplier = distanceFromRectToMultiplier (rect.left () - pos.x ()); } else if (pos.x () > rect.right ()) { dx = +DragScrollNumPixels; dxMultiplier = distanceFromRectToMultiplier (pos.x () - rect.right ()); } if (pos.y () < rect.top ()) { dy = -DragScrollNumPixels; dyMultiplier = distanceFromRectToMultiplier (rect.top () - pos.y ()); } else if (pos.y () > rect.bottom ()) { dy = +DragScrollNumPixels; dyMultiplier = distanceFromRectToMultiplier (pos.y () - rect.bottom ()); } dx *= dxMultiplier;// * qMax (1, m_zoomLevel / 100); dy *= dyMultiplier;// * qMax (1, m_zoomLevel / 100); if (dx || dy) { const int oldContentsX = horizontalScrollBar()->value (), oldContentsY = verticalScrollBar()->value (); horizontalScrollBar()->setValue(oldContentsX + dx); verticalScrollBar()->setValue(oldContentsY + dy); scrolled = (oldContentsX != horizontalScrollBar()->value () || oldContentsY != verticalScrollBar()->value ()); if (scrolled) { QRegion region = QRect (horizontalScrollBar()->value (), verticalScrollBar()->value (), viewport()->width(), viewport()->height()); region -= QRect (oldContentsX, oldContentsY, viewport()->width(), viewport()->height()); // Repaint newly exposed region immediately to reduce tearing // of scrollView. m_view->repaint (region); } } m_dragScrollTimer->start (DragScrollInterval); m_scrollTimerRunOnce = true; if (didSomething) { *didSomething = scrolled; } return scrolled; } //--------------------------------------------------------------------- // protected virtual void kpViewScrollableContainer::wheelEvent (QWheelEvent *e) { e->ignore (); if (m_view) { m_view->wheelEvent (e); } if ( !e->isAccepted() ) { QScrollArea::wheelEvent(e); } } //--------------------------------------------------------------------------------- QRect kpViewScrollableContainer::noDragScrollRect () const { return {DragScrollLeftTopMargin, DragScrollLeftTopMargin, width () - DragScrollLeftTopMargin - DragScrollRightBottomMargin, height () - DragScrollLeftTopMargin - DragScrollRightBottomMargin}; } //--------------------------------------------------------------------- // protected virtual [base QScrollView] void kpViewScrollableContainer::resizeEvent (QResizeEvent *e) { QScrollArea::resizeEvent (e); emit resized (); } //--------------------------------------------------------------------- diff --git a/layers/selections/image/kpAbstractImageSelection.cpp b/layers/selections/image/kpAbstractImageSelection.cpp index 5b739aa1..f23f77a1 100644 --- a/layers/selections/image/kpAbstractImageSelection.cpp +++ b/layers/selections/image/kpAbstractImageSelection.cpp @@ -1,589 +1,558 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "layers/selections/image/kpAbstractImageSelection.h" #include #include #include "kpLogCategories.h" //--------------------------------------------------------------------- // Returns whether can be set to have . // In other words, this is the precondition for .setBaseImage(width () && baseImage.height () == sel->height ()); } //--------------------------------------------------------------------- struct kpAbstractImageSelectionPrivate { kpImage baseImage; kpImageSelectionTransparency transparency; // The mask for the image, after selection transparency (a.k.a. background // subtraction) is applied. QBitmap transparencyMaskCache; // OPT: calculate lazily i.e. on-demand only }; //--------------------------------------------------------------------- // protected kpAbstractImageSelection::kpAbstractImageSelection ( const kpImageSelectionTransparency &transparency) : kpAbstractSelection (), d (new kpAbstractImageSelectionPrivate ()) { setTransparency (transparency); } //--------------------------------------------------------------------- // protected kpAbstractImageSelection::kpAbstractImageSelection (const QRect &rect, const kpImage &baseImage, const kpImageSelectionTransparency &transparency) : kpAbstractSelection (rect), d (new kpAbstractImageSelectionPrivate ()) { // This also checks that and have compatible // relative dimensions. setBaseImage (baseImage); setTransparency (transparency); } //--------------------------------------------------------------------- // protected kpAbstractImageSelection::kpAbstractImageSelection (const QRect &rect, const kpImageSelectionTransparency &transparency) : kpAbstractSelection (rect), d (new kpAbstractImageSelectionPrivate ()) { setTransparency (transparency); } //--------------------------------------------------------------------- // protected kpAbstractImageSelection &kpAbstractImageSelection::operator= ( const kpAbstractImageSelection &rhs) { kpAbstractSelection::operator= (rhs); d->baseImage = rhs.d->baseImage; d->transparency = rhs.d->transparency; d->transparencyMaskCache = rhs.d->transparencyMaskCache; return *this; } //--------------------------------------------------------------------- // protected kpAbstractImageSelection::~kpAbstractImageSelection () { delete d; } //--------------------------------------------------------------------- // public virtual [base kpAbstractSelection] bool kpAbstractImageSelection::readFromStream (QDataStream &stream) { if (!kpAbstractSelection::readFromStream (stream )) { return false; } QImage qimage; stream >> qimage; -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\timage: w=" << qimage.width () << " h=" << qimage.height () << " depth=" << qimage.depth (); -#endif if (!qimage.isNull ()) { // Image size does not match the selection's dimensions? // This call only accesses our superclass' fields, which have already // been read in. if (!::CanSetBaseImageTo (this, qimage)) { return false; } d->baseImage = qimage; } // (was just a selection border in the clipboard, even though KolourPaint's // GUI doesn't allow you to copy such a thing into the clipboard) else { d->baseImage = kpImage (); } // TODO: Reset transparency mask? // TODO: Concrete subclass need to emit changed()? // [we can't since changed() must be called after all reading // is complete and subclasses always call this method // _before_ their reading logic] return true; } //--------------------------------------------------------------------- // public virtual [base kpAbstractSelection] void kpAbstractImageSelection::writeToStream (QDataStream &stream) const { kpAbstractSelection::writeToStream (stream); if (!d->baseImage.isNull ()) { const QImage image = d->baseImage; - #if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\twrote image rect=" << image.rect (); - #endif stream << image; } else { - #if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\twrote no image because no pixmap"; - #endif stream << QImage (); } } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] QString kpAbstractImageSelection::name () const { return i18n ("Selection"); } //--------------------------------------------------------------------- // public virtual [base kpAbstractSelection] kpCommandSize::SizeType kpAbstractImageSelection::size () const { return kpAbstractSelection::size () + kpCommandSize::ImageSize (d->baseImage) + (d->transparencyMaskCache.width() * d->transparencyMaskCache.height()) / 8; } //--------------------------------------------------------------------- // public kpCommandSize::SizeType kpAbstractImageSelection::sizeWithoutImage () const { return (size () - kpCommandSize::ImageSize (d->baseImage)); } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] int kpAbstractImageSelection::minimumWidth () const { return 1; } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] int kpAbstractImageSelection::minimumHeight () const { return 1; } //--------------------------------------------------------------------- // public virtual QBitmap kpAbstractImageSelection::shapeBitmap (bool nullForRectangular) const { (void) nullForRectangular; Q_ASSERT (boundingRect ().isValid ()); QBitmap maskBitmap (width (), height ()); maskBitmap.fill (Qt::color0/*transparent*/); { QPainter painter(&maskBitmap); painter.setPen (Qt::color1/*opaque*/); painter.setBrush (Qt::color1/*opaque*/); QPolygon points = calculatePoints (); points.translate (-x (), -y ()); // Unlike QPainter::drawRect(), this draws the points literally // without being 1 pixel wider and higher. This requires a QPen // or it will draw 1 pixel narrower and shorter. painter.drawPolygon (points, Qt::OddEvenFill); } return maskBitmap; } //--------------------------------------------------------------------- // public kpImage kpAbstractImageSelection::givenImageMaskedByShape (const kpImage &image) const { -#if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "kpAbstractImageSelection::givenImageMaskedByShape() boundingRect=" - << boundingRect () << endl; -#endif + << boundingRect (); Q_ASSERT (image.width () == width () && image.height () == height ()); if (isRectangular ()) { return image; } const QRegion mRegion = shapeRegion ().translated (-topLeft ()); -#if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "\tshapeRegion=" << shapeRegion () << " [rect=" << shapeRegion ().boundingRect () << "]" << " calculatePoints=" << calculatePoints () << " [rect=" << calculatePoints ().boundingRect () << "]" << endl; -#endif kpImage retImage(width (), height (), QImage::Format_ARGB32_Premultiplied); retImage.fill(0); // transparent QPainter painter(&retImage); painter.setClipRegion(mRegion); painter.drawImage(0, 0, image); painter.end(); return retImage; } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] bool kpAbstractImageSelection::hasContent () const { return !d->baseImage.isNull (); } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] void kpAbstractImageSelection::deleteContent () { if (!hasContent ()) { return; } setBaseImage (kpImage ()); } //--------------------------------------------------------------------- // public kpImage kpAbstractImageSelection::baseImage () const { return d->baseImage; } //--------------------------------------------------------------------- // public void kpAbstractImageSelection::setBaseImage (const kpImage &baseImage) { Q_ASSERT (::CanSetBaseImageTo (this, baseImage)); // qt doc: the image format must be set to Format_ARGB32Premultiplied or Format_ARGB32 // for the composition modes to have any effect d->baseImage = baseImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); recalculateTransparencyMaskCache (); emit changed (boundingRect ()); } //--------------------------------------------------------------------- // public kpImageSelectionTransparency kpAbstractImageSelection::transparency () const { return d->transparency; } //--------------------------------------------------------------------- // public bool kpAbstractImageSelection::setTransparency ( const kpImageSelectionTransparency &transparency, bool checkTransparentPixmapChanged) { if (d->transparency == transparency) { return false; } d->transparency = transparency; bool haveChanged = true; QBitmap oldTransparencyMaskCache = d->transparencyMaskCache; recalculateTransparencyMaskCache (); if ( oldTransparencyMaskCache.size() == d->transparencyMaskCache.size() ) { if (d->transparencyMaskCache.isNull ()) { - #if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "\tboth old and new pixmaps are null - nothing changed"; - #endif haveChanged = false; } else if (checkTransparentPixmapChanged) { QImage oldTransparencyMaskImage = oldTransparencyMaskCache.toImage(); QImage newTransparencyMaskImage = d->transparencyMaskCache.toImage(); bool changed = false; for (int y = 0; y < oldTransparencyMaskImage.height () && !changed; y++) { for (int x = 0; x < oldTransparencyMaskImage.width () && !changed; x++) { if (kpPixmapFX::getColorAtPixel (oldTransparencyMaskImage, x, y) != kpPixmapFX::getColorAtPixel (newTransparencyMaskImage, x, y)) { - #if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "\tdiffer at " << QPoint (x, y) << " old=" << kpPixmapFX::getColorAtPixel (oldTransparencyMaskImage, x, y).toQRgb () - << " new=" << kpPixmapFX::getColorAtPixel (newTransparencyMaskImage, x, y).toQRgb () - << endl; - #endif + << " new=" << kpPixmapFX::getColorAtPixel (newTransparencyMaskImage, x, y).toQRgb (); changed = true; break; } } } if (!changed) { haveChanged = false; } } } if (haveChanged) { emit changed (boundingRect ()); } return haveChanged; } //--------------------------------------------------------------------- // private void kpAbstractImageSelection::recalculateTransparencyMaskCache () { -#if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "kpAbstractImageSelection::recalculateTransparencyMaskCache()"; -#endif if (d->baseImage.isNull ()) { - #if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "\tno image - no need for transparency mask"; - #endif d->transparencyMaskCache = QBitmap (); return; } if (d->transparency.isOpaque ()) { - #if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "\topaque - no need for transparency mask"; - #endif d->transparencyMaskCache = QBitmap (); return; } d->transparencyMaskCache = QBitmap(d->baseImage.size()); QPainter transparencyMaskPainter (&d->transparencyMaskCache); bool hasTransparent = false; for (int y = 0; y < d->baseImage.height (); y++) { for (int x = 0; x < d->baseImage.width (); x++) { const kpColor pixelCol = kpPixmapFX::getColorAtPixel (d->baseImage, x, y); if (pixelCol == kpColor::Transparent || pixelCol.isSimilarTo (d->transparency.transparentColor (), d->transparency.processedColorSimilarity ())) { transparencyMaskPainter.setPen (Qt::color1/*transparent*/); hasTransparent = true; } else { transparencyMaskPainter.setPen (Qt::color0/*opaque*/); } transparencyMaskPainter.drawPoint (x, y); } } transparencyMaskPainter.end (); if (!hasTransparent) { - #if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "\tcolour useless - completely opaque"; - #endif d->transparencyMaskCache = QBitmap (); return; } } //--------------------------------------------------------------------- // public kpImage kpAbstractImageSelection::transparentImage () const { kpImage image = baseImage (); if (!d->transparencyMaskCache.isNull ()) { QPainter painter(&image); painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.drawPixmap(0, 0, d->transparencyMaskCache); } return image; } //--------------------------------------------------------------------- // public void kpAbstractImageSelection::fill (const kpColor &color) { QImage newImage(width(), height(), QImage::Format_ARGB32_Premultiplied); newImage.fill(color.toQRgb()); // LOTODO: Maybe disable Image/Clear menu item if transparent color if ( !color.isTransparent() ) { QPainter painter(&newImage); painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.drawPixmap(0, 0, shapeBitmap()); } setBaseImage (newImage); } //--------------------------------------------------------------------- // public virtual void kpAbstractImageSelection::flip (bool horiz, bool vert) { -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "kpAbstractImageSelection::flip(horiz=" << horiz << ",vert=" << vert << ")"; -#endif if (!d->baseImage.isNull ()) { - #if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\thave pixmap - flipping that"; - #endif d->baseImage = d->baseImage.mirrored(horiz, vert); } if (!d->transparencyMaskCache.isNull ()) { - #if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\thave transparency mask - flipping that"; - #endif QImage image = d->transparencyMaskCache.toImage().mirrored(horiz, vert); d->transparencyMaskCache = QBitmap::fromImage(image); } emit changed (boundingRect ()); } //--------------------------------------------------------------------- static void Paint (const kpAbstractImageSelection *sel, const kpImage &srcImage, QImage *destImage, const QRect &docRect) { if (!srcImage.isNull ()) { kpPixmapFX::paintPixmapAt (destImage, sel->topLeft () - docRect.topLeft (), srcImage); } } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] void kpAbstractImageSelection::paint (QImage *destImage, const QRect &docRect) const { ::Paint (this, transparentImage (), destImage, docRect); } //--------------------------------------------------------------------- // public void kpAbstractImageSelection::paintWithBaseImage (QImage *destImage, const QRect &docRect) const { ::Paint (this, baseImage (), destImage, docRect); } //--------------------------------------------------------------------- diff --git a/layers/selections/image/kpEllipticalImageSelection.cpp b/layers/selections/image/kpEllipticalImageSelection.cpp index a9098f6c..36a6a03c 100644 --- a/layers/selections/image/kpEllipticalImageSelection.cpp +++ b/layers/selections/image/kpEllipticalImageSelection.cpp @@ -1,185 +1,183 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "layers/selections/image/kpEllipticalImageSelection.h" #include #include #include struct kpEllipticalImageSelectionPrivate { }; kpEllipticalImageSelection::kpEllipticalImageSelection ( const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (transparency), d (new kpEllipticalImageSelectionPrivate ()) { } kpEllipticalImageSelection::kpEllipticalImageSelection (const QRect &rect, const kpImage &baseImage, const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (rect, baseImage, transparency), d (new kpEllipticalImageSelectionPrivate ()) { } kpEllipticalImageSelection::kpEllipticalImageSelection (const QRect &rect, const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (rect, transparency), d (new kpEllipticalImageSelectionPrivate ()) { } kpEllipticalImageSelection::kpEllipticalImageSelection (const kpEllipticalImageSelection &rhs) : kpAbstractImageSelection (), d (new kpEllipticalImageSelectionPrivate ()) { *this = rhs; } kpEllipticalImageSelection &kpEllipticalImageSelection::operator= ( const kpEllipticalImageSelection &rhs) { kpAbstractImageSelection::operator= (rhs); return *this; } kpEllipticalImageSelection *kpEllipticalImageSelection::clone () const { kpEllipticalImageSelection *sel = new kpEllipticalImageSelection (); *sel = *this; return sel; } kpEllipticalImageSelection::~kpEllipticalImageSelection () { delete d; } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] int kpEllipticalImageSelection::serialID () const { return SerialID; } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] bool kpEllipticalImageSelection::isRectangular () const { return false; } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] QPolygon kpEllipticalImageSelection::calculatePoints () const { Q_ASSERT (boundingRect ().isValid ()); if (width () == 1 && height () == 1) { QPolygon ret; ret.append (topLeft ()); return ret; } QPainterPath path; if (width () == 1 || height () == 1) { path.moveTo (x (), y ()); // This does not work when the width _and_ height are 1 since lineTo() // would not move at all. This is why we have a separate case for that // at the top of the method. path.lineTo (x () + width () - 1, y () + height () - 1); } else { // The adjusting is to fight QPainterPath::addEllipse() making // the ellipse 1 pixel higher and wider than specified. path.addEllipse (boundingRect ().adjusted (0, 0, -1, -1)); } const QList polygons = path.toSubpathPolygons (); Q_ASSERT (polygons.size () == 1); const QPolygonF& firstPolygonF = polygons.first (); return firstPolygonF.toPolygon (); } //--------------------------------------------------------------------- // protected virtual [kpAbstractImageSelection] QRegion kpEllipticalImageSelection::shapeRegion () const { QRegion reg(calculatePoints()); return reg; } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] bool kpEllipticalImageSelection::contains (const QPoint &point) const { if (!boundingRect ().contains (point)) { return false; } return shapeRegion ().contains (point); } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] void kpEllipticalImageSelection::paintBorder (QImage *destPixmap, const QRect &docRect, bool selectionFinished) const { if ( !boundingRect().isValid() ) { return; } paintPolygonalBorder (calculatePoints (), destPixmap, docRect, selectionFinished); } diff --git a/layers/selections/image/kpFreeFormImageSelection.cpp b/layers/selections/image/kpFreeFormImageSelection.cpp index 82c47c40..5237d04a 100644 --- a/layers/selections/image/kpFreeFormImageSelection.cpp +++ b/layers/selections/image/kpFreeFormImageSelection.cpp @@ -1,394 +1,388 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "layers/selections/image/kpFreeFormImageSelection.h" #include "kpLogCategories.h" #include "imagelib/kpPainter.h" struct kpFreeFormImageSelectionPrivate { QPolygon orgPoints; // Various Qt methods that take a QPolygon interpolate points differently // (e.g. QPainter::drawPolygon() vs QRegion(QPolygon)) when given consecutive // points that are not cardinally adjacent e.g. these 2 points: // // # // # // // are diagonally, but not cardinally, adjacent. They are rendered // inconsistently. Also, points which are not adjacent at all definitely // require interpolation and are inconsistently rendered: // // # // # // // So, we only pass cardinally interpolated points to those methods to // avoid this issue: // // ## // # // // These interpolated points are stored in . Regarding // , see the APIDoc for cardinallyAdjacentPointsLoop(). QPolygon cardPointsCache, cardPointsLoopCache; }; kpFreeFormImageSelection::kpFreeFormImageSelection ( const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (transparency), d (new kpFreeFormImageSelectionPrivate ()) { } kpFreeFormImageSelection::kpFreeFormImageSelection (const QPolygon &points, const kpImage &baseImage, const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (points.boundingRect (), baseImage, transparency), d (new kpFreeFormImageSelectionPrivate ()) { d->orgPoints = points; recalculateCardinallyAdjacentPoints (); } kpFreeFormImageSelection::kpFreeFormImageSelection (const QPolygon &points, const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (points.boundingRect (), transparency), d (new kpFreeFormImageSelectionPrivate ()) { d->orgPoints = points; recalculateCardinallyAdjacentPoints (); } kpFreeFormImageSelection::kpFreeFormImageSelection (const kpFreeFormImageSelection &rhs) : kpAbstractImageSelection (), d (new kpFreeFormImageSelectionPrivate ()) { *this = rhs; } kpFreeFormImageSelection &kpFreeFormImageSelection::operator= (const kpFreeFormImageSelection &rhs) { kpAbstractImageSelection::operator= (rhs); d->orgPoints = rhs.d->orgPoints; d->cardPointsCache = rhs.d->cardPointsCache; d->cardPointsLoopCache = rhs.d->cardPointsLoopCache; return *this; } // public virtual [kpAbstractSelection] kpFreeFormImageSelection *kpFreeFormImageSelection::clone () const { kpFreeFormImageSelection *sel = new kpFreeFormImageSelection (); *sel = *this; return sel; } kpFreeFormImageSelection::~kpFreeFormImageSelection () { delete d; } // public virtual [kpAbstractSelection] int kpFreeFormImageSelection::serialID () const { return SerialID; } // public virtual [base kpAbstractImageSelection] bool kpFreeFormImageSelection::readFromStream (QDataStream &stream) { if (!kpAbstractImageSelection::readFromStream (stream)) { return false; } stream >> d->orgPoints; recalculateCardinallyAdjacentPoints (); return true; } // public virtual [base kpAbstractImageSelection] void kpFreeFormImageSelection::writeToStream (QDataStream &stream) const { kpAbstractImageSelection::writeToStream (stream); stream << d->orgPoints; } // public virtual [base kpAbstractImageSelection] kpCommandSize::SizeType kpFreeFormImageSelection::size () const { return kpAbstractImageSelection::size () + (kpCommandSize::PolygonSize (d->orgPoints) + kpCommandSize::PolygonSize (d->cardPointsCache) + kpCommandSize::PolygonSize (d->cardPointsLoopCache)); } // public virtual [kpAbstractSelection] bool kpFreeFormImageSelection::isRectangular () const { return false; } // public QPolygon kpFreeFormImageSelection::originalPoints () const { return d->orgPoints; } static QPolygon RecalculateCardinallyAdjacentPoints (const QPolygon &points) { -#if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "kpFreeFormImageSelection.cpp:RecalculateCardinallyAdjacentPoints()"; qCDebug(kpLogLayers) << "\tpoints=" << points; -#endif // Filter out duplicates. QPolygon noDups; for (const auto &p : points) { if (!noDups.isEmpty () && p == noDups.last ()) { continue; } noDups.append (p); } -#if DEBUG_KP_SELECTION + qCDebug(kpLogLayers) << "\twithout dups=" << noDups; -#endif // Interpolate to ensure cardinal adjacency. QPolygon cardPoints; for (const auto &p : noDups) { if (!cardPoints.isEmpty () && !kpPainter::pointsAreCardinallyAdjacent (p, cardPoints.last ())) { const QPoint lastPoint = cardPoints.last (); QList interpPoints = kpPainter::interpolatePoints ( lastPoint, p, true/*cardinal adjacency*/); Q_ASSERT (interpPoints.size () >= 2); Q_ASSERT (interpPoints [0] == lastPoint); Q_ASSERT (interpPoints.last () == p); for (int i = 1/*skip already existing point*/; i < interpPoints.size (); i++) { cardPoints.append (interpPoints [i]); } } else { cardPoints.append (p); } } -#if DEBUG_KP_SELECTION + qCDebug(kpLogLayers) << "\tcardinally adjacent=" << cardPoints; -#endif return cardPoints; } // protected void kpFreeFormImageSelection::recalculateCardinallyAdjacentPoints () { d->cardPointsCache = ::RecalculateCardinallyAdjacentPoints (d->orgPoints); QPolygon pointsLoop = d->cardPointsCache; if (!pointsLoop.isEmpty ()) { pointsLoop.append (pointsLoop.first ()); } // OPT: We know this method only needs to act on the last 2 points of // "pointLoop", since the previous points are definitely cardinally // adjacent. d->cardPointsLoopCache = ::RecalculateCardinallyAdjacentPoints (pointsLoop); } // public QPolygon kpFreeFormImageSelection::cardinallyAdjacentPoints () const { return d->cardPointsCache; } // public QPolygon kpFreeFormImageSelection::cardinallyAdjacentPointsLoop () const { return d->cardPointsLoopCache; } // public virtual [kpAbstractSelection] QPolygon kpFreeFormImageSelection::calculatePoints () const { return d->cardPointsLoopCache; } // protected virtual [kpAbstractSelection] QRegion kpFreeFormImageSelection::shapeRegion () const { const QRegion region = QRegion (d->cardPointsLoopCache, Qt::OddEvenFill); // In Qt4, while QPainter::drawRect() gives you rectangles 1 pixel // wider and higher, QRegion(QPolygon) gives you regions 1 pixel // narrower and shorter! Compensate for this by merging shifted // versions of the region. This seems to be consistent with shapeBitmap() // but I am a bit worried. // // Regarding alternative solutions: // 1. Instead of doing this region shifting and merging, if we were to // construct a QRegion simply from a point array with 4 points for // every point in "d->cardPointsLoopCache" (4 points = original point + 3 // translations below), it probably wouldn't work because the order of // the points in any point array matter for the odd-even fill // algorithm. This would probably manifest as problems with // self-intersecting borders. // 2. Constructing a QRegion from QBitmap (from shapeBitmap()) is probably // very slow since it would have to read each pixel of the QBitmap. // Having said that, this is probably the safest option as region shifting // is dodgy. Also, this would guarantee that shapeBitmap() and shapeRegion() // are consistent and we wouldn't need cardinally adjacent points either // (d->cardPointsCache and d->cardPointsLoopCache). const QRegion regionX = region.translated (1, 0); const QRegion regionY = region.translated (0, 1); const QRegion regionXY = region.translated (1, 1); return region.united (regionX).united (regionY).united (regionXY); } // public virtual [kpAbstractSelection] bool kpFreeFormImageSelection::contains (const QPoint &point) const { if (!boundingRect ().contains (point)) { return false; } // We can't use the baseImage() (when non-null) and get the transparency of // the pixel at , instead of this region test, as the pixel may be // transparent but still within the border. return shapeRegion ().contains (point); } // public virtual [base kpAbstractSelection] void kpFreeFormImageSelection::moveBy (int dx, int dy) { d->orgPoints.translate (dx, dy); d->cardPointsCache.translate (dx, dy); d->cardPointsLoopCache.translate (dx, dy); // Call base last since it fires the changed() signal and we only // want that to fire at the very end of this method, after all // the selection state has been changed. kpAbstractImageSelection::moveBy (dx, dy); } //--------------------------------------------------------------------- static void FlipPoints (QPolygon *points, bool horiz, bool vert, const QRect &oldRect) { points->translate (-oldRect.x (), -oldRect.y ()); const QTransform matrix (horiz ? -1 : +1, // m11 0, // m12 0, // m21 vert ? -1 : +1, // m22 horiz ? (oldRect.width() - 1) : 0, // dx vert ? (oldRect.height() - 1) : 0); // dy #if !defined (QT_NO_DEBUG) && !defined (NDEBUG) QPolygon oldPoints = *points; #endif *points = matrix.map (*points); #if !defined (QT_NO_DEBUG) && !defined (NDEBUG) // Sanity check: flipping the points twice gives us the original points. Q_ASSERT (oldPoints == matrix.map (*points)); #endif points->translate (oldRect.x (), oldRect.y ()); } //--------------------------------------------------------------------- // public virtual [base kpAbstractImageSelection] void kpFreeFormImageSelection::flip (bool horiz, bool vert) { ::FlipPoints (&d->orgPoints, horiz, vert, boundingRect ()); ::FlipPoints (&d->cardPointsCache, horiz, vert, boundingRect ()); ::FlipPoints (&d->cardPointsLoopCache, horiz, vert, boundingRect ()); // Call base last since it fires the changed() signal and we only // want that to fire at the very end of this method, after all // the selection state has been changed. kpAbstractImageSelection::flip (horiz, vert); } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] void kpFreeFormImageSelection::paintBorder (QImage *destPixmap, const QRect &docRect, bool selectionFinished) const { if (selectionFinished) { paintPolygonalBorder (cardinallyAdjacentPointsLoop (), destPixmap, docRect, selectionFinished); } else { paintPolygonalBorder (cardinallyAdjacentPoints (), destPixmap, docRect, selectionFinished); } } diff --git a/layers/selections/image/kpImageSelectionTransparency.cpp b/layers/selections/image/kpImageSelectionTransparency.cpp index 519fb15e..8064925a 100644 --- a/layers/selections/image/kpImageSelectionTransparency.cpp +++ b/layers/selections/image/kpImageSelectionTransparency.cpp @@ -1,206 +1,194 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION_TRANSPARENCY 1 #include "layers/selections/image/kpImageSelectionTransparency.h" #include "kpLogCategories.h" #include "imagelib/kpColor.h" #include "widgets/colorSimilarity/kpColorSimilarityHolder.h" //--------------------------------------------------------------------- kpImageSelectionTransparency::kpImageSelectionTransparency () : m_isOpaque (true) { setColorSimilarity (0); } //--------------------------------------------------------------------- kpImageSelectionTransparency::kpImageSelectionTransparency (const kpColor &transparentColor, double colorSimilarity) : m_isOpaque (false), m_transparentColor (transparentColor) { setColorSimilarity (colorSimilarity); } //--------------------------------------------------------------------- kpImageSelectionTransparency::kpImageSelectionTransparency (bool isOpaque, const kpColor &transparentColor, double colorSimilarity) : m_isOpaque (isOpaque), m_transparentColor (transparentColor) { setColorSimilarity (colorSimilarity); } //--------------------------------------------------------------------- bool kpImageSelectionTransparency::operator== (const kpImageSelectionTransparency &rhs) const { -#if DEBUG_KP_SELECTION_TRANSPARENCY && 0 qCDebug(kpLogLayers) << "kpImageSelectionTransparency::operator==()"; -#endif if (m_isOpaque != rhs.m_isOpaque) { - #if DEBUG_KP_SELECTION_TRANSPARENCY && 0 qCDebug(kpLogLayers) << "\tdifferent opacity: lhs=" << m_isOpaque - << " rhs=" << rhs.m_isOpaque - << endl; - #endif + << " rhs=" << rhs.m_isOpaque; return false; } if (m_isOpaque) { - #if DEBUG_KP_SELECTION_TRANSPARENCY && 0 qCDebug(kpLogLayers) << "\tboth opaque - eq"; - #endif return true; } -#if DEBUG_KP_SELECTION_TRANSPARENCY && 0 qCDebug(kpLogLayers) << "\tcolours: lhs=" << (int *) m_transparentColor.toQRgb () - << " rhs=" << (int *) rhs.m_transparentColor.toQRgb () - << endl; + << " rhs=" << (int *) rhs.m_transparentColor.toQRgb (); qCDebug(kpLogLayers) << "\tcolour similarity: lhs=" << m_colorSimilarity - << " rhs=" << rhs.m_colorSimilarity - << endl; -#endif + << " rhs=" << rhs.m_colorSimilarity; return (m_transparentColor == rhs.m_transparentColor && m_colorSimilarity == rhs.m_colorSimilarity); } //--------------------------------------------------------------------- bool kpImageSelectionTransparency::operator!= (const kpImageSelectionTransparency &rhs) const { return !(*this == rhs); } //--------------------------------------------------------------------- kpImageSelectionTransparency::~kpImageSelectionTransparency () = default; //--------------------------------------------------------------------- // public bool kpImageSelectionTransparency::isOpaque () const { return m_isOpaque; } //--------------------------------------------------------------------- // public bool kpImageSelectionTransparency::isTransparent () const { return !isOpaque (); } //--------------------------------------------------------------------- // public void kpImageSelectionTransparency::setOpaque (bool yes) { m_isOpaque = yes; } //--------------------------------------------------------------------- // public void kpImageSelectionTransparency::setTransparent (bool yes) { setOpaque (!yes); } //--------------------------------------------------------------------- // public kpColor kpImageSelectionTransparency::transparentColor () const { if (m_isOpaque) { // There are legitimate uses for this so no qCCritical(kpLogLayers) qCDebug(kpLogLayers) << "kpImageSelectionTransparency::transparentColor() " "getting transparent color even though opaque"; } return m_transparentColor; } //--------------------------------------------------------------------- // public void kpImageSelectionTransparency::setTransparentColor (const kpColor &transparentColor) { m_transparentColor = transparentColor; } //--------------------------------------------------------------------- // public double kpImageSelectionTransparency::colorSimilarity () const { if (m_colorSimilarity < 0 || m_colorSimilarity > kpColorSimilarityHolder::MaxColorSimilarity) { qCCritical(kpLogLayers) << "kpImageSelectionTransparency::colorSimilarity() invalid colorSimilarity"; return 0; } return m_colorSimilarity; } //--------------------------------------------------------------------- // pubulic void kpImageSelectionTransparency::setColorSimilarity (double colorSimilarity) { m_colorSimilarity = colorSimilarity; m_processedColorSimilarity = kpColor::processSimilarity (colorSimilarity); } //--------------------------------------------------------------------- // public int kpImageSelectionTransparency::processedColorSimilarity () const { return m_processedColorSimilarity; } //--------------------------------------------------------------------- diff --git a/layers/selections/image/kpRectangularImageSelection.cpp b/layers/selections/image/kpRectangularImageSelection.cpp index 514d1fe8..d6157f61 100644 --- a/layers/selections/image/kpRectangularImageSelection.cpp +++ b/layers/selections/image/kpRectangularImageSelection.cpp @@ -1,149 +1,147 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "layers/selections/image/kpRectangularImageSelection.h" #include #include struct kpRectangularImageSelectionPrivate { }; kpRectangularImageSelection::kpRectangularImageSelection ( const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (transparency), d (new kpRectangularImageSelectionPrivate ()) { } kpRectangularImageSelection::kpRectangularImageSelection (const QRect &rect, const kpImage &baseImage, const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (rect, baseImage, transparency), d (new kpRectangularImageSelectionPrivate ()) { } kpRectangularImageSelection::kpRectangularImageSelection (const QRect &rect, const kpImageSelectionTransparency &transparency) : kpAbstractImageSelection (rect, transparency), d (new kpRectangularImageSelectionPrivate ()) { } kpRectangularImageSelection::kpRectangularImageSelection (const kpRectangularImageSelection &rhs) : kpAbstractImageSelection (), d (new kpRectangularImageSelectionPrivate ()) { *this = rhs; } kpRectangularImageSelection &kpRectangularImageSelection::operator= ( const kpRectangularImageSelection &rhs) { kpAbstractImageSelection::operator= (rhs); return *this; } kpRectangularImageSelection *kpRectangularImageSelection::clone () const { kpRectangularImageSelection *sel = new kpRectangularImageSelection (); *sel = *this; return sel; } kpRectangularImageSelection::~kpRectangularImageSelection () { delete d; } // public virtual [kpAbstractSelection] int kpRectangularImageSelection::serialID () const { return SerialID; } // public virtual [kpAbstractSelection] bool kpRectangularImageSelection::isRectangular () const { return true; } // public virtual [kpAbstractSelection] QPolygon kpRectangularImageSelection::calculatePoints () const { return kpAbstractImageSelection::CalculatePointsForRectangle (boundingRect ()); } // public virtual [base kpAbstractImageSelection] QBitmap kpRectangularImageSelection::shapeBitmap (bool nullForRectangular) const { Q_ASSERT (boundingRect ().isValid ()); if (nullForRectangular) { return {}; } QBitmap maskBitmap (width (), height ()); maskBitmap.fill (Qt::color1/*opaque*/); return maskBitmap; } // public virtual [kpAbstractImageSelection] QRegion kpRectangularImageSelection::shapeRegion () const { return QRegion (boundingRect (), QRegion::Rectangle); } // public virtual [kpAbstractSelection] bool kpRectangularImageSelection::contains (const QPoint &point) const { return boundingRect ().contains (point); } // public virtual [kpAbstractSelection] void kpRectangularImageSelection::paintBorder (QImage *destPixmap, const QRect &docRect, bool selectionFinished) const { paintRectangularBorder (destPixmap, docRect, selectionFinished); } diff --git a/layers/selections/kpAbstractSelection.cpp b/layers/selections/kpAbstractSelection.cpp index 5cf44f2e..ae323b86 100644 --- a/layers/selections/kpAbstractSelection.cpp +++ b/layers/selections/kpAbstractSelection.cpp @@ -1,315 +1,295 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "layers/selections/kpAbstractSelection.h" #include #include #include "kpLogCategories.h" struct kpAbstractSelectionPrivate { QRect rect; }; // protected kpAbstractSelection::kpAbstractSelection () : QObject (), d (new kpAbstractSelectionPrivate ()) { d->rect = QRect (); } // protected kpAbstractSelection::kpAbstractSelection (const QRect &rect) : QObject (), d (new kpAbstractSelectionPrivate ()) { d->rect = rect; } // protected kpAbstractSelection &kpAbstractSelection::operator= (const kpAbstractSelection &rhs) { if (this == &rhs) { return *this; } d->rect = rhs.d->rect; return *this; } // protected kpAbstractSelection::~kpAbstractSelection () { delete d; } // public virtual bool kpAbstractSelection::readFromStream (QDataStream &stream) { stream >> d->rect; return true; } // public virtual void kpAbstractSelection::writeToStream (QDataStream &stream) const { stream << d->rect; } // friend QDataStream &operator<< (QDataStream &stream, const kpAbstractSelection &selection) { -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "kpAbstractSelection::operator<<(sel: rect=" << selection.boundingRect (); -#endif stream << selection.serialID (); selection.writeToStream (stream); return stream; } // public virtual kpCommandSize::SizeType kpAbstractSelection::size () const { return 0/*constant size*/; } // public QSize kpAbstractSelection::minimumSize () const { return {minimumWidth (), minimumHeight ()}; } // public int kpAbstractSelection::x () const { return d->rect.x (); } // public int kpAbstractSelection::y () const { return d->rect.y (); } // public QPoint kpAbstractSelection::topLeft () const { return d->rect.topLeft (); } // public int kpAbstractSelection::width () const { return boundingRect ().width (); } // public int kpAbstractSelection::height () const { return boundingRect ().height (); } // public QRect kpAbstractSelection::boundingRect () const { return d->rect; } // public static QPolygon kpAbstractSelection::CalculatePointsForRectangle (const QRect &rect) { QPolygon points; // OPT: not space optimal - current code adds duplicate corner points. // top for (int x = 0; x < rect.width (); x++) { points.append (QPoint (rect.x () + x, rect.top ())); } // right for (int y = 0; y < rect.height (); y++) { points.append (QPoint (rect.right (), rect.y () + y)); } // bottom for (int x = rect.width () - 1; x >= 0; x--) { points.append (QPoint (rect.x () + x, rect.bottom ())); } // left for (int y = rect.height () - 1; y >= 0; y--) { points.append (QPoint (rect.left (), rect.y () + y)); } return points; } // public bool kpAbstractSelection::contains (int x, int y) const { return contains (QPoint (x, y)); } // public virtual void kpAbstractSelection::moveBy (int dx, int dy) { -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "kpAbstractSelection::moveBy(" << dx << "," << dy << ")"; -#endif if (dx == 0 && dy == 0) { return; } QRect oldRect = boundingRect (); -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\toldRect=" << oldRect; -#endif d->rect.translate (dx, dy); -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\tnewRect=" << d->rect; -#endif emit changed (oldRect); emit changed (boundingRect ()); } // public void kpAbstractSelection::moveTo (int dx, int dy) { moveTo (QPoint (dx, dy)); } // public void kpAbstractSelection::moveTo (const QPoint &topLeftPoint) { -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "kpAbstractSelection::moveTo(" << topLeftPoint << ")"; -#endif QRect oldBoundingRect = boundingRect (); -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\toldBoundingRect=" << oldBoundingRect; -#endif if (topLeftPoint == oldBoundingRect.topLeft ()) { return; } QPoint delta (topLeftPoint - oldBoundingRect.topLeft ()); moveBy (delta.x (), delta.y ()); } //--------------------------------------------------------------------- // protected void kpAbstractSelection::paintRectangularBorder (QImage *destPixmap, const QRect &docRect, bool selectionFinished) const { (void) selectionFinished; -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "kpAbstractSelection::paintRectangularBorder() boundingRect=" << boundingRect (); -#endif - -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\tselection border = rectangle"; qCDebug(kpLogLayers) << "\t\tx=" << boundingRect ().x () - docRect.x () << " y=" << boundingRect ().y () - docRect.y () << " w=" << boundingRect ().width () << " h=" << boundingRect ().height (); -#endif + kpPixmapFX::drawStippleRect(destPixmap, boundingRect ().x () - docRect.x (), boundingRect ().y () - docRect.y (), boundingRect ().width (), boundingRect ().height (), kpColor::Blue, kpColor::Yellow); } //--------------------------------------------------------------------- // protected void kpAbstractSelection::paintPolygonalBorder (const QPolygon &points, QImage *destPixmap, const QRect &docRect, bool selectionFinished) const { -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "kpAbstractSelection::paintPolygonalBorder() boundingRect=" << boundingRect (); -#endif QPolygon pointsTranslated = points; pointsTranslated.translate (-docRect.x (), -docRect.y ()); if ( !selectionFinished ) { kpPixmapFX::drawPolyline(destPixmap, pointsTranslated, kpColor::Blue, 1/*pen width*/, kpColor::Yellow); } else { kpPixmapFX::drawPolygon(destPixmap, pointsTranslated, kpColor::Blue, 1/*pen width*/, kpColor::Invalid/*no background*/, true/*is final*/, kpColor::Yellow); kpPixmapFX::drawStippleRect(destPixmap, boundingRect ().x () - docRect.x (), boundingRect ().y () - docRect.y (), boundingRect ().width (), boundingRect ().height (), kpColor::LightGray, kpColor::DarkGray); } } diff --git a/layers/selections/kpSelectionDrag.cpp b/layers/selections/kpSelectionDrag.cpp index ca2a8290..e74a79b8 100644 --- a/layers/selections/kpSelectionDrag.cpp +++ b/layers/selections/kpSelectionDrag.cpp @@ -1,165 +1,161 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2011 Martin Koller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION_DRAG 1 - #include "kpSelectionDrag.h" #include #include #include #include "kpLogCategories.h" #include "kpSelectionFactory.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "layers/selections/image/kpRectangularImageSelection.h" //--------------------------------------------------------------------- // public static const char * const kpSelectionDrag::SelectionMimeType = "application/x-kolourpaint-selection-400"; //--------------------------------------------------------------------- kpSelectionDrag::kpSelectionDrag (const kpAbstractImageSelection &sel) { -#if DEBUG_KP_SELECTION_DRAG && 1 qCDebug(kpLogLayers) << "kpSelectionDrag() w=" << sel.width () << " h=" << sel.height (); -#endif Q_ASSERT (sel.hasContent ()); // Store as selection. QByteArray ba; { QDataStream stream (&ba, QIODevice::WriteOnly); stream << sel; } setData (kpSelectionDrag::SelectionMimeType, ba); // Store as image (so that QMimeData::hasImage()) works). // OPT: an awful waste of memory storing image in both selection and QImage const QImage image = sel.baseImage (); -#if DEBUG_KP_SELECTION_DRAG && 1 + qCDebug(kpLogLayers) << "\timage: w=" << image.width () << " h=" << image.height (); -#endif + if (image.isNull ()) { // TODO: proper error handling. qCCritical(kpLogLayers) << "kpSelectionDrag::setSelection() could not convert to image"; } else { setImageData (image); } } //--------------------------------------------------------------------- // public static bool kpSelectionDrag::canDecode(const QMimeData *mimeData) { Q_ASSERT(mimeData); #if DEBUG_KP_SELECTION_DRAG qCDebug(kpLogLayers) << "kpSelectionDrag::canDecode()" << "hasSel=" << mimeData->hasFormat(kpSelectionDrag::SelectionMimeType) << "hasImage=" << mimeData->hasImage(); #endif // mimeData->hasImage() would not check if the data is a valid image return mimeData->hasFormat(kpSelectionDrag::SelectionMimeType) || !qvariant_cast(mimeData->imageData()).isNull(); } //--------------------------------------------------------------------- // public static kpAbstractImageSelection *kpSelectionDrag::decode(const QMimeData *mimeData) { #if DEBUG_KP_SELECTION_DRAG qCDebug(kpLogLayers) << "kpSelectionDrag::decode(kpAbstractSelection)"; #endif Q_ASSERT (mimeData); if (mimeData->hasFormat (kpSelectionDrag::SelectionMimeType)) { #if DEBUG_KP_SELECTION_DRAG qCDebug(kpLogLayers) << "\tmimeSource hasFormat selection - just return it in QByteArray"; #endif QByteArray data = mimeData->data (kpSelectionDrag::SelectionMimeType); QDataStream stream (&data, QIODevice::ReadOnly); return kpSelectionFactory::FromStream (stream); } #if DEBUG_KP_SELECTION_DRAG qCDebug(kpLogLayers) << "\tmimeSource doesn't provide selection - try image"; #endif QImage image = qvariant_cast (mimeData->imageData ()); if (!image.isNull ()) { #if DEBUG_KP_SELECTION_DRAG qCDebug(kpLogLayers) << "\tok w=" << image.width () << " h=" << image.height (); #endif return new kpRectangularImageSelection ( QRect (0, 0, image.width (), image.height ()), image); } if ( mimeData->hasUrls() ) // no image, check for path to local image file { QList urls = mimeData->urls(); if ( urls.count() && urls[0].isLocalFile() ) { image.load(urls[0].toLocalFile()); if ( !image.isNull() ) { return new kpRectangularImageSelection( QRect(0, 0, image.width(), image.height()), image); } } } #if DEBUG_KP_SELECTION_DRAG qCDebug(kpLogLayers) << "kpSelectionDrag::decode(kpAbstractSelection) mimeSource had no sel " "and could not decode to image"; #endif return nullptr; } //--------------------------------------------------------------------- diff --git a/layers/selections/kpSelectionFactory.cpp b/layers/selections/kpSelectionFactory.cpp index 21372b7b..086f33c6 100644 --- a/layers/selections/kpSelectionFactory.cpp +++ b/layers/selections/kpSelectionFactory.cpp @@ -1,94 +1,89 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 #include "kpSelectionFactory.h" #include "kpLogCategories.h" #include #include "layers/selections/image/kpRectangularImageSelection.h" #include "layers/selections/image/kpEllipticalImageSelection.h" #include "layers/selections/image/kpFreeFormImageSelection.h" //--------------------------------------------------------------------- // public static // TODO: KolourPaint has not been tested against invalid or malicious // clipboard data [Bug #28]. kpAbstractImageSelection *kpSelectionFactory::FromStream (QDataStream &stream) { -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "kpSelectionFactory::FromStream()"; -#endif int serialID; stream >> serialID; -#if DEBUG_KP_SELECTION && 1 qCDebug(kpLogLayers) << "\tserialID=" << serialID; -#endif // Only image selections are marshalled. // // Text selections are only ever seen in the clipboard as ordinary text, // not selections, since copying text formatting over the clipboard doesn't // seem compelling. kpAbstractImageSelection *imageSel = nullptr; switch (serialID) { case kpRectangularImageSelection::SerialID: imageSel = new kpRectangularImageSelection (); break; case kpEllipticalImageSelection::SerialID: imageSel = new kpEllipticalImageSelection (); break; case kpFreeFormImageSelection::SerialID: imageSel = new kpFreeFormImageSelection (); break; } // Unknown selection type? if (imageSel == nullptr) { return nullptr; } if (!imageSel->readFromStream (stream)) { delete imageSel; return nullptr; } return imageSel; } //--------------------------------------------------------------------- diff --git a/layers/selections/text/kpTextSelection.cpp b/layers/selections/text/kpTextSelection.cpp index 0263f2a1..2070d3cb 100644 --- a/layers/selections/text/kpTextSelection.cpp +++ b/layers/selections/text/kpTextSelection.cpp @@ -1,347 +1,345 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2010 Tasuku Suzuki All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "kpTextSelection.h" #include "kpTextSelectionPrivate.h" #include "kpDefs.h" #include "kpTextStyle.h" #include "kpLogCategories.h" #include #include // public kpTextSelection::kpTextSelection (const QRect &rect, const kpTextStyle &textStyle) : kpAbstractSelection (rect), d (new kpTextSelectionPrivate ()) { d->textStyle = textStyle; } // public kpTextSelection::kpTextSelection (const QRect &rect, const QList &textLines, const kpTextStyle &textStyle) : kpAbstractSelection (rect), d (new kpTextSelectionPrivate ()) { d->textLines = textLines; d->textStyle = textStyle; } // public kpTextSelection::kpTextSelection (const kpTextSelection &rhs) : kpAbstractSelection (), d (new kpTextSelectionPrivate ()) { *this = rhs; } // public kpTextSelection &kpTextSelection::operator= (const kpTextSelection &rhs) { kpAbstractSelection::operator= (rhs); d->textLines = rhs.d->textLines; d->textStyle = rhs.d->textStyle; d->preeditText = rhs.d->preeditText; return *this; } // public virtual [base kpAbstractSelection] kpTextSelection *kpTextSelection::clone () const { kpTextSelection *sel = new kpTextSelection (); *sel = *this; return sel; } // public kpTextSelection *kpTextSelection::resized (int newWidth, int newHeight) const { return new kpTextSelection (QRect (x (), y (), newWidth, newHeight), d->textLines, d->textStyle); } // public kpTextSelection::~kpTextSelection () { delete d; } // public virtual [kpAbstractSelection] int kpTextSelection::serialID () const { Q_ASSERT (!"Marshalling not supported"); return -1; } // public virtual [base kpAbstractSelection] bool kpTextSelection::readFromStream (QDataStream &stream) { (void) stream; Q_ASSERT (!"Marshalling not supported"); return false; } // public virtual [base kpAbstractSelection] void kpTextSelection::writeToStream (QDataStream &stream) const { (void) stream; Q_ASSERT (!"Marshalling not supported"); } // public virtual [kpAbstractSelection] QString kpTextSelection::name () const { return i18n ("Text"); } // public virtual [base kpAbstractSelection] kpCommandSize::SizeType kpTextSelection::size () const { return kpAbstractSelection::size () + kpCommandSize::StringSize (text ()); } // public virtual [kpAbstractSelection] bool kpTextSelection::isRectangular () const { return true; } // public static int kpTextSelection::MinimumWidthForTextStyle (const kpTextStyle &) { return (kpTextSelection::TextBorderSize () * 2 + 5); } // public static int kpTextSelection::MinimumHeightForTextStyle (const kpTextStyle &) { return (kpTextSelection::TextBorderSize () * 2 + 5); } // public static QSize kpTextSelection::MinimumSizeForTextStyle (const kpTextStyle &textStyle) { return {kpTextSelection::MinimumWidthForTextStyle (textStyle), kpTextSelection::MinimumHeightForTextStyle (textStyle)}; } // public virtual [kpAbstractSelection] int kpTextSelection::minimumWidth () const { return kpTextSelection::MinimumWidthForTextStyle (textStyle ()); } // public virtual [kpAbstractSelection] int kpTextSelection::minimumHeight () const { return kpTextSelection::MinimumHeightForTextStyle (textStyle ()); } // public static int kpTextSelection::PreferredMinimumWidthForTextStyle (const kpTextStyle &textStyle) { const int about15CharsWidth = textStyle.fontMetrics ().width ( QStringLiteral ("1234567890abcde")); const int preferredMinWidth = qMax (150, kpTextSelection::TextBorderSize () * 2 + about15CharsWidth); return qMax (kpTextSelection::MinimumWidthForTextStyle (textStyle), qMin (250, preferredMinWidth)); } // public static int kpTextSelection::PreferredMinimumHeightForTextStyle (const kpTextStyle &textStyle) { const int preferredMinHeight = kpTextSelection::TextBorderSize () * 2 + textStyle.fontMetrics ().height (); return qMax (kpTextSelection::MinimumHeightForTextStyle (textStyle), qMin (150, preferredMinHeight)); } // public static QSize kpTextSelection::PreferredMinimumSizeForTextStyle (const kpTextStyle &textStyle) { return {kpTextSelection::PreferredMinimumWidthForTextStyle (textStyle), kpTextSelection::PreferredMinimumHeightForTextStyle (textStyle)}; } // public static int kpTextSelection::TextBorderSize () { return 1; } // public QRect kpTextSelection::textAreaRect () const { return {x () + kpTextSelection::TextBorderSize (), y () + kpTextSelection::TextBorderSize (), width () - kpTextSelection::TextBorderSize () * 2, height () - kpTextSelection::TextBorderSize () * 2}; } // public virtual [kpAbstractSelection] QPolygon kpTextSelection::calculatePoints () const { return kpAbstractSelection::CalculatePointsForRectangle (boundingRect ()); } // public virtual [kpAbstractSelection] bool kpTextSelection::contains (const QPoint &point) const { return boundingRect ().contains (point); } // public bool kpTextSelection::pointIsInTextBorderArea (const QPoint &point) const { return (boundingRect ().contains (point) && !pointIsInTextArea (point)); } // public bool kpTextSelection::pointIsInTextArea (const QPoint &point) const { return textAreaRect ().contains (point); } // public virtual [kpAbstractSelection] bool kpTextSelection::hasContent () const { return !d->textLines.isEmpty (); } // public virtual [kpAbstractSelection] void kpTextSelection::deleteContent () { if (!hasContent ()) { return; } setTextLines (QList ()); } // public QList kpTextSelection::textLines () const { return d->textLines; } // public void kpTextSelection::setTextLines (const QList &textLines_) { d->textLines = textLines_; emit changed (boundingRect ()); } // public static QString kpTextSelection::TextForTextLines (const QList &textLines) { if (textLines.isEmpty ()) { return {}; } QString bigString = textLines [0]; for (QList ::const_iterator it = textLines.begin () + 1; it != textLines.end (); ++it) { bigString += QLatin1String ("\n"); bigString += (*it); } return bigString; } // public QString kpTextSelection::text () const { return kpTextSelection::TextForTextLines (d->textLines); } // public kpTextStyle kpTextSelection::textStyle () const { return d->textStyle; } // public void kpTextSelection::setTextStyle (const kpTextStyle &textStyle) { d->textStyle = textStyle; emit changed (boundingRect ()); } kpPreeditText kpTextSelection::preeditText () const { return d->preeditText; } void kpTextSelection::setPreeditText (const kpPreeditText &preeditText) { d->preeditText = preeditText; emit changed (boundingRect ()); } diff --git a/layers/selections/text/kpTextSelection_Cursor.cpp b/layers/selections/text/kpTextSelection_Cursor.cpp index a90078c2..3ef7ecb6 100644 --- a/layers/selections/text/kpTextSelection_Cursor.cpp +++ b/layers/selections/text/kpTextSelection_Cursor.cpp @@ -1,129 +1,125 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2010 Tasuku Suzuki All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "kpTextSelection.h" #include "kpTextSelectionPrivate.h" #include "kpDefs.h" #include "kpTextStyle.h" #include "kpPreeditText.h" #include "kpLogCategories.h" #include #include // public int kpTextSelection::closestTextRowForPoint (const QPoint &point) const { if (!pointIsInTextArea (point)) { return -1; } const QFontMetrics fontMetrics (d->textStyle.fontMetrics ()); int row = (point.y () - textAreaRect ().y ()) / fontMetrics.lineSpacing (); if (row >= static_cast (d->textLines.size ())) { row = d->textLines.size () - 1; } return row; } // public int kpTextSelection::closestTextColForPoint (const QPoint &point) const { int row = closestTextRowForPoint (point); if (row < 0 || row >= static_cast (d->textLines.size ())) { return -1; } const int localX = point.x () - textAreaRect ().x (); const QFontMetrics fontMetrics (d->textStyle.fontMetrics ()); // (should be 0 but call just in case) int charLocalLeft = fontMetrics.width (d->textLines [row], 0); // OPT: binary search or guess location then move for (int col = 0; col < static_cast (d->textLines [row].length ()); col++) { // OPT: fontMetrics::charWidth() might be faster const int nextCharLocalLeft = fontMetrics.width (d->textLines [row], col + 1); if (localX <= (charLocalLeft + nextCharLocalLeft) / 2) { return col; } charLocalLeft = nextCharLocalLeft; } return d->textLines [row].length ()/*past end of line*/; } //--------------------------------------------------------------------- // public QPoint kpTextSelection::pointForTextRowCol (int row, int col) const { kpPreeditText preeditText = d->preeditText; if ((row < 0 || col < 0) || (preeditText.isEmpty () && (row >= static_cast (d->textLines.size ()) || col > static_cast (d->textLines [row].length ())))) { -#if DEBUG_KP_SELECTION && 1 - qCDebug(kpLogLayers) << "kpTextSelection::pointForTextRowCol(" - << row << "," - << col << ") out of range" - << " textLines='" - << text () - << "'"; -#endif + qCDebug(kpLogLayers) << "kpTextSelection::pointForTextRowCol(" + << row << "," + << col << ") out of range" + << " textLines='" + << text () + << "'"; return KP_INVALID_POINT; } const QFontMetrics fontMetrics (d->textStyle.fontMetrics ()); QString line = (d->textLines.count () > row) ? d->textLines[row] : QString (); if (row == preeditText.position ().y ()) { line.insert (preeditText.position ().x (), preeditText.preeditString ()); } const int x = fontMetrics.width (line.left (col)); const int y = row * fontMetrics.height () + (row >= 1 ? row * fontMetrics.leading () : 0); return textAreaRect ().topLeft () + QPoint (x, y); } //--------------------------------------------------------------------- diff --git a/layers/selections/text/kpTextSelection_Paint.cpp b/layers/selections/text/kpTextSelection_Paint.cpp index 6c962eef..06174512 100644 --- a/layers/selections/text/kpTextSelection_Paint.cpp +++ b/layers/selections/text/kpTextSelection_Paint.cpp @@ -1,274 +1,268 @@ // REFACTOR: Move into kpPainter /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2010 Tasuku Suzuki All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_SELECTION 1 - #include "kpTextSelection.h" #include "kpTextSelectionPrivate.h" #include "kpTextStyle.h" #include "kpPreeditText.h" #include "pixmapfx/kpPixmapFX.h" #include "kpLogCategories.h" #include #include #include #include #include //--------------------------------------------------------------------- void kpTextSelection::drawPreeditString(QPainter &painter, int &x, int y, const kpPreeditText &preeditText) const { int i = 0; const QString& preeditString = preeditText.preeditString (); QString str; for (const auto &attr : preeditText.textFormatList ()) { int start = attr.start; int length = attr.length; QTextCharFormat format = qvariant_cast (attr.value).toCharFormat (); if (i > start) { length = length - i + start; start = i; } if (length <= 0) { continue; } if (i < start) { str = preeditString.mid (i, start - i); painter.drawText (x, y, str); x += painter.fontMetrics ().width (str); } painter.save(); str = preeditString.mid (start, length); int width = painter.fontMetrics().width (str); if (format.background ().color () != Qt::black) { painter.save (); painter.setPen (format.background ().color ()); painter.setBrush (format.background()); painter.drawRect (x, y - painter.fontMetrics ().ascent (), width, painter.fontMetrics ().height ()); painter.restore (); } if (format.foreground ().color () != Qt::black) { painter.setBrush (format.foreground ()); painter.setPen (format.foreground ().color ()); } if (format.underlineStyle ()) { painter.drawLine (x, y + painter.fontMetrics ().descent (), x + width, y + painter.fontMetrics ().descent ()); } painter.drawText (x, y, str); x += width; painter.restore (); i = start + length; } if (i < preeditString.length ()) { str = preeditString.mid (i); painter.drawText (x, y, str); x += painter.fontMetrics ().width (str); } } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] void kpTextSelection::paint(QImage *destPixmap, const QRect &docRect) const { -#if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "kpTextSelection::paint() textStyle: fcol=" << (int *) d->textStyle.foregroundColor ().toQRgb () << " bcol=" << (int *) d->textStyle.backgroundColor ().toQRgb (); -#endif // Drawing text is slow so if the text box will be rendered completely // outside of , don't bother rendering it at all. const QRect modifyingRect = docRect.intersected (boundingRect ()); if (modifyingRect.isEmpty ()) { return; } // Is the text box completely invisible? if (textStyle ().foregroundColor ().isTransparent () && textStyle ().backgroundColor ().isTransparent ()) { return; } kpImage floatImage(modifyingRect.size(), QImage::Format_ARGB32_Premultiplied); floatImage.fill(0); QRect theWholeAreaRect, theTextAreaRect; theWholeAreaRect = boundingRect ().translated (-modifyingRect.topLeft ()); theTextAreaRect = textAreaRect ().translated (-modifyingRect.topLeft ()); QList theTextLines = textLines(); kpTextStyle theTextStyle = textStyle(); const QFontMetrics fontMetrics (theTextStyle.font ()); -#if DEBUG_KP_SELECTION qCDebug(kpLogLayers) << "kpTextSelection_Paint.cpp:DrawTextHelper"; qCDebug(kpLogLayers) << "\theight=" << fontMetrics.height () << " leading=" << fontMetrics.leading () << " ascent=" << fontMetrics.ascent () << " descent=" << fontMetrics.descent () << " lineSpacing=" << fontMetrics.lineSpacing (); -#endif QPainter painter(&floatImage); // Fill in the background using the transparent/opaque tool setting if ( theTextStyle.isBackgroundTransparent() ) { painter.fillRect(theWholeAreaRect, Qt::transparent); } else { painter.fillRect(theWholeAreaRect, theTextStyle.backgroundColor().toQColor()); } painter.setClipRect(theWholeAreaRect); painter.setPen(theTextStyle.foregroundColor().toQColor()); painter.setFont(theTextStyle.font()); if ( theTextStyle.foregroundColor().toQColor().alpha() < 255 ) { // if the foreground color has an alpha channel, we want to // see through the background, so we first need to punch holes // into the background where the text is painter.setCompositionMode(QPainter::CompositionMode_Clear); int baseLine = theTextAreaRect.y () + fontMetrics.ascent (); for (const auto &str : theTextLines) { painter.drawText (theTextAreaRect.x (), baseLine, str); baseLine += fontMetrics.lineSpacing (); // if the next textline would already be below the visible text area, stop drawing if ( (baseLine - fontMetrics.ascent()) > (theTextAreaRect.y() + theTextAreaRect.height()) ) { break; } } // the next text drawing will now blend the text foreground color with // what is really below the text background painter.setCompositionMode(QPainter::CompositionMode_SourceOver); } // Draw a line at a time instead of using QPainter::drawText(QRect,...). // Else, the line heights become >QFontMetrics::height() if you type Chinese // characters (!) and then the cursor gets out of sync. int baseLine = theTextAreaRect.y () + fontMetrics.ascent (); kpPreeditText thePreeditText = preeditText(); if ( theTextLines.isEmpty() ) { if ( ! thePreeditText.isEmpty() ) { int x = theTextAreaRect.x(); drawPreeditString(painter, x, baseLine, thePreeditText); } } else { int i = 0; int row = thePreeditText.position().y(); int col = thePreeditText.position().x(); for (const auto &str : theTextLines) { if (row == i && !thePreeditText.isEmpty()) { QString left = str.left(col); QString right = str.mid(col); int x = theTextAreaRect.x(); painter.drawText(x, baseLine, left); x += fontMetrics.width(left); drawPreeditString(painter, x, baseLine, thePreeditText); painter.drawText(x, baseLine, right); } else { painter.drawText(theTextAreaRect.x (), baseLine, str); } baseLine += fontMetrics.lineSpacing(); i++; // if the next textline would already be below the visible text area, stop drawing if ( (baseLine - fontMetrics.ascent()) > (theTextAreaRect.y() + theTextAreaRect.height()) ) { break; } } } // ... convert that into "painting" transparent pixels on top of // the document. kpPixmapFX::paintPixmapAt (destPixmap, modifyingRect.topLeft () - docRect.topLeft (), floatImage); } //--------------------------------------------------------------------- // public virtual [kpAbstractSelection] void kpTextSelection::paintBorder (QImage *destPixmap, const QRect &docRect, bool selectionFinished) const { paintRectangularBorder (destPixmap, docRect, selectionFinished); } //--------------------------------------------------------------------- // public kpImage kpTextSelection::approximateImage () const { kpImage retImage (width (), height (), QImage::Format_ARGB32_Premultiplied); retImage.fill(0); paint (&retImage, boundingRect ()); return retImage; } //--------------------------------------------------------------------- diff --git a/lgpl/generic/kpColorCollection.cpp b/lgpl/generic/kpColorCollection.cpp index 8ac75ace..392fbb71 100644 --- a/lgpl/generic/kpColorCollection.cpp +++ b/lgpl/generic/kpColorCollection.cpp @@ -1,520 +1,494 @@ // REFACT0R: Remote open/save file logic is duplicated in kpDocument. // HITODO: Test when remote file support in KDE 4 stabilizes /* This file is part of the KDE libraries Copyright (C) 1999 Waldo Bastian (bastian@kde.org) Copyright (C) 2007 Clarence Dang (dang@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //----------------------------------------------------------------------------- // KDE color collection -#define DEBUG_KP_COLOR_COLLECTION 1 - #include "kpColorCollection.h" #include "kpUrlFormatter.h" #include #include #include #include #include #include "kpLogCategories.h" #include #include #include #include #include #include #include #include struct ColorNode { ColorNode(const QColor &c, const QString &n) : color(c), name(n) {} QColor color; QString name; }; //--------------------------------------------------------------------- Q_LOGGING_CATEGORY(kpLogColorCollection, "kp.colorCollection", QtWarningMsg) //BEGIN kpColorCollectionPrivate class kpColorCollectionPrivate { public: kpColorCollectionPrivate(); kpColorCollectionPrivate(const kpColorCollectionPrivate&); QList colorList; QString name; QString desc; kpColorCollection::Editable editable; }; kpColorCollectionPrivate::kpColorCollectionPrivate() : editable(kpColorCollection::Yes) { } kpColorCollectionPrivate::kpColorCollectionPrivate(const kpColorCollectionPrivate& p) : colorList(p.colorList), name(p.name), desc(p.desc), editable(p.editable) { } //END kpColorCollectionPrivate //--------------------------------------------------------------------- QStringList kpColorCollection::installedCollections() { QStringList paletteList; QStringList paths = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("colors"), QStandardPaths::LocateDirectory); for (const auto &path : paths) { paletteList.append(QDir(path).entryList(QStringList(), QDir::Files)); } return paletteList; } kpColorCollection::kpColorCollection() { d = new kpColorCollectionPrivate(); } kpColorCollection::kpColorCollection(const kpColorCollection &p) { d = new kpColorCollectionPrivate(*p.d); } kpColorCollection::~kpColorCollection() { // Need auto-save? delete d; } static void CouldNotOpenDialog (const QUrl &url, QWidget *parent) { KMessageBox::sorry (parent, i18n ("Could not open color palette \"%1\".", kpUrlFormatter::PrettyFilename (url))); } // TODO: Set d->editable? bool kpColorCollection::open(const QUrl &url, QWidget *parent) { if (url.isEmpty()) { return false; } KIO::StoredTransferJob *job = KIO::storedGet (url); KJobWidgets::setWindow (job, parent); if (!job->exec ()) { -#if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "\tcould not download"; -#endif ::CouldNotOpenDialog (url, parent); return false; } const QByteArray &data = job->data(); QTextStream stream(data); // Read first line // Expected "GIMP Palette" QString line = stream.readLine(); if (line.indexOf(QLatin1String(" Palette")) == -1) { KMessageBox::sorry (parent, i18n ("Could not open color palette \"%1\" - unsupported format.\n" "The file may be corrupt.", kpUrlFormatter::PrettyFilename (url))); return false; } QList newColorList; QString newDesc; while( !stream.atEnd() ) { line = stream.readLine(); if (line[0] == '#') { // This is a comment line line = line.mid(1); // Strip '#' line = line.trimmed(); // Strip remaining white space.. if (!line.isEmpty()) { newDesc += line+'\n'; // Add comment to description } } else { // This is a color line, hopefully line = line.trimmed(); if (line.isEmpty()) continue; int r, g, b; int pos = 0; if (sscanf(line.toLatin1(), "%d %d %d%n", &r, &g, &b, &pos) >= 3) { r = qBound(0, r, 255); g = qBound(0, g, 255); b = qBound(0, b, 255); QString name = line.mid(pos).trimmed(); newColorList.append(ColorNode(QColor(r, g, b), name)); } } } d->colorList = newColorList; d->name.clear (); d->desc = newDesc; return true; } static void CouldNotOpenKDEDialog (const QString &name, QWidget *parent) { KMessageBox::sorry (parent, i18n ("Could not open KDE color palette \"%1\".", name)); } bool kpColorCollection::openKDE(const QString &name, QWidget *parent) { -#if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "name=" << name; -#endif if (name.isEmpty()) { - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "name.isEmpty"; - #endif ::CouldNotOpenKDEDialog (name, parent); return false; } QString filename = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, "colors/" + name); if (filename.isEmpty()) { - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "could not find file"; - #endif ::CouldNotOpenKDEDialog (name, parent); return false; } // (this will pop up an error dialog on failure) if (!open (QUrl::fromLocalFile (filename), parent)) { - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "could not open"; - #endif return false; } d->name = name; -#if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "opened"; -#endif return true; } static void CouldNotSaveDialog (const QUrl &url, QWidget *parent) { // TODO: use file.errorString() KMessageBox::error (parent, i18n ("Could not save color palette as \"%1\".", kpUrlFormatter::PrettyFilename (url))); } static void SaveToFile (kpColorCollectionPrivate *d, QIODevice *device) { // HITODO: QTextStream can fail but does not report errors. // Bug in KColorCollection too. QTextStream str (device); QString description = d->desc.trimmed(); description = '#'+description.split( '\n', QString::KeepEmptyParts).join(QLatin1String("\n#")); str << "KDE RGB Palette\n"; str << description << "\n"; for (const auto &node : d->colorList) { // Added for KolourPaint. if(!node.color.isValid ()) continue; int r,g,b; node.color.getRgb(&r, &g, &b); str << r << " " << g << " " << b << " " << node.name << "\n"; } str.flush(); } bool kpColorCollection::saveAs(const QUrl &url, QWidget *parent) const { if (url.isLocalFile ()) { const QString filename = url.toLocalFile (); // sync: All failure exit paths _must_ call QSaveFile::cancelWriting() or // else, the QSaveFile destructor will overwrite the file, // , despite the failure. QSaveFile atomicFileWriter (filename); { if (!atomicFileWriter.open (QIODevice::WriteOnly)) { // We probably don't need this as has not been // opened. atomicFileWriter.cancelWriting (); - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "\treturning false because could not open QSaveFile" << " error=" << atomicFileWriter.error (); - #endif ::CouldNotSaveDialog (url, parent); return false; } // Write to local temporary file. ::SaveToFile (d, &atomicFileWriter); // Atomically overwrite local file with the temporary file // we saved to. if (!atomicFileWriter.commit ()) { atomicFileWriter.cancelWriting (); - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "\tcould not close QSaveFile"; - #endif ::CouldNotSaveDialog (url, parent); return false; } } // sync QSaveFile.cancelWriting() } // Remote file? else { // Create temporary file that is deleted when the variable goes // out of scope. QTemporaryFile tempFile; if (!tempFile.open ()) { - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "\treturning false because could not open tempFile"; - #endif ::CouldNotSaveDialog (url, parent); return false; } // Write to local temporary file. ::SaveToFile (d, &tempFile); // Collect name of temporary file now, as QTemporaryFile::fileName() // stops working after close() is called. const QString tempFileName = tempFile.fileName (); - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "\ttempFileName='" << tempFileName << "'"; - #endif Q_ASSERT (!tempFileName.isEmpty ()); tempFile.close (); if (tempFile.error () != QFile::NoError) { - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "\treturning false because could not close"; - #endif ::CouldNotSaveDialog (url, parent); return false; } // Copy local temporary file to overwrite remote. KIO::FileCopyJob *job = KIO::file_copy (QUrl::fromLocalFile (tempFileName), url, -1, KIO::Overwrite); KJobWidgets::setWindow (job, parent); if (!job->exec ()) { - #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogColorCollection) << "\treturning false because could not upload"; - #endif ::CouldNotSaveDialog (url, parent); return false; } } d->name.clear (); return true; } bool kpColorCollection::saveKDE(QWidget *parent) const { const QString name = d->name; QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "colors/" + name; const bool ret = saveAs (QUrl::fromLocalFile (filename), parent); // (d->name is wiped by saveAs()). d->name = name; return ret; } QString kpColorCollection::description() const { return d->desc; } void kpColorCollection::setDescription(const QString &desc) { d->desc = desc; } QString kpColorCollection::name() const { return d->name; } void kpColorCollection::setName(const QString &name) { d->name = name; } kpColorCollection::Editable kpColorCollection::editable() const { return d->editable; } void kpColorCollection::setEditable(Editable editable) { d->editable = editable; } int kpColorCollection::count() const { return (int) d->colorList.count(); } void kpColorCollection::resize(int newCount) { if (newCount == count()) return; else if (newCount < count()) { d->colorList.erase(d->colorList.begin() + newCount, d->colorList.end()); } else if (newCount > count()) { while(newCount > count()) { const int ret = addColor(QColor(), QString()/*color name*/); Q_ASSERT(ret == count() - 1); } } } kpColorCollection& kpColorCollection::operator=( const kpColorCollection &p) { if (&p == this) return *this; d->colorList = p.d->colorList; d->name = p.d->name; d->desc = p.d->desc; d->editable = p.d->editable; return *this; } QColor kpColorCollection::color(int index) const { if ((index < 0) || (index >= count())) return {}; return d->colorList[index].color; } int kpColorCollection::findColor(const QColor &color) const { for (int i = 0; i < d->colorList.size(); ++i) { if (d->colorList[i].color == color) return i; } return -1; } QString kpColorCollection::name(int index) const { if ((index < 0) || (index >= count())) return {}; return d->colorList[index].name; } QString kpColorCollection::name(const QColor &color) const { return name(findColor(color)); } int kpColorCollection::addColor(const QColor &newColor, const QString &newColorName) { d->colorList.append(ColorNode(newColor, newColorName)); return count() - 1; } int kpColorCollection::changeColor(int index, const QColor &newColor, const QString &newColorName) { if ((index < 0) || (index >= count())) return -1; ColorNode& node = d->colorList[index]; node.color = newColor; node.name = newColorName; return index; } int kpColorCollection::changeColor(const QColor &oldColor, const QColor &newColor, const QString &newColorName) { return changeColor( findColor(oldColor), newColor, newColorName); } diff --git a/lgpl/generic/widgets/kpColorCellsBase.cpp b/lgpl/generic/widgets/kpColorCellsBase.cpp index ce62c952..a2392cc3 100644 --- a/lgpl/generic/widgets/kpColorCellsBase.cpp +++ b/lgpl/generic/widgets/kpColorCellsBase.cpp @@ -1,563 +1,545 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Martin Jones (mjones@kde.org) Copyright (C) 2007 Roberto Raggi (roberto@kdevelop.org) Copyright (C) 2007 Clarence Dang (dang@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //----------------------------------------------------------------------------- -#define DEBUG_KP_COLOR_CELLS_BASE 1 - #include "kpColorCellsBase.h" #include #include #include #include #include #include #include #include #include #include #include "kpLogCategories.h" #include class kpColorCellsBase::kpColorCellsBasePrivate { public: kpColorCellsBasePrivate(kpColorCellsBase *q): q(q) { colors = nullptr; inMouse = false; selected = -1; shade = false; acceptDrags = false; cellsResizable = true; } kpColorCellsBase *q; // Note: This is a good thing and is _not_ data duplication with the // colors of QTableWidget cells, for the following reasons: // // 1. QColor in Qt4 is full-quality RGB. However, QTableWidget // cells are lossy as their colors may be dithered on the screen. // // Eventually, this field will be changed to a kpColor. // // 2. We change the QTableWidget cells' colors when the widget is // disabled (see changeEvent()). // // Therefore, do not remove this field without better reasons. QColor *colors; QPoint mousePos; int selected; bool shade; bool acceptDrags; bool cellsResizable; bool inMouse; }; kpColorCellsBase::kpColorCellsBase( QWidget *parent, int rows, int cols ) : QTableWidget( parent ), d(new kpColorCellsBasePrivate(this)) { setItemDelegate(new QItemDelegate(this)); setFrameShape(QFrame::NoFrame); d->shade = true; setRowCount( rows ); setColumnCount( cols ); verticalHeader()->setMinimumSectionSize(16); verticalHeader()->hide(); horizontalHeader()->setMinimumSectionSize(16); horizontalHeader()->hide(); d->colors = new QColor [ rows * cols ]; d->selected = 0; d->inMouse = false; // Drag'n'Drop setAcceptDrops( true); setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); viewport()->setBackgroundRole( QPalette::Window ); setBackgroundRole( QPalette::Window ); } kpColorCellsBase::~kpColorCellsBase() { delete [] d->colors; delete d; } void kpColorCellsBase::invalidateAllColors () { for (int r = 0; r < rowCount (); r++) for (int c = 0; c < columnCount (); c++) d->colors [r * columnCount () + c] = QColor (); } void kpColorCellsBase::clear() { invalidateAllColors (); QTableWidget::clear (); } void kpColorCellsBase::clearContents() { invalidateAllColors (); QTableWidget::clearContents (); } void kpColorCellsBase::setRowColumnCounts (int rows, int columns) { const int oldRows = rowCount (), oldCols = columnCount (); const int newRows = rows, newCols = columns; -#if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "oldRows=" << oldRows << "oldCols=" << oldCols << "newRows=" << newRows << "newCols=" << newCols; -#endif if (oldRows == newRows && oldCols == newCols) return; QTableWidget::setColumnCount (newCols); QTableWidget::setRowCount (newRows); QColor *oldColors = d->colors; d->colors = new QColor [newRows * newCols]; for (int r = 0; r < qMin (oldRows, newRows); r++) for (int c = 0; c < qMin (oldCols, newCols); c++) d->colors [r * newCols + c] = oldColors [r * oldCols + c]; delete [] oldColors; } void kpColorCellsBase::setColumnCount (int newColumns) { setRowColumnCounts (rowCount (), newColumns); } void kpColorCellsBase::setRowCount (int newRows) { setRowColumnCounts (newRows, columnCount ()); } QColor kpColorCellsBase::color(int index) const { return d->colors[index]; } int kpColorCellsBase::count() const { return rowCount() * columnCount(); } void kpColorCellsBase::setShading(bool _shade) { d->shade = _shade; } void kpColorCellsBase::setAcceptDrags(bool _acceptDrags) { d->acceptDrags = _acceptDrags; } void kpColorCellsBase::setCellsResizable(bool yes) { d->cellsResizable = yes; } void kpColorCellsBase::setSelected(int index) { Q_ASSERT( index >= 0 && index < count() ); d->selected = index; } int kpColorCellsBase::selectedIndex() const { return d->selected; } //--------------------------------------------------------------------- static void TableWidgetItemSetColor (QTableWidgetItem *tableItem, const QColor &color) { Q_ASSERT (tableItem); QImage image(16, 16, QImage::Format_ARGB32_Premultiplied); QPainter painter(&image); const int StippleSize = 4; QColor useColor; for (int dy = 0; dy < 16; dy += StippleSize) { for (int dx = 0; dx < 16; dx += StippleSize) { const bool parity = ((dy + dx) / StippleSize) % 2; if (!parity) useColor = Qt::white; else useColor = Qt::lightGray; painter.fillRect(dx, dy, StippleSize, StippleSize, useColor); } } painter.fillRect(image.rect(), color); painter.end(); tableItem->setData(Qt::BackgroundRole , QBrush(image)); } //--------------------------------------------------------------------- void kpColorCellsBase::setColor( int column, const QColor &colorIn ) { const int tableRow = column / columnCount(); const int tableColumn = column % columnCount(); Q_ASSERT( tableRow >= 0 && tableRow < rowCount() ); Q_ASSERT( tableColumn >= 0 && tableColumn < columnCount() ); QColor color = colorIn; d->colors[column] = color; QTableWidgetItem* tableItem = item(tableRow,tableColumn); if (color.isValid ()) { if ( tableItem == nullptr ) { tableItem = new QTableWidgetItem(); setItem(tableRow,tableColumn,tableItem); } if (isEnabled ()) ::TableWidgetItemSetColor (tableItem, color); } else { delete tableItem; } emit colorChanged (column, color); } void kpColorCellsBase::changeEvent( QEvent* event ) { QTableWidget::changeEvent (event); if (event->type () != QEvent::EnabledChange) return; for (int r = 0; r < rowCount (); r++) { for (int c = 0; c < columnCount (); c++) { const int index = r * columnCount () + c; QTableWidgetItem* tableItem = item(r, c); // See API Doc for this invariant. Q_ASSERT (!!tableItem == d->colors [index].isValid ()); if (!tableItem) continue; QColor color; if (isEnabled ()) color = d->colors [index]; else color = palette ().color (backgroundRole ()); ::TableWidgetItemSetColor (tableItem, color); } } } /*void kpColorCellsBase::paintCell( QPainter *painter, int row, int col ) { painter->setRenderHint( QPainter::Antialiasing , true ); QBrush brush; int w = 1; if (shade) { qDrawShadePanel( painter, 1, 1, cellWidth()-2, cellHeight()-2, palette(), true, 1, &brush ); w = 2; } QColor color = colors[ row * numCols() + col ]; if (!color.isValid()) { if (!shade) return; color = palette().color(backgroundRole()); } const QRect colorRect( w, w, cellWidth()-w*2, cellHeight()-w*2 ); painter->fillRect( colorRect, color ); if ( row * numCols() + col == selected ) { painter->setPen( qGray(color.rgb())>=127 ? Qt::black : Qt::white ); painter->drawLine( colorRect.topLeft(), colorRect.bottomRight() ); painter->drawLine( colorRect.topRight(), colorRect.bottomLeft() ); } }*/ void kpColorCellsBase::resizeEvent( QResizeEvent* e ) { if (d->cellsResizable) { // According to the Qt doc: // If you need to set the width of a given column to a fixed value, call // QHeaderView::resizeSection() on the table's {horizontal,vertical} // header. // Therefore we iterate over each row and column and set the header section // size, as the sizeHint does indeed appear to be ignored in favor of a // minimum size that is larger than what we want. for ( int index = 0 ; index < columnCount() ; index++ ) horizontalHeader()->resizeSection( index, sizeHintForColumn(index) ); for ( int index = 0 ; index < rowCount() ; index++ ) verticalHeader()->resizeSection( index, sizeHintForRow(index) ); } else { // Update scrollbars if they're forced on by a subclass. // TODO: Should the d->cellsResizable path (from kdelibs) do this as well? QTableWidget::resizeEvent (e); } } int kpColorCellsBase::sizeHintForColumn(int /*column*/) const { // TODO: Should it be "(width() - frameWidth() * 2) / columnCount()"? return width() / columnCount() ; } int kpColorCellsBase::sizeHintForRow(int /*row*/) const { // TODO: Should be "(height() - frameWidth() * 2) / rowCount()"? return height() / rowCount() ; } void kpColorCellsBase::mousePressEvent( QMouseEvent *e ) { d->inMouse = true; d->mousePos = e->pos(); } int kpColorCellsBase::positionToCell(const QPoint &pos, bool ignoreBorders, bool allowEmptyCell) const { //TODO ignoreBorders not yet handled Q_UNUSED( ignoreBorders ) const int r = indexAt (pos).row (), c = indexAt (pos).column (); -#if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "r=" << r << "c=" << c; -#endif if (r == -1 || c == -1) return -1; if (!allowEmptyCell && !itemAt(pos)) return -1; const int cell = r * columnCount() + c; /*if (!ignoreBorders) { int border = 2; int x = pos.x() - col * cellWidth(); int y = pos.y() - row * cellHeight(); if ( (x < border) || (x > cellWidth()-border) || (y < border) || (y > cellHeight()-border)) return -1; }*/ return cell; } void kpColorCellsBase::mouseMoveEvent( QMouseEvent *e ) { if( !(e->buttons() & Qt::LeftButton)) return; if(d->inMouse) { int delay = QApplication::startDragDistance(); if(e->x() > d->mousePos.x()+delay || e->x() < d->mousePos.x()-delay || e->y() > d->mousePos.y()+delay || e->y() < d->mousePos.y()-delay){ // Drag color object int cell = positionToCell(d->mousePos); if (cell != -1) { - #if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "beginning drag from cell=" << cell << "color: isValid=" << d->colors [cell].isValid () << " rgba=" << (int *) d->colors [cell].rgba(); - #endif + Q_ASSERT (d->colors[cell].isValid()); KColorMimeData::createDrag(d->colors[cell], this)->start(Qt::CopyAction | Qt::MoveAction); - #if DEBUG_KP_COLOR_CELLS_BASE + qCDebug(kpLogColorCollection) << "finished drag"; - #endif } } } } // LOTODO: I'm not quite clear on how the drop actions logic is supposed // to be done e.g.: // // 1. Who is supposed to call setDropAction(). // 2. Which variant of accept(), setAccepted(), acceptProposedAction() etc. // is supposed to be called to accept a move -- rather than copy -- // action. // // Nevertheless, it appears to work -- probably because we restrict // the non-Qt-default move/swap action to be intrawidget. static void SetDropAction (QWidget *self, QDropEvent *event) { // TODO: Would be nice to default to CopyAction if the destination cell // is null. if (event->source () == self && (event->keyboardModifiers () & Qt::ControlModifier) == 0) event->setDropAction(Qt::MoveAction); else event->setDropAction(Qt::CopyAction); } void kpColorCellsBase::dragEnterEvent( QDragEnterEvent *event) { -#if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "kpColorCellsBase::dragEnterEvent() acceptDrags=" << d->acceptDrags << " canDecode=" << KColorMimeData::canDecode(event->mimeData()); -#endif event->setAccepted( d->acceptDrags && KColorMimeData::canDecode( event->mimeData())); if (event->isAccepted ()) ::SetDropAction (this, event); } // Reimplemented to override QTableWidget's override. Else dropping doesn't work. void kpColorCellsBase::dragMoveEvent (QDragMoveEvent *event) { -#if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "kpColorCellsBase::dragMoveEvent() acceptDrags=" << d->acceptDrags << " canDecode=" << KColorMimeData::canDecode(event->mimeData()); -#endif // TODO: Disallow drag that isn't onto a cell. event->setAccepted( d->acceptDrags && KColorMimeData::canDecode( event->mimeData())); if (event->isAccepted ()) ::SetDropAction (this, event); } void kpColorCellsBase::dropEvent( QDropEvent *event) { QColor c=KColorMimeData::fromMimeData(event->mimeData()); const int dragSourceCell = event->source () == this ? positionToCell (d->mousePos, true) : -1; -#if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "kpColorCellsBase::dropEvent()" << "color: rgba=" << (const int *) c.rgba () << "isValid=" << c.isValid() << "source=" << event->source () << "dragSourceCell=" << dragSourceCell; -#endif if( c.isValid()) { ::SetDropAction (this, event); int cell = positionToCell(event->pos(), true, true/*allow empty cell*/); - #if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "\tcell=" << cell; - #endif // TODO: I believe kdelibs forgets to do this. if (cell == -1) return; // Avoid NOP. if (cell == dragSourceCell) return; QColor destOldColor = d->colors [cell]; setColor(cell,c); - #if DEBUG_KP_COLOR_CELLS_BASE qCDebug(kpLogColorCollection) << "\tdropAction=" << event->dropAction () << "destOldColor.rgba=" << (const int *) destOldColor.rgba (); - #endif if (event->dropAction () == Qt::MoveAction && dragSourceCell != -1) { setColor(dragSourceCell, destOldColor); } } } void kpColorCellsBase::mouseReleaseEvent( QMouseEvent *e ) { int cell = positionToCell(d->mousePos); int currentCell = positionToCell(e->pos()); // If we release the mouse in another cell and we don't have // a drag we should ignore this event. if (currentCell != cell) cell = -1; if ( (cell != -1) && (d->selected != cell) ) { d->selected = cell; const int newRow = cell/columnCount(); const int newColumn = cell%columnCount(); clearSelection(); // we do not want old violet selected cells item(newRow,newColumn)->setSelected(true); } d->inMouse = false; if (cell != -1) { emit colorSelected( cell , color(cell) ); emit colorSelectedWhitButton( cell , color(cell), e->button() ); } } void kpColorCellsBase::mouseDoubleClickEvent( QMouseEvent * /*e*/ ) { int cell = positionToCell(d->mousePos, false, true/*allow empty cell*/); if (cell != -1) emit colorDoubleClicked( cell , color(cell) ); } diff --git a/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp b/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp index 74d8f9af..8b6cedd9 100644 --- a/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp +++ b/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp @@ -1,142 +1,138 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_PIXMAP_FX 1 - #include "kpPixmapFX.h" #include #include #include #include #include "kpLogCategories.h" #include "imagelib/kpColor.h" //--------------------------------------------------------------------- // public static QImage kpPixmapFX::getPixmapAt (const QImage &image, const QRect &rect) { return image.copy(rect); } //--------------------------------------------------------------------- // public static void kpPixmapFX::setPixmapAt(QImage *destPtr, const QRect &destRect, const QImage &src) { -#if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "kpPixmapFX::setPixmapAt(destPixmap->rect=" << destPtr->rect () << ",destRect=" << destRect << ",src.rect=" << src.rect () << ")"; -#endif Q_ASSERT (destPtr); // You cannot copy more than what you have. Q_ASSERT (destRect.width () <= src.width () && destRect.height () <= src.height ()); QPainter painter(destPtr); // destination shall be source only painter.setCompositionMode(QPainter::CompositionMode_Source); painter.drawImage(destRect.topLeft(), src, QRect(0, 0, destRect.width(), destRect.height())); } //--------------------------------------------------------------------- // public static void kpPixmapFX::setPixmapAt (QImage *destPtr, const QPoint &destAt, const QImage &src) { kpPixmapFX::setPixmapAt (destPtr, QRect (destAt.x (), destAt.y (), src.width (), src.height ()), src); } //--------------------------------------------------------------------- // public static void kpPixmapFX::setPixmapAt (QImage *destPtr, int destX, int destY, const QImage &src) { kpPixmapFX::setPixmapAt (destPtr, QPoint (destX, destY), src); } //--------------------------------------------------------------------- // public static void kpPixmapFX::paintPixmapAt (QImage *destPtr, const QPoint &destAt, const QImage &src) { // draw image with SourceOver composition mode QPainter painter(destPtr); painter.drawImage(destAt, src); } //--------------------------------------------------------------------- // public static void kpPixmapFX::paintPixmapAt (QImage *destPtr, int destX, int destY, const QImage &src) { kpPixmapFX::paintPixmapAt(destPtr, QPoint (destX, destY), src); } //--------------------------------------------------------------------- // public static kpColor kpPixmapFX::getColorAtPixel (const QImage &img, const QPoint &at) { if (!img.valid (at.x (), at.y ())) { return kpColor::Invalid; } QRgb rgba = img.pixel(at); return kpColor (rgba); } //--------------------------------------------------------------------- // public static kpColor kpPixmapFX::getColorAtPixel (const QImage &img, int x, int y) { return kpPixmapFX::getColorAtPixel (img, QPoint (x, y)); } //--------------------------------------------------------------------- diff --git a/tools/flow/kpToolColorEraser.cpp b/tools/flow/kpToolColorEraser.cpp index 676c5932..a7d4ab93 100644 --- a/tools/flow/kpToolColorEraser.cpp +++ b/tools/flow/kpToolColorEraser.cpp @@ -1,160 +1,152 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_COLOR_ERASER 1 - #include "kpToolColorEraser.h" #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "commands/kpMacroCommand.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "commands/tools/flow/kpToolFlowCommand.h" #include "environments/tools/kpToolEnvironment.h" //-------------------------------------------------------------------------------- kpToolColorEraser::kpToolColorEraser (kpToolEnvironment *environ, QObject *parent) : kpToolFlowBase (i18n ("Color Eraser"), i18n ("Replaces pixels of the foreground color with the background color"), Qt::Key_O, environ, parent, QStringLiteral("tool_color_eraser")) { } //-------------------------------------------------------------------------------- kpToolColorEraser::~kpToolColorEraser () = default; //-------------------------------------------------------------------------------- // public virtual [base kpTool] void kpToolColorEraser::globalDraw () { -#if DEBUG_KP_TOOL_COLOR_ERASER qCDebug(kpLogTools) << "kpToolColorEraser::globalDraw()"; -#endif + if (!drawShouldProceed (QPoint ()/*unused*/, QPoint ()/*unused*/, QRect ()/*unused*/)) { return; } QApplication::setOverrideCursor (Qt::WaitCursor); environ ()->flashColorSimilarityToolBarItem (); kpToolFlowCommand *cmd = new kpToolFlowCommand ( i18n ("Color Eraser"), environ ()->commandEnvironment ()); const QRect dirtyRect = kpPainter::washRect (document ()->imagePointer (), 0, 0, document ()->width (), document ()->height (), backgroundColor ()/*color to draw in*/, foregroundColor ()/*color to replace*/, processedColorSimilarity ()); if (!dirtyRect.isEmpty ()) { document ()->slotContentsChanged (dirtyRect); cmd->updateBoundingRect (dirtyRect); cmd->finalize (); commandHistory ()->addCommand (cmd, false /* don't exec */); // don't delete - it's up to the commandHistory cmd = nullptr; } else { - #if DEBUG_KP_TOOL_COLOR_ERASER qCDebug(kpLogTools) << "\tisNOP"; - #endif + delete cmd; cmd = nullptr; } QApplication::restoreOverrideCursor (); } //-------------------------------------------------------------------------------- QString kpToolColorEraser::haventBegunDrawUserMessage () const { return i18n ("Click or drag to erase pixels of the foreground color."); } //-------------------------------------------------------------------------------- bool kpToolColorEraser::drawShouldProceed (const QPoint & /*thisPoint*/, const QPoint & /*lastPoint*/, const QRect & /*normalizedRect*/) { return !(foregroundColor () == backgroundColor () && processedColorSimilarity () == 0); } //-------------------------------------------------------------------------------- QRect kpToolColorEraser::drawLine (const QPoint &thisPoint, const QPoint &lastPoint) { -#if DEBUG_KP_TOOL_COLOR_ERASER qCDebug(kpLogTools) << "kpToolColorEraser::drawLine(thisPoint=" << thisPoint << ",lastPoint=" << lastPoint << ")"; -#endif environ ()->flashColorSimilarityToolBarItem (); const QRect dirtyRect = kpPainter::washLine (document ()->imagePointer (), lastPoint.x (), lastPoint.y (), thisPoint.x (), thisPoint.y (), color (mouseButton ())/*color to draw in*/, brushWidth (), brushHeight (), color (1 - mouseButton ())/*color to replace*/, processedColorSimilarity ()); -#if DEBUG_KP_TOOL_COLOR_ERASER qCDebug(kpLogTools) << "\tdirtyRect=" << dirtyRect; -#endif if (!dirtyRect.isEmpty ()) { document ()->slotContentsChanged (dirtyRect); return dirtyRect; } return {}; } //-------------------------------------------------------------------------------- diff --git a/tools/flow/kpToolEraser.cpp b/tools/flow/kpToolEraser.cpp index 1854ac39..cc84a819 100644 --- a/tools/flow/kpToolEraser.cpp +++ b/tools/flow/kpToolEraser.cpp @@ -1,80 +1,76 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_ERASER 1 - #include "kpToolEraser.h" #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "commands/imagelib/effects/kpEffectClearCommand.h" #include "environments/tools/kpToolEnvironment.h" #include //--------------------------------------------------------------------- kpToolEraser::kpToolEraser (kpToolEnvironment *environ, QObject *parent) : kpToolFlowPixmapBase (i18n ("Eraser"), i18n ("Lets you rub out mistakes"), Qt::Key_A, environ, parent, QStringLiteral("tool_eraser")) { } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpToolEraser::globalDraw () { -#if DEBUG_KP_TOOL_ERASER qCDebug(kpLogTools) << "kpToolEraser::globalDraw()"; -#endif commandHistory ()->addCommand ( new kpEffectClearCommand ( false/*act on doc, not sel*/, backgroundColor (), environ ()->commandEnvironment ())); } //--------------------------------------------------------------------- // protected virtual [base kpToolFlowBase] QString kpToolEraser::haventBegunDrawUserMessage () const { return i18n ("Click or drag to erase."); } //--------------------------------------------------------------------- // See the our corresponding .h for brush selection. // Logic is in kpToolFlowPixmapBase. diff --git a/tools/flow/kpToolFlowBase.cpp b/tools/flow/kpToolFlowBase.cpp index 4c2437c8..5bf12719 100644 --- a/tools/flow/kpToolFlowBase.cpp +++ b/tools/flow/kpToolFlowBase.cpp @@ -1,493 +1,484 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_FLOW_BASE 1 - #include "kpToolFlowBase.h" #include #include #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "commands/kpCommandHistory.h" #include "cursors/kpCursorProvider.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "imagelib/kpImage.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/tempImage/kpTempImage.h" #include "environments/tools/kpToolEnvironment.h" #include "commands/tools/flow/kpToolFlowCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetBrush.h" #include "widgets/toolbars/options/kpToolWidgetEraserSize.h" #include "views/manager/kpViewManager.h" //--------------------------------------------------------------------- struct kpToolFlowBasePrivate { kpToolWidgetBrush *toolWidgetBrush{}; kpToolWidgetEraserSize *toolWidgetEraserSize{}; // // Cursor and Brush Data // (must be zero if unused) // kpTempImage::UserFunctionType brushDrawFunc{}, cursorDrawFunc{}; // Can't use union since package types contain fields requiring // constructors. kpToolWidgetBrush::DrawPackage brushDrawPackageForMouseButton [2]; kpToolWidgetEraserSize::DrawPackage eraserDrawPackageForMouseButton [2]; // Each element points to one of the above (both elements from the same // array). void *drawPackageForMouseButton [2]{}; int brushWidth{}, brushHeight{}; int cursorWidth{}, cursorHeight{}; bool brushIsDiagonalLine{}; kpToolFlowCommand *currentCommand{}; }; //--------------------------------------------------------------------- kpToolFlowBase::kpToolFlowBase (const QString &text, const QString &description, int key, kpToolEnvironment *environ, QObject *parent, const QString &name) : kpTool (text, description, key, environ, parent, name), d (new kpToolFlowBasePrivate ()) { d->toolWidgetBrush = nullptr; d->toolWidgetEraserSize = nullptr; clearBrushCursorData (); d->currentCommand = nullptr; } //--------------------------------------------------------------------- kpToolFlowBase::~kpToolFlowBase () { delete d; } //--------------------------------------------------------------------- // private void kpToolFlowBase::clearBrushCursorData () { d->brushDrawFunc = d->cursorDrawFunc = nullptr; memset (&d->brushDrawPackageForMouseButton, 0, sizeof (d->brushDrawPackageForMouseButton)); memset (&d->eraserDrawPackageForMouseButton, 0, sizeof (d->eraserDrawPackageForMouseButton)); memset (&d->drawPackageForMouseButton, 0, sizeof (d->drawPackageForMouseButton)); d->brushWidth = d->brushHeight = 0; d->cursorWidth = d->cursorHeight = 0; d->brushIsDiagonalLine = false; } //--------------------------------------------------------------------- // virtual void kpToolFlowBase::begin () { kpToolToolBar *tb = toolToolBar (); Q_ASSERT (tb); // TODO: Bad smell. Mutually exclusive. Use inheritance. if (haveSquareBrushes ()) { d->toolWidgetEraserSize = tb->toolWidgetEraserSize (); connect (d->toolWidgetEraserSize, &kpToolWidgetEraserSize::eraserSizeChanged, this, &kpToolFlowBase::updateBrushAndCursor); d->toolWidgetEraserSize->show (); updateBrushAndCursor (); viewManager ()->setCursor (kpCursorProvider::lightCross ()); } else if (haveDiverseBrushes ()) { d->toolWidgetBrush = tb->toolWidgetBrush (); connect (d->toolWidgetBrush, &kpToolWidgetBrush::brushChanged, this, &kpToolFlowBase::updateBrushAndCursor); d->toolWidgetBrush->show (); updateBrushAndCursor (); viewManager ()->setCursor (kpCursorProvider::lightCross ()); } setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // virtual void kpToolFlowBase::end () { if (d->toolWidgetEraserSize) { disconnect (d->toolWidgetEraserSize, &kpToolWidgetEraserSize::eraserSizeChanged, this, &kpToolFlowBase::updateBrushAndCursor); d->toolWidgetEraserSize = nullptr; } else if (d->toolWidgetBrush) { disconnect (d->toolWidgetBrush, &kpToolWidgetBrush::brushChanged, this, &kpToolFlowBase::updateBrushAndCursor); d->toolWidgetBrush = nullptr; } kpViewManager *vm = viewManager (); Q_ASSERT (vm); if (vm->tempImage () && vm->tempImage ()->isBrush ()) { vm->invalidateTempImage (); } if (haveAnyBrushes ()) { vm->unsetCursor (); } clearBrushCursorData (); } //--------------------------------------------------------------------- // virtual void kpToolFlowBase::beginDraw () { d->currentCommand = new kpToolFlowCommand (text (), environ ()->commandEnvironment ()); // We normally show the brush cursor in the foreground colour but if the // user starts drawing in the background color, we don't want to leave // the brush cursor in the foreground colour -- just hide it in all cases // to avoid confusion. viewManager ()->invalidateTempImage (); setUserMessage (cancelUserMessage ()); } //--------------------------------------------------------------------- // virtual void kpToolFlowBase::hover (const QPoint &point) { -#if DEBUG_KP_TOOL_FLOW_BASE && 0 +#if 0 qCDebug(kpLogTools) << "kpToolFlowBase::hover(" << point << ")" << " hasBegun=" << hasBegun () << " hasBegunDraw=" << hasBegunDraw () - << " cursorPixmap.isNull=" << m_cursorPixmap.isNull () - << endl; + << " cursorPixmap.isNull=" << m_cursorPixmap.isNull (); #endif if (point != KP_INVALID_POINT && d->cursorDrawFunc) { viewManager ()->setFastUpdates (); viewManager ()->setTempImage ( kpTempImage (true/*brush*/, hotRect ().topLeft (), d->cursorDrawFunc, d->drawPackageForMouseButton [0/*left button*/], d->cursorWidth, d->cursorHeight)); viewManager ()->restoreFastUpdates (); } setUserShapePoints (point); } //--------------------------------------------------------------------- // virtual QRect kpToolFlowBase::drawPoint (const QPoint &point) { return drawLine (point, point); } //--------------------------------------------------------------------- // virtual void kpToolFlowBase::draw (const QPoint &thisPoint, const QPoint &lastPoint, const QRect &normalizedRect) { if (!/*virtual*/drawShouldProceed (thisPoint, lastPoint, normalizedRect)) { return; } // sync: remember to restoreFastUpdates() in all exit paths viewManager ()->setFastUpdates (); QRect dirtyRect; // TODO: I'm beginning to wonder this drawPoint() "optimization" actually // optimises. Is it worth the complexity? Hence drawPoint() impl above. if (d->brushIsDiagonalLine ? currentPointCardinallyNextToLast () : currentPointNextToLast ()) { dirtyRect = drawPoint (thisPoint); } // in reality, the system is too slow to give us all the MouseMove events // so we "interpolate" the missing points :) else { dirtyRect = drawLine (thisPoint, lastPoint); } d->currentCommand->updateBoundingRect (dirtyRect); viewManager ()->restoreFastUpdates (); setUserShapePoints (thisPoint); } //--------------------------------------------------------------------- // virtual void kpToolFlowBase::cancelShape () { d->currentCommand->finalize (); d->currentCommand->cancel (); delete d->currentCommand; d->currentCommand = nullptr; updateBrushAndCursor (); setUserMessage (i18n ("Let go of all the mouse buttons.")); } //--------------------------------------------------------------------- void kpToolFlowBase::releasedAllButtons () { setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // virtual void kpToolFlowBase::endDraw (const QPoint &, const QRect &) { d->currentCommand->finalize (); environ ()->commandHistory ()->addCommand (d->currentCommand, false/*don't exec*/); // don't delete - it's up to the commandHistory d->currentCommand = nullptr; updateBrushAndCursor (); setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // TODO: maybe the base should be virtual? kpColor kpToolFlowBase::color (int which) { -#if DEBUG_KP_TOOL_FLOW_BASE && 0 +#if 0 qCDebug(kpLogTools) << "kpToolFlowBase::color (" << which << ")"; #endif // Pen & Brush if (!colorsAreSwapped ()) { return kpTool::color (which); } // only the (Color) Eraser uses the opposite color return kpTool::color (which ? 0 : 1); // don't trust !0 == 1 } //--------------------------------------------------------------------- // protected kpTempImage::UserFunctionType kpToolFlowBase::brushDrawFunction () const { return d->brushDrawFunc; } //--------------------------------------------------------------------- // protected void *kpToolFlowBase::brushDrawFunctionData () const { return d->drawPackageForMouseButton [mouseButton ()]; } // protected int kpToolFlowBase::brushWidth () const { return d->brushWidth; } // protected int kpToolFlowBase::brushHeight () const { return d->brushHeight; } // protected bool kpToolFlowBase::brushIsDiagonalLine () const { return d->brushIsDiagonalLine; } // protected kpToolFlowCommand *kpToolFlowBase::currentCommand () const { return d->currentCommand; } //--------------------------------------------------------------------- // protected slot void kpToolFlowBase::updateBrushAndCursor () { -#if DEBUG_KP_TOOL_FLOW_BASE && 1 qCDebug(kpLogTools) << "kpToolFlowBase::updateBrushAndCursor()"; -#endif if (haveSquareBrushes ()) { d->brushDrawFunc = d->toolWidgetEraserSize->drawFunction (); d->cursorDrawFunc = d->toolWidgetEraserSize->drawCursorFunction (); for (int i = 0; i < 2; i++) { d->drawPackageForMouseButton [i] = &(d->eraserDrawPackageForMouseButton [i] = d->toolWidgetEraserSize->drawFunctionData (color (i))); } d->brushWidth = d->brushHeight = d->cursorWidth = d->cursorHeight = d->toolWidgetEraserSize->eraserSize (); d->brushIsDiagonalLine = false; } else if (haveDiverseBrushes ()) { d->brushDrawFunc = d->cursorDrawFunc = d->toolWidgetBrush->drawFunction (); for (int i = 0; i < 2; i++) { d->drawPackageForMouseButton [i] = &(d->brushDrawPackageForMouseButton [i] = d->toolWidgetBrush->drawFunctionData (color (i))); } d->brushWidth = d->brushHeight = d->cursorWidth = d->cursorHeight = d->toolWidgetBrush->brushSize (); d->brushIsDiagonalLine = d->toolWidgetBrush->brushIsDiagonalLine (); } hover (hasBegun () ? currentPoint () : calculateCurrentPoint ()); } //--------------------------------------------------------------------- // virtual private slot void kpToolFlowBase::slotForegroundColorChanged (const kpColor & /*col*/) { -#if DEBUG_KP_TOOL_FLOW_BASE qCDebug(kpLogTools) << "kpToolFlowBase::slotForegroundColorChanged()"; -#endif updateBrushAndCursor (); } //--------------------------------------------------------------------- // virtual private slot void kpToolFlowBase::slotBackgroundColorChanged (const kpColor & /*col*/) { -#if DEBUG_KP_TOOL_FLOW_BASE qCDebug(kpLogTools) << "kpToolFlowBase::slotBackgroundColorChanged()"; -#endif updateBrushAndCursor (); } //--------------------------------------------------------------------- // public static QRect kpToolFlowBase::hotRectForMousePointAndBrushWidthHeight ( const QPoint &mousePoint, int brushWidth, int brushHeight) { /* * e.g. * Width 5: * 0 1 2 3 4 * ^ * | * Center */ return {mousePoint.x () - brushWidth / 2, mousePoint.y () - brushHeight / 2, brushWidth, brushHeight}; } //--------------------------------------------------------------------- // protected QRect kpToolFlowBase::hotRect () const { return hotRectForMousePointAndBrushWidthHeight (currentPoint (), d->brushWidth, d->brushHeight); } //--------------------------------------------------------------------- diff --git a/tools/flow/kpToolSpraycan.cpp b/tools/flow/kpToolSpraycan.cpp index e8a9dddb..eef4924b 100644 --- a/tools/flow/kpToolSpraycan.cpp +++ b/tools/flow/kpToolSpraycan.cpp @@ -1,259 +1,241 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SPRAYCAN 1 - #include "kpToolSpraycan.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "environments/tools/kpToolEnvironment.h" #include "commands/tools/flow/kpToolFlowCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetSpraycanSize.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include #include "kpLogCategories.h" #include #include #include #include //--------------------------------------------------------------------- kpToolSpraycan::kpToolSpraycan (kpToolEnvironment *environ, QObject *parent) : kpToolFlowBase (i18n ("Spraycan"), i18n ("Sprays graffiti"), Qt::Key_Y, environ, parent, QStringLiteral("tool_spraycan")), m_toolWidgetSpraycanSize(nullptr) { m_timer = new QTimer (this); m_timer->setInterval (25/*ms*/); connect (m_timer, &QTimer::timeout, this, &kpToolSpraycan::timeoutDraw); } //--------------------------------------------------------------------- // protected virtual [base kpToolFlowBase] QString kpToolSpraycan::haventBegunDrawUserMessage () const { return i18n ("Click or drag to spray graffiti."); } //--------------------------------------------------------------------- // public virtual [base kpToolFlowBase] void kpToolSpraycan::begin () { kpToolToolBar *tb = toolToolBar (); Q_ASSERT (tb); m_toolWidgetSpraycanSize = tb->toolWidgetSpraycanSize (); connect (m_toolWidgetSpraycanSize, &kpToolWidgetSpraycanSize::spraycanSizeChanged, this, &kpToolSpraycan::slotSpraycanSizeChanged); m_toolWidgetSpraycanSize->show (); kpToolFlowBase::begin (); } // public virtual [base kpToolFlowBase] void kpToolSpraycan::end () { kpToolFlowBase::end (); disconnect (m_toolWidgetSpraycanSize, &kpToolWidgetSpraycanSize::spraycanSizeChanged, this, &kpToolSpraycan::slotSpraycanSizeChanged); m_toolWidgetSpraycanSize = nullptr; } // public virtual [base kpToolFlowBase] void kpToolSpraycan::beginDraw () { -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "kpToolSpraycan::beginDraw()"; -#endif kpToolFlowBase::beginDraw (); // We draw even if the user doesn't move the mouse. // We still timeout-draw even if the user _does_ move the mouse. m_timer->start (); } // protected QRect kpToolSpraycan::drawLineWithProbability (const QPoint &thisPoint, const QPoint &lastPoint, double probability) { -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "CALL(thisPoint=" << thisPoint << ",lastPoint=" << lastPoint << ")"; -#endif QList docPoints = kpPainter::interpolatePoints (lastPoint, thisPoint, false/*no need for cardinally adjacency points*/, probability); -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "\tdocPoints=" << docPoints; -#endif // By chance no points to draw? if (docPoints.empty ()) { return {}; } // For efficiency, only get image after NOP check above. QRect docRect = kpPainter::normalizedRect(thisPoint, lastPoint); docRect = neededRect (docRect, spraycanSize ()); kpImage image = document ()->getImageAt (docRect); // Spray at each point, onto the image. // // Note in passing: Unlike other tools such as the Brush, drawing // over the same point does result in a different // appearance. QList imagePoints; for (const auto &dp : docPoints) imagePoints.append (dp - docRect.topLeft ()); kpPainter::sprayPoints (&image, imagePoints, color (mouseButton ()), spraycanSize ()); viewManager ()->setFastUpdates (); document ()->setImageAt (image, docRect.topLeft ()); viewManager ()->restoreFastUpdates (); return docRect; } // public virtual [base kpToolFlowBase] QRect kpToolSpraycan::drawPoint (const QPoint &point) { -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "kpToolSpraycan::drawPoint" << point << " lastPoint=" << lastPoint (); -#endif // If this is the first in the flow or if the user is moving the spray, // make the spray line continuous. if (point != lastPoint ()) { // Draw at this single point without delay. return drawLineWithProbability (point, point, 1.0/*100% chance of drawing*/); } return {}; } // public virtual [base kpToolFlowBase] QRect kpToolSpraycan::drawLine (const QPoint &thisPoint, const QPoint &lastPoint) { -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "CALL(thisPoint=" << thisPoint << ",lastPoint=" << lastPoint; -#endif // Draw only every so often in response to movement. return drawLineWithProbability (thisPoint, lastPoint, 0.05/*less dense: select 5% of adjacent pixels - not all*/); } // protected slot void kpToolSpraycan::timeoutDraw () { -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "kpToolSpraycan::timeoutDraw()"; -#endif // Draw at this single point without delay. const QRect drawnRect = drawLineWithProbability (currentPoint (), currentPoint (), 1.0/*100% chance of drawing*/); // kpToolFlowBase() does this after calling drawPoint() and drawLine() so // we need to do it too. currentCommand ()->updateBoundingRect (drawnRect); } // public virtual [base kpToolFlowBase] void kpToolSpraycan::cancelShape () { -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "kpToolSpraycan::cancelShape()"; -#endif m_timer->stop (); kpToolFlowBase::cancelShape (); } // public virtual [base kpToolFlowBase] void kpToolSpraycan::endDraw (const QPoint &thisPoint, const QRect &normalizedRect) { -#if DEBUG_KP_TOOL_SPRAYCAN qCDebug(kpLogTools) << "kpToolSpraycan::endDraw(thisPoint=" << thisPoint << ")"; -#endif m_timer->stop (); kpToolFlowBase::endDraw (thisPoint, normalizedRect); } // protected int kpToolSpraycan::spraycanSize () const { return m_toolWidgetSpraycanSize->spraycanSize (); } // protected slot void kpToolSpraycan::slotSpraycanSizeChanged (int size) { (void) size; } diff --git a/tools/kpTool.cpp b/tools/kpTool.cpp index 09be60a6..6c9f4a6f 100644 --- a/tools/kpTool.cpp +++ b/tools/kpTool.cpp @@ -1,263 +1,261 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool initialisation and basic accessors. // -#define DEBUG_KP_TOOL 1 - #include "kpTool.h" #include "kpToolPrivate.h" #include #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "tools/kpToolAction.h" #include "environments/tools/kpToolEnvironment.h" #include "widgets/toolbars/kpToolToolBar.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #undef environ // macro on win32 //--------------------------------------------------------------------- kpTool::kpTool(const QString &text, const QString &description, int key, kpToolEnvironment *environ, QObject *parent, const QString &name) : QObject(parent), d(new kpToolPrivate()) { d->key = key; d->action = nullptr; d->ignoreColorSignals = 0; d->shiftPressed = false; d->controlPressed = false; d->altPressed = false; // set in beginInternal() d->beganDraw = false; d->text = text; d->description = description; d->began = false; d->viewUnderStartPoint = nullptr; d->userShapeStartPoint = KP_INVALID_POINT; d->userShapeEndPoint = KP_INVALID_POINT; d->userShapeSize = KP_INVALID_SIZE; d->environ = environ; setObjectName(name); initAction(); } //--------------------------------------------------------------------- kpTool::~kpTool () { // before destructing, stop using the tool if (d->began) { endInternal (); } delete d->action; delete d; } //--------------------------------------------------------------------- // private void kpTool::initAction () { KActionCollection *ac = d->environ->actionCollection (); Q_ASSERT (ac); d->action = new kpToolAction(text(), objectName(), shortcutForKey(d->key), this, SIGNAL(actionActivated()), ac, objectName()); // Make tools mutually exclusive by placing them in the same group. d->action->setActionGroup(d->environ->toolsActionGroup ()); d->action->setWhatsThis(d->description); connect (d->action, &kpToolAction::changed, this, &kpTool::actionToolTipChanged); } //--------------------------------------------------------------------- // public QString kpTool::text () const { return d->text; } //--------------------------------------------------------------------- static bool KeyIsText (int key) { // TODO: should work like !QKeyEvent::text().isEmpty() return !(static_cast (key) & (Qt::KeyboardModifierMask ^ Qt::ShiftModifier)); } //--------------------------------------------------------------------- // public static QString kpTool::toolTipForTextAndShortcut (const QString &text, const QList &shortcut) { for(const auto &seq : shortcut) { if (seq.count () == 1 && ::KeyIsText (seq [0])) { return i18nc (" ()", "%1 (%2)", text, seq.toString ().toUpper ()); } } return text; } //--------------------------------------------------------------------- QString kpTool::toolTip () const { return toolTipForTextAndShortcut(d->text, d->action->shortcuts()); } //--------------------------------------------------------------------- // public static QList kpTool::shortcutForKey (int key) { QList shortcut; if (key) { shortcut.append (QKeySequence (key)); // (CTRL+, ALT+, CTRL+ALT+, CTRL+SHIFT+ // all clash with global KDE shortcuts) shortcut.append (QKeySequence (static_cast(Qt::ALT) + static_cast(Qt::SHIFT) + key)); } return shortcut; } //--------------------------------------------------------------------- // public kpToolAction *kpTool::action () const { return d->action; } //--------------------------------------------------------------------- kpDocument *kpTool::document () const { return d->environ->document (); } //--------------------------------------------------------------------- kpViewManager *kpTool::viewManager () const { return d->environ->viewManager (); } //--------------------------------------------------------------------- kpToolToolBar *kpTool::toolToolBar () const { return d->environ->toolToolBar (); } //--------------------------------------------------------------------- kpColor kpTool::color (int which) const { return d->environ->color (which); } //--------------------------------------------------------------------- kpColor kpTool::foregroundColor () const { return color (0); } //--------------------------------------------------------------------- kpColor kpTool::backgroundColor () const { return color (1); } //--------------------------------------------------------------------- // TODO: Some of these might not be common enough. // Just put in kpToolEnvironment? double kpTool::colorSimilarity () const { return d->environ->colorSimilarity (); } int kpTool::processedColorSimilarity () const { return d->environ->processedColorSimilarity (); } kpColor kpTool::oldForegroundColor () const { return d->environ->oldForegroundColor (); } kpColor kpTool::oldBackgroundColor () const { return d->environ->oldBackgroundColor (); } double kpTool::oldColorSimilarity () const { return d->environ->oldColorSimilarity (); } kpCommandHistory *kpTool::commandHistory () const { return d->environ->commandHistory (); } kpToolEnvironment *kpTool::environ () const { return d->environ; } diff --git a/tools/kpToolColorPicker.cpp b/tools/kpToolColorPicker.cpp index 56f0f9f3..afde902b 100644 --- a/tools/kpToolColorPicker.cpp +++ b/tools/kpToolColorPicker.cpp @@ -1,137 +1,133 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_COLOR_PICKER 1 - #include "kpToolColorPicker.h" #include "kpLogCategories.h" #include "widgets/toolbars/kpColorToolBar.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "commands/tools/kpToolColorPickerCommand.h" #include "environments/tools/kpToolEnvironment.h" #include kpToolColorPicker::kpToolColorPicker (kpToolEnvironment *environ, QObject *parent) : kpTool (i18n ("Color Picker"), i18n ("Lets you select a color from the image"), Qt::Key_C, environ, parent, QStringLiteral("tool_color_picker")) { } kpToolColorPicker::~kpToolColorPicker () = default; // private kpColor kpToolColorPicker::colorAtPixel (const QPoint &p) { -#if DEBUG_KP_TOOL_COLOR_PICKER && 0 qCDebug(kpLogTools) << "kpToolColorPicker::colorAtPixel" << p; -#endif return kpPixmapFX::getColorAtPixel (document ()->image (), p); } // private QString kpToolColorPicker::haventBegunDrawUserMessage () const { return i18n ("Click to select a color."); } // public virtual [base kpTool] void kpToolColorPicker::begin () { setUserMessage (haventBegunDrawUserMessage ()); } // public virtual [base kpTool] void kpToolColorPicker::beginDraw () { m_oldColor = color (mouseButton ()); setUserMessage (cancelUserMessage ()); } // public virtual [base kpTool] void kpToolColorPicker::draw (const QPoint &thisPoint, const QPoint &, const QRect &) { const kpColor color = colorAtPixel (thisPoint); if (color.isValid ()) { environ ()->setColor (mouseButton (), color); setUserShapePoints (thisPoint); } else { environ ()->setColor (mouseButton (), m_oldColor); setUserShapePoints (); } } // public virtual [base kpTool] void kpToolColorPicker::cancelShape () { environ ()->setColor (mouseButton (), m_oldColor); setUserMessage (i18n ("Let go of all the mouse buttons.")); } // public virtual [base kpTool] void kpToolColorPicker::releasedAllButtons () { setUserMessage (haventBegunDrawUserMessage ()); } // public virtual [base kpTool] void kpToolColorPicker::endDraw (const QPoint &thisPoint, const QRect &) { const kpColor color = colorAtPixel (thisPoint); if (color.isValid ()) { auto *cmd = new kpToolColorPickerCommand ( mouseButton (), color, m_oldColor, environ ()->commandEnvironment ()); environ ()->commandHistory ()->addCommand (cmd, false/*no exec*/); setUserMessage (haventBegunDrawUserMessage ()); } else { cancelShape (); } } diff --git a/tools/kpToolFloodFill.cpp b/tools/kpToolFloodFill.cpp index 88c5d308..901ed251 100644 --- a/tools/kpToolFloodFill.cpp +++ b/tools/kpToolFloodFill.cpp @@ -1,169 +1,163 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_FLOOD_FILL 1 - #include "kpToolFloodFill.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "environments/tools/kpToolEnvironment.h" #include "commands/tools/kpToolFloodFillCommand.h" #include "kpLogCategories.h" #include #include //--------------------------------------------------------------------- struct kpToolFloodFillPrivate { kpToolFloodFillCommand *currentCommand; }; //--------------------------------------------------------------------- kpToolFloodFill::kpToolFloodFill (kpToolEnvironment *environ, QObject *parent) : kpTool (i18n ("Flood Fill"), i18n ("Fills regions in the image"), Qt::Key_F, environ, parent, QStringLiteral("tool_flood_fill")), d (new kpToolFloodFillPrivate ()) { d->currentCommand = nullptr; } //--------------------------------------------------------------------- kpToolFloodFill::~kpToolFloodFill () { delete d; } //--------------------------------------------------------------------- // private QString kpToolFloodFill::haventBegunDrawUserMessage () const { return i18n ("Click to fill a region."); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpToolFloodFill::begin () { setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpToolFloodFill::beginDraw () { -#if DEBUG_KP_TOOL_FLOOD_FILL && 1 qCDebug(kpLogTools) << "kpToolFloodFill::beginDraw()"; -#endif QApplication::setOverrideCursor (Qt::WaitCursor); { environ ()->flashColorSimilarityToolBarItem (); // Flood Fill is an expensive CPU operation so we only fill at a // mouse click (beginDraw ()), not on mouse move (virtually draw()) d->currentCommand = new kpToolFloodFillCommand ( currentPoint ().x (), currentPoint ().y (), color (mouseButton ()), processedColorSimilarity (), environ ()->commandEnvironment ()); - #if DEBUG_KP_TOOL_FLOOD_FILL && 1 qCDebug(kpLogTools) << "\tperforming new-doc-corner-case check"; - #endif if (document ()->url ().isEmpty () && !document ()->isModified ()) { // Collect the colour that gets changed before we change the pixels // (execute() below). Needed in unexecute(). d->currentCommand->prepareColorToChange (); d->currentCommand->setFillEntireImage (); } d->currentCommand->execute (); } QApplication::restoreOverrideCursor (); setUserMessage (cancelUserMessage ()); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpToolFloodFill::draw (const QPoint &thisPoint, const QPoint &, const QRect &) { setUserShapePoints (thisPoint); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpToolFloodFill::cancelShape () { d->currentCommand->unexecute (); delete d->currentCommand; d->currentCommand = nullptr; setUserMessage (i18n ("Let go of all the mouse buttons.")); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpToolFloodFill::releasedAllButtons () { setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpToolFloodFill::endDraw (const QPoint &, const QRect &) { environ ()->commandHistory ()->addCommand (d->currentCommand, false/*no exec - we already did it up there*/); // Don't delete - it just got added to the history. d->currentCommand = nullptr; setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- diff --git a/tools/kpToolZoom.cpp b/tools/kpToolZoom.cpp index 6527a0c0..e9839fcc 100644 --- a/tools/kpToolZoom.cpp +++ b/tools/kpToolZoom.cpp @@ -1,254 +1,246 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_ZOOM 1 - #include "kpToolZoom.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "generic/kpSetOverrideCursorSaver.h" #include "layers/tempImage/kpTempImage.h" #include "environments/tools/kpToolEnvironment.h" #include "tools/kpToolAction.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include #include //--------------------------------------------------------------------- struct DrawZoomRectPackage { QRect normalizedRect; }; static void DrawZoomRect (kpImage *destImage, const QPoint &topLeft, void *userData) { auto *pack = static_cast (userData); kpPixmapFX::drawStippleRect(destImage, topLeft.x (), topLeft.y (), pack->normalizedRect.width (), pack->normalizedRect.height (), kpColor::Yellow, kpColor::Green); } struct kpToolZoomPrivate { bool dragHasBegun{}, dragCompleted{}; DrawZoomRectPackage drawPackage; }; kpToolZoom::kpToolZoom (kpToolEnvironment *environ, QWidget *parent) : kpTool (i18n ("Zoom"), i18n ("Zooms in and out of the image"), Qt::Key_Z, environ, parent, QStringLiteral("tool_zoom")), d (new kpToolZoomPrivate ()) { // different from objectName() action()->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original"))); } //--------------------------------------------------------------------- kpToolZoom::~kpToolZoom () { delete d; } //--------------------------------------------------------------------- // public virtual [base kpTool] bool kpToolZoom::returnToPreviousToolAfterEndDraw () const { // If the user clicks to zoom in or out, s/he generally wants to click // some more to get the exact zoom level wanted. // // However, if they drag out a rectangle to zoom into a particular area, // they probably don't need to do any further zooming so we can return // them to their previous tool. // // Note that if they cancel a drag (cancelShape()), we do _not_ return // them to their previous tool, unlike the Color Picker. This is because // cancelling a drag generally means that the user got the top-left of // the drag wrong and wants to try a different top-left. In contrast, // with the Color Picket, if you've made a mistake while pressing the // mouse, you can just keep holding down the mouse and drag to the intended // color -- a cancel with a Color Picker really means "I've decided not // to pick another color after all", not "I got the start of the drag wrong" // because you can correct that drag. return d->dragCompleted; } // private QString kpToolZoom::haventBegunDrawUserMessage () const { return i18n ("Click to zoom in/out or left drag to zoom into a specific area."); } // public virtual [base kpTool] void kpToolZoom::begin () { viewManager ()->setCursor (Qt::CrossCursor); setUserMessage (haventBegunDrawUserMessage ()); } // public virtual [base kpTool] void kpToolZoom::end () { viewManager ()->unsetCursor (); } // public virtual [base kpTool] void kpToolZoom::globalDraw () { -#if DEBUG_KP_TOOL_ZOOM qCDebug(kpLogTools) << "CALL"; -#endif environ ()->fitToPage (); } // public virtual [base kpTool] void kpToolZoom::beginDraw () { d->dragHasBegun = false; d->dragCompleted = false; setUserMessage (cancelUserMessage ()); } // public virtual [base kpTool] void kpToolZoom::draw (const QPoint &thisPoint, const QPoint &, const QRect &normalizedRect) { -#if DEBUG_KP_TOOL_ZOOM qCDebug(kpLogTools) << "kpToomZoom::draw() currentPoint=" << currentPoint () << " lastPoint=" << lastPoint () << endl; -#endif // TODO: Need accidental drag detection from selection tool (when dragging // out new selection) if (!d->dragHasBegun) { if (thisPoint == startPoint ()) { return; } // Left mouse drags select an area to zoom into. // However, it wouldn't make sense to select an area to "zoom out of" // (using the right mouse button). Therefore, make RMB drags do the // same as RMB clicks i.e. a simple zoom out, with no "area" to worry // about. if (mouseButton () == 1/*RMB*/) { return; } d->dragHasBegun = true; } d->drawPackage.normalizedRect = normalizedRect; kpTempImage newTempImage (false/*always display*/, normalizedRect.topLeft (), &::DrawZoomRect, &d->drawPackage, normalizedRect.width (), normalizedRect.height ()); viewManager ()->setFastUpdates (); { viewManager ()->setTempImage (newTempImage); } viewManager ()->restoreFastUpdates (); } // public virtual [base kpTool] void kpToolZoom::cancelShape () { viewManager ()->invalidateTempImage (); // LOREFACTOR: A lot of tools use this - push up to kpTool? setUserMessage (i18n ("Let go of all the mouse buttons.")); } // public virtual [base kpTool] void kpToolZoom::releasedAllButtons () { setUserMessage (haventBegunDrawUserMessage ()); } // public virtual [base kpTool] void kpToolZoom::endDraw (const QPoint &, const QRect &normalizedRect) { -#if DEBUG_KP_TOOL_ZOOM qCDebug(kpLogTools) << "kpToolZoom::endDraw(rect=" << normalizedRect << ")" - << " dragHasBegun=" << d->dragHasBegun << endl; -#endif + << " dragHasBegun=" << d->dragHasBegun; // TODO: This cursor doesn't stay on for long enough because zooming uses // event loop tricks. kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor); viewManager ()->invalidateTempImage (); // Click? if (!d->dragHasBegun) { if (mouseButton () == 0/*LMB*/) { environ ()->zoomIn (true/*center under cursor*/); } else { environ ()->zoomOut (false/*don't center under cursor - as is confusing behaviour when zooming out*/); } } // Drag? else if (normalizedRect.isValid()) { environ ()->zoomToRect ( normalizedRect, false/*don't account for grips*/, true/*care about width*/, true/*care about height*/); d->dragCompleted = true; } } diff --git a/tools/kpTool_Drawing.cpp b/tools/kpTool_Drawing.cpp index 35f52cdf..243e2aa2 100644 --- a/tools/kpTool_Drawing.cpp +++ b/tools/kpTool_Drawing.cpp @@ -1,423 +1,405 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool methods for drawing shapes (subclasses reimplement most of these). // -#define DEBUG_KP_TOOL 1 - #include "tools/kpTool.h" #include "kpToolPrivate.h" #include #include "kpLogCategories.h" #include "environments/tools/kpToolEnvironment.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include "imagelib/kpPainter.h" #undef environ // macro on win32 //--------------------------------------------------------------------- // protected int kpTool::mouseButton () const { return d->mouseButton; } //--------------------------------------------------------------------- // protected bool kpTool::shiftPressed () const { return d->shiftPressed; } //--------------------------------------------------------------------- // protected bool kpTool::controlPressed () const { return d->controlPressed; } //--------------------------------------------------------------------- // protected bool kpTool::altPressed () const { return d->altPressed; } // protected QPoint kpTool::startPoint () const { return d->startPoint; } //--------------------------------------------------------------------- // protected QPoint kpTool::currentPoint () const { // TODO: Q_ASSERT (hasBegun()) and similar in other accessors. // We currently violate these kinds of invariants. return d->currentPoint; } //--------------------------------------------------------------------- // protected QPoint kpTool::currentViewPoint () const { return d->currentViewPoint; } //--------------------------------------------------------------------- // protected QRect kpTool::normalizedRect () const { return kpPainter::normalizedRect(d->startPoint, d->currentPoint); } //--------------------------------------------------------------------- // protected QPoint kpTool::lastPoint () const { return d->lastPoint; } //--------------------------------------------------------------------- // protected kpView *kpTool::viewUnderStartPoint () const { return d->viewUnderStartPoint; } //--------------------------------------------------------------------- // protected kpView *kpTool::viewUnderCursor () const { kpViewManager *vm = viewManager (); return vm ? vm->viewUnderCursor () : nullptr; } //--------------------------------------------------------------------- void kpTool::beginInternal () { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool::beginInternal()"; -#endif if (!d->began) { // clear leftover statusbar messages setUserMessage (); d->currentPoint = calculateCurrentPoint (); d->currentViewPoint = calculateCurrentPoint (false/*view point*/); setUserShapePoints (d->currentPoint); // TODO: Audit all the code in this file - states like "d->began" & // "d->beganDraw" should be set before calling user func. // Also, d->currentPoint should be more frequently initialised. // call user virtual func begin (); // we've starting using the tool... d->began = true; // but we haven't started drawing with it d->beganDraw = false; uint keyState = QApplication::keyboardModifiers (); d->shiftPressed = (keyState & Qt::ShiftModifier); d->controlPressed = (keyState & Qt::ControlModifier); // TODO: Can't do much about ALT - unless it's always KApplication::Modifier1? // Ditto for everywhere else where I set SHIFT & CTRL but not alt. // COMPAT: Later: This is now supported by Qt. d->altPressed = false; } } //--------------------------------------------------------------------- void kpTool::endInternal () { if (d->began) { // before we can stop using the tool, we must stop the current drawing operation (if any) if (hasBegunShape ()) { endShapeInternal (d->currentPoint, normalizedRect ()); } // call user virtual func end (); // clear leftover statusbar messages setUserMessage (); setUserShapePoints (calculateCurrentPoint ()); // we've stopped using the tool... d->began = false; // and so we can't be drawing with it d->beganDraw = false; d->environ->hideAllToolWidgets (); } } //--------------------------------------------------------------------- // virtual void kpTool::begin () { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool::begin() base implementation"; -#endif } //--------------------------------------------------------------------- // virtual void kpTool::end () { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool::end() base implementation"; -#endif } //--------------------------------------------------------------------- bool kpTool::hasBegun () const { return d->began; } //--------------------------------------------------------------------- bool kpTool::hasBegunDraw () const { return d->beganDraw; } //--------------------------------------------------------------------- // virtual bool kpTool::hasBegunShape () const { return hasBegunDraw (); } //--------------------------------------------------------------------- void kpTool::beginDrawInternal () { if (!d->beganDraw) { beginDraw (); d->beganDraw = true; emit beganDraw (d->currentPoint); } } //--------------------------------------------------------------------- // virtual void kpTool::beginDraw () { } //--------------------------------------------------------------------- // virtual void kpTool::hover (const QPoint &point) { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool::hover" << point << " base implementation"; -#endif setUserShapePoints (point); } //--------------------------------------------------------------------- // virtual void kpTool::globalDraw () { } //--------------------------------------------------------------------- // virtual void kpTool::reselect () { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool::reselect() base implementation"; -#endif } //--------------------------------------------------------------------- // virtual void kpTool::draw (const QPoint &, const QPoint &, const QRect &) { } //--------------------------------------------------------------------- // private void kpTool::drawInternal () { draw (d->currentPoint, d->lastPoint, normalizedRect ()); } //--------------------------------------------------------------------- // also called by kpView void kpTool::cancelShapeInternal () { if (hasBegunShape ()) { d->beganDraw = false; cancelShape (); d->viewUnderStartPoint = nullptr; emit cancelledShape (viewUnderCursor () ? d->currentPoint : KP_INVALID_POINT); if (viewUnderCursor ()) { hover (d->currentPoint); } else { d->currentPoint = KP_INVALID_POINT; d->currentViewPoint = KP_INVALID_POINT; hover (d->currentPoint); } if (returnToPreviousToolAfterEndDraw ()) { d->environ->selectPreviousTool (); } } } //--------------------------------------------------------------------- // virtual void kpTool::cancelShape () { qCWarning(kpLogTools) << "Tool cannot cancel operation!" ; } //--------------------------------------------------------------------- void kpTool::releasedAllButtons () { } //--------------------------------------------------------------------- void kpTool::endDrawInternal (const QPoint &thisPoint, const QRect &normalizedRect, bool wantEndShape) { -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::endDrawInternal() wantEndShape=" << wantEndShape; -#endif if (wantEndShape && !hasBegunShape ()) { return; } if (!wantEndShape && !hasBegunDraw ()) { return; } d->beganDraw = false; if (wantEndShape) { - #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "\tcalling endShape()"; - #endif endShape (thisPoint, normalizedRect); } else { - #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "\tcalling endDraw()"; - #endif endDraw (thisPoint, normalizedRect); } d->viewUnderStartPoint = nullptr; emit endedDraw (d->currentPoint); if (viewUnderCursor ()) { hover (d->currentPoint); } else { d->currentPoint = KP_INVALID_POINT; d->currentViewPoint = KP_INVALID_POINT; hover (d->currentPoint); } if (returnToPreviousToolAfterEndDraw ()) { d->environ->selectPreviousTool (); } } //--------------------------------------------------------------------- // private void kpTool::endShapeInternal (const QPoint &thisPoint, const QRect &normalizedRect) { endDrawInternal (thisPoint, normalizedRect, true/*end shape*/); } //--------------------------------------------------------------------- // virtual void kpTool::endDraw (const QPoint &, const QRect &) { } //--------------------------------------------------------------------- diff --git a/tools/kpTool_KeyboardEvents.cpp b/tools/kpTool_KeyboardEvents.cpp index 9cf521cd..e0121445 100644 --- a/tools/kpTool_KeyboardEvents.cpp +++ b/tools/kpTool_KeyboardEvents.cpp @@ -1,431 +1,429 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool reaction to view keyboard input. // -#define DEBUG_KP_TOOL 1 - // TODO: reduce number of includes #include "tools/kpTool.h" #include "kpToolPrivate.h" #include #include #include #include #include #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpToolAction.h" #include "environments/tools/kpToolEnvironment.h" #include "widgets/toolbars/kpToolToolBar.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" //--------------------------------------------------------------------- void kpTool::seeIfAndHandleModifierKey (QKeyEvent *e) { switch (e->key ()) { case 0: case Qt::Key_unknown: - #if DEBUG_KP_TOOL && 0 + #if 0 qCDebug(kpLogTools) << "kpTool::seeIfAndHandleModifierKey() picked up unknown key!"; #endif // HACK: around Qt bug: if you hold a modifier before you start the // program and then release it over the view, // Qt reports it as the release of an unknown key // Qt4 update: I don't think this happens anymore... // --- fall thru and update all modifiers --- case Qt::Key_Alt: case Qt::Key_Shift: case Qt::Key_Control: - #if DEBUG_KP_TOOL && 0 + #if 0 qCDebug(kpLogTools) << "kpTool::setIfAndHandleModifierKey() accepting"; #endif keyUpdateModifierState (e); e->accept (); break; } } //--------------------------------------------------------------------- // Returns in and the direction the arrow key "e->key()" is // pointing in or (0,0) if it's not a recognised arrow key. void kpTool::arrowKeyPressDirection (const QKeyEvent *e, int *dx, int *dy) { int dxLocal = 0, dyLocal = 0; switch (e->key ()) { case Qt::Key_Home: dxLocal = -1; dyLocal = -1; break; case Qt::Key_Up: dyLocal = -1; break; case Qt::Key_PageUp: dxLocal = +1; dyLocal = -1; break; case Qt::Key_Left: dxLocal = -1; break; case Qt::Key_Right: dxLocal = +1; break; case Qt::Key_End: dxLocal = -1; dyLocal = +1; break; case Qt::Key_Down: dyLocal = +1; break; case Qt::Key_PageDown: dxLocal = +1; dyLocal = +1; break; } if (dx) { *dx = dxLocal; } if (dy) { *dy = dyLocal; } } //--------------------------------------------------------------------- void kpTool::seeIfAndHandleArrowKeyPress (QKeyEvent *e) { int dx, dy; arrowKeyPressDirection (e, &dx, &dy); if (dx == 0 && dy == 0) { return; } kpView * const view = viewUnderCursor (); if (!view) { return; } const QPoint oldPoint = view->mapFromGlobal (QCursor::pos ()); -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "\toldPoint=" << oldPoint << " dx=" << dx << " dy=" << dy << endl; #endif const int viewIncX = (dx ? qMax (1, view->zoomLevelX () / 100) * dx : 0); const int viewIncY = (dy ? qMax (1, view->zoomLevelY () / 100) * dy : 0); int newViewX = oldPoint.x () + viewIncX; int newViewY = oldPoint.y () + viewIncY; -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "\tnewPoint=" << QPoint (newViewX, newViewY); #endif // Make sure we really moved at least one doc point (needed due to // rounding error). if (view->transformViewToDoc (QPoint (newViewX, newViewY)) == view->transformViewToDoc (oldPoint)) { newViewX += viewIncX; newViewY += viewIncY; - #if DEBUG_KP_TOOL && 0 + #if 0 qCDebug(kpLogTools) << "\tneed adjust for doc - newPoint=" << QPoint (newViewX, newViewY) << endl; #endif } // TODO: visible width/height (e.g. with scrollbars) const int x = qMin (qMax (newViewX, 0), view->width () - 1); const int y = qMin (qMax (newViewY, 0), view->height () - 1); // QCursor::setPos conveniently causes mouseMoveEvents QCursor::setPos (view->mapToGlobal (QPoint (x, y))); e->accept (); } //--------------------------------------------------------------------- bool kpTool::isDrawKey (int key) { return (key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Insert || key == Qt::Key_Clear/*Numpad 5 Key*/ || key == Qt::Key_L); } //--------------------------------------------------------------------- void kpTool::seeIfAndHandleBeginDrawKeyPress (QKeyEvent *e) { if (e->isAutoRepeat ()) { return; } if (!isDrawKey (e->key ())) { return; } -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::seeIfAndHandleBeginDrawKeyPress() accept"; #endif // TODO: wrong for dragging lines outside of view (for e.g.) kpView * const view = viewUnderCursor (); if (!view) { return; } // TODO: what about the modifiers? QMouseEvent me (QEvent::MouseButtonPress, view->mapFromGlobal (QCursor::pos ()), Qt::LeftButton, Qt::LeftButton/*button state after event*/, Qt::NoModifier); mousePressEvent (&me); e->accept (); } void kpTool::seeIfAndHandleEndDrawKeyPress (QKeyEvent *e) { -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::setIfAndHandleEndDrawKeyPress() key=" << e->key () << " isAutoRepeat=" << e->isAutoRepeat () << " isDrawKey=" << isDrawKey (e->key ()) << " view=" << viewUnderCursor () << endl; #endif if (e->isAutoRepeat ()) { return; } if (!isDrawKey (e->key ())) { return; } -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::seeIfAndHandleEndDrawKeyPress() accept"; #endif kpView * const view = viewUnderCursor (); if (!view) { return; } // TODO: what about the modifiers? QMouseEvent me (QEvent::MouseButtonRelease, view->mapFromGlobal (QCursor::pos ()), Qt::LeftButton, Qt::NoButton/*button state after event*/, Qt::NoModifier); mouseReleaseEvent (&me); e->accept (); } //--------------------------------------------------------------------- void kpTool::keyPressEvent (QKeyEvent *e) { -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::keyPressEvent() key=" << (int *) e->key () << " stateAfter: modifiers=" << (int *) (int) e->modifiers () << " isAutoRep=" << e->isAutoRepeat (); #endif e->ignore (); seeIfAndHandleModifierKey (e); if (e->isAccepted ()) { return; } seeIfAndHandleArrowKeyPress (e); if (e->isAccepted ()) { return; } seeIfAndHandleBeginDrawKeyPress (e); if (e->isAccepted ()) { return; } switch (e->key ()) { case Qt::Key_Delete: d->environ->deleteSelection (); break; case Qt::Key_Escape: if (hasBegunDraw ()) { cancelShapeInternal (); e->accept (); } break; } } //--------------------------------------------------------------------- void kpTool::keyReleaseEvent (QKeyEvent *e) { -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::keyReleaseEvent() key=" << (int *) e->key () << " stateAfter: modifiers=" << (int *) (int) e->modifiers () << " isAutoRep=" << e->isAutoRepeat (); #endif e->ignore (); seeIfAndHandleModifierKey (e); if (e->isAccepted ()) { return; } seeIfAndHandleEndDrawKeyPress (e); if (e->isAccepted ()) { return; } } //--------------------------------------------------------------------- // private void kpTool::keyUpdateModifierState (QKeyEvent *e) { -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::keyUpdateModifierState() e->key=" << (int *) e->key (); qCDebug(kpLogTools) << "\tshift=" << (e->modifiers () & Qt::ShiftModifier) << " control=" << (e->modifiers () & Qt::ControlModifier) << " alt=" << (e->modifiers () & Qt::AltModifier) << endl; #endif if (e->key () & (Qt::Key_Alt | Qt::Key_Shift | Qt::Key_Control)) { - #if DEBUG_KP_TOOL && 0 + #if 0 qCDebug(kpLogTools) << "\t\tmodifier changed - use e's claims"; #endif setShiftPressed (e->modifiers () & Qt::ShiftModifier); setControlPressed (e->modifiers () & Qt::ControlModifier); setAltPressed (e->modifiers () & Qt::AltModifier); } // See seeIfAndHandleModifierKey() for why this code path exists. else { - #if DEBUG_KP_TOOL && 0 + #if 0 qCDebug(kpLogTools) << "\t\tmodifiers not changed - figure out the truth"; #endif const Qt::KeyboardModifiers keyState = QApplication::keyboardModifiers (); setShiftPressed (keyState & Qt::ShiftModifier); setControlPressed (keyState & Qt::ControlModifier); setAltPressed (keyState & Qt::AltModifier); } } //--------------------------------------------------------------------- void kpTool::notifyModifierStateChanged () { if (careAboutModifierState ()) { if (d->beganDraw) { draw (d->currentPoint, d->lastPoint, normalizedRect ()); } else { d->currentPoint = calculateCurrentPoint (); d->currentViewPoint = calculateCurrentPoint (false/*view point*/); hover (d->currentPoint); } } } //--------------------------------------------------------------------- void kpTool::setShiftPressed (bool pressed) { if (pressed == d->shiftPressed) { return; } d->shiftPressed = pressed; notifyModifierStateChanged (); } //--------------------------------------------------------------------- void kpTool::setControlPressed (bool pressed) { if (pressed == d->controlPressed) { return; } d->controlPressed = pressed; notifyModifierStateChanged (); } //--------------------------------------------------------------------- void kpTool::setAltPressed (bool pressed) { if (pressed == d->altPressed) { return; } d->altPressed = pressed; notifyModifierStateChanged (); } //--------------------------------------------------------------------- diff --git a/tools/kpTool_MouseEvents.cpp b/tools/kpTool_MouseEvents.cpp index cd7abc68..2f73de37 100644 --- a/tools/kpTool_MouseEvents.cpp +++ b/tools/kpTool_MouseEvents.cpp @@ -1,337 +1,309 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool reaction to view mouse input. // -#define DEBUG_KP_TOOL 1 - #include "tools/kpTool.h" #include "kpToolPrivate.h" #include "kpLogCategories.h" #include "environments/tools/kpToolEnvironment.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include #include #include //--------------------------------------------------------------------- // HITODO: If you press a mouse button and move it out of the view _really_ fast // and let go of the mouse button outside of the view, a mouseRelease // event will not be generated, so the tool will still be in drawing mode // (this is especially noticeable with the spraycan). // // When you move the mouse back into the view, it will still continue // continue drawing even though no mouse button is held down. // // It is somewhat hard to reproduce so the best way is to position the // mouse close to an edge of the view. If you do it right, no mouseMoveEvent // is generated at _all_, until you move it back into the view. void kpTool::mousePressEvent (QMouseEvent *e) { -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::mousePressEvent pos=" << e->pos () << " button=" << (int) e->button () << " stateAfter: buttons=" << (int *) (int) e->buttons () << " modifiers=" << (int *) (int) e->modifiers () << " beganDraw=" << d->beganDraw << endl; -#endif if (e->button () == Qt::MidButton) { const QString text = QApplication::clipboard ()->text (QClipboard::Selection); - #if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "\tMMB pasteText='" << text << "'"; - #endif if (!text.isEmpty ()) { if (hasBegunShape ()) { - #if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "\t\thasBegunShape - end"; - #endif endShapeInternal (d->currentPoint, normalizedRect ()); } if (viewUnderCursor ()) { d->environ->pasteTextAt (text, viewUnderCursor ()->transformViewToDoc (e->pos ()), true/*adjust topLeft so that cursor isn't on top of resize handle*/); } return; } } int mb = mouseButton (e->buttons ()); -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "\tmb=" << mb << " d->beganDraw=" << d->beganDraw; -#endif if (mb == -1 && !d->beganDraw) { // Ignore mouse press. return; } if (d->beganDraw) { if (mb == -1 || mb != d->mouseButton) { - #if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "\tCancelling operation as " << mb << " == -1 or != " << d->mouseButton; - #endif kpView *view = viewUnderStartPoint (); Q_ASSERT (view); // if we get a mousePressEvent when we're drawing, then the other // mouse button must have been pressed d->currentPoint = view->transformViewToDoc (e->pos ()); d->currentViewPoint = e->pos (); cancelShapeInternal (); } return; } kpView *view = viewUnderCursor (); Q_ASSERT (view); -#if DEBUG_KP_TOOL && 1 if (view) { qCDebug(kpLogTools) << "\tview=" << view->objectName (); } -#endif // let user know what mouse button is being used for entire draw d->mouseButton = mouseButton (e->buttons ()); d->shiftPressed = (e->modifiers () & Qt::ShiftModifier); d->controlPressed = (e->modifiers () & Qt::ControlModifier); d->altPressed = (e->modifiers () & Qt::AltModifier); d->startPoint = d->currentPoint = view->transformViewToDoc (e->pos ()); d->currentViewPoint = e->pos (); d->viewUnderStartPoint = view; d->lastPoint = QPoint (-1, -1); -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "\tBeginning draw @ " << d->currentPoint; -#endif beginDrawInternal (); draw (d->currentPoint, d->lastPoint, QRect (d->currentPoint, d->currentPoint)); d->lastPoint = d->currentPoint; } //--------------------------------------------------------------------- // OPT: If the mouse is moving in terms of view pixels, it still might // not be moving in terms of document pixels (when zoomed in). // // So we should detect this and not call draw() or hover(). // // However, kpToolSelection needs hover() to be called on all view // point changes, not just document points, since the selection resize // handles may be smaller than document points. Also, I wonder if // selections' accidental drag detection feature cares? void kpTool::mouseMoveEvent (QMouseEvent *e) { -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::mouseMoveEvent pos=" << e->pos () << " stateAfter: buttons=" << (int *) (int) e->buttons () << " modifiers=" << (int *) (int) e->modifiers (); kpView *v0 = viewUnderCursor (), *v1 = viewManager ()->viewUnderCursor (true/*use Qt*/), *v2 = viewUnderStartPoint (); qCDebug(kpLogTools) << "\tviewUnderCursor=" << (v0 ? v0->objectName () : "(none)") << " viewUnderCursorQt=" << (v1 ? v1->objectName () : "(none)") << " viewUnderStartPoint=" << (v2 ? v2->objectName () : "(none)"); qCDebug(kpLogTools) << "\tfocusWidget=" << kapp->focusWidget (); qCDebug(kpLogTools) << "\tbeganDraw=" << d->beganDraw; #endif d->shiftPressed = (e->modifiers () & Qt::ShiftModifier); d->controlPressed = (e->modifiers () & Qt::ControlModifier); d->altPressed = (e->modifiers () & Qt::AltModifier); if (d->beganDraw) { kpView *view = viewUnderStartPoint (); Q_ASSERT (view); d->currentPoint = view->transformViewToDoc (e->pos ()); d->currentViewPoint = e->pos (); - #if DEBUG_KP_TOOL && 0 + #if 0 qCDebug(kpLogTools) << "\tDraw!"; #endif bool dragScrolled = false; movedAndAboutToDraw (d->currentPoint, d->lastPoint, view->zoomLevelX (), &dragScrolled); if (dragScrolled) { d->currentPoint = calculateCurrentPoint (); d->currentViewPoint = calculateCurrentPoint (false/*view point*/); // Scrollview has scrolled contents and has scheduled an update // for the newly exposed region. If draw() schedules an update // as well (instead of immediately updating), the scrollview's // update will be executed first and it'll only update part of // the screen resulting in ugly tearing of the viewManager's // tempImage. viewManager ()->setFastUpdates (); } drawInternal (); if (dragScrolled) { viewManager ()->restoreFastUpdates (); } d->lastPoint = d->currentPoint; } else { kpView *view = viewUnderCursor (); if (!view) // possible if cancelShape()'ed but still holding down initial mousebtn { d->currentPoint = KP_INVALID_POINT; d->currentViewPoint = KP_INVALID_POINT; return; } d->currentPoint = view->transformViewToDoc (e->pos ()); d->currentViewPoint = e->pos (); hover (d->currentPoint); } } //--------------------------------------------------------------------- void kpTool::mouseReleaseEvent (QMouseEvent *e) { -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::mouseReleaseEvent pos=" << e->pos () << " button=" << (int) e->button () << " stateAfter: buttons=" << (int *) (int) e->buttons () << " modifiers=" << (int *) (int) e->modifiers () << " beganDraw=" << d->beganDraw; -#endif // Have _not_ already cancelShape()'ed by pressing other mouse button? // (e.g. you can cancel a line dragged out with the LMB, by pressing // the RMB) if (d->beganDraw) { kpView *view = viewUnderStartPoint (); Q_ASSERT (view); d->currentPoint = view->transformViewToDoc (e->pos ()); d->currentViewPoint = e->pos (); drawInternal (); endDrawInternal (d->currentPoint, normalizedRect ()); } if ((e->buttons () & Qt::MouseButtonMask) == 0) { releasedAllButtons (); } } //--------------------------------------------------------------------- void kpTool::wheelEvent (QWheelEvent *e) { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool::wheelEvent() modifiers=" << (int *) (int) e->modifiers () << " hasBegunDraw=" << hasBegunDraw () << " delta=" << e->delta (); -#endif e->ignore (); // If CTRL not pressed, bye. if ((e->modifiers () & Qt::ControlModifier) == 0) { - #if DEBUG_KP_TOOL qCDebug(kpLogTools) << "\tno CTRL -> bye"; - #endif return; } // If drawing, bye; don't care if a shape in progress though. if (hasBegunDraw ()) { - #if DEBUG_KP_TOOL qCDebug(kpLogTools) << "\thasBegunDraw() -> bye"; - #endif return; } // Zoom in/out depending on wheel direction. // Moved wheel away from user? if (e->delta () > 0) { - #if DEBUG_KP_TOOL qCDebug(kpLogTools) << "\tzoom in"; - #endif d->environ->zoomIn (true/*center under cursor*/); e->accept (); } // Moved wheel towards user? else if (e->delta () < 0) { - #if DEBUG_KP_TOOL qCDebug(kpLogTools) << "\tzoom out"; - #endif #if 1 d->environ->zoomOut (true/*center under cursor - make zoom in/out stay under same doc pos*/); #else d->environ->zoomOut (false/*don't center under cursor - as is confusing behaviour when zooming out*/); #endif e->accept (); } } //--------------------------------------------------------------------- diff --git a/tools/kpTool_OtherEvents.cpp b/tools/kpTool_OtherEvents.cpp index 7217d6ae..83d896b3 100644 --- a/tools/kpTool_OtherEvents.cpp +++ b/tools/kpTool_OtherEvents.cpp @@ -1,170 +1,158 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool reaction to all remaining events. // // 1. View events // 2. Non-view events // -#define DEBUG_KP_TOOL 1 - #include "tools/kpTool.h" #include "kpToolPrivate.h" #include "kpLogCategories.h" #include "imagelib/kpColor.h" #include //--------------------------------------------------------------------- // // 1. View Events // bool kpTool::viewEvent (QEvent *e) { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool<" << objectName () << "," << this << ">::viewEvent(type=" << e->type () << ") returning false" << endl; -#else - (void) e; -#endif // Don't handle. return false; } //--------------------------------------------------------------------- void kpTool::focusInEvent (QFocusEvent *) { } //--------------------------------------------------------------------- void kpTool::focusOutEvent (QFocusEvent *) { -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::focusOutEvent() beganDraw=" << d->beganDraw; #endif if (d->beganDraw) { endDrawInternal (d->currentPoint, normalizedRect ()); } } //--------------------------------------------------------------------- void kpTool::enterEvent (QEvent *) { -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::enterEvent() beganDraw=" << d->beganDraw; -#endif } //--------------------------------------------------------------------- void kpTool::leaveEvent (QEvent *) { -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::leaveEvent() beganDraw=" << d->beganDraw; -#endif // if we haven't started drawing (e.g. dragging a rectangle)... if (!d->beganDraw) { d->currentPoint = KP_INVALID_POINT; d->currentViewPoint = KP_INVALID_POINT; hover (d->currentPoint); } } //--------------------------------------------------------------------- // // 2. Non-view events // void kpTool::slotColorsSwappedInternal (const kpColor &newForegroundColor, const kpColor &newBackgroundColor) { if (careAboutColorsSwapped ()) { slotColorsSwapped (newForegroundColor, newBackgroundColor); d->ignoreColorSignals = 2; } else { d->ignoreColorSignals = 0; } } //--------------------------------------------------------------------- void kpTool::slotForegroundColorChangedInternal (const kpColor &color) { if (d->ignoreColorSignals > 0) { - #if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::slotForegroundColorChangedInternal() ignoreColorSignals=" << d->ignoreColorSignals; - #endif + d->ignoreColorSignals--; return; } slotForegroundColorChanged (color); } //--------------------------------------------------------------------- void kpTool::slotBackgroundColorChangedInternal (const kpColor &color) { if (d->ignoreColorSignals > 0) { - #if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::slotBackgroundColorChangedInternal() ignoreColorSignals=" << d->ignoreColorSignals; - #endif + d->ignoreColorSignals--; return; } slotBackgroundColorChanged (color); } //--------------------------------------------------------------------- void kpTool::slotColorSimilarityChangedInternal (double similarity, int processedSimilarity) { slotColorSimilarityChanged (similarity, processedSimilarity); } //--------------------------------------------------------------------- diff --git a/tools/kpTool_Utilities.cpp b/tools/kpTool_Utilities.cpp index b85100a3..a39b38eb 100644 --- a/tools/kpTool_Utilities.cpp +++ b/tools/kpTool_Utilities.cpp @@ -1,294 +1,287 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool utility methods - mainly for subclasses' convenience. // -#define DEBUG_KP_TOOL 1 - #include "tools/kpTool.h" #include "kpToolPrivate.h" #include #include #include "kpLogCategories.h" #include #include "commands/kpCommandSize.h" #include "kpDefs.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "views/kpView.h" //--------------------------------------------------------------------- // static QRect kpTool::neededRect (const QRect &rect, int lineWidth) { int x1, y1, x2, y2; rect.getCoords (&x1, &y1, &x2, &y2); if (lineWidth < 1) { lineWidth = 1; } // TODO: why not divide by 2? return QRect (QPoint (x1 - lineWidth + 1, y1 - lineWidth + 1), QPoint (x2 + lineWidth - 1, y2 + lineWidth - 1)); } //--------------------------------------------------------------------- // static QImage kpTool::neededPixmap (const QImage &image, const QRect &boundingRect) { return kpPixmapFX::getPixmapAt (image, boundingRect); } //--------------------------------------------------------------------- // public bool kpTool::hasCurrentPoint () const { return (viewUnderStartPoint () || viewUnderCursor ()); } //--------------------------------------------------------------------- // public QPoint kpTool::calculateCurrentPoint (bool zoomToDoc) const { -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "kpTool::currentPoint(zoomToDoc=" << zoomToDoc << ")"; qCDebug(kpLogTools) << "\tviewUnderStartPoint=" << (viewUnderStartPoint () ? viewUnderStartPoint ()->objectName () : "(none)") << " viewUnderCursor=" << (viewUnderCursor () ? viewUnderCursor ()->objectName () : "(none)") << endl; #endif kpView *v = viewUnderStartPoint (); if (!v) { v = viewUnderCursor (); if (!v) { - #if DEBUG_KP_TOOL && 0 + #if 0 qCDebug(kpLogTools) << "\tno view - returning sentinel"; #endif return KP_INVALID_POINT; } } const QPoint globalPos = QCursor::pos (); const QPoint viewPos = v->mapFromGlobal (globalPos); -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "\tglobalPos=" << globalPos << " viewPos=" << viewPos; #endif if (!zoomToDoc) { return viewPos; } const QPoint docPos = v->transformViewToDoc (viewPos); -#if DEBUG_KP_TOOL && 0 +#if 0 qCDebug(kpLogTools) << "\tdocPos=" << docPos; #endif return docPos; } //--------------------------------------------------------------------- // public slot void kpTool::somethingBelowTheCursorChanged () { somethingBelowTheCursorChanged (calculateCurrentPoint (), calculateCurrentPoint (false/*view point*/)); } //--------------------------------------------------------------------- // private // TODO: don't dup code from mouseMoveEvent() void kpTool::somethingBelowTheCursorChanged (const QPoint ¤tPoint_, const QPoint ¤tViewPoint_) { -#if DEBUG_KP_TOOL && 1 qCDebug(kpLogTools) << "kpTool::somethingBelowTheCursorChanged(docPoint=" << currentPoint_ << " viewPoint=" << currentViewPoint_ << ")" << endl; qCDebug(kpLogTools) << "\tviewUnderStartPoint=" << (viewUnderStartPoint () ? viewUnderStartPoint ()->objectName () : "(none)") << " viewUnderCursor=" << (viewUnderCursor () ? viewUnderCursor ()->objectName () : "(none)") << endl; qCDebug(kpLogTools) << "\tbegan draw=" << d->beganDraw; -#endif d->currentPoint = currentPoint_; d->currentViewPoint = currentViewPoint_; if (d->beganDraw) { if (d->currentPoint != KP_INVALID_POINT) { draw (d->currentPoint, d->lastPoint, normalizedRect ()); d->lastPoint = d->currentPoint; } } else { hover (d->currentPoint); } } //--------------------------------------------------------------------- bool kpTool::currentPointNextToLast () const { if (d->lastPoint == QPoint (-1, -1)) { return true; } int dx = qAbs (d->currentPoint.x () - d->lastPoint.x ()); int dy = qAbs (d->currentPoint.y () - d->lastPoint.y ()); return (dx <= 1 && dy <= 1); } //--------------------------------------------------------------------- bool kpTool::currentPointCardinallyNextToLast () const { if (d->lastPoint == QPoint (-1, -1)) { return true; } return (d->currentPoint == d->lastPoint || kpPainter::pointsAreCardinallyAdjacent (d->currentPoint, d->lastPoint)); } //--------------------------------------------------------------------- // static // TODO: we don't handle Qt::XButton1 and Qt::XButton2 at the moment. int kpTool::mouseButton (Qt::MouseButtons mouseButtons) { // we have nothing to do with mid-buttons if (mouseButtons & Qt::MidButton) { return -1; } // both left & right together is quite meaningless... const Qt::MouseButtons bothButtons = (Qt::LeftButton | Qt::RightButton); if ((mouseButtons & bothButtons) == bothButtons) { return -1; } if (mouseButtons & Qt::LeftButton) { return 0; } if (mouseButtons & Qt::RightButton) { return 1; } return -1; } //--------------------------------------------------------------------- // public static int kpTool::calculateLength (int start, int end) { if (start <= end) { return end - start + 1; } return end - start - 1; } //--------------------------------------------------------------------- // public static bool kpTool::warnIfBigImageSize (int oldWidth, int oldHeight, int newWidth, int newHeight, const QString &text, const QString &caption, const QString &continueButtonText, QWidget *parent) { -#if DEBUG_KP_TOOL qCDebug(kpLogTools) << "kpTool::warnIfBigImageSize()" << " old: w=" << oldWidth << " h=" << oldWidth << " new: w=" << newWidth << " h=" << newHeight << " pixmapSize=" << kpCommandSize::PixmapSize (newWidth, newHeight, QPixmap::defaultDepth ()) - << " vs BigImageSize=" << KP_BIG_IMAGE_SIZE - << endl; -#endif + << " vs BigImageSize=" << KP_BIG_IMAGE_SIZE; // Only got smaller or unchanged - don't complain if (!(newWidth > oldWidth || newHeight > oldHeight)) { return true; } // Was already large - user was warned before, don't annoy him/her again if (kpCommandSize::PixmapSize (oldWidth, oldHeight, QPixmap::defaultDepth ()) >= KP_BIG_IMAGE_SIZE) { return true; } if (kpCommandSize::PixmapSize (newWidth, newHeight, QPixmap::defaultDepth ()) >= KP_BIG_IMAGE_SIZE) { int accept = KMessageBox::warningContinueCancel (parent, text, caption, KGuiItem (continueButtonText), KStandardGuiItem::cancel(), QStringLiteral ("BigImageDontAskAgain")); return (accept == KMessageBox::Continue); } return true; } //--------------------------------------------------------------------- diff --git a/tools/polygonal/kpToolCurve.cpp b/tools/polygonal/kpToolCurve.cpp index 779f4807..529868b4 100644 --- a/tools/polygonal/kpToolCurve.cpp +++ b/tools/polygonal/kpToolCurve.cpp @@ -1,196 +1,191 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2017 Martin Koller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_CURVE 1 - #include "kpToolCurve.h" #include "kpLogCategories.h" #include "environments/tools/kpToolEnvironment.h" #include "pixmapfx/kpPixmapFX.h" #include #include #include #include //-------------------------------------------------------------------------------- static void DrawCurveShape (kpImage *image, const QPolygon &points, const kpColor &fcolor, int penWidth, const kpColor &bcolor, bool isFinal) { (void) bcolor; (void) isFinal; Q_ASSERT (points.count () >= 2 && points.count () <= 4); const QPoint startPoint = points [0]; const QPoint endPoint = points [1]; QPoint controlPointP, controlPointQ; switch (points.count ()) { // Just a line? case 2: controlPointP = startPoint; controlPointQ = endPoint; break; // Single control point? case 3: controlPointP = controlPointQ = points [2]; break; // Two control points? case 4: controlPointP = points [2]; controlPointQ = points [3]; break; } QPainter painter(image); painter.setRenderHint(QPainter::Antialiasing, kpToolEnvironment::drawAntiAliased); painter.setPen(QPen(fcolor.toQColor(), penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); if ( kpPixmapFX::Only1PixelInPointArray(points) ) { painter.drawPoint(points[0]); return; } QPainterPath curvePath; curvePath.moveTo(startPoint); curvePath.cubicTo(controlPointP, controlPointQ, endPoint); painter.strokePath(curvePath, painter.pen()); } //-------------------------------------------------------------------------------- kpToolCurve::kpToolCurve (kpToolEnvironment *environ, QObject *parent) : kpToolPolygonalBase ( i18n ("Curve"), i18n ("Draws curves"), &::DrawCurveShape, Qt::Key_V, environ, parent, QStringLiteral("tool_curve")) { } kpToolCurve::~kpToolCurve () = default; // protected virtual [base kpToolPolygonalBase] QString kpToolCurve::haventBegunShapeUserMessage () const { return i18n ("Drag out the start and end points."); } // protected virtual [base kpToolPolygonalBase] bool kpToolCurve::drawingALine () const { // On the initial drag (consisting of 2 points) creates a line. // Future drags are for control points. return (points ()->count () == 2); } // public virtual [base kpTool] void kpToolCurve::endDraw (const QPoint &, const QRect &) { -#if DEBUG_KP_TOOL_CURVE qCDebug(kpLogTools) << "kpToolCurve::endDraw() points=" << points ()->toList (); -#endif switch (points ()->count ()) { // A click of the other mouse button (to finish shape, instead of adding // another control point) would have caused endShape() to have been // called in kpToolPolygonalBase::beginDraw(). The points list would now // be empty. We are being called by kpTool::mouseReleaseEvent(). case 0: break; case 1: Q_ASSERT (!"kpToolPolygonalBase::beginDraw() ensures we have >= 2 ctrl points"); break; // Just completed initial line? case 2: if (originatingMouseButton () == 0) { setUserMessage ( i18n ("Left drag to set the first control point or right click to finish.")); } else { setUserMessage ( i18n ("Right drag to set the first control point or left click to finish.")); } break; // Have initial line and first control point? case 3: if (originatingMouseButton () == 0) { setUserMessage ( i18n ("Left drag to set the last control point or right click to finish.")); } else { setUserMessage ( i18n ("Right drag to set the last control point or left click to finish.")); } break; // Have initial line and both control points? case 4: - #if DEBUG_KP_TOOL_CURVE qCDebug(kpLogTools) << "\tending shape"; - #endif + endShape (); break; default: Q_ASSERT (!"Impossible number of points"); break; } } diff --git a/tools/polygonal/kpToolLine.cpp b/tools/polygonal/kpToolLine.cpp index 5166a404..66f2313f 100644 --- a/tools/polygonal/kpToolLine.cpp +++ b/tools/polygonal/kpToolLine.cpp @@ -1,73 +1,69 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_LINE 1 - #include "kpToolLine.h" #include "kpToolPolyline.h" #include "kpLogCategories.h" #include //-------------------------------------------------------------------------------- kpToolLine::kpToolLine (kpToolEnvironment *environ, QObject *parent) : kpToolPolygonalBase ( i18n ("Line"), i18n ("Draws lines"), &kpToolPolyline::drawShape, Qt::Key_L, environ, parent, QStringLiteral("tool_line")) { } //-------------------------------------------------------------------------------- // private virtual [base kpToolPolygonalBase] QString kpToolLine::haventBegunShapeUserMessage () const { return i18n ("Drag to draw."); } //-------------------------------------------------------------------------------- // public virtual [base kpTool] void kpToolLine::endDraw (const QPoint &, const QRect &) { -#if DEBUG_KP_TOOL_LINE qCDebug(kpLogTools) << "kpToolLine::endDraw() points=" << points ()->toList () << endl; -#endif // After the first drag, we should have a line. Q_ASSERT (points ()->count () == 2); endShape (); } //-------------------------------------------------------------------------------- diff --git a/tools/polygonal/kpToolPolygon.cpp b/tools/polygonal/kpToolPolygon.cpp index 0494c320..3fc04a7a 100644 --- a/tools/polygonal/kpToolPolygon.cpp +++ b/tools/polygonal/kpToolPolygon.cpp @@ -1,191 +1,186 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2017 Martin Koller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_POLYGON 1 #include "kpToolPolygon.h" #include "widgets/toolbars/kpToolToolBar.h" #include "environments/tools/kpToolEnvironment.h" #include "imagelib/kpColor.h" #include "pixmapfx/kpPixmapFX.h" #include "kpLogCategories.h" #include #include #include //-------------------------------------------------------------------------------- static void DrawPolygonShape (kpImage *image, const QPolygon &points, const kpColor &fcolor, int penWidth, const kpColor &bcolor, bool isFinal) { QPainter painter(image); painter.setRenderHint(QPainter::Antialiasing, kpToolEnvironment::drawAntiAliased); painter.setPen(QPen(fcolor.toQColor(), penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); if ( kpPixmapFX::Only1PixelInPointArray(points) ) { painter.drawPoint(points[0]); return; } if ( bcolor.isValid() ) { painter.setBrush(QBrush(bcolor.toQColor())); } else { painter.setBrush(Qt::NoBrush); } painter.drawPolygon(points, Qt::OddEvenFill); if ( isFinal ) { return; } if ( points.count() <= 2 ) { return; } painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination); painter.setPen(QPen(Qt::white)); painter.drawLine(points[0], points[points.count() - 1]); } //-------------------------------------------------------------------------------- struct kpToolPolygonPrivate { kpToolWidgetFillStyle *toolWidgetFillStyle; }; kpToolPolygon::kpToolPolygon (kpToolEnvironment *environ, QObject *parent) : kpToolPolygonalBase ( i18n ("Polygon"), i18n ("Draws polygons"), &::DrawPolygonShape, Qt::Key_G, environ, parent, QStringLiteral("tool_polygon")), d (new kpToolPolygonPrivate ()) { } kpToolPolygon::~kpToolPolygon () { delete d; } // private virtual [base kpToolPolygonBase] QString kpToolPolygon::haventBegunShapeUserMessage () const { return i18n ("Drag to draw the first line."); } // public virtual [base kpToolPolygonalBase] void kpToolPolygon::begin () { kpToolPolygonalBase::begin (); kpToolToolBar *tb = toolToolBar (); Q_ASSERT (tb); d->toolWidgetFillStyle = tb->toolWidgetFillStyle (); connect (d->toolWidgetFillStyle, &kpToolWidgetFillStyle::fillStyleChanged, this, &kpToolPolygon::updateShape); d->toolWidgetFillStyle->show (); } // public virtual [base kpToolPolygonalBase] void kpToolPolygon::end () { kpToolPolygonalBase::end (); disconnect (d->toolWidgetFillStyle, &kpToolWidgetFillStyle::fillStyleChanged, this, &kpToolPolygon::updateShape); d->toolWidgetFillStyle = nullptr; } // TODO: code dup with kpToolRectangle // protected virtual [base kpToolPolygonalBase] kpColor kpToolPolygon::drawingBackgroundColor () const { const kpColor foregroundColor = color (originatingMouseButton ()); const kpColor backgroundColor = color (1 - originatingMouseButton ()); return d->toolWidgetFillStyle->drawingBackgroundColor ( foregroundColor, backgroundColor); } // public virtual [base kpTool] // TODO: dup with kpToolPolyline but we don't want to create another level of // inheritance and readability. void kpToolPolygon::endDraw (const QPoint &, const QRect &) { -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "kpToolPolygon::endDraw() points=" << points ()->toList (); -#endif // A click of the other mouse button (to finish shape, instead of adding // another control point) would have caused endShape() to have been // called in kpToolPolygonalBase::beginDraw(). The points list would now // be empty. We are being called by kpTool::mouseReleaseEvent(). if (points ()->count () == 0) { return; } if (points ()->count () >= kpToolPolygonalBase::MaxPoints) { - #if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "\tending shape"; - #endif endShape (); return; } if (originatingMouseButton () == 0) { setUserMessage (i18n ("Left drag another line or right click to finish.")); } else { setUserMessage (i18n ("Right drag another line or left click to finish.")); } } diff --git a/tools/polygonal/kpToolPolygonalBase.cpp b/tools/polygonal/kpToolPolygonalBase.cpp index aaf59c50..fb06f412 100644 --- a/tools/polygonal/kpToolPolygonalBase.cpp +++ b/tools/polygonal/kpToolPolygonalBase.cpp @@ -1,501 +1,480 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_POLYGON 1 - #include "kpToolPolygonalBase.h" #include #include #include #include #include #include #include #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "kpDefs.h" #include "imagelib/kpImage.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/tempImage/kpTempImage.h" #include "environments/tools/kpToolEnvironment.h" #include "commands/tools/polygonal/kpToolPolygonalCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetLineWidth.h" #include "views/manager/kpViewManager.h" struct kpToolPolygonalBasePrivate { kpToolPolygonalBasePrivate () : drawShapeFunc(nullptr), toolWidgetLineWidth(nullptr), originatingMouseButton(-1) { } kpToolPolygonalBase::DrawShapeFunc drawShapeFunc; kpToolWidgetLineWidth *toolWidgetLineWidth; int originatingMouseButton; QPolygon points; }; //--------------------------------------------------------------------- kpToolPolygonalBase::kpToolPolygonalBase ( const QString &text, const QString &description, DrawShapeFunc drawShapeFunc, int key, kpToolEnvironment *environ, QObject *parent, const QString &name) : kpTool (text, description, key, environ, parent, name), d (new kpToolPolygonalBasePrivate ()) { d->drawShapeFunc = drawShapeFunc; d->toolWidgetLineWidth = nullptr; // (hopefully cause crash if we use it before initialising it) d->originatingMouseButton = -1; } //--------------------------------------------------------------------- kpToolPolygonalBase::~kpToolPolygonalBase () { delete d; } //--------------------------------------------------------------------- // virtual void kpToolPolygonalBase::begin () { kpToolToolBar *tb = toolToolBar (); Q_ASSERT (tb); -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "kpToolPolygonalBase::begin() tb=" << tb; -#endif d->toolWidgetLineWidth = tb->toolWidgetLineWidth (); connect (d->toolWidgetLineWidth, &kpToolWidgetLineWidth::lineWidthChanged, this, &kpToolPolygonalBase::updateShape); d->toolWidgetLineWidth->show (); viewManager ()->setCursor (QCursor (Qt::ArrowCursor)); d->originatingMouseButton = -1; setUserMessage (/*virtual*/haventBegunShapeUserMessage ()); } //--------------------------------------------------------------------- // virtual void kpToolPolygonalBase::end () { // TODO: needed? endShape (); disconnect (d->toolWidgetLineWidth, &kpToolWidgetLineWidth::lineWidthChanged, this, &kpToolPolygonalBase::updateShape); d->toolWidgetLineWidth = nullptr; viewManager ()->unsetCursor (); } void kpToolPolygonalBase::beginDraw () { -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "kpToolPolygonalBase::beginDraw() d->points=" << d->points.toList () << ", startPoint=" << startPoint (); -#endif bool endedShape = false; // We now need to start with dragging out the initial line? if (d->points.count () == 0) { d->originatingMouseButton = mouseButton (); // The line starts and ends at the start point of the drag. // draw() will modify the last point in d->points to reflect the // mouse drag, as the drag proceeds. d->points.append (startPoint ()); d->points.append (startPoint ()); } // Already have control points - not dragging out initial line. else { // Clicking the other mouse button? if (mouseButton () != d->originatingMouseButton) { // Finish shape. TODO: I suspect we need to call endShapeInternal instead. endShape (); endedShape = true; } // Are we dragging out an extra control point? else { // Add another control point. d->points.append (startPoint ()); } } -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "\tafterwards, d->points=" << d->points.toList (); -#endif if (!endedShape) { // We've started dragging. Print instructions on how to cancel shape. setUserMessage (cancelUserMessage ()); } } // protected void kpToolPolygonalBase::applyModifiers () { const int count = d->points.count (); QPoint &lineStartPoint = d->points [count - 2]; QPoint &lineEndPoint = d->points [count - 1]; -#if DEBUG_KP_TOOL_POLYGON && 1 qCDebug(kpLogTools) << "kpToolPolygonalBase::applyModifiers() #pts=" << count << " line: startPt=" << lineStartPoint << " endPt=" << lineEndPoint << " modifiers: shift=" << shiftPressed () << " alt=" << altPressed () << " ctrl=" << controlPressed (); -#endif // angles if (shiftPressed () || controlPressed ()) { int diffx = lineEndPoint.x () - lineStartPoint.x (); int diffy = lineEndPoint.y () - lineStartPoint.y (); double ratio; if (diffx == 0) { ratio = DBL_MAX; } else { ratio = fabs (double (diffy) / double (diffx)); } - #if DEBUG_KP_TOOL_POLYGON && 1 + qCDebug(kpLogTools) << "\tdiffx=" << diffx << " diffy=" << diffy << " ratio=" << ratio; - #endif // Shift = 0, 45, 90 // Ctrl = 0, 30, 60, 90 // Shift + Ctrl = 0, 30, 45, 60, 90 double angles [10]; // "ought to be enough for anybody" int numAngles = 0; angles [numAngles++] = 0; if (controlPressed ()) { angles [numAngles++] = M_PI / 6; } if (shiftPressed ()) { angles [numAngles++] = M_PI / 4; } if (controlPressed ()) { angles [numAngles++] = M_PI / 3; } angles [numAngles++] = M_PI / 2; Q_ASSERT (numAngles <= int (sizeof (angles) / sizeof (angles [0]))); double angle = angles [numAngles - 1]; for (int i = 0; i < numAngles - 1; i++) { double acceptingRatio = std::tan ((angles [i] + angles [i + 1]) / 2.0); if (ratio < acceptingRatio) { angle = angles [i]; break; } } // horizontal (dist from start not maintained) if (std::fabs (qRadiansToDegrees (angle) - 0) < kpPixmapFX::AngleInDegreesEpsilon) { lineEndPoint = QPoint (lineEndPoint.x (), lineStartPoint.y ()); } // vertical (dist from start not maintained) else if (std::fabs (qRadiansToDegrees (angle) - 90) < kpPixmapFX::AngleInDegreesEpsilon) { lineEndPoint = QPoint (lineStartPoint.x (), lineEndPoint.y ()); } // diagonal (dist from start maintained) else { const double dist = std::sqrt (static_cast (diffx * diffx + diffy * diffy)); #define sgn(a) ((a)<0?-1:1) // Round distances _before_ adding to any coordinate // (ensures consistent rounding behaviour in x & y directions) const int newdx = qRound (dist * cos (angle) * sgn (diffx)); const int newdy = qRound (dist * sin (angle) * sgn (diffy)); #undef sgn lineEndPoint = QPoint (lineStartPoint.x () + newdx, lineStartPoint.y () + newdy); - #if DEBUG_KP_TOOL_POLYGON && 1 qCDebug(kpLogTools) << "\t\tdiagonal line: dist=" << dist << " angle=" << (angle * 180 / M_PI) << " endPoint=" << lineEndPoint; - #endif } } // if (shiftPressed () || controlPressed ()) { // centring if (altPressed () && 0/*ALT is unreliable*/) { // start = start - diff // = start - (end - start) // = start - end + start // = 2 * start - end if (count == 2) { lineStartPoint += (lineStartPoint - lineEndPoint); } else { lineEndPoint += (lineEndPoint - lineStartPoint); } } // if (altPressed ()) { } // protected QPolygon *kpToolPolygonalBase::points () const { return &d->points; } // protected int kpToolPolygonalBase::originatingMouseButton () const { Q_ASSERT (hasBegunShape ()); return d->originatingMouseButton; } // virtual void kpToolPolygonalBase::draw (const QPoint &, const QPoint &, const QRect &) { // A click of the other mouse button (to finish shape, instead of adding // another control point) would have caused endShape() to have been // called in kpToolPolygonalBase::beginDraw(). The points list would now // be empty. We are being called by kpTool::mouseReleaseEvent(). if (d->points.count () == 0) { return; } -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "kpToolPolygonalBase::draw() d->points=" << d->points.toList () << ", endPoint=" << currentPoint (); -#endif // Update points() so that last point reflects current mouse position. const int count = d->points.count (); d->points [count - 1] = currentPoint (); -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "\tafterwards, d->points=" << d->points.toList (); -#endif // Are we drawing a line? if (/*virtual*/drawingALine ()) { // Adjust the line (end points given by the last 2 points of points()) // in response to keyboard modifiers. applyModifiers (); // Update the preview of the shape. updateShape (); // Inform the user that we're dragging out a line with 2 control points. setUserShapePoints (d->points [count - 2], d->points [count - 1]); } // We're modifying a point. else { // Update the preview of the shape. updateShape (); // Informs the user that we're just modifying a point (perhaps, a control // point of a Bezier). setUserShapePoints (d->points [count - 1]); } } // TODO: code dup with kpToolRectangle // private kpColor kpToolPolygonalBase::drawingForegroundColor () const { return color (originatingMouseButton ()); } // protected virtual kpColor kpToolPolygonalBase::drawingBackgroundColor () const { return kpColor::Invalid; } // TODO: code dup with kpToolRectangle // protected slot void kpToolPolygonalBase::updateShape () { if (d->points.count () == 0) { return; } const QRect boundingRect = kpTool::neededRect ( d->points.boundingRect (), d->toolWidgetLineWidth->lineWidth ()); -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "kpToolPolygonalBase::updateShape() boundingRect=" << boundingRect << " lineWidth=" << d->toolWidgetLineWidth->lineWidth () << endl; -#endif kpImage image = document ()->getImageAt (boundingRect); QPolygon pointsTranslated = d->points; pointsTranslated.translate (-boundingRect.x (), -boundingRect.y ()); (*d->drawShapeFunc) (&image, pointsTranslated, drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (), /*virtual*/drawingBackgroundColor (), false/*not final*/); kpTempImage newTempImage (false/*always display*/, kpTempImage::SetImage/*render mode*/, boundingRect.topLeft (), image); viewManager ()->setFastUpdates (); { viewManager ()->setTempImage (newTempImage); } viewManager ()->restoreFastUpdates (); } // virtual void kpToolPolygonalBase::cancelShape () { viewManager ()->invalidateTempImage (); d->points.resize (0); setUserMessage (i18n ("Let go of all the mouse buttons.")); } void kpToolPolygonalBase::releasedAllButtons () { if (!hasBegunShape ()) { setUserMessage (/*virtual*/haventBegunShapeUserMessage ()); } // --- else case already handled by endDraw() --- } // public virtual [base kpTool] void kpToolPolygonalBase::endShape (const QPoint &, const QRect &) { -#if DEBUG_KP_TOOL_POLYGON qCDebug(kpLogTools) << "kpToolPolygonalBase::endShape() d->points=" << d->points.toList () << endl; -#endif if (!hasBegunShape ()) { return; } viewManager ()->invalidateTempImage (); QRect boundingRect = kpTool::neededRect ( d->points.boundingRect (), d->toolWidgetLineWidth->lineWidth ()); commandHistory ()->addCommand ( new kpToolPolygonalCommand ( text (), d->drawShapeFunc, d->points, boundingRect, drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (), /*virtual*/drawingBackgroundColor (), environ ()->commandEnvironment ())); d->points.resize (0); setUserMessage (/*virtual*/haventBegunShapeUserMessage ()); } // public virtual [base kpTool] bool kpToolPolygonalBase::hasBegunShape () const { return (d->points.count () > 0); } // virtual protected slot [base kpTool] void kpToolPolygonalBase::slotForegroundColorChanged (const kpColor &) { updateShape (); } // virtual protected slot [base kpTool] void kpToolPolygonalBase::slotBackgroundColorChanged (const kpColor &) { updateShape (); } diff --git a/tools/polygonal/kpToolPolyline.cpp b/tools/polygonal/kpToolPolyline.cpp index 612035be..e0b958fa 100644 --- a/tools/polygonal/kpToolPolyline.cpp +++ b/tools/polygonal/kpToolPolyline.cpp @@ -1,125 +1,120 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_POLYLINE 1 - #include "kpToolPolyline.h" #include "kpLogCategories.h" #include "environments/tools/kpToolEnvironment.h" #include "pixmapfx/kpPixmapFX.h" #include #include #include //-------------------------------------------------------------------------------- kpToolPolyline::kpToolPolyline (kpToolEnvironment *environ, QObject *parent) : kpToolPolygonalBase ( i18n ("Connected Lines"), i18n ("Draws connected lines"), &drawShape, Qt::Key_N, environ, parent, QStringLiteral("tool_polyline")) { } //-------------------------------------------------------------------------------- // private virtual [base kpToolPolygonalBase] QString kpToolPolyline::haventBegunShapeUserMessage () const { return i18n ("Drag to draw the first line."); } //-------------------------------------------------------------------------------- // public static void kpToolPolyline::drawShape(kpImage *image, const QPolygon &points, const kpColor &fcolor, int penWidth, const kpColor &bcolor, bool isFinal) { (void) bcolor; (void) isFinal; QPainter painter(image); painter.setRenderHint(QPainter::Antialiasing, kpToolEnvironment::drawAntiAliased); painter.setPen(QPen(fcolor.toQColor(), penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); if ( kpPixmapFX::Only1PixelInPointArray(points) ) { painter.drawPoint(points[0]); } else { painter.drawPolyline(points); } } //-------------------------------------------------------------------------------- // public virtual [base kpTool] void kpToolPolyline::endDraw (const QPoint &, const QRect &) { -#if DEBUG_KP_TOOL_POLYLINE qCDebug(kpLogTools) << "kpToolPolyline::endDraw() points=" << points ()->toList (); -#endif // A click of the other mouse button (to finish shape, instead of adding // another control point) would have caused endShape() to have been // called in kpToolPolygonalBase::beginDraw(). The points list would now // be empty. We are being called by kpTool::mouseReleaseEvent(). if (points ()->count () == 0) { return; } if (points ()->count () >= kpToolPolygonalBase::MaxPoints) { - #if DEBUG_KP_TOOL_POLYLINE qCDebug(kpLogTools) << "\tending shape"; - #endif + endShape (); return; } if (originatingMouseButton () == 0) { setUserMessage (i18n ("Left drag another line or right click to finish.")); } else { setUserMessage (i18n ("Right drag another line or left click to finish.")); } } diff --git a/tools/selection/kpAbstractSelectionTool.cpp b/tools/selection/kpAbstractSelectionTool.cpp index a8f9a437..0a594f4e 100644 --- a/tools/selection/kpAbstractSelectionTool.cpp +++ b/tools/selection/kpAbstractSelectionTool.cpp @@ -1,641 +1,610 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_TOOL_SELECTION 1 - #include "kpAbstractSelectionTool.h" #include "kpAbstractSelectionToolPrivate.h" #include #include #include #include "kpLogCategories.h" #include "layers/selections/kpAbstractSelection.h" #include "commands/tools/selection/kpAbstractSelectionContentCommand.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "commands/kpMacroCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include "imagelib/kpPainter.h" #include //--------------------------------------------------------------------- // For either of these timers, they are only active during the "drawing" phase // of kpTool. static void AssertAllTimersInactive (struct kpAbstractSelectionToolPrivate *d) { Q_ASSERT (!d->createNOPTimer->isActive ()); Q_ASSERT (!d->RMBMoveUpdateGUITimer->isActive ()); } //--------------------------------------------------------------------- kpAbstractSelectionTool::kpAbstractSelectionTool ( const QString &text, const QString &description, int key, kpToolSelectionEnvironment *environ, QObject *parent, const QString &name) : kpTool (text, description, key, environ, parent, name), d (new kpAbstractSelectionToolPrivate ()) { d->drawType = None; d->currentSelContentCommand = nullptr; // d->dragAccepted // d->hadSelectionBeforeDrag // d->cancelledShapeButStillHoldingButtons d->toolWidgetOpaqueOrTransparent = nullptr; initCreate (); initMove (); initResizeScale (); // It would be bad practice to have timers ticking even when this tool // is not in use. ::AssertAllTimersInactive (d); } //--------------------------------------------------------------------- kpAbstractSelectionTool::~kpAbstractSelectionTool () { uninitCreate (); uninitMove (); uninitResizeScale (); // (state must be after construction, or after some time after end()) Q_ASSERT (d->drawType == None); Q_ASSERT (!d->currentSelContentCommand); // d->dragAccepted // d->hadSelectionBeforeDraw // d->cancelledShapeButStillHoldingButtons // d->toolWidgetOpaqueOrTransparent delete d; } //--------------------------------------------------------------------- // protected kpAbstractSelectionTool::DrawType kpAbstractSelectionTool::drawType () const { return d->drawType; } //--------------------------------------------------------------------- // protected bool kpAbstractSelectionTool::hadSelectionBeforeDraw () const { return d->hadSelectionBeforeDraw; } //--------------------------------------------------------------------- // protected overrides [base kpTool] kpToolSelectionEnvironment *kpAbstractSelectionTool::environ () const { kpToolEnvironment *e = kpTool::environ (); Q_ASSERT (dynamic_cast (e)); return dynamic_cast (e); } //--------------------------------------------------------------------- // protected bool kpAbstractSelectionTool::controlOrShiftPressed () const { return (controlPressed () || shiftPressed ()); } //--------------------------------------------------------------------- // protected void kpAbstractSelectionTool::pushOntoDocument () { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "kpAbstractSelectionTool::pushOntoDocument() selection=" << document ()->selection (); -#endif + Q_ASSERT (document ()->selection ()); environ ()->deselectSelection (); } //--------------------------------------------------------------------- // protected void kpAbstractSelectionTool::giveContentIfNeeded () { kpAbstractSelection *sel = document ()->selection (); Q_ASSERT (sel); if (sel->hasContent ()) { return; } if (d->currentSelContentCommand) { return; } d->currentSelContentCommand = /*virtual*/newGiveContentCommand (); d->currentSelContentCommand->execute (); } //--------------------------------------------------------------------- // protected // REFACTOR: sync: Code dup with kpMainWindow::addImageOrSelectionCommand (). void kpAbstractSelectionTool::addNeedingContentCommand (kpCommand *cmd) { Q_ASSERT (cmd); // Did we fill the selection with content? if (d->currentSelContentCommand) { // Make the border creation a command. - #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\thave currentSelContentCommand"; - #endif + commandHistory ()->addCreateSelectionCommand ( new kpToolSelectionCreateCommand ( /*virtual*/nameOfCreateCommand (), *d->currentSelContentCommand->originalSelection (), environ ()->commandEnvironment ()), false/*no exec - user already dragged out sel*/); } // Do we have a content setting command we need to commit? // (yes, this is the same check as the previous "if") if (d->currentSelContentCommand) { // Put the content command + given command (e.g. movement) together // as a macro command, in the command history. kpMacroCommand *macroCmd = new kpMacroCommand ( cmd->name (), environ ()->commandEnvironment ()); macroCmd->addCommand (d->currentSelContentCommand); d->currentSelContentCommand = nullptr; macroCmd->addCommand (cmd); commandHistory ()->addCommand (macroCmd, false/*no exec*/); } else { // Put the given command into the command history. commandHistory ()->addCommand (cmd, false/*no exec*/); } } //--------------------------------------------------------------------- // protected virtual void kpAbstractSelectionTool::setSelectionBorderForHaventBegunDraw () { viewManager ()->setQueueUpdates (); { viewManager ()->setSelectionBorderVisible (true); viewManager ()->setSelectionBorderFinished (true); } viewManager ()->restoreQueueUpdates (); } //--------------------------------------------------------------------- // private QString kpAbstractSelectionTool::haventBegunDrawUserMessage () { -#if DEBUG_KP_TOOL_SELECTION && 0 +#if 0 qCDebug(kpLogTools) << "kpAbstractSelectionTool::haventBegunDrawUserMessage()" " cancelledShapeButStillHoldingButtons=" << d->cancelledShapeButStillHoldingButtons; #endif if (d->cancelledShapeButStillHoldingButtons) { return i18n ("Let go of all the mouse buttons."); } return operation (calculateDrawType (), HaventBegunDrawUserMessage).toString (); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::begin () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractSelectionTool<" << objectName () << ">::begin()"; -#endif ::AssertAllTimersInactive (d); // (state must be after construction, or after some time after end()) Q_ASSERT (d->drawType == None); Q_ASSERT (!d->currentSelContentCommand); d->dragAccepted = false; // d->hadSelectionBeforeDraw d->cancelledShapeButStillHoldingButtons = false; kpToolToolBar *tb = toolToolBar (); Q_ASSERT (tb); d->toolWidgetOpaqueOrTransparent = tb->toolWidgetOpaqueOrTransparent (); Q_ASSERT (d->toolWidgetOpaqueOrTransparent); connect (d->toolWidgetOpaqueOrTransparent, &kpToolWidgetOpaqueOrTransparent::isOpaqueChanged, this, &kpAbstractSelectionTool::slotIsOpaqueChanged); d->toolWidgetOpaqueOrTransparent->show (); /*virtual*/setSelectionBorderForHaventBegunDraw (); beginCreate (); beginMove (); beginResizeScale (); setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::end () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractSelectionTool<" << objectName () << ">::end()"; -#endif if (document ()->selection ()) { pushOntoDocument (); } endCreate (); endMove (); endResizeScale (); // (should have been killed by cancelShape() or endDraw()) Q_ASSERT (d->drawType == None); Q_ASSERT (!d->currentSelContentCommand); // d->dragAccepted // d->hadSelectionBeforeDraw // d->cancelledShapeButStillHoldingButtons Q_ASSERT (d->toolWidgetOpaqueOrTransparent); disconnect (d->toolWidgetOpaqueOrTransparent, &kpToolWidgetOpaqueOrTransparent::isOpaqueChanged, this, &kpAbstractSelectionTool::slotIsOpaqueChanged); d->toolWidgetOpaqueOrTransparent = nullptr; viewManager ()->unsetCursor (); ::AssertAllTimersInactive (d); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::reselect () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractSelectionTool::reselect()"; -#endif if (document ()->selection ()) { pushOntoDocument (); } } //--------------------------------------------------------------------- // protected virtual kpAbstractSelectionTool::DrawType kpAbstractSelectionTool::calculateDrawTypeInsideSelection () const { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\t\tis move"; -#endif + return kpAbstractSelectionTool::Move; } //--------------------------------------------------------------------- // protected virtual kpAbstractSelectionTool::DrawType kpAbstractSelectionTool::calculateDrawType () const { kpAbstractSelection *sel = document ()->selection (); if (!sel) { return Create; } -#if DEBUG_KP_TOOL_SELECTION + qCDebug(kpLogTools) << "\thas sel region rect=" << sel->boundingRect (); -#endif if (onSelectionResizeHandle () && !controlOrShiftPressed ()) { return ResizeScale; } if (sel->contains (currentPoint ())) { return /*virtual*/calculateDrawTypeInsideSelection (); } return Create; } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::beginDraw () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractSelectionTool::beginDraw() startPoint ()=" << startPoint () << " QCursor::pos() view startPoint=" << viewUnderStartPoint ()->mapFromGlobal (QCursor::pos ()); -#endif // endDraw() and cancelShape() should have taken care of these. ::AssertAllTimersInactive (d); // In case the cursor was wrong to start with // (forgot to call kpTool::somethingBelowTheCursorChanged()), // make sure it is correct during this operation. hover (currentPoint ()); // Currently used only to end the current text if (hasBegunShape ()) { endShape(currentPoint(), kpPainter::normalizedRect(startPoint()/* TODO: wrong */, currentPoint())); } d->drawType = calculateDrawType (); d->dragAccepted = false; kpAbstractSelection *sel = document ()->selection (); d->hadSelectionBeforeDraw = bool (sel); operation (d->drawType, BeginDraw); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::hover (const QPoint &point) { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "kpAbstractSelectionTool::hover" << point; -#endif + operation (calculateDrawType (), SetCursor); setUserShapePoints (point, KP_INVALID_POINT, false/*don't set size*/); if (document () && document ()->selection ()) { setUserShapeSize (document ()->selection ()->width (), document ()->selection ()->height ()); } else { setUserShapeSize (KP_INVALID_SIZE); } QString mess = haventBegunDrawUserMessage (); if (mess != userMessage ()) { setUserMessage (mess); } } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::draw (const QPoint &thisPoint, const QPoint & /*lastPoint*/, const QRect &normalizedRect) { -#if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "kpAbstractSelectionTool::draw (" << thisPoint << ",startPoint=" << startPoint () << ",normalizedRect=" << normalizedRect << ")"; -#else - Q_UNUSED (thisPoint); - Q_UNUSED (normalizedRect); -#endif // OPT: return when thisPoint == lastPoint () so that e.g. when creating // Points sel, press modifiers doesn't add multiple points in same // place operation (d->drawType, Draw); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::cancelShape () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractSelectionTool::cancelShape() mouseButton=" << mouseButton (); -#endif const DrawType oldDrawType = d->drawType; // kpTool::hasBegunDraw() returns false in this method so be consistent // and clear "drawType" before dispatching the operation() below. d->drawType = None; viewManager ()->setQueueUpdates (); { operation (oldDrawType, Cancel); if (d->currentSelContentCommand) { - #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\t\tundo sel content"; - #endif + d->currentSelContentCommand->unexecute (); delete d->currentSelContentCommand; d->currentSelContentCommand = nullptr; } /*virtual*/setSelectionBorderForHaventBegunDraw (); } viewManager ()->restoreQueueUpdates (); d->cancelledShapeButStillHoldingButtons = true; setUserMessage (i18n ("Let go of all the mouse buttons.")); ::AssertAllTimersInactive (d); } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::releasedAllButtons () { d->cancelledShapeButStillHoldingButtons = false; setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // protected void kpAbstractSelectionTool::popupRMBMenu () { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "CALL - exec'ing menu"; -#endif QMenu *pop = environ ()->selectionToolRMBMenu (); Q_ASSERT (pop); // Blocks until the menu closes. // WARNING: Enters event loop - may re-enter view/tool event handlers. pop->exec (QCursor::pos ()); -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "calling somethingBelowTheCursorChanged()"; -#endif // Cursor may have moved while the menu was up, triggering QMouseMoveEvents // for the menu -- but not the view -- so we may have missed cursor moves. // Update cursor position now. somethingBelowTheCursorChanged (); -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "DONE"; -#endif } //--------------------------------------------------------------------- // public virtual [base kpTool] void kpAbstractSelectionTool::endDraw (const QPoint & /*thisPoint*/, const QRect & /*normalizedRect*/) { -#if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractSelectionTool::endDraw()"; -#endif const DrawType oldDrawType = d->drawType; // kpTool::hasBegunDraw() returns false in this method so be consistent // and clear "drawType" before dispatching the operation() below. d->drawType = None; viewManager ()->setQueueUpdates (); { operation (oldDrawType, EndDraw); /*virtual*/setSelectionBorderForHaventBegunDraw (); } viewManager ()->restoreQueueUpdates (); setUserMessage (haventBegunDrawUserMessage ()); ::AssertAllTimersInactive (d); if (mouseButton () == 1/*right*/) { popupRMBMenu (); } // WARNING: Do not place any code after the popupRMBMenu() call // (see the popupRMBMenu() API). } //--------------------------------------------------------------------- // protected virtual QVariant kpAbstractSelectionTool::operation (DrawType drawType, Operation op, const QVariant &data1, const QVariant &data2) { switch (drawType) { case None: // NOP. return {}; case Create: return operationCreate (op, data1, data2); case Move: return operationMove (op, data1, data2); case ResizeScale: return operationResizeScale (op, data1, data2); default: Q_ASSERT (!"Unhandled draw type"); return {}; } } //--------------------------------------------------------------------- diff --git a/views/kpThumbnailView.cpp b/views/kpThumbnailView.cpp index 17d0149b..ffc1727f 100644 --- a/views/kpThumbnailView.cpp +++ b/views/kpThumbnailView.cpp @@ -1,94 +1,88 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_THUMBNAIL_VIEW 1 - #include "views/kpThumbnailView.h" #include "kpLogCategories.h" kpThumbnailView::kpThumbnailView (kpDocument *document, kpToolToolBar *toolToolBar, kpViewManager *viewManager, kpView *buddyView, kpViewScrollableContainer *scrollableContainer, QWidget *parent) : kpView (document, toolToolBar, viewManager, buddyView, scrollableContainer, parent) { } kpThumbnailView::~kpThumbnailView () = default; // protected void kpThumbnailView::setMaskToCoverDocument () { -#if DEBUG_KP_THUMBNAIL_VIEW qCDebug(kpLogViews) << "kpThumbnailView::setMaskToCoverDocument()" << " origin=" << origin () << " zoomedDoc: width=" << zoomedDocWidth () << " height=" << zoomedDocHeight (); -#endif setMask (QRegion (QRect (origin ().x (), origin ().y (), zoomedDocWidth (), zoomedDocHeight ()))); } // protected virtual [base kpView] void kpThumbnailView::resizeEvent (QResizeEvent *e) { -#if DEBUG_KP_THUMBNAIL_VIEW qCDebug(kpLogViews) << "kpThumbnailView(" << caption () << ")::resizeEvent()"; -#endif // For QResizeEvent's, Qt already throws an entire widget repaint into // the event loop. So eat useless update() calls that can only slow // things down. // TODO: this doesn't seem to work. // Later: In Qt4, setUpdatesEnabled(true) calls update(). const bool oldIsUpdatesEnabled = updatesEnabled (); setUpdatesEnabled (false); { kpView::resizeEvent (e); adjustToEnvironment (); } setUpdatesEnabled (oldIsUpdatesEnabled); } diff --git a/views/kpView.cpp b/views/kpView.cpp index 7b894743..dd996f59 100644 --- a/views/kpView.cpp +++ b/views/kpView.cpp @@ -1,688 +1,666 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2005 Kazuki Ohta Copyright (c) 2010 Tasuku Suzuki All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_VIEW 1 -#define DEBUG_KP_VIEW_RENDERER ((DEBUG_KP_VIEW && 1) || 0) - #include "kpView.h" #include "kpViewPrivate.h" #include #include #include #include #include #include #include "kpLogCategories.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "tools/kpTool.h" #include "widgets/toolbars/kpToolToolBar.h" #include "views/manager/kpViewManager.h" #include "kpViewScrollableContainer.h" //--------------------------------------------------------------------- // public static const int kpView::MinZoomLevel = 1; const int kpView::MaxZoomLevel = 3200; //--------------------------------------------------------------------- kpView::kpView (kpDocument *document, kpToolToolBar *toolToolBar, kpViewManager *viewManager, kpView *buddyView, kpViewScrollableContainer *scrollableContainer, QWidget *parent) : QWidget (parent), d (new kpViewPrivate ()) { d->document = document; d->toolToolBar = toolToolBar; d->viewManager = viewManager; d->buddyView = buddyView; d->scrollableContainer = scrollableContainer; d->hzoom = 100; d->vzoom = 100; d->origin = QPoint (0, 0); d->showGrid = false; d->isBuddyViewScrollableContainerRectangleShown = false; // Don't waste CPU drawing default background since its overridden by // our fully opaque drawing. In reality, this seems to make no // difference in performance. setAttribute(Qt::WA_OpaquePaintEvent, true); setFocusPolicy (Qt::WheelFocus); setMouseTracking (true); // mouseMoveEvent's even when no mousebtn down setAttribute (Qt::WA_KeyCompression, true); } //--------------------------------------------------------------------- kpView::~kpView () { setHasMouse (false); delete d; } //--------------------------------------------------------------------- // public kpDocument *kpView::document () const { return d->document; } //--------------------------------------------------------------------- // protected kpAbstractSelection *kpView::selection () const { return document () ? document ()->selection () : nullptr; } //--------------------------------------------------------------------- // protected kpTextSelection *kpView::textSelection () const { return document () ? document ()->textSelection () : nullptr; } //--------------------------------------------------------------------- // public kpToolToolBar *kpView::toolToolBar () const { return d->toolToolBar; } // protected kpTool *kpView::tool () const { return toolToolBar () ? toolToolBar ()->tool () : nullptr; } // public kpViewManager *kpView::viewManager () const { return d->viewManager; } // public kpView *kpView::buddyView () const { return d->buddyView; } // public kpViewScrollableContainer *kpView::buddyViewScrollableContainer () const { return (buddyView () ? buddyView ()->scrollableContainer () : nullptr); } // public kpViewScrollableContainer *kpView::scrollableContainer () const { return d->scrollableContainer; } // public int kpView::zoomLevelX () const { return d->hzoom; } // public int kpView::zoomLevelY () const { return d->vzoom; } // public virtual void kpView::setZoomLevel (int hzoom, int vzoom) { hzoom = qBound (MinZoomLevel, hzoom, MaxZoomLevel); vzoom = qBound (MinZoomLevel, vzoom, MaxZoomLevel); if (hzoom == d->hzoom && vzoom == d->vzoom) { return; } d->hzoom = hzoom; d->vzoom = vzoom; if (viewManager ()) { viewManager ()->updateView (this); } emit zoomLevelChanged (hzoom, vzoom); } // public QPoint kpView::origin () const { return d->origin; } // public virtual void kpView::setOrigin (const QPoint &origin) { -#if DEBUG_KP_VIEW qCDebug(kpLogViews) << "kpView(" << objectName () << ")::setOrigin" << origin; -#endif if (origin == d->origin) { - #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tNOP"; - #endif return; } d->origin = origin; if (viewManager ()) { viewManager ()->updateView (this); } emit originChanged (origin); } // public bool kpView::canShowGrid () const { // (minimum zoom level < 400% would probably be reported as a bug by // users who thought that the grid was a part of the image!) return ((zoomLevelX () >= 400 && zoomLevelX () % 100 == 0) && (zoomLevelY () >= 400 && zoomLevelY () % 100 == 0)); } // public bool kpView::isGridShown () const { return d->showGrid; } // public void kpView::showGrid (bool yes) { if (d->showGrid == yes) { return; } if (yes && !canShowGrid ()) { return; } d->showGrid = yes; if (viewManager ()) { viewManager ()->updateView (this); } } // public bool kpView::isBuddyViewScrollableContainerRectangleShown () const { return d->isBuddyViewScrollableContainerRectangleShown; } // public void kpView::showBuddyViewScrollableContainerRectangle (bool yes) { if (yes == d->isBuddyViewScrollableContainerRectangleShown) { return; } d->isBuddyViewScrollableContainerRectangleShown = yes; if (d->isBuddyViewScrollableContainerRectangleShown) { // Got these connect statements by analysing deps of // updateBuddyViewScrollableContainerRectangle() rect update code. connect (this, &kpView::zoomLevelChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); connect (this, &kpView::originChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); if (buddyViewScrollableContainer ()) { connect (buddyViewScrollableContainer (), &kpViewScrollableContainer::contentsMoved, this, &kpView::updateBuddyViewScrollableContainerRectangle); connect (buddyViewScrollableContainer (), &kpViewScrollableContainer::resized, this, &kpView::updateBuddyViewScrollableContainerRectangle); } if (buddyView ()) { connect (buddyView (), &kpView::zoomLevelChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); connect (buddyView (), &kpView::originChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); connect (buddyView (), static_cast(&kpView::sizeChanged), this, &kpView::updateBuddyViewScrollableContainerRectangle); } } else { disconnect (this, &kpView::zoomLevelChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); disconnect (this, &kpView::originChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); if (buddyViewScrollableContainer ()) { disconnect (buddyViewScrollableContainer (), &kpViewScrollableContainer::contentsMoved, this, &kpView::updateBuddyViewScrollableContainerRectangle); disconnect (buddyViewScrollableContainer (), &kpViewScrollableContainer::resized, this, &kpView::updateBuddyViewScrollableContainerRectangle); } if (buddyView ()) { disconnect (buddyView (), &kpView::zoomLevelChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); disconnect (buddyView (), &kpView::originChanged, this, &kpView::updateBuddyViewScrollableContainerRectangle); disconnect (buddyView (), static_cast(&kpView::sizeChanged), this, &kpView::updateBuddyViewScrollableContainerRectangle); } } updateBuddyViewScrollableContainerRectangle (); } // protected QRect kpView::buddyViewScrollableContainerRectangle () const { return d->buddyViewScrollableContainerRectangle; } // protected slot void kpView::updateBuddyViewScrollableContainerRectangle () { if (viewManager ()) { viewManager ()->setQueueUpdates (); } { if (d->buddyViewScrollableContainerRectangle.isValid ()) { if (viewManager ()) { // Erase last viewManager ()->updateViewRectangleEdges (this, d->buddyViewScrollableContainerRectangle); } } QRect newRect; if (isBuddyViewScrollableContainerRectangleShown () && buddyViewScrollableContainer () && buddyView ()) { QRect docRect = buddyView ()->transformViewToDoc ( QRect (buddyViewScrollableContainer ()->horizontalScrollBar()->value(), buddyViewScrollableContainer ()->verticalScrollBar()->value(), qMin (buddyView ()->width (), buddyViewScrollableContainer ()->viewport()->width ()), qMin (buddyView ()->height (), buddyViewScrollableContainer ()->viewport()->height ()))); QRect viewRect = this->transformDocToView (docRect); // (Surround the area of interest by moving outwards by 1 pixel in each // direction - don't overlap area) newRect = QRect (viewRect.x () - 1, viewRect.y () - 1, viewRect.width () + 2, viewRect.height () + 2); } else { newRect = QRect (); } if (newRect != d->buddyViewScrollableContainerRectangle) { // (must set before updateView() for paintEvent() to see new // rect) d->buddyViewScrollableContainerRectangle = newRect; if (newRect.isValid ()) { if (viewManager ()) { viewManager ()->updateViewRectangleEdges (this, d->buddyViewScrollableContainerRectangle); } } } } if (viewManager ()) { viewManager ()->restoreQueueUpdates (); } } //--------------------------------------------------------------------- // public double kpView::transformViewToDocX (double viewX) const { return (viewX - origin ().x ()) * 100.0 / zoomLevelX (); } //--------------------------------------------------------------------- // public double kpView::transformViewToDocY (double viewY) const { return (viewY - origin ().y ()) * 100.0 / zoomLevelY (); } //--------------------------------------------------------------------- // public QPoint kpView::transformViewToDoc (const QPoint &viewPoint) const { return {static_cast (transformViewToDocX (viewPoint.x ())), static_cast (transformViewToDocY (viewPoint.y ()))}; } //--------------------------------------------------------------------- // public QRect kpView::transformViewToDoc (const QRect &viewRect) const { if (zoomLevelX () == 100 && zoomLevelY () == 100) { return {viewRect.x () - origin ().x (), viewRect.y () - origin ().y (), viewRect.width (), viewRect.height ()}; } const QPoint docTopLeft = transformViewToDoc (viewRect.topLeft ()); // (don't call transformViewToDoc[XY]() - need to round up dimensions) const auto docWidth = qRound (double (viewRect.width ()) * 100.0 / double (zoomLevelX ())); const auto docHeight = qRound (double (viewRect.height ()) * 100.0 / double (zoomLevelY ())); // (like QWMatrix::Areas) return {docTopLeft.x (), docTopLeft.y (), docWidth, docHeight}; } //--------------------------------------------------------------------- // public double kpView::transformDocToViewX (double docX) const { return (docX * zoomLevelX () / 100.0) + origin ().x (); } // public double kpView::transformDocToViewY (double docY) const { return (docY * zoomLevelY () / 100.0) + origin ().y (); } // public QPoint kpView::transformDocToView (const QPoint &docPoint) const { return {static_cast (transformDocToViewX (docPoint.x ())), static_cast (transformDocToViewY (docPoint.y ()))}; } // public QRect kpView::transformDocToView (const QRect &docRect) const { if (zoomLevelX () == 100 && zoomLevelY () == 100) { return {docRect.x () + origin ().x (), docRect.y () + origin ().y (), docRect.width (), docRect.height ()}; } const QPoint viewTopLeft = transformDocToView (docRect.topLeft ()); // (don't call transformDocToView[XY]() - need to round up dimensions) const int viewWidth = qRound (double (docRect.width ()) * double (zoomLevelX ()) / 100.0); const int viewHeight = qRound (double (docRect.height ()) * double (zoomLevelY ()) / 100.0); // (like QWMatrix::Areas) return QRect (viewTopLeft.x (), viewTopLeft.y (), viewWidth, viewHeight); } // public QPoint kpView::transformViewToOtherView (const QPoint &viewPoint, const kpView *otherView) { if (this == otherView) { return viewPoint; } const double docX = transformViewToDocX (viewPoint.x ()); const double docY = transformViewToDocY (viewPoint.y ()); const double otherViewX = otherView->transformDocToViewX (docX); const double otherViewY = otherView->transformDocToViewY (docY); return {static_cast (otherViewX), static_cast (otherViewY)}; } // public int kpView::zoomedDocWidth () const { return document () ? document ()->width () * zoomLevelX () / 100 : 0; } // public int kpView::zoomedDocHeight () const { return document () ? document ()->height () * zoomLevelY () / 100 : 0; } // public void kpView::setHasMouse (bool yes) { kpViewManager *vm = viewManager (); if (!vm) { return; } -#if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::setHasMouse(" << yes << ") existing viewUnderCursor=" << (vm->viewUnderCursor () ? vm->viewUnderCursor ()->objectName () : "(none)"); -#endif if (yes && vm->viewUnderCursor () != this) { vm->setViewUnderCursor (this); } else if (!yes && vm->viewUnderCursor () == this) { vm->setViewUnderCursor (nullptr); } } //--------------------------------------------------------------------- // public void kpView::addToQueuedArea (const QRegion ®ion) { -#if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::addToQueuedArea() already=" << d->queuedUpdateArea - << " - plus - " << region - << endl; -#endif + << " - plus - " << region; d->queuedUpdateArea += region; } //--------------------------------------------------------------------- // public void kpView::addToQueuedArea (const QRect &rect) { -#if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::addToQueuedArea() already=" << d->queuedUpdateArea - << " - plus - " << rect - << endl; -#endif + << " - plus - " << rect; d->queuedUpdateArea += rect; } //--------------------------------------------------------------------- // public void kpView::invalidateQueuedArea () { -#if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView::invalidateQueuedArea()"; -#endif d->queuedUpdateArea = QRegion (); } //--------------------------------------------------------------------- // public void kpView::updateQueuedArea () { kpViewManager *vm = viewManager (); -#if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::updateQueuedArea() vm=" << (bool) vm << " queueUpdates=" << (vm && vm->queueUpdates ()) << " fastUpdates=" << (vm && vm->fastUpdates ()) - << " area=" << d->queuedUpdateArea - << endl; -#endif + << " area=" << d->queuedUpdateArea; if (!vm) { return; } if (vm->queueUpdates ()) { return; } if (!d->queuedUpdateArea.isEmpty ()) { vm->updateView (this, d->queuedUpdateArea); } invalidateQueuedArea (); } //--------------------------------------------------------------------- // public QPoint kpView::mouseViewPoint (const QPoint &returnViewPoint) const { if (returnViewPoint != KP_INVALID_POINT) { return returnViewPoint; } // TODO: I don't think this is right for the main view since that's // inside the scrollview (which can scroll). return mapFromGlobal (QCursor::pos ()); } //--------------------------------------------------------------------- // public virtual QVariant kpView::inputMethodQuery (Qt::InputMethodQuery query) const { -#if DEBUG_KP_VIEW && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::inputMethodQuery()"; -#endif QVariant ret; switch (query) { case Qt::ImMicroFocus: { QRect r = d->viewManager->textCursorRect (); r.setTopLeft (r.topLeft () + origin ()); r.setHeight (r.height() + 2); r = transformDocToView (r); ret = r; break; } case Qt::ImFont: { if (textSelection ()) { ret = textSelection ()->textStyle ().font (); } break; } default: break; } return ret; } //--------------------------------------------------------------------- diff --git a/views/kpView_Selections.cpp b/views/kpView_Selections.cpp index da2f8af4..44fe612e 100644 --- a/views/kpView_Selections.cpp +++ b/views/kpView_Selections.cpp @@ -1,382 +1,356 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_VIEW 1 -#define DEBUG_KP_VIEW_RENDERER ((DEBUG_KP_VIEW && 1) || 0) - #include "views/kpView.h" #include "kpViewPrivate.h" #include "layers/selections/kpAbstractSelection.h" #include "layers/selections/text/kpTextSelection.h" #include "tools/kpTool.h" #include "kpLogCategories.h" // public QRect kpView::selectionViewRect () const { return selection () ? transformDocToView (selection ()->boundingRect ()) : QRect (); } // public QPoint kpView::mouseViewPointRelativeToSelection (const QPoint &viewPoint) const { if (!selection ()) { return KP_INVALID_POINT; } return mouseViewPoint (viewPoint) - transformDocToView (selection ()->topLeft ()); } // public bool kpView::mouseOnSelection (const QPoint &viewPoint) const { const QRect selViewRect = selectionViewRect (); if (!selViewRect.isValid ()) { return false; } return selViewRect.contains (mouseViewPoint (viewPoint)); } // public int kpView::textSelectionMoveBorderAtomicSize () const { if (!textSelection ()) { return 0; } return qMax (4, zoomLevelX () / 100); } // public bool kpView::mouseOnSelectionToMove (const QPoint &viewPoint) const { if (!mouseOnSelection (viewPoint)) { return false; } if (!textSelection ()) { return true; } if (mouseOnSelectionResizeHandle (viewPoint)) { return false; } const QPoint viewPointRelSel = mouseViewPointRelativeToSelection (viewPoint); // Middle point should always be selectable const QPoint selCenterDocPoint = selection ()->boundingRect ().center (); if (tool () && tool ()->calculateCurrentPoint () == selCenterDocPoint) { return false; } const int atomicSize = textSelectionMoveBorderAtomicSize (); const QRect selViewRect = selectionViewRect (); return (viewPointRelSel.x () < atomicSize || viewPointRelSel.x () >= selViewRect.width () - atomicSize || viewPointRelSel.y () < atomicSize || viewPointRelSel.y () >= selViewRect.height () - atomicSize); } //--------------------------------------------------------------------- // protected bool kpView::selectionLargeEnoughToHaveResizeHandlesIfAtomicSize (int atomicSize) const { if (!selection ()) { return false; } const QRect selViewRect = selectionViewRect (); return (selViewRect.width () >= atomicSize * 5 || selViewRect.height () >= atomicSize * 5); } //--------------------------------------------------------------------- // public int kpView::selectionResizeHandleAtomicSize () const { int atomicSize = qMin (13, qMax (9, zoomLevelX () / 100)); while (atomicSize > 0 && !selectionLargeEnoughToHaveResizeHandlesIfAtomicSize (atomicSize)) { atomicSize--; } return atomicSize; } //--------------------------------------------------------------------- // public bool kpView::selectionLargeEnoughToHaveResizeHandles () const { return (selectionResizeHandleAtomicSize () > 0); } //--------------------------------------------------------------------- // public QRegion kpView::selectionResizeHandlesViewRegion (bool forRenderer) const { const int atomicLength = selectionResizeHandleAtomicSize (); if (atomicLength <= 0) { return {}; } // HACK: At low zoom (e.g. 100%), resize handles will probably be too // big and overlap text / cursor / too much of selection. // // So limit the _visual_ size of handles at low zoom. The // handles' grab area remains the same for usability; so yes, // there are a few pixels that don't look grabable but they are. // // The real solution is to be able to partially render the // handles outside of the selection view rect. If not possible, // at least for text boxes, render text on top of handles. int normalAtomicLength = atomicLength; int vertEdgeAtomicLength = atomicLength; if (forRenderer && selection ()) { if (zoomLevelX () <= 150) { if (normalAtomicLength > 1) { normalAtomicLength--; } if (vertEdgeAtomicLength > 1) { vertEdgeAtomicLength--; } } // 1 line of text? if (textSelection () && textSelection ()->textLines ().size () == 1) { if (zoomLevelX () <= 150) { vertEdgeAtomicLength = qMin (vertEdgeAtomicLength, qMax (2, zoomLevelX () / 100)); } else if (zoomLevelX () <= 250) { vertEdgeAtomicLength = qMin (vertEdgeAtomicLength, qMax (3, zoomLevelX () / 100)); } } } const QRect selViewRect = selectionViewRect (); QRegion ret; // top left ret += QRect(0, 0, normalAtomicLength, normalAtomicLength); // top middle ret += QRect((selViewRect.width() - normalAtomicLength) / 2, 0, normalAtomicLength, normalAtomicLength); // top right ret += QRect(selViewRect.width() - normalAtomicLength - 1, 0, normalAtomicLength, normalAtomicLength); // left middle ret += QRect(0, (selViewRect.height() - vertEdgeAtomicLength) / 2, vertEdgeAtomicLength, vertEdgeAtomicLength); // right middle ret += QRect(selViewRect.width() - vertEdgeAtomicLength - 1, (selViewRect.height() - vertEdgeAtomicLength) / 2, vertEdgeAtomicLength, vertEdgeAtomicLength); // bottom left ret += QRect(0, selViewRect.height() - normalAtomicLength - 1, normalAtomicLength, normalAtomicLength); // bottom middle ret += QRect((selViewRect.width() - normalAtomicLength) / 2, selViewRect.height() - normalAtomicLength - 1, normalAtomicLength, normalAtomicLength); // bottom right ret += QRect(selViewRect.width() - normalAtomicLength - 1, selViewRect.height() - normalAtomicLength - 1, normalAtomicLength, normalAtomicLength); ret.translate (selViewRect.x (), selViewRect.y ()); ret = ret.intersected (selViewRect); return ret; } //--------------------------------------------------------------------- // public // REFACTOR: use QFlags as the return type for better type safety. int kpView::mouseOnSelectionResizeHandle (const QPoint &viewPoint) const { -#if DEBUG_KP_VIEW qCDebug(kpLogViews) << "kpView::mouseOnSelectionResizeHandle(viewPoint=" - << viewPoint << ")" << endl; -#endif + << viewPoint << ")"; if (!mouseOnSelection (viewPoint)) { - #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tmouse not on sel"; - #endif return 0; } const QRect selViewRect = selectionViewRect (); -#if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tselViewRect=" << selViewRect; -#endif const int atomicLength = selectionResizeHandleAtomicSize (); -#if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tatomicLength=" << atomicLength; -#endif if (atomicLength <= 0) { - #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tsel not large enough to have resize handles"; - #endif // Want to make it possible to move a small selection return 0; } const QPoint viewPointRelSel = mouseViewPointRelativeToSelection (viewPoint); -#if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tviewPointRelSel=" << viewPointRelSel; -#endif #define LOCAL_POINT_IN_BOX_AT(x,y) \ QRect ((x), (y), atomicLength, atomicLength).contains (viewPointRelSel) // Favour the bottom & right and the corners. if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength, selViewRect.height () - atomicLength)) { return kpView::Bottom | kpView::Right; } if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength, 0)) { return kpView::Top | kpView::Right; } if (LOCAL_POINT_IN_BOX_AT (0, selViewRect.height () - atomicLength)) { return kpView::Bottom | kpView::Left; } if (LOCAL_POINT_IN_BOX_AT (0, 0)) { return kpView::Top | kpView::Left; } if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength, (selViewRect.height () - atomicLength) / 2)) { return kpView::Right; } if (LOCAL_POINT_IN_BOX_AT ((selViewRect.width () - atomicLength) / 2, selViewRect.height () - atomicLength)) { return kpView::Bottom; } if (LOCAL_POINT_IN_BOX_AT ((selViewRect.width () - atomicLength) / 2, 0)) { return kpView::Top; } if (LOCAL_POINT_IN_BOX_AT (0, (selViewRect.height () - atomicLength) / 2)) { return kpView::Left; } else { - #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tnot on sel resize handle"; - #endif return 0; } #undef LOCAL_POINT_IN_BOX_AT } // public bool kpView::mouseOnSelectionToSelectText (const QPoint &viewPoint) const { -#if DEBUG_KP_VIEW qCDebug(kpLogViews) << "kpView::mouseOnSelectionToSelectText(viewPoint=" - << viewPoint << ")" << endl; -#endif + << viewPoint << ")"; if (!mouseOnSelection (viewPoint)) { - #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tmouse non on sel"; - #endif return false; } if (!textSelection ()) { - #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tsel not text"; - #endif return false; } -#if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tmouse on sel: to move=" << mouseOnSelectionToMove () - << " to resize=" << mouseOnSelectionResizeHandle () - << endl; -#endif + << " to resize=" << mouseOnSelectionResizeHandle (); return (!mouseOnSelectionToMove (viewPoint) && !mouseOnSelectionResizeHandle (viewPoint)); } diff --git a/widgets/colorSimilarity/kpColorSimilarityFrame.cpp b/widgets/colorSimilarity/kpColorSimilarityFrame.cpp index 196732c0..0ac67f82 100644 --- a/widgets/colorSimilarity/kpColorSimilarityFrame.cpp +++ b/widgets/colorSimilarity/kpColorSimilarityFrame.cpp @@ -1,78 +1,76 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_COLOR_SIMILARITY_CUBE 1 - #include "kpColorSimilarityFrame.h" #include "kpColorSimilarityCubeRenderer.h" #include "kpLogCategories.h" //--------------------------------------------------------------------- kpColorSimilarityFrame::kpColorSimilarityFrame(QWidget *parent) : QWidget(parent) { setWhatsThis (WhatsThis ()); } //--------------------------------------------------------------------- // public virtual [base kpColorSimilarityHolder] void kpColorSimilarityFrame::setColorSimilarity (double similarity) { kpColorSimilarityHolder::setColorSimilarity (similarity); repaint (); } //--------------------------------------------------------------------- // protected virtual [base QWidget] QSize kpColorSimilarityFrame::sizeHint () const { return {52, 52}; } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpColorSimilarityFrame::paintEvent (QPaintEvent *) { int cubeRectSize = qMin(width() * 6 / 8, height() * 6 / 8); int x = (width() - cubeRectSize) / 2; int y = (height() - cubeRectSize) / 2; kpColorSimilarityCubeRenderer::Paint(this, x, y, cubeRectSize, colorSimilarity()); } //--------------------------------------------------------------------- diff --git a/widgets/imagelib/effects/kpEffectBalanceWidget.cpp b/widgets/imagelib/effects/kpEffectBalanceWidget.cpp index 7f5afd3f..627e209f 100644 --- a/widgets/imagelib/effects/kpEffectBalanceWidget.cpp +++ b/widgets/imagelib/effects/kpEffectBalanceWidget.cpp @@ -1,330 +1,324 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_BALANCE 1 - #include "kpEffectBalanceWidget.h" #include "imagelib/effects/kpEffectBalance.h" #include "commands/imagelib/effects/kpEffectBalanceCommand.h" #include "pixmapfx/kpPixmapFX.h" #include "kpLogCategories.h" #include #include "kpNumInput.h" #include #include #include #include #include #include -#if DEBUG_KP_EFFECT_BALANCE - #include -#endif - kpEffectBalanceWidget::kpEffectBalanceWidget (bool actOnSelection, QWidget *parent) : kpEffectWidgetBase (actOnSelection, parent) { auto *lay = new QGridLayout (this); lay->setContentsMargins(0, 0, 0, 0); auto *brightnessLabel = new QLabel (i18n ("&Brightness:"), this); m_brightnessInput = new kpIntNumInput (0/*value*/, this); m_brightnessInput->setRange (-50, 50); auto *brightnessResetPushButton = new QPushButton (i18n ("Re&set"), this); auto *contrastLabel = new QLabel (i18n ("Co&ntrast:"), this); m_contrastInput = new kpIntNumInput (0/*value*/, this); m_contrastInput->setRange (-50, 50); auto *contrastResetPushButton = new QPushButton (i18n ("&Reset"), this); auto *gammaLabel = new QLabel (i18n ("&Gamma:"), this); m_gammaInput = new kpIntNumInput (0/*value*/, this); m_gammaInput->setRange (-50, 50); // TODO: This is what should be shown in the m_gammaInput spinbox m_gammaLabel = new QLabel (this); // TODO: This doesn't seem to be wide enough with some fonts so the // whole layout moves when we drag the gamma slider. m_gammaLabel->setMinimumWidth (m_gammaLabel->fontMetrics ().width (QStringLiteral (" 10.00 "))); m_gammaLabel->setAlignment (m_gammaLabel->alignment () | Qt::AlignRight); auto *gammaResetPushButton = new QPushButton (i18n ("Rese&t"), this); auto *spaceWidget = new QLabel (this); spaceWidget->setFixedSize (1, fontMetrics ().height () / 4); auto *channelLabel = new QLabel (i18n ("C&hannels:"), this); m_channelsComboBox = new QComboBox (this); m_channelsComboBox->addItem (i18n ("All")); m_channelsComboBox->addItem (i18n ("Red")); m_channelsComboBox->addItem (i18n ("Green")); m_channelsComboBox->addItem (i18n ("Blue")); auto *resetPushButton = new QPushButton (i18n ("Reset &All Values"), this); brightnessLabel->setBuddy (m_brightnessInput); contrastLabel->setBuddy (m_contrastInput); gammaLabel->setBuddy (m_gammaInput); channelLabel->setBuddy (m_channelsComboBox); lay->addWidget (brightnessLabel, 0, 0); lay->addWidget (m_brightnessInput, 0, 1, 1, 2); lay->addWidget (brightnessResetPushButton, 0, 4); lay->addWidget (contrastLabel, 1, 0); lay->addWidget (m_contrastInput, 1, 1, 1, 2); lay->addWidget (contrastResetPushButton, 1, 4); lay->addWidget (gammaLabel, 2, 0); lay->addWidget (m_gammaInput, 2, 1, 1, 2); lay->addWidget (m_gammaLabel, 2, 3); lay->addWidget (gammaResetPushButton, 2, 4); lay->addWidget (spaceWidget, 3, 0, 1, 5); lay->addWidget (resetPushButton, 4, 2, 1, 3, Qt::AlignRight); lay->addWidget (channelLabel, 4, 0); lay->addWidget (m_channelsComboBox, 4, 1, Qt::AlignLeft); //lay->addWidget (resetPushButton, 4, 2, Qt::AlignRight); lay->setColumnStretch (1, 1); // (no need for settingsChangedDelayed() since BCG effect is so fast :)) connect (m_brightnessInput, &kpIntNumInput::valueChanged, this, &kpEffectBalanceWidget::settingsChangedNoWaitCursor); connect (m_contrastInput, &kpIntNumInput::valueChanged, this, &kpEffectBalanceWidget::settingsChangedNoWaitCursor); connect (m_gammaInput, &kpIntNumInput::valueChanged, this, &kpEffectBalanceWidget::recalculateGammaLabel); connect (m_gammaInput, &kpIntNumInput::valueChanged, this, &kpEffectBalanceWidget::settingsChangedNoWaitCursor); connect (m_channelsComboBox, static_cast(&QComboBox::activated), this, &kpEffectBalanceWidget::settingsChanged); connect (brightnessResetPushButton, &QPushButton::clicked, this, &kpEffectBalanceWidget::resetBrightness); connect (contrastResetPushButton, &QPushButton::clicked, this, &kpEffectBalanceWidget::resetContrast); connect (gammaResetPushButton, &QPushButton::clicked, this, &kpEffectBalanceWidget::resetGamma); connect (resetPushButton, &QPushButton::clicked, this, &kpEffectBalanceWidget::resetAll); recalculateGammaLabel (); } kpEffectBalanceWidget::~kpEffectBalanceWidget () = default; // public virtual [base kpEffectWidgetBase] QString kpEffectBalanceWidget::caption () const { return i18n ("Settings"); } // public virtual [base kpEffectWidgetBase] bool kpEffectBalanceWidget::isNoOp () const { return (brightness () == 0 && contrast () == 0 && gamma () == 0); } // public virtual [base kpEffectWidgetBase] kpImage kpEffectBalanceWidget::applyEffect (const kpImage &image) { return kpEffectBalance::applyEffect (image, channels (), brightness (), contrast (), gamma ()); } // public virtual [base kpEffectWidgetBase] kpEffectCommandBase *kpEffectBalanceWidget::createCommand ( kpCommandEnvironment *cmdEnviron) const { return new kpEffectBalanceCommand (channels (), brightness (), contrast (), gamma (), m_actOnSelection, cmdEnviron); } // protected int kpEffectBalanceWidget::channels () const { switch (m_channelsComboBox->currentIndex ()) { default: case 0: return kpEffectBalance::RGB; case 1: return kpEffectBalance::Red; case 2: return kpEffectBalance::Green; case 3: return kpEffectBalance::Blue; } } // protected int kpEffectBalanceWidget::brightness () const { return m_brightnessInput->value (); } // protected int kpEffectBalanceWidget::contrast () const { return m_contrastInput->value (); } // protected int kpEffectBalanceWidget::gamma () const { return m_gammaInput->value (); } // protected slot void kpEffectBalanceWidget::recalculateGammaLabel () { m_gammaLabel->setText ( QLatin1String (" ") + QString::number (std::pow (10, gamma () / 50.0), 'f'/*[-]9.9*/, 2/*precision*/) + QLatin1String (" ")); m_gammaLabel->repaint (); } // protected slot void kpEffectBalanceWidget::resetBrightness () { if (brightness () == 0) { return; } bool sb = signalsBlocked (); if (!sb) { blockSignals (true); } m_brightnessInput->setValue (0); if (!sb) { blockSignals (false); } // Immediate update (if signals aren't blocked) emit settingsChanged (); } // protected slot void kpEffectBalanceWidget::resetContrast () { if (contrast () == 0) { return; } bool sb = signalsBlocked (); if (!sb) { blockSignals (true); } m_contrastInput->setValue (0); if (!sb) { blockSignals (false); } // Immediate update (if signals aren't blocked) emit settingsChanged (); } // protected slot void kpEffectBalanceWidget::resetGamma () { if (gamma () == 0) { return; } bool sb = signalsBlocked (); if (!sb) { blockSignals (true); } m_gammaInput->setValue (0); recalculateGammaLabel (); if (!sb) { blockSignals (false); } // Immediate update (if signals aren't blocked) emit settingsChanged (); } // protected slot void kpEffectBalanceWidget::resetAll () { if (isNoOp ()) { return; } // Prevent multiple settingsChanged() which would normally result in // redundant, expensive preview repaints blockSignals (true); resetBrightness (); resetContrast (); resetGamma (); recalculateGammaLabel (); blockSignals (false); emit settingsChanged (); } diff --git a/widgets/imagelib/effects/kpEffectEmbossWidget.cpp b/widgets/imagelib/effects/kpEffectEmbossWidget.cpp index 28158c40..42df48c0 100644 --- a/widgets/imagelib/effects/kpEffectEmbossWidget.cpp +++ b/widgets/imagelib/effects/kpEffectEmbossWidget.cpp @@ -1,106 +1,104 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_EMBOSS 1 - #include "kpEffectEmbossWidget.h" #include #include #include "kpLogCategories.h" #include #include "imagelib/effects/kpEffectEmboss.h" #include "commands/imagelib/effects/kpEffectEmbossCommand.h" kpEffectEmbossWidget::kpEffectEmbossWidget (bool actOnSelection, QWidget *parent) : kpEffectWidgetBase (actOnSelection, parent) { auto *lay = new QGridLayout (this); lay->setContentsMargins(0, 0, 0, 0); m_enableCheckBox = new QCheckBox (i18n ("E&nable"), this); lay->addWidget (m_enableCheckBox, 0, 0, 1, 2, Qt::AlignCenter); // (settingsChangedDelayed() instead of settingsChanged() so that the // user can quickly press OK to apply effect to document directly and // not have to wait for the also slow preview) connect (m_enableCheckBox, &QCheckBox::toggled, this, &kpEffectEmbossWidget::settingsChangedDelayed); } kpEffectEmbossWidget::~kpEffectEmbossWidget () = default; // public virtual [base kpEffectWidgetBase] QString kpEffectEmbossWidget::caption () const { return {}; } // public virtual [base kpEffectWidgetBase] bool kpEffectEmbossWidget::isNoOp () const { //return (m_amountInput->value () == 0); return !m_enableCheckBox->isChecked (); } // public virtual [base kpEffectWidgetBase] kpImage kpEffectEmbossWidget::applyEffect (const kpImage &image) { if (isNoOp ()) { return image; } return kpEffectEmboss::applyEffect (image, strength ()); } // public virtual [base kpEffectWidgetBase] kpEffectCommandBase *kpEffectEmbossWidget::createCommand ( kpCommandEnvironment *cmdEnviron) const { return new kpEffectEmbossCommand (strength (), m_actOnSelection, cmdEnviron); } // protected int kpEffectEmbossWidget::strength () const { return kpEffectEmboss::MaxStrength; } diff --git a/widgets/imagelib/effects/kpEffectFlattenWidget.cpp b/widgets/imagelib/effects/kpEffectFlattenWidget.cpp index 739b3461..cba33402 100644 --- a/widgets/imagelib/effects/kpEffectFlattenWidget.cpp +++ b/widgets/imagelib/effects/kpEffectFlattenWidget.cpp @@ -1,182 +1,176 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_FLATTEN 1 - #include "kpEffectFlattenWidget.h" #include "kpDefs.h" #include "imagelib/effects/kpEffectFlatten.h" #include "commands/imagelib/effects/kpEffectFlattenCommand.h" #include "kpLogCategories.h" #include #include #include #include #include #include // public static QColor kpEffectFlattenWidget::s_lastColor1; QColor kpEffectFlattenWidget::s_lastColor2; kpEffectFlattenWidget::kpEffectFlattenWidget (bool actOnSelection, QWidget *parent) : kpEffectWidgetBase (actOnSelection, parent) { if (!s_lastColor1.isValid () || !s_lastColor2.isValid ()) { KConfigGroup cfgGroupSaver (KSharedConfig::openConfig (), kpSettingsGroupFlattenEffect); s_lastColor1 = cfgGroupSaver.readEntry (kpSettingFlattenEffectColor1, QColor ()); if (!s_lastColor1.isValid ()) { s_lastColor1 = Qt::red; } s_lastColor2 = cfgGroupSaver.readEntry (kpSettingFlattenEffectColor2, QColor ()); if (!s_lastColor2.isValid ()) { s_lastColor2 = Qt::blue; } } m_enableCheckBox = new QCheckBox (i18n ("E&nable"), this); m_color1Button = new KColorButton (s_lastColor1, this); m_color2Button = new KColorButton (s_lastColor2, this); m_color1Button->setEnabled (false); m_color2Button->setEnabled (false); auto *lay = new QVBoxLayout (this); lay->setContentsMargins(0, 0, 0, 0); lay->addWidget (m_enableCheckBox); lay->addWidget (m_color1Button); lay->addWidget (m_color2Button); connect (m_enableCheckBox, &QCheckBox::toggled, this, &kpEffectFlattenWidget::slotEnableChanged); connect (m_color1Button, &KColorButton::changed, this, &kpEffectFlattenWidget::settingsChanged); connect (m_color2Button, &KColorButton::changed, this, &kpEffectFlattenWidget::settingsChanged); } kpEffectFlattenWidget::~kpEffectFlattenWidget () { s_lastColor1 = color1 (); s_lastColor2 = color2 (); KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupFlattenEffect); cfg.writeEntry (kpSettingFlattenEffectColor1, s_lastColor1); cfg.writeEntry (kpSettingFlattenEffectColor2, s_lastColor2); cfg.sync (); } // public QColor kpEffectFlattenWidget::color1 () const { return m_color1Button->color (); } // public QColor kpEffectFlattenWidget::color2 () const { return m_color2Button->color (); } // // kpEffectFlattenWidget implements kpEffectWidgetBase interface // // public virtual [base kpEffectWidgetBase] QString kpEffectFlattenWidget::caption () const { return i18n ("Colors"); } // public virtual [base kpEffectWidgetBase] bool kpEffectFlattenWidget::isNoOp () const { return !m_enableCheckBox->isChecked (); } // public virtual [base kpEffectWidgetBase] kpImage kpEffectFlattenWidget::applyEffect (const kpImage &image) { -#if DEBUG_KP_EFFECT_FLATTEN qCDebug(kpLogWidgets) << "kpEffectFlattenWidget::applyEffect() nop=" << isNoOp () << endl; -#endif if (isNoOp ()) { return image; } return kpEffectFlatten::applyEffect (image, color1 (), color2 ()); } // public virtual [base kpEffectWidgetBase] kpEffectCommandBase *kpEffectFlattenWidget::createCommand ( kpCommandEnvironment *cmdEnviron) const { return new kpEffectFlattenCommand (color1 (), color2 (), m_actOnSelection, cmdEnviron); } // protected slot: void kpEffectFlattenWidget::slotEnableChanged (bool enable) { -#if DEBUG_KP_EFFECT_FLATTEN qCDebug(kpLogWidgets) << "kpEffectFlattenWidget::slotEnableChanged(" << enable << ") enableButton=" << m_enableCheckBox->isChecked () << endl; -#endif m_color1Button->setEnabled (enable); m_color2Button->setEnabled (enable); emit settingsChanged (); } diff --git a/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp b/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp index e764e54a..7f09b730 100644 --- a/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp +++ b/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp @@ -1,178 +1,176 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define DEBUG_KP_EFFECT_REDUCE_COLORS 1 - #include "kpEffectReduceColorsWidget.h" #include "imagelib/effects/kpEffectReduceColors.h" #include "commands/imagelib/effects/kpEffectReduceColorsCommand.h" #include "pixmapfx/kpPixmapFX.h" #include "kpLogCategories.h" #include #include #include #include #include #include #include #include kpEffectReduceColorsWidget::kpEffectReduceColorsWidget (bool actOnSelection, QWidget *parent) : kpEffectWidgetBase (actOnSelection, parent) { auto *lay = new QVBoxLayout (this); lay->setContentsMargins(0, 0, 0, 0); m_blackAndWhiteRadioButton = new QRadioButton (i18n ("&Monochrome"), this); m_blackAndWhiteDitheredRadioButton = new QRadioButton (i18n ("Mo&nochrome (dithered)"), this); m_8BitRadioButton = new QRadioButton (i18n ("256 co&lor"), this); m_8BitDitheredRadioButton = new QRadioButton (i18n ("256 colo&r (dithered)"), this); m_24BitRadioButton = new QRadioButton (i18n ("24-&bit color"), this); // LOCOMPAT: don't think this is needed auto *buttonGroup = new QButtonGroup (this); buttonGroup->addButton (m_blackAndWhiteRadioButton); buttonGroup->addButton (m_blackAndWhiteDitheredRadioButton); buttonGroup->addButton (m_8BitRadioButton); buttonGroup->addButton (m_8BitDitheredRadioButton); buttonGroup->addButton (m_24BitRadioButton); m_defaultRadioButton = m_24BitRadioButton; m_defaultRadioButton->setChecked (true); lay->addWidget (m_blackAndWhiteRadioButton); lay->addWidget (m_blackAndWhiteDitheredRadioButton); lay->addWidget (m_8BitRadioButton); lay->addWidget (m_8BitDitheredRadioButton); lay->addWidget (m_24BitRadioButton); connect (m_blackAndWhiteRadioButton, &QRadioButton::toggled, this, &kpEffectReduceColorsWidget::settingsChanged); connect (m_blackAndWhiteDitheredRadioButton, &QRadioButton::toggled, this, &kpEffectReduceColorsWidget::settingsChanged); connect (m_8BitRadioButton, &QRadioButton::toggled, this, &kpEffectReduceColorsWidget::settingsChanged); connect (m_8BitDitheredRadioButton, &QRadioButton::toggled, this, &kpEffectReduceColorsWidget::settingsChanged); connect (m_24BitRadioButton, &QRadioButton::toggled, this, &kpEffectReduceColorsWidget::settingsChanged); } //--------------------------------------------------------------------- // public int kpEffectReduceColorsWidget::depth () const { // These values (1, 8, 32) are QImage's supported depths. // TODO: Qt-4.7.1: 1, 8, 16, 24 and 32 if (m_blackAndWhiteRadioButton->isChecked () || m_blackAndWhiteDitheredRadioButton->isChecked ()) { return 1; } if (m_8BitRadioButton->isChecked () || m_8BitDitheredRadioButton->isChecked ()) { return 8; } if (m_24BitRadioButton->isChecked ()) { return 32; } return 0; } //--------------------------------------------------------------------- // public bool kpEffectReduceColorsWidget::dither () const { return (m_blackAndWhiteDitheredRadioButton->isChecked () || m_8BitDitheredRadioButton->isChecked ()); } //--------------------------------------------------------------------- // // kpEffectReduceColorsWidget implements kpEffectWidgetBase interface // // public virtual [base kpEffectWidgetBase] QString kpEffectReduceColorsWidget::caption () const { return i18n ("Reduce To"); } //--------------------------------------------------------------------- // public virtual [base kpEffectWidgetBase] bool kpEffectReduceColorsWidget::isNoOp () const { return (!m_defaultRadioButton || m_defaultRadioButton->isChecked ()); } //--------------------------------------------------------------------- // public virtual [base kpEffectWidgetBase] kpImage kpEffectReduceColorsWidget::applyEffect (const kpImage &image) { return kpEffectReduceColors::applyEffect (image, depth (), dither ()); } //--------------------------------------------------------------------- // public virtual [base kpEffectWidgetBase] kpEffectCommandBase *kpEffectReduceColorsWidget::createCommand ( kpCommandEnvironment *cmdEnviron) const { return new kpEffectReduceColorsCommand (depth (), dither (), m_actOnSelection, cmdEnviron); } //---------------------------------------------------------------------