diff --git a/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp b/commands/imagelib/effects/kpEffectBlurSharpenCommand.cpp index 9e35845f..58a18842 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 #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 QString(); + 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/kpEffectReduceColorsCommand.cpp b/commands/imagelib/effects/kpEffectReduceColorsCommand.cpp index 600e6621..d4977d66 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 #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 QString(); + 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/kpTransformFlipCommand.cpp b/commands/imagelib/transforms/kpTransformFlipCommand.cpp index fc4553f2..a086d293 100644 --- a/commands/imagelib/transforms/kpTransformFlipCommand.cpp +++ b/commands/imagelib/transforms/kpTransformFlipCommand.cpp @@ -1,135 +1,135 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "kpTransformFlipCommand.h" #include #include "kpLogCategories.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include //--------------------------------------------------------------------- kpTransformFlipCommand::kpTransformFlipCommand (bool actOnSelection, bool horiz, bool vert, kpCommandEnvironment *environ) : kpCommand (environ), m_actOnSelection (actOnSelection), m_horiz (horiz), m_vert (vert) { } //--------------------------------------------------------------------- kpTransformFlipCommand::~kpTransformFlipCommand () = default; //--------------------------------------------------------------------- // public virtual [base kpCommand] QString kpTransformFlipCommand::name () const { QString opName; #if 1 opName = i18n ("Flip"); #else // re-enable when giving full descriptions for all actions if (m_horiz && m_vert) opName = i18n ("Flip horizontally and vertically"); else if (m_horiz) opName = i18n ("Flip horizontally"); else if (m_vert) opName = i18n ("Flip vertically"); else { qCCritical(kpLogCommands) << "kpTransformFlipCommand::name() not asked to flip"; - return QString(); + return {}; } #endif if (m_actOnSelection) { return i18n ("Selection: %1", opName); } return opName; } //--------------------------------------------------------------------- // public virtual [base kpCommand] kpCommandSize::SizeType kpTransformFlipCommand::size () const { return 0; } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpTransformFlipCommand::execute () { flip (); } //--------------------------------------------------------------------- // public virtual [base kpCommand] void kpTransformFlipCommand::unexecute () { flip (); } //--------------------------------------------------------------------- // private void kpTransformFlipCommand::flip () { kpDocument *doc = document (); Q_ASSERT (doc); QApplication::setOverrideCursor (Qt::WaitCursor); if (m_actOnSelection) { Q_ASSERT (doc->imageSelection ()); doc->imageSelection ()->flip (m_horiz, m_vert); environ ()->somethingBelowTheCursorChanged (); } else { doc->setImage(doc->image().mirrored(m_horiz, m_vert)); } QApplication::restoreOverrideCursor (); } diff --git a/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp b/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp index cd2dcb52..bb6a5b93 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 #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 QString (); + 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 accidently? 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/document/kpDocumentSaveOptions.cpp b/document/kpDocumentSaveOptions.cpp index 587e7a37..3a249c0b 100644 --- a/document/kpDocumentSaveOptions.cpp +++ b/document/kpDocumentSaveOptions.cpp @@ -1,622 +1,622 @@ /* 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 #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) { d->m_mimeType = mimeType; } //--------------------------------------------------------------------- // public static QString kpDocumentSaveOptions::invalidMimeType () { - return QString(); + 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 8155a62c..137c2d3c 100644 --- a/document/kpDocument_Open.cpp +++ b/document/kpDocument_Open.cpp @@ -1,243 +1,243 @@ /* 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 #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 "kpLogCategories.h" #include // kdelibs4support #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 (); } QString tempFile; if (url.isEmpty () || !KIO::NetAccess::download (url, tempFile, parent)) { 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 QImage (); + return {}; } QMimeDatabase db; QMimeType mimeType = db.mimeTypeForFile(tempFile); if (saveOptions) { saveOptions->setMimeType(mimeType.name()); } #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "\ttempFile=" << tempFile; qCDebug(kpLogDocument) << "\tmimetype=" << mimeType.name(); qCDebug(kpLogDocument) << "\tsrc=" << url.path (); #endif QImageReader reader(tempFile); reader.setAutoTransform(true); reader.setDecideFormatFromContent(true); QImage image = reader.read(); KIO::NetAccess::removeTempFile(tempFile); if (image.isNull ()) { KMessageBox::sorry (parent, i18n ("Could not open \"%1\" - unsupported image format.\n" "The file may be corrupt.", kpUrlFormatter::PrettyFilename (url))); - return QImage (); + 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 (!url.isEmpty () && // not just a permission error? !KIO::NetAccess::exists (url, KIO::NetAccess::SourceSide/*open*/, d->environ->dialogParent ())) { openNew (url); } else { openNew (QUrl ()); } return true; } return false; } //--------------------------------------------------------------------- diff --git a/imagelib/transforms/kpTransformCrop_ImageSelection.cpp b/imagelib/transforms/kpTransformCrop_ImageSelection.cpp index 795e6885..7a527e73 100644 --- a/imagelib/transforms/kpTransformCrop_ImageSelection.cpp +++ b/imagelib/transforms/kpTransformCrop_ImageSelection.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_CROP 0 #include "kpTransformCrop.h" #include "kpTransformCropPrivate.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 QString(); } + 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/lgpl/generic/kpColorCollection.cpp b/lgpl/generic/kpColorCollection.cpp index 2aff094d..c06d43b1 100644 --- a/lgpl/generic/kpColorCollection.cpp +++ b/lgpl/generic/kpColorCollection.cpp @@ -1,535 +1,535 @@ // 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 #include "kpColorCollection.h" #include "kpUrlFormatter.h" #include // kdelibs4support #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; }; //--------------------------------------------------------------------- //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) { QString tempPaletteFilePath; if (url.isEmpty () || !KIO::NetAccess::download (url, tempPaletteFilePath, parent)) { #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogMisc) << "\tcould not download"; #endif ::CouldNotOpenDialog (url, parent); return false; } // sync: remember to "KIO::NetAccess::removeTempFile (tempPaletteFilePath)" in all exit paths QFile paletteFile(tempPaletteFilePath); if (!paletteFile.exists() || !paletteFile.open(QIODevice::ReadOnly)) { #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogMisc) << "\tcould not open qfile"; #endif KIO::NetAccess::removeTempFile (tempPaletteFilePath); ::CouldNotOpenDialog (url, parent); return false; } // Read first line // Expected "GIMP Palette" QString line = QString::fromLocal8Bit(paletteFile.readLine()); if (line.indexOf(QLatin1String(" Palette")) == -1) { KIO::NetAccess::removeTempFile (tempPaletteFilePath); 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( !paletteFile.atEnd() ) { line = QString::fromLocal8Bit(paletteFile.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; KIO::NetAccess::removeTempFile (tempPaletteFilePath); 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(kpLogMisc) << "name=" << name; #endif if (name.isEmpty()) { #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogMisc) << "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(kpLogMisc) << "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(kpLogMisc) << "could not open"; #endif return false; } d->name = name; #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogMisc) << "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(QStringLiteral("\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, bool showOverwritePrompt, QWidget *parent) const { if (showOverwritePrompt && KIO::NetAccess::exists (url, KIO::NetAccess::DestinationSide/*write*/, parent)) { int result = KMessageBox::warningContinueCancel (parent, i18n ("A color palette called \"%1\" already exists.\n" "Do you want to overwrite it?", kpUrlFormatter::PrettyFilename (url)), QString (), KStandardGuiItem::overwrite ()); if (result != KMessageBox::Continue) return false; } 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(kpLogMisc) << "\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(kpLogMisc) << "\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(kpLogMisc) << "\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(kpLogMisc) << "\ttempFileName='" << tempFileName << "'"; #endif Q_ASSERT (!tempFileName.isEmpty ()); tempFile.close (); if (tempFile.error () != QFile::NoError) { #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogMisc) << "\treturning false because could not close"; #endif ::CouldNotSaveDialog (url, parent); return false; } // Copy local temporary file to overwrite remote. // TODO: No one seems to know how to do this atomically // [http://lists.kde.org/?l=kde-core-devel&m=117845162728484&w=2]. // At least, fish:// (ssh) is definitely not atomic. if (!KIO::NetAccess::upload (tempFileName, url, parent)) { #if DEBUG_KP_COLOR_COLLECTION qCDebug(kpLogMisc) << "\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), false/*no overwite prompt*/, 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 QColor(); + 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 QString(); + 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/mainWindow/kpMainWindow_File.cpp b/mainWindow/kpMainWindow_File.cpp index 92c1a625..e54f7af0 100644 --- a/mainWindow/kpMainWindow_File.cpp +++ b/mainWindow/kpMainWindow_File.cpp @@ -1,1524 +1,1524 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2007 John Layt Copyright (c) 2007,2011,2015 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. */ #include "kpMainWindow.h" #include "kpMainWindowPrivate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // kdelibs4support #include #include // kdelibs4support #include #include #include #include #include #include #include // kdelibs4support #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "commands/imagelib/kpDocumentMetaInfoCommand.h" #include "dialogs/imagelib/kpDocumentMetaInfoDialog.h" #include "widgets/kpDocumentSaveOptionsWidget.h" #include "pixmapfx/kpPixmapFX.h" #include "widgets/kpPrintDialogPage.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #if HAVE_KSANE #include "../scan/sanedialog.h" #endif // HAVE_KSANE // private void kpMainWindow::setupFileMenuActions () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::setupFileMenuActions()"; #endif KActionCollection *ac = actionCollection (); d->actionNew = KStandardAction::openNew (this, SLOT (slotNew()), ac); d->actionOpen = KStandardAction::open (this, SLOT (slotOpen()), ac); d->actionOpenRecent = KStandardAction::openRecent(this, &kpMainWindow::slotOpenRecent, ac); connect(d->actionOpenRecent, &KRecentFilesAction::recentListCleared, this, &kpMainWindow::slotRecentListCleared); d->actionOpenRecent->loadEntries (KSharedConfig::openConfig ()->group (kpSettingsGroupRecentFiles)); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\trecent URLs=" << d->actionOpenRecent->items (); #endif d->actionSave = KStandardAction::save (this, SLOT (slotSave()), ac); d->actionSaveAs = KStandardAction::saveAs (this, SLOT (slotSaveAs()), ac); d->actionExport = ac->addAction(QStringLiteral("file_export")); d->actionExport->setText (i18n ("E&xport...")); d->actionExport->setIcon(KDE::icon(QStringLiteral("document-export"))); connect (d->actionExport, &QAction::triggered, this, &kpMainWindow::slotExport); d->actionScan = ac->addAction(QStringLiteral("file_scan")); d->actionScan->setText(i18n ("Scan...")); d->actionScan->setIcon(SmallIcon("scanner")); #if HAVE_KSANE connect (d->actionScan, &QAction::triggered, this, &kpMainWindow::slotScan); #else d->actionScan->setEnabled(false); #endif // HAVE_KSANE d->actionScreenshot = ac->addAction(QStringLiteral("file_screenshot")); d->actionScreenshot->setText(i18n("Acquire Screenshot")); connect (d->actionScreenshot, &QAction::triggered, this, &kpMainWindow::slotScreenshot); d->actionProperties = ac->addAction (QStringLiteral("file_properties")); d->actionProperties->setText (i18n ("Properties")); d->actionProperties->setIcon(KDE::icon(QStringLiteral("document-properties"))); connect (d->actionProperties, &QAction::triggered, this, &kpMainWindow::slotProperties); //d->actionRevert = KStandardAction::revert (this, SLOT (slotRevert()), ac); d->actionReload = ac->addAction (QStringLiteral("file_revert")); d->actionReload->setText (i18n ("Reloa&d")); d->actionReload->setIcon(KDE::icon(QStringLiteral("view-refresh"))); connect (d->actionReload, &QAction::triggered, this, &kpMainWindow::slotReload); ac->setDefaultShortcuts (d->actionReload, KStandardShortcut::reload ()); slotEnableReload (); d->actionPrint = KStandardAction::print (this, SLOT (slotPrint()), ac); d->actionPrintPreview = KStandardAction::printPreview (this, SLOT (slotPrintPreview()), ac); d->actionMail = KStandardAction::mail (this, SLOT (slotMail()), ac); d->actionClose = KStandardAction::close (this, SLOT (slotClose()), ac); d->actionQuit = KStandardAction::quit (this, SLOT (slotQuit()), ac); d->scanDialog = nullptr; enableFileMenuDocumentActions (false); } //--------------------------------------------------------------------- // private void kpMainWindow::enableFileMenuDocumentActions (bool enable) { // d->actionNew // d->actionOpen // d->actionOpenRecent d->actionSave->setEnabled (enable); d->actionSaveAs->setEnabled (enable); d->actionExport->setEnabled (enable); // d->actionScan d->actionProperties->setEnabled (enable); // d->actionReload d->actionPrint->setEnabled (enable); d->actionPrintPreview->setEnabled (enable); d->actionMail->setEnabled (enable); d->actionClose->setEnabled (enable); // d->actionQuit->setEnabled (enable); } //--------------------------------------------------------------------- // private void kpMainWindow::addRecentURL (const QUrl &url_) { // HACK: KRecentFilesAction::loadEntries() clears the KRecentFilesAction::d->urls // map. // // So afterwards, the URL ref, our method is given, points to an // element in this now-cleared map (see KRecentFilesAction::urlSelected(QAction*)). // Accessing it would result in a crash. // // To avoid the crash, make a copy of it before calling // loadEntries() and use this copy, instead of the to-be-dangling // ref. const QUrl& url = url_; #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::addRecentURL(" << url << ")"; #endif if (url.isEmpty ()) return; KSharedConfig::Ptr cfg = KSharedConfig::openConfig(); // KConfig::readEntry() does not actually reread from disk, hence doesn't // realize what other processes have done e.g. Settings / Show Path cfg->reparseConfiguration (); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\trecent URLs=" << d->actionOpenRecent->items (); #endif // HACK: Something might have changed interprocess. // If we could PROPAGATE: interprocess, then this wouldn't be required. d->actionOpenRecent->loadEntries (cfg->group (kpSettingsGroupRecentFiles)); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tafter loading config=" << d->actionOpenRecent->items (); #endif d->actionOpenRecent->addUrl (url); d->actionOpenRecent->saveEntries (cfg->group (kpSettingsGroupRecentFiles)); cfg->sync (); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tnew recent URLs=" << d->actionOpenRecent->items (); #endif // TODO: PROPAGATE: interprocess // TODO: Is this loop safe since a KMainWindow later along in the list, // could be closed as the code in the body almost certainly re-enters // the event loop? Problem for KDE 3 as well, I think. for (auto *kmw : KMainWindow::memberList ()) { Q_ASSERT (dynamic_cast (kmw)); auto *mw = dynamic_cast (kmw); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\tmw=" << mw; #endif if (mw != this) { // WARNING: Do not use KRecentFilesAction::setItems() // - it does not work since only its superclass, // KSelectAction, implements setItems() and can't // update KRecentFilesAction's URL list. // Avoid URL memory leak in KRecentFilesAction::loadEntries(). mw->d->actionOpenRecent->clear (); mw->d->actionOpenRecent->loadEntries (cfg->group (kpSettingsGroupRecentFiles)); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\t\tcheck recent URLs=" << mw->d->actionOpenRecent->items (); #endif } } } //--------------------------------------------------------------------- // private slot // TODO: Disable action if // (d->configOpenImagesInSameWindow && d->document && d->document->isEmpty()) // as it does nothing if this is true. void kpMainWindow::slotNew () { toolEndShape (); if (d->document && !d->configOpenImagesInSameWindow) { // A document -- empty or otherwise -- is open. // Force open a new window. In contrast, open() might not open // a new window in this case. auto *win = new kpMainWindow (); win->show (); } else { open (QUrl (), true/*create an empty doc*/); } } //--------------------------------------------------------------------- // private QSize kpMainWindow::defaultDocSize () const { // KConfig::readEntry() does not actually reread from disk, hence doesn't // realize what other processes have done e.g. Settings / Show Path KSharedConfig::openConfig ()->reparseConfiguration (); KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupGeneral); QSize docSize = cfg.readEntry (kpSettingLastDocSize, QSize ()); if (docSize.isEmpty ()) { docSize = QSize (400, 300); } else { // Don't get too big or you'll thrash (or even lock up) the computer // just by opening a window docSize = QSize (qMin (2048, docSize.width ()), qMin (2048, docSize.height ())); } return docSize; } //--------------------------------------------------------------------- // private void kpMainWindow::saveDefaultDocSize (const QSize &size) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tCONFIG: saving Last Doc Size = " << size; #endif KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupGeneral); cfg.writeEntry (kpSettingLastDocSize, size); cfg.sync (); } //--------------------------------------------------------------------- // private bool kpMainWindow::shouldOpen () { if (d->configOpenImagesInSameWindow) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\topenImagesInSameWindow"; #endif // (this brings up a dialog and might save the current doc) if (!queryCloseDocument ()) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\tqueryCloseDocument() aborts open"; #endif return false; } } return true; } //--------------------------------------------------------------------- // private void kpMainWindow::setDocumentChoosingWindow (kpDocument *doc) { // Want new window? if (d->document && !d->document->isEmpty () && !d->configOpenImagesInSameWindow) { // Send doc to new window. auto *win = new kpMainWindow (doc); win->show (); } else { // (sets up views, doc signals) setDocument (doc); } } //--------------------------------------------------------------------- // private kpDocument *kpMainWindow::openInternal (const QUrl &url, const QSize &fallbackDocSize, bool newDocSameNameIfNotExist) { // If using OpenImagesInSameWindow mode, ask whether to close the // current document. if (!shouldOpen ()) return nullptr; // Create/open doc. auto *newDoc = new kpDocument (fallbackDocSize.width (), fallbackDocSize.height (), documentEnvironment ()); if (!newDoc->open (url, newDocSameNameIfNotExist)) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\topen failed"; #endif delete newDoc; return nullptr; } #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\topen OK"; #endif // Send document to current or new window. setDocumentChoosingWindow (newDoc); return newDoc; } //--------------------------------------------------------------------- // private bool kpMainWindow::open (const QUrl &url, bool newDocSameNameIfNotExist) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::open(" << url << ",newDocSameNameIfNotExist=" << newDocSameNameIfNotExist << ")"; #endif kpDocument *newDoc = openInternal (url, defaultDocSize (), newDocSameNameIfNotExist); if (newDoc) { if (newDoc->isFromURL (false/*don't bother checking exists*/)) addRecentURL (url); return true; } return false; } //--------------------------------------------------------------------- // private QList kpMainWindow::askForOpenURLs(const QString &caption, bool allowMultipleURLs) { QMimeDatabase db; QStringList filterList; QString filter; for (const auto &type : QImageReader::supportedMimeTypes()) { if ( !filter.isEmpty() ) { filter += QLatin1Char(' '); } QMimeType mime(db.mimeTypeForName(QString::fromLatin1(type))); if ( mime.isValid() ) { QString glob = mime.globPatterns().join(QLatin1Char(' ')); filter += glob; // I want to show the mime comment AND the file glob pattern, // but to avoid that the "All Supported Files" entry shows ALL glob patterns, // I must add the pattern here a second time so that QFileDialog::HideNameFilterDetails // can hide the first pattern and I still see the second one filterList << mime.comment() + QStringLiteral(" (%1)(%2)").arg(glob).arg(glob); } } filterList.prepend(i18n("All Supported Files (%1)", filter)); QFileDialog fd(this); fd.setNameFilters(filterList); fd.setOption(QFileDialog::HideNameFilterDetails); fd.setWindowTitle(caption); if ( allowMultipleURLs ) { fd.setFileMode(QFileDialog::ExistingFiles); } if ( fd.exec() ) { return fd.selectedUrls(); } - return QList(); + return {}; } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotOpen () { toolEndShape (); const QList urls = askForOpenURLs(i18nc("@title:window", "Open Image")); for (const auto & url : urls) { open (url); } } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotOpenRecent (const QUrl &url) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotOpenRecent(" << url << ")"; qCDebug(kpLogMainWindow) << "\titems=" << d->actionOpenRecent->items (); #endif toolEndShape (); open (url); // If the open is successful, addRecentURL() would have bubbled up the // URL in the File / Open Recent action. As a side effect, the URL is // deselected. // // If the open fails, we should deselect the URL: // // 1. for consistency // // 2. because it has not been opened. // d->actionOpenRecent->setCurrentItem (-1); } //--------------------------------------------------------------------- void kpMainWindow::slotRecentListCleared() { d->actionOpenRecent->saveEntries(KSharedConfig::openConfig()->group(kpSettingsGroupRecentFiles)); } //--------------------------------------------------------------------- #if HAVE_KSANE // private slot void kpMainWindow::slotScan () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotScan() scanDialog=" << d->scanDialog; #endif toolEndShape (); if (!d->scanDialog) { // Create scan dialog d->scanDialog = new SaneDialog(this); // No scanning support (kdegraphics/libkscan) installed? if (!d->scanDialog) { KMessageBox::sorry (this, i18n("Failed to open scanning dialog."), i18nc("@title:window", "Scanning Failed")); return; } #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tcreated scanDialog=" << d->scanDialog; #endif connect (d->scanDialog, &SaneDialog::finalImage, this, &kpMainWindow::slotScanned); } // If using OpenImagesInSameWindow mode, ask whether to close the // current document. // // Do this after scan support is detected. Because if it's not, what // would be the point of closing the document? // // Ideally, we would do this after the user presses "Final Scan" in // the scan dialog and before the scan begins (if the user wants to // cancel the scan operation, it would be annoying to offer this choice // only after the slow scan is completed) but the KScanDialog API does // not allow this. So we settle for doing this before any // scan dialogs are shown. We don't do this between KScanDialog::setup() // and KScanDialog::exec() as it could be confusing alternating between // scanning and KolourPaint dialogs. if (!shouldOpen ()) { return; } #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tcalling setup"; #endif // Bring up dialog to select scan device. // If there is no scanner, we find that this does not bring up a dialog // but still returns true. if (d->scanDialog->setup ()) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\tOK - showing dialog"; #endif // Called only if scanner configured/available. // // In reality, this seems to be called even if you press "Cancel" in // the KScanDialog::setup() dialog! // // We use exec() to make sure it's modal. show() seems to work too // but better safe than sorry. d->scanDialog->exec (); } else { // Have never seen this code path execute even if "Cancel" is pressed. #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\tFAIL"; #endif } } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotScanned (const QImage &image, int) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotScanned() image.rect=" << image.rect (); #endif #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\thiding dialog"; #endif // (KScanDialog does not close itself after a scan is made) // // Close the dialog, first thing: // // 1. This means that any dialogs we bring up won't be nested on top. // // 2. We don't want to return from this method but forget to close // the dialog. So do it before anything else. d->scanDialog->hide (); // (just in case there's some drawing between slotScan() exiting and // us being called) toolEndShape (); // TODO: Maybe this code should be moved into kpdocument.cpp - // since it resembles the responsibilities of kpDocument::open(). kpDocumentSaveOptions saveOptions; kpDocumentMetaInfo metaInfo; kpDocument::getDataFromImage(image, saveOptions, metaInfo); // Create document from image and meta info. auto *doc = new kpDocument (image.width (), image.height (), documentEnvironment ()); doc->setImage (image); doc->setSaveOptions (saveOptions); doc->setMetaInfo (metaInfo); // Send document to current or new window. setDocumentChoosingWindow (doc); } #endif // HAVE_KSANE //--------------------------------------------------------------------- void kpMainWindow::slotScreenshot() { toolEndShape(); auto *dialog = new QDialog(this); auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog); connect (buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept); connect (buttons, &QDialogButtonBox::rejected, dialog, &QDialog::reject); auto *label = new QLabel(i18n("Snapshot Delay")); auto *seconds = new KPluralHandlingSpinBox; seconds->setRange(0, 99); seconds->setSuffix(ki18np(" second", " seconds")); seconds->setSpecialValueText(i18n("No delay")); auto *hideWindow = new QCheckBox(i18n("Hide Main Window")); hideWindow->setChecked(true); auto *vbox = new QVBoxLayout(dialog); vbox->addWidget(label); vbox->addWidget(seconds); vbox->addWidget(hideWindow); vbox->addWidget(buttons); if ( dialog->exec() == QDialog::Rejected ) { delete dialog; return; } if ( hideWindow->isChecked() ) { hide(); } // at least 1 seconds to make sure the window is hidden and the hide effect already stopped QTimer::singleShot((seconds->value() + 1) * 1000, this, &kpMainWindow::slotMakeScreenshot); delete dialog; } //--------------------------------------------------------------------- void kpMainWindow::slotMakeScreenshot() { QCoreApplication::processEvents(); QPixmap pixmap = QGuiApplication::primaryScreen()->grabWindow(QApplication::desktop()->winId()); auto *doc = new kpDocument(pixmap.width(), pixmap.height(), documentEnvironment()); doc->setImage(pixmap.toImage()); // Send document to current or new window. setDocumentChoosingWindow(doc); show(); // in case we hid the mainwindow, show it again } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotProperties () { toolEndShape (); kpDocumentMetaInfoDialog dialog (document ()->metaInfo (), this); if (dialog.exec () && !dialog.isNoOp ()) { commandHistory ()->addCommand ( new kpDocumentMetaInfoCommand ( i18n ("Document Properties"), dialog.metaInfo ()/*new*/, *document ()->metaInfo ()/*old*/, commandEnvironment ())); } } //--------------------------------------------------------------------- // private slot bool kpMainWindow::save (bool localOnly) { if (d->document->url ().isEmpty () || !QImageWriter::supportedMimeTypes() .contains(d->document->saveOptions ()->mimeType().toLatin1()) || // SYNC: kpDocument::getPixmapFromFile() can't determine quality // from file so it has been set initially to an invalid value. (d->document->saveOptions ()->mimeTypeHasConfigurableQuality () && d->document->saveOptions ()->qualityIsInvalid ()) || (localOnly && !d->document->url ().isLocalFile ())) { return saveAs (localOnly); } if (d->document->save (false/*no overwrite prompt*/, !d->document->savedAtLeastOnceBefore ()/*lossy prompt*/)) { addRecentURL (d->document->url ()); return true; } return false; } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotSave () { toolEndShape (); return save (); } //--------------------------------------------------------------------- // private QUrl kpMainWindow::askForSaveURL (const QString &caption, const QString &startURL, const kpImage &imageToBeSaved, const kpDocumentSaveOptions &startSaveOptions, const kpDocumentMetaInfo &docMetaInfo, const QString &forcedSaveOptionsGroup, bool localOnly, kpDocumentSaveOptions *chosenSaveOptions, bool isSavingForFirstTime, bool *allowOverwritePrompt, bool *allowLossyPrompt) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::askForURL() startURL=" << startURL; startSaveOptions.printDebug ("\tstartSaveOptions"); #endif bool reparsedConfiguration = false; // KConfig::readEntry() does not actually reread from disk, hence doesn't // realize what other processes have done e.g. Settings / Show Path // so reparseConfiguration() must be called #define SETUP_READ_CFG() \ if (!reparsedConfiguration) \ { \ KSharedConfig::openConfig ()->reparseConfiguration (); \ reparsedConfiguration = true; \ } \ \ KConfigGroup cfg (KSharedConfig::openConfig (), forcedSaveOptionsGroup); if (chosenSaveOptions) { *chosenSaveOptions = kpDocumentSaveOptions (); } if (allowOverwritePrompt) { *allowOverwritePrompt = true; // play it safe for now } if (allowLossyPrompt) { *allowLossyPrompt = true; // play it safe for now } kpDocumentSaveOptions fdSaveOptions = startSaveOptions; QStringList mimeTypes; for (const auto &type : QImageWriter::supportedMimeTypes()) { mimeTypes << QString::fromLatin1(type); } #if DEBUG_KP_MAIN_WINDOW QStringList sortedMimeTypes = mimeTypes; sortedMimeTypes.sort (); qCDebug(kpLogMainWindow) << "\tmimeTypes=" << mimeTypes << "\tsortedMimeTypes=" << sortedMimeTypes; #endif if (mimeTypes.isEmpty ()) { qCCritical(kpLogMainWindow) << "No output mimetypes!"; - return QUrl (); + return {}; } #define MIME_TYPE_IS_VALID() (!fdSaveOptions.mimeTypeIsInvalid () && \ mimeTypes.contains (fdSaveOptions.mimeType ())) if (!MIME_TYPE_IS_VALID ()) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tmimeType=" << fdSaveOptions.mimeType () << " not valid, get default"; #endif SETUP_READ_CFG (); fdSaveOptions.setMimeType (kpDocumentSaveOptions::defaultMimeType (cfg)); if (!MIME_TYPE_IS_VALID ()) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tmimeType=" << fdSaveOptions.mimeType () << " not valid, get hardcoded"; #endif if (mimeTypes.contains (QStringLiteral("image/png"))) { fdSaveOptions.setMimeType (QStringLiteral("image/png")); } else if (mimeTypes.contains (QStringLiteral("image/bmp"))) { fdSaveOptions.setMimeType (QStringLiteral("image/bmp")); } else { fdSaveOptions.setMimeType (mimeTypes.first ()); } } } #undef MIME_TYPE_IS_VALID if (fdSaveOptions.colorDepthIsInvalid ()) { SETUP_READ_CFG (); fdSaveOptions.setColorDepth (kpDocumentSaveOptions::defaultColorDepth (cfg)); fdSaveOptions.setDither (kpDocumentSaveOptions::defaultDither (cfg)); } if (fdSaveOptions.qualityIsInvalid ()) { SETUP_READ_CFG (); fdSaveOptions.setQuality (kpDocumentSaveOptions::defaultQuality (cfg)); } #if DEBUG_KP_MAIN_WINDOW fdSaveOptions.printDebug ("\tcorrected saveOptions passed to fileDialog"); #endif auto *saveOptionsWidget = new kpDocumentSaveOptionsWidget (imageToBeSaved, fdSaveOptions, docMetaInfo, this); KFileDialog fd (QUrl (startURL), QString(), this, saveOptionsWidget); saveOptionsWidget->setVisualParent (&fd); fd.setWindowTitle (caption); fd.setOperationMode (KFileDialog::Saving); fd.setMimeFilter (mimeTypes, fdSaveOptions.mimeType ()); if (localOnly) { fd.setMode (KFile::File | KFile::LocalOnly); } connect (&fd, &KFileDialog::filterChanged, saveOptionsWidget, &kpDocumentSaveOptionsWidget::setMimeType); if ( fd.exec() == QDialog::Accepted ) { kpDocumentSaveOptions newSaveOptions = saveOptionsWidget->documentSaveOptions (); #if DEBUG_KP_MAIN_WINDOW newSaveOptions.printDebug ("\tnewSaveOptions"); #endif KConfigGroup cfg (KSharedConfig::openConfig (), forcedSaveOptionsGroup); // Save options user forced - probably want to use them in future kpDocumentSaveOptions::saveDefaultDifferences (cfg, fdSaveOptions, newSaveOptions); cfg.sync (); if (chosenSaveOptions) { *chosenSaveOptions = newSaveOptions; } bool shouldAllowOverwritePrompt = (fd.selectedUrl () != QUrl (startURL) || newSaveOptions.mimeType () != startSaveOptions.mimeType ()); if (allowOverwritePrompt) { *allowOverwritePrompt = shouldAllowOverwritePrompt; #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tallowOverwritePrompt=" << *allowOverwritePrompt; #endif } if (allowLossyPrompt) { // SYNC: kpDocumentSaveOptions elements - everything except quality // (one quality setting is "just as lossy" as another so no // need to continually warn due to quality change) *allowLossyPrompt = (isSavingForFirstTime || shouldAllowOverwritePrompt || newSaveOptions.mimeType () != startSaveOptions.mimeType () || newSaveOptions.colorDepth () != startSaveOptions.colorDepth () || newSaveOptions.dither () != startSaveOptions.dither ()); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tallowLossyPrompt=" << *allowLossyPrompt; #endif } #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tselectedUrl=" << fd.selectedUrl (); #endif return fd.selectedUrl (); } - return QUrl (); + return {}; #undef SETUP_READ_CFG } //--------------------------------------------------------------------- // private slot bool kpMainWindow::saveAs (bool localOnly) { kpDocumentSaveOptions chosenSaveOptions; bool allowOverwritePrompt, allowLossyPrompt; QUrl chosenURL = askForSaveURL (i18nc ("@title:window", "Save Image As"), d->document->url ().url (), d->document->imageWithSelection (), *d->document->saveOptions (), *d->document->metaInfo (), kpSettingsGroupFileSaveAs, localOnly, &chosenSaveOptions, !d->document->savedAtLeastOnceBefore (), &allowOverwritePrompt, &allowLossyPrompt); if (chosenURL.isEmpty ()) { return false; } if (!d->document->saveAs (chosenURL, chosenSaveOptions, allowOverwritePrompt, allowLossyPrompt)) { return false; } addRecentURL (chosenURL); return true; } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotSaveAs () { toolEndShape (); return saveAs (); } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotExport () { toolEndShape (); kpDocumentSaveOptions chosenSaveOptions; bool allowOverwritePrompt, allowLossyPrompt; QUrl chosenURL = askForSaveURL (i18nc ("@title:window", "Export"), d->lastExportURL.url (), d->document->imageWithSelection (), d->lastExportSaveOptions, *d->document->metaInfo (), kpSettingsGroupFileExport, false/*allow remote files*/, &chosenSaveOptions, d->exportFirstTime, &allowOverwritePrompt, &allowLossyPrompt); if (chosenURL.isEmpty ()) { return false; } if (!kpDocument::savePixmapToFile (d->document->imageWithSelection (), chosenURL, chosenSaveOptions, *d->document->metaInfo (), allowOverwritePrompt, allowLossyPrompt, this)) { return false; } addRecentURL (chosenURL); d->lastExportURL = chosenURL; d->lastExportSaveOptions = chosenSaveOptions; d->exportFirstTime = false; return true; } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotEnableReload () { d->actionReload->setEnabled (d->document); } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotReload () { toolEndShape (); Q_ASSERT (d->document); QUrl oldURL = d->document->url (); if (d->document->isModified ()) { int result = KMessageBox::Cancel; if (d->document->isFromURL (false/*don't bother checking exists*/) && !oldURL.isEmpty ()) { result = KMessageBox::warningContinueCancel (this, i18n ("The document \"%1\" has been modified.\n" "Reloading will lose all changes since you last saved it.\n" "Are you sure?", d->document->prettyFilename ()), QString()/*caption*/, KGuiItem(i18n ("&Reload"))); } else { result = KMessageBox::warningContinueCancel (this, i18n ("The document \"%1\" has been modified.\n" "Reloading will lose all changes.\n" "Are you sure?", d->document->prettyFilename ()), QString()/*caption*/, KGuiItem(i18n ("&Reload"))); } if (result != KMessageBox::Continue) { return false; } } kpDocument *doc = nullptr; // If it's _supposed to_ come from a URL or it exists if (d->document->isFromURL (false/*don't bother checking exists*/) || (!oldURL.isEmpty () && KIO::NetAccess::exists (oldURL, KIO::NetAccess::SourceSide/*open*/, this))) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotReload() reloading from disk!"; #endif doc = new kpDocument (1, 1, documentEnvironment ()); if (!doc->open (oldURL)) { delete doc; doc = nullptr; return false; } addRecentURL (oldURL); } else { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotReload() create doc"; #endif doc = new kpDocument (d->document->constructorWidth (), d->document->constructorHeight (), documentEnvironment ()); doc->setURL (oldURL, false/*not from URL*/); } setDocument (doc); return true; } // private void kpMainWindow::sendDocumentNameToPrinter (QPrinter *printer) { QUrl url = d->document->url (); if (!url.isEmpty ()) { int dot; QString fileName = url.fileName (); dot = fileName.lastIndexOf ('.'); // file.ext but not .hidden-file? if (dot > 0) { fileName.truncate (dot); } #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::sendDocumentNameToPrinter() fileName=" << fileName << " dir=" << url.path(); #endif printer->setDocName (fileName); } } //-------------------------------------------------------------------------------- void kpMainWindow::sendPreviewToPrinter(QPrinter *printer) { sendImageToPrinter(printer, false); } //-------------------------------------------------------------------------------- // private void kpMainWindow::sendImageToPrinter (QPrinter *printer, bool showPrinterSetupDialog) { // Get image to be printed. kpImage image = d->document->imageWithSelection (); // Get image DPI. auto imageDotsPerMeterX = double (d->document->metaInfo ()->dotsPerMeterX ()); auto imageDotsPerMeterY = double (d->document->metaInfo ()->dotsPerMeterY ()); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::sendImageToPrinter() image:" << " width=" << image.width () << " height=" << image.height () << " dotsPerMeterX=" << imageDotsPerMeterX << " dotsPerMeterY=" << imageDotsPerMeterY; #endif // Image DPI invalid (e.g. new image, could not read from file // or Qt3 doesn't implement DPI for JPEG)? if (imageDotsPerMeterX <= 0 || imageDotsPerMeterY <= 0) { // Even if just one DPI dimension is invalid, mutate both DPI // dimensions as we have no information about the intended // aspect ratio anyway (and other dimension likely to be invalid). // When rendering text onto a document, the fonts are rasterised // according to the screen's DPI. // TODO: I think we should use the image's DPI. Technically // possible? // // So no matter what computer you draw text on, you get // the same pixels. // // So we must print at the screen's DPI to get the right text size. // // Unfortunately, this means that moving to a different screen DPI // affects printing. If you edited the image at a different screen // DPI than when you print, you get incorrect results. Furthermore, // this is bogus if you don't have text in your image. Worse still, // what if you have multiple screens connected to the same computer // with different DPIs? // TODO: mysteriously, someone else is setting this to 96dpi always. QPixmap arbitraryScreenElement(1, 1); const QPaintDevice *screenDevice = &arbitraryScreenElement; const auto dpiX = screenDevice->logicalDpiX (); const auto dpiY = screenDevice->logicalDpiY (); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tusing screen dpi: x=" << dpiX << " y=" << dpiY; #endif imageDotsPerMeterX = dpiX * KP_INCHES_PER_METER; imageDotsPerMeterY = dpiY * KP_INCHES_PER_METER; } // Get page size (excluding margins). // Coordinate (0,0) is the X here: // mmmmm // mX m // m m m = margin // m m // mmmmm const auto printerWidthMM = printer->widthMM (); const auto printerHeightMM = printer->heightMM (); auto dpiX = imageDotsPerMeterX / KP_INCHES_PER_METER; auto dpiY = imageDotsPerMeterY / KP_INCHES_PER_METER; #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tprinter: widthMM=" << printerWidthMM << " heightMM=" << printerHeightMM; qCDebug(kpLogMainWindow) << "\timage: dpiX=" << dpiX << " dpiY=" << dpiY; #endif // // If image doesn't fit on page at intended DPI, change the DPI. // const auto scaleDpiX = (image.width () / (printerWidthMM / KP_MILLIMETERS_PER_INCH)) / dpiX; const auto scaleDpiY = (image.height () / (printerHeightMM / KP_MILLIMETERS_PER_INCH)) / dpiY; const auto scaleDpi = qMax (scaleDpiX, scaleDpiY); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\tscaleDpi: x=" << scaleDpiX << " y=" << scaleDpiY << " --> scale at " << scaleDpi << " to fit?"; #endif // Need to increase resolution to fit page? if (scaleDpi > 1.0) { dpiX *= scaleDpi; dpiY *= scaleDpi; #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\t\tto fit page, scaled to:" << " dpiX=" << dpiX << " dpiY=" << dpiY; #endif } // Make sure DPIs are equal as that's all QPrinter::setResolution() // supports. We do this in such a way that we only ever stretch an // image, to avoid losing information. Don't antialias as the printer // will do that to translate our DPI to its physical resolution and // double-antialiasing looks bad. if (dpiX > dpiY) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tdpiX > dpiY; stretching image height to equalise DPIs to dpiX=" << dpiX; #endif kpPixmapFX::scale (&image, image.width (), qMax (1, qRound (image.height () * dpiX / dpiY)), false/*don't antialias*/); dpiY = dpiX; } else if (dpiY > dpiX) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tdpiY > dpiX; stretching image width to equalise DPIs to dpiY=" << dpiY; #endif kpPixmapFX::scale (&image, qMax (1, qRound (image.width () * dpiY / dpiX)), image.height (), false/*don't antialias*/); dpiX = dpiY; } Q_ASSERT (dpiX == dpiY); // QPrinter::setResolution() has to be called before QPrinter::setup(). printer->setResolution (qMax (1, qRound (dpiX))); sendDocumentNameToPrinter (printer); if (showPrinterSetupDialog) { auto *optionsPage = new kpPrintDialogPage (this); optionsPage->setPrintImageCenteredOnPage (d->configPrintImageCenteredOnPage); QPrintDialog *printDialog = KdePrint::createPrintDialog ( printer, QList () << optionsPage, this); printDialog->setWindowTitle (i18nc ("@title:window", "Print Image")); // Display dialog. const bool wantToPrint = printDialog->exec (); if (optionsPage->printImageCenteredOnPage () != d->configPrintImageCenteredOnPage) { // Save config option even if the dialog was cancelled. d->configPrintImageCenteredOnPage = optionsPage->printImageCenteredOnPage (); KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupGeneral); cfg.writeEntry (kpSettingPrintImageCenteredOnPage, d->configPrintImageCenteredOnPage); cfg.sync (); } delete printDialog; if (!wantToPrint) { return; } } // Send image to printer. QPainter painter; painter.begin(printer); double originX = 0, originY = 0; // Center image on page? if (d->configPrintImageCenteredOnPage) { originX = (printer->width() - image.width ()) / 2; originY = (printer->height() - image.height ()) / 2; } painter.drawImage(qRound(originX), qRound(originY), image); painter.end(); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotPrint () { toolEndShape (); QPrinter printer; sendImageToPrinter (&printer, true/*showPrinterSetupDialog*/); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotPrintPreview () { toolEndShape (); QPrintPreviewDialog printPreview(this); connect(&printPreview, &QPrintPreviewDialog::paintRequested, this, &kpMainWindow::sendPreviewToPrinter); printPreview.exec (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotMail () { toolEndShape (); if (d->document->url ().isEmpty ()/*no name*/ || !d->document->isFromURL () || d->document->isModified ()/*needs to be saved*/) { int result = KMessageBox::questionYesNo (this, i18n ("You must save this image before sending it.\n" "Do you want to save it?"), QString(), KStandardGuiItem::save (), KStandardGuiItem::cancel ()); if (result == KMessageBox::Yes) { if (!save ()) { // save failed or aborted - don't email return; } } else { // don't want to save - don't email return; } } KToolInvocation::invokeMailer ( QString()/*to*/, QString()/*cc*/, QString()/*bcc*/, d->document->prettyFilename()/*subject*/, QString()/*body*/, QString()/*messageFile*/, QStringList(d->document->url().url())/*attachments*/); } //--------------------------------------------------------------------- // private bool kpMainWindow::queryCloseDocument () { toolEndShape (); if (!d->document || !d->document->isModified ()) { return true; // ok to close current doc } int result = KMessageBox::warningYesNoCancel (this, i18n ("The document \"%1\" has been modified.\n" "Do you want to save it?", d->document->prettyFilename ()), QString()/*caption*/, KStandardGuiItem::save (), KStandardGuiItem::discard ()); switch (result) { case KMessageBox::Yes: return slotSave (); // close only if save succeeds case KMessageBox::No: return true; // close without saving default: return false; // don't close current doc } } //--------------------------------------------------------------------- // private virtual [base KMainWindow] bool kpMainWindow::queryClose () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::queryClose()"; #endif toolEndShape (); if (!queryCloseDocument ()) { return false; } if (!queryCloseColors ()) { return false; } return true; } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotClose () { toolEndShape (); if (!queryCloseDocument ()) { return; } setDocument (nullptr); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotQuit () { toolEndShape (); close (); // will call queryClose() } //--------------------------------------------------------------------- diff --git a/tools/selection/kpAbstractSelectionTool.cpp b/tools/selection/kpAbstractSelectionTool.cpp index be048a83..f04aca35 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 #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 QVariant (); + 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 QVariant (); + return {}; } } //--------------------------------------------------------------------- diff --git a/tools/selection/kpAbstractSelectionTool_Create.cpp b/tools/selection/kpAbstractSelectionTool_Create.cpp index 08b01e00..f43e6051 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 #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 QVariant (); + return {}; } diff --git a/tools/selection/kpAbstractSelectionTool_Move.cpp b/tools/selection/kpAbstractSelectionTool_Move.cpp index 0517bb6e..0fc08d7a 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 #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 QVariant (); + return {}; } //--------------------------------------------------------------------- diff --git a/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp b/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp index 13055c15..f5e0609f 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 #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; } // Calcluate 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; } // Calcluate 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 QVariant (); + return {}; } //--------------------------------------------------------------------- diff --git a/tools/selection/text/kpToolText_SelectText.cpp b/tools/selection/text/kpToolText_SelectText.cpp index 6375791d..6a0f92d7 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 #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 QVariant (); + return {}; } diff --git a/views/kpView_Selections.cpp b/views/kpView_Selections.cpp index d5edd96e..669c79d5 100644 --- a/views/kpView_Selections.cpp +++ b/views/kpView_Selections.cpp @@ -1,381 +1,381 @@ /* 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_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" // 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 QRegion (); + 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/widgets/imagelib/effects/kpEffectBlurSharpenWidget.cpp b/widgets/imagelib/effects/kpEffectBlurSharpenWidget.cpp index a4a2bbf7..d7069d97 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 #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 QString(); + 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 114a2069..02c07cc6 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 #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 QString(); + 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/kpEffectToneEnhanceWidget.cpp b/widgets/imagelib/effects/kpEffectToneEnhanceWidget.cpp index cb59c6b4..efe169e8 100644 --- a/widgets/imagelib/effects/kpEffectToneEnhanceWidget.cpp +++ b/widgets/imagelib/effects/kpEffectToneEnhanceWidget.cpp @@ -1,139 +1,139 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2006 Mike Gashler 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 "kpEffectToneEnhanceWidget.h" #include "imagelib/effects/kpEffectToneEnhance.h" #include "commands/imagelib/effects/kpEffectToneEnhanceCommand.h" #include "pixmapfx/kpPixmapFX.h" #include "kpNumInput.h" #include #include #include "kpLogCategories.h" #include kpEffectToneEnhanceWidget::kpEffectToneEnhanceWidget (bool actOnSelection, QWidget *parent) : kpEffectWidgetBase (actOnSelection, parent), m_granularityInput (nullptr), m_amountInput (nullptr) { auto *lay = new QGridLayout (this); lay->setContentsMargins(0, 0, 0, 0); // See kpEffectToneEnhance::applyEffect(). auto *granularityLabel = new QLabel (i18n ("&Granularity:"), this); auto *amountLabel = new QLabel (i18n ("&Amount:"), this); m_granularityInput = new kpDoubleNumInput (this); m_granularityInput->setRange (0, 1, 0.1/*step*/); m_amountInput = new kpDoubleNumInput (this); m_amountInput->setRange (0, 1, 0.1/*step*/); granularityLabel->setBuddy (m_granularityInput); amountLabel->setBuddy (m_amountInput); lay->addWidget (granularityLabel, 0, 0); lay->addWidget (m_granularityInput, 0, 1); lay->addWidget (amountLabel, 1, 0); lay->addWidget (m_amountInput, 1, 1); lay->setColumnStretch (1, 1); connect (m_granularityInput, &kpDoubleNumInput::valueChanged, this, &kpEffectToneEnhanceWidget::settingsChangedDelayed); connect (m_amountInput, &kpDoubleNumInput::valueChanged, this, &kpEffectToneEnhanceWidget::settingsChangedDelayed); } kpEffectToneEnhanceWidget::~kpEffectToneEnhanceWidget () = default; // public virtual [base kpEffectWidgetBase] QString kpEffectToneEnhanceWidget::caption () const { // TODO: Why doesn't this have a caption? Ditto for the other effects. - return QString(); + return {}; } // private double kpEffectToneEnhanceWidget::amount () const { return m_amountInput ? m_amountInput->value () : 0; } // private double kpEffectToneEnhanceWidget::granularity () const { return m_granularityInput ? m_granularityInput->value () : 0; } // public virtual [base kpEffectWidgetBase] bool kpEffectToneEnhanceWidget::isNoOp () const { // If the "amount" is 0, nothing happens regardless of the granularity. // Note that if "granularity" is 0 but "amount" > 0, the effect _is_ active. // Therefore, "granularity" should have no involvement in this check. if (amount () == 0) { return true; } return false; } // public virtual [base kpEffectWidgetBase] kpImage kpEffectToneEnhanceWidget::applyEffect (const kpImage &image) { return kpEffectToneEnhance::applyEffect (image, granularity (), amount ()); } // public virtual [base kpEffectWidgetBase] kpEffectCommandBase *kpEffectToneEnhanceWidget::createCommand ( kpCommandEnvironment *cmdEnviron) const { return new kpEffectToneEnhanceCommand (granularity (), amount (), m_actOnSelection, cmdEnviron); } diff --git a/widgets/imagelib/effects/kpEffectWidgetBase.cpp b/widgets/imagelib/effects/kpEffectWidgetBase.cpp index ba00e854..db9bf4f1 100644 --- a/widgets/imagelib/effects/kpEffectWidgetBase.cpp +++ b/widgets/imagelib/effects/kpEffectWidgetBase.cpp @@ -1,48 +1,48 @@ /* 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. */ #include "kpEffectWidgetBase.h" kpEffectWidgetBase::kpEffectWidgetBase (bool actOnSelection, QWidget *parent) : QWidget (parent), m_actOnSelection (actOnSelection) { } kpEffectWidgetBase::~kpEffectWidgetBase () = default; // public QString kpEffectWidgetBase::caption () const { - return QString(); + return {}; } diff --git a/widgets/toolbars/options/kpToolWidgetBase.cpp b/widgets/toolbars/options/kpToolWidgetBase.cpp index 49c38a0e..2acc6d8c 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 #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 QList (); + 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/kpToolWidgetFillStyle.cpp b/widgets/toolbars/options/kpToolWidgetFillStyle.cpp index c7003e46..c442d74a 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 #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 QString(); + 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; } //---------------------------------------------------------------------