diff --git a/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp b/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp index 58a18842..37bba0f4 100644 --- a/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp +++ b/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp @@ -1,68 +1,68 @@ /* 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 0 +#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 dab84608..1d8aa8c9 100644 --- a/commands/imagelib/effects/kpEffectEmbossCommand.cpp +++ b/commands/imagelib/effects/kpEffectEmbossCommand.cpp @@ -1,55 +1,55 @@ /* 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 0 +#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 f76aa51d..aa3113ef 100644 --- a/commands/imagelib/effects/kpEffectFlattenCommand.cpp +++ b/commands/imagelib/effects/kpEffectFlattenCommand.cpp @@ -1,61 +1,61 @@ /* 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 0 +#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 d4977d66..32f5012c 100644 --- a/commands/imagelib/effects/kpEffectReduceColorsCommand.cpp +++ b/commands/imagelib/effects/kpEffectReduceColorsCommand.cpp @@ -1,80 +1,80 @@ /* 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 0 +#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 bade7642..2df9a55e 100644 --- a/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp +++ b/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp @@ -1,486 +1,486 @@ /* 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 0 -#define DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG 0 +#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 28e62588..33eb3bd5 100644 --- a/commands/imagelib/transforms/kpTransformRotateCommand.cpp +++ b/commands/imagelib/transforms/kpTransformRotateCommand.cpp @@ -1,220 +1,220 @@ /* 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 0 +#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 e8aeb337..36e5b773 100644 --- a/commands/imagelib/transforms/kpTransformSkewCommand.cpp +++ b/commands/imagelib/transforms/kpTransformSkewCommand.cpp @@ -1,195 +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_TOOL_SKEW 0 -#define DEBUG_KP_TOOL_SKEW_DIALOG 0 +#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 55e3e7c0..e0f01c4b 100644 --- a/commands/kpCommand.cpp +++ b/commands/kpCommand.cpp @@ -1,83 +1,83 @@ /* 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 0 +#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 dd44de50..2b78d1fe 100644 --- a/commands/kpCommandHistory.cpp +++ b/commands/kpCommandHistory.cpp @@ -1,131 +1,132 @@ /* 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 0 +#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 1d55b263..aa2f0e70 100644 --- a/commands/kpCommandHistoryBase.cpp +++ b/commands/kpCommandHistoryBase.cpp @@ -1,729 +1,729 @@ /* 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 0 +#define DEBUG_KP_COMMAND_HISTORY 1 #include "kpCommandHistoryBase.h" #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 << ")" + << ",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 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 + 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 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 74d7ca21..2e1c1e36 100644 --- a/commands/kpCommandSize.cpp +++ b/commands/kpCommandSize.cpp @@ -1,152 +1,154 @@ /* 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 0 +#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 a1030396..3df8be33 100644 --- a/commands/kpMacroCommand.cpp +++ b/commands/kpMacroCommand.cpp @@ -1,147 +1,149 @@ /* 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 0 +#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 79827356..ad75bbdc 100644 --- a/commands/tools/flow/kpToolFlowCommand.cpp +++ b/commands/tools/flow/kpToolFlowCommand.cpp @@ -1,140 +1,140 @@ /* 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 0 +#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 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 5173fd68..25e9cdc3 100644 --- a/commands/tools/kpToolColorPickerCommand.cpp +++ b/commands/tools/kpToolColorPickerCommand.cpp @@ -1,81 +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_TOOL_COLOR_PICKER 0 +#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 bcd54fb4..ae5a88a4 100644 --- a/commands/tools/kpToolFloodFillCommand.cpp +++ b/commands/tools/kpToolFloodFillCommand.cpp @@ -1,169 +1,169 @@ /* 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 0 +#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 76ab1ae1..88a19067 100644 --- a/commands/tools/polygonal/kpToolPolygonalCommand.cpp +++ b/commands/tools/polygonal/kpToolPolygonalCommand.cpp @@ -1,124 +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_POLYGON 0 +#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 7cc27348..63f135dd 100644 --- a/commands/tools/rectangular/kpToolRectangularCommand.cpp +++ b/commands/tools/rectangular/kpToolRectangularCommand.cpp @@ -1,126 +1,126 @@ /* 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 0 +#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 2ef45955..1383aec8 100644 --- a/commands/tools/selection/kpToolImageSelectionTransparencyCommand.cpp +++ b/commands/tools/selection/kpToolImageSelectionTransparencyCommand.cpp @@ -1,96 +1,96 @@ /* 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 0 +#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 241fd951..fbcb512b 100644 --- a/commands/tools/selection/kpToolSelectionCreateCommand.cpp +++ b/commands/tools/selection/kpToolSelectionCreateCommand.cpp @@ -1,161 +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_SELECTION 0 +#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 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 2be17d86..e8037489 100644 --- a/commands/tools/selection/kpToolSelectionDestroyCommand.cpp +++ b/commands/tools/selection/kpToolSelectionDestroyCommand.cpp @@ -1,176 +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_TOOL_SELECTION 0 +#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 00904f36..2b3cfe9a 100644 --- a/commands/tools/selection/kpToolSelectionMoveCommand.cpp +++ b/commands/tools/selection/kpToolSelectionMoveCommand.cpp @@ -1,219 +1,219 @@ /* 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 0 +#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 bd486443..c165f28f 100644 --- a/commands/tools/selection/kpToolSelectionPullFromDocumentCommand.cpp +++ b/commands/tools/selection/kpToolSelectionPullFromDocumentCommand.cpp @@ -1,140 +1,140 @@ /* 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 0 +#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 da772a1e..73dedda0 100644 --- a/commands/tools/selection/kpToolSelectionResizeScaleCommand.cpp +++ b/commands/tools/selection/kpToolSelectionResizeScaleCommand.cpp @@ -1,257 +1,257 @@ /* 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 0 +#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 03b3cc16..a5ad238d 100644 --- a/commands/tools/selection/text/kpToolTextBackspaceCommand.cpp +++ b/commands/tools/selection/text/kpToolTextBackspaceCommand.cpp @@ -1,152 +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_TEXT 0 +#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 1e9a73a1..d259e6d2 100644 --- a/commands/tools/selection/text/kpToolTextChangeStyleCommand.cpp +++ b/commands/tools/selection/text/kpToolTextChangeStyleCommand.cpp @@ -1,96 +1,96 @@ /* 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 0 +#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 459657c7..985d04d7 100644 --- a/commands/tools/selection/text/kpToolTextDeleteCommand.cpp +++ b/commands/tools/selection/text/kpToolTextDeleteCommand.cpp @@ -1,140 +1,140 @@ /* 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 0 +#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 c341c779..39b3cdbc 100644 --- a/commands/tools/selection/text/kpToolTextEnterCommand.cpp +++ b/commands/tools/selection/text/kpToolTextEnterCommand.cpp @@ -1,126 +1,126 @@ /* 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 0 +#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 7ee0c8fe..ec063c9e 100644 --- a/commands/tools/selection/text/kpToolTextGiveContentCommand.cpp +++ b/commands/tools/selection/text/kpToolTextGiveContentCommand.cpp @@ -1,151 +1,151 @@ /* 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 0 +#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 + << 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 7db6e13e..8992723f 100644 --- a/commands/tools/selection/text/kpToolTextInsertCommand.cpp +++ b/commands/tools/selection/text/kpToolTextInsertCommand.cpp @@ -1,110 +1,110 @@ /* 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 0 +#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 915ea0c7..02992617 100644 --- a/cursors/kpCursorLightCross.cpp +++ b/cursors/kpCursorLightCross.cpp @@ -1,131 +1,131 @@ /* 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 0 +#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 99779f10..88e2ea2b 100644 --- a/dialogs/imagelib/effects/kpEffectsDialog.cpp +++ b/dialogs/imagelib/effects/kpEffectsDialog.cpp @@ -1,372 +1,372 @@ /* 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 0 +#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 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 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 m_delayedUpdateTimer->stop (); // (single shot) m_delayedUpdateTimer->start (400/*ms*/); } diff --git a/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp b/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp index a475ea09..41115c96 100644 --- a/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp +++ b/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp @@ -1,753 +1,753 @@ /* 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 0 +#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 9833f989..6014bb2d 100644 --- a/dialogs/imagelib/transforms/kpTransformPreviewDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformPreviewDialog.cpp @@ -1,468 +1,468 @@ /* 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 0 +#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 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 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 } } // 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 4f18beae..24410bf7 100644 --- a/dialogs/imagelib/transforms/kpTransformResizeScaleDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformResizeScaleDialog.cpp @@ -1,840 +1,840 @@ /* 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 0 +#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 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 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 bae586a8..c41d31f5 100644 --- a/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp @@ -1,321 +1,321 @@ /* 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 0 +#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 c9e77e7f..8544c67a 100644 --- a/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp @@ -1,297 +1,297 @@ /* 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 0 -#define DEBUG_KP_TOOL_SKEW_DIALOG 0 +#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 25f6f738..5f73dde0 100644 --- a/dialogs/kpDocumentSaveOptionsPreviewDialog.cpp +++ b/dialogs/kpDocumentSaveOptionsPreviewDialog.cpp @@ -1,232 +1,232 @@ /* 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 0 +#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 4931f490..521ad5ee 100644 --- a/document/kpDocument.cpp +++ b/document/kpDocument.cpp @@ -1,459 +1,459 @@ /* 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 0 +#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 762ccaeb..b941b22b 100644 --- a/document/kpDocumentSaveOptions.cpp +++ b/document/kpDocumentSaveOptions.cpp @@ -1,623 +1,623 @@ /* 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 0 +#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 5f922de6..4b82a346 100644 --- a/document/kpDocument_Open.cpp +++ b/document/kpDocument_Open.cpp @@ -1,249 +1,249 @@ /* 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 0 +#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 00f67858..53041dd2 100644 --- a/document/kpDocument_Save.cpp +++ b/document/kpDocument_Save.cpp @@ -1,481 +1,481 @@ /* 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 0 +#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 ::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 ::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 ::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 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 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 04ae4721..4370fcfe 100644 --- a/document/kpDocument_Selection.cpp +++ b/document/kpDocument_Selection.cpp @@ -1,350 +1,350 @@ /* 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 0 +#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 21ac912a..950e0211 100644 --- a/environments/document/kpDocumentEnvironment.cpp +++ b/environments/document/kpDocumentEnvironment.cpp @@ -1,211 +1,211 @@ /* 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 0 +#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 "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 *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 6793bdea..66471cce 100644 --- a/generic/widgets/kpResizeSignallingLabel.cpp +++ b/generic/widgets/kpResizeSignallingLabel.cpp @@ -1,65 +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_RESIZE_SIGNALLING_LABEL 0 +#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 c63ae81f..f3997ed3 100644 --- a/imagelib/effects/kpEffectBalance.cpp +++ b/imagelib/effects/kpEffectBalance.cpp @@ -1,210 +1,210 @@ /* 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 0 +#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 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 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 6cabf9aa..967547c5 100644 --- a/imagelib/effects/kpEffectBlurSharpen.cpp +++ b/imagelib/effects/kpEffectBlurSharpen.cpp @@ -1,184 +1,184 @@ /* 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 0 +#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 //--------------------------------------------------------------------- // // 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 for (int i = 0; i < repeat; i++) { #if DEBUG_KP_EFFECT_BLUR_SHARPEN QTime timer; timer.start (); #endif 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 72b62ecc..35b981ac 100644 --- a/imagelib/effects/kpEffectEmboss.cpp +++ b/imagelib/effects/kpEffectEmboss.cpp @@ -1,81 +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_EFFECT_EMBOSS 0 +#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 b657fb35..e1dc4657 100644 --- a/imagelib/effects/kpEffectGrayscale.cpp +++ b/imagelib/effects/kpEffectGrayscale.cpp @@ -1,75 +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_PIXMAP_FX 0 +#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 db305b4d..537a291f 100644 --- a/imagelib/effects/kpEffectInvert.cpp +++ b/imagelib/effects/kpEffectInvert.cpp @@ -1,89 +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_EFFECT_INVERT 0 +#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 40ce0dba..afce41cc 100644 --- a/imagelib/effects/kpEffectReduceColors.cpp +++ b/imagelib/effects/kpEffectReduceColors.cpp @@ -1,240 +1,240 @@ /* 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 0 +#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++) { fprintf (stderr, " %08X", image.pixel (x, y)); } 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 12b2f3b0..a1e63829 100644 --- a/imagelib/kpColor.cpp +++ b/imagelib/kpColor.cpp @@ -1,317 +1,317 @@ /* 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 0 +#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 b0292285..b518a758 100644 --- a/imagelib/kpColor_Constants.cpp +++ b/imagelib/kpColor_Constants.cpp @@ -1,117 +1,117 @@ /* 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 0 +#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 566dadee..fbfa18f3 100644 --- a/imagelib/kpFloodFill.cpp +++ b/imagelib/kpFloodFill.cpp @@ -1,430 +1,430 @@ /* 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 0 +#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 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 qCDebug(kpLogImagelib) << "Expanding from y=" << (*it).m_y << " x1=" << (*it).m_x1 << " x2=" << (*it).m_x2 << endl; #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 d20e7ccd..f9497600 100644 --- a/imagelib/kpPainter.cpp +++ b/imagelib/kpPainter.cpp @@ -1,523 +1,523 @@ /* 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 0 +#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 "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 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 fprintf (stderr, "similar\n"); #endif if (startDrawX < 0) { startDrawX = x; } } else { #if DEBUG_KP_PAINTER && 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 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 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; 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 // 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; 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 0a91e986..82e1edda 100644 --- a/imagelib/transforms/kpTransformAutoCrop.cpp +++ b/imagelib/transforms/kpTransformAutoCrop.cpp @@ -1,756 +1,756 @@ /* 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 0 +#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 34d1011e..ce636c0e 100644 --- a/imagelib/transforms/kpTransformCrop.cpp +++ b/imagelib/transforms/kpTransformCrop.cpp @@ -1,77 +1,77 @@ /* 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 0 +#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 7a527e73..30a2937a 100644 --- a/imagelib/transforms/kpTransformCrop_ImageSelection.cpp +++ b/imagelib/transforms/kpTransformCrop_ImageSelection.cpp @@ -1,257 +1,258 @@ /* 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 0 +#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 f629b31e..78dd0665 100644 --- a/imagelib/transforms/kpTransformCrop_TextSelection.cpp +++ b/imagelib/transforms/kpTransformCrop_TextSelection.cpp @@ -1,76 +1,77 @@ /* 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 0 +#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/kpLogCategories.cpp b/kpLogCategories.cpp index c6a6fe23..6d3216cc 100644 --- a/kpLogCategories.cpp +++ b/kpLogCategories.cpp @@ -1,41 +1,42 @@ /* Copyright (c) 2016 Martin Sandsmark 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. */ #include "kpLogCategories.h" +#include -Q_LOGGING_CATEGORY(kpLogMisc, "kp.misc") -Q_LOGGING_CATEGORY(kpLogDialogs, "kp.dialogs") -Q_LOGGING_CATEGORY(kpLogCommands, "kp.commands") -Q_LOGGING_CATEGORY(kpLogDocument, "kp.document") -Q_LOGGING_CATEGORY(kpLogTools, "kp.tools") -Q_LOGGING_CATEGORY(kpLogViews, "kp.views") -Q_LOGGING_CATEGORY(kpLogEnvironments, "kp.environments") -Q_LOGGING_CATEGORY(kpLogPixmapfx, "kp.pixmapfx") -Q_LOGGING_CATEGORY(kpLogWidgets, "kp.widgets") -Q_LOGGING_CATEGORY(kpLogMainWindow, "kp.mainwindow") -Q_LOGGING_CATEGORY(kpLogLayers, "kp.layers") -Q_LOGGING_CATEGORY(kpLogImagelib, "kp.imagelib") +Q_LOGGING_CATEGORY(kpLogMisc, "kp.misc", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogDialogs, "kp.dialogs", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogCommands, "kp.commands", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogDocument, "kp.document", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogTools, "kp.tools", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogViews, "kp.views", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogEnvironments, "kp.environments", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogPixmapfx, "kp.pixmapfx", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogWidgets, "kp.widgets", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogMainWindow, "kp.mainwindow", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogLayers, "kp.layers", QtWarningMsg) +Q_LOGGING_CATEGORY(kpLogImagelib, "kp.imagelib", QtWarningMsg) diff --git a/kpThumbnail.cpp b/kpThumbnail.cpp index 206d0eef..9f38ca24 100644 --- a/kpThumbnail.cpp +++ b/kpThumbnail.cpp @@ -1,183 +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_THUMBNAIL 0 +#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 77a69148..41675ee1 100644 --- a/kpViewScrollableContainer.cpp +++ b/kpViewScrollableContainer.cpp @@ -1,1208 +1,1208 @@ /* 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 0 +#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())); + 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 288dc92c..5b739aa1 100644 --- a/layers/selections/image/kpAbstractImageSelection.cpp +++ b/layers/selections/image/kpAbstractImageSelection.cpp @@ -1,589 +1,589 @@ /* 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 0 +#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 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 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 ec475d0b..a9098f6c 100644 --- a/layers/selections/image/kpEllipticalImageSelection.cpp +++ b/layers/selections/image/kpEllipticalImageSelection.cpp @@ -1,185 +1,185 @@ /* 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 0 +#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 1268fe2a..82c47c40 100644 --- a/layers/selections/image/kpFreeFormImageSelection.cpp +++ b/layers/selections/image/kpFreeFormImageSelection.cpp @@ -1,394 +1,394 @@ /* 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 0 +#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 37b79ef5..519fb15e 100644 --- a/layers/selections/image/kpImageSelectionTransparency.cpp +++ b/layers/selections/image/kpImageSelectionTransparency.cpp @@ -1,206 +1,206 @@ /* 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 0 +#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 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; qCDebug(kpLogLayers) << "\tcolour similarity: lhs=" << m_colorSimilarity << " rhs=" << rhs.m_colorSimilarity << endl; #endif 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 e219c8e1..514d1fe8 100644 --- a/layers/selections/image/kpRectangularImageSelection.cpp +++ b/layers/selections/image/kpRectangularImageSelection.cpp @@ -1,149 +1,149 @@ /* 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 0 +#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 f8f09b0e..5cf44f2e 100644 --- a/layers/selections/kpAbstractSelection.cpp +++ b/layers/selections/kpAbstractSelection.cpp @@ -1,315 +1,315 @@ /* 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 0 +#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 7d446a23..ca2a8290 100644 --- a/layers/selections/kpSelectionDrag.cpp +++ b/layers/selections/kpSelectionDrag.cpp @@ -1,165 +1,165 @@ /* 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 0 +#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 1c6bba3c..21372b7b 100644 --- a/layers/selections/kpSelectionFactory.cpp +++ b/layers/selections/kpSelectionFactory.cpp @@ -1,93 +1,94 @@ /* 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 0 +#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 e6157edf..0263f2a1 100644 --- a/layers/selections/text/kpTextSelection.cpp +++ b/layers/selections/text/kpTextSelection.cpp @@ -1,347 +1,347 @@ /* 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 0 +#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 9503032f..a90078c2 100644 --- a/layers/selections/text/kpTextSelection_Cursor.cpp +++ b/layers/selections/text/kpTextSelection_Cursor.cpp @@ -1,129 +1,129 @@ /* 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 0 +#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 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 4811d5cf..6c962eef 100644 --- a/layers/selections/text/kpTextSelection_Paint.cpp +++ b/layers/selections/text/kpTextSelection_Paint.cpp @@ -1,274 +1,274 @@ // 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 0 +#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 514a3d18..8ac75ace 100644 --- a/lgpl/generic/kpColorCollection.cpp +++ b/lgpl/generic/kpColorCollection.cpp @@ -1,520 +1,520 @@ // 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 0 +#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") +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 ac29994c..ce62c952 100644 --- a/lgpl/generic/widgets/kpColorCellsBase.cpp +++ b/lgpl/generic/widgets/kpColorCellsBase.cpp @@ -1,563 +1,563 @@ /* 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 0 +#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/mainWindow/kpMainWindowPrivate.h b/mainWindow/kpMainWindowPrivate.h index 64fb6046..1ec1f9e0 100644 --- a/mainWindow/kpMainWindowPrivate.h +++ b/mainWindow/kpMainWindowPrivate.h @@ -1,445 +1,445 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2014 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. */ #ifndef kpMainWindowPrivate_H #define kpMainWindowPrivate_H -#define DEBUG_KP_MAIN_WINDOW 0 +#define DEBUG_KP_MAIN_WINDOW 1 #include "document/kpDocumentSaveOptions.h" class QAction; class QActionGroup; class QLabel; class KSelectAction; class KToggleAction; class KSqueezedTextLabel; class KRecentFilesAction; class KFontAction; class KFontSizeAction; class KToggleFullScreenAction; class kpCommandEnvironment; class kpDocumentEnvironment; class kpToolSelectionEnvironment; class kpTransformDialogEnvironment; class kpViewScrollableContainer; class kpZoomedView; class kpThumbnail; class kpThumbnailView; class kpDocument; class kpViewManager; class kpColorToolBar; class kpToolToolBar; class kpCommandHistory; class kpTool; class kpToolText; class SaneDialog; struct kpMainWindowPrivate { kpMainWindowPrivate () : isFullyConstructed(false), scrollView(nullptr), mainView(nullptr), thumbnail(nullptr), thumbnailView(nullptr), document(nullptr), viewManager(nullptr), colorToolBar(nullptr), toolToolBar(nullptr), commandHistory(nullptr), configFirstTime(false), configShowGrid(false), configShowPath(false), configThumbnailShown(false), configZoomedThumbnail(false), documentEnvironment(nullptr), commandEnvironment(nullptr), // Tools toolSelectionEnvironment(nullptr), toolsActionGroup(nullptr), toolSpraycan(nullptr), toolBrush(nullptr), toolColorEraser(nullptr), toolColorPicker(nullptr), toolCurve(nullptr), toolEllipse(nullptr), toolEllipticalSelection(nullptr), toolEraser(nullptr), toolFloodFill(nullptr), toolFreeFormSelection(nullptr), toolLine(nullptr), toolPen(nullptr), toolPolygon(nullptr), toolPolyline(nullptr), toolRectangle(nullptr), toolRectSelection(nullptr), toolRoundedRectangle(nullptr), toolZoom(nullptr), toolText(nullptr), lastToolNumber(0), toolActionsEnabled(false), actionPrevToolOptionGroup1(nullptr), actionNextToolOptionGroup1(nullptr), actionPrevToolOptionGroup2(nullptr), actionNextToolOptionGroup2(nullptr), settingImageSelectionTransparency(0), docResizeWidth(0), docResizeHeight(0), docResizeToBeCompleted(false), configOpenImagesInSameWindow(false), configPrintImageCenteredOnPage(false), actionNew(nullptr), actionOpen(nullptr), actionOpenRecent(nullptr), actionScan(nullptr), actionScreenshot(nullptr), actionProperties(nullptr), actionSave(nullptr), actionSaveAs(nullptr), actionExport(nullptr), actionReload(nullptr), actionPrint(nullptr), actionPrintPreview(nullptr), actionMail(nullptr), actionClose(nullptr), actionQuit(nullptr), scanDialog(nullptr), exportFirstTime(false), // Edit Menu editMenuDocumentActionsEnabled(false), actionUndo(nullptr), actionRedo(nullptr), actionCut(nullptr), actionCopy(nullptr), actionPaste(nullptr), actionPasteInNewWindow(nullptr), actionDelete(nullptr), actionSelectAll(nullptr), actionDeselect(nullptr), actionCopyToFile(nullptr), actionPasteFromFile(nullptr), copyToFirstTime(false), // View Menu configThumbnailShowRectangle(false), actionShowThumbnailRectangle(nullptr), viewMenuDocumentActionsEnabled(false), actionActualSize(nullptr), actionFitToPage(nullptr), actionFitToWidth(nullptr), actionFitToHeight(nullptr), actionZoomIn(nullptr), actionZoomOut(nullptr), actionZoom(nullptr), actionShowGrid(nullptr), actionShowThumbnail(nullptr), actionZoomedThumbnail(nullptr), thumbnailSaveConfigTimer(nullptr), // Image Menu transformDialogEnvironment(nullptr), imageMenuDocumentActionsEnabled(false), actionResizeScale(nullptr), actionCrop(nullptr), actionAutoCrop(nullptr), actionFlip(nullptr), actionMirror(nullptr), actionRotate(nullptr), actionRotateLeft(nullptr), actionRotateRight(nullptr), actionSkew(nullptr), actionConvertToBlackAndWhite(nullptr), actionConvertToGrayscale(nullptr), actionBlur(nullptr), actionMoreEffects(nullptr), actionInvertColors(nullptr), actionClear(nullptr), actionDrawOpaque(nullptr), actionDrawColorSimilarity(nullptr), moreEffectsDialogLastEffect(0), // Colors Menu colorMenuDocumentActionsEnabled(false), actionColorsDefault(nullptr), actionColorsKDE(nullptr), actionColorsOpen(nullptr), actionColorsReload(nullptr), actionColorsSave(nullptr), actionColorsSaveAs(nullptr), actionColorsAppendRow(nullptr), actionColorsDeleteRow(nullptr), // Settings Menu actionShowPath(nullptr), actionKeyBindings(nullptr), actionConfigureToolbars(nullptr), actionConfigure(nullptr), actionFullScreen(nullptr), // Status Bar statusBarCreated(false), statusBarMessageLabel(nullptr), statusBarShapeLastPointsInitialised(false), statusBarShapeLastSizeInitialised(false), // Text ToolBar actionTextFontFamily(nullptr), actionTextFontSize(nullptr), actionTextBold(nullptr), actionTextItalic(nullptr), actionTextUnderline(nullptr), actionTextStrikeThru(nullptr), settingTextStyle(0), textOldFontSize(0) { } bool isFullyConstructed; kpViewScrollableContainer *scrollView; kpZoomedView *mainView; kpThumbnail *thumbnail; kpThumbnailView *thumbnailView; kpDocument *document; kpViewManager *viewManager; kpColorToolBar *colorToolBar; kpToolToolBar *toolToolBar; kpCommandHistory *commandHistory; bool configFirstTime; bool configShowGrid; bool configShowPath; bool configThumbnailShown; QRect configThumbnailGeometry; bool configZoomedThumbnail; kpDocumentEnvironment *documentEnvironment; kpCommandEnvironment *commandEnvironment; // // Tools // kpToolSelectionEnvironment *toolSelectionEnvironment; QActionGroup *toolsActionGroup; kpTool *toolSpraycan, *toolBrush, *toolColorEraser, *toolColorPicker, *toolCurve, *toolEllipse, *toolEllipticalSelection, *toolEraser, *toolFloodFill, *toolFreeFormSelection, *toolLine, *toolPen, *toolPolygon, *toolPolyline, *toolRectangle, *toolRectSelection, *toolRoundedRectangle, *toolZoom; kpToolText *toolText; QList tools; int lastToolNumber; bool toolActionsEnabled; QAction *actionPrevToolOptionGroup1, *actionNextToolOptionGroup1, *actionPrevToolOptionGroup2, *actionNextToolOptionGroup2; int settingImageSelectionTransparency; int docResizeWidth, docResizeHeight; bool docResizeToBeCompleted; // // File Menu // bool configOpenImagesInSameWindow, configPrintImageCenteredOnPage; QAction *actionNew, *actionOpen; KRecentFilesAction *actionOpenRecent; QAction *actionScan, *actionScreenshot, *actionProperties, *actionSave, *actionSaveAs, *actionExport, *actionReload, *actionPrint, *actionPrintPreview, *actionMail, *actionClose, *actionQuit; SaneDialog *scanDialog; QUrl lastExportURL; kpDocumentSaveOptions lastExportSaveOptions; bool exportFirstTime; // // Edit Menu // bool editMenuDocumentActionsEnabled; QAction *actionUndo, *actionRedo, *actionCut, *actionCopy, *actionPaste, *actionPasteInNewWindow, *actionDelete, *actionSelectAll, *actionDeselect, *actionCopyToFile, *actionPasteFromFile; QUrl lastCopyToURL; kpDocumentSaveOptions lastCopyToSaveOptions; bool copyToFirstTime; // // View Menu // bool configThumbnailShowRectangle; KToggleAction *actionShowThumbnailRectangle; bool viewMenuDocumentActionsEnabled; QAction *actionActualSize, *actionFitToPage, *actionFitToWidth, *actionFitToHeight, *actionZoomIn, *actionZoomOut; KSelectAction *actionZoom; KToggleAction *actionShowGrid, *actionShowThumbnail, *actionZoomedThumbnail; QList zoomList; QTimer *thumbnailSaveConfigTimer; // // Image Menu // kpTransformDialogEnvironment *transformDialogEnvironment; bool imageMenuDocumentActionsEnabled; QAction *actionResizeScale, *actionCrop, *actionAutoCrop, *actionFlip, *actionMirror, *actionRotate, *actionRotateLeft, *actionRotateRight, *actionSkew, *actionConvertToBlackAndWhite, *actionConvertToGrayscale, *actionBlur, *actionMoreEffects, *actionInvertColors, *actionClear; // Implemented in kpMainWindow_Tools.cpp, not kpImageWindow_Image.cpp // since they're really setting tool options. KToggleAction *actionDrawOpaque; QAction *actionDrawColorSimilarity; int moreEffectsDialogLastEffect; // // Colors Menu // bool colorMenuDocumentActionsEnabled; QAction *actionColorsDefault; KSelectAction *actionColorsKDE; QAction *actionColorsOpen, *actionColorsReload; QAction *actionColorsSave, *actionColorsSaveAs; QAction *actionColorsAppendRow; QAction *actionColorsDeleteRow; // // Settings Menu // KToggleAction *actionShowPath; QAction *actionKeyBindings, *actionConfigureToolbars, *actionConfigure; KToggleFullScreenAction *actionFullScreen; // // Status Bar // bool statusBarCreated; KSqueezedTextLabel *statusBarMessageLabel; QList statusBarLabels; bool statusBarShapeLastPointsInitialised; QPoint statusBarShapeLastStartPoint, statusBarShapeLastEndPoint; bool statusBarShapeLastSizeInitialised; QSize statusBarShapeLastSize; // // Text ToolBar // KFontAction *actionTextFontFamily; KFontSizeAction *actionTextFontSize; KToggleAction *actionTextBold, *actionTextItalic, *actionTextUnderline, *actionTextStrikeThru; int settingTextStyle; QString textOldFontFamily; int textOldFontSize; }; #endif // kpMainWindowPrivate_H diff --git a/pixmapfx/kpPixmapFX_DrawShapes.cpp b/pixmapfx/kpPixmapFX_DrawShapes.cpp index 6e164e10..fa47a1a5 100644 --- a/pixmapfx/kpPixmapFX_DrawShapes.cpp +++ b/pixmapfx/kpPixmapFX_DrawShapes.cpp @@ -1,285 +1,285 @@ /* 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 0 +#define DEBUG_KP_PIXMAP_FX 1 #include "kpPixmapFX.h" #include #include #include #include #include #include "kpLogCategories.h" #include "layers/selections/kpAbstractSelection.h" #include "imagelib/kpColor.h" #include "kpDefs.h" //--------------------------------------------------------------------- // Returns whether there is only 1 distinct point in . bool kpPixmapFX::Only1PixelInPointArray (const QPolygon &points) { if (points.count () == 0) { return false; } for (int i = 1; i < static_cast (points.count ()); i++) { if (points [i] != points [0]) { return false; } } return true; } //--------------------------------------------------------------------- // Warp the given from 1 to 0. // This is not always done (specifically if ) because // width 0 sometimes looks worse. // // Qt lines of width 1 look like they have a width between 1-2 i.e.: // // # // ## // # // # // // compared to Qt's special "width 0" which just means a "proper" width 1: // // # // # // # // # // static int WidthToQPenWidth (int width, bool drawingEllipse = false) { if (width == 1) { // 3x10 ellipse with Qt width 0 looks like rectangle. // Therefore, do not apply this 1 -> 0 transformations for ellipses. if (!drawingEllipse) { // Closer to looking width 1, for lines at least. return 0; } } return width; } //--------------------------------------------------------------------- static void QPainterSetPenWithStipple (QPainter *p, const kpColor &fColor, int penWidth, const kpColor &fStippleColor = kpColor::Invalid, bool isEllipseLike = false) { if (!fStippleColor.isValid ()) { p->setPen ( kpPixmapFX::QPainterDrawLinePen ( fColor.toQColor(), ::WidthToQPenWidth (penWidth, isEllipseLike))); } else { QPen usePen = kpPixmapFX::QPainterDrawLinePen ( fColor.toQColor(), ::WidthToQPenWidth (penWidth, isEllipseLike)); usePen.setStyle (Qt::DashLine); p->setPen (usePen); p->setBackground (fStippleColor.toQColor()); p->setBackgroundMode (Qt::OpaqueMode); } } //--------------------------------------------------------------------- // public static QPen kpPixmapFX::QPainterDrawRectPen (const QColor &color, int qtWidth) { return QPen (color, qtWidth, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin); } //--------------------------------------------------------------------- // public static QPen kpPixmapFX::QPainterDrawLinePen (const QColor &color, int qtWidth) { return QPen (color, qtWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); } //--------------------------------------------------------------------- // // drawPolyline() / drawLine() // // public static void kpPixmapFX::drawPolyline (QImage *image, const QPolygon &points, const kpColor &color, int penWidth, const kpColor &stippleColor) { QPainter painter(image); ::QPainterSetPenWithStipple(&painter, color, penWidth, stippleColor); // Qt bug: single point doesn't show up depending on penWidth. if (Only1PixelInPointArray(points)) { #if DEBUG_KP_PIXMAP_FX qCDebug(kpLogPixmapfx) << "\tinvoking single point hack"; #endif painter.drawPoint(points[0]); return; } painter.drawPolyline(points); } //--------------------------------------------------------------------- // // drawPolygon() // // public static void kpPixmapFX::drawPolygon (QImage *image, const QPolygon &points, const kpColor &fcolor, int penWidth, const kpColor &bcolor, bool isFinal, const kpColor &fStippleColor) { QPainter p(image); ::QPainterSetPenWithStipple (&p, fcolor, penWidth, fStippleColor); if (bcolor.isValid ()) { p.setBrush (QBrush (bcolor.toQColor())); } // HACK: seems to be needed if set_Pen_(Qt::color0) else fills with Qt::color0. else { p.setBrush (Qt::NoBrush); } // Qt bug: single point doesn't show up depending on penWidth. if (Only1PixelInPointArray (points)) { #if DEBUG_KP_PIXMAP_FX qCDebug(kpLogPixmapfx) << "\tinvoking single point hack"; #endif p.drawPoint(points [0]); return; } // TODO: why aren't the ends rounded? p.drawPolygon(points, Qt::OddEvenFill); if ( isFinal ) { return; } if ( points.count() <= 2 ) { return; } p.setCompositionMode(QPainter::RasterOp_SourceXorDestination); p.setPen(QPen(Qt::white)); p.drawLine(points[0], points[points.count() - 1]); } //--------------------------------------------------------------------- // public static void kpPixmapFX::fillRect (QImage *image, int x, int y, int width, int height, const kpColor &color, const kpColor &stippleColor) { QPainter painter(image); if (!stippleColor.isValid ()) { painter.fillRect (x, y, width, height, color.toQColor()); } else { const int StippleSize = 4; painter.setClipRect (x, y, width, height); for (int dy = 0; dy < height; dy += StippleSize) { for (int dx = 0; dx < width; dx += StippleSize) { const bool parity = ((dy + dx) / StippleSize) % 2; kpColor useColor; if (!parity) { useColor = color; } else { useColor = stippleColor; } painter.fillRect (x + dx, y + dy, StippleSize, StippleSize, useColor.toQColor()); } } } } //--------------------------------------------------------------------- void kpPixmapFX::drawStippleRect(QImage *image, int x, int y, int width, int height, const kpColor &fColor, const kpColor &fStippleColor) { QPainter painter(image); painter.setPen(QPen(fColor.toQColor(), 1, Qt::DashLine)); painter.setBackground(fStippleColor.toQColor()); painter.setBackgroundMode(Qt::OpaqueMode); painter.drawRect(x, y, width - 1, height - 1); } //--------------------------------------------------------------------- diff --git a/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp b/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp index 2c28747e..74d8f9af 100644 --- a/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp +++ b/pixmapfx/kpPixmapFX_GetSetPixmapParts.cpp @@ -1,142 +1,142 @@ /* 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 0 +#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/pixmapfx/kpPixmapFX_Transforms.cpp b/pixmapfx/kpPixmapFX_Transforms.cpp index 083c66d4..8e38752f 100644 --- a/pixmapfx/kpPixmapFX_Transforms.cpp +++ b/pixmapfx/kpPixmapFX_Transforms.cpp @@ -1,669 +1,669 @@ /* 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 0 +#define DEBUG_KP_PIXMAP_FX 1 #include "kpPixmapFX.h" #include #include #include #include #include #include "kpLogCategories.h" #include "layers/selections/kpAbstractSelection.h" #include "imagelib/kpColor.h" #include "kpDefs.h" //--------------------------------------------------------------------- // public static void kpPixmapFX::resize (QImage *destPtr, int w, int h, const kpColor &backgroundColor) { #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "kpPixmapFX::resize()"; #endif if (!destPtr) { return; } const int oldWidth = destPtr->width (); const int oldHeight = destPtr->height (); if (w == oldWidth && h == oldHeight) { return; } QImage newImage (w, h, QImage::Format_ARGB32_Premultiplied); // Would have new undefined areas? if (w > oldWidth || h > oldHeight) { newImage.fill (backgroundColor.toQRgb ()); } // Copy over old pixmap. QPainter painter(&newImage); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.drawImage(0, 0, *destPtr); painter.end(); // Replace pixmap with new one. *destPtr = newImage; } //--------------------------------------------------------------------- // public static QImage kpPixmapFX::resize (const QImage &image, int w, int h, const kpColor &backgroundColor) { QImage ret = image; kpPixmapFX::resize (&ret, w, h, backgroundColor); return ret; } //--------------------------------------------------------------------- // public static void kpPixmapFX::scale (QImage *destPtr, int w, int h, bool pretty) { if (!destPtr) { return; } *destPtr = kpPixmapFX::scale (*destPtr, w, h, pretty); } //--------------------------------------------------------------------- // public static QImage kpPixmapFX::scale (const QImage &image, int w, int h, bool pretty) { #if DEBUG_KP_PIXMAP_FX && 0 qCDebug(kpLogPixmapfx) << "kpPixmapFX::scale(oldRect=" << image.rect () << ",w=" << w << ",h=" << h << ",pretty=" << pretty << ")"; #endif if (w == image.width () && h == image.height ()) { return image; } return image.scaled(w, h, Qt::IgnoreAspectRatio, pretty ? Qt::SmoothTransformation : Qt::FastTransformation); } //--------------------------------------------------------------------- // public static const double kpPixmapFX::AngleInDegreesEpsilon = qRadiansToDegrees (std::tan (1.0 / 10000.0)) / (2.0/*max error allowed*/ * 2.0/*for good measure*/); static void MatrixDebug (const QString& matrixName, const QTransform &matrix, int srcPixmapWidth = -1, int srcPixmapHeight = -1) { #if DEBUG_KP_PIXMAP_FX const int w = srcPixmapWidth, h = srcPixmapHeight; qCDebug(kpLogPixmapfx) << matrixName << "=" << matrix; // Sometimes this precision lets us see unexpected rounding errors. fprintf (stderr, "m11=%.24f m12=%.24f m21=%.24f m22=%.24f dx=%.24f dy=%.24f\n", matrix.m11 (), matrix.m12 (), matrix.m21 (), matrix.m22 (), matrix.dx (), matrix.dy ()); if (w > 0 && h > 0) { qCDebug(kpLogPixmapfx) << "(0,0) ->" << matrix.map (QPoint (0, 0)); qCDebug(kpLogPixmapfx) << "(w-1,0) ->" << matrix.map (QPoint (w - 1, 0)); qCDebug(kpLogPixmapfx) << "(0,h-1) ->" << matrix.map (QPoint (0, h - 1)); qCDebug(kpLogPixmapfx) << "(w-1,h-1) ->" << matrix.map (QPoint (w - 1, h - 1)); } #else Q_UNUSED (matrixName); Q_UNUSED (matrix); Q_UNUSED (srcPixmapWidth); Q_UNUSED (srcPixmapHeight); #endif // DEBUG_KP_PIXMAP_FX } //--------------------------------------------------------------------- // Theoretically, this should act the same as QPixmap::trueMatrix() but // it doesn't. As an example, if you rotate tests/transforms.png by 90 // degrees clockwise, this returns the correct of 26 but // QPixmap::trueMatrix() returns 27. // // You should use the returned matrix to map points accurately (e.g. selection // borders). For QPainter::drawPixmap()/drawImage() + setWorldMatrix() // rendering accuracy, pass the returned matrix through QPixmap::trueMatrix() // and use that. // // TODO: If you put the flipMatrix() of tests/transforms.png through this, // the output is the same as QPixmap::trueMatrix(): is one off // (dy=27 instead of 26). // SYNC: I bet this is a Qt4 bug. static QTransform MatrixWithZeroOrigin (const QTransform &matrix, int width, int height) { #if DEBUG_KP_PIXMAP_FX qCDebug(kpLogPixmapfx) << "matrixWithZeroOrigin(w=" << width << ",h=" << height << ")"; qCDebug(kpLogPixmapfx) << "\tmatrix: m11=" << matrix.m11 () << "m12=" << matrix.m12 () << "m21=" << matrix.m21 () << "m22=" << matrix.m22 () << "dx=" << matrix.dx () << "dy=" << matrix.dy (); #endif QRect mappedRect = matrix.mapRect (QRect (0, 0, width, height)); #if DEBUG_KP_PIXMAP_FX qCDebug(kpLogPixmapfx) << "\tmappedRect=" << mappedRect; #endif QTransform translatedMatrix ( matrix.m11 (), matrix.m12 (), matrix.m21 (), matrix.m22 (), matrix.dx () - mappedRect.left (), matrix.dy () - mappedRect.top ()); #if DEBUG_KP_PIXMAP_FX qCDebug(kpLogPixmapfx) << "\treturning" << translatedMatrix; qCDebug(kpLogPixmapfx) << "(0,0) ->" << translatedMatrix.map (QPoint (0, 0)); qCDebug(kpLogPixmapfx) << "(w-1,0) ->" << translatedMatrix.map (QPoint (width - 1, 0)); qCDebug(kpLogPixmapfx) << "(0,h-1) ->" << translatedMatrix.map (QPoint (0, height - 1)); qCDebug(kpLogPixmapfx) << "(w-1,h-1) ->" << translatedMatrix.map (QPoint (width - 1, height - 1)); #endif return translatedMatrix; } //--------------------------------------------------------------------- static double TrueMatrixEpsilon = 0.000001; // An attempt to reverse tiny rounding errors introduced by QPixmap::trueMatrix() // when skewing tests/transforms.png by 45% horizontally (with TransformPixmap() // using a QPixmap painter, prior to the 2007-10-09 change -- did not test after // the change). // Unfortunately, this does not work enough to stop the rendering errors // that follow. But it was worth a try and might still help us given the // sometimes excessive aliasing QPainter::draw{Pixmap,Image}() gives us, when // QPainter::SmoothPixmapTransform is disabled. static double TrueMatrixFixInts (double x) { if (std::fabs (x - qRound (x)) < TrueMatrixEpsilon) { return qRound (x); } return x; } //--------------------------------------------------------------------- static QTransform TrueMatrix (const QTransform &matrix, int srcPixmapWidth, int srcPixmapHeight) { ::MatrixDebug (QStringLiteral("TrueMatrix(): org"), matrix); const QTransform truMat = QPixmap::trueMatrix (matrix, srcPixmapWidth, srcPixmapHeight); ::MatrixDebug (QStringLiteral("TrueMatrix(): passed through QPixmap::trueMatrix()"), truMat); const QTransform retMat ( ::TrueMatrixFixInts (truMat.m11 ()), ::TrueMatrixFixInts (truMat.m12 ()), ::TrueMatrixFixInts (truMat.m21 ()), ::TrueMatrixFixInts (truMat.m22 ()), ::TrueMatrixFixInts (truMat.dx ()), ::TrueMatrixFixInts (truMat.dy ())); ::MatrixDebug (QStringLiteral("TrueMatrix(): fixed ints"), retMat); return retMat; } //--------------------------------------------------------------------- // Like QPixmap::transformed() but fills new areas with // (unless is invalid) and works around internal QTransform // floating point -> integer oddities, that would otherwise give fatally // incorrect results. If you don't believe me on this latter point, compare // QPixmap::transformed() to us using a flip matrix or a rotate-by-multiple-of-90 // matrix on tests/transforms.png -- QPixmap::transformed()'s output is 1 // pixel too high or low depending on whether the matrix is passed through // QPixmap::trueMatrix(). // // Use and to specify the intended output size // of the pixmap. -1 if don't care. static QImage TransformPixmap (const QImage &pm, const QTransform &transformMatrix_, const kpColor &backgroundColor, int targetWidth, int targetHeight) { QTransform transformMatrix = transformMatrix_; #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "kppixmapfx.cpp: TransformPixmap(pm.size=" << pm.size () << ",targetWidth=" << targetWidth << ",targetHeight=" << targetHeight << ")"; #endif QRect newRect = transformMatrix.mapRect (pm.rect ()); #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "\tmappedRect=" << newRect; #endif QTransform scaleMatrix; if (targetWidth > 0 && targetWidth != newRect.width ()) { #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "\tadjusting for targetWidth"; #endif scaleMatrix.scale (double (targetWidth) / double (newRect.width ()), 1); } if (targetHeight > 0 && targetHeight != newRect.height ()) { #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "\tadjusting for targetHeight"; #endif scaleMatrix.scale (1, double (targetHeight) / double (newRect.height ())); } if (!scaleMatrix.isIdentity ()) { #if DEBUG_KP_PIXMAP_FX && 1 // TODO: What is going on here??? Why isn't matrix * working properly? QTransform wrongMatrix = transformMatrix * scaleMatrix; QTransform oldHat = transformMatrix; if (targetWidth > 0 && targetWidth != newRect.width ()) { oldHat.scale (double (targetWidth) / double (newRect.width ()), 1); } if (targetHeight > 0 && targetHeight != newRect.height ()) { oldHat.scale (1, double (targetHeight) / double (newRect.height ())); } QTransform altHat = transformMatrix; altHat.scale ((targetWidth > 0 && targetWidth != newRect.width ()) ? double (targetWidth) / double (newRect.width ()) : 1, (targetHeight > 0 && targetHeight != newRect.height ()) ? double (targetHeight) / double (newRect.height ()) : 1); QTransform correctMatrix = scaleMatrix * transformMatrix; qCDebug(kpLogPixmapfx) << "\tsupposedlyWrongMatrix: m11=" << wrongMatrix.m11 () // <<<---- this is the correct matrix??? << " m12=" << wrongMatrix.m12 () << " m21=" << wrongMatrix.m21 () << " m22=" << wrongMatrix.m22 () << " dx=" << wrongMatrix.dx () << " dy=" << wrongMatrix.dy () << " rect=" << wrongMatrix.mapRect (pm.rect ()) << "\n" << "\ti_used_to_use_thisMatrix: m11=" << oldHat.m11 () << " m12=" << oldHat.m12 () << " m21=" << oldHat.m21 () << " m22=" << oldHat.m22 () << " dx=" << oldHat.dx () << " dy=" << oldHat.dy () << " rect=" << oldHat.mapRect (pm.rect ()) << "\n" << "\tabove but scaled at the same time: m11=" << altHat.m11 () << " m12=" << altHat.m12 () << " m21=" << altHat.m21 () << " m22=" << altHat.m22 () << " dx=" << altHat.dx () << " dy=" << altHat.dy () << " rect=" << altHat.mapRect (pm.rect ()) << "\n" << "\tsupposedlyCorrectMatrix: m11=" << correctMatrix.m11 () << " m12=" << correctMatrix.m12 () << " m21=" << correctMatrix.m21 () << " m22=" << correctMatrix.m22 () << " dx=" << correctMatrix.dx () << " dy=" << correctMatrix.dy () << " rect=" << correctMatrix.mapRect (pm.rect ()); #endif transformMatrix = transformMatrix * scaleMatrix; newRect = transformMatrix.mapRect (pm.rect ()); #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "\tnewRect after targetWidth,targetHeight adjust=" << newRect; #endif } ::MatrixDebug (QStringLiteral("TransformPixmap(): before trueMatrix"), transformMatrix, pm.width (), pm.height ()); #if DEBUG_KP_PIXMAP_FX && 1 - QMatrix oldMatrix = transformMatrix; + QTransform oldMatrix = transformMatrix; #endif // Translate the matrix to account for Qt rounding errors, // so that flipping (if it used this method) and rotating by a multiple // of 90 degrees actually work as expected (try tests/transforms.png). // // SYNC: This was not required with Qt3 so we are actually working // around a Qt4 bug/feature. // // COMPAT: Qt4's rendering with a matrix enabled is low quality anyway // but does this reduce quality even further? // // With or without it, skews by 45 degrees with the QImage // painter below look bad (with it, you get an extra transparent // line on the right; without, you get only about 1/4 of a source // line on the left). In Qt3, with TrueMatrix(), the source // image is translated 1 pixel off the destination image. // // Also, if you skew a rectangular selection, the skewed selection // border does not line up with the skewed image data. // TODO: do we need to pass through this new matrix? transformMatrix = ::TrueMatrix (transformMatrix, pm.width (), pm.height ()); #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "trueMatrix changed matrix?" << (oldMatrix == transformMatrix); #endif ::MatrixDebug (QStringLiteral("TransformPixmap(): after trueMatrix"), transformMatrix, pm.width (), pm.height ()); QImage newQImage (targetWidth > 0 ? targetWidth : newRect.width (), targetHeight > 0 ? targetHeight : newRect.height (), QImage::Format_ARGB32_Premultiplied); if ((targetWidth > 0 && targetWidth != newRect.width ()) || (targetHeight > 0 && targetHeight != newRect.height ())) { #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "kppixmapfx.cpp: TransformPixmap(pm.size=" << pm.size () << ",targetWidth=" << targetWidth << ",targetHeight=" << targetHeight << ") newRect=" << newRect << " (you are a victim of rounding error)"; #endif } #if DEBUG_KP_PIXMAP_FX && 0 qCDebug(kpLogPixmapfx) << "\ttranslate top=" << painter.xForm (QPoint (0, 0)); qCDebug(kpLogPixmapfx) << "\tmatrix: m11=" << painter.worldMatrix ().m11 () << " m12=" << painter.worldMatrix ().m12 () << " m21=" << painter.worldMatrix ().m21 () << " m22=" << painter.worldMatrix ().m22 () << " dx=" << painter.worldMatrix ().dx () << " dy=" << painter.worldMatrix ().dy () << endl; #endif // Note: Do _not_ use "p.setRenderHints (QPainter::SmoothPixmapTransform);" // as the user does not want their image to get blurier every // time they e.g. rotate it (especially important for multiples // of 90 degrees but also true for every other angle). Being a // pixel-based program, we generally like to preserve RGB values // and avoid unnecessary blurs -- in the worst case, we'd rather // drop pixels, than blur. QPainter p (&newQImage); { // Make sure transparent pixels are drawn into the destination image. p.setCompositionMode (QPainter::CompositionMode_Source); // Fill the entire new image with the background color. if (backgroundColor.isValid ()) { p.fillRect (newQImage.rect (), backgroundColor.toQColor ()); } p.setWorldTransform (transformMatrix); p.drawImage (QPoint (0, 0), pm); } p.end (); #if DEBUG_KP_PIXMAP_FX && 1 qCDebug(kpLogPixmapfx) << "Done"; #endif return newQImage; } //--------------------------------------------------------------------- // public static QTransform kpPixmapFX::skewMatrix (int width, int height, double hangle, double vangle) { if (std::fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon && std::fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon) { return {}; } /* Diagram for completeness :) * * |---------- w ----------| * (0,0) * _ _______________________ (w,0) * | |\~_ va | * | | \ ~_ | * | |ha\ ~__ | * | \ ~__ | dy * h | \ ~___ | * | \ ~___ | * | | \ ~___| (w,w*tan(va)=dy) * | | \ * \ * _ |________\________|_____|\ vertical shear factor * (0,h) dx ^~_ | \ | * | ~_ \________\________ General Point (x,y) V * | ~__ \ Skewed Point (x + y*tan(ha),y + x*tan(va)) * (h*tan(ha)=dx,h) ~__ \ ^ * ~___ \ | * ~___ \ horizontal shear factor * Key: ~___\ * ha = hangle (w + h*tan(ha)=w+dx,h + w*tan(va)=w+dy) * va = vangle * * Skewing really just twists a rectangle into a parallelogram. * */ //QTransform matrix (1, tan (KP_DEGREES_TO_RADIANS (vangle)), tan (KP_DEGREES_TO_RADIANS (hangle)), 1, 0, 0); // I think this is clearer than above :) QTransform matrix; matrix.shear (std::tan (qDegreesToRadians (hangle)), std::tan (qDegreesToRadians (vangle))); return ::MatrixWithZeroOrigin (matrix, width, height); } //--------------------------------------------------------------------- // public static QTransform kpPixmapFX::skewMatrix (const QImage &pixmap, double hangle, double vangle) { return kpPixmapFX::skewMatrix (pixmap.width (), pixmap.height (), hangle, vangle); } //--------------------------------------------------------------------- // public static void kpPixmapFX::skew (QImage *destPtr, double hangle, double vangle, const kpColor &backgroundColor, int targetWidth, int targetHeight) { if (!destPtr) { return; } *destPtr = kpPixmapFX::skew (*destPtr, hangle, vangle, backgroundColor, targetWidth, targetHeight); } //--------------------------------------------------------------------- // public static QImage kpPixmapFX::skew (const QImage &pm, double hangle, double vangle, const kpColor &backgroundColor, int targetWidth, int targetHeight) { #if DEBUG_KP_PIXMAP_FX qCDebug(kpLogPixmapfx) << "kpPixmapFX::skew() pm.width=" << pm.width () << " pm.height=" << pm.height () << " hangle=" << hangle << " vangle=" << vangle << " targetWidth=" << targetWidth << " targetHeight=" << targetHeight; #endif if (std::fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon && std::fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon && (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/) { return pm; } if (std::fabs (hangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon || std::fabs (vangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon) { qCCritical(kpLogPixmapfx) << "kpPixmapFX::skew() passed hangle and/or vangle out of range (-90 < x < 90)"; return pm; } QTransform matrix = skewMatrix (pm, hangle, vangle); return ::TransformPixmap (pm, matrix, backgroundColor, targetWidth, targetHeight); } //--------------------------------------------------------------------- // public static QTransform kpPixmapFX::rotateMatrix (int width, int height, double angle) { if (std::fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon) { return {}; } QTransform matrix; matrix.translate (width / 2, height / 2); matrix.rotate (angle); return ::MatrixWithZeroOrigin (matrix, width, height); } //--------------------------------------------------------------------- // public static QTransform kpPixmapFX::rotateMatrix (const QImage &pixmap, double angle) { return kpPixmapFX::rotateMatrix (pixmap.width (), pixmap.height (), angle); } //--------------------------------------------------------------------- // public static bool kpPixmapFX::isLosslessRotation (double angle) { const double angleIn = angle; // Reflect angle into positive if negative if (angle < 0) { angle = -angle; } // Remove multiples of 90 to make sure 0 <= angle <= 90 angle -= (static_cast (angle)) / 90 * 90; // "Impossible" situation? if (angle < 0 || angle > 90) { qCCritical(kpLogPixmapfx) << "kpPixmapFX::isLosslessRotation(" << angleIn << ") result=" << angle; return false; // better safe than sorry } const bool ret = (angle < kpPixmapFX::AngleInDegreesEpsilon || 90 - angle < kpPixmapFX::AngleInDegreesEpsilon); #if DEBUG_KP_PIXMAP_FX qCDebug(kpLogPixmapfx) << "kpPixmapFX::isLosslessRotation(" << angleIn << ")" << " residual angle=" << angle << " returning " << ret; #endif return ret; } //--------------------------------------------------------------------- // public static void kpPixmapFX::rotate (QImage *destPtr, double angle, const kpColor &backgroundColor, int targetWidth, int targetHeight) { if (!destPtr) { return; } *destPtr = kpPixmapFX::rotate (*destPtr, angle, backgroundColor, targetWidth, targetHeight); } //--------------------------------------------------------------------- // public static QImage kpPixmapFX::rotate (const QImage &pm, double angle, const kpColor &backgroundColor, int targetWidth, int targetHeight) { if (std::fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon && (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/) { return pm; } QTransform matrix = rotateMatrix (pm, angle); return ::TransformPixmap (pm, matrix, backgroundColor, targetWidth, targetHeight); } //--------------------------------------------------------------------- diff --git a/tools/flow/kpToolColorEraser.cpp b/tools/flow/kpToolColorEraser.cpp index 366e8d8b..676c5932 100644 --- a/tools/flow/kpToolColorEraser.cpp +++ b/tools/flow/kpToolColorEraser.cpp @@ -1,160 +1,160 @@ /* 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 0 +#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 621cb106..1854ac39 100644 --- a/tools/flow/kpToolEraser.cpp +++ b/tools/flow/kpToolEraser.cpp @@ -1,79 +1,80 @@ /* 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 0 +#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 1f4f4db1..4c2437c8 100644 --- a/tools/flow/kpToolFlowBase.cpp +++ b/tools/flow/kpToolFlowBase.cpp @@ -1,493 +1,493 @@ /* 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 0 +#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 qCDebug(kpLogTools) << "kpToolFlowBase::hover(" << point << ")" << " hasBegun=" << hasBegun () << " hasBegunDraw=" << hasBegunDraw () << " cursorPixmap.isNull=" << m_cursorPixmap.isNull () << endl; #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 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 9d8b2604..e8a9dddb 100644 --- a/tools/flow/kpToolSpraycan.cpp +++ b/tools/flow/kpToolSpraycan.cpp @@ -1,259 +1,259 @@ /* 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 0 +#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 345c891e..09be60a6 100644 --- a/tools/kpTool.cpp +++ b/tools/kpTool.cpp @@ -1,263 +1,263 @@ /* 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 0 +#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 8a2f1713..56f0f9f3 100644 --- a/tools/kpToolColorPicker.cpp +++ b/tools/kpToolColorPicker.cpp @@ -1,137 +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_COLOR_PICKER 0 +#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 d562e87e..88c5d308 100644 --- a/tools/kpToolFloodFill.cpp +++ b/tools/kpToolFloodFill.cpp @@ -1,169 +1,169 @@ /* 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 0 +#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 cfe5c53e..6527a0c0 100644 --- a/tools/kpToolZoom.cpp +++ b/tools/kpToolZoom.cpp @@ -1,254 +1,254 @@ /* 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 0 +#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 // 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 1ce028da..35f52cdf 100644 --- a/tools/kpTool_Drawing.cpp +++ b/tools/kpTool_Drawing.cpp @@ -1,423 +1,423 @@ /* 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 0 +#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 ed0657f5..9cf521cd 100644 --- a/tools/kpTool_KeyboardEvents.cpp +++ b/tools/kpTool_KeyboardEvents.cpp @@ -1,431 +1,431 @@ /* 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 0 +#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 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 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 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 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 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 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 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 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 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 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 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 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 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 546baf4e..cd7abc68 100644 --- a/tools/kpTool_MouseEvents.cpp +++ b/tools/kpTool_MouseEvents.cpp @@ -1,337 +1,337 @@ /* 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 0 +#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 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 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 dc67513e..7217d6ae 100644 --- a/tools/kpTool_OtherEvents.cpp +++ b/tools/kpTool_OtherEvents.cpp @@ -1,168 +1,170 @@ /* 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 0 +#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 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 dd3b4585..b85100a3 100644 --- a/tools/kpTool_Utilities.cpp +++ b/tools/kpTool_Utilities.cpp @@ -1,294 +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. */ // // Tool utility methods - mainly for subclasses' convenience. // -#define DEBUG_KP_TOOL 0 +#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 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 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 qCDebug(kpLogTools) << "\tglobalPos=" << globalPos << " viewPos=" << viewPos; #endif if (!zoomToDoc) { return viewPos; } const QPoint docPos = v->transformViewToDoc (viewPos); #if DEBUG_KP_TOOL && 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=" - << kpPixmapFX::pixmapSize (newWidth, + << kpCommandSize::PixmapSize (newWidth, newHeight, QPixmap::defaultDepth ()) << " vs BigImageSize=" << KP_BIG_IMAGE_SIZE << endl; #endif // 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 0c1ce7a0..779f4807 100644 --- a/tools/polygonal/kpToolCurve.cpp +++ b/tools/polygonal/kpToolCurve.cpp @@ -1,196 +1,196 @@ /* 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 0 +#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 ef12bb20..5166a404 100644 --- a/tools/polygonal/kpToolLine.cpp +++ b/tools/polygonal/kpToolLine.cpp @@ -1,73 +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_TOOL_LINE 0 +#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 07505f0a..0494c320 100644 --- a/tools/polygonal/kpToolPolygon.cpp +++ b/tools/polygonal/kpToolPolygon.cpp @@ -1,190 +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_POLYGON 0 +#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 131b4852..aaf59c50 100644 --- a/tools/polygonal/kpToolPolygonalBase.cpp +++ b/tools/polygonal/kpToolPolygonalBase.cpp @@ -1,501 +1,501 @@ /* 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 0 +#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 7bc8cba4..612035be 100644 --- a/tools/polygonal/kpToolPolyline.cpp +++ b/tools/polygonal/kpToolPolyline.cpp @@ -1,125 +1,125 @@ /* 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 0 +#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/rectangular/kpToolRectangularBase.cpp b/tools/rectangular/kpToolRectangularBase.cpp index ef961ebb..4f83eb99 100644 --- a/tools/rectangular/kpToolRectangularBase.cpp +++ b/tools/rectangular/kpToolRectangularBase.cpp @@ -1,388 +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_TOOL_RECTANGULAR_BASE 0 +#define DEBUG_KP_TOOL_RECTANGULAR_BASE 1 #include "tools/rectangular/kpToolRectangularBase.h" #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/tempImage/kpTempImage.h" #include "environments/tools/kpToolEnvironment.h" #include "commands/tools/rectangular/kpToolRectangularCommand.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" //--------------------------------------------------------------------- struct kpToolRectangularBasePrivate { kpToolRectangularBase::DrawShapeFunc drawShapeFunc{}; kpToolWidgetLineWidth *toolWidgetLineWidth{}; kpToolWidgetFillStyle *toolWidgetFillStyle{}; QRect toolRectangleRect; }; //--------------------------------------------------------------------- kpToolRectangularBase::kpToolRectangularBase ( 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 kpToolRectangularBasePrivate ()) { d->drawShapeFunc = drawShapeFunc; d->toolWidgetLineWidth = nullptr; d->toolWidgetFillStyle = nullptr; } //--------------------------------------------------------------------- kpToolRectangularBase::~kpToolRectangularBase () { delete d; } //--------------------------------------------------------------------- // private slot virtual void kpToolRectangularBase::slotLineWidthChanged () { if (hasBegunDraw ()) { updateShape (); } } //--------------------------------------------------------------------- // private slot virtual void kpToolRectangularBase::slotFillStyleChanged () { if (hasBegunDraw ()) { updateShape (); } } //--------------------------------------------------------------------- // private QString kpToolRectangularBase::haventBegunDrawUserMessage () const { return i18n ("Drag to draw."); } //--------------------------------------------------------------------- // virtual void kpToolRectangularBase::begin () { #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "kpToolRectangularBase::begin ()"; #endif kpToolToolBar *tb = toolToolBar (); Q_ASSERT (tb); #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "\ttoolToolBar=" << tb; #endif d->toolWidgetLineWidth = tb->toolWidgetLineWidth (); connect (d->toolWidgetLineWidth, &kpToolWidgetLineWidth::lineWidthChanged, this, &kpToolRectangularBase::slotLineWidthChanged); d->toolWidgetLineWidth->show (); d->toolWidgetFillStyle = tb->toolWidgetFillStyle (); connect (d->toolWidgetFillStyle, &kpToolWidgetFillStyle::fillStyleChanged, this, &kpToolRectangularBase::slotFillStyleChanged); d->toolWidgetFillStyle->show (); viewManager ()->setCursor (QCursor (Qt::ArrowCursor)); setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // virtual void kpToolRectangularBase::end () { #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "kpToolRectangularBase::end ()"; #endif if (d->toolWidgetLineWidth) { disconnect (d->toolWidgetLineWidth, &kpToolWidgetLineWidth::lineWidthChanged, this, &kpToolRectangularBase::slotLineWidthChanged); d->toolWidgetLineWidth = nullptr; } if (d->toolWidgetFillStyle) { disconnect (d->toolWidgetFillStyle, &kpToolWidgetFillStyle::fillStyleChanged, this, &kpToolRectangularBase::slotFillStyleChanged); d->toolWidgetFillStyle = nullptr; } viewManager ()->unsetCursor (); } //--------------------------------------------------------------------- void kpToolRectangularBase::applyModifiers () { QRect rect = normalizedRect (); #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "kpToolRectangularBase::applyModifiers(" << rect << ") shift=" << shiftPressed () << " ctrl=" << controlPressed () << endl; #endif // user wants to startPoint () == center if (controlPressed ()) { int xdiff = qAbs (startPoint ().x () - currentPoint ().x ()); int ydiff = qAbs (startPoint ().y () - currentPoint ().y ()); rect = QRect (startPoint ().x () - xdiff, startPoint ().y () - ydiff, xdiff * 2 + 1, ydiff * 2 + 1); } // user wants major axis == minor axis: // rectangle --> square // rounded rectangle --> rounded square // ellipse --> circle if (shiftPressed ()) { if (!controlPressed ()) { if (rect.width () < rect.height ()) { if (startPoint ().y () == rect.y ()) { rect.setHeight (rect.width ()); } else { rect.setY (rect.bottom () - rect.width () + 1); } } else { if (startPoint ().x () == rect.x ()) { rect.setWidth (rect.height ()); } else { rect.setX (rect.right () - rect.height () + 1); } } } // have to maintain the center else { if (rect.width () < rect.height ()) { QPoint center = rect.center (); rect.setHeight (rect.width ()); rect.moveCenter (center); } else { QPoint center = rect.center (); rect.setWidth (rect.height ()); rect.moveCenter (center); } } } d->toolRectangleRect = rect; } //--------------------------------------------------------------------- void kpToolRectangularBase::beginDraw () { setUserMessage (cancelUserMessage ()); } //--------------------------------------------------------------------- // private kpColor kpToolRectangularBase::drawingForegroundColor () const { return color (mouseButton ()); } //--------------------------------------------------------------------- // private kpColor kpToolRectangularBase::drawingBackgroundColor () const { const kpColor foregroundColor = color (mouseButton ()); const kpColor backgroundColor = color (1 - mouseButton ()); return d->toolWidgetFillStyle->drawingBackgroundColor ( foregroundColor, backgroundColor); } //--------------------------------------------------------------------- // private void kpToolRectangularBase::updateShape () { kpImage image = document ()->getImageAt (d->toolRectangleRect); // Invoke shape drawing function passed in ctor. (*d->drawShapeFunc) (&image, 0, 0, d->toolRectangleRect.width (), d->toolRectangleRect.height (), drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (), drawingBackgroundColor ()); kpTempImage newTempImage (false/*always display*/, kpTempImage::SetImage/*render mode*/, d->toolRectangleRect.topLeft (), image); viewManager ()->setFastUpdates (); viewManager ()->setTempImage (newTempImage); viewManager ()->restoreFastUpdates (); } //--------------------------------------------------------------------- void kpToolRectangularBase::draw (const QPoint &, const QPoint &, const QRect &) { applyModifiers (); updateShape (); // Recover the start and end points from the transformed & normalized d->toolRectangleRect // S. or S or SC or S == C // .C C if (currentPoint ().x () >= startPoint ().x () && currentPoint ().y () >= startPoint ().y ()) { setUserShapePoints (d->toolRectangleRect.topLeft (), d->toolRectangleRect.bottomRight ()); } // .C or C // S. S else if (currentPoint ().x () >= startPoint ().x () && currentPoint ().y () < startPoint ().y ()) { setUserShapePoints (d->toolRectangleRect.bottomLeft (), d->toolRectangleRect.topRight ()); } // .S or CS // C. else if (currentPoint ().x () < startPoint ().x () && currentPoint ().y () >= startPoint ().y ()) { setUserShapePoints (d->toolRectangleRect.topRight (), d->toolRectangleRect.bottomLeft ()); } // C. // .S else { setUserShapePoints (d->toolRectangleRect.bottomRight (), d->toolRectangleRect.topLeft ()); } } //--------------------------------------------------------------------- void kpToolRectangularBase::cancelShape () { viewManager ()->invalidateTempImage (); setUserMessage (i18n ("Let go of all the mouse buttons.")); } //--------------------------------------------------------------------- void kpToolRectangularBase::releasedAllButtons () { setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- void kpToolRectangularBase::endDraw (const QPoint &, const QRect &) { applyModifiers (); // TODO: flicker // Later: So why can't we use kpViewManager::setQueueUpdates()? Check SVN // log to see if this method was not available at the time of the // TODO, hence justifying the TODO. // Later2: kpToolPolygonalBase, and perhaps, other shapes will have the // same problem. viewManager ()->invalidateTempImage (); environ ()->commandHistory ()->addCommand ( new kpToolRectangularCommand ( text (), d->drawShapeFunc, d->toolRectangleRect, drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (), drawingBackgroundColor (), environ ()->commandEnvironment ())); setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- diff --git a/tools/selection/image/kpAbstractImageSelectionTool_Transparency.cpp b/tools/selection/image/kpAbstractImageSelectionTool_Transparency.cpp index 850f848e..782befbb 100644 --- a/tools/selection/image/kpAbstractImageSelectionTool_Transparency.cpp +++ b/tools/selection/image/kpAbstractImageSelectionTool_Transparency.cpp @@ -1,207 +1,207 @@ /* 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 0 +#define DEBUG_KP_TOOL_SELECTION 1 #include "kpAbstractImageSelectionTool.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "layers/selections/kpAbstractSelection.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "commands/kpMacroCommand.h" #include "generic/kpSetOverrideCursorSaver.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "commands/tools/selection/kpToolSelectionDestroyCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/kpToolSelectionMoveCommand.h" #include "commands/tools/selection/kpToolSelectionResizeScaleCommand.h" #include "commands/tools/selection/kpToolImageSelectionTransparencyCommand.h" #include "commands/tools/selection/text/kpToolTextGiveContentCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include // protected bool kpAbstractImageSelectionTool::shouldChangeImageSelectionTransparency () const { if (environ ()->settingImageSelectionTransparency ()) { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\trecursion - abort setting selection transparency: " << environ ()->settingImageSelectionTransparency (); #endif return false; } if (!document ()->imageSelection ()) { return false; } // TODO: Can probably return false if the selection transparency mode // is Opaque, since neither background color nor color similarity // would matter. return true; } // protected void kpAbstractImageSelectionTool::changeImageSelectionTransparency ( const QString &name, const kpImageSelectionTransparency &newTrans, const kpImageSelectionTransparency &oldTrans) { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "CALL(" << name << ")"; #endif kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor); if (hasBegunShape ()) { endShapeInternal (); } kpAbstractImageSelection *imageSel = document ()->imageSelection (); if (imageSel->hasContent () && newTrans.isTransparent ()) { environ ()->flashColorSimilarityToolBarItem (); } imageSel->setTransparency (newTrans); // We _must_ add the command even if kpAbstractImageSelection::setTransparency() // above did not change the selection transparency mask at all. // Consider the following case: // // 0. Ensure that selection transparency is opaque and any // color other than red is the background color. Ensure that // the color similarity setting is 0. // // 1. Select a solid red rectangle and pull it off. // // 2. Switch to transparent and set red as the background color. // [the selection is now invisible as red is the background // color, which is the same as the contents of the selection] // // 3. Invert Colors. // [the selection is now cyan, red is still the background color] // // 4. Change the background color to green. // [the selection transparency mask does not change so the // selection is still cyan; green is the background color] // // 5. Undo // // If no transparency command were added for Step 4., the Undo // in Step 5. would take us straight to the state after Step 2., // where we would expect the red selection to be invisible. However, // as the background color was changed to green in Step 4. and was not // undone, the red selection is not invisible when it should be -- Undo // has moved us to an incorrect state. // // KDE3: Copy this comment into the KDE 3 branch. commandHistory ()->addCommand (new kpToolImageSelectionTransparencyCommand ( name, newTrans, oldTrans, environ ()->commandEnvironment ()), false/*no exec*/); } // protected slot virtual [kpAbstractSelectionTool] void kpAbstractImageSelectionTool::slotIsOpaqueChanged (bool /*isOpaque*/) { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractImageSelectionTool::slotIsOpaqueChanged()"; #endif if (!shouldChangeImageSelectionTransparency ()) { return; } kpImageSelectionTransparency st = environ ()->imageSelectionTransparency (); kpImageSelectionTransparency oldST = st; oldST.setOpaque (!oldST.isOpaque ()); changeImageSelectionTransparency ( st.isOpaque () ? i18n ("Selection: Opaque") : i18n ("Selection: Transparent"), st, oldST); } // protected slot virtual [base kpTool] void kpAbstractImageSelectionTool::slotBackgroundColorChanged (const kpColor &) { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractImageSelectionTool::slotBackgroundColorChanged()"; #endif if (!shouldChangeImageSelectionTransparency ()) { return; } kpImageSelectionTransparency st = environ ()->imageSelectionTransparency (); kpImageSelectionTransparency oldST = st; oldST.setTransparentColor (oldBackgroundColor ()); changeImageSelectionTransparency ( i18n ("Selection: Transparency Color"), st, oldST); } // protected slot virtual [base kpTool] void kpAbstractImageSelectionTool::slotColorSimilarityChanged (double, int) { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "kpAbstractImageSelectionTool::slotColorSimilarityChanged()"; #endif if (!shouldChangeImageSelectionTransparency ()) { return; } kpImageSelectionTransparency st = environ ()->imageSelectionTransparency (); kpImageSelectionTransparency oldST = st; oldST.setColorSimilarity (oldColorSimilarity ()); changeImageSelectionTransparency ( i18n ("Selection: Transparency Color Similarity"), st, oldST); } diff --git a/tools/selection/image/kpToolEllipticalSelection.cpp b/tools/selection/image/kpToolEllipticalSelection.cpp index d74ec667..4e505eeb 100644 --- a/tools/selection/image/kpToolEllipticalSelection.cpp +++ b/tools/selection/image/kpToolEllipticalSelection.cpp @@ -1,79 +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_ELLIPTICAL_SELECTION 0 +#define DEBUG_KP_TOOL_ELLIPTICAL_SELECTION 1 #include "kpToolEllipticalSelection.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "layers/selections/image/kpEllipticalImageSelection.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include kpToolEllipticalSelection::kpToolEllipticalSelection (kpToolSelectionEnvironment *environ, QObject *parent) : kpAbstractImageSelectionTool (i18n ("Selection (Elliptical)"), i18n ("Makes an elliptical or circular selection"), Qt::Key_I, environ, parent, QStringLiteral("tool_elliptical_selection")) { } kpToolEllipticalSelection::~kpToolEllipticalSelection () = default; // protected virtual [base kpAbstractSelectionTool] bool kpToolEllipticalSelection::drawCreateMoreSelectionAndUpdateStatusBar ( bool dragAccepted, const QPoint &accidentalDragAdjustedPoint, const QRect &normalizedRect) { // Prevent unintentional creation of 1-pixel selections. if (!dragAccepted && accidentalDragAdjustedPoint == startPoint ()) { #if DEBUG_KP_TOOL_ELLIPTICAL_SELECTION && 1 qCDebug(kpLogTools) << "\tnon-text NOP - return"; #endif setUserShapePoints (accidentalDragAdjustedPoint); return false; } Q_ASSERT (accidentalDragAdjustedPoint == currentPoint ()); document ()->setSelection ( kpEllipticalImageSelection ( normalizedRect, environ ()->imageSelectionTransparency ())); setUserShapePoints (startPoint (), currentPoint ()); return true; } diff --git a/tools/selection/image/kpToolFreeFormSelection.cpp b/tools/selection/image/kpToolFreeFormSelection.cpp index 9afb1e44..7db7af33 100644 --- a/tools/selection/image/kpToolFreeFormSelection.cpp +++ b/tools/selection/image/kpToolFreeFormSelection.cpp @@ -1,139 +1,139 @@ /* 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_FREE_FROM_SELECTION 0 +#define DEBUG_KP_TOOL_FREE_FROM_SELECTION 1 #include "kpToolFreeFormSelection.h" #include "kpLogCategories.h" #include #include "document/kpDocument.h" #include "layers/selections/image/kpFreeFormImageSelection.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" //--------------------------------------------------------------------- kpToolFreeFormSelection::kpToolFreeFormSelection (kpToolSelectionEnvironment *environ, QObject *parent) : kpAbstractImageSelectionTool (i18n ("Selection (Free-Form)"), i18n ("Makes a free-form selection"), Qt::Key_M, environ, parent, QStringLiteral("tool_free_form_selection")) { } //--------------------------------------------------------------------- kpToolFreeFormSelection::~kpToolFreeFormSelection () = default; //--------------------------------------------------------------------- // protected virtual [base kpAbstractSelectionTool] bool kpToolFreeFormSelection::drawCreateMoreSelectionAndUpdateStatusBar ( bool dragAccepted, const QPoint &accidentalDragAdjustedPoint, const QRect &/*normalizedRect*/) { #if DEBUG_KP_TOOL_FREE_FROM_SELECTION qCDebug(kpLogTools) << "kpToolFreeFormSelection::createMoreSelectionAndUpdateStatusBar(" << "dragAccepted=" << dragAccepted << ",accidentalDragAdjustedPoint=" << accidentalDragAdjustedPoint << ")"; #endif // Prevent unintentional creation of 1-pixel selections. if (!dragAccepted && accidentalDragAdjustedPoint == startPoint ()) { #if DEBUG_KP_TOOL_FREE_FROM_SELECTION && 1 qCDebug(kpLogTools) << "\tnon-text NOP - return"; #endif setUserShapePoints (accidentalDragAdjustedPoint); return false; } Q_ASSERT (accidentalDragAdjustedPoint == currentPoint ()); Q_ASSERT (dragAccepted == static_cast (document ()->selection ())); const kpFreeFormImageSelection *oldPointsSel = nullptr; if (document ()->selection ()) { kpAbstractSelection *sel = document ()->selection (); Q_ASSERT (dynamic_cast (sel)); oldPointsSel = dynamic_cast (sel); } QPolygon points; // First point in drag? if (!dragAccepted) { points.append (startPoint ()); } // Not first point in drag. else { if ( !oldPointsSel ) { // assert above says we never reach this, but let's make coverity happy return false; } // Get existing points in selection. points = oldPointsSel->cardinallyAdjacentPoints (); } #if DEBUG_KP_TOOL_FREE_FROM_SELECTION qCDebug(kpLogTools) << "\tlast old point=" << points.last (); #endif // TODO: There should be an upper limit on this before drawing the // polygon becomes too slow. points.append (accidentalDragAdjustedPoint); document ()->setSelection ( kpFreeFormImageSelection (points, environ ()->imageSelectionTransparency ())); // Prevent accidental usage of dangling pointer to old selection // (deleted by kpDocument::setSelection()). oldPointsSel = nullptr; #if DEBUG_KP_TOOL_FREE_FROM_SELECTION && 1 qCDebug(kpLogTools) << "\t\tfreeform; #points=" << document ()->selection ()->calculatePoints ().count (); #endif setUserShapePoints (accidentalDragAdjustedPoint); return true; } //--------------------------------------------------------------------- diff --git a/tools/selection/image/kpToolRectSelection.cpp b/tools/selection/image/kpToolRectSelection.cpp index a6d0732e..87aae019 100644 --- a/tools/selection/image/kpToolRectSelection.cpp +++ b/tools/selection/image/kpToolRectSelection.cpp @@ -1,81 +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_TOOL_RECT_SELECTION 0 +#define DEBUG_KP_TOOL_RECT_SELECTION 1 #include "kpToolRectSelection.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "layers/selections/image/kpRectangularImageSelection.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include kpToolRectSelection::kpToolRectSelection (kpToolSelectionEnvironment *environ, QObject *parent) : kpAbstractImageSelectionTool (i18n ("Selection (Rectangular)"), i18n ("Makes a rectangular selection"), Qt::Key_S, environ, parent, QStringLiteral("tool_rect_selection")) { } kpToolRectSelection::~kpToolRectSelection () = default; // protected virtual [base kpAbstractSelectionTool] bool kpToolRectSelection::drawCreateMoreSelectionAndUpdateStatusBar ( bool dragAccepted, const QPoint &accidentalDragAdjustedPoint, const QRect &normalizedRect) { // Prevent unintentional creation of 1-pixel selections. // REFACTOR: This line is duplicated code with other tools. if (!dragAccepted && accidentalDragAdjustedPoint == startPoint ()) { #if DEBUG_KP_TOOL_RECT_SELECTION && 1 qCDebug(kpLogTools) << "\tnon-text NOP - return"; #endif setUserShapePoints (accidentalDragAdjustedPoint); return false; } Q_ASSERT (accidentalDragAdjustedPoint == currentPoint ()); const QRect usefulRect = normalizedRect.intersected (document ()->rect ()); document ()->setSelection ( kpRectangularImageSelection ( usefulRect, environ ()->imageSelectionTransparency ())); setUserShapePoints (startPoint (), QPoint (qMax (0, qMin (currentPoint ().x (), document ()->width () - 1)), qMax (0, qMin (currentPoint ().y (), document ()->height () - 1)))); return true; } diff --git a/tools/selection/kpAbstractSelectionTool.cpp b/tools/selection/kpAbstractSelectionTool.cpp index f04aca35..a8f9a437 100644 --- a/tools/selection/kpAbstractSelectionTool.cpp +++ b/tools/selection/kpAbstractSelectionTool.cpp @@ -1,641 +1,641 @@ /* 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 0 +#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 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/tools/selection/kpAbstractSelectionTool_Create.cpp b/tools/selection/kpAbstractSelectionTool_Create.cpp index f43e6051..2d64ff01 100644 --- a/tools/selection/kpAbstractSelectionTool_Create.cpp +++ b/tools/selection/kpAbstractSelectionTool_Create.cpp @@ -1,300 +1,300 @@ /* 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 0 +#define DEBUG_KP_TOOL_SELECTION 1 #include "kpAbstractSelectionTool.h" #include "kpAbstractSelectionToolPrivate.h" #include #include #include #include #include "kpLogCategories.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "layers/selections/kpAbstractSelection.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "commands/kpMacroCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "commands/tools/selection/kpToolSelectionDestroyCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/kpToolSelectionMoveCommand.h" #include "commands/tools/selection/kpToolSelectionResizeScaleCommand.h" #include "commands/tools/selection/kpToolImageSelectionTransparencyCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" // private void kpAbstractSelectionTool::initCreate () { d->createNOPTimer = new QTimer (this); d->createNOPTimer->setSingleShot (true); connect (d->createNOPTimer, &QTimer::timeout, this, &kpAbstractSelectionTool::delayedDrawCreate); } // private void kpAbstractSelectionTool::uninitCreate () { // d->createNOPTimer (deleted by QObject mechanism) } // private void kpAbstractSelectionTool::beginCreate () { // d->createNOPTimer } // private void kpAbstractSelectionTool::endCreate () { // d->createNOPTimer } //--------------------------------------------------------------------- // use a crosshair cursor which is really always exactly 1 pixel wide // to the contrary of the "themed" crosshair cursors which might look nice // but does not allow to exactly position the hot-spot. /* XPM */ static const char *crosshair[]={ "17 17 3 1", ". c None", "x c #FFFFFF", "# c #000000", ".......xxx.......", ".......x#x.......", ".......x#x.......", ".......x#x.......", ".......x#x.......", ".......x#x.......", ".......x#x.......", "xxxxxxxx#xxxxxxxx", "x#######.#######x", "xxxxxxxx#xxxxxxxx", ".......x#x.......", ".......x#x.......", ".......x#x.......", ".......x#x.......", ".......x#x.......", ".......x#x.......", ".......xxx......."}; // private void kpAbstractSelectionTool::setCursorCreate () { viewManager()->setCursor(QCursor(QPixmap(crosshair), 8, 8)); } //--------------------------------------------------------------------- // protected virtual void kpAbstractSelectionTool::setSelectionBorderForBeginDrawCreate () { viewManager ()->setQueueUpdates (); { // LOREFACTOR: I suspect some calls to viewManager() in this // file (including this) are redundant since any // code that tweaks such settings, returns them to // their original state, after the code is complete. viewManager ()->setSelectionBorderVisible (true); viewManager ()->setSelectionBorderFinished (false); } viewManager ()->restoreQueueUpdates (); } // private void kpAbstractSelectionTool::beginDrawCreate () { if (document ()->selection ()) { pushOntoDocument (); } /*virtual*/setSelectionBorderForBeginDrawCreate (); // (single shot) d->createNOPTimer->start (200/*ms*/); setUserMessage (cancelUserMessage ()); } // private void kpAbstractSelectionTool::drawCreate (const QPoint &thisPoint, const QRect &normalizedRect) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\tnot moving - resizing rect to" << normalizedRect; qCDebug(kpLogTools) << "\t\tcreateNOPTimer->isActive()=" << d->createNOPTimer->isActive () << " viewManhattanLength from startPoint=" << viewUnderStartPoint ()->transformDocToViewX ((thisPoint - startPoint ()).manhattanLength ()); #endif QPoint accidentalDragAdjustedPoint = thisPoint; if (d->createNOPTimer->isActive ()) { // See below "d->createNOPTimer->stop()". Q_ASSERT (!d->dragAccepted); if (viewUnderStartPoint ()->transformDocToViewX ( (accidentalDragAdjustedPoint - startPoint ()).manhattanLength ()) <= 6) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tsuppress accidental movement"; #endif accidentalDragAdjustedPoint = startPoint (); } else { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tit's a \"big\" intended move - stop timer"; #endif d->createNOPTimer->stop (); } } const bool hadSelection = document ()->selection (); const bool oldDrawAcceptedAsDrag = d->dragAccepted; d->dragAccepted = /*virtual*/drawCreateMoreSelectionAndUpdateStatusBar ( d->dragAccepted, accidentalDragAdjustedPoint, normalizedRect); if (oldDrawAcceptedAsDrag) { Q_ASSERT (d->dragAccepted); } if (d->dragAccepted) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tdrawHasDoneSomething - kill create timer"; #endif // No longer a NOP. d->createNOPTimer->stop (); } // Did we just create a selection? if (!hadSelection && document ()->selection ()) { viewManager ()->setSelectionBorderVisible (true); } } // private slot void kpAbstractSelectionTool::delayedDrawCreate () { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "kpAbstractSelectionTool::delayedDrawCreate() hasBegunDraw=" << hasBegunDraw () << " currentPoint=" << currentPoint () << " lastPoint=" << lastPoint () << " startPoint=" << startPoint (); #endif // (just in case not called from single shot) d->createNOPTimer->stop (); if (hasBegunDraw ()) { draw (currentPoint (), lastPoint (), normalizedRect ()); } } // private void kpAbstractSelectionTool::cancelCreate () { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\twas creating sel - kill"; #endif d->createNOPTimer->stop (); // TODO: should we give the user back the selection s/he had before (if any)? if (document ()->selection ()) { document ()->selectionDelete (); } } // private void kpAbstractSelectionTool::endDrawCreate () { d->createNOPTimer->stop (); } // private QVariant kpAbstractSelectionTool::operationCreate (Operation op, const QVariant &data1, const QVariant &data2) { (void) data1; (void) data2; switch (op) { case HaventBegunDrawUserMessage: return /*virtual*/haventBegunDrawUserMessageCreate (); case SetCursor: setCursorCreate (); break; case BeginDraw: beginDrawCreate (); break; case Draw: drawCreate (currentPoint (), normalizedRect ()); break; case Cancel: cancelCreate (); break; case EndDraw: endDrawCreate (); break; default: Q_ASSERT (!"Unhandled operation"); break; } return {}; } diff --git a/tools/selection/kpAbstractSelectionTool_KeyboardEvents.cpp b/tools/selection/kpAbstractSelectionTool_KeyboardEvents.cpp index bd6c80f4..0e810d8e 100644 --- a/tools/selection/kpAbstractSelectionTool_KeyboardEvents.cpp +++ b/tools/selection/kpAbstractSelectionTool_KeyboardEvents.cpp @@ -1,102 +1,102 @@ /* 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 0 +#define DEBUG_KP_TOOL_SELECTION 1 #include "kpAbstractSelectionTool.h" #include "kpAbstractSelectionToolPrivate.h" #include "document/kpDocument.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/kpToolSelectionMoveCommand.h" #include "layers/selections/kpAbstractSelection.h" #include #include "kpLogCategories.h" //--------------------------------------------------------------------- // protected virtual [base kpTool] void kpAbstractSelectionTool::keyPressEvent (QKeyEvent *e) { #if DEBUG_KP_TOOL_SELECTION && 0 qCDebug(kpLogTools) << "kpAbstractSelectionTool::keyPressEvent(e->text='" << e->text () << "')"; #endif e->ignore (); if (document ()->selection () && !hasBegunDraw () && e->key () == Qt::Key_Escape) { #if DEBUG_KP_TOOL_SELECTION && 0 qCDebug(kpLogTools) << "\tescape pressed with sel when not begun draw - deselecting"; #endif pushOntoDocument (); e->accept (); } else { #if DEBUG_KP_TOOL_SELECTION && 0 qCDebug(kpLogTools) << "\tkey processing did not accept (text was '" << e->text () << "') - passing on event to kpTool"; #endif if ( document()->selection() && !hasBegunDraw() && ((e->key() == Qt::Key_Left) || (e->key() == Qt::Key_Right) || (e->key() == Qt::Key_Up) || (e->key() == Qt::Key_Down)) ) { // move selection with cursor keys pixel-wise giveContentIfNeeded(); if ( !d->currentMoveCommand ) { d->currentMoveCommand = new kpToolSelectionMoveCommand( QString()/*uninteresting child of macro cmd*/, environ()->commandEnvironment()); d->currentMoveCommandIsSmear = false; } int dx, dy; arrowKeyPressDirection(e, &dx, &dy); d->currentMoveCommand->moveTo(document()->selection()->topLeft() + QPoint(dx, dy)); endDrawMove(); } else kpTool::keyPressEvent(e); } } //--------------------------------------------------------------------- diff --git a/tools/selection/kpAbstractSelectionTool_Move.cpp b/tools/selection/kpAbstractSelectionTool_Move.cpp index 0fc08d7a..06f277d1 100644 --- a/tools/selection/kpAbstractSelectionTool_Move.cpp +++ b/tools/selection/kpAbstractSelectionTool_Move.cpp @@ -1,407 +1,407 @@ /* 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 0 +#define DEBUG_KP_TOOL_SELECTION 1 #include "kpAbstractSelectionTool.h" #include "kpAbstractSelectionToolPrivate.h" #include "kpLogCategories.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "layers/selections/kpAbstractSelection.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "commands/kpMacroCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "commands/tools/selection/kpToolSelectionDestroyCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/kpToolSelectionMoveCommand.h" #include "commands/tools/selection/kpToolSelectionResizeScaleCommand.h" #include "commands/tools/selection/kpToolImageSelectionTransparencyCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include #include //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::initMove () { d->currentMoveCommand = nullptr; // d->currentMoveCommandIsSmear // d->startMoveDragFromSelectionTopLeft d->RMBMoveUpdateGUITimer = new QTimer (this); d->RMBMoveUpdateGUITimer->setSingleShot (true); connect (d->RMBMoveUpdateGUITimer, &QTimer::timeout, this, &kpAbstractSelectionTool::slotRMBMoveUpdateGUI); } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::uninitMove () { // (state must be after construction, or after some time after endMove()) Q_ASSERT (!d->currentMoveCommand); // d->currentMoveCommandIsSmear // d->startMoveDragFromSelectionTopLeft // d->RMBMoveUpdateGUITimer (deleted by QObject mechanism) } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::beginMove () { // (state must be after construction, or after some time after endMove()) Q_ASSERT (!d->currentMoveCommand); // d->currentMoveCommandIsSmear // d->startMoveDragFromSelectionTopLeft; // d->RMBMoveUpdateGUITimer } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::endMove () { // (should have been killed by cancelMove() or endDrawMove()) Q_ASSERT (!d->currentMoveCommand); // d->currentMoveCommandIsSmear // d->startMoveDragFromSelectionTopLeft // d->RMBMoveUpdateGUITimer } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::setCursorMove () { viewManager ()->setCursor (Qt::SizeAllCursor); } //--------------------------------------------------------------------- // protected virtual void kpAbstractSelectionTool::setSelectionBorderForBeginDrawMove () { // don't show border while moving viewManager ()->setQueueUpdates (); { viewManager ()->setSelectionBorderVisible (false); viewManager ()->setSelectionBorderFinished (true); } viewManager ()->restoreQueueUpdates (); } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::beginDrawMove () { d->startMoveDragFromSelectionTopLeft = currentPoint () - document ()->selection ()->topLeft (); if (mouseButton () == 0) { /*virtual*/setSelectionBorderForBeginDrawMove (); } else { // Don't hide sel border momentarily if user is just // right _clicking_ selection. // (single shot timer) d->RMBMoveUpdateGUITimer->start (100/*ms*/); } setUserMessage (cancelUserMessage ()); } //--------------------------------------------------------------------- // private slot void kpAbstractSelectionTool::slotRMBMoveUpdateGUI () { // (just in case not called from single shot) d->RMBMoveUpdateGUITimer->stop (); /*virtual*/setSelectionBorderForBeginDrawMove (); kpAbstractSelection * const sel = document ()->selection (); if (sel) { setUserShapePoints (sel->topLeft ()); } } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::drawMove (const QPoint &thisPoint, const QRect &/*normalizedRect*/) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\tmoving selection"; #endif kpAbstractSelection *sel = document ()->selection (); QRect targetSelRect (thisPoint.x () - d->startMoveDragFromSelectionTopLeft.x (), thisPoint.y () - d->startMoveDragFromSelectionTopLeft.y (), sel->width (), sel->height ()); #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tstartPoint=" << startPoint () << " thisPoint=" << thisPoint << " startDragFromSel=" << d->startMoveDragFromSelectionTopLeft << " targetSelRect=" << targetSelRect; #endif // Try to make sure selection still intersects document so that it's // reachable. if (targetSelRect.right () < 0) { targetSelRect.translate (-targetSelRect.right (), 0); } else if (targetSelRect.left () >= document ()->width ()) { targetSelRect.translate (document ()->width () - targetSelRect.left () - 1, 0); } if (targetSelRect.bottom () < 0) { targetSelRect.translate (0, -targetSelRect.bottom ()); } else if (targetSelRect.top () >= document ()->height ()) { targetSelRect.translate (0, document ()->height () - targetSelRect.top () - 1); } #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\t\tafter ensure sel rect clickable=" << targetSelRect; #endif if (!d->dragAccepted && targetSelRect.topLeft () + d->startMoveDragFromSelectionTopLeft == startPoint ()) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\t\t\tnop"; #endif if (!d->RMBMoveUpdateGUITimer->isActive ()) { // (slotRMBMoveUpdateGUI() calls similar line) setUserShapePoints (sel->topLeft ()); } // Prevent both NOP drag-moves return; } if (d->RMBMoveUpdateGUITimer->isActive ()) { d->RMBMoveUpdateGUITimer->stop (); slotRMBMoveUpdateGUI (); } giveContentIfNeeded (); if (!d->currentMoveCommand) { d->currentMoveCommand = new kpToolSelectionMoveCommand ( QString()/*uninteresting child of macro cmd*/, environ ()->commandEnvironment ()); d->currentMoveCommandIsSmear = false; } //viewManager ()->setQueueUpdates (); //viewManager ()->setFastUpdates (); if (shiftPressed ()) { d->currentMoveCommandIsSmear = true; } if (!d->dragAccepted && (controlPressed () || shiftPressed ())) { d->currentMoveCommand->copyOntoDocument (); } d->currentMoveCommand->moveTo (targetSelRect.topLeft ()); if (shiftPressed ()) { d->currentMoveCommand->copyOntoDocument (); } //viewManager ()->restoreFastUpdates (); //viewManager ()->restoreQueueUpdates (); // REFACTOR: yuck, yuck kpAbstractSelection *orgSel = d->currentMoveCommand->originalSelectionClone (); QPoint start = orgSel->topLeft (); delete orgSel; QPoint end = targetSelRect.topLeft (); setUserShapePoints (start, end, false/*don't set size*/); setUserShapeSize (end.x () - start.x (), end.y () - start.y ()); d->dragAccepted = true; } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::cancelMove () { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\twas drag moving - undo drag and undo acquire"; #endif d->RMBMoveUpdateGUITimer->stop (); // NOP drag? if (!d->currentMoveCommand) { return; } #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\t\tundo currentMoveCommand"; #endif d->currentMoveCommand->finalize (); d->currentMoveCommand->unexecute (); delete d->currentMoveCommand; d->currentMoveCommand = nullptr; } //--------------------------------------------------------------------- // protected virtual QString kpAbstractSelectionTool::nonSmearMoveCommandName () const { return i18n ("Selection: Move"); } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::endDrawMove () { d->RMBMoveUpdateGUITimer->stop (); // NOP drag? if (!d->currentMoveCommand) { return; } d->currentMoveCommand->finalize (); kpMacroCommand *renamedCmd = nullptr; #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\thave moveCommand"; #endif if (d->currentMoveCommandIsSmear) { renamedCmd = new kpMacroCommand (i18n ("%1: Smear", document ()->selection ()->name ()), environ ()->commandEnvironment ()); } else { renamedCmd = new kpMacroCommand ( /*virtual*/nonSmearMoveCommandName (), environ ()->commandEnvironment ()); } renamedCmd->addCommand (d->currentMoveCommand); d->currentMoveCommand = nullptr; addNeedingContentCommand (renamedCmd); } //--------------------------------------------------------------------- // private QVariant kpAbstractSelectionTool::operationMove (Operation op, const QVariant &data1, const QVariant &data2) { (void) data1; (void) data2; switch (op) { case HaventBegunDrawUserMessage: return /*virtual*/haventBegunDrawUserMessageMove (); case SetCursor: setCursorMove (); break; case BeginDraw: beginDrawMove (); break; case Draw: drawMove (currentPoint (), normalizedRect ()); break; case Cancel: cancelMove (); break; case EndDraw: endDrawMove (); break; default: Q_ASSERT (!"Unhandled operation"); break; } return {}; } //--------------------------------------------------------------------- diff --git a/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp b/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp index 8fd1a56a..1b6005cb 100644 --- a/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp +++ b/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp @@ -1,450 +1,450 @@ /* 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 0 +#define DEBUG_KP_TOOL_SELECTION 1 #include "kpAbstractSelectionTool.h" #include "kpAbstractSelectionToolPrivate.h" #include "kpLogCategories.h" #include #include "layers/selections/image/kpAbstractImageSelection.h" #include "layers/selections/kpAbstractSelection.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "commands/kpMacroCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "commands/tools/selection/kpToolSelectionDestroyCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/kpToolSelectionMoveCommand.h" #include "commands/tools/selection/kpToolSelectionResizeScaleCommand.h" #include "commands/tools/selection/kpToolImageSelectionTransparencyCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" // private int kpAbstractSelectionTool::onSelectionResizeHandle () const { kpView *v = viewManager ()->viewUnderCursor (); if (!v) { return 0; } return v->mouseOnSelectionResizeHandle (currentViewPoint ()); } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::initResizeScale () { d->currentResizeScaleCommand = nullptr; // d->resizeScaleType } // private void kpAbstractSelectionTool::uninitResizeScale () { // (state must be after construction, or after some time after endResizeScale()) Q_ASSERT (!d->currentResizeScaleCommand); // d->resizeScaleType } // private void kpAbstractSelectionTool::beginResizeScale () { // (state must be after construction, or after some time after endResizeScale()) Q_ASSERT (!d->currentResizeScaleCommand); // d->resizeScaleType } // private void kpAbstractSelectionTool::endResizeScale () { // (should have been killed by cancelResizeScale() or endResizeScale()) Q_ASSERT (!d->currentResizeScaleCommand); // d->resizeScaleType } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::setCursorResizeScale () { #if DEBUG_KP_TOOL_SELECTION && 0 qCDebug(kpLogTools) << "\tonSelectionResizeHandle=" << onSelectionResizeHandle (); #endif Qt::CursorShape shape = Qt::ArrowCursor; switch (onSelectionResizeHandle ()) { case (kpView::Top | kpView::Left): case (kpView::Bottom | kpView::Right): shape = Qt::SizeFDiagCursor; break; case (kpView::Bottom | kpView::Left): case (kpView::Top | kpView::Right): shape = Qt::SizeBDiagCursor; break; case kpView::Top: case kpView::Bottom: shape = Qt::SizeVerCursor; break; case kpView::Left: case kpView::Right: shape = Qt::SizeHorCursor; break; } viewManager ()->setCursor (shape); } //--------------------------------------------------------------------- // protected virtual void kpAbstractSelectionTool::setSelectionBorderForBeginDrawResizeScale () { viewManager ()->setQueueUpdates (); { viewManager ()->setSelectionBorderVisible (true); viewManager ()->setSelectionBorderFinished (true); } viewManager ()->restoreQueueUpdates (); } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::beginDrawResizeScale () { d->resizeScaleType = onSelectionResizeHandle (); /*virtual*/setSelectionBorderForBeginDrawResizeScale (); setUserMessage (cancelUserMessage ()); } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::drawResizeScaleTryKeepAspect ( int newWidth, int newHeight, bool horizontalGripDragged, bool verticalGripDragged, const kpAbstractSelection &originalSelection, int *newWidthOut, int *newHeightOut) { const int oldWidth = originalSelection.width (), oldHeight = originalSelection.height (); // Width changed more than height? At equality, favor width. // Fix width, change height. // // We use and to prevent // e.g. the situation where we've dragged such that newWidth < oldWidth but // we're not dragging a vertical grip. We certainly don't want this // code to modify the width - we want to fix the width and change the // height. if ((horizontalGripDragged ? double (newWidth) / oldWidth : 0) >= (verticalGripDragged ? double (newHeight) / oldHeight : 0)) { *newHeightOut = newWidth * oldHeight / oldWidth; *newHeightOut = qMax (originalSelection.minimumHeight (), *newHeightOut); } // Height changed more than width? // Fix height, change width. else { *newWidthOut = newHeight * oldWidth / oldHeight; *newWidthOut = qMax (originalSelection.minimumWidth (), *newWidthOut); } } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::drawResizeScaleCalculateNewSelectionPosSize ( const kpAbstractSelection &originalSelection, int *newX, int *newY, int *newWidth, int *newHeight) { // // Determine new width. // // Dragging left or right grip? // If left, positive X drags decrease width. // If right, positive X drags increase width. int userXSign = 0; if (d->resizeScaleType & kpView::Left) { userXSign = -1; } else if (d->resizeScaleType & kpView::Right) { userXSign = +1; } // Calculate new width. *newWidth = originalSelection.width () + userXSign * (currentPoint ().x () - startPoint ().x ()); // Don't allow new width to be less than that kind of selection type's // minimum. *newWidth = qMax (originalSelection.minimumWidth (), *newWidth); // // Determine new height. // // Dragging top or bottom grip? // If top, positive Y drags decrease height. // If bottom, positive Y drags increase height. int userYSign = 0; if (d->resizeScaleType & kpView::Top) { userYSign = -1; } else if (d->resizeScaleType & kpView::Bottom) { userYSign = +1; } // Calculate new height. *newHeight = originalSelection.height () + userYSign * (currentPoint ().y () - startPoint ().y ()); // Don't allow new height to be less than that kind of selection type's // minimum. *newHeight = qMax (originalSelection.minimumHeight (), *newHeight); // Keep aspect ratio? if (shiftPressed ()) { drawResizeScaleTryKeepAspect (*newWidth, *newHeight, (userXSign != 0)/*X or XY grip dragged*/, (userYSign != 0)/*Y or XY grip dragged*/, originalSelection, newWidth/*ptr*/, newHeight/*ptr*/); } *newX = originalSelection.x (); *newY = originalSelection.y (); // // Adjust x/y to new width/height for left/top resizes. // if (d->resizeScaleType & kpView::Left) { *newX -= (*newWidth - originalSelection.width ()); } if (d->resizeScaleType & kpView::Top) { *newY -= (*newHeight - originalSelection.height ()); } #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tnewX=" << *newX << " newY=" << *newY << " newWidth=" << *newWidth << " newHeight=" << *newHeight; #endif } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::drawResizeScale ( const QPoint &thisPoint, const QRect &/*normalizedRect*/) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\tresize/scale"; #endif kpAbstractSelection *sel = document ()->selection (); if (!d->dragAccepted && thisPoint == startPoint ()) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tnop"; #endif setUserShapePoints (QPoint (sel->width (), sel->height ())); return; } giveContentIfNeeded (); if (!d->currentResizeScaleCommand) { d->currentResizeScaleCommand = new kpToolSelectionResizeScaleCommand (environ ()->commandEnvironment ()); } const kpAbstractSelection *originalSelection = d->currentResizeScaleCommand->originalSelection (); // There is nothing illegal about position (-1,-1) but why not. int newX = -1, newY = -1, newWidth = 0, newHeight = 0; // This should change all of the above values. drawResizeScaleCalculateNewSelectionPosSize ( *originalSelection, &newX, &newY, &newWidth, &newHeight); viewManager ()->setFastUpdates (); { d->currentResizeScaleCommand->resizeAndMoveTo ( newWidth, newHeight, QPoint (newX, newY), true/*smooth scale delayed*/); } viewManager ()->restoreFastUpdates (); setUserShapePoints (QPoint (originalSelection->width (), originalSelection->height ()), QPoint (newWidth, newHeight), false/*don't set size*/); setUserShapeSize (newWidth - originalSelection->width (), newHeight - originalSelection->height ()); d->dragAccepted = true; } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::cancelResizeScale () { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\twas resize/scale sel - kill"; #endif // NOP drag? if (!d->currentResizeScaleCommand) { return; } #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\t\tundo currentResizeScaleCommand"; #endif d->currentResizeScaleCommand->finalize (); // (unneeded but let's be safe) d->currentResizeScaleCommand->unexecute (); delete d->currentResizeScaleCommand; d->currentResizeScaleCommand = nullptr; } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::endDrawResizeScale () { // NOP drag? if (!d->currentResizeScaleCommand) { return; } d->currentResizeScaleCommand->finalize (); addNeedingContentCommand (d->currentResizeScaleCommand); d->currentResizeScaleCommand = nullptr; } //--------------------------------------------------------------------- // private QVariant kpAbstractSelectionTool::operationResizeScale (Operation op, const QVariant &data1, const QVariant &data2) { (void) data1; (void) data2; switch (op) { case HaventBegunDrawUserMessage: return /*virtual*/haventBegunDrawUserMessageResizeScale (); case SetCursor: setCursorResizeScale (); break; case BeginDraw: beginDrawResizeScale (); break; case Draw: drawResizeScale (currentPoint (), normalizedRect ()); break; case Cancel: cancelResizeScale (); break; case EndDraw: endDrawResizeScale (); break; default: Q_ASSERT (!"Unhandled operation"); break; } return {}; } //--------------------------------------------------------------------- diff --git a/tools/selection/text/kpToolText.cpp b/tools/selection/text/kpToolText.cpp index 9e0d3651..0c38c3d9 100644 --- a/tools/selection/text/kpToolText.cpp +++ b/tools/selection/text/kpToolText.cpp @@ -1,222 +1,222 @@ // REFACTOR: For all files involved in the class, refactor remaining bits and pieces and add APIDoc /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextChangeStyleCommand.h" #include "commands/tools/selection/text/kpToolTextGiveContentCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include kpToolText::kpToolText (kpToolSelectionEnvironment *environ, QObject *parent) : kpAbstractSelectionTool (i18n ("Text"), i18n ("Writes text"), Qt::Key_T, environ, parent, QStringLiteral("tool_text")), d (new kpToolTextPrivate ()) { } kpToolText::~kpToolText () { delete d; } // protected virtual [kpAbstractSelectionTool] kpAbstractSelectionContentCommand *kpToolText::newGiveContentCommand () const { kpTextSelection *textSel = document ()->textSelection (); #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::newGiveContentCommand()" << " textSel=" << textSel << "; hasContent=" << textSel->hasContent (); #endif Q_ASSERT (textSel && !textSel->hasContent ()); return new kpToolTextGiveContentCommand ( *textSel, QString()/*uninteresting child of macro cmd*/, environ ()->commandEnvironment ()); } // protected virtual [kpAbstractSelectionTool] QString kpToolText::nameOfCreateCommand () const { return i18n ("Text: Create Box"); } // protected virtual [base kpAbstractSelectionTool] void kpToolText::setSelectionBorderForHaventBegunDraw () { viewManager ()->setQueueUpdates (); { kpAbstractSelectionTool::setSelectionBorderForHaventBegunDraw (); viewManager ()->setTextCursorEnabled (true); } viewManager ()->restoreQueueUpdates (); } // public virtual [base kpAbstractSelectionTool] void kpToolText::begin () { #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "kpToolText::begin()"; #endif environ ()->enableTextToolBarActions (true); // We don't actually need this since begin() already calls it via // setSelectionBorderForHaventBegunDraw(). We leave this in for // consistency with end(). viewManager ()->setTextCursorEnabled (true); viewManager()->setInputMethodEnabled (true); endTypingCommands (); kpAbstractSelectionTool::begin (); } // public virtual [base kpAbstractSelectionTool] void kpToolText::end () { #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "kpToolText::end()"; #endif kpAbstractSelectionTool::end (); viewManager()->setInputMethodEnabled (false); viewManager ()->setTextCursorEnabled (false); environ ()->enableTextToolBarActions (false); } // public bool kpToolText::hasBegunText () const { return (d->insertCommand || d->enterCommand || d->backspaceCommand || d->backspaceWordCommand || d->deleteCommand || d->deleteWordCommand); } // public virtual [base kpTool] bool kpToolText::hasBegunShape () const { return (hasBegunDraw () || hasBegunText ()); } // protected virtual [base kpAbstractSelectionTool] kpAbstractSelectionTool::DrawType kpToolText::calculateDrawTypeInsideSelection () const { if (onSelectionToSelectText () && !controlOrShiftPressed ()) { return kpAbstractSelectionTool::SelectText; } return kpAbstractSelectionTool::calculateDrawTypeInsideSelection (); } // public virtual [base kpAbstractSelectionTool] void kpToolText::cancelShape () { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::cancelShape()"; #endif if (drawType () != None) { kpAbstractSelectionTool::cancelShape (); } else if (hasBegunText ()) { endTypingCommands (); commandHistory ()->undo (); } else { kpAbstractSelectionTool::cancelShape (); } } // public virtual [base kpTool] void kpToolText::endShape (const QPoint &thisPoint, const QRect &normalizedRect) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::endShape()"; #endif if (drawType () != None) { kpAbstractSelectionTool::endDraw (thisPoint, normalizedRect); } else if (hasBegunText ()) { endTypingCommands (); } else { kpAbstractSelectionTool::endDraw (thisPoint, normalizedRect); } } // protected virtual [base kpAbstractSelectionTool] QVariant kpToolText::operation (DrawType drawType, Operation op, const QVariant &data1, const QVariant &data2) { if (drawType == SelectText) { return selectTextOperation (op, data1, data2); } return kpAbstractSelectionTool::operation (drawType, op, data1, data2); } diff --git a/tools/selection/text/kpToolText_Commands.cpp b/tools/selection/text/kpToolText_Commands.cpp index 55bac2d3..de12dc4f 100644 --- a/tools/selection/text/kpToolText_Commands.cpp +++ b/tools/selection/text/kpToolText_Commands.cpp @@ -1,125 +1,125 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "kpToolText.h" #include "kpToolTextPrivate.h" #include "kpLogCategories.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "views/manager/kpViewManager.h" #include // private void kpToolText::endTypingCommands () { d->insertCommand = nullptr; d->enterCommand = nullptr; d->backspaceCommand = nullptr; d->backspaceWordCommand = nullptr; d->deleteCommand = nullptr; d->deleteWordCommand = nullptr; } // private void kpToolText::addNewBackspaceCommand (kpToolTextBackspaceCommand **cmd) { // TODO: why not endShapeInternal(); ditto for everywhere else in kpToolText*.cpp? if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } giveContentIfNeeded (); *cmd = new kpToolTextBackspaceCommand (i18n ("Text: Backspace"), viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), kpToolTextBackspaceCommand::DontAddBackspaceYet, environ ()->commandEnvironment ()); addNeedingContentCommand (*cmd); } // private void kpToolText::addNewDeleteCommand (kpToolTextDeleteCommand **cmd) { if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } giveContentIfNeeded (); *cmd = new kpToolTextDeleteCommand (i18n ("Text: Delete"), viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), kpToolTextDeleteCommand::DontAddDeleteYet, environ ()->commandEnvironment ()); addNeedingContentCommand (*cmd); } // private void kpToolText::addNewEnterCommand (kpToolTextEnterCommand **cmd) { if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } giveContentIfNeeded (); *cmd = new kpToolTextEnterCommand (i18n ("Text: New Line"), viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), kpToolTextEnterCommand::DontAddEnterYet, environ ()->commandEnvironment ()); addNeedingContentCommand (*cmd); } // private void kpToolText::addNewInsertCommand (kpToolTextInsertCommand **cmd) { if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } giveContentIfNeeded (); *cmd = new kpToolTextInsertCommand (i18n ("Text: Write"), viewManager ()->textCursorRow (), viewManager ()->textCursorCol (), QString (), environ ()->commandEnvironment ()); addNeedingContentCommand (*cmd); } diff --git a/tools/selection/text/kpToolText_Create.cpp b/tools/selection/text/kpToolText_Create.cpp index 0e2feb75..434c48c4 100644 --- a/tools/selection/text/kpToolText_Create.cpp +++ b/tools/selection/text/kpToolText_Create.cpp @@ -1,293 +1,293 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include #include #include "kpLogCategories.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "views/manager/kpViewManager.h" // protected virtual [kpAbstractSelectionTool] QString kpToolText::haventBegunDrawUserMessageCreate () const { return i18n ("Left drag to create text box."); } // protected virtual [base kpAbstractSelectionTool] void kpToolText::setSelectionBorderForBeginDrawCreate () { viewManager ()->setQueueUpdates (); { kpAbstractSelectionTool::setSelectionBorderForBeginDrawCreate (); viewManager ()->setTextCursorEnabled (false); } viewManager ()->restoreQueueUpdates (); } // private int kpToolText::calcClickCreateDimension (int mouseStart, int mouseEnd, int preferredMin, int smallestMin, int docSize) { Q_ASSERT (preferredMin >= smallestMin); Q_ASSERT (docSize > 0); // Get reasonable width/height for a text box. int ret = preferredMin; // X or Y increasing? if (mouseEnd >= mouseStart) { // Text box extends past document width/height? if (mouseStart + ret - 1 >= docSize) { // Cap width/height to not extend past but not below smallest // possible selection width/height ret = qMax (smallestMin, docSize - mouseStart); } } // X or Y decreasing else { // Text box extends past document start? // TODO: I doubt this code can be invoked for a click. // Maybe very tricky interplay with accidental drag detection? if (mouseStart - ret + 1 < 0) { // Cap width/height to not extend past but not below smallest // possible selection width/height. ret = qMax (smallestMin, mouseStart + 1); } } return ret; } // private bool kpToolText::shouldCreate (bool dragAccepted, const QPoint &accidentalDragAdjustedPoint, const kpTextStyle &textStyle, int *minimumWidthOut, int *minimumHeightOut, bool *newDragAccepted) { #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "CALL(dragAccepted=" << dragAccepted << ",accidentalDragAdjustedPoint=" << accidentalDragAdjustedPoint << ")"; #endif *newDragAccepted = dragAccepted; // Is the drag so short that we're essentially just clicking? // Basically, we're trying to prevent unintentional creation of 1-pixel // selections. if (!dragAccepted && accidentalDragAdjustedPoint == startPoint ()) { // We had an existing text box before the click? if (hadSelectionBeforeDraw ()) { #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "\ttext box deselect - NOP - return"; #endif // We must be attempting to deselect the text box. // This deselection has already been done by kpAbstractSelectionTool::beginDraw(). // Therefore, we are not doing a drag. return false; } // We are probably creating a new box. // This drag is currently a click -- not a drag. // As a special case, allow user to create a text box, // of reasonable ("preferred minimum") size, using a single // click. // // If the user drags further, the normal drag-to-create-a-textbox // branch [x] will execute and the size will be determined based on // the size of the drag instead. #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "\tclick creating text box"; #endif // (Click creating text box with RMB would not be obvious // since RMB menu most likely hides text box immediately // afterwards) // TODO: I suspect this logic is simply too late // TODO: We setUserShapePoints() on return but didn't before. if (mouseButton () == 1) { return false/*do not create text box*/; } // Calculate suggested width. *minimumWidthOut = calcClickCreateDimension ( startPoint ().x (), accidentalDragAdjustedPoint.x (), kpTextSelection::PreferredMinimumWidthForTextStyle (textStyle), kpTextSelection::MinimumWidthForTextStyle (textStyle), document ()->width ()); // Calculate suggested height. *minimumHeightOut = calcClickCreateDimension ( startPoint ().y (), accidentalDragAdjustedPoint.y (), kpTextSelection::PreferredMinimumHeightForTextStyle (textStyle), kpTextSelection::MinimumHeightForTextStyle (textStyle), document ()->height ()); // Do _not_ set "newDragAccepted" to true as we want // this text box to remain at the click-given size, in the absence // of any dragging. In other words, if draw() is called again // and therefore, we are called again, but the mouse has not // moved, we do want this branch to execute again, not // Branch [x]. return true/*do create text box*/; } // Dragging to create a text box [x]. // // The size will be determined based on the size of the drag. #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "\tdrag creating text box"; #endif *minimumWidthOut = kpTextSelection::MinimumWidthForTextStyle (textStyle); *minimumHeightOut = kpTextSelection::MinimumHeightForTextStyle (textStyle); *newDragAccepted = true; return true/*do create text box*/; } // protected virtual [kpAbstractSelectionTool] bool kpToolText::drawCreateMoreSelectionAndUpdateStatusBar ( bool dragAccepted, const QPoint &accidentalDragAdjustedPoint, const QRect &normalizedRectIn) { // (will mutate this) QRect normalizedRect = normalizedRectIn; const kpTextStyle textStyle = environ ()->textStyle (); // // Calculate Text Box Rectangle. // bool newDragAccepted = dragAccepted; // (will set both variables) int minimumWidth = 0, minimumHeight = 0; if (!shouldCreate (dragAccepted, accidentalDragAdjustedPoint, textStyle, &minimumWidth, &minimumHeight, &newDragAccepted)) { setUserShapePoints (accidentalDragAdjustedPoint); return newDragAccepted; } // Make sure the dragged out rectangle is of the minimum width we just // calculated. if (normalizedRect.width () < minimumWidth) { if (accidentalDragAdjustedPoint.x () >= startPoint ().x ()) { normalizedRect.setWidth (minimumWidth); } else { normalizedRect.setX (normalizedRect.right () - minimumWidth + 1); } } // Make sure the dragged out rectangle is of the minimum height we just // calculated. if (normalizedRect.height () < minimumHeight) { if (accidentalDragAdjustedPoint.y () >= startPoint ().y ()) { normalizedRect.setHeight (minimumHeight); } else { normalizedRect.setY (normalizedRect.bottom () - minimumHeight + 1); } } #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "\t\tnormalizedRect=" << normalizedRect << " kpTextSelection::preferredMinimumSize=" << QSize (minimumWidth, minimumHeight); #endif // // Construct and Deploy Text Box. // // Create empty text box. QList textLines; kpTextSelection textSel (normalizedRect, textLines, textStyle); // Render. viewManager ()->setTextCursorPosition (0, 0); document ()->setSelection (textSel); // // Update Status Bar. // QPoint actualEndPoint = KP_INVALID_POINT; if (startPoint () == normalizedRect.topLeft ()) { actualEndPoint = normalizedRect.bottomRight (); } else if (startPoint () == normalizedRect.bottomRight ()) { actualEndPoint = normalizedRect.topLeft (); } else if (startPoint () == normalizedRect.topRight ()) { actualEndPoint = normalizedRect.bottomLeft (); } else if (startPoint () == normalizedRect.bottomLeft ()) { actualEndPoint = normalizedRect.topRight (); } setUserShapePoints (startPoint (), actualEndPoint); return newDragAccepted; } diff --git a/tools/selection/text/kpToolText_CursorCalc.cpp b/tools/selection/text/kpToolText_CursorCalc.cpp index 5813b1d0..0969b6ed 100644 --- a/tools/selection/text/kpToolText_CursorCalc.cpp +++ b/tools/selection/text/kpToolText_CursorCalc.cpp @@ -1,220 +1,220 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextChangeStyleCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include #include // protected static bool kpToolText::CursorIsOnWordChar (const QList &textLines, int cursorRow, int cursorCol) { return (cursorRow >= 0 && cursorRow < textLines.size () && cursorCol >= 0 && cursorCol < textLines [cursorRow].length () && !textLines [cursorRow][cursorCol].isSpace ()); } // protected static bool kpToolText::CursorIsAtStart (const QList &, int cursorRow, int cursorCol) { return (cursorRow == 0 && cursorCol == 0); } // protected static bool kpToolText::CursorIsAtEnd (const QList &textLines, int cursorRow, int cursorCol) { if (textLines.isEmpty ()) { return (cursorRow == 0 && cursorCol == 0); } return (cursorRow == textLines.size () - 1 && cursorCol == textLines [cursorRow].length ()); } // protected static void kpToolText::MoveCursorLeft (const QList &textLines, int *cursorRow, int *cursorCol) { if (textLines.isEmpty ()) { return; } (*cursorCol)--; if (*cursorCol < 0) { (*cursorRow)--; if (*cursorRow < 0) { *cursorRow = 0; *cursorCol = 0; } else { *cursorCol = textLines [*cursorRow].length (); } } } // protected static void kpToolText::MoveCursorRight (const QList &textLines, int *cursorRow, int *cursorCol) { if (textLines.isEmpty ()) { return; } (*cursorCol)++; if (*cursorCol > textLines [*cursorRow].length ()) { (*cursorRow)++; if (*cursorRow > textLines.size () - 1) { *cursorRow = textLines.size () - 1; *cursorCol = textLines [*cursorRow].length (); } else { *cursorCol = 0; } } } #define IS_ON_SPACE_OR_EOL() !CursorIsOnWordChar (textLines, *cursorRow, *cursorCol) // protected static int kpToolText::MoveCursorToWordStart (const QList &textLines, int *cursorRow, int *cursorCol) { if (textLines.isEmpty ()) { return 0; } int numMoves = 0; #define IS_ON_ANCHOR() \ (CursorIsOnWordChar (textLines, *cursorRow, *cursorCol) && \ (cursorCol == 0 || \ !CursorIsOnWordChar (textLines, *cursorRow, *cursorCol - 1))) #define MOVE_CURSOR_LEFT() \ (MoveCursorLeft (textLines, cursorRow, cursorCol), ++numMoves) // (these comments will exclude the row=0,col=0 boundary case) if (IS_ON_ANCHOR ()) { MOVE_CURSOR_LEFT (); } // --- now we're not on an anchor point (start of word) --- // End up on a letter... while (!(*cursorRow == 0 && *cursorCol == 0) && (IS_ON_SPACE_OR_EOL ())) { MOVE_CURSOR_LEFT (); } // --- now we're on a letter --- // Find anchor point while (!(*cursorRow == 0 && *cursorCol == 0) && !IS_ON_ANCHOR ()) { MOVE_CURSOR_LEFT (); } #undef IS_ON_ANCHOR #undef MOVE_CURSOR_LEFT return numMoves; } // protected static int kpToolText::MoveCursorToNextWordStart (const QList &textLines, int *cursorRow, int *cursorCol) { if (textLines.isEmpty ()) { return 0; } int numMoves = 0; #define IS_AT_END() CursorIsAtEnd (textLines, *cursorRow, *cursorCol) #define MOVE_CURSOR_RIGHT() \ (MoveCursorRight (textLines, cursorRow, cursorCol), ++numMoves) // (these comments will exclude the last row,end col boundary case) // Find space while (!IS_AT_END () && !IS_ON_SPACE_OR_EOL ()) { MOVE_CURSOR_RIGHT (); } // --- now we're on a space --- // Find letter while (!IS_AT_END () && IS_ON_SPACE_OR_EOL ()) { MOVE_CURSOR_RIGHT (); } // --- now we're on a letter --- #undef IS_AT_END #undef MOVE_CURSOR_RIGHT return numMoves; } #undef IS_ON_SPACE_OR_EOL diff --git a/tools/selection/text/kpToolText_InputMethodEvents.cpp b/tools/selection/text/kpToolText_InputMethodEvents.cpp index 878d2d1d..0ffde07f 100644 --- a/tools/selection/text/kpToolText_InputMethodEvents.cpp +++ b/tools/selection/text/kpToolText_InputMethodEvents.cpp @@ -1,96 +1,96 @@ /* 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_TOOL_TEXT 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include #include "kpLogCategories.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" //--------------------------------------------------------------------- void kpToolText::inputMethodEvent (QInputMethodEvent *e) { #if DEBUG_KP_TOOL_TEXT && 1 qCDebug(kpLogTools) << "kpToolText::inputMethodEvent() preeditString='" << e->preeditString () << "commitString = " << e->commitString () << " replacementStart=" << e->replacementStart () << " replacementLength=" << e->replacementLength (); #endif kpTextSelection *textSel = document ()->textSelection (); if (hasBegunDraw() || !textSel) { e->ignore(); return; } kpPreeditText previous = textSel->preeditText (); kpPreeditText next (e); int textCursorRow = viewManager ()->textCursorRow (); int textCursorCol = viewManager ()->textCursorCol (); if (!next.isEmpty ()) { if (previous.position().x () < 0 && previous.position().y () < 0) { next.setPosition (QPoint(textCursorCol, textCursorRow)); } else { next.setPosition(previous.position ()); } } textSel->setPreeditText (next); textCursorCol = textCursorCol - previous.cursorPosition () + next.cursorPosition (); viewManager ()->setTextCursorPosition (textCursorRow, textCursorCol); QString commitString = e->commitString (); if (!commitString.isEmpty ()) { // commit string if (!d->insertCommand) { addNewInsertCommand (&d->insertCommand); } d->insertCommand->addText (commitString); } e->accept (); } //--------------------------------------------------------------------- diff --git a/tools/selection/text/kpToolText_KeyboardEvents.cpp b/tools/selection/text/kpToolText_KeyboardEvents.cpp index 7b80cda9..1fb10dfd 100644 --- a/tools/selection/text/kpToolText_KeyboardEvents.cpp +++ b/tools/selection/text/kpToolText_KeyboardEvents.cpp @@ -1,212 +1,212 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include #include #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextChangeStyleCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include // protected virtual [base kpTool] bool kpToolText::viewEvent (QEvent *e) { const bool isShortcutOverrideEvent = (e->type () == QEvent::ShortcutOverride); const bool haveTextSelection = document ()->textSelection (); #if DEBUG_KP_TOOL_TEXT && 0 qCDebug(kpLogTools) << "kpToolText::viewEvent() type=" << e->type () << " isShortcutOverrideEvent=" << isShortcutOverrideEvent << " haveTextSel=" << haveTextSelection; #endif if (!isShortcutOverrideEvent || !haveTextSelection) { return kpAbstractSelectionTool::viewEvent (e); } auto *ke = dynamic_cast (e); #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::viewEvent() key=" << ke->key () << " modifiers=" << ke->modifiers () << " QChar.isPrint()=" << QChar (ke->key ()).isPrint (); #endif // Can't be shortcut? if (ke->key () == 0 && ke->key () == Qt::Key_unknown) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tcan't be shortcut - safe to not react"; #endif } // Normal letter (w/ or w/o shift, keypad button ok)? // TODO: don't like this check else if ((ke->modifiers () & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) == 0 && !( ke->text ().isEmpty ())) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tis text - grab"; #endif e->accept (); } else { // Strictly speaking, we should grab stuff like the arrow keys // and enter. In any case, should be done down in kpTool (as that // uses arrow keys too). } return kpAbstractSelectionTool::event (e); } // protected virtual [base kpAbstractSelectionTool] void kpToolText::keyPressEvent (QKeyEvent *e) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::keyPressEvent(e->text='" << e->text () << "')"; #endif e->ignore (); if (hasBegunDraw ()) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\talready began draw with mouse - passing on event to kpTool"; #endif kpAbstractSelectionTool::keyPressEvent (e); return; } kpTextSelection * const textSel = document ()->textSelection (); if (!textSel) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tno text sel - passing on event to kpTool"; #endif //if (hasBegunShape ()) // endShape (currentPoint (), normalizedRect ()); kpAbstractSelectionTool::keyPressEvent (e); return; } // (All handle.+()'s require this info) const QList textLines = textSel->textLines (); const int cursorRow = viewManager ()->textCursorRow (); const int cursorCol = viewManager ()->textCursorCol (); // TODO: KTextEdit::keyPressEvent() uses KStandardShortcut instead of hardcoding; same fix for kpTool? switch (e->key ()) { case Qt::Key_Up: handleUpKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Down: handleDownKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Left: handleLeftKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Right: handleRightKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Home: handleHomeKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_End: handleEndKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Backspace: handleBackspaceKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Delete: handleDeleteKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Enter: case Qt::Key_Return: handleEnterKeyPress (e, textLines, cursorRow, cursorCol); break; default: handleTextTyped (e, textLines, cursorRow, cursorCol); break; } if (!e->isAccepted ()) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tkey processing did not accept (text was '" << e->text () << "') - passing on event to kpAbstractSelectionTool"; #endif //if (hasBegunShape ()) // endShape (currentPoint (), normalizedRect ()); kpAbstractSelectionTool::keyPressEvent (e); return; } } diff --git a/tools/selection/text/kpToolText_KeyboardEvents_HandleArrowKeys.cpp b/tools/selection/text/kpToolText_KeyboardEvents_HandleArrowKeys.cpp index 2eaefeb3..00c10872 100644 --- a/tools/selection/text/kpToolText_KeyboardEvents_HandleArrowKeys.cpp +++ b/tools/selection/text/kpToolText_KeyboardEvents_HandleArrowKeys.cpp @@ -1,221 +1,221 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include #include #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextChangeStyleCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" // protected void kpToolText::handleUpKeyPress (QKeyEvent *e, const QList &textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tup pressed"; #endif if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } if (!textLines.isEmpty () && cursorRow > 0) { cursorRow--; cursorCol = qMin (cursorCol, textLines [cursorRow].length ()); viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } e->accept (); } // protected void kpToolText::handleDownKeyPress (QKeyEvent *e, const QList &textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tdown pressed"; #endif if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } if (!textLines.isEmpty () && cursorRow < textLines.size () - 1) { cursorRow++; cursorCol = qMin (cursorCol, textLines [cursorRow].length ()); viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } e->accept (); } // protected void kpToolText::handleLeftKeyPress (QKeyEvent *e, const QList &textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tleft pressed"; #endif if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } if (!textLines.isEmpty ()) { if ((e->modifiers () & Qt::ControlModifier) == 0) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tmove single char"; #endif MoveCursorLeft (textLines, &cursorRow, &cursorCol); viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } else { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tmove to start of word"; #endif MoveCursorToWordStart (textLines, &cursorRow, &cursorCol); viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } } e->accept (); } // protected void kpToolText::handleRightKeyPress (QKeyEvent *e, const QList &textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tright pressed"; #endif if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } if (!textLines.isEmpty ()) { if ((e->modifiers () & Qt::ControlModifier) == 0) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tmove single char"; #endif MoveCursorRight (textLines, &cursorRow, &cursorCol); viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } else { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tmove to start of next word"; #endif MoveCursorToNextWordStart (textLines, &cursorRow, &cursorCol); viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } } e->accept (); } // protected void kpToolText::handleHomeKeyPress (QKeyEvent *e, const QList &textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\thome pressed"; #endif if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } if (!textLines.isEmpty ()) { if (e->modifiers () & Qt::ControlModifier) { cursorRow = 0; } cursorCol = 0; viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } e->accept (); } // protected void kpToolText::handleEndKeyPress (QKeyEvent *e, const QList &textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tend pressed"; #endif if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } if (!textLines.isEmpty ()) { if (e->modifiers () & Qt::ControlModifier) { cursorRow = textLines.size () - 1; } cursorCol = textLines [cursorRow].length (); viewManager ()->setTextCursorPosition (cursorRow, cursorCol); } e->accept (); } diff --git a/tools/selection/text/kpToolText_KeyboardEvents_HandleTypingKeys.cpp b/tools/selection/text/kpToolText_KeyboardEvents_HandleTypingKeys.cpp index a6e12762..cc43e4fb 100644 --- a/tools/selection/text/kpToolText_KeyboardEvents_HandleTypingKeys.cpp +++ b/tools/selection/text/kpToolText_KeyboardEvents_HandleTypingKeys.cpp @@ -1,202 +1,202 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextChangeStyleCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include //--------------------------------------------------------------------- // protected void kpToolText::handleBackspaceKeyPress (QKeyEvent *e, const QList &textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tbackspace pressed"; #endif if (!textLines.isEmpty ()) { if ((e->modifiers () & Qt::ControlModifier) == 0) { if (!d->backspaceCommand) { addNewBackspaceCommand (&d->backspaceCommand); } d->backspaceCommand->addBackspace (); } else { if (!d->backspaceWordCommand) { addNewBackspaceCommand (&d->backspaceWordCommand); } const int numMoves = MoveCursorToWordStart (textLines, &cursorRow, &cursorCol); viewManager ()->setQueueUpdates (); { for (int i = 0; i < numMoves; i++) { d->backspaceWordCommand->addBackspace (); } } viewManager ()->restoreQueueUpdates (); Q_ASSERT (cursorRow == viewManager ()->textCursorRow ()); Q_ASSERT (cursorCol == viewManager ()->textCursorCol ()); } } e->accept (); } //--------------------------------------------------------------------- // protected void kpToolText::handleDeleteKeyPress (QKeyEvent *e, const QList & textLines, int cursorRow, int cursorCol) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tdelete pressed"; #endif if (!textLines.isEmpty ()) { if ((e->modifiers () & Qt::ControlModifier) == 0) { if (!d->deleteCommand) { addNewDeleteCommand (&d->deleteCommand); } d->deleteCommand->addDelete (); } else { if (!d->deleteWordCommand) { addNewDeleteCommand (&d->deleteWordCommand); } // We don't want to know the cursor pos of the next word start // as delete should keep cursor in same pos. int cursorRowThrowAway = cursorRow, cursorColThrowAway = cursorCol; const int numMoves = MoveCursorToNextWordStart (textLines, &cursorRowThrowAway, &cursorColThrowAway); viewManager ()->setQueueUpdates (); { for (int i = 0; i < numMoves; i++) { d->deleteWordCommand->addDelete (); } } viewManager ()->restoreQueueUpdates (); // Assert unchanged as delete should keep cursor in same pos. Q_ASSERT (cursorRow == viewManager ()->textCursorRow ()); Q_ASSERT (cursorCol == viewManager ()->textCursorCol ()); } } e->accept (); } //--------------------------------------------------------------------- // protected void kpToolText::handleEnterKeyPress (QKeyEvent *e, const QList & /*textLines*/, int /*cursorRow*/, int /*cursorCol*/) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tenter pressed"; #endif // It's OK for to be empty. if (!d->enterCommand) { addNewEnterCommand (&d->enterCommand); } d->enterCommand->addEnter (); e->accept (); } //--------------------------------------------------------------------- // protected void kpToolText::handleTextTyped (QKeyEvent *e, const QList & /*textLines*/, int /*cursorRow*/, int /*cursorCol*/) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\ttext=" << e->text(); #endif QString usableText; for (int i = 0; i < e->text ().length (); i++) { if (e->text ().at (i).isPrint ()) { usableText += e->text ().at (i); } } #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tusableText=" << usableText; #endif if (usableText.isEmpty ()) { // Don't end the current shape nor accept the event -- the event // wasn't for us. return; } // --- It's OK for to be empty. --- if (!d->insertCommand) { addNewInsertCommand (&d->insertCommand); } d->insertCommand->addText (usableText); e->accept (); } diff --git a/tools/selection/text/kpToolText_Move.cpp b/tools/selection/text/kpToolText_Move.cpp index 91f61490..0d547caa 100644 --- a/tools/selection/text/kpToolText_Move.cpp +++ b/tools/selection/text/kpToolText_Move.cpp @@ -1,63 +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_TOOL_TEXT 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include #include "views/manager/kpViewManager.h" // protected virtual [kpAbstractSelectionTool] QString kpToolText::haventBegunDrawUserMessageMove () const { return i18n ("Left drag to move text box."); } // protected virtual [base kpAbstractSelectionTool] void kpToolText::setSelectionBorderForBeginDrawMove () { viewManager ()->setQueueUpdates (); { kpAbstractSelectionTool::setSelectionBorderForBeginDrawMove (); viewManager ()->setTextCursorEnabled (false); } viewManager ()->restoreQueueUpdates (); } // protected virtual [kpAbstractSelectionTool] QString kpToolText::nonSmearMoveCommandName () const { return i18n ("Text: Move Box"); } diff --git a/tools/selection/text/kpToolText_ResizeScale.cpp b/tools/selection/text/kpToolText_ResizeScale.cpp index fb322551..0ddcf9ca 100644 --- a/tools/selection/text/kpToolText_ResizeScale.cpp +++ b/tools/selection/text/kpToolText_ResizeScale.cpp @@ -1,55 +1,55 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include #include "views/manager/kpViewManager.h" // protected virtual [kpAbstractSelectionTool] QString kpToolText::haventBegunDrawUserMessageResizeScale () const { return i18n ("Left drag to resize text box."); } // protected virtual [base kpAbstractSelectionTool] void kpToolText::setSelectionBorderForBeginDrawResizeScale () { viewManager ()->setQueueUpdates (); { kpAbstractSelectionTool::setSelectionBorderForBeginDrawResizeScale (); viewManager ()->setTextCursorEnabled (false); } viewManager ()->restoreQueueUpdates (); } diff --git a/tools/selection/text/kpToolText_SelectText.cpp b/tools/selection/text/kpToolText_SelectText.cpp index 6a0f92d7..521ae857 100644 --- a/tools/selection/text/kpToolText_SelectText.cpp +++ b/tools/selection/text/kpToolText_SelectText.cpp @@ -1,137 +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_TEXT 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" // private bool kpToolText::onSelectionToSelectText () const { kpView *v = viewManager ()->viewUnderCursor (); if (!v) { return false; } return v->mouseOnSelectionToSelectText (currentViewPoint ()); } // private QString kpToolText::haventBegunDrawUserMessageSelectText () const { return i18n ("Left click to change cursor position."); } // private void kpToolText::setCursorSelectText () { viewManager ()->setCursor (Qt::IBeamCursor); } // private void kpToolText::beginDrawSelectText () { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\t\tis select cursor pos"; #endif kpTextSelection *textSel = document ()->textSelection (); Q_ASSERT (textSel); int newRow, newCol; if (textSel->hasContent ()) { newRow = textSel->closestTextRowForPoint (currentPoint ()); newCol = textSel->closestTextColForPoint (currentPoint ()); } else { newRow = newCol = 0; } #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\t\t\told: row=" << viewManager ()->textCursorRow () << "col=" << viewManager ()->textCursorCol (); qCDebug(kpLogTools) << "\t\t\tnew: row=" << newRow << "col=" << newCol; #endif viewManager ()->setTextCursorPosition (newRow, newCol); } // protected virtual QVariant kpToolText::selectTextOperation (Operation op, const QVariant &data1, const QVariant &data2) { (void) data1; (void) data2; switch (op) { case HaventBegunDrawUserMessage: return haventBegunDrawUserMessageSelectText (); case SetCursor: setCursorSelectText (); break; case BeginDraw: beginDrawSelectText (); break; case Draw: // Do nothing. break; case Cancel: // Not called. REFACTOR: Change this? break; case EndDraw: // Do nothing. break; default: Q_ASSERT (!"Unhandled operation"); break; } return {}; } diff --git a/tools/selection/text/kpToolText_TextStyle.cpp b/tools/selection/text/kpToolText_TextStyle.cpp index ef1fcaef..31564d60 100644 --- a/tools/selection/text/kpToolText_TextStyle.cpp +++ b/tools/selection/text/kpToolText_TextStyle.cpp @@ -1,334 +1,334 @@ /* 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 0 +#define DEBUG_KP_TOOL_TEXT 1 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextChangeStyleCommand.h" #include "commands/tools/selection/text/kpToolTextGiveContentCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include // protected bool kpToolText::shouldChangeTextStyle () const { if (environ ()->settingTextStyle ()) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\trecursion - abort setting text style: " << environ ()->settingTextStyle (); #endif return false; } if (!document ()->textSelection ()) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tno text selection - abort setting text style"; #endif return false; } return true; } // protected void kpToolText::changeTextStyle (const QString &name, const kpTextStyle &newTextStyle, const kpTextStyle &oldTextStyle) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::changeTextStyle(" << name << ")"; #endif if (hasBegunShape ()) { endShape (currentPoint (), normalizedRect ()); } commandHistory ()->addCommand ( new kpToolTextChangeStyleCommand ( name, newTextStyle, oldTextStyle, environ ()->commandEnvironment ())); } // protected slot virtual [base kpAbstractSelectionTool] void kpToolText::slotIsOpaqueChanged (bool isOpaque) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotIsOpaqueChanged()"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setBackgroundOpaque (!isOpaque); changeTextStyle (newTextStyle.isBackgroundOpaque () ? i18n ("Text: Opaque Background") : i18n ("Text: Transparent Background"), newTextStyle, oldTextStyle); } // protected slot virtual [base kpTool] void kpToolText::slotColorsSwapped (const kpColor &newForegroundColor, const kpColor &newBackgroundColor) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotColorsSwapped()"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setForegroundColor (newBackgroundColor); oldTextStyle.setBackgroundColor (newForegroundColor); changeTextStyle (i18n ("Text: Swap Colors"), newTextStyle, oldTextStyle); } // protected slot virtual [base kpTool] void kpToolText::slotForegroundColorChanged (const kpColor & /*color*/) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotForegroundColorChanged()"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setForegroundColor (oldForegroundColor ()); changeTextStyle (i18n ("Text: Foreground Color"), newTextStyle, oldTextStyle); } // protected slot virtual [base kpAbstractSelectionTool] void kpToolText::slotBackgroundColorChanged (const kpColor & /*color*/) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotBackgroundColorChanged()"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setBackgroundColor (oldBackgroundColor ()); changeTextStyle (i18n ("Text: Background Color"), newTextStyle, oldTextStyle); } // protected slot virtual [base kpAbstractSelectionTool] void kpToolText::slotColorSimilarityChanged (double, int) { // --- don't pass on event to kpAbstractSelectionTool which would have set the // SelectionTransparency - not relevant to the Text Tool --- } // public slot void kpToolText::slotFontFamilyChanged (const QString &fontFamily, const QString &oldFontFamily) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotFontFamilyChanged() new=" << fontFamily << " old=" << oldFontFamily; #else (void) fontFamily; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); // Figure out old text style. kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setFontFamily (oldFontFamily); changeTextStyle (i18n ("Text: Font"), newTextStyle, oldTextStyle); } // public slot void kpToolText::slotFontSizeChanged (int fontSize, int oldFontSize) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotFontSizeChanged() new=" << fontSize << " old=" << oldFontSize; #else (void) fontSize; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); // Figure out old text style. kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setFontSize (oldFontSize); changeTextStyle (i18n ("Text: Font Size"), newTextStyle, oldTextStyle); } // public slot void kpToolText::slotBoldChanged (bool isBold) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotBoldChanged(" << isBold << ")"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); // Figure out old text style. kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setBold (!isBold); changeTextStyle (i18n ("Text: Bold"), newTextStyle, oldTextStyle); } // public slot void kpToolText::slotItalicChanged (bool isItalic) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotItalicChanged(" << isItalic << ")"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); // Figure out old text style. kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setItalic (!isItalic); changeTextStyle (i18n ("Text: Italic"), newTextStyle, oldTextStyle); } // public slot void kpToolText::slotUnderlineChanged (bool isUnderline) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotUnderlineChanged(" << isUnderline << ")"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); // Figure out old text style. kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setUnderline (!isUnderline); changeTextStyle (i18n ("Text: Underline"), newTextStyle, oldTextStyle); } // public slot void kpToolText::slotStrikeThruChanged (bool isStrikeThru) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::slotStrikeThruChanged(" << isStrikeThru << ")"; #endif if (!shouldChangeTextStyle ()) { return; } kpTextStyle newTextStyle = environ ()->textStyle (); // Figure out old text style. kpTextStyle oldTextStyle = newTextStyle; oldTextStyle.setStrikeThru (!isStrikeThru); changeTextStyle (i18n ("Text: Strike Through"), newTextStyle, oldTextStyle); } diff --git a/views/kpThumbnailView.cpp b/views/kpThumbnailView.cpp index e001aba5..17d0149b 100644 --- a/views/kpThumbnailView.cpp +++ b/views/kpThumbnailView.cpp @@ -1,96 +1,94 @@ /* 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 0 +#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 () - << endl; + << " 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(" << name () << ")::resizeEvent()" - << endl; + 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/kpUnzoomedThumbnailView.cpp b/views/kpUnzoomedThumbnailView.cpp index ee31f432..7cd878ab 100644 --- a/views/kpUnzoomedThumbnailView.cpp +++ b/views/kpUnzoomedThumbnailView.cpp @@ -1,222 +1,222 @@ /* 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_UNZOOMED_THUMBNAIL_VIEW 0 +#define DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW 1 #include "views/kpUnzoomedThumbnailView.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "views/manager/kpViewManager.h" #include "kpViewScrollableContainer.h" #include #include //--------------------------------------------------------------------- struct kpUnzoomedThumbnailViewPrivate { }; kpUnzoomedThumbnailView::kpUnzoomedThumbnailView ( kpDocument *document, kpToolToolBar *toolToolBar, kpViewManager *viewManager, kpView *buddyView, kpViewScrollableContainer *scrollableContainer, QWidget *parent) : kpThumbnailView (document, toolToolBar, viewManager, buddyView, scrollableContainer, parent), d (new kpUnzoomedThumbnailViewPrivate ()) { if (buddyViewScrollableContainer ()) { connect (buddyViewScrollableContainer(), &kpViewScrollableContainer::contentsMoved, this, &kpUnzoomedThumbnailView::adjustToEnvironment); } // Call to virtual function - this is why the class is sealed adjustToEnvironment (); } //--------------------------------------------------------------------- kpUnzoomedThumbnailView::~kpUnzoomedThumbnailView () { delete d; } //--------------------------------------------------------------------- // public virtual [base kpThumbnailView] QString kpUnzoomedThumbnailView::caption () const { return i18n ("Unzoomed Mode - Thumbnail"); } //--------------------------------------------------------------------- // public slot virtual [base kpView] void kpUnzoomedThumbnailView::adjustToEnvironment () { if (!buddyView () || !buddyViewScrollableContainer () || !document ()) { return; } const int scrollViewContentsX = buddyViewScrollableContainer()->horizontalScrollBar()->value(); const int scrollViewContentsY = buddyViewScrollableContainer ()->verticalScrollBar()->value(); #if DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW - qCDebug(kpLogViews) << "kpUnzoomedThumbnailView(" << name () + qCDebug(kpLogViews) << "kpUnzoomedThumbnailView(" << caption () << ")::adjustToEnvironment(" << scrollViewContentsX << "," << scrollViewContentsY << ") width=" << width () << " height=" << height () << endl; #endif #if 1 int x; if (document ()->width () > width ()) { x = static_cast (buddyView ()->transformViewToDocX (scrollViewContentsX)); const int rightMostAllowedX = qMax (0, document ()->width () - width ()); #if DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW qCDebug(kpLogViews) << "\tdocX=" << x << " docWidth=" << document ()->width () << " rightMostAllowedX=" << rightMostAllowedX; #endif if (x > rightMostAllowedX) { x = rightMostAllowedX; } } // Thumbnail width <= doc width else { // Center X (rather than flush left to be consistent with // kpZoomedThumbnailView) x = -(width () - document ()->width ()) / 2; } int y; if (document ()->height () > height ()) { y = static_cast (buddyView ()->transformViewToDocY (scrollViewContentsY)); const int bottomMostAllowedY = qMax (0, document ()->height () - height ()); #if DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW qCDebug(kpLogViews) << "\tdocY=" << y << " docHeight=" << document ()->height () << " bottomMostAllowedY=" << bottomMostAllowedY; #endif if (y > bottomMostAllowedY) { y = bottomMostAllowedY; } } // Thumbnail height <= doc height else { // Center Y (rather than flush top to be consistent with // kpZoomedThumbnailView) y = -(height () - document ()->height ()) / 2; } // Prefer to keep visible area centred in thumbnail instead of flushed left. // Gives more editing context to the left and top. // But feels awkward for left-to-right users. So disabled for now. // Not totally tested. #else if (!buddyViewScrollableContainer ()) { return; } QRect docRect = buddyView ()->transformViewToDoc ( QRect (buddyViewScrollableContainer ()->horizontalScrollBar()->value(), buddyViewScrollableContainer ()->verticalScrollBar()->value(), qMin (buddyView ()->width (), buddyViewScrollableContainer ()->viewport()->width ()), qMin (buddyView ()->height (), buddyViewScrollableContainer ()->viewport()->height ()))); x = docRect.x () - (width () - docRect.width ()) / 2; qCDebug(kpLogViews) << "\tnew suggest x=" << x; const int rightMostAllowedX = qMax (0, document ()->width () - width ()); if (x < 0) { x = 0; } if (x > rightMostAllowedX) { x = rightMostAllowedX; } y = docRect.y () - (height () - docRect.height ()) / 2; qCDebug(kpLogViews) << "\tnew suggest y=" << y; const int bottomMostAllowedY = qMax (0, document ()->height () - height ()); if (y < 0) { y = 0; } if (y > bottomMostAllowedY) { y = bottomMostAllowedY; } #endif if (viewManager ()) { viewManager ()->setFastUpdates (); viewManager ()->setQueueUpdates (); } { // OPT: scrollView impl would be much, much faster setOrigin (QPoint (-x, -y)); setMaskToCoverDocument (); // Above might be a NOP even if e.g. doc size changed so force // update if (viewManager ()) { viewManager ()->updateView (this); } } if (viewManager ()) { viewManager ()->restoreQueueUpdates (); viewManager ()->restoreFastUpdates (); } } diff --git a/views/kpView.cpp b/views/kpView.cpp index 0497016e..7b894743 100644 --- a/views/kpView.cpp +++ b/views/kpView.cpp @@ -1,688 +1,688 @@ /* 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 0 +#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 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 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 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_Events.cpp b/views/kpView_Events.cpp index e47d7c9d..02177548 100644 --- a/views/kpView_Events.cpp +++ b/views/kpView_Events.cpp @@ -1,281 +1,281 @@ /* 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 0 +#define DEBUG_KP_VIEW 1 #define DEBUG_KP_VIEW_RENDERER ((DEBUG_KP_VIEW && 1) || 0) #include "views/kpView.h" #include "kpViewPrivate.h" #if DEBUG_KP_VIEW #include "kpLogCategories.h" #endif #include #include #include "tools/kpTool.h" //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpView::mouseMoveEvent (QMouseEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::mouseMoveEvent (" << e->x () << "," << e->y () << ")" << endl; #endif // TODO: This is wrong if you leaveEvent the mainView by mouseMoving on the // mainView, landing on top of the thumbnailView cleverly put on top // of the mainView. setHasMouse (rect ().contains (e->pos ())); if (tool ()) { tool ()->mouseMoveEvent (e); } e->accept (); } // protected virtual [base QWidget] void kpView::mousePressEvent (QMouseEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::mousePressEvent (" << e->x () << "," << e->y () << ")" << endl; #endif setHasMouse (true); if (tool ()) { tool ()->mousePressEvent (e); } e->accept (); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpView::mouseReleaseEvent (QMouseEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::mouseReleaseEvent (" << e->x () << "," << e->y () << ")" << endl; #endif setHasMouse (rect ().contains (e->pos ())); if (tool ()) { tool ()->mouseReleaseEvent (e); } e->accept (); } //--------------------------------------------------------------------- // public virtual [base QWidget] void kpView::wheelEvent (QWheelEvent *e) { if (tool ()) { tool ()->wheelEvent (e); } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpView::keyPressEvent (QKeyEvent *e) { #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "kpView(" << objectName () << ")::keyPressEvent()" << e->text(); #endif if (tool ()) { tool ()->keyPressEvent (e); } e->accept (); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpView::keyReleaseEvent (QKeyEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::keyReleaseEvent()"; #endif if (tool ()) { tool ()->keyReleaseEvent (e); } e->accept (); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpView::inputMethodEvent (QInputMethodEvent *e) { #if DEBUG_KP_VIEW && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::inputMethodEvent()"; #endif if (tool ()) { tool ()->inputMethodEvent (e); } e->accept (); } // protected virtual [base QWidget] bool kpView::event (QEvent *e) { #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "kpView::event() invoking kpTool::event()"; #endif if (tool () && tool ()->viewEvent (e)) { #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tkpView::event() - tool said eat event, ret true"; #endif return true; } #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tkpView::event() - no tool or said false, call QWidget::event()"; #endif return QWidget::event (e); } // protected virtual [base QWidget] void kpView::focusInEvent (QFocusEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::focusInEvent()"; #endif if (tool ()) { tool ()->focusInEvent (e); } } // protected virtual [base QWidget] void kpView::focusOutEvent (QFocusEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::focusOutEvent()"; #endif if (tool ()) { tool ()->focusOutEvent (e); } } // protected virtual [base QWidget] void kpView::enterEvent (QEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::enterEvent()"; #endif // Don't call setHasMouse(true) as it displays the brush cursor (if // active) when dragging open a menu and then dragging // past the extents of the menu due to Qt sending us an EnterEvent. // We're already covered by MouseMoveEvent anyway. // // But disabling this causes a more serious problem: RMB on a text // box and Esc. We have no other reliable way to determine if the // mouse is still above the view (user could have moved mouse out // while RMB menu was up) and hence the cursor is not updated. setHasMouse (true); if (tool ()) { tool ()->enterEvent (e); } } // protected virtual [base QWidget] void kpView::leaveEvent (QEvent *e) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::leaveEvent()"; #endif setHasMouse (false); if (tool ()) { tool ()->leaveEvent (e); } } // protected virtual [base QWidget] void kpView::dragEnterEvent (QDragEnterEvent *) { #if DEBUG_KP_VIEW && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::dragEnterEvent()"; #endif setHasMouse (true); } // protected virtual [base QWidget] void kpView::dragLeaveEvent (QDragLeaveEvent *) { #if DEBUG_KP_VIEW && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::dragLeaveEvent"; #endif setHasMouse (false); } // protected virtual [base QWidget] void kpView::resizeEvent (QResizeEvent *e) { #if DEBUG_KP_VIEW && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::resizeEvent(" << e->size () << " vs actual=" << size () << ") old=" << e->oldSize () << endl; #endif QWidget::resizeEvent (e); emit sizeChanged (width (), height ()); emit sizeChanged (size ()); } diff --git a/views/kpView_Paint.cpp b/views/kpView_Paint.cpp index 1a30359b..cf84cffb 100644 --- a/views/kpView_Paint.cpp +++ b/views/kpView_Paint.cpp @@ -1,644 +1,644 @@ /* 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 0 +#define DEBUG_KP_VIEW 1 #define DEBUG_KP_VIEW_RENDERER ((DEBUG_KP_VIEW && 1) || 0) #include "views/kpView.h" #include "kpViewPrivate.h" #include #include #include #include #include "kpLogCategories.h" #include "layers/selections/kpAbstractSelection.h" #include "imagelib/kpColor.h" #include "document/kpDocument.h" #include "layers/tempImage/kpTempImage.h" #include "layers/selections/text/kpTextSelection.h" #include "views/manager/kpViewManager.h" #include "kpViewScrollableContainer.h" //--------------------------------------------------------------------- // protected QRect kpView::paintEventGetDocRect (const QRect &viewRect) const { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "kpView::paintEventGetDocRect(" << viewRect << ")"; #endif QRect docRect; // From the "we aren't sure whether to round up or round down" department: if (zoomLevelX () < 100 || zoomLevelY () < 100) { docRect = transformViewToDoc (viewRect); } else { // think of a grid - you need to fully cover the zoomed-in pixels // when docRect is zoomed back to the view later docRect = QRect (transformViewToDoc (viewRect.topLeft ()), // round down transformViewToDoc (viewRect.bottomRight ())); // round down } if (zoomLevelX () % 100 || zoomLevelY () % 100) { // at least round up the bottom-right point and deal with matrix weirdness: // - helpful because it ensures we at least cover the required area // at e.g. 67% or 573% docRect.setBottomRight (docRect.bottomRight () + QPoint (2, 2)); } #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tdocRect=" << docRect; #endif kpDocument *doc = document (); if (doc) { docRect = docRect.intersected (doc->rect ()); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tintersected with doc=" << docRect; #endif } return docRect; } //--------------------------------------------------------------------- // public static void kpView::drawTransparentBackground (QPainter *painter, const QPoint &patternOrigin, const QRect &viewRect, bool isPreview) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "kpView::drawTransparentBackground() patternOrigin=" << patternOrigin << " viewRect=" << viewRect << " isPreview=" << isPreview << endl; #endif const int cellSize = !isPreview ? 16 : 10; // TODO: % is unpredictable with negatives. int starty = viewRect.y (); if ((starty - patternOrigin.y ()) % cellSize) { starty -= ((starty - patternOrigin.y ()) % cellSize); } int startx = viewRect.x (); if ((startx - patternOrigin.x ()) % cellSize) { startx -= ((startx - patternOrigin.x ()) % cellSize); } #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tstartXY=" << QPoint (startx, starty); #endif painter->save (); // Clip to as we may draw outside it on all sides. painter->setClipRect (viewRect, Qt::IntersectClip/*honor existing clip*/); for (int y = starty; y <= viewRect.bottom (); y += cellSize) { for (int x = startx; x <= viewRect.right (); x += cellSize) { bool parity = ((x - patternOrigin.x ()) / cellSize + (y - patternOrigin.y ()) / cellSize) % 2; QColor col; if (parity) { if (!isPreview) { col = QColor (213, 213, 213); } else { col = QColor (224, 224, 224); } } else { col = Qt::white; } painter->fillRect (x, y, cellSize, cellSize, col); } } painter->restore (); } //--------------------------------------------------------------------- // protected void kpView::paintEventDrawCheckerBoard (QPainter *painter, const QRect &viewRect) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::paintEventDrawCheckerBoard(viewRect=" << viewRect << ") origin=" << origin (); #endif kpDocument *doc = document (); if (!doc) { return; } QPoint patternOrigin = origin (); if (scrollableContainer ()) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tscrollableContainer: contents[XY]=" << QPoint (scrollableContainer ()->horizontalScrollBar()->value (), scrollableContainer ()->verticalScrollBar()->value ()) << endl; #endif // Make checkerboard appear static relative to the scroll view. // This makes it more obvious that any visible bits of the // checkboard represent transparent pixels and not gray and white // squares. patternOrigin = QPoint (scrollableContainer ()->horizontalScrollBar()->value(), scrollableContainer ()->verticalScrollBar()->value()); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\t\tpatternOrigin=" << patternOrigin; #endif } // TODO: this static business doesn't work yet patternOrigin = QPoint (0, 0); drawTransparentBackground (painter, patternOrigin, viewRect); } //--------------------------------------------------------------------- // protected void kpView::paintEventDrawSelection (QImage *destPixmap, const QRect &docRect) { #if DEBUG_KP_VIEW_RENDERER && 1 || 0 qCDebug(kpLogViews) << "kpView::paintEventDrawSelection() docRect=" << docRect; #endif kpDocument *doc = document (); if (!doc) { #if DEBUG_KP_VIEW_RENDERER && 1 || 0 qCDebug(kpLogViews) << "\tno doc - abort"; #endif return; } kpAbstractSelection *sel = doc->selection (); if (!sel) { #if DEBUG_KP_VIEW_RENDERER && 1 || 0 qCDebug(kpLogViews) << "\tno sel - abort"; #endif return; } // // Draw selection pixmap (if there is one) // #if DEBUG_KP_VIEW_RENDERER && 1 || 0 qCDebug(kpLogViews) << "\tdraw sel pixmap @ " << sel->topLeft (); #endif sel->paint (destPixmap, docRect); // // Draw selection border // kpViewManager *vm = viewManager (); #if DEBUG_KP_VIEW_RENDERER && 1 || 0 qCDebug(kpLogViews) << "\tsel border visible=" << vm->selectionBorderVisible (); #endif if (vm->selectionBorderVisible ()) { sel->paintBorder (destPixmap, docRect, vm->selectionBorderFinished ()); } // // Draw text cursor // // TODO: It would be nice to display the text cursor even if it's not // within the text box (this can happen if the text box is too // small for the text it contains). // // However, too much selection repaint code assumes that it // only paints inside its kpAbstractSelection::boundingRect(). auto *textSel = dynamic_cast (sel); if (textSel && vm->textCursorEnabled () && (vm->textCursorBlinkState () || // For the current main window: // As long as _any_ view has focus, blink _all_ views not just the // one with focus. !vm->hasAViewWithFocus ())) // sync: call will break when vm is not held by 1 mainWindow { QRect rect = vm->textCursorRect (); rect = rect.intersected (textSel->textAreaRect ()); if (!rect.isEmpty ()) { kpPixmapFX::fillRect(destPixmap, rect.x () - docRect.x (), rect.y () - docRect.y (), rect.width (), rect.height (), kpColor::LightGray, kpColor::DarkGray); } } } //--------------------------------------------------------------------- // protected void kpView::paintEventDrawSelectionResizeHandles (const QRect &clipRect) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "kpView::paintEventDrawSelectionResizeHandles(" << clipRect << ")"; #endif if (!selectionLargeEnoughToHaveResizeHandles ()) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tsel not large enough to have resize handles"; #endif return; } kpViewManager *vm = viewManager (); if (!vm || !vm->selectionBorderVisible () || !vm->selectionBorderFinished ()) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tsel border not visible or not finished"; #endif return; } const QRect selViewRect = selectionViewRect (); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tselViewRect=" << selViewRect; #endif if (!selViewRect.intersects (clipRect)) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tdoesn't intersect viewRect"; #endif return; } QRegion selResizeHandlesRegion = selectionResizeHandlesViewRegion (true/*for renderer*/); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tsel resize handles view region=" << selResizeHandlesRegion << endl; #endif QPainter painter(this); painter.setPen(Qt::black); painter.setBrush(Qt::cyan); for (const auto &r : selResizeHandlesRegion.rects()) { painter.drawRect(r); } } //--------------------------------------------------------------------- // protected void kpView::paintEventDrawTempImage (QImage *destPixmap, const QRect &docRect) { kpViewManager *vm = viewManager (); if (!vm) { return; } const kpTempImage *tpi = vm->tempImage (); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "kpView::paintEventDrawTempImage() tempImage=" << tpi << " isVisible=" << (tpi ? tpi->isVisible (vm) : false) << endl; #endif if (!tpi || !tpi->isVisible (vm)) { return; } tpi->paint (destPixmap, docRect); } //--------------------------------------------------------------------- // protected void kpView::paintEventDrawGridLines (QPainter *painter, const QRect &viewRect) { int hzoomMultiple = zoomLevelX () / 100; int vzoomMultiple = zoomLevelY () / 100; painter->setPen(Qt::gray); // horizontal lines int starty = viewRect.top(); if (starty % vzoomMultiple) { starty = (starty + vzoomMultiple) / vzoomMultiple * vzoomMultiple; } for (int y = starty; y <= viewRect.bottom(); y += vzoomMultiple) { painter->drawLine(viewRect.left(), y, viewRect.right(), y); } // vertical lines int startx = viewRect.left(); if (startx % hzoomMultiple) { startx = (startx + hzoomMultiple) / hzoomMultiple * hzoomMultiple; } for (int x = startx; x <= viewRect.right(); x += hzoomMultiple) { painter->drawLine(x, viewRect.top (), x, viewRect.bottom()); } } //--------------------------------------------------------------------- // This is called "_Unclipped" because it may draw outside of // . // // There are 2 reasons for doing so: // // A. If, for instance: // // 1. = QRect (0, 0, 2, 3) [top-left of the view] // 2. zoomLevelX() == 800 // 3. zoomLevelY() == 800 // // Then, the local variable will be QRect (0, 0, 1, 1). // When the part of the document corresponding to // (a single document pixel) is drawn with QPainter::scale(), the // view rectangle QRect (0, 0, 7, 7) will be overwritten due to the // 8x zoom. This view rectangle is bigger than . // // We can't use QPainter::setClipRect() since it is buggy in Qt 4.3.1 // and clips too many pixels when used in combination with scale() // [qt-bugs@trolltech.com issue N181038]. ==> MK 10.2.2011 - fixed since Qt-4.4.4 // // B. paintEventGetDocRect() may, by design, return a larger document // rectangle than what corresponds to, if the zoom levels // are not perfectly divisible by 100. // // This over-drawing is dangerous -- see the comments in paintEvent(). // This over-drawing is only safe from Qt's perspective since Qt // automatically clips all drawing in paintEvent() (which calls us) to // QPaintEvent::region(). void kpView::paintEventDrawDoc_Unclipped (const QRect &viewRect) { #if DEBUG_KP_VIEW_RENDERER QTime timer; timer.start (); qCDebug(kpLogViews) << "\tviewRect=" << viewRect; #endif kpViewManager *vm = viewManager (); const kpDocument *doc = document (); Q_ASSERT (vm); Q_ASSERT (doc); if (viewRect.isEmpty ()) { return; } QRect docRect = paintEventGetDocRect (viewRect); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tdocRect=" << docRect; #endif QPainter painter (this); //painter.setCompositionMode(QPainter::CompositionMode_Source); QImage docPixmap; bool tempImageWillBeRendered = false; // LOTODO: I think being empty would be a bug. if (!docRect.isEmpty ()) { docPixmap = doc->getImageAt (docRect); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tdocPixmap.hasAlphaChannel()=" << docPixmap.hasAlphaChannel (); #endif tempImageWillBeRendered = (!doc->selection () && vm->tempImage () && vm->tempImage ()->isVisible (vm) && docRect.intersects (vm->tempImage ()->rect ())); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\ttempImageWillBeRendered=" << tempImageWillBeRendered << " (sel=" << doc->selection () << " tempImage=" << vm->tempImage () << " tempImage.isVisible=" << (vm->tempImage () ? vm->tempImage ()->isVisible (vm) : false) << " docRect.intersects(tempImage.rect)=" << (vm->tempImage () ? docRect.intersects (vm->tempImage ()->rect ()) : false) << ")" << endl; #endif } // // Draw checkboard for transparent images and/or views with borders // if (docPixmap.hasAlphaChannel() || (tempImageWillBeRendered && vm->tempImage ()->paintMayAddMask ())) { paintEventDrawCheckerBoard (&painter, viewRect); } if (!docRect.isEmpty ()) { // // Draw docPixmap + tempImage // if (doc->selection ()) { paintEventDrawSelection (&docPixmap, docRect); } else if (tempImageWillBeRendered) { paintEventDrawTempImage (&docPixmap, docRect); } #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\torigin=" << origin (); #endif // Blit scaled version of docPixmap + tempImage. #if DEBUG_KP_VIEW_RENDERER && 1 QTime scaleTimer; scaleTimer.start (); #endif // This is the only troublesome part of the method that draws unclipped. painter.translate (origin ().x (), origin ().y ()); painter.scale (double (zoomLevelX ()) / 100.0, double (zoomLevelY ()) / 100.0); painter.drawImage (docRect, docPixmap); //painter.resetMatrix (); // back to 1-1 scaling #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tscale time=" << scaleTimer.elapsed (); #endif } // if (!docRect.isEmpty ()) { #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tdrawDocRect done in: " << timer.restart () << "ms"; #endif } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpView::paintEvent (QPaintEvent *e) { // sync: kpViewPrivate // WARNING: document(), viewManager() and friends might be 0 in this method. // TODO: I'm not 100% convinced that we always check if their friends are 0. #if DEBUG_KP_VIEW_RENDERER && 1 QTime timer; timer.start (); #endif kpViewManager *vm = viewManager (); #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::paintEvent() vm=" << (bool) vm << " queueUpdates=" << (vm && vm->queueUpdates ()) << " fastUpdates=" << (vm && vm->fastUpdates ()) << " viewRect=" << e->rect () << " topLeft=" << QPoint (x (), y ()) << endl; #endif if (!vm) { return; } if (vm->queueUpdates ()) { // OPT: if this update was due to the document, // use document coordinates (in case of a zoom change in // which view coordinates become out of date) addToQueuedArea (e->region ()); return; } kpDocument *doc = document (); if (!doc) { return; } // It seems that e->region() is already clipped by Qt to the visible // part of the view (which could be quite small inside a scrollview). const auto& viewRegion = e->region (); QVector rects = viewRegion.rects (); #if DEBUG_KP_VIEW_RENDERER qCDebug(kpLogViews) << "\t#rects = " << rects.count (); #endif // Draw all of the requested regions of the document _before_ drawing // the grid lines, buddy rectangle and selection resize handles. // This ordering is important since paintEventDrawDoc_Unclipped() // may draw outside of the view rectangle passed to it. // // To illustrate this, suppose we changed each iteration of the loop // to call paintEventDrawDoc_Unclipped() _and_ then, // paintEventDrawGridLines(). If there are 2 or more iterations of this // loop, paintEventDrawDoc_Unclipped() in one iteration may draw over // parts of nearby grid lines (which were drawn in a previous iteration) // with document pixels. Those grid line parts are probably not going to // be redrawn, so will appear to be missing. for (const auto &r : rects) { paintEventDrawDoc_Unclipped (r); } // // Draw Grid Lines // if ( isGridShown() ) { QPainter painter(this); for (const auto &r : rects) paintEventDrawGridLines(&painter, r); } const QRect r = buddyViewScrollableContainerRectangle(); if ( !r.isEmpty() ) { QPainter painter(this); painter.setPen(QPen(Qt::lightGray, 1/*width*/, Qt::DotLine)); painter.setBackground(Qt::darkGray); painter.setBackgroundMode(Qt::OpaqueMode); painter.drawRect(r.x(), r.y(), r.width() - 1, r.height() - 1); } if (doc->selection ()) { // Draw resize handles on top of possible grid lines paintEventDrawSelectionResizeHandles (e->rect ()); } #if DEBUG_KP_VIEW_RENDERER && 1 qCDebug(kpLogViews) << "\tall done in: " << timer.restart () << "ms"; #endif } //--------------------------------------------------------------------- diff --git a/views/kpView_Selections.cpp b/views/kpView_Selections.cpp index 669c79d5..da2f8af4 100644 --- a/views/kpView_Selections.cpp +++ b/views/kpView_Selections.cpp @@ -1,381 +1,382 @@ /* 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 0 +#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 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 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 return (!mouseOnSelectionToMove (viewPoint) && !mouseOnSelectionResizeHandle (viewPoint)); } diff --git a/views/kpZoomedThumbnailView.cpp b/views/kpZoomedThumbnailView.cpp index 8a4b7494..49600026 100644 --- a/views/kpZoomedThumbnailView.cpp +++ b/views/kpZoomedThumbnailView.cpp @@ -1,144 +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_ZOOMED_THUMBNAIL_VIEW 0 +#define DEBUG_KP_ZOOMED_THUMBNAIL_VIEW 1 #include "views/kpZoomedThumbnailView.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "views/manager/kpViewManager.h" #include //-------------------------------------------------------------------------------- kpZoomedThumbnailView::kpZoomedThumbnailView (kpDocument *document, kpToolToolBar *toolToolBar, kpViewManager *viewManager, kpView *buddyView, kpViewScrollableContainer *scrollableContainer, QWidget *parent ) : kpThumbnailView (document, toolToolBar, viewManager, buddyView, scrollableContainer, parent) { // Call to virtual function - this is why the class is sealed adjustToEnvironment (); } kpZoomedThumbnailView::~kpZoomedThumbnailView () = default; // public virtual [base kpThumbnailView] QString kpZoomedThumbnailView::caption () const { return i18n ("%1% - Thumbnail", zoomLevelX ()); } // public slot virtual [base kpView] void kpZoomedThumbnailView::adjustToEnvironment () { #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW - qCDebug(kpLogViews) << "kpZoomedThumbnailView(" << name () + qCDebug(kpLogViews) << "kpZoomedThumbnailView(" << caption () << ")::adjustToEnvironment()" << " width=" << width () - << " height=" << height () - << endl; + << " height=" << height (); #endif if (!document ()) { return; } #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW qCDebug(kpLogViews) << "\tdoc: width=" << document ()->width () << " height=" << document ()->height () << endl; #endif if (document ()->width () <= 0 || document ()->height () <= 0) { qCCritical(kpLogViews) << "kpZoomedThumbnailView::adjustToEnvironment() doc:" << " width=" << document ()->width () << " height=" << document ()->height (); return; } int hzoom = qMax (1, width () * 100 / document ()->width ()); int vzoom = qMax (1, height () * 100 / document ()->height ()); // keep aspect ratio if (hzoom < vzoom) { vzoom = hzoom; } else { hzoom = vzoom; } #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW && 1 qCDebug(kpLogViews) << "\tproposed zoom=" << hzoom; #endif if (hzoom > 100 || vzoom > 100) { #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW && 1 qCDebug(kpLogViews) << "\twon't magnify - setting zoom to 100%"; #endif hzoom = 100; vzoom = 100; } if (viewManager ()) { viewManager ()->setQueueUpdates (); } { setZoomLevel (hzoom, vzoom); setOrigin (QPoint ((width () - zoomedDocWidth ()) / 2, (height () - zoomedDocHeight ()) / 2)); setMaskToCoverDocument (); if (viewManager ()) { viewManager ()->updateView (this); } } if (viewManager ()) { viewManager ()->restoreQueueUpdates (); } } diff --git a/views/kpZoomedView.cpp b/views/kpZoomedView.cpp index 09310979..53c98a43 100644 --- a/views/kpZoomedView.cpp +++ b/views/kpZoomedView.cpp @@ -1,102 +1,102 @@ /* 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_ZOOMED_VIEW 0 +#define DEBUG_KP_ZOOMED_VIEW 1 #include "views/kpZoomedView.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" kpZoomedView::kpZoomedView (kpDocument *document, kpToolToolBar *toolToolBar, kpViewManager *viewManager, kpView *buddyView, kpViewScrollableContainer *scrollableContainer, QWidget *parent) : kpView (document, toolToolBar, viewManager, buddyView, scrollableContainer, parent) { // Call to virtual function - this is why the class is sealed adjustToEnvironment (); } kpZoomedView::~kpZoomedView () = default; // public virtual [base kpView] void kpZoomedView::setZoomLevel (int hzoom, int vzoom) { #if DEBUG_KP_ZOOMED_VIEW - qCDebug(kpLogViews) << "kpZoomedView(" << name () << ")::setZoomLevel(" + qCDebug(kpLogViews) << "kpZoomedView(" << objectName () << ")::setZoomLevel(" << hzoom << "," << vzoom << ")" << endl; #endif if (viewManager ()) { viewManager ()->setQueueUpdates (); } { kpView::setZoomLevel (hzoom, vzoom); adjustToEnvironment (); } if (viewManager ()) { viewManager ()->restoreQueueUpdates (); } } // public slot virtual [base kpView] void kpZoomedView::adjustToEnvironment () { #if DEBUG_KP_ZOOMED_VIEW - qCDebug(kpLogViews) << "kpZoomedView(" << name () << ")::adjustToEnvironment()" + qCDebug(kpLogViews) << "kpZoomedView(" << objectName () << ")::adjustToEnvironment()" << " doc: width=" << document ()->width () << " height=" << document ()->height () << endl; #endif if (document ()) { // TODO: use zoomedDocWidth() & zoomedDocHeight()? resize (static_cast (transformDocToViewX (document ()->width ())), static_cast (transformDocToViewY (document ()->height ()))); } } diff --git a/views/manager/kpViewManager.cpp b/views/manager/kpViewManager.cpp index da10bd85..5bf046c9 100644 --- a/views/manager/kpViewManager.cpp +++ b/views/manager/kpViewManager.cpp @@ -1,374 +1,374 @@ /* 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_MANAGER 0 +#define DEBUG_KP_VIEW_MANAGER 1 #include "views/manager/kpViewManager.h" #include "kpViewManagerPrivate.h" #include #include #include #include "kpLogCategories.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "layers/tempImage/kpTempImage.h" #include "layers/selections/text/kpTextSelection.h" #include "tools/kpTool.h" #include "views/kpView.h" //--------------------------------------------------------------------- kpViewManager::kpViewManager (kpMainWindow *mainWindow) : d (new kpViewManagerPrivate ()) { Q_ASSERT (mainWindow); d->mainWindow = mainWindow; // d->views d->viewUnderCursor = nullptr; // d->cursor d->tempImage = nullptr; d->selectionBorderVisible = false; d->selectionBorderFinished = false; d->textCursorBlinkTimer = nullptr; d->textCursorRow = -1; d->textCursorCol = -1; d->textCursorBlinkState = true; d->queueUpdatesCounter = d->fastUpdatesCounter = 0; d->inputMethodEnabled = false; } //--------------------------------------------------------------------- kpViewManager::~kpViewManager () { unregisterAllViews (); delete d->tempImage; delete d; } //--------------------------------------------------------------------- // private kpDocument *kpViewManager::document () const { return d->mainWindow->document (); } //--------------------------------------------------------------------- // public void kpViewManager::registerView (kpView *view) { #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "kpViewManager::registerView (" << view << ")"; #endif Q_ASSERT (view); Q_ASSERT (!d->views.contains (view)); #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "\tadded view"; #endif view->setCursor (d->cursor); d->views.append (view); } //--------------------------------------------------------------------- // public void kpViewManager::unregisterView (kpView *view) { Q_ASSERT (view); Q_ASSERT (d->views.contains (view)); if (view == d->viewUnderCursor) { d->viewUnderCursor = nullptr; } view->unsetCursor (); d->views.removeAll (view); } //--------------------------------------------------------------------- // public void kpViewManager::unregisterAllViews () { d->views.clear (); } //--------------------------------------------------------------------- // public kpView *kpViewManager::viewUnderCursor (bool usingQt) const { if (!usingQt) { Q_ASSERT (!d->viewUnderCursor || d->views.contains (d->viewUnderCursor)); return d->viewUnderCursor; } for (QLinkedList ::const_iterator it = d->views.begin (); it != d->views.end (); ++it) { if ((*it)->underMouse ()) { return (*it); } } return nullptr; } //--------------------------------------------------------------------- // public void kpViewManager::setViewUnderCursor (kpView *view) { #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "kpViewManager::setViewUnderCursor (" << (view ? view->objectName () : "(none)") << ")" << " old=" << (d->viewUnderCursor ? d->viewUnderCursor->objectName () : "(none)"); #endif if (view == d->viewUnderCursor) { return; } d->viewUnderCursor = view; if (d->viewUnderCursor) { d->viewUnderCursor->setAttribute (Qt::WA_InputMethodEnabled, d->inputMethodEnabled); } if (!d->viewUnderCursor) { // Hide the brush if the mouse cursor just left the view. if (d->tempImage && d->tempImage->isBrush ()) { #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "\thiding brush pixmap since cursor left view"; #endif updateViews (d->tempImage->rect ()); } } else { if (d->mainWindow->tool ()) { #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "\tnotify tool that something changed below cursor"; #endif d->mainWindow->tool ()->somethingBelowTheCursorChanged (); } } } //--------------------------------------------------------------------- // public bool kpViewManager::hasAViewWithFocus () const { for (QLinkedList ::const_iterator it = d->views.begin (); it != d->views.end (); ++it) { if ((*it)->isActiveWindow ()) { return true; } } return false; } //--------------------------------------------------------------------- // public void kpViewManager::setCursor (const QCursor &cursor) { for (QLinkedList ::const_iterator it = d->views.begin (); it != d->views.end (); ++it) { (*it)->setCursor (cursor); } d->cursor = cursor; } //--------------------------------------------------------------------- // public void kpViewManager::unsetCursor () { for (QLinkedList ::const_iterator it = d->views.begin (); it != d->views.end (); ++it) { (*it)->unsetCursor (); } d->cursor = QCursor (); } //--------------------------------------------------------------------- // public const kpTempImage *kpViewManager::tempImage () const { return d->tempImage; } //--------------------------------------------------------------------- // public void kpViewManager::setTempImage (const kpTempImage &tempImage) { #if DEBUG_KP_VIEW_MANAGER qCDebug(kpLogViews) << "kpViewManager::setTempImage(isBrush=" << tempImage.isBrush () << ",topLeft=" << tempImage.topLeft () << ",image.rect=" << tempImage.image ().rect () << ")"; #endif QRect oldRect; if (d->tempImage) { oldRect = d->tempImage->rect (); delete d->tempImage; d->tempImage = nullptr; } d->tempImage = new kpTempImage (tempImage); setQueueUpdates (); { if (oldRect.isValid ()) { updateViews (oldRect); } updateViews (d->tempImage->rect ()); } restoreQueueUpdates (); } //--------------------------------------------------------------------- // public void kpViewManager::invalidateTempImage () { if (!d->tempImage) { return; } QRect oldRect = d->tempImage->rect (); delete d->tempImage; d->tempImage = nullptr; updateViews (oldRect); } //--------------------------------------------------------------------- // public bool kpViewManager::selectionBorderVisible () const { return d->selectionBorderVisible; } //--------------------------------------------------------------------- // public void kpViewManager::setSelectionBorderVisible (bool yes) { if (d->selectionBorderVisible == yes) { return; } d->selectionBorderVisible = yes; if (document ()->selection ()) { updateViews (document ()->selection ()->boundingRect ()); } } //--------------------------------------------------------------------- // public bool kpViewManager::selectionBorderFinished () const { return d->selectionBorderFinished; } //--------------------------------------------------------------------- // public void kpViewManager::setSelectionBorderFinished (bool yes) { if (d->selectionBorderFinished == yes) { return; } d->selectionBorderFinished = yes; if (document ()->selection ()) { updateViews (document ()->selection ()->boundingRect ()); } } //--------------------------------------------------------------------- void kpViewManager::setInputMethodEnabled (bool inputMethodEnabled) { d->inputMethodEnabled = inputMethodEnabled; if (d->viewUnderCursor) { d->viewUnderCursor->setAttribute (Qt::WA_InputMethodEnabled, inputMethodEnabled); } } //--------------------------------------------------------------------- diff --git a/views/manager/kpViewManager_TextCursor.cpp b/views/manager/kpViewManager_TextCursor.cpp index c5e319fd..ec91f9b0 100644 --- a/views/manager/kpViewManager_TextCursor.cpp +++ b/views/manager/kpViewManager_TextCursor.cpp @@ -1,236 +1,236 @@ // TODO: This is bad design as it's easy to get out of sync with the selection. // e.g. You could have textCursorEnabled() but no text selection or // vice versa. And the cursor could be outside of the selection. // // In fact, our text commands momentarily violate these "invariants": // // 1. A text box with content must have the cursor somewhere on an // existing text line, possibly 1 position after the last character // on a line. // // 2. Special case: A content-less text box (i.e. no text lines) must // have the cursor at (0,0). // // We don't assert-check them at the moment. We should when we fix // the design so that the invariants are always maintained. /* 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_MANAGER 0 +#define DEBUG_KP_VIEW_MANAGER 1 #include "kpViewManager.h" #include "kpViewManagerPrivate.h" #include #include #include //#include #include "kpLogCategories.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "layers/tempImage/kpTempImage.h" #include "layers/selections/text/kpTextSelection.h" #include "tools/kpTool.h" #include "views/kpView.h" // public bool kpViewManager::textCursorEnabled () const { return static_cast (d->textCursorBlinkTimer); } // public void kpViewManager::setTextCursorEnabled (bool yes) { #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "kpViewManager::setTextCursorEnabled(" << yes << ")"; #endif if (yes == textCursorEnabled ()) { return; } delete d->textCursorBlinkTimer; d->textCursorBlinkTimer = nullptr; setFastUpdates (); setQueueUpdates (); { if (yes) { d->textCursorBlinkTimer = new QTimer (this); d->textCursorBlinkTimer->setSingleShot (true); connect (d->textCursorBlinkTimer, &QTimer::timeout, this, &kpViewManager::slotTextCursorBlink); d->textCursorBlinkState = true; slotTextCursorBlink (); } else { d->textCursorBlinkState = false; updateTextCursor (); } } restoreQueueUpdates (); restoreFastUpdates (); } // public bool kpViewManager::textCursorBlinkState () const { return d->textCursorBlinkState; } // public void kpViewManager::setTextCursorBlinkState (bool on) { if (on == d->textCursorBlinkState) { return; } d->textCursorBlinkState = on; updateTextCursor (); } // public int kpViewManager::textCursorRow () const { return d->textCursorRow; } // public int kpViewManager::textCursorCol () const { return d->textCursorCol; } // public void kpViewManager::setTextCursorPosition (int row, int col) { if (row == d->textCursorRow && col == d->textCursorCol) { return; } setFastUpdates (); setQueueUpdates (); { // Clear the old cursor. d->textCursorBlinkState = false; updateTextCursor (); d->textCursorRow = row; d->textCursorCol = col; // Render the new cursor. d->textCursorBlinkState = true; updateTextCursor (); } restoreQueueUpdates (); restoreFastUpdates (); } // public QRect kpViewManager::textCursorRect () const { kpTextSelection *textSel = document ()->textSelection (); if (!textSel) { return {}; } QPoint topLeft = textSel->pointForTextRowCol (d->textCursorRow, d->textCursorCol); if (topLeft == KP_INVALID_POINT) { // Text cursor row/col hasn't been specified yet? if (textSel->hasContent ()) { return {}; } // Empty text box should still display a cursor so that the user // knows where typed text will go. topLeft = textSel->textAreaRect ().topLeft (); } Q_ASSERT (topLeft != KP_INVALID_POINT); return {topLeft.x (), topLeft.y (), 1, textSel->textStyle ().fontMetrics ().height ()}; } // protected void kpViewManager::updateTextCursor () { #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "kpViewManager::updateTextCursor()"; #endif const QRect r = textCursorRect (); if (!r.isValid ()) { return; } setFastUpdates (); { // If !textCursorEnabled(), this will clear. updateViews (r); } restoreFastUpdates (); } // protected slot void kpViewManager::slotTextCursorBlink () { #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "kpViewManager::slotTextCursorBlink() cursorBlinkState=" << d->textCursorBlinkState; #endif if (d->textCursorBlinkTimer) { // (single shot) d->textCursorBlinkTimer->start (QApplication::cursorFlashTime () / 2); } updateTextCursor (); // TODO: Shouldn't this be done _before_ updating the text cursor // because textCursorBlinkState() is supposed to reflect what // updateTextCursor() just rendered, until the next timer tick? d->textCursorBlinkState = !d->textCursorBlinkState; } diff --git a/views/manager/kpViewManager_ViewUpdates.cpp b/views/manager/kpViewManager_ViewUpdates.cpp index 824528fa..33ca3530 100644 --- a/views/manager/kpViewManager_ViewUpdates.cpp +++ b/views/manager/kpViewManager_ViewUpdates.cpp @@ -1,260 +1,259 @@ /* 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_MANAGER 0 +#define DEBUG_KP_VIEW_MANAGER 1 #include "views/manager/kpViewManager.h" #include "kpViewManagerPrivate.h" #include #include #include #include "kpLogCategories.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "layers/tempImage/kpTempImage.h" #include "layers/selections/text/kpTextSelection.h" #include "tools/kpTool.h" #include "views/kpView.h" // public slot bool kpViewManager::queueUpdates () const { return (d->queueUpdatesCounter > 0); } // public slot void kpViewManager::setQueueUpdates () { d->queueUpdatesCounter++; #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "kpViewManager::setQueueUpdates() counter=" << d->queueUpdatesCounter << endl; #endif } // public slot void kpViewManager::restoreQueueUpdates () { d->queueUpdatesCounter--; #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "kpViewManager::restoreQueueUpdates() counter=" << d->queueUpdatesCounter; #endif Q_ASSERT (d->queueUpdatesCounter >= 0); if (d->queueUpdatesCounter == 0) { for (QLinkedList ::const_iterator it = d->views.begin (); it != d->views.end (); ++it) { (*it)->updateQueuedArea (); } } } // public slot bool kpViewManager::fastUpdates () const { return (d->fastUpdatesCounter > 0); } // public slot void kpViewManager::setFastUpdates () { d->fastUpdatesCounter++; #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "kpViewManager::setFastUpdates() counter=" << d->fastUpdatesCounter << endl; #endif } // public slot void kpViewManager::restoreFastUpdates () { d->fastUpdatesCounter--; #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "kpViewManager::restoreFastUpdates() counter=" << d->fastUpdatesCounter << endl; #endif Q_ASSERT (d->fastUpdatesCounter >= 0); } // public slot void kpViewManager::updateView (kpView *v) { updateView (v, QRect (0, 0, v->width (), v->height ())); } // public slot void kpViewManager::updateView (kpView *v, const QRect &viewRect) { if (!queueUpdates ()) { if (fastUpdates ()) { v->repaint (viewRect); } else { v->update (viewRect); } } else { v->addToQueuedArea (viewRect); } } // public slot void kpViewManager::updateView (kpView *v, int x, int y, int w, int h) { updateView (v, QRect (x, y, w, h)); } // public slot void kpViewManager::updateView (kpView *v, const QRegion &viewRegion) { if (!queueUpdates ()) { if (fastUpdates ()) { v->repaint (viewRegion); } else { v->update (viewRegion.boundingRect ()); } } else { v->addToQueuedArea (viewRegion); } } // public slot void kpViewManager::updateViewRectangleEdges (kpView *v, const QRect &viewRect) { if (viewRect.height () <= 0 || viewRect.width () <= 0) { return; } // Top line updateView (v, QRect (viewRect.x (), viewRect.y (), viewRect.width (), 1)); if (viewRect.height () >= 2) { // Bottom line updateView (v, QRect (viewRect.x (), viewRect.bottom (), viewRect.width (), 1)); if (viewRect.height () > 2) { // Left line updateView (v, QRect (viewRect.x (), viewRect.y () + 1, 1, viewRect.height () - 2)); if (viewRect.width () >= 2) { // Right line updateView (v, QRect (viewRect.right (), viewRect.y () + 1, 1, viewRect.height () - 2)); } } } } // public slot void kpViewManager::updateViews (const QRect &docRect) { #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "kpViewManager::updateViews (" << docRect << ")"; #endif for (QLinkedList ::const_iterator it = d->views.begin (); it != d->views.end (); ++it) { kpView *view = *it; #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "\tupdating view " << view->name (); #endif if (view->zoomLevelX () % 100 == 0 && view->zoomLevelY () % 100 == 0) { #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "\t\tviewRect=" << view->transformDocToView (docRect); #endif updateView (view, view->transformDocToView (docRect)); } else { QRect viewRect = view->transformDocToView (docRect); int diff = qRound (double (qMax (view->zoomLevelX (), view->zoomLevelY ())) / 100.0) + 1; QRect newRect = QRect (viewRect.x () - diff, viewRect.y () - diff, viewRect.width () + 2 * diff, viewRect.height () + 2 * diff) .intersected (QRect (0, 0, view->width (), view->height ())); #if DEBUG_KP_VIEW_MANAGER && 0 qCDebug(kpLogViews) << "\t\tviewRect (+compensate)=" << newRect; #endif updateView (view, newRect); } } } // public slot void kpViewManager::adjustViewsToEnvironment () { #if DEBUG_KP_VIEW_MANAGER && 1 qCDebug(kpLogViews) << "kpViewManager::adjustViewsToEnvironment()" << " numViews=" << d->views.count () << endl; #endif for (QLinkedList ::const_iterator it = d->views.begin (); it != d->views.end (); ++it) { kpView *view = *it; #if DEBUG_KP_VIEW_MANAGER && 1 - qCDebug(kpLogViews) << "\tview: " << view->name () - << endl; + qCDebug(kpLogViews) << "\tview: " << view->objectName (); #endif view->adjustToEnvironment (); } } diff --git a/widgets/colorSimilarity/kpColorSimilarityCubeRenderer.cpp b/widgets/colorSimilarity/kpColorSimilarityCubeRenderer.cpp index 3150f11d..47a41a1c 100644 --- a/widgets/colorSimilarity/kpColorSimilarityCubeRenderer.cpp +++ b/widgets/colorSimilarity/kpColorSimilarityCubeRenderer.cpp @@ -1,236 +1,236 @@ /* 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 0 +#define DEBUG_KP_COLOR_SIMILARITY_CUBE 1 #include "kpColorSimilarityCubeRenderer.h" #include #include #include #include "kpLogCategories.h" #include "widgets/colorSimilarity/kpColorSimilarityHolder.h" #include "kpDefs.h" //--------------------------------------------------------------------- static QColor Color (int redOrGreenOrBlue, int baseBrightness, double colorSimilarity, int similarityDirection, int highlight) { int brightness = int (baseBrightness + similarityDirection * 0.5 * colorSimilarity * kpColorSimilarityHolder::ColorCubeDiagonalDistance); if (brightness < 0) { brightness = 0; } else if (brightness > 255) { brightness = 255; } switch (redOrGreenOrBlue) { default: case 0: return {brightness, highlight, highlight}; case 1: return {highlight, brightness, highlight}; case 2: return {highlight, highlight, brightness}; } } //--------------------------------------------------------------------- static QPointF PointBetween(const QPointF &p, const QPointF &q) { return {(p.x() + q.x()) / 2.0, (p.y() + q.y()) / 2.0}; } //--------------------------------------------------------------------- static void DrawQuadrant(QPaintDevice *target, const QColor &col, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &pointNotOnOutline) { QPolygonF points (4); points [0] = p1; points [1] = p2; points [2] = p3; points [3] = pointNotOnOutline; QPainter p(target); p.setRenderHints(QPainter::Antialiasing, true); // Polygon fill. p.setPen(col); p.setBrush(col); p.drawPolygon(points); // do not draw a black border. It looks ugly } //--------------------------------------------------------------------- static void DrawFace (QPaintDevice *target, double colorSimilarity, int redOrGreenOrBlue, const QPointF &tl, const QPointF &tr, const QPointF &bl, const QPointF &br, int highlight) { #if DEBUG_KP_COLOR_SIMILARITY_CUBE qCDebug(kpLogWidgets) << "kpColorSimilarityCubeRenderer.cpp:DrawFace(RorGorB=" << redOrGreenOrBlue << ",tl=" << tl << ",tr=" << tr << ",bl=" << bl << ",br=" << br << ")" << endl; #endif // tl --- tm --- tr // | | | // | | | // ml --- mm --- mr // | | | // | | | // bl --- bm --- br const QPointF tm (::PointBetween (tl, tr)); const QPointF bm (::PointBetween (bl, br)); const QPointF ml (::PointBetween (tl, bl)); const QPointF mr (::PointBetween (tr, br)); const QPointF mm (::PointBetween (ml, mr)); const int baseBrightness = qMax (127, 255 - int (kpColorSimilarityHolder::MaxColorSimilarity * kpColorSimilarityHolder::ColorCubeDiagonalDistance / 2)); QColor colors [2] = { ::Color (redOrGreenOrBlue, baseBrightness, colorSimilarity, -1, highlight), ::Color (redOrGreenOrBlue, baseBrightness, colorSimilarity, +1, highlight) }; #if DEBUG_KP_COLOR_SIMILARITY_CUBE qCDebug(kpLogWidgets) << "\tmaxColorSimilarity=" << kpColorSimilarityHolder::MaxColorSimilarity << " colorCubeDiagDist=" << kpColorSimilarityHolder::ColorCubeDiagonalDistance << "\n" << "\tbaseBrightness=" << baseBrightness << " color[0]=" << ((colors [0].rgba() & RGB_MASK) >> ((2 - redOrGreenOrBlue) * 8)) << " color[1]=" << ((colors [1].rgba() & RGB_MASK) >> ((2 - redOrGreenOrBlue) * 8)) << endl; #endif ::DrawQuadrant(target, colors [0], tm, tl, ml, mm); ::DrawQuadrant(target, colors [1], tm, tr, mr, mm); ::DrawQuadrant(target, colors [1], ml, bl, bm, mm); ::DrawQuadrant(target, colors [0], bm, br, mr, mm); } //--------------------------------------------------------------------- // public static void kpColorSimilarityCubeRenderer::Paint(QPaintDevice *target, int x, int y, int cubeRectSize, double colorSimilarity, int highlight) { Q_ASSERT (highlight >= 0 && highlight <= 255); // // P------- Q --- --- // / / | | | // /A / | side | // R-------S T --- cubeRectSize // | | / / | // S | | / side | // U-------V --- --- // |-------| // side // |-----------| // cubeRectSize // // const double angle = qDegreesToRadians (45.0); // S + S sin A = cubeRectSize // (1 + sin A) x S = cubeRectSize const double side = double(cubeRectSize) / (1.0 + std::sin(angle)); const QPointF pointP(x + (side * std::cos (angle)), y); const QPointF pointQ(x + cubeRectSize - 1, y); const QPointF pointR(x, y + (side * std::sin (angle))); const QPointF pointS(x + (side), y + (side * std::sin (angle))); const QPointF pointT(x + cubeRectSize - 1, y + (side)); const QPointF pointU(x, y + cubeRectSize - 1); const QPointF pointV(x + (side), y + cubeRectSize - 1); // Top Face ::DrawFace(target, colorSimilarity, 0/*red*/, pointP, pointQ, pointR, pointS, highlight); // Front Face ::DrawFace(target, colorSimilarity, 1/*green*/, pointR, pointS, pointU, pointV, highlight); // Right Face ::DrawFace(target, colorSimilarity, 2/*blue*/, pointS, pointQ, pointV, pointT, highlight); } //--------------------------------------------------------------------- diff --git a/widgets/colorSimilarity/kpColorSimilarityFrame.cpp b/widgets/colorSimilarity/kpColorSimilarityFrame.cpp index dd98e449..196732c0 100644 --- a/widgets/colorSimilarity/kpColorSimilarityFrame.cpp +++ b/widgets/colorSimilarity/kpColorSimilarityFrame.cpp @@ -1,78 +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_COLOR_SIMILARITY_CUBE 0 +#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/colorSimilarity/kpColorSimilarityHolder.cpp b/widgets/colorSimilarity/kpColorSimilarityHolder.cpp index 373fcb38..cbecd9d8 100644 --- a/widgets/colorSimilarity/kpColorSimilarityHolder.cpp +++ b/widgets/colorSimilarity/kpColorSimilarityHolder.cpp @@ -1,190 +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_COLOR_SIMILARITY_CUBE 0 +#define DEBUG_KP_COLOR_SIMILARITY_CUBE 1 #include "kpColorSimilarityHolder.h" #include "kpColorSimilarityCubeRenderer.h" #include "imagelib/kpColor.h" #include "kpDefs.h" #include #include #include #include #include "kpLogCategories.h" #include // public static const double kpColorSimilarityHolder::ColorCubeDiagonalDistance = std::sqrt (255.0 * 255 * 3); // public static const double kpColorSimilarityHolder::MaxColorSimilarity = 0.30; kpColorSimilarityHolder::kpColorSimilarityHolder () : m_colorSimilarity (0) { } kpColorSimilarityHolder::~kpColorSimilarityHolder () = default; // Don't cause the translators grief by appending strings etc. // - duplicate text with 2 cases // public static QString kpColorSimilarityHolder::WhatsThisWithClickInstructions () { return i18n ("" "

Color Similarity is how similar the colors of different pixels" " must be, for operations to consider them to be the same.

" "

If you set it to something other than Exact Match," " you can work more effectively with dithered" " images and photos, in a comparable manner to the \"Magic Wand\"" " feature of other paint programs.

" "

This feature applies to:

" "
    " "
  • Selections: In Transparent mode, any color in the" " selection that is similar to the background color will" " be made transparent.
  • " "
  • Flood Fill: For regions with similar - but not" " identical - colored pixels, a higher setting is likely to" " fill more pixels.
  • " "
  • Color Eraser: Any pixel whose color is similar" " to the foreground color will be replaced with the background" " color.
  • " "
  • Autocrop and Remove Internal Border: For" " borders with similar - but not identical - colored pixels," " a higher setting is more likely to crop the whole border.
  • " "
" "

Higher settings mean that operations consider an increased range" " of colors to be sufficiently similar so as to be the same. Therefore," " you should increase the setting if the above operations are not" " affecting pixels whose colors you consider to be similar enough.

" "

However, if they are having too much of an effect and are changing" " pixels whose colors you do not consider to be similar" " (e.g. if Flood Fill is changing too many pixels), you" " should decrease this setting.

" // sync: Compared to the other string below, we've added this line. "

To configure it, click on the cube.

" "
"); } // public static QString kpColorSimilarityHolder::WhatsThis () { return i18n ("" "

Color Similarity is how similar the colors of different pixels" " must be, for operations to consider them to be the same.

" "

If you set it to something other than Exact Match," " you can work more effectively with dithered" " images and photos, in a comparable manner to the \"Magic Wand\"" " feature of other paint programs.

" "

This feature applies to:

" "
    " "
  • Selections: In Transparent mode, any color in the" " selection that is similar to the background color will" " be made transparent.
  • " "
  • Flood Fill: For regions with similar - but not" " identical - colored pixels, a higher setting is likely to" " fill more pixels.
  • " "
  • Color Eraser: Any pixel whose color is similar" " to the foreground color will be replaced with the background" " color.
  • " "
  • Autocrop and Remove Internal Border: For" " borders with similar - but not identical - colored pixels," " a higher setting is more likely to crop the whole border.
  • " "
" "

Higher settings mean that operations consider an increased range" " of colors to be sufficiently similar so as to be the same. Therefore," " you should increase the setting if the above operations are not" " affecting pixels whose colors you consider to be similar enough.

" "

However, if they are having too much of an effect and are changing" " pixels whose colors you do not consider to be similar" " (e.g. if Flood Fill is changing too many pixels), you" " should decrease this setting.

" "
"); } // public double kpColorSimilarityHolder::colorSimilarity () const { return m_colorSimilarity; } // public virtual void kpColorSimilarityHolder::setColorSimilarity (double similarity) { #if DEBUG_KP_COLOR_SIMILARITY_CUBE qCDebug(kpLogWidgets) << "kpColorSimilarityHolder::setColorSimilarity(" << similarity << ")"; #endif if (m_colorSimilarity == similarity) { return; } if (similarity < 0) { similarity = 0; } else if (similarity > MaxColorSimilarity) { similarity = MaxColorSimilarity; } m_colorSimilarity = similarity; } diff --git a/widgets/colorSimilarity/kpColorSimilarityToolBarItem.cpp b/widgets/colorSimilarity/kpColorSimilarityToolBarItem.cpp index 7bcca39b..c7028dd4 100644 --- a/widgets/colorSimilarity/kpColorSimilarityToolBarItem.cpp +++ b/widgets/colorSimilarity/kpColorSimilarityToolBarItem.cpp @@ -1,286 +1,288 @@ /* 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_TOOL_BAR_ITEM 0 +#define DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM 1 #include "kpColorSimilarityToolBarItem.h" #include #include +#include #include #include #include #include "imagelib/kpColor.h" #include "dialogs/kpColorSimilarityDialog.h" #include "kpColorSimilarityCubeRenderer.h" #include "kpDefs.h" +#include "kpLogCategories.h" //--------------------------------------------------------------------- kpColorSimilarityToolBarItem::kpColorSimilarityToolBarItem (QWidget *parent) : QToolButton (parent), kpColorSimilarityHolder (), m_oldColorSimilarity (0), m_processedColorSimilarity (kpColor::Exact), m_flashTimer (new QTimer (this)), m_flashHighlight (0), m_suppressingFlashCounter (0) { setAutoRaise (true); setFixedSize (52, 52); setWhatsThis (WhatsThisWithClickInstructions ()); connect (this, &kpColorSimilarityToolBarItem::clicked, this, &kpColorSimilarityToolBarItem::openDialog); KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupGeneral); setColorSimilarityInternal (cfg.readEntry (kpSettingColorSimilarity, 0.0), false/*don't write config*/); m_flashTimer->setInterval (100/*ms*/); connect (m_flashTimer, &QTimer::timeout, this, &kpColorSimilarityToolBarItem::slotFlashTimerTimeout); } //--------------------------------------------------------------------- // public int kpColorSimilarityToolBarItem::processedColorSimilarity () const { return m_processedColorSimilarity; } //--------------------------------------------------------------------- // private void kpColorSimilarityToolBarItem::setColorSimilarityInternal (double similarity, bool writeConfig) { #if DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM qCDebug(kpLogWidgets) << "kpColorSimilarityToolBarItem::setColorSimilarityInternal(" << "similarity=" << similarity << ",writeConfig=" << writeConfig << ")"; #endif m_oldColorSimilarity = colorSimilarity (); kpColorSimilarityHolder::setColorSimilarity (similarity); m_processedColorSimilarity = kpColor::processSimilarity (colorSimilarity ()); updateIcon (); updateToolTip (); if (writeConfig) { KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupGeneral); cfg.writeEntry (kpSettingColorSimilarity, colorSimilarity ()); cfg.sync (); } emit colorSimilarityChanged (colorSimilarity (), m_processedColorSimilarity); } //--------------------------------------------------------------------- // public virtual [base kopColorSimilarityHolder] void kpColorSimilarityToolBarItem::setColorSimilarity (double similarity) { // (this calls the base setColorSimilarity() as required by base) setColorSimilarityInternal (similarity, true/*write config*/); } //--------------------------------------------------------------------- // public double kpColorSimilarityToolBarItem::oldColorSimilarity () const { return m_oldColorSimilarity; } //--------------------------------------------------------------------- // public void kpColorSimilarityToolBarItem::openDialog () { kpColorSimilarityDialog dialog (this); dialog.setColorSimilarity (colorSimilarity ()); if (dialog.exec ()) { setColorSimilarity (dialog.colorSimilarity ()); } } //--------------------------------------------------------------------- // private slot: void kpColorSimilarityToolBarItem::slotFlashTimerTimeout () { #if DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM qCDebug(kpLogWidgets) << "kpColorSimilarityToolBarItem::slotFlashTimerTimeout()" << " highlight=" << m_flashHighlight << endl; #endif int newHigh = m_flashHighlight - 20; if (newHigh < 0) { newHigh = 0; } m_flashHighlight = newHigh; updateIcon (); if (newHigh == 0) { m_flashTimer->stop (); } } //--------------------------------------------------------------------- // public void kpColorSimilarityToolBarItem::flash () { #if DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM qCDebug(kpLogWidgets) << "kpColorSimilarityToolBarItem::flash()"; #endif if (isSuppressingFlash ()) { return; } if (m_flashHighlight == 255) { #if DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM qCDebug(kpLogWidgets) << "\tNOP"; #endif } else { m_flashHighlight = 255; updateIcon (); } m_flashTimer->start (); } //--------------------------------------------------------------------- // public bool kpColorSimilarityToolBarItem::isSuppressingFlash () const { return (m_suppressingFlashCounter > 0); } //--------------------------------------------------------------------- // public void kpColorSimilarityToolBarItem::suppressFlash () { m_suppressingFlashCounter++; } //--------------------------------------------------------------------- // public void kpColorSimilarityToolBarItem::unsupressFlash () { m_suppressingFlashCounter--; Q_ASSERT (m_suppressingFlashCounter >= 0); } //--------------------------------------------------------------------- // private void kpColorSimilarityToolBarItem::updateToolTip () { #if DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM qCDebug(kpLogWidgets) << "kpColorSimilarityToolBarItem::updateToolTip()"; #endif if (colorSimilarity () > 0) { setToolTip ( i18n ("

Color Similarity: %1%

" "

Click to configure.

", qRound (colorSimilarity () * 100))); } else { setToolTip ( i18n ("

Color Similarity: Exact Match

" "

Click to configure.

")); } } //--------------------------------------------------------------------- // private // LOOPT: This gets called twice on KolourPaint startup by: // // 1. setColorSimilarityInternal() called by the ctor // 2. resizeEvent() when it's first shown() // // We could get rid of the first and save a few milliseconds. void kpColorSimilarityToolBarItem::updateIcon () { const int side = width () * 6 / 8; #if DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM qCDebug(kpLogWidgets) << "kpColorSimilarityToolBarItem::updateIcon() width=" << width () << " side=" << side; #endif QPixmap icon(side, side); icon.fill(Qt::transparent); kpColorSimilarityCubeRenderer::Paint (&icon, 0/*x*/, 0/*y*/, side, colorSimilarity (), m_flashHighlight); setIconSize(QSize(side, side)); setIcon(icon); } //--------------------------------------------------------------------- // private virtual [base QWidget] void kpColorSimilarityToolBarItem::resizeEvent (QResizeEvent *e) { #if DEBUG_KP_COLOR_SIMILARITY_TOOL_BAR_ITEM qCDebug(kpLogWidgets) << "kpColorSimilarityToolBarItem::resizeEvent() size=" << size () << " oldSize=" << e->oldSize (); #endif QToolButton::resizeEvent (e); updateIcon (); } //--------------------------------------------------------------------- diff --git a/widgets/imagelib/effects/kpEffectBalanceWidget.cpp b/widgets/imagelib/effects/kpEffectBalanceWidget.cpp index 489b9231..7f5afd3f 100644 --- a/widgets/imagelib/effects/kpEffectBalanceWidget.cpp +++ b/widgets/imagelib/effects/kpEffectBalanceWidget.cpp @@ -1,330 +1,330 @@ /* 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 0 +#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/kpEffectBlurSharpenWidget.cpp b/widgets/imagelib/effects/kpEffectBlurSharpenWidget.cpp index d7069d97..098c1ae0 100644 --- a/widgets/imagelib/effects/kpEffectBlurSharpenWidget.cpp +++ b/widgets/imagelib/effects/kpEffectBlurSharpenWidget.cpp @@ -1,184 +1,184 @@ /* 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 0 +#define DEBUG_KP_EFFECT_BLUR_SHARPEN 1 #include "kpEffectBlurSharpenWidget.h" #include "commands/imagelib/effects/kpEffectBlurSharpenCommand.h" #include "kpLogCategories.h" #include #include "kpNumInput.h" #include #include #include kpEffectBlurSharpenWidget::kpEffectBlurSharpenWidget (bool actOnSelection, QWidget *parent) : kpEffectWidgetBase (actOnSelection, parent) { auto *lay = new QGridLayout (this); lay->setContentsMargins(0, 0, 0, 0); auto *amountLabel = new QLabel (i18n ("&Amount:"), this); m_amountInput = new kpIntNumInput (this); m_amountInput->setRange (-kpEffectBlurSharpen::MaxStrength/*- for blur*/, +kpEffectBlurSharpen::MaxStrength/*+ for sharpen*/); m_typeLabel = new QLabel (this); // Make sure doesn't expand when the effect type changes, // as otherwise, that would cause the preview pixmap label in the // "More Effects" dialog (which our widget is inside) to contract, // which would look a bit weird. // // We do this by setting the label to every possible string it could // contain and fixing its height to the maximum seen size hint height. auto h = m_typeLabel->sizeHint ().height (); #if DEBUG_KP_EFFECT_BLUR_SHARPEN qCDebug(kpLogWidgets) << "initial size hint height=" << h; #endif m_typeLabel->setText ( kpEffectBlurSharpenCommand::nameForType (kpEffectBlurSharpen::Blur)); h = qMax (h, m_typeLabel->sizeHint ().height ()); m_typeLabel->setText ( kpEffectBlurSharpenCommand::nameForType (kpEffectBlurSharpen::Sharpen)); h = qMax (h, m_typeLabel->sizeHint ().height ()); // Set this text last as the label's text needs to reflect the default // effect of "None". m_typeLabel->setText ( kpEffectBlurSharpenCommand::nameForType (kpEffectBlurSharpen::None)); h = qMax (h, m_typeLabel->sizeHint ().height ()); #if DEBUG_KP_EFFECT_BLUR_SHARPEN qCDebug(kpLogWidgets) << "maximum size hint height" << h; #endif m_typeLabel->setFixedHeight (h); m_typeLabel->setAlignment (Qt::AlignCenter); amountLabel->setBuddy (m_amountInput); lay->addWidget (amountLabel, 0, 0); lay->addWidget (m_amountInput, 0, 1); lay->addWidget (m_typeLabel, 1, 0, 1, 2, Qt::AlignCenter); lay->setColumnStretch (1, 1); connect (m_amountInput, &kpIntNumInput::valueChanged, this, &kpEffectBlurSharpenWidget::settingsChangedDelayed); connect (m_amountInput, &kpIntNumInput::valueChanged, this, &kpEffectBlurSharpenWidget::slotUpdateTypeLabel); } kpEffectBlurSharpenWidget::~kpEffectBlurSharpenWidget () = default; // public virtual [base kpEffectWidgetBase] QString kpEffectBlurSharpenWidget::caption () const { return {}; } // public virtual [base kpEffectWidgetBase] bool kpEffectBlurSharpenWidget::isNoOp () const { return (type () == kpEffectBlurSharpen::None); } // public virtual [base kpEffectWidgetBase] kpImage kpEffectBlurSharpenWidget::applyEffect (const kpImage &image) { return kpEffectBlurSharpen::applyEffect (image, type (), strength ()); } // public virtual [base kpEffectWidgetBase] kpEffectCommandBase *kpEffectBlurSharpenWidget::createCommand ( kpCommandEnvironment *cmdEnviron) const { return new kpEffectBlurSharpenCommand (type (), strength (), m_actOnSelection, cmdEnviron); } // protected slot void kpEffectBlurSharpenWidget::slotUpdateTypeLabel () { QString text = kpEffectBlurSharpenCommand::nameForType (type ()); #if DEBUG_KP_EFFECT_BLUR_SHARPEN qCDebug(kpLogWidgets) << "kpEffectBlurSharpenWidget::slotUpdateTypeLabel() text=" << text; #endif const int h = m_typeLabel->height (); m_typeLabel->setText (text); if (m_typeLabel->height () != h) { qCCritical(kpLogWidgets) << "Label changed height despite the hack in ctor:" << "was=" << h << "now=" << m_typeLabel->height (); } } // protected kpEffectBlurSharpen::Type kpEffectBlurSharpenWidget::type () const { if (m_amountInput->value () == 0) { return kpEffectBlurSharpen::None; } if (m_amountInput->value () < 0) { return kpEffectBlurSharpen::Blur; } return kpEffectBlurSharpen::Sharpen; } // protected int kpEffectBlurSharpenWidget::strength () const { return qAbs (m_amountInput->value ()); } diff --git a/widgets/imagelib/effects/kpEffectEmbossWidget.cpp b/widgets/imagelib/effects/kpEffectEmbossWidget.cpp index 02c07cc6..28158c40 100644 --- a/widgets/imagelib/effects/kpEffectEmbossWidget.cpp +++ b/widgets/imagelib/effects/kpEffectEmbossWidget.cpp @@ -1,106 +1,106 @@ /* 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 0 +#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 cfe8c52e..739b3461 100644 --- a/widgets/imagelib/effects/kpEffectFlattenWidget.cpp +++ b/widgets/imagelib/effects/kpEffectFlattenWidget.cpp @@ -1,182 +1,182 @@ /* 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 0 +#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/kpEffectInvertWidget.cpp b/widgets/imagelib/effects/kpEffectInvertWidget.cpp index f14cf3bf..8a74327a 100644 --- a/widgets/imagelib/effects/kpEffectInvertWidget.cpp +++ b/widgets/imagelib/effects/kpEffectInvertWidget.cpp @@ -1,212 +1,212 @@ /* 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 0 +#define DEBUG_KP_EFFECT_INVERT 1 #include "kpEffectInvertWidget.h" #include "imagelib/effects/kpEffectInvert.h" #include "commands/imagelib/effects/kpEffectInvertCommand.h" #include "pixmapfx/kpPixmapFX.h" #include "kpLogCategories.h" #include #include #include #include #include kpEffectInvertWidget::kpEffectInvertWidget (bool actOnSelection, QWidget *parent) : kpEffectWidgetBase (actOnSelection, parent) { auto *topLevelLay = new QVBoxLayout (this); topLevelLay->setContentsMargins(0, 0, 0, 0); auto *centerWidget = new QWidget (this); topLevelLay->addWidget (centerWidget, 0/*stretch*/, Qt::AlignCenter); auto *centerWidgetLay = new QVBoxLayout (centerWidget ); centerWidgetLay->setContentsMargins(0, 0, 0, 0); m_redCheckBox = new QCheckBox (i18n ("&Red"), centerWidget); m_greenCheckBox = new QCheckBox (i18n ("&Green"), centerWidget); m_blueCheckBox = new QCheckBox (i18n ("&Blue"), centerWidget); auto *spaceWidget = new QWidget (centerWidget); spaceWidget->setFixedSize (1, fontMetrics ().height () / 4); m_allCheckBox = new QCheckBox (i18n ("&All"), centerWidget); m_redCheckBox->setChecked (false); m_greenCheckBox->setChecked (false); m_blueCheckBox->setChecked (false); m_allCheckBox->setChecked (false); centerWidgetLay->addWidget (m_redCheckBox); centerWidgetLay->addWidget (m_greenCheckBox); centerWidgetLay->addWidget (m_blueCheckBox); centerWidgetLay->addWidget (spaceWidget); centerWidgetLay->addWidget (m_allCheckBox); m_inSignalHandler = false; connect (m_redCheckBox, &QCheckBox::toggled, this, &kpEffectInvertWidget::slotRGBCheckBoxToggled); connect (m_greenCheckBox, &QCheckBox::toggled, this, &kpEffectInvertWidget::slotRGBCheckBoxToggled); connect (m_blueCheckBox, &QCheckBox::toggled, this, &kpEffectInvertWidget::slotRGBCheckBoxToggled); connect (m_allCheckBox, &QCheckBox::toggled, this, &kpEffectInvertWidget::slotAllCheckBoxToggled); } kpEffectInvertWidget::~kpEffectInvertWidget () = default; // public int kpEffectInvertWidget::channels () const { #if DEBUG_KP_EFFECT_INVERT qCDebug(kpLogWidgets) << "kpEffectInvertWidget::channels()" << " isChecked: r=" << m_redCheckBox->isChecked () << " g=" << m_greenCheckBox->isChecked () << " b=" << m_blueCheckBox->isChecked () << endl; #endif int channels = 0; if (m_redCheckBox->isChecked ()) { channels |= kpEffectInvert::Red; } if (m_greenCheckBox->isChecked ()) { channels |= kpEffectInvert::Green; } if (m_blueCheckBox->isChecked ()) { channels |= kpEffectInvert::Blue; } #if DEBUG_KP_EFFECT_INVERT qCDebug(kpLogWidgets) << "\treturning channels=" << (int *) channels; #endif return channels; } // // kpEffectInvertWidget implements kpEffectWidgetBase interface // // public virtual [base kpEffectWidgetBase] QString kpEffectInvertWidget::caption () const { return i18n ("Channels"); } // public virtual [base kpEffectWidgetBase] bool kpEffectInvertWidget::isNoOp () const { return (channels () == kpEffectInvert::None); } // public virtual [base kpEffectWidgetBase] kpImage kpEffectInvertWidget::applyEffect (const kpImage &image) { return kpEffectInvert::applyEffect (image, channels ()); } // public virtual [base kpEffectWidgetBase] kpEffectCommandBase *kpEffectInvertWidget::createCommand ( kpCommandEnvironment *cmdEnviron) const { return new kpEffectInvertCommand (channels (), m_actOnSelection, cmdEnviron); } // protected slots void kpEffectInvertWidget::slotRGBCheckBoxToggled () { if (m_inSignalHandler) { return; } m_inSignalHandler = true; //blockSignals (true); m_allCheckBox->setChecked (m_redCheckBox->isChecked () && m_blueCheckBox->isChecked () && m_greenCheckBox->isChecked ()); //blockSignals (false); emit settingsChanged (); m_inSignalHandler = false; } // protected slot void kpEffectInvertWidget::slotAllCheckBoxToggled () { if (m_inSignalHandler) { return; } m_inSignalHandler = true; //blockSignals (true); m_redCheckBox->setChecked (m_allCheckBox->isChecked ()); m_greenCheckBox->setChecked (m_allCheckBox->isChecked ()); m_blueCheckBox->setChecked (m_allCheckBox->isChecked ()); //blockSignals (false); emit settingsChanged (); m_inSignalHandler = false; } diff --git a/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp b/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp index 362b9169..e764e54a 100644 --- a/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp +++ b/widgets/imagelib/effects/kpEffectReduceColorsWidget.cpp @@ -1,178 +1,178 @@ /* 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 0 +#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); } //--------------------------------------------------------------------- diff --git a/widgets/kpColorCells.cpp b/widgets/kpColorCells.cpp index 36bf1ccb..fe2c398d 100644 --- a/widgets/kpColorCells.cpp +++ b/widgets/kpColorCells.cpp @@ -1,604 +1,604 @@ /* 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_CELLS 0 +#define DEBUG_KP_COLOR_CELLS 1 #include "widgets/kpColorCells.h" #include "imagelib/kpColor.h" #include "lgpl/generic/kpColorCollection.h" #include "widgets/kpDefaultColorCollection.h" #include "kpLogCategories.h" #include #include #include #include #include //--------------------------------------------------------------------- // // Table Geometry // // The number of columns that the table normally has. const int TableDefaultNumColumns = 11; const int TableDefaultWidth = ::TableDefaultNumColumns * 26; const int TableDefaultHeight = 52; static int TableNumColumns (const kpColorCollection &colorCol) { if (colorCol.count () == 0) { return 0; } return ::TableDefaultNumColumns; } static int TableNumRows (const kpColorCollection &colorCol) { const int cols = ::TableNumColumns (colorCol); if (cols == 0) { return 0; } return (colorCol.count () + (cols - 1)) / cols; } static int TableCellWidth (const kpColorCollection &colorCol) { Q_UNUSED (colorCol); return ::TableDefaultWidth / ::TableDefaultNumColumns; } static int TableCellHeight (const kpColorCollection &colorCol) { if (::TableNumRows (colorCol) <= 2) { return ::TableDefaultHeight / 2; } return ::TableDefaultHeight / 3; } // // kpColorCells // struct kpColorCellsPrivate { Qt::Orientation orientation{}; // REFACTOR: This is data duplication with kpColorCellsBase::color[]. // We've probably forgotten to synchronize them in some points. // // Calls to kpColorCellsBase::setColor() (which also come from // kpColorCellsBase itself) will automatically update both // kpColorCellsBase::d->color[] and the table cells. setColor() emits // colorChanged(), which is caught by our slotColorChanged(), // which synchronizes this color collection and updates the modified flag. // // Avoid calling our grandparent's, QTableWidget's, mutating methods as we // don't override enough of them, to fire signals that we can catch to update // this color collection. // // If you modify this color collection directly (e.g. in setColorCollection(), // openColorCollection(), appendRow(), deleteLastRow(), ...), you must work // the other way and call makeCellsMatchColorCollection() to synchronize // kpColorCellsBase::d->color[] and the table cells. You still need to update // the modified flag. kpColorCollection colorCol; QUrl url; bool isModified{}; bool blockColorChangedSig{}; }; //--------------------------------------------------------------------- kpColorCells::kpColorCells (QWidget *parent, Qt::Orientation o) : kpColorCellsBase (parent, 0/*rows for now*/, 0/*cols for now*/), d (new kpColorCellsPrivate ()) { d->orientation = o; d->isModified = false; d->blockColorChangedSig = false; // When a text box is active, clicking to change the background color // should not move the keyboard focus away from the text box. setFocusPolicy (Qt::TabFocus); setShading (false); // no 3D look setAcceptDrops (true); setAcceptDrags (true); setCellsResizable (false); if (o == Qt::Horizontal) { // Reserve enough room for the default color collection's cells _and_ // a vertical scrollbar, which only appears when it's required. // This ensures that if the vertical scrollbar appears, it does not obscure // any cells or require the addition of a horizontal scrollbar, which would // look ugly and take even more precious room. // // We do not dynamically reserve room based on the actual number of rows // of cells, as that would make our containing widgets too big. setMinimumSize (::TableDefaultWidth + frameWidth () * 2 + verticalScrollBar()->sizeHint().width(), ::TableDefaultHeight + frameWidth () * 2); } else { Q_ASSERT (!"implemented"); } setVerticalScrollBarPolicy (Qt::ScrollBarAsNeeded); // The default QTableWidget policy of QSizePolicy::Expanding forces our // containing widgets to get too big. Override it. setSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum); connect (this, &kpColorCells::colorSelectedWhitButton, this, &kpColorCells::slotColorSelected); connect (this, &kpColorCells::colorDoubleClicked, this, &kpColorCells::slotColorDoubleClicked); connect (this, &kpColorCells::colorChanged, this, &kpColorCells::slotColorChanged); setColorCollection (DefaultColorCollection ()); setWhatsThis ( i18n ( "" "

To select the foreground color that tools use to draw," " left-click on a filled-in color cell." " To select the background color, right-click instead.

" "

To change the color of a color cell itself, double-click on it.

" "

You can also swap the color of a filled-in cell with any other" " cell using drag and drop." " Also, if you hold down the Ctrl key, the destination" " cell's color will be" " overwritten, instead of being swapped with the color of the source cell.

" "
")); } //--------------------------------------------------------------------- kpColorCells::~kpColorCells () { delete d; } //--------------------------------------------------------------------- // public static kpColorCollection kpColorCells::DefaultColorCollection () { return kpDefaultColorCollection (); } //--------------------------------------------------------------------- // public Qt::Orientation kpColorCells::orientation () const { return d->orientation; } //--------------------------------------------------------------------- // public void kpColorCells::setOrientation (Qt::Orientation o) { d->orientation = o; makeCellsMatchColorCollection (); } //--------------------------------------------------------------------- // protected // OPT: Find out why this is being called multiple times on startup. void kpColorCells::makeCellsMatchColorCollection () { int c, r; if (orientation () == Qt::Horizontal) { c = ::TableNumColumns (d->colorCol); r = ::TableNumRows (d->colorCol); } else { c = ::TableNumRows (d->colorCol); r = ::TableNumColumns (d->colorCol); } #if DEBUG_KP_COLOR_CELLS qCDebug(kpLogWidgets) << "kpColorCells::makeCellsMatchColorCollection():" << "r=" << r << "c=" << c; qCDebug(kpLogWidgets) << "verticalScrollBar=" << verticalScrollBar () << " sizeHint=" << (verticalScrollBar () ? verticalScrollBar ()->sizeHint () : QSize (-12, -34)); #endif // Delete all cell widgets. This ensures that there will be no left-over // cell widgets, for the colors in the new color collection that are // actually invalid (which should not have cell widgets). clearContents (); setRowCount (r); setColumnCount (c); int CellWidth = ::TableCellWidth (d->colorCol), CellHeight = ::TableCellHeight (d->colorCol); // TODO: Take a screenshot of KolourPaint, magnify it and you'll find the // cells don't have exactly the sizes requested here. e.g. the // top row of cells is 1 pixel shorter than the bottom row. There // are probably other glitches. for (int y = 0; y < r; y++) { setRowHeight (y, CellHeight); } for (int x = 0; x < c; x++) { setColumnWidth (x, CellWidth); } const bool oldBlockColorChangedSig = d->blockColorChangedSig; d->blockColorChangedSig = true; // The last "(rowCount() * columnCount()) - d->colorCol.count()" cells // will be empty because we did not initialize them. for (int i = 0; i < d->colorCol.count (); i++) { int y, x; int pos; if (orientation () == Qt::Horizontal) { y = i / c; x = i % c; pos = i; } else { y = i % r; x = i / r; // int x = c - 1 - i / r; pos = y * c + x; } #if DEBUG_KP_COLOR_CELLS && 0 qCDebug(kpLogWidgets) << "\tSetting cell " << i << ": y=" << y << " x=" << x << " pos=" << pos; qCDebug(kpLogWidgets) << "\t\tcolor=" << (int *) d->colorCol.color (i).rgba() << "isValid=" << d->colorCol.color (i).isValid (); #endif // (color may be invalid resulting in a hole in the middle of the table) setColor (pos, d->colorCol.color (i)); //this->setToolTip( cellGeometry (y, x), colors [i].name ()); } d->blockColorChangedSig = oldBlockColorChangedSig; } //--------------------------------------------------------------------- bool kpColorCells::isModified () const { return d->isModified; } //--------------------------------------------------------------------- void kpColorCells::setModified (bool yes) { #if DEBUG_KP_COLOR_CELLS qCDebug(kpLogWidgets) << "kpColorCells::setModified(" << yes << ")"; #endif if (yes == d->isModified) { return; } d->isModified = yes; emit isModifiedChanged (yes); } //--------------------------------------------------------------------- void kpColorCells::setModified () { setModified (true); } //--------------------------------------------------------------------- QUrl kpColorCells::url () const { return d->url; } //--------------------------------------------------------------------- QString kpColorCells::name () const { return d->colorCol.name (); } //--------------------------------------------------------------------- const kpColorCollection *kpColorCells::colorCollection () const { return &d->colorCol; } //--------------------------------------------------------------------- void kpColorCells::ensureHaveAtLeastOneRow () { if (d->colorCol.count () == 0) { d->colorCol.resize (::TableDefaultNumColumns); } } //--------------------------------------------------------------------- void kpColorCells::setColorCollection (const kpColorCollection &colorCol, const QUrl &url) { d->colorCol = colorCol; ensureHaveAtLeastOneRow (); d->url = url; setModified (false); makeCellsMatchColorCollection (); emit rowCountChanged (rowCount ()); emit urlChanged (d->url); emit nameChanged (name ()); } //--------------------------------------------------------------------- bool kpColorCells::openColorCollection (const QUrl &url) { // (this will pop up an error dialog on failure) if (d->colorCol.open (url, this)) { ensureHaveAtLeastOneRow (); d->url = url; setModified (false); makeCellsMatchColorCollection (); emit rowCountChanged (rowCount ()); emit urlChanged (d->url); emit nameChanged (name ()); return true; } return false; } //--------------------------------------------------------------------- bool kpColorCells::saveColorCollectionAs (const QUrl &url) { // (this will pop up an error dialog on failure) if (d->colorCol.saveAs (url, this)) { d->url = url; setModified (false); emit urlChanged (d->url); return true; } return false; } //--------------------------------------------------------------------- bool kpColorCells::saveColorCollection () { // (this will pop up an error dialog on failure) if (d->colorCol.saveAs (d->url, this)) { setModified (false); return true; } return false; } //--------------------------------------------------------------------- void kpColorCells::appendRow () { // This is the easiest implementation: change the color collection // and then synchronize the table cells. The other way is to call // setRowCount() and then, synchronize the color collection. const int targetNumCells = (rowCount () + 1) * ::TableDefaultNumColumns; d->colorCol.resize (targetNumCells); setModified (true); makeCellsMatchColorCollection (); emit rowCountChanged (rowCount ()); } //--------------------------------------------------------------------- void kpColorCells::deleteLastRow () { // This is the easiest implementation: change the color collection // and then synchronize the table cells. The other way is to call // setRowCount() and then, synchronize the color collection. const int targetNumCells = qMax (0, (rowCount () - 1) * ::TableDefaultNumColumns); d->colorCol.resize (targetNumCells); // If there was only one row of colors to start with, the effect of this // line (after the above resize()) is to change that row to a row of // invalid colors. ensureHaveAtLeastOneRow (); setModified (true); makeCellsMatchColorCollection (); emit rowCountChanged (rowCount ()); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpColorCells::contextMenuEvent (QContextMenuEvent *e) { // Eat right-mouse press to prevent it from getting to the toolbar. e->accept (); } //--------------------------------------------------------------------- // protected slot void kpColorCells::slotColorSelected (int cell, const QColor &color, Qt::MouseButton button) { #if DEBUG_KP_COLOR_CELLS qCDebug(kpLogWidgets) << "kpColorCells::slotColorSelected(cell=" << cell << ") mouseButton = " << button << " rgb=" << (int *) color.rgba(); #else Q_UNUSED (cell); #endif if (button == Qt::LeftButton) { emit foregroundColorChanged (kpColor (color.rgba())); } else if (button == Qt::RightButton) { emit backgroundColorChanged (kpColor (color.rgba())); } // REFACTOR: Make selectedness configurable inside kpColorCellsBase? // // Deselect the selected cell (selected by above kpColorCellsBase::mouseReleaseEvent()). // KolourPaint's palette has no concept of a current cell/color: you can // pick a color but you can't mark a cell as selected. In any case, a // selected cell would be rendered as violet, which would ruin the cell. // // setSelectionMode (kpColorCellsBase::NoSelection); does not work so we // clearSelection(). I think setSelectionMode() concerns when the user // directly selects a cell - not when kpColorCellsBase::mouseReleaseEvent() // selects a cell programmatically. clearSelection (); } //--------------------------------------------------------------------- // protected slot void kpColorCells::slotColorDoubleClicked (int cell, const QColor &) { QColorDialog dialog(this); dialog.setCurrentColor(kpColorCellsBase::color(cell)); dialog.setOptions(QColorDialog::ShowAlphaChannel); if ( dialog.exec() == QDialog::Accepted ) setColor (cell, dialog.currentColor()); } //--------------------------------------------------------------------- // protected slot void kpColorCells::slotColorChanged (int cell, const QColor &color) { #if DEBUG_KP_COLOR_CELLS qCDebug(kpLogWidgets) << "cell=" << cell << "color=" << (const int *) color.rgba() << "d->colorCol.count()=" << d->colorCol.count (); #endif if (d->blockColorChangedSig) { return; } // Cater for adding new colors to the end. if (cell >= d->colorCol.count ()) { d->colorCol.resize (cell + 1); } // TODO: We lose color names on a color swap (during drag-and-drop). const int ret = d->colorCol.changeColor (cell, color, QString ()/*color name*/); Q_ASSERT (ret == cell); setModified (true); } diff --git a/widgets/kpColorPalette.cpp b/widgets/kpColorPalette.cpp index 66793e60..229aa5c8 100644 --- a/widgets/kpColorPalette.cpp +++ b/widgets/kpColorPalette.cpp @@ -1,126 +1,126 @@ /* 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_PALETTE 0 +#define DEBUG_KP_COLOR_PALETTE 1 #include "kpColorPalette.h" #include "kpColorCells.h" #include "kpTransparentColorCell.h" #include //--------------------------------------------------------------------- struct kpColorPalettePrivate { Qt::Orientation orientation; QBoxLayout *boxLayout; kpTransparentColorCell *transparentColorCell; kpColorCells *colorCells; }; //--------------------------------------------------------------------- kpColorPalette::kpColorPalette (QWidget *parent, Qt::Orientation o) : QWidget (parent), d (new kpColorPalettePrivate ()) { d->boxLayout = nullptr; d->transparentColorCell = new kpTransparentColorCell (this); connect (d->transparentColorCell, &kpTransparentColorCell::foregroundColorChanged, this, &kpColorPalette::foregroundColorChanged); connect (d->transparentColorCell, &kpTransparentColorCell::backgroundColorChanged, this, &kpColorPalette::backgroundColorChanged); d->colorCells = new kpColorCells (this); connect (d->colorCells, &kpColorCells::foregroundColorChanged, this, &kpColorPalette::foregroundColorChanged); connect (d->colorCells, &kpColorCells::backgroundColorChanged, this, &kpColorPalette::backgroundColorChanged); setOrientation (o); } //--------------------------------------------------------------------- kpColorPalette::~kpColorPalette () { delete d; } //--------------------------------------------------------------------- // public Qt::Orientation kpColorPalette::orientation () const { return d->orientation; } //--------------------------------------------------------------------- void kpColorPalette::setOrientation (Qt::Orientation o) { d->colorCells->setOrientation (o); delete d->boxLayout; if (o == Qt::Horizontal) { d->boxLayout = new QBoxLayout (QBoxLayout::LeftToRight, this); d->boxLayout->addWidget (d->transparentColorCell, 0/*stretch*/, Qt::AlignTop); d->boxLayout->addWidget (d->colorCells); } else { d->boxLayout = new QBoxLayout (QBoxLayout::TopToBottom, this); d->boxLayout->addWidget (d->transparentColorCell, 0/*stretch*/, Qt::AlignTop); d->boxLayout->addWidget (d->colorCells); } d->boxLayout->setSpacing (5); d->orientation = o; } //--------------------------------------------------------------------- // public kpColorCells *kpColorPalette::colorCells () const { return d->colorCells; } //--------------------------------------------------------------------- diff --git a/widgets/kpDocumentSaveOptionsWidget.cpp b/widgets/kpDocumentSaveOptionsWidget.cpp index fed56424..60beceee 100644 --- a/widgets/kpDocumentSaveOptionsWidget.cpp +++ b/widgets/kpDocumentSaveOptionsWidget.cpp @@ -1,754 +1,754 @@ /* 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 0 +#define DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET 1 #include "widgets/kpDocumentSaveOptionsWidget.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "dialogs/kpDocumentSaveOptionsPreviewDialog.h" #include "pixmapfx/kpPixmapFX.h" #include "generic/widgets/kpResizeSignallingLabel.h" #include "dialogs/imagelib/transforms/kpTransformPreviewDialog.h" #include "generic/kpWidgetMapper.h" #include "kpLogCategories.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include kpDocumentSaveOptionsWidget::kpDocumentSaveOptionsWidget ( const QImage &docPixmap, const kpDocumentSaveOptions &saveOptions, const kpDocumentMetaInfo &metaInfo, QWidget *parent) : QWidget (parent), m_visualParent (parent) { init (); setDocumentSaveOptions (saveOptions); setDocumentPixmap (docPixmap); setDocumentMetaInfo (metaInfo); } kpDocumentSaveOptionsWidget::kpDocumentSaveOptionsWidget ( QWidget *parent) : QWidget (parent), m_visualParent (parent) { init (); } // private void kpDocumentSaveOptionsWidget::init () { m_documentPixmap = nullptr; m_previewDialog = nullptr; m_visualParent = nullptr; m_colorDepthLabel = new QLabel (i18n ("Convert &to:"), this); m_colorDepthCombo = new QComboBox (this); m_colorDepthSpaceWidget = new QWidget (this); m_qualityLabel = new QLabel(i18n ("Quali&ty:"), this); m_qualityInput = new QSpinBox(this); // Note that we set min to 1 not 0 since "0 Quality" is a bit misleading // and 101 quality settings would be weird. So we lose 1 quality setting // according to QImage::save(). // TODO: 100 quality is also misleading since that implies perfect quality. m_qualityInput->setRange (1, 100); m_previewButton = new QPushButton (i18n ("&Preview"), this); m_previewButton->setCheckable (true); m_colorDepthLabel->setBuddy (m_colorDepthCombo); m_qualityLabel->setBuddy (m_qualityInput); auto *lay = new QHBoxLayout (this); lay->setContentsMargins(0, 0, 0, 0); lay->addWidget (m_colorDepthLabel, 0/*stretch*/, Qt::AlignLeft); lay->addWidget (m_colorDepthCombo, 0/*stretch*/); lay->addWidget (m_colorDepthSpaceWidget, 1/*stretch*/); lay->addWidget (m_qualityLabel, 0/*stretch*/, Qt::AlignLeft); lay->addWidget (m_qualityInput, 2/*stretch*/); lay->addWidget (m_previewButton, 0/*stretch*/, Qt::AlignRight); connect (m_colorDepthCombo, static_cast(&QComboBox::activated), this, &kpDocumentSaveOptionsWidget::slotColorDepthSelected); connect (m_colorDepthCombo, static_cast(&QComboBox::activated), this, &kpDocumentSaveOptionsWidget::updatePreview); connect (m_qualityInput, static_cast(&QSpinBox::valueChanged), this, &kpDocumentSaveOptionsWidget::updatePreviewDelayed); connect (m_previewButton, &QPushButton::toggled, this, &kpDocumentSaveOptionsWidget::showPreview); m_updatePreviewDelay = 200/*ms*/; m_updatePreviewTimer = new QTimer (this); m_updatePreviewTimer->setSingleShot (true); connect (m_updatePreviewTimer, &QTimer::timeout, this, &kpDocumentSaveOptionsWidget::updatePreview); m_updatePreviewDialogLastRelativeGeometryTimer = new QTimer (this); connect (m_updatePreviewDialogLastRelativeGeometryTimer, &QTimer::timeout, this, &kpDocumentSaveOptionsWidget::updatePreviewDialogLastRelativeGeometry); setMode (None); slotColorDepthSelected (); } kpDocumentSaveOptionsWidget::~kpDocumentSaveOptionsWidget () { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::()"; #endif hidePreview (); delete m_documentPixmap; } // public void kpDocumentSaveOptionsWidget::setVisualParent (QWidget *visualParent) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::setVisualParent(" << visualParent << ")" << endl; #endif m_visualParent = visualParent; } // protected bool kpDocumentSaveOptionsWidget::mimeTypeHasConfigurableColorDepth () const { return kpDocumentSaveOptions::mimeTypeHasConfigurableColorDepth (mimeType ()); } // protected bool kpDocumentSaveOptionsWidget::mimeTypeHasConfigurableQuality () const { return kpDocumentSaveOptions::mimeTypeHasConfigurableQuality (mimeType ()); } // public QString kpDocumentSaveOptionsWidget::mimeType () const { return m_baseDocumentSaveOptions.mimeType (); } // public slots void kpDocumentSaveOptionsWidget::setMimeType (const QString &string) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::setMimeType(" << string << ") maxColorDepth=" << kpDocumentSaveOptions::mimeTypeMaximumColorDepth (string) << endl; #endif const int newMimeTypeMaxDepth = kpDocumentSaveOptions::mimeTypeMaximumColorDepth (string); #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\toldMimeType=" << mimeType () << " maxColorDepth=" << kpDocumentSaveOptions::mimeTypeMaximumColorDepth ( mimeType ()) << endl; #endif if (mimeType ().isEmpty () || kpDocumentSaveOptions::mimeTypeMaximumColorDepth (mimeType ()) != newMimeTypeMaxDepth) { m_colorDepthCombo->clear (); m_colorDepthCombo->insertItem (0, i18n ("Monochrome")); m_colorDepthCombo->insertItem (1, i18n ("Monochrome (Dithered)")); if (newMimeTypeMaxDepth >= 8) { m_colorDepthCombo->insertItem (2, i18n ("256 Color")); m_colorDepthCombo->insertItem (3, i18n ("256 Color (Dithered)")); } if (newMimeTypeMaxDepth >= 24) { m_colorDepthCombo->insertItem (4, i18n ("24-bit Color")); } if (m_colorDepthComboLastSelectedItem >= 0 && m_colorDepthComboLastSelectedItem < m_colorDepthCombo->count ()) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tsetting colorDepthCombo to " << m_colorDepthComboLastSelectedItem << endl; #endif m_colorDepthCombo->setCurrentIndex (m_colorDepthComboLastSelectedItem); } else { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tsetting colorDepthCombo to max item since" << " m_colorDepthComboLastSelectedItem=" << m_colorDepthComboLastSelectedItem << " out of range" << endl; #endif m_colorDepthCombo->setCurrentIndex (m_colorDepthCombo->count () - 1); } } m_baseDocumentSaveOptions.setMimeType (string); if (mimeTypeHasConfigurableColorDepth ()) { setMode (ColorDepth); } else if (mimeTypeHasConfigurableQuality ()) { setMode (Quality); } else { setMode (None); } updatePreview (); } // public int kpDocumentSaveOptionsWidget::colorDepth () const { if (mode () & ColorDepth) { // The returned values match QImage's supported depths. switch (m_colorDepthCombo->currentIndex ()) { case 0: case 1: return 1; case 2: case 3: return 8; case 4: // 24-bit is known as 32-bit with QImage. return 32; default: return kpDocumentSaveOptions::invalidColorDepth (); } } else { return m_baseDocumentSaveOptions.colorDepth (); } } // public bool kpDocumentSaveOptionsWidget::dither () const { if (mode () & ColorDepth) { return (m_colorDepthCombo->currentIndex () == 1 || m_colorDepthCombo->currentIndex () == 3); } return m_baseDocumentSaveOptions.dither (); } // protected static int kpDocumentSaveOptionsWidget::colorDepthComboItemFromColorDepthAndDither ( int depth, bool dither) { switch (depth) { case 1: if (!dither) { return 0; } return 1; case 8: if (!dither) { return 2; } return 3; case 32: return 4; default: return -1; } } // public slots void kpDocumentSaveOptionsWidget::setColorDepthDither (int newDepth, bool newDither) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::setColorDepthDither(" << "depth=" << newDepth << ",dither=" << newDither << ")" << endl; #endif m_baseDocumentSaveOptions.setColorDepth (newDepth); m_baseDocumentSaveOptions.setDither (newDither); const int comboItem = colorDepthComboItemFromColorDepthAndDither ( newDepth, newDither); // TODO: Ignoring when comboItem >= m_colorDepthCombo->count() is wrong. // This happens if this mimeType has configurable colour depth // and an incorrect maximum colour depth (less than a QImage of // this mimeType, opened by kpDocument). if (comboItem >= 0 && comboItem < m_colorDepthCombo->count ()) { m_colorDepthCombo->setCurrentIndex (comboItem); } slotColorDepthSelected (); } // protected slot void kpDocumentSaveOptionsWidget::slotColorDepthSelected () { if (mode () & ColorDepth) { m_colorDepthComboLastSelectedItem = m_colorDepthCombo->currentIndex (); } else { m_colorDepthComboLastSelectedItem = colorDepthComboItemFromColorDepthAndDither ( m_baseDocumentSaveOptions.colorDepth (), m_baseDocumentSaveOptions.dither ()); } #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::slotColorDepthSelected()" << " mode&ColorDepth=" << (mode () & ColorDepth) << " colorDepthComboLastSelectedItem=" << m_colorDepthComboLastSelectedItem << endl; #endif } // public int kpDocumentSaveOptionsWidget::quality () const { if (mode () & Quality) { return m_qualityInput->value (); } return m_baseDocumentSaveOptions.quality (); } // public void kpDocumentSaveOptionsWidget::setQuality (int newQuality) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::setQuality(" << newQuality << ")" << endl; #endif m_baseDocumentSaveOptions.setQuality (newQuality); m_qualityInput->setValue (newQuality == -1/*QImage::save() default*/ ? 75 : newQuality); } // public kpDocumentSaveOptions kpDocumentSaveOptionsWidget::documentSaveOptions () const { return kpDocumentSaveOptions (mimeType (), colorDepth (), dither (), quality ()); } // public void kpDocumentSaveOptionsWidget::setDocumentSaveOptions ( const kpDocumentSaveOptions &saveOptions) { setMimeType (saveOptions.mimeType ()); setColorDepthDither (saveOptions.colorDepth (), saveOptions.dither ()); setQuality (saveOptions.quality ()); } // public void kpDocumentSaveOptionsWidget::setDocumentPixmap (const QImage &documentPixmap) { delete m_documentPixmap; m_documentPixmap = new QImage (documentPixmap); updatePreview (); } // public void kpDocumentSaveOptionsWidget::setDocumentMetaInfo ( const kpDocumentMetaInfo &metaInfo) { m_documentMetaInfo = metaInfo; updatePreview (); } // public kpDocumentSaveOptionsWidget::Mode kpDocumentSaveOptionsWidget::mode () const { return m_mode; } // public void kpDocumentSaveOptionsWidget::setMode (Mode mode) { m_mode = mode; // If mode == None, we show still show the Color Depth widgets but disabled m_colorDepthLabel->setVisible (mode != Quality); m_colorDepthCombo->setVisible (mode != Quality); m_colorDepthSpaceWidget->setVisible (mode != Quality); m_qualityLabel->setVisible (mode == Quality); m_qualityInput->setVisible (mode == Quality); m_colorDepthLabel->setEnabled (mode == ColorDepth); m_colorDepthCombo->setEnabled (mode == ColorDepth); m_qualityLabel->setEnabled (mode == Quality); m_qualityInput->setEnabled (mode == Quality); // SYNC: HACK: When changing between color depth and quality widgets, // we change the height of "this", causing the text on the labels // to move but the first instance of the text doesn't get erased. // Qt bug. QTimer::singleShot (0, this, &kpDocumentSaveOptionsWidget::repaintLabels); } // protected slot void kpDocumentSaveOptionsWidget::repaintLabels () { if (mode () != Quality) { m_colorDepthLabel->repaint (); } if (mode () == Quality) { m_qualityLabel->repaint (); } } // protected slot void kpDocumentSaveOptionsWidget::showPreview (bool yes) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::showPreview(" << yes << ")" << " m_previewDialog=" << bool (m_previewDialog) << endl; #endif if (yes == bool (m_previewDialog)) { return; } if (!m_visualParent) { return; } if (yes) { m_previewDialog = new kpDocumentSaveOptionsPreviewDialog( m_visualParent ); m_previewDialog->setObjectName( QStringLiteral( "previewSaveDialog" ) ); updatePreview (); connect (m_previewDialog, &kpDocumentSaveOptionsPreviewDialog::finished, this, &kpDocumentSaveOptionsWidget::hidePreview); KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupPreviewSave); if (cfg.hasKey (kpSettingPreviewSaveUpdateDelay)) { m_updatePreviewDelay = cfg.readEntry (kpSettingPreviewSaveUpdateDelay, 0); } else { cfg.writeEntry (kpSettingPreviewSaveUpdateDelay, m_updatePreviewDelay); cfg.sync (); } if (m_updatePreviewDelay < 0) { m_updatePreviewDelay = 0; } #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tread cfg preview dialog update delay=" << m_updatePreviewDelay; #endif if (m_previewDialogLastRelativeGeometry.isEmpty ()) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tread cfg preview dialog last rel geometry"; #endif KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupPreviewSave); m_previewDialogLastRelativeGeometry = cfg.readEntry ( kpSettingPreviewSaveGeometry, QRect ()); } #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tpreviewDialogLastRelativeGeometry=" << m_previewDialogLastRelativeGeometry << " visualParent->rect()=" << m_visualParent->rect () << endl; #endif QRect relativeGeometry; if (!m_previewDialogLastRelativeGeometry.isEmpty () && m_visualParent->rect ().intersects (m_previewDialogLastRelativeGeometry)) { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tok"; #endif relativeGeometry = m_previewDialogLastRelativeGeometry; } else { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\t\tinvalid"; #endif const int margin = 20; relativeGeometry = QRect (m_visualParent->width () - m_previewDialog->preferredMinimumSize ().width () - margin, margin * 2, // Avoid folder combo m_previewDialog->preferredMinimumSize ().width (), m_previewDialog->preferredMinimumSize ().height ()); } const QRect globalGeometry = kpWidgetMapper::toGlobal (m_visualParent, relativeGeometry); #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\trelativeGeometry=" << relativeGeometry << " globalGeometry=" << globalGeometry << endl; #endif m_previewDialog->resize (globalGeometry.size ()); m_previewDialog->move (globalGeometry.topLeft ()); m_previewDialog->show (); #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tgeometry after show=" << QRect (m_previewDialog->x (), m_previewDialog->y (), m_previewDialog->width (), m_previewDialog->height ()) << endl; #endif updatePreviewDialogLastRelativeGeometry (); connect (m_previewDialog, &kpDocumentSaveOptionsPreviewDialog::moved, this, &kpDocumentSaveOptionsWidget::updatePreviewDialogLastRelativeGeometry); connect (m_previewDialog, &kpDocumentSaveOptionsPreviewDialog::resized, this, &kpDocumentSaveOptionsWidget::updatePreviewDialogLastRelativeGeometry); m_updatePreviewDialogLastRelativeGeometryTimer->start (200/*ms*/); } else { m_updatePreviewDialogLastRelativeGeometryTimer->stop (); KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupPreviewSave); cfg.writeEntry (kpSettingPreviewSaveGeometry, m_previewDialogLastRelativeGeometry); cfg.sync (); #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tsaving preview geometry " << m_previewDialogLastRelativeGeometry << " (Qt would have us believe " << kpWidgetMapper::fromGlobal (m_visualParent, QRect (m_previewDialog->x (), m_previewDialog->y (), m_previewDialog->width (), m_previewDialog->height ())) << ")" << endl; #endif m_previewDialog->deleteLater (); m_previewDialog = nullptr; } } // protected slot void kpDocumentSaveOptionsWidget::hidePreview () { if (m_previewButton->isChecked ()) { m_previewButton->toggle (); } } // protected slot void kpDocumentSaveOptionsWidget::updatePreviewDelayed () { // (single shot) m_updatePreviewTimer->start (m_updatePreviewDelay); } // protected slot void kpDocumentSaveOptionsWidget::updatePreview () { if (!m_previewDialog || !m_documentPixmap) { return; } m_updatePreviewTimer->stop (); QApplication::setOverrideCursor (Qt::WaitCursor); QByteArray data; QBuffer buffer (&data); buffer.open (QIODevice::WriteOnly); bool savedOK = kpDocument::savePixmapToDevice (*m_documentPixmap, &buffer, documentSaveOptions (), m_documentMetaInfo, false/*no lossy prompt*/, this); buffer.close (); QImage image; // Ignore any failed saves. // // Failed saves might literally have written half a file. The final // save (when the user clicks OK), _will_ fail so we shouldn't have a // preview even if this "half a file" is actually loadable by // QImage::loadFormData(). if (savedOK) { image.loadFromData(data); } else { // Leave as invalid. // TODO: This code path has not been well tested. // Will we trigger divide by zero errors in "m_previewDialog"? } // REFACTOR: merge with kpDocument::getPixmapFromFile() m_previewDialog->setFilePixmapAndSize (image, data.size ()); QApplication::restoreOverrideCursor (); } // protected slot void kpDocumentSaveOptionsWidget::updatePreviewDialogLastRelativeGeometry () { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "kpDocumentSaveOptionsWidget::" << "updatePreviewDialogLastRelativeGeometry()" << endl; #endif if (m_previewDialog && m_previewDialog->isVisible ()) { m_previewDialogLastRelativeGeometry = kpWidgetMapper::fromGlobal (m_visualParent, QRect (m_previewDialog->x (), m_previewDialog->y (), m_previewDialog->width (), m_previewDialog->height ())); #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tcaching pos = " << m_previewDialogLastRelativeGeometry; #endif } else { #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET qCDebug(kpLogWidgets) << "\tnot visible - ignoring geometry"; #endif } } diff --git a/widgets/kpDualColorButton.cpp b/widgets/kpDualColorButton.cpp index 8f277536..c02ca398 100644 --- a/widgets/kpDualColorButton.cpp +++ b/widgets/kpDualColorButton.cpp @@ -1,469 +1,465 @@ /* 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_DUAL_COLOR_BUTTON 0 +#define DEBUG_KP_DUAL_COLOR_BUTTON 1 #include "kpDualColorButton.h" #include "views/kpView.h" #include #include "kpLogCategories.h" #include #include #include #include #include #include #include //--------------------------------------------------------------------- kpDualColorButton::kpDualColorButton (QWidget *parent) : QFrame (parent), m_dragStartPoint (KP_INVALID_POINT) { setSizePolicy (QSizePolicy::Fixed/*horizontal*/, QSizePolicy::Fixed/*vertical*/); setFrameStyle (QFrame::Panel | QFrame::Sunken); m_color [0] = kpColor (0, 0, 0); // black m_color [1] = kpColor (255, 255, 255); // white setAcceptDrops (true); } //--------------------------------------------------------------------- kpColor kpDualColorButton::color (int which) const { Q_ASSERT (which == 0 || which == 1); return m_color [which]; } //--------------------------------------------------------------------- kpColor kpDualColorButton::foregroundColor () const { return color (0); } //--------------------------------------------------------------------- kpColor kpDualColorButton::backgroundColor () const { return color (1); } //--------------------------------------------------------------------- void kpDualColorButton::setColor (int which, const kpColor &color) { Q_ASSERT (which == 0 || which == 1); if (m_color [which] == color) { return; } m_oldColor [which] = m_color [which]; m_color [which] = color; update (); if (which == 0) { emit foregroundColorChanged (color); } else { emit backgroundColorChanged (color); } } //--------------------------------------------------------------------- void kpDualColorButton::setForegroundColor (const kpColor &color) { setColor (0, color); } //--------------------------------------------------------------------- void kpDualColorButton::setBackgroundColor (const kpColor &color) { setColor (1, color); } //--------------------------------------------------------------------- // public kpColor kpDualColorButton::oldForegroundColor () const { return m_oldColor [0]; } //--------------------------------------------------------------------- // public kpColor kpDualColorButton::oldBackgroundColor () const { return m_oldColor [1]; } //--------------------------------------------------------------------- // public virtual [base QWidget] QSize kpDualColorButton::sizeHint () const { return {52, 52}; } //--------------------------------------------------------------------- // protected QRect kpDualColorButton::swapPixmapRect () const { QPixmap swapPixmap = QStringLiteral(":/icons/colorbutton_swap_16x16"); return {contentsRect ().width () - swapPixmap.width (), 0, swapPixmap.width (), swapPixmap.height ()}; } //--------------------------------------------------------------------- // protected QRect kpDualColorButton::foregroundBackgroundRect () const { QRect cr (contentsRect ()); return {cr.width () / 8, cr.height () / 8, cr.width () * 6 / 8, cr.height () * 6 / 8}; } //--------------------------------------------------------------------- // protected QRect kpDualColorButton::foregroundRect () const { QRect fbr (foregroundBackgroundRect ()); return {fbr.x (), fbr.y (), fbr.width () * 3 / 4, fbr.height () * 3 / 4}; } //--------------------------------------------------------------------- // protected QRect kpDualColorButton::backgroundRect () const { QRect fbr (foregroundBackgroundRect ()); return {fbr.x () + fbr.width () / 4, fbr.y () + fbr.height () / 4, fbr.width () * 3 / 4, fbr.height () * 3 / 4}; } //--------------------------------------------------------------------- // protected virtual void kpDualColorButton::dragEnterEvent (QDragEnterEvent *e) { #if DEBUG_KP_DUAL_COLOR_BUTTON qCDebug(kpLogWidgets) << "kpDualColorButton::dragEnterEvent() canDecode=" << KColorMimeData::canDecode (e->mimeData ()); #endif e->accept (); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpDualColorButton::dragMoveEvent (QDragMoveEvent *e) { #if DEBUG_KP_DUAL_COLOR_BUTTON qCDebug(kpLogWidgets) << "kpDualColorButton::dragMoveEvent() canDecode=" << KColorMimeData::canDecode (e->mimeData ()); #endif e->setAccepted ( (foregroundRect ().contains (e->pos ()) || backgroundRect ().contains (e->pos ())) && KColorMimeData::canDecode (e->mimeData ())); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpDualColorButton::dropEvent (QDropEvent *e) { QColor col = KColorMimeData::fromMimeData (e->mimeData ()); #if DEBUG_KP_DUAL_COLOR_BUTTON qCDebug(kpLogWidgets) << "kpDualColorButton::dropEvent() col=" << (int *) col.rgba() << " (with alpha=" << (int *) col.rgba () << ")"; #endif if (col.isValid ()) { if (foregroundRect ().contains (e->pos ())) { setForegroundColor (kpColor (col.rgba())); } else if (backgroundRect ().contains (e->pos ())) { setBackgroundColor (kpColor (col.rgba())); } } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpDualColorButton::mousePressEvent (QMouseEvent *e) { #if DEBUG_KP_DUAL_COLOR_BUTTON qCDebug(kpLogWidgets) << "kpDualColorButton::mousePressEvent() pos=" << e->pos (); #endif m_dragStartPoint = KP_INVALID_POINT; if (e->button () == Qt::LeftButton) { m_dragStartPoint = e->pos (); } } //--------------------------------------------------------------------- void kpDualColorButton::mouseMoveEvent (QMouseEvent *e) { #if DEBUG_KP_DUAL_COLOR_BUTTON qCDebug(kpLogWidgets) << "kpDualColorButton::mouseMoveEvent() pos=" << e->pos () << " buttons=" << e->buttons () - << " dragStartPoint=" << m_dragStartPoint << endl; + << " dragStartPoint=" << m_dragStartPoint; #endif if (m_dragStartPoint == KP_INVALID_POINT) { return; } if (!(e->buttons () & Qt::LeftButton)) { m_dragStartPoint = KP_INVALID_POINT; return; } const int delay = QApplication::startDragDistance (); if (e->x () < m_dragStartPoint.x () - delay || e->x () > m_dragStartPoint.x () + delay || e->y () < m_dragStartPoint.y () - delay || e->y () > m_dragStartPoint.y () + delay) { #if DEBUG_KP_DUAL_COLOR_BUTTON qCDebug(kpLogWidgets) << "\tstarting drag as long as it's in a rectangle"; #endif kpColor color; if (foregroundRect ().contains (m_dragStartPoint)) { color = foregroundColor (); } else if (backgroundRect ().contains (m_dragStartPoint)) { color = backgroundColor (); } #if DEBUG_KP_DUAL_COLOR_BUTTON qCDebug(kpLogWidgets) << "\tcolor.isValid=" << color.isValid () - << " rgb=" << (color.isValid () ? (int *) color.toQRgb () : 0) - << endl; + << " rgb=" << (color.isValid () ? (int *) color.toQRgb () : 0); #endif if (color.isValid ()) { if (!color.isTransparent ()) { KColorMimeData::createDrag (color.toQColor (), this)->exec (); } } m_dragStartPoint = KP_INVALID_POINT; } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpDualColorButton::mouseReleaseEvent (QMouseEvent *e) { m_dragStartPoint = KP_INVALID_POINT; if (swapPixmapRect ().contains (e->pos ()) && m_color [0] != m_color [1]) { #if DEBUG_KP_DUAL_COLOR_BUTTON && 1 qCDebug(kpLogWidgets) << "kpDualColorButton::mouseReleaseEvent() swap colors:"; #endif m_oldColor [0] = m_color [0]; m_oldColor [1] = m_color [1]; kpColor temp = m_color [0]; m_color [0] = m_color [1]; m_color [1] = temp; update (); emit colorsSwapped (m_color [0], m_color [1]); emit foregroundColorChanged (m_color [0]); emit backgroundColorChanged (m_color [1]); } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpDualColorButton::mouseDoubleClickEvent (QMouseEvent *e) { int whichColor = -1; if (foregroundRect ().contains (e->pos ())) { whichColor = 0; } else if (backgroundRect ().contains (e->pos ())) { whichColor = 1; } if (whichColor == 0 || whichColor == 1) { QColorDialog dialog(this); dialog.setCurrentColor(color(whichColor).toQColor()); dialog.setOptions(QColorDialog::ShowAlphaChannel); if ( dialog.exec() == QDialog::Accepted ) { setColor(whichColor, kpColor(dialog.currentColor().rgba())); } } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpDualColorButton::paintEvent (QPaintEvent *e) { #if DEBUG_KP_DUAL_COLOR_BUTTON && 1 qCDebug(kpLogWidgets) << "kpDualColorButton::draw() rect=" << rect () - << " contentsRect=" << contentsRect () - << endl; + << " contentsRect=" << contentsRect (); #endif // Draw frame first. QFrame::paintEvent (e); QPainter painter (this); // Fill with background. if (isEnabled ()) { kpView::drawTransparentBackground (&painter, contentsRect ().topLeft ()/*checkerboard top-left*/, contentsRect (), true/*preview*/); } else { // Use default widget background. } painter.translate (contentsRect ().x (), contentsRect ().y ()); // Draw "Swap Colours" button (top-right). QPixmap swapPixmap = QStringLiteral(":/icons/colorbutton_swap_16x16"); if (!isEnabled ()) { // Don't let the fill() touch the mask. QBitmap swapBitmapMask = swapPixmap.mask (); swapPixmap.setMask (QBitmap ()); // Grey out the opaque parts of "swapPixmap". swapPixmap.fill (palette ().color (QPalette::Dark)); swapPixmap.setMask (swapBitmapMask); } painter.drawPixmap (swapPixmapRect ().topLeft (), swapPixmap); // Draw background colour patch. QRect bgRect = backgroundRect (); QRect bgRectInside = QRect (bgRect.x () + 2, bgRect.y () + 2, bgRect.width () - 4, bgRect.height () - 4); if (isEnabled ()) { #if DEBUG_KP_DUAL_COLOR_BUTTON && 1 - qCDebug(kpLogWidgets) << "\tbackgroundColor=" << (int *) m_color [1].toQRgb () - << endl; + qCDebug(kpLogWidgets) << "\tbackgroundColor=" << (int *) m_color [1].toQRgb (); #endif if (m_color [1].isTransparent ()) { // only if fully transparent painter.drawPixmap (bgRectInside, QStringLiteral(":/icons/color_transparent_26x26")); } else { painter.fillRect (bgRectInside, m_color [1].toQColor ()); } } else { painter.fillRect (bgRectInside, palette().color (QPalette::Button)); } qDrawShadePanel (&painter, bgRect, palette(), false/*not sunken*/, 2/*lineWidth*/, nullptr/*never fill*/); // Draw foreground colour patch. // Must be drawn after background patch since we're on top. QRect fgRect = foregroundRect (); QRect fgRectInside = QRect (fgRect.x () + 2, fgRect.y () + 2, fgRect.width () - 4, fgRect.height () - 4); if (isEnabled ()) { #if DEBUG_KP_DUAL_COLOR_BUTTON && 1 - qCDebug(kpLogWidgets) << "\tforegroundColor=" << (int *) m_color [0].toQRgb () - << endl; + qCDebug(kpLogWidgets) << "\tforegroundColor=" << (int *) m_color [0].toQRgb (); #endif if (m_color [0].isTransparent ()) { // only if fully transparent painter.drawPixmap (fgRectInside, QStringLiteral(":/icons/color_transparent_26x26")); } else { painter.fillRect (fgRectInside, m_color [0].toQColor ()); } } else { painter.fillRect (fgRectInside, palette ().color (QPalette::Button)); } qDrawShadePanel (&painter, fgRect, palette (), false/*not sunken*/, 2/*lineWidth*/, nullptr/*never fill*/); } diff --git a/widgets/kpPrintDialogPage.cpp b/widgets/kpPrintDialogPage.cpp index a7b11574..8824eaec 100644 --- a/widgets/kpPrintDialogPage.cpp +++ b/widgets/kpPrintDialogPage.cpp @@ -1,102 +1,102 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2007 John Layt 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_PRINT_DIALOG_PAGE 0 +#define DEBUG_KP_PRINT_DIALOG_PAGE 1 #include "kpPrintDialogPage.h" #include #include #include #include "kpLogCategories.h" #include #include "kpDefs.h" struct kpPrintDialogPagePrivate { QRadioButton *printCenteredRadio, *printTopLeftRadio; }; kpPrintDialogPage::kpPrintDialogPage (QWidget *parent) : QWidget (parent), d (new kpPrintDialogPagePrivate ()) { #if DEBUG_KP_PRINT_DIALOG_PAGE qCDebug(kpLogWidgets) << "kpPrintDialogPage::()"; #endif setWindowTitle (i18nc ("@title:tab", "I&mage Position")); d->printCenteredRadio = new QRadioButton (i18n ("&Center of the page"), this); d->printTopLeftRadio = new QRadioButton (i18n ("Top-&left of the page"), this); auto *lay = new QVBoxLayout (this); lay->addWidget (d->printCenteredRadio); lay->addWidget (d->printTopLeftRadio); lay->addStretch (); setPrintImageCenteredOnPage (true); } kpPrintDialogPage::~kpPrintDialogPage () { delete d; } bool kpPrintDialogPage::printImageCenteredOnPage () { #if DEBUG_KP_PRINT_DIALOG_PAGE qCDebug(kpLogWidgets) << "kpPrintDialogPage::printImageCenteredOnPage()" << " returning " << d->printCenteredRadio->isChecked(); #endif return d->printCenteredRadio->isChecked (); } void kpPrintDialogPage::setPrintImageCenteredOnPage (bool printCentered) { #if DEBUG_KP_PRINT_DIALOG_PAGE qCDebug(kpLogWidgets) << "kpPrintDialogPage::setOptions(" << printCentered << ")"; #endif if (printCentered) { d->printCenteredRadio->setChecked (true); } else { d->printTopLeftRadio->setChecked (true); } } diff --git a/widgets/kpTransparentColorCell.cpp b/widgets/kpTransparentColorCell.cpp index 942cb0fb..bf24ae7b 100644 --- a/widgets/kpTransparentColorCell.cpp +++ b/widgets/kpTransparentColorCell.cpp @@ -1,127 +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_TRANSPARENT_COLOR_CELL 0 +#define DEBUG_KP_TRANSPARENT_COLOR_CELL 1 #include "kpTransparentColorCell.h" #include "imagelib/kpColor.h" +#include "kpLogCategories.h" #include #include #include #include //--------------------------------------------------------------------- kpTransparentColorCell::kpTransparentColorCell (QWidget *parent) : QFrame (parent) { setSizePolicy (QSizePolicy::Fixed/*horizontal*/, QSizePolicy::Fixed/*vertical*/); setFrameStyle (QFrame::Panel | QFrame::Sunken); m_pixmap = QStringLiteral(":/icons/color_transparent_26x26"); this->setToolTip( i18n ("Transparent")); } //--------------------------------------------------------------------- // public virtual [base QWidget] QSize kpTransparentColorCell::sizeHint () const { return {m_pixmap.width () + frameWidth () * 2, m_pixmap.height () + frameWidth () * 2}; } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpTransparentColorCell::mousePressEvent (QMouseEvent * /*e*/) { // Eat press so that we own the mouseReleaseEvent(). // [https://www.qt.io/blog/2006/05/27/mouse-event-propagation] // // However, contrary to that blog, it doesn't seem to be needed? } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpTransparentColorCell::contextMenuEvent (QContextMenuEvent *e) { // Eat right-mouse press to prevent it from getting to the toolbar. e->accept (); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpTransparentColorCell::mouseReleaseEvent (QMouseEvent *e) { if (rect ().contains (e->pos ())) { if (e->button () == Qt::LeftButton) { emit transparentColorSelected (0); emit foregroundColorChanged (kpColor::Transparent); } else if (e->button () == Qt::RightButton) { emit transparentColorSelected (1); emit backgroundColorChanged (kpColor::Transparent); } } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpTransparentColorCell::paintEvent (QPaintEvent *e) { // Draw frame first. QFrame::paintEvent (e); if (isEnabled ()) { #if DEBUG_KP_TRANSPARENT_COLOR_CELL qCDebug(kpLogWidgets) << "kpTransparentColorCell::paintEvent() contentsRect=" << contentsRect () << endl; #endif QPainter p (this); p.drawPixmap (contentsRect (), m_pixmap); } } //--------------------------------------------------------------------- diff --git a/widgets/toolbars/kpColorToolBar.cpp b/widgets/toolbars/kpColorToolBar.cpp index fce3a9d8..36bf8844 100644 --- a/widgets/toolbars/kpColorToolBar.cpp +++ b/widgets/toolbars/kpColorToolBar.cpp @@ -1,341 +1,341 @@ /* 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_TOOL_BAR 0 +#define DEBUG_KP_COLOR_TOOL_BAR 1 #include "widgets/toolbars/kpColorToolBar.h" #include #include #include #include #include #include "kpLogCategories.h" #include "widgets/kpColorCells.h" #include "widgets/kpColorPalette.h" #include "widgets/colorSimilarity/kpColorSimilarityToolBarItem.h" #include "kpDefs.h" #include "widgets/kpDualColorButton.h" #include "lgpl/generic/kpUrlFormatter.h" //--------------------------------------------------------------------- kpColorToolBar::kpColorToolBar (const QString &label, QWidget *parent) : QDockWidget (parent) { setWindowTitle (label); // not closable, as it's not a KDE toolbar yet and can not be made shown easily again setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); setAcceptDrops (true); QWidget *base = new QWidget (this); m_boxLayout = new QBoxLayout (QBoxLayout::LeftToRight, base); m_boxLayout->setMargin (5); m_boxLayout->setSpacing (10 * 3); // This holds the current global foreground and background colors, for // tools. m_dualColorButton = new kpDualColorButton (base); connect (m_dualColorButton, &kpDualColorButton::colorsSwapped, this, &kpColorToolBar::colorsSwapped); connect (m_dualColorButton, &kpDualColorButton::foregroundColorChanged, this, &kpColorToolBar::foregroundColorChanged); connect (m_dualColorButton, &kpDualColorButton::backgroundColorChanged, this, &kpColorToolBar::backgroundColorChanged); m_boxLayout->addWidget (m_dualColorButton, 0/*stretch*/, Qt::AlignVCenter); m_colorPalette = new kpColorPalette (base); connect (m_colorPalette, &kpColorPalette::foregroundColorChanged, m_dualColorButton, &kpDualColorButton::setForegroundColor); connect (m_colorPalette, &kpColorPalette::backgroundColorChanged, m_dualColorButton, &kpDualColorButton::setBackgroundColor); connect (m_colorPalette->colorCells (), &kpColorCells::isModifiedChanged, this, &kpColorToolBar::updateNameOrUrlLabel); connect (m_colorPalette->colorCells (), &kpColorCells::urlChanged, this, &kpColorToolBar::updateNameOrUrlLabel); connect (m_colorPalette->colorCells (), &kpColorCells::nameChanged, this, &kpColorToolBar::updateNameOrUrlLabel); updateNameOrUrlLabel (); m_boxLayout->addWidget (m_colorPalette, 0/*stretch*/); m_colorSimilarityToolBarItem = new kpColorSimilarityToolBarItem (base); connect (m_colorSimilarityToolBarItem, &kpColorSimilarityToolBarItem::colorSimilarityChanged, this, &kpColorToolBar::colorSimilarityChanged); m_boxLayout->addWidget (m_colorSimilarityToolBarItem, 0/*stretch*/); // Pad out all the horizontal space on the right of the Color Tool Bar so that // that the real Color Tool Bar widgets aren't placed in the center of the // Color Tool Bar. m_boxLayout->addItem ( new QSpacerItem (1, 1, QSizePolicy::Expanding, QSizePolicy::Preferred)); adjustToOrientation (Qt::Horizontal); setWidget (base); } //--------------------------------------------------------------------- void kpColorToolBar::adjustToOrientation (Qt::Orientation o) { #if DEBUG_KP_COLOR_TOOL_BAR qCDebug(kpLogWidgets) << "kpColorToolBar::adjustToOrientation(" << (o == Qt::Vertical ? "vertical" : "horizontal") << ") called!"; #endif Q_ASSERT (o == Qt::Horizontal); if (o == Qt::Horizontal) { m_boxLayout->setDirection (QBoxLayout::LeftToRight); } else { m_boxLayout->setDirection (QBoxLayout::TopToBottom); } m_colorPalette->setOrientation (o); } //--------------------------------------------------------------------- // public kpColorCells *kpColorToolBar::colorCells () const { return m_colorPalette->colorCells (); } //--------------------------------------------------------------------- kpColor kpColorToolBar::color (int which) const { Q_ASSERT (which == 0 || which == 1); return m_dualColorButton->color (which); } //--------------------------------------------------------------------- void kpColorToolBar::setColor (int which, const kpColor &color) { Q_ASSERT (which == 0 || which == 1); m_dualColorButton->setColor (which, color); } //--------------------------------------------------------------------- kpColor kpColorToolBar::foregroundColor () const { return m_dualColorButton->foregroundColor (); } //--------------------------------------------------------------------- void kpColorToolBar::setForegroundColor (const kpColor &color) { #if DEBUG_KP_COLOR_TOOL_BAR qCDebug(kpLogWidgets) << "kpColorToolBar::setForegroundColor(" << (int *) color.toQRgb () << ")"; #endif m_dualColorButton->setForegroundColor (color); } //--------------------------------------------------------------------- kpColor kpColorToolBar::backgroundColor () const { return m_dualColorButton->backgroundColor (); } //--------------------------------------------------------------------- void kpColorToolBar::setBackgroundColor (const kpColor &color) { #if DEBUG_KP_COLOR_TOOL_BAR qCDebug(kpLogWidgets) << "kpColorToolBar::setBackgroundColor(" << (int *) color.toQRgb () << ")"; #endif m_dualColorButton->setBackgroundColor (color); } //--------------------------------------------------------------------- kpColor kpColorToolBar::oldForegroundColor () const { return m_dualColorButton->oldForegroundColor (); } //--------------------------------------------------------------------- kpColor kpColorToolBar::oldBackgroundColor () const { return m_dualColorButton->oldBackgroundColor (); } //--------------------------------------------------------------------- double kpColorToolBar::oldColorSimilarity () const { return m_colorSimilarityToolBarItem->oldColorSimilarity (); } //--------------------------------------------------------------------- double kpColorToolBar::colorSimilarity () const { return m_colorSimilarityToolBarItem->colorSimilarity (); } //--------------------------------------------------------------------- void kpColorToolBar::setColorSimilarity (double similarity) { m_colorSimilarityToolBarItem->setColorSimilarity (similarity); } //--------------------------------------------------------------------- int kpColorToolBar::processedColorSimilarity () const { return m_colorSimilarityToolBarItem->processedColorSimilarity (); } //--------------------------------------------------------------------- void kpColorToolBar::openColorSimilarityDialog () { m_colorSimilarityToolBarItem->openDialog (); } //--------------------------------------------------------------------- void kpColorToolBar::flashColorSimilarityToolBarItem () { m_colorSimilarityToolBarItem->flash (); } //--------------------------------------------------------------------- // private slot void kpColorToolBar::updateNameOrUrlLabel () { QString name; kpColorCells *colorCells = m_colorPalette->colorCells (); if (!colorCells->url ().isEmpty ()) { name = kpUrlFormatter::PrettyFilename (colorCells->url ()); } else { if (!colorCells->name ().isEmpty ()) { name = colorCells->name (); } else { name = i18n ("KolourPaint Defaults"); } } if (name.isEmpty ()) { name = i18n ("Untitled"); } KLocalizedString labelStr; if (!m_colorPalette->colorCells ()->isModified ()) { labelStr = ki18nc ("Colors: name_or_url_of_color_palette", "Colors: %1") .subs (name); } else { labelStr = ki18nc ("Colors: name_or_url_of_color_palette [modified]", "Colors: %1 [modified]") .subs (name); } // Kill 2 birds with 1 stone: // // 1. Hide the windowTitle() when it's docked. // 2. Add a label containing the name of the open color palette. // // TODO: This currently hides the windowTitle() even when it's not docked, // because we've abused it to show the name of open color palette // instead. setWindowTitle (labelStr.toString ()); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpColorToolBar::dragEnterEvent (QDragEnterEvent *e) { // Grab the color drag for this widget, preventing it from being // handled by our parent, the main window. e->setAccepted (KColorMimeData::canDecode (e->mimeData ())); #if DEBUG_KP_COLOR_TOOL_BAR qCDebug(kpLogWidgets) << "isAccepted=" << e->isAccepted (); #endif } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpColorToolBar::dragMoveEvent (QDragMoveEvent *e) { // Stop the grabbed drag from being dropped. e->setAccepted (!KColorMimeData::canDecode (e->mimeData ())); #if DEBUG_KP_COLOR_TOOL_BAR qCDebug(kpLogWidgets) << "isAccepted=" << e->isAccepted (); #endif } //--------------------------------------------------------------------- diff --git a/widgets/toolbars/kpToolToolBar.cpp b/widgets/toolbars/kpToolToolBar.cpp index 6a584fbd..25f48527 100644 --- a/widgets/toolbars/kpToolToolBar.cpp +++ b/widgets/toolbars/kpToolToolBar.cpp @@ -1,471 +1,471 @@ /* 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_TOOL_BAR 0 +#define DEBUG_KP_TOOL_TOOL_BAR 1 #include "widgets/toolbars/kpToolToolBar.h" #include #include #include #include #include #include "kpLogCategories.h" #include "kpDefs.h" #include "tools/kpTool.h" #include "tools/kpToolAction.h" #include "widgets/toolbars/options/kpToolWidgetBrush.h" #include "widgets/toolbars/options/kpToolWidgetEraserSize.h" #include "widgets/toolbars/options/kpToolWidgetFillStyle.h" #include "widgets/toolbars/options/kpToolWidgetLineWidth.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "widgets/toolbars/options/kpToolWidgetSpraycanSize.h" //--------------------------------------------------------------------- class kpToolButton : public QToolButton { public: kpToolButton (kpTool *tool, QWidget *parent) : QToolButton (parent), m_tool (tool) { } kpTool *tool() const { return m_tool; } protected: void mouseDoubleClickEvent(QMouseEvent *e) override { if (e->button () == Qt::LeftButton && m_tool) { m_tool->globalDraw (); } } kpTool *m_tool; }; //--------------------------------------------------------------------- kpToolToolBar::kpToolToolBar(const QString &name, int colsOrRows, QMainWindow *parent) : KToolBar(name, parent, Qt::LeftToolBarArea), m_vertCols (colsOrRows), m_buttonGroup (nullptr), m_baseWidget (nullptr), m_baseLayout (nullptr), m_toolLayout (nullptr), m_previousTool (nullptr), m_currentTool (nullptr) { m_baseWidget = new QWidget(this); m_toolWidgets.append (m_toolWidgetBrush = new kpToolWidgetBrush (m_baseWidget, QStringLiteral("Tool Widget Brush"))); m_toolWidgets.append (m_toolWidgetEraserSize = new kpToolWidgetEraserSize (m_baseWidget, QStringLiteral("Tool Widget Eraser Size"))); m_toolWidgets.append (m_toolWidgetFillStyle = new kpToolWidgetFillStyle (m_baseWidget, QStringLiteral("Tool Widget Fill Style"))); m_toolWidgets.append (m_toolWidgetLineWidth = new kpToolWidgetLineWidth (m_baseWidget, QStringLiteral("Tool Widget Line Width"))); m_toolWidgets.append (m_toolWidgetOpaqueOrTransparent = new kpToolWidgetOpaqueOrTransparent (m_baseWidget, QStringLiteral("Tool Widget Opaque/Transparent"))); m_toolWidgets.append (m_toolWidgetSpraycanSize = new kpToolWidgetSpraycanSize (m_baseWidget, QStringLiteral("Tool Widget Spraycan Size"))); for (auto *w : m_toolWidgets) { connect (w, &kpToolWidgetBase::optionSelected, this, &kpToolToolBar::toolWidgetOptionSelected); } adjustToOrientation(orientation()); connect (this, &kpToolToolBar::orientationChanged, this, &kpToolToolBar::adjustToOrientation); m_buttonGroup = new QButtonGroup (this); connect (m_buttonGroup, static_cast(&QButtonGroup::buttonClicked), this, &kpToolToolBar::slotToolButtonClicked); hideAllToolWidgets (); addWidget(m_baseWidget); connect (this, &kpToolToolBar::iconSizeChanged, this, &kpToolToolBar::slotIconSizeChanged); connect (this, &kpToolToolBar::toolButtonStyleChanged, this, &kpToolToolBar::slotToolButtonStyleChanged); } //--------------------------------------------------------------------- kpToolToolBar::~kpToolToolBar() { while ( !m_toolButtons.isEmpty() ) { delete m_toolButtons.takeFirst(); } } //--------------------------------------------------------------------- // public void kpToolToolBar::registerTool (kpTool *tool) { for (const auto *b : m_toolButtons) { if ( b->tool() == tool ) { // already given return; } } auto *b = new kpToolButton(tool, m_baseWidget); b->setToolButtonStyle(toolButtonStyle()); b->setIconSize(iconSize()); b->setAutoRaise(true); // tell layout to make all with equal width (much better when text-below-icon) b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); b->setDefaultAction(tool->action()); m_buttonGroup->addButton(b); addButton(b, orientation(), m_toolButtons.count()); m_toolButtons.append(b); connect (tool, &kpTool::actionActivated, this, &kpToolToolBar::slotToolActionActivated); adjustSizeConstraint(); } //--------------------------------------------------------------------- // public void kpToolToolBar::unregisterTool(kpTool *tool) { for (int i = 0; i < m_toolButtons.count(); i++) { if ( m_toolButtons[i]->tool() == tool ) { delete m_toolButtons.takeAt(i); disconnect (tool, &kpTool::actionActivated, this, &kpToolToolBar::slotToolActionActivated); return; } } } //--------------------------------------------------------------------- // public kpTool *kpToolToolBar::tool () const { return m_currentTool; } //--------------------------------------------------------------------- // public void kpToolToolBar::selectTool (const kpTool *tool, bool reselectIfSameTool) { #if DEBUG_KP_TOOL_TOOL_BAR qCDebug(kpLogWidgets) << "kpToolToolBar::selectTool (tool=" << tool << ") currentTool=" << m_currentTool << endl; #endif if (!reselectIfSameTool && tool == m_currentTool) { return; } if (tool) { tool->action()->setChecked(true); slotToolButtonClicked(); } else { QAbstractButton *b = m_buttonGroup->checkedButton(); if (b) { // HACK: qbuttongroup.html says the following about exclusive // button groups: // // "to untoggle a button you must click on another button // in the group" // // But we don't want any button to be selected. // So don't be an exclusive button group temporarily. m_buttonGroup->setExclusive (false); b->setChecked (false); m_buttonGroup->setExclusive (true); slotToolButtonClicked (); } } } //--------------------------------------------------------------------- // public kpTool *kpToolToolBar::previousTool () const { return m_previousTool; } //--------------------------------------------------------------------- // public void kpToolToolBar::selectPreviousTool () { selectTool (m_previousTool); } //--------------------------------------------------------------------- // public void kpToolToolBar::hideAllToolWidgets () { for (auto *w : m_toolWidgets) { w->hide (); } } //--------------------------------------------------------------------- // public kpToolWidgetBase *kpToolToolBar::shownToolWidget (int which) const { int uptoVisibleWidget = 0; for(auto *w : m_toolWidgets) { if ( !w->isHidden() ) { if (which == uptoVisibleWidget) { return w; } uptoVisibleWidget++; } } return nullptr; } //--------------------------------------------------------------------- // private slot void kpToolToolBar::slotToolButtonClicked () { QAbstractButton *b = m_buttonGroup->checkedButton(); #if DEBUG_KP_TOOL_TOOL_BAR qCDebug(kpLogWidgets) << "kpToolToolBar::slotToolButtonClicked() button=" << b; #endif kpTool *tool = nullptr; for (const auto *button : m_toolButtons) { if ( button == b ) { tool = button->tool(); break; } } #if DEBUG_KP_TOOL_TOOL_BAR qCDebug(kpLogWidgets) << "\ttool=" << tool << " currentTool=" << m_currentTool << endl; #endif if (tool == m_currentTool) { if (m_currentTool) { m_currentTool->reselect (); } return; } if (m_currentTool) { m_currentTool->endInternal (); } m_previousTool = m_currentTool; m_currentTool = tool; if (m_currentTool) { kpToolAction *action = m_currentTool->action (); if (action) { action->setChecked (true); } m_currentTool->beginInternal (); } emit sigToolSelected (m_currentTool); m_baseLayout->activate(); adjustSizeConstraint(); } //--------------------------------------------------------------------- // private slot void kpToolToolBar::slotToolActionActivated () { const auto *tool = dynamic_cast(sender()); #if DEBUG_KP_TOOL_TOOL_BAR qCDebug(kpLogWidgets) << "kpToolToolBar::slotToolActionActivated() tool=" << (tool ? tool->objectName () : "null") << endl; #endif selectTool (tool, true/*reselect if same tool*/); } //--------------------------------------------------------------------- // public void kpToolToolBar::adjustToOrientation(Qt::Orientation o) { #if DEBUG_KP_TOOL_TOOL_BAR qCDebug(kpLogWidgets) << "kpToolToolBar::adjustToOrientation(" << (o == Qt::Vertical ? "vertical" : "horizontal") << ") called!" << endl; #endif delete m_baseLayout; if (o == Qt::Vertical) { m_baseLayout = new QBoxLayout (QBoxLayout::TopToBottom, m_baseWidget); } else // if (o == Qt::Horizontal) { m_baseLayout = new QBoxLayout (QBoxLayout::LeftToRight, m_baseWidget); } m_baseLayout->setSizeConstraint(QLayout::SetFixedSize); m_baseLayout->setContentsMargins(0, 0, 0, 0); m_toolLayout = new QGridLayout(); m_toolLayout->setContentsMargins(0, 0, 0, 0); // (ownership is transferred to m_baseLayout) m_baseLayout->addItem (m_toolLayout); auto num = 0; for (auto *b : m_toolButtons) { addButton(b, o, num); num++; } for (auto *w : m_toolWidgets) { m_baseLayout->addWidget(w, 0/*stretch*/, o == Qt::Vertical ? Qt::AlignHCenter : Qt::AlignVCenter); } adjustSizeConstraint(); } //--------------------------------------------------------------------- // this makes the size handled correctly during dragging/undocking the toolbar void kpToolToolBar::adjustSizeConstraint() { // remove constraints setFixedSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); if ( orientation() == Qt::Vertical ) { setFixedWidth(m_baseLayout->sizeHint().width() + layout()->contentsMargins().left() + layout()->contentsMargins().right()); } else { setFixedHeight(m_baseLayout->sizeHint().height() + layout()->contentsMargins().top() + layout()->contentsMargins().bottom()); } } //--------------------------------------------------------------------- // private void kpToolToolBar::addButton(QAbstractButton *button, Qt::Orientation o, int num) { if (o == Qt::Vertical) { m_toolLayout->addWidget (button, num / m_vertCols, num % m_vertCols); } else { // maps Left (o = vertical) to Bottom (o = horizontal) int row = (m_vertCols - 1) - (num % m_vertCols); m_toolLayout->addWidget (button, row, num / m_vertCols); } } //--------------------------------------------------------------------- void kpToolToolBar::slotIconSizeChanged(const QSize &size) { for (auto *b : m_toolButtons) { b->setIconSize(size); } m_baseLayout->activate(); adjustSizeConstraint(); } //--------------------------------------------------------------------- void kpToolToolBar::slotToolButtonStyleChanged(Qt::ToolButtonStyle style) { for (auto *b : m_toolButtons) { b->setToolButtonStyle(style); } m_baseLayout->activate(); adjustSizeConstraint(); } //--------------------------------------------------------------------- diff --git a/widgets/toolbars/options/kpToolWidgetBase.cpp b/widgets/toolbars/options/kpToolWidgetBase.cpp index 2acc6d8c..efab143a 100644 --- a/widgets/toolbars/options/kpToolWidgetBase.cpp +++ b/widgets/toolbars/options/kpToolWidgetBase.cpp @@ -1,754 +1,754 @@ /* 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_WIDGET_BASE 0 +#define DEBUG_KP_TOOL_WIDGET_BASE 1 #include "kpToolWidgetBase.h" #include "kpDefs.h" #include #include #include #include "kpLogCategories.h" #include #include #include #include #include #include #include #include //--------------------------------------------------------------------- kpToolWidgetBase::kpToolWidgetBase (QWidget *parent, const QString &name) : QFrame(parent), m_baseWidget(nullptr), m_selectedRow(-1), m_selectedCol(-1) { setObjectName (name); setFrameStyle (QFrame::Panel | QFrame::Sunken); setFixedSize (44, 66); setSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum); } //--------------------------------------------------------------------- kpToolWidgetBase::~kpToolWidgetBase () = default; //--------------------------------------------------------------------- // public void kpToolWidgetBase::addOption (const QPixmap &pixmap, const QString &toolTip) { if (m_pixmaps.isEmpty ()) { startNewOptionRow (); } m_pixmaps.last ().append (pixmap); m_pixmapRects.last ().append (QRect ()); m_toolTips.last ().append (toolTip); } //--------------------------------------------------------------------- // public void kpToolWidgetBase::startNewOptionRow () { m_pixmaps.append (QList ()); m_pixmapRects.append (QList ()); m_toolTips.append (QList ()); } //--------------------------------------------------------------------- // public void kpToolWidgetBase::finishConstruction (int fallBackRow, int fallBackCol) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::kpToolWidgetBase(fallBack:row=" << fallBackRow << ",col=" << fallBackCol << ")"; #endif relayoutOptions (); // HACK: Undo the maximum half of setFixedSize() in the ctor to avoid // bizarre redraw errors when tool widgets are hidden and others // are shown. // // The reason why we didn't just use setMinimumSize() in the ctor is // because all tool widgets construct pixmaps whose sizes are dependent // on the size() in the ctor, so we needed to get the correct size // in there. This is bad design because it means that tool widgets // can't really be resized. setMaximumSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); const QPair rowColPair = defaultSelectedRowAndCol (); if (!setSelected (rowColPair.first, rowColPair.second, false/*don't save*/)) { if (!setSelected (fallBackRow, fallBackCol)) { if (!setSelected (0, 0)) { qCCritical(kpLogWidgets) << "kpToolWidgetBase::finishConstruction() " "can't even fall back to setSelected(row=0,col=0)"; } } } } //--------------------------------------------------------------------- // private QList kpToolWidgetBase::spreadOutElements (const QList &sizes, int max) { if (sizes.count () == 0) { return {}; } if (sizes.count () == 1) { QList ret; ret.append (sizes.first () > max ? 0 : 1/*margin*/); return ret; } QList retOffsets; for (int i = 0; i < sizes.count (); i++) { retOffsets.append (0); } int totalSize = 0; for (int i = 0; i < sizes.count (); i++) { totalSize += sizes [i]; } int margin = 1; // if don't fit with margin, then just return elements // packed right next to each other if (totalSize + margin * 2 > max) { retOffsets [0] = 0; for (int i = 1; i < sizes.count (); i++) { retOffsets [i] = retOffsets [i - 1] + sizes [i - 1]; } return retOffsets; } int maxLeftOver = max - (totalSize + margin * 2 * sizes.count()); int startCompensating = -1; int numCompensate = 0; int spacing = 0; spacing = maxLeftOver / (sizes.count () - 1); if (spacing * int (sizes.count () - 1) < maxLeftOver) { numCompensate = maxLeftOver - spacing * (sizes.count () - 1); startCompensating = ((sizes.count () - 1) - numCompensate) / 2; } retOffsets [0] = margin; for (int i = 1; i < sizes.count (); i++) { retOffsets [i] += retOffsets [i - 1] + sizes [i - 1] + spacing + ((numCompensate && i >= startCompensating && i < startCompensating + numCompensate) ? 1 : 0); } return retOffsets; } //--------------------------------------------------------------------- // public QPair kpToolWidgetBase::defaultSelectedRowAndCol () const { int row = -1, col = -1; if (!objectName ().isEmpty ()) { KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupTools); row = cfg.readEntry (objectName () + QLatin1String (" Row"), -1); col = cfg.readEntry (objectName () + QLatin1String (" Col"), -1); } #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::defaultSelectedRowAndCol() returning row=" << row << " col=" << col; #endif return qMakePair (row, col); } //--------------------------------------------------------------------- // public int kpToolWidgetBase::defaultSelectedRow () const { return defaultSelectedRowAndCol ().first; } //--------------------------------------------------------------------- // public int kpToolWidgetBase::defaultSelectedCol () const { return defaultSelectedRowAndCol ().second; } //--------------------------------------------------------------------- // public void kpToolWidgetBase::saveSelectedAsDefault () const { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::saveSelectedAsDefault() row=" << m_selectedRow << " col=" << m_selectedCol; #endif if (objectName ().isEmpty ()) { return; } KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupTools); cfg.writeEntry (objectName () + QLatin1String (" Row"), m_selectedRow); cfg.writeEntry (objectName () + QLatin1String (" Col"), m_selectedCol); cfg.sync (); } //--------------------------------------------------------------------- // public void kpToolWidgetBase::relayoutOptions () { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase::relayoutOptions() size=" << size (); #endif while (!m_pixmaps.isEmpty () && m_pixmaps.last ().count () == 0) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tkilling #" << m_pixmaps.count () - 1; #endif m_pixmaps.removeLast (); m_pixmapRects.removeLast (); m_toolTips.removeLast (); } if (m_pixmaps.isEmpty ()) { return; } #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tsurvived killing of empty rows"; qCDebug(kpLogWidgets) << "\tfinding heights of rows:"; #endif QList maxHeightOfRow; for (int r = 0; r < m_pixmaps.count (); r++) { maxHeightOfRow.append (0); } for (int r = 0; r < m_pixmaps.count (); r++) { for (int c = 0; c < m_pixmaps [r].count (); c++) { if (c == 0 || m_pixmaps [r][c].height () > maxHeightOfRow [r]) { maxHeightOfRow [r] = m_pixmaps [r][c].height (); } } #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\t\t" << r << ": " << maxHeightOfRow [r]; #endif } QList rowYOffset = spreadOutElements (maxHeightOfRow, height ()); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tspread out offsets of rows:"; for (int r = 0; r < (int) rowYOffset.count (); r++) { qCDebug(kpLogWidgets) << "\t\t" << r << ": " << rowYOffset [r]; } #endif for (int r = 0; r < m_pixmaps.count (); r++) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tlaying out row " << r << ":"; #endif QList widths; for (int c = 0; c < m_pixmaps [r].count (); c++) widths.append (m_pixmaps [r][c].width ()); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\t\twidths of cols:"; for (int c = 0; c < m_pixmaps [r].count (); c++) { qCDebug(kpLogWidgets) << "\t\t\t" << c << ": " << widths [c]; } #endif QList colXOffset = spreadOutElements (widths, width ()); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\t\tspread out offsets of cols:"; for (int c = 0; c < colXOffset.count (); c++) { qCDebug(kpLogWidgets) << "\t\t\t" << c << ": " << colXOffset [c]; } #endif for (int c = 0; c < colXOffset.count (); c++) { int x = colXOffset [c]; int y = rowYOffset [r]; int w, h; if (c == colXOffset.count () - 1) { if (x + m_pixmaps [r][c].width () >= width ()) { w = m_pixmaps [r][c].width (); } else { w = width () - 1 - x; } } else { w = colXOffset [c + 1] - x; } if (r == m_pixmaps.count () - 1) { if (y + m_pixmaps [r][c].height () >= height ()) { h = m_pixmaps [r][c].height (); } else { h = height () - 1 - y; } } else { h = rowYOffset [r + 1] - y; } m_pixmapRects [r][c] = QRect (x, y, w, h); } } update (); } //--------------------------------------------------------------------- // public int kpToolWidgetBase::selectedRow () const { return m_selectedRow; } //--------------------------------------------------------------------- // public int kpToolWidgetBase::selectedCol () const { return m_selectedCol; } //--------------------------------------------------------------------- // public int kpToolWidgetBase::selected () const { if (m_selectedRow < 0 || m_selectedRow >= m_pixmaps.count () || m_selectedCol < 0) { return -1; } int upto = 0; for (int y = 0; y < m_selectedRow; y++) { upto += m_pixmaps [y].count (); } if (m_selectedCol >= m_pixmaps [m_selectedRow].count ()) { return -1; } upto += m_selectedCol; return upto; } //--------------------------------------------------------------------- // public bool kpToolWidgetBase::hasPreviousOption (int *row, int *col) const { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::hasPreviousOption() current row=" << m_selectedRow << " col=" << m_selectedCol; #endif if (row) { *row = -1; } if (col) { *col = -1; } if (m_selectedRow < 0 || m_selectedCol < 0) { return false; } int newRow = m_selectedRow, newCol = m_selectedCol; newCol--; if (newCol < 0) { newRow--; if (newRow < 0) { return false; } newCol = m_pixmaps [newRow].count () - 1; if (newCol < 0) { return false; } } if (row) { *row = newRow; } if (col) { *col = newCol; } return true; } //--------------------------------------------------------------------- // public bool kpToolWidgetBase::hasNextOption (int *row, int *col) const { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::hasNextOption() current row=" << m_selectedRow << " col=" << m_selectedCol; #endif if (row) { *row = -1; } if (col) { *col = -1; } if (m_selectedRow < 0 || m_selectedCol < 0) { return false; } int newRow = m_selectedRow, newCol = m_selectedCol; newCol++; if (newCol >= m_pixmaps [newRow].count ()) { newRow++; if (newRow >= m_pixmaps.count ()) { return false; } newCol = 0; if (newCol >= m_pixmaps [newRow].count ()) { return false; } } if (row) { *row = newRow; } if (col) { *col = newCol; } return true; } //--------------------------------------------------------------------- // public slot virtual bool kpToolWidgetBase::setSelected (int row, int col, bool saveAsDefault) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase::setSelected(row=" << row << ",col=" << col << ",saveAsDefault=" << saveAsDefault << ")"; #endif if (row < 0 || col < 0 || row >= m_pixmapRects.count () || col >= m_pixmapRects [row].count ()) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tout of range"; #endif return false; } if (row == m_selectedRow && col == m_selectedCol) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tNOP"; #endif if (saveAsDefault) { saveSelectedAsDefault (); } return true; } const int wasSelectedRow = m_selectedRow; const int wasSelectedCol = m_selectedCol; m_selectedRow = row; m_selectedCol = col; if (wasSelectedRow >= 0 && wasSelectedCol >= 0) { // unhighlight old option update (m_pixmapRects [wasSelectedRow][wasSelectedCol]); } // highlight new option update (m_pixmapRects [row][col]); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tOK"; #endif if (saveAsDefault) { saveSelectedAsDefault (); } emit optionSelected (row, col); return true; } //--------------------------------------------------------------------- // public slot bool kpToolWidgetBase::setSelected (int row, int col) { return setSelected (row, col, true/*set as default*/); } //--------------------------------------------------------------------- // public slot bool kpToolWidgetBase::selectPreviousOption () { int newRow, newCol; if (!hasPreviousOption (&newRow, &newCol)) { return false; } return setSelected (newRow, newCol); } //--------------------------------------------------------------------- // public slot bool kpToolWidgetBase::selectNextOption () { int newRow, newCol; if (!hasNextOption (&newRow, &newCol)) { return false; } return setSelected (newRow, newCol); } //--------------------------------------------------------------------- // protected virtual [base QWidget] bool kpToolWidgetBase::event (QEvent *e) { // TODO: It's unclear when we should call the base, call accept() and // return true or false. Look at other event() handlers. The // kpToolText one is wrong since after calling accept(), it calls // its base which calls ignore() :) if (e->type () == QEvent::ToolTip) { auto *he = dynamic_cast (e); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase::event() QHelpEvent pos=" << he->pos (); #endif bool showedText = false; for (int r = 0; r < m_pixmapRects.count (); r++) { for (int c = 0; c < m_pixmapRects [r].count (); c++) { if (m_pixmapRects [r][c].contains (he->pos ())) { const QString tip = m_toolTips [r][c]; #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tin option: r=" << r << "c=" << c << "tip='" << tip << "'"; #endif if (!tip.isEmpty ()) { QToolTip::showText (he->globalPos (), tip, this); showedText = true; } e->accept (); goto exit_loops; } } } exit_loops: if (!showedText) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\thiding text"; #endif QToolTip::hideText (); } return true; } return QWidget::event (e); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpToolWidgetBase::mousePressEvent (QMouseEvent *e) { e->ignore (); if (e->button () != Qt::LeftButton) { return; } for (int i = 0; i < m_pixmapRects.count (); i++) { for (int j = 0; j < m_pixmapRects [i].count (); j++) { if (m_pixmapRects [i][j].contains (e->pos ())) { setSelected (i, j); e->accept (); return; } } } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpToolWidgetBase::paintEvent (QPaintEvent *e) { #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "kpToolWidgetBase::paintEvent(): rect=" << contentsRect (); #endif // Draw frame first. QFrame::paintEvent (e); QPainter painter (this); for (int i = 0; i < m_pixmaps.count (); i++) { #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "\tRow: " << i; #endif for (int j = 0; j < m_pixmaps [i].count (); j++) { QRect rect = m_pixmapRects [i][j]; QPixmap pixmap = m_pixmaps [i][j]; #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "\t\tCol: " << j << " rect=" << rect; #endif if (i == m_selectedRow && j == m_selectedCol) { painter.fillRect(rect, palette().color(QPalette::Highlight).rgb()); } #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "\t\t\tdraw pixmap @ x=" << rect.x () + (rect.width () - pixmap.width ()) / 2 << " y=" << rect.y () + (rect.height () - pixmap.height ()) / 2; #endif painter.drawPixmap(QPoint(rect.x () + (rect.width () - pixmap.width ()) / 2, rect.y () + (rect.height () - pixmap.height ()) / 2), pixmap); } } } //--------------------------------------------------------------------- diff --git a/widgets/toolbars/options/kpToolWidgetBrush.cpp b/widgets/toolbars/options/kpToolWidgetBrush.cpp index 5dbabbb4..b37ebd18 100644 --- a/widgets/toolbars/options/kpToolWidgetBrush.cpp +++ b/widgets/toolbars/options/kpToolWidgetBrush.cpp @@ -1,297 +1,297 @@ /* 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_WIDGET_BRUSH 0 +#define DEBUG_KP_TOOL_WIDGET_BRUSH 1 #include "widgets/toolbars/options/kpToolWidgetBrush.h" #include #include #include #include "kpLogCategories.h" #include "kpDefs.h" //--------------------------------------------------------------------- // LOREFACTOR: more OO, no arrays (use safer structs). /* sync: */ static int BrushSizes [][3] = { {8, 4, 1/*like Pen*/}, {9, 5, 2}, {9, 5, 2}, {9, 5, 2} }; #define BRUSH_SIZE_NUM_COLS (int (sizeof (::BrushSizes [0]) / sizeof (::BrushSizes [0][0]))) #define BRUSH_SIZE_NUM_ROWS (int (sizeof (::BrushSizes) / sizeof (::BrushSizes [0]))) //--------------------------------------------------------------------- static void Draw (kpImage *destImage, const QPoint &topLeft, void *userData) { auto *pack = static_cast (userData); #if DEBUG_KP_TOOL_WIDGET_BRUSH qCDebug(kpLogWidgets) << "kptoolwidgetbrush.cpp:Draw(destImage,topLeft=" << topLeft << " pack: row=" << pack->row << " col=" << pack->col << " color=" << (int *) pack->color.toQRgb (); #endif const int size = ::BrushSizes [pack->row][pack->col]; #if DEBUG_KP_TOOL_WIDGET_BRUSH qCDebug(kpLogWidgets) << "\tsize=" << size; #endif QPainter painter(destImage); if ( size == 1 ) { painter.setPen(pack->color.toQColor()); painter.drawPoint(topLeft); return; } // sync: switch (pack->row/*shape*/) { case 0: { // work around ugly circle when using QPainter on QImage if ( size == 4 ) { // do not draw a pixel twice, as with an alpha color it will become darker painter.setPen(Qt::NoPen); painter.setBrush(pack->color.toQColor()); painter.drawRect(topLeft.x() + 1, topLeft.y(), 2, size); painter.setPen(pack->color.toQColor()); painter.drawLine(topLeft.x(), topLeft.y() + 1, topLeft.x(), topLeft.y() + 2); painter.drawLine(topLeft.x() + 3, topLeft.y() + 1, topLeft.x() + 3, topLeft.y() + 2); } else if ( size == 8 ) // size defined in BrushSizes above { // do not draw a pixel twice, as with an alpha color it will become darker painter.setPen(Qt::NoPen); painter.setBrush(pack->color.toQColor()); painter.drawRect(topLeft.x() + 2, topLeft.y(), 4, size); painter.drawRect(topLeft.x(), topLeft.y() + 2, 2, 4); painter.drawRect(topLeft.x() + 6, topLeft.y() + 2, 2, 4); painter.setPen(pack->color.toQColor()); painter.drawPoint(topLeft.x() + 1, topLeft.y() + 1); painter.drawPoint(topLeft.x() + 6, topLeft.y() + 1); painter.drawPoint(topLeft.x() + 1, topLeft.y() + 6); painter.drawPoint(topLeft.x() + 6, topLeft.y() + 6); } else { Q_ASSERT(!"illegal size"); } break; } case 1: { // only paint filling so that a color with an alpha channel does not // create a darker border due to drawing some pixels twice with composition painter.setPen(Qt::NoPen); painter.setBrush(pack->color.toQColor()); painter.drawRect(topLeft.x(), topLeft.y(), size, size); break; } case 2: { painter.setPen(pack->color.toQColor()); painter.drawLine(topLeft.x() + size - 1, topLeft.y(), topLeft.x(), topLeft.y() + size - 1); break; } case 3: { painter.setPen(pack->color.toQColor()); painter.drawLine(topLeft.x(), topLeft.y(), topLeft.x() + size - 1, topLeft.y() + size - 1); break; } default: Q_ASSERT (!"Unknown row"); break; } } //--------------------------------------------------------------------- kpToolWidgetBrush::kpToolWidgetBrush (QWidget *parent, const QString &name) : kpToolWidgetBase (parent, name) { for (int shape = 0; shape < BRUSH_SIZE_NUM_ROWS; shape++) { for (int i = 0; i < BRUSH_SIZE_NUM_COLS; i++) { const int s = ::BrushSizes [shape][i]; const int w = (width () - 2/*margin*/ - 2/*spacing*/) / BRUSH_SIZE_NUM_COLS; const int h = (height () - 2/*margin*/ - 3/*spacing*/) / BRUSH_SIZE_NUM_ROWS; Q_ASSERT (w >= s && h >= s); QImage previewPixmap (w, h, QImage::Format_ARGB32_Premultiplied); previewPixmap.fill(0); DrawPackage pack = drawFunctionDataForRowCol (kpColor::Black, shape, i); ::Draw (&previewPixmap, QPoint ((previewPixmap.width () - s) / 2, (previewPixmap.height () - s) / 2), &pack); addOption (QPixmap::fromImage(previewPixmap), brushName (shape, i)/*tooltip*/); } startNewOptionRow (); } finishConstruction (0, 0); } //--------------------------------------------------------------------- kpToolWidgetBrush::~kpToolWidgetBrush () = default; //--------------------------------------------------------------------- // private QString kpToolWidgetBrush::brushName (int shape, int whichSize) const { int s = ::BrushSizes [shape][whichSize]; if (s == 1) { return i18n ("1x1"); } QString shapeName; // sync: switch (shape) { case 0: shapeName = i18n ("Circle"); break; case 1: shapeName = i18n ("Square"); break; case 2: // TODO: is this really the name of a shape? :) shapeName = i18n ("Slash"); break; case 3: // TODO: is this really the name of a shape? :) shapeName = i18n ("Backslash"); break; } if (shapeName.isEmpty ()) { return {}; } return i18n ("%1x%2 %3", s, s, shapeName); } //--------------------------------------------------------------------- // public int kpToolWidgetBrush::brushSize () const { return ::BrushSizes [selectedRow ()][selectedCol ()]; } //--------------------------------------------------------------------- // public bool kpToolWidgetBrush::brushIsDiagonalLine () const { // sync: return (selectedRow () >= 2); } //--------------------------------------------------------------------- // public kpTempImage::UserFunctionType kpToolWidgetBrush::drawFunction () const { return &::Draw; } //--------------------------------------------------------------------- // public static kpToolWidgetBrush::DrawPackage kpToolWidgetBrush::drawFunctionDataForRowCol ( const kpColor &color, int row, int col) { Q_ASSERT (row >= 0 && col >= 0); DrawPackage pack; pack.row = row; pack.col = col; pack.color = color; return pack; } //--------------------------------------------------------------------- // public kpToolWidgetBrush::DrawPackage kpToolWidgetBrush::drawFunctionData ( const kpColor &color) const { return drawFunctionDataForRowCol (color, selectedRow (), selectedCol ()); } //--------------------------------------------------------------------- // protected slot virtual [base kpToolWidgetBase] bool kpToolWidgetBrush::setSelected (int row, int col, bool saveAsDefault) { const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault); if (ret) { emit brushChanged (); } return ret; } //--------------------------------------------------------------------- diff --git a/widgets/toolbars/options/kpToolWidgetEraserSize.cpp b/widgets/toolbars/options/kpToolWidgetEraserSize.cpp index 0304dbac..4c4be16e 100644 --- a/widgets/toolbars/options/kpToolWidgetEraserSize.cpp +++ b/widgets/toolbars/options/kpToolWidgetEraserSize.cpp @@ -1,185 +1,185 @@ /* 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_WIDGET_ERASER_SIZE 0 +#define DEBUG_KP_TOOL_WIDGET_ERASER_SIZE 1 #include "kpToolWidgetEraserSize.h" #include "imagelib/kpColor.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "kpLogCategories.h" #include #include #include #include static int EraserSizes [] = {2, 3, 5, 9, 17, 29}; static const int NumEraserSizes = int (sizeof (::EraserSizes) / sizeof (::EraserSizes [0])); static void DrawImage (kpImage *destImage, const QPoint &topLeft, void *userData) { auto *pack = static_cast (userData); const int size = ::EraserSizes [pack->selected]; kpPainter::fillRect (destImage, topLeft.x (), topLeft.y (), size, size, pack->color); } static void DrawCursor (kpImage *destImage, const QPoint &topLeft, void *userData) { ::DrawImage (destImage, topLeft, userData); auto *pack = static_cast (userData); const int size = ::EraserSizes [pack->selected]; // Would 1-pixel border on all sides completely cover the color of the // eraser? if (size <= 2) { return; } // Draw 1-pixel border on all sides. QPainter painter(destImage); painter.drawRect(topLeft.x(), topLeft.y(), size - 1, size - 1); } //--------------------------------------------------------------------- kpToolWidgetEraserSize::kpToolWidgetEraserSize (QWidget *parent, const QString &name) : kpToolWidgetBase (parent, name) { for (int i = 0; i < ::NumEraserSizes; i++) { if (i == 3 || i == 5) { startNewOptionRow (); } const int s = ::EraserSizes [i]; QImage previewPixmap (s, s, QImage::Format_ARGB32_Premultiplied); if (i < 3) { // HACK: kpToolWidgetBase's layout code sucks and gives uneven spacing previewPixmap = QImage ((width () - 4) / 3, 9, QImage::Format_ARGB32_Premultiplied); Q_ASSERT (previewPixmap.width () >= s && previewPixmap.height () >= s); } previewPixmap.fill(0); DrawPackage pack = drawFunctionDataForSelected (kpColor::Black, i); ::DrawImage (&previewPixmap, QPoint ((previewPixmap.width () - s) / 2, (previewPixmap.height () - s) / 2), &pack); addOption (QPixmap::fromImage(previewPixmap), i18n ("%1x%2", s, s)/*tooltip*/); } finishConstruction (1, 0); } //--------------------------------------------------------------------- kpToolWidgetEraserSize::~kpToolWidgetEraserSize () = default; //--------------------------------------------------------------------- // public int kpToolWidgetEraserSize::eraserSize () const { return ::EraserSizes[selected() < 0 ? 0 : selected()]; } // public kpTempImage::UserFunctionType kpToolWidgetEraserSize::drawFunction () const { return &::DrawImage; } // public kpTempImage::UserFunctionType kpToolWidgetEraserSize::drawCursorFunction () const { return &::DrawCursor; } //--------------------------------------------------------------------- // public static kpToolWidgetEraserSize::DrawPackage kpToolWidgetEraserSize::drawFunctionDataForSelected ( const kpColor &color, int selectedIndex) { DrawPackage pack; pack.selected = selectedIndex; pack.color = color; return pack; } //--------------------------------------------------------------------- // public kpToolWidgetEraserSize::DrawPackage kpToolWidgetEraserSize::drawFunctionData ( const kpColor &color) const { return drawFunctionDataForSelected (color, selected ()); } //--------------------------------------------------------------------- // protected slot virtual [base kpToolWidgetBase] bool kpToolWidgetEraserSize::setSelected (int row, int col, bool saveAsDefault) { const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault); if (ret) { emit eraserSizeChanged (eraserSize ()); } return ret; } //--------------------------------------------------------------------- diff --git a/widgets/toolbars/options/kpToolWidgetFillStyle.cpp b/widgets/toolbars/options/kpToolWidgetFillStyle.cpp index c442d74a..0374cace 100644 --- a/widgets/toolbars/options/kpToolWidgetFillStyle.cpp +++ b/widgets/toolbars/options/kpToolWidgetFillStyle.cpp @@ -1,175 +1,175 @@ /* 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_WIDGET_FILL_STYLE 0 +#define DEBUG_KP_TOOL_WIDGET_FILL_STYLE 1 #include "kpToolWidgetFillStyle.h" #include "imagelib/kpColor.h" #include "kpDefs.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "kpLogCategories.h" #include #include #include //--------------------------------------------------------------------- kpToolWidgetFillStyle::kpToolWidgetFillStyle (QWidget *parent, const QString &name) : kpToolWidgetBase (parent, name) { for (int i = 0; i < FillStyleNum; i++) { QPixmap pixmap; pixmap = fillStylePixmap (static_cast (i), (width () - 2/*margin*/) * 3 / 4, (height () - 2/*margin*/ - 2/*spacing*/) * 3 / (3 * 4)); addOption (pixmap, fillStyleName (static_cast (i))/*tooltip*/); startNewOptionRow (); } finishConstruction (0, 0); } //--------------------------------------------------------------------- kpToolWidgetFillStyle::~kpToolWidgetFillStyle () = default; //--------------------------------------------------------------------- // private QPixmap kpToolWidgetFillStyle::fillStylePixmap (FillStyle fs, int w, int h) { QPixmap pixmap ((w <= 0 ? width () : w), (h <= 0 ? height () : h)); pixmap.fill(palette().color(QPalette::Window)); const int penWidth = 2; const QRect rectRect(1, 1, w - 2, h - 2); QPainter painter(&pixmap); painter.setPen(kpPixmapFX::QPainterDrawRectPen(Qt::black, penWidth)); switch ( fs ) { case NoFill: { painter.setBrush(Qt::NoBrush); break; } case FillWithBackground: { painter.setBrush(Qt::gray); break; } case FillWithForeground: { painter.setBrush(Qt::black); break; } default: ; } painter.drawRect(rectRect); painter.end(); return pixmap; } //--------------------------------------------------------------------- // private QString kpToolWidgetFillStyle::fillStyleName (FillStyle fs) const { switch (fs) { case NoFill: return i18n ("No Fill"); case FillWithBackground: return i18n ("Fill with Background Color"); case FillWithForeground: return i18n ("Fill with Foreground Color"); default: return {}; } } //--------------------------------------------------------------------- // public kpToolWidgetFillStyle::FillStyle kpToolWidgetFillStyle::fillStyle () const { #if DEBUG_KP_TOOL_WIDGET_FILL_STYLE qCDebug(kpLogWidgets) << "kpToolWidgetFillStyle::fillStyle() selected=" << selectedRow (); #endif return static_cast (selectedRow ()); } //--------------------------------------------------------------------- kpColor kpToolWidgetFillStyle::drawingBackgroundColor ( const kpColor &foregroundColor, const kpColor &backgroundColor) const { switch (fillStyle ()) { default: case NoFill: return kpColor::Invalid; case FillWithBackground: return backgroundColor; case FillWithForeground: return foregroundColor; } } //--------------------------------------------------------------------- // virtual protected slot [base kpToolWidgetBase] bool kpToolWidgetFillStyle::setSelected (int row, int col, bool saveAsDefault) { const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault); if (ret) { emit fillStyleChanged (fillStyle ()); } return ret; } //--------------------------------------------------------------------- diff --git a/widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.cpp b/widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.cpp index 500353f5..44a9417c 100644 --- a/widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.cpp +++ b/widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.cpp @@ -1,101 +1,101 @@ /* 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_WIDGET_OPAQUE_OR_TRANSPARENT 0 +#define DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT 1 #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "kpLogCategories.h" #include //--------------------------------------------------------------------- kpToolWidgetOpaqueOrTransparent::kpToolWidgetOpaqueOrTransparent (QWidget *parent, const QString &name) : kpToolWidgetBase (parent, name) { addOption (QStringLiteral(":/icons/option_opaque"), i18n ("Opaque")/*tooltip*/); startNewOptionRow (); addOption (QStringLiteral(":/icons/option_transparent"), i18n ("Transparent")/*tooltip*/); finishConstruction (0, 0); } //--------------------------------------------------------------------- kpToolWidgetOpaqueOrTransparent::~kpToolWidgetOpaqueOrTransparent () = default; //--------------------------------------------------------------------- // public bool kpToolWidgetOpaqueOrTransparent::isOpaque () const { return (selected () == 0); } // public bool kpToolWidgetOpaqueOrTransparent::isTransparent () const { return (!isOpaque ()); } // public void kpToolWidgetOpaqueOrTransparent::setOpaque (bool yes) { #if DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT && 1 qCDebug(kpLogWidgets) << "kpToolWidgetOpaqueOrTransparent::setOpaque(" << yes << ")"; #endif setSelected (yes ? 0 : 1, 0, false/*don't save*/); } // public void kpToolWidgetOpaqueOrTransparent::setTransparent (bool yes) { #if DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT && 1 qCDebug(kpLogWidgets) << "kpToolWidgetOpaqueOrTransparent::setTransparent(" << yes << ")"; #endif setSelected (yes ? 1 : 0, 0, false/*don't save*/); } // protected slot virtual [base kpToolWidgetBase] bool kpToolWidgetOpaqueOrTransparent::setSelected (int row, int col, bool saveAsDefault) { #if DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT && 1 qCDebug(kpLogWidgets) << "kpToolWidgetOpaqueOrTransparent::setSelected(" << row << "," << col << ")"; #endif const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault); if (ret) { emit isOpaqueChanged (isOpaque ()); } return ret; } diff --git a/widgets/toolbars/options/kpToolWidgetSpraycanSize.cpp b/widgets/toolbars/options/kpToolWidgetSpraycanSize.cpp index e744ffa1..a6c9cd01 100644 --- a/widgets/toolbars/options/kpToolWidgetSpraycanSize.cpp +++ b/widgets/toolbars/options/kpToolWidgetSpraycanSize.cpp @@ -1,119 +1,119 @@ /* 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_WIDGET_SPRAYCAN_SIZE 0 +#define DEBUG_KP_TOOL_WIDGET_SPRAYCAN_SIZE 1 #include "kpToolWidgetSpraycanSize.h" #include "pixmapfx/kpPixmapFX.h" #include "kpLogCategories.h" #include #include #include #include #include static int spraycanSizes [] = {9, 17, 29}; kpToolWidgetSpraycanSize::kpToolWidgetSpraycanSize (QWidget *parent, const QString &name) : kpToolWidgetBase (parent, name) { #if DEBUG_KP_TOOL_WIDGET_SPRAYCAN_SIZE qCDebug(kpLogWidgets) << "kpToolWidgetSpraycanSize::kpToolWidgetSpraycanSize() CALLED!"; #endif for (int i = 0; i < int (sizeof (spraycanSizes) / sizeof (spraycanSizes [0])); i++) { int s = spraycanSizes [i]; const QString iconName = QStringLiteral (":/icons/tool_spraycan_%1x%2").arg (s).arg(s); #if DEBUG_KP_TOOL_WIDGET_SPRAYCAN_SIZE qCDebug(kpLogWidgets) << "\ticonName=" << iconName; #endif QPixmap pixmap (s, s); pixmap.fill (Qt::white); QPainter painter (&pixmap); painter.drawPixmap (0, 0, QPixmap (iconName)); painter.end (); QImage image = pixmap.toImage(); QBitmap mask (pixmap.width (), pixmap.height ()); mask.fill (Qt::color0); painter.begin (&mask); painter.setPen (Qt::color1); for (int y = 0; y < image.height (); y++) { for (int x = 0; x < image.width (); x++) { if ((image.pixel (x, y) & RGB_MASK) == 0/*black*/) { painter.drawPoint (x, y); // mark as opaque } } } painter.end (); pixmap.setMask (mask); addOption (pixmap, i18n ("%1x%2", s, s)/*tooltip*/); if (i == 1) { startNewOptionRow (); } } finishConstruction (0, 0); } kpToolWidgetSpraycanSize::~kpToolWidgetSpraycanSize () = default; // public int kpToolWidgetSpraycanSize::spraycanSize () const { return spraycanSizes[selected() < 0 ? 0 : selected()]; } // protected slot virtual [base kpToolWidgetBase] bool kpToolWidgetSpraycanSize::setSelected (int row, int col, bool saveAsDefault) { const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault); if (ret) { emit spraycanSizeChanged (spraycanSize ()); } return ret; }