diff --git a/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp b/commands/imagelib/transforms/kpTransformResizeScaleCommand.cpp index bb6a5b93..bade7642 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 {}; } // 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? + // TODO: Can we get to size 0x0 accidentally? QTransform scaleMatrix; scaleMatrix.scale (horizScale, vertScale); currentPoints = scaleMatrix.map (currentPoints); currentPoints.translate ( -currentPoints.boundingRect ().x () + newX, -currentPoints.boundingRect ().y () + newY); auto *imageSel = dynamic_cast (m_oldSelectionPtr); auto *textSel = dynamic_cast (m_oldSelectionPtr); if (imageSel) { document ()->setSelection ( kpFreeFormImageSelection (currentPoints, kpImage (), imageSel->transparency ())); } else if (textSel) { document ()->setSelection ( kpTextSelection (currentPoints.boundingRect (), textSel->textLines (), textSel->textStyle ())); } else { Q_ASSERT (!"Unknown selection type"); } environ ()->somethingBelowTheCursorChanged (); } // public virtual [base kpCommand] void kpTransformResizeScaleCommand::execute () { #if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND qCDebug(kpLogCommands) << "kpTransformResizeScaleCommand::execute() type=" << (int) m_type << " oldWidth=" << m_oldWidth << " oldHeight=" << m_oldHeight << " newWidth=" << m_newWidth << " newHeight=" << m_newHeight; #endif if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight) return; if (m_type == Resize) { if (m_actOnSelection) { if (!m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::execute() resizing sel doesn't make sense"); } QApplication::setOverrideCursor (Qt::WaitCursor); kpTextSelection *textSel = textSelection (); Q_ASSERT (textSel); kpTextSelection *newSel = textSel->resized (m_newWidth, m_newHeight); document ()->setSelection (*newSel); delete newSel; environ ()->somethingBelowTheCursorChanged (); QApplication::restoreOverrideCursor (); } else { QApplication::setOverrideCursor (Qt::WaitCursor); if (m_newWidth < m_oldWidth) { m_oldRightImage = document ()->getImageAt ( QRect (m_newWidth, 0, m_oldWidth - m_newWidth, m_oldHeight)); } if (m_newHeight < m_oldHeight) { m_oldBottomImage = document ()->getImageAt ( QRect (0, m_newHeight, m_newWidth, m_oldHeight - m_newHeight)); } document ()->resize (m_newWidth, m_newHeight, m_backgroundColor); QApplication::restoreOverrideCursor (); } } // Scale else { QApplication::setOverrideCursor (Qt::WaitCursor); kpImage oldImage = document ()->image (m_actOnSelection); if (!m_isLosslessScale) { m_oldImage = oldImage; } kpImage newImage = kpPixmapFX::scale (oldImage, m_newWidth, m_newHeight, m_type == SmoothScale); if (!m_oldSelectionPtr && document ()->selection ()) { // Save sel border m_oldSelectionPtr = document ()->selection ()->clone (); m_oldSelectionPtr->deleteContent (); } if (m_actOnSelection) { if (m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::execute() scaling text sel doesn't make sense"); } Q_ASSERT (m_oldSelectionPtr); if ( !m_oldSelectionPtr ) { // make coverity happy return; } QRect newRect = QRect (m_oldSelectionPtr->x (), m_oldSelectionPtr->y (), newImage.width (), newImage.height ()); // Not possible to retain non-rectangular selection borders on scale // (think about e.g. a 45 deg line as part of the border & 2x scale) Q_ASSERT (dynamic_cast (m_oldSelectionPtr)); document ()->setSelection ( kpRectangularImageSelection (newRect, newImage, dynamic_cast (m_oldSelectionPtr) ->transparency ())); environ ()->somethingBelowTheCursorChanged (); } else { document ()->setImage (newImage); if (m_scaleSelectionWithImage) { scaleSelectionRegionWithDocument (); } } QApplication::restoreOverrideCursor (); } } // public virtual [base kpCommand] void kpTransformResizeScaleCommand::unexecute () { #if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND qCDebug(kpLogCommands) << "kpTransformResizeScaleCommand::unexecute() type=" << m_type; #endif if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight) { return; } kpDocument *doc = document (); Q_ASSERT (doc); if (m_type == Resize) { if (m_actOnSelection) { if (!m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::unexecute() resizing sel doesn't make sense"); } QApplication::setOverrideCursor (Qt::WaitCursor); kpTextSelection *textSel = textSelection (); Q_ASSERT (textSel); kpTextSelection *newSel = textSel->resized (m_oldWidth, m_oldHeight); document ()->setSelection (*newSel); delete newSel; environ ()->somethingBelowTheCursorChanged (); QApplication::restoreOverrideCursor (); } else { QApplication::setOverrideCursor (Qt::WaitCursor); kpImage newImage (m_oldWidth, m_oldHeight, QImage::Format_ARGB32_Premultiplied); kpPixmapFX::setPixmapAt (&newImage, QPoint (0, 0), doc->image ()); if (m_newWidth < m_oldWidth) { kpPixmapFX::setPixmapAt (&newImage, QPoint (m_newWidth, 0), m_oldRightImage); } if (m_newHeight < m_oldHeight) { kpPixmapFX::setPixmapAt (&newImage, QPoint (0, m_newHeight), m_oldBottomImage); } doc->setImage (newImage); QApplication::restoreOverrideCursor (); } } // Scale else { QApplication::setOverrideCursor (Qt::WaitCursor); kpImage oldImage; if (!m_isLosslessScale) { oldImage = m_oldImage; } else { oldImage = kpPixmapFX::scale (doc->image (m_actOnSelection), m_oldWidth, m_oldHeight); } if (m_actOnSelection) { if (m_actOnTextSelection) { Q_ASSERT (!"kpTransformResizeScaleCommand::unexecute() scaling text sel doesn't make sense"); } Q_ASSERT (dynamic_cast (m_oldSelectionPtr)); auto *oldImageSel = dynamic_cast (m_oldSelectionPtr); kpAbstractImageSelection *oldSelection = oldImageSel->clone (); oldSelection->setBaseImage (oldImage); doc->setSelection (*oldSelection); delete oldSelection; environ ()->somethingBelowTheCursorChanged (); } else { doc->setImage (oldImage); if (m_scaleSelectionWithImage) { doc->setSelection (*m_oldSelectionPtr); environ ()->somethingBelowTheCursorChanged (); } } QApplication::restoreOverrideCursor (); } } diff --git a/mainWindow/kpMainWindow_Colors.cpp b/mainWindow/kpMainWindow_Colors.cpp index e9b235db..172a0ab4 100644 --- a/mainWindow/kpMainWindow_Colors.cpp +++ b/mainWindow/kpMainWindow_Colors.cpp @@ -1,495 +1,495 @@ /* 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 "kpMainWindow.h" #include "kpMainWindowPrivate.h" #include "widgets/kpColorCells.h" #include "lgpl/generic/kpColorCollection.h" #include "lgpl/generic/kpUrlFormatter.h" #include "widgets/toolbars/kpColorToolBar.h" #include #include #include #include #include "kpLogCategories.h" #include #include //--------------------------------------------------------------------- static QStringList KDEColorCollectionNames () { return kpColorCollection::installedCollections (); } //--------------------------------------------------------------------- // private void kpMainWindow::setupColorsMenuActions () { KActionCollection *ac = actionCollection (); d->actionColorsDefault = ac->addAction (QStringLiteral("colors_default")); d->actionColorsDefault->setText (i18n ("Use KolourPaint Defaults")); connect (d->actionColorsDefault, &QAction::triggered, this, &kpMainWindow::slotColorsDefault); d->actionColorsKDE = ac->add (QStringLiteral("colors_kde")); d->actionColorsKDE->setText (i18nc ("@item:inmenu colors", "Use KDE's")); // TODO: Will this slot be called spuriously if there are no colors // installed? connect (d->actionColorsKDE, static_cast(&KSelectAction::triggered), this, &kpMainWindow::slotColorsKDE); for (const auto &colName : ::KDEColorCollectionNames ()) { d->actionColorsKDE->addAction (colName); } d->actionColorsOpen = ac->addAction (QStringLiteral("colors_open")); d->actionColorsOpen->setText (i18nc ("@item:inmenu colors", "&Open...")); connect (d->actionColorsOpen, &QAction::triggered, this, &kpMainWindow::slotColorsOpen); d->actionColorsReload = ac->addAction (QStringLiteral("colors_reload")); d->actionColorsReload->setText (i18nc ("@item:inmenu colors", "Reloa&d")); connect (d->actionColorsReload, &QAction::triggered, this, &kpMainWindow::slotColorsReload); d->actionColorsSave = ac->addAction (QStringLiteral("colors_save")); d->actionColorsSave->setText (i18nc ("@item:inmenu colors", "&Save")); connect (d->actionColorsSave, &QAction::triggered, this, &kpMainWindow::slotColorsSave); d->actionColorsSaveAs = ac->addAction (QStringLiteral("colors_save_as")); d->actionColorsSaveAs->setText (i18nc ("@item:inmenu colors", "Save &As...")); connect (d->actionColorsSaveAs, &QAction::triggered, this, &kpMainWindow::slotColorsSaveAs); d->actionColorsAppendRow = ac->addAction (QStringLiteral("colors_append_row")); d->actionColorsAppendRow->setText (i18nc ("@item:inmenu colors", "Add Row")); connect (d->actionColorsAppendRow, &QAction::triggered, this, &kpMainWindow::slotColorsAppendRow); d->actionColorsDeleteRow = ac->addAction (QStringLiteral("colors_delete_row")); d->actionColorsDeleteRow->setText (i18nc ("@item:inmenu colors", "Delete Last Row")); connect (d->actionColorsDeleteRow, &QAction::triggered, this, &kpMainWindow::slotColorsDeleteRow); enableColorsMenuDocumentActions (false); } //--------------------------------------------------------------------- // private void kpMainWindow::createColorBox () { d->colorToolBar = new kpColorToolBar (i18n ("Color Box"), this); // (needed for QMainWindow::saveState()) d->colorToolBar->setObjectName ( QStringLiteral("Color Box" )); connect (colorCells (), &kpColorCells::rowCountChanged, this, &kpMainWindow::slotUpdateColorsDeleteRowActionEnabled); } //--------------------------------------------------------------------- // private void kpMainWindow::enableColorsMenuDocumentActions (bool enable) { d->actionColorsDefault->setEnabled (enable); d->actionColorsKDE->setEnabled (enable); d->actionColorsOpen->setEnabled (enable); d->actionColorsReload->setEnabled (enable); d->actionColorsSave->setEnabled (enable); d->actionColorsSaveAs->setEnabled (enable); d->actionColorsAppendRow->setEnabled (enable); d->colorMenuDocumentActionsEnabled = enable; slotUpdateColorsDeleteRowActionEnabled (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotUpdateColorsDeleteRowActionEnabled () { // Currently, this is always enabled since kpColorCells guarantees that // there will be at least one row of cells (which might all be of the // invalid color). // // But this method is left here for future extensibility. d->actionColorsDeleteRow->setEnabled ( d->colorMenuDocumentActionsEnabled && (colorCells ()->rowCount () > 0)); } //--------------------------------------------------------------------- // Used in 2 situations: // // 1. User opens a color without using the "Use KDE's" submenu. // 2. User attempts to open a color using the "Use KDE's" submenu but the // opening fails. // // TODO: Maybe we could put the 3 actions (for different ways of opening -// colors) in an exclusive group -- this might elminate the need for +// colors) in an exclusive group -- this might eliminate the need for // this hack. // // private void kpMainWindow::deselectActionColorsKDE () { d->actionColorsKDE->setCurrentItem (-1); } //--------------------------------------------------------------------- // private bool kpMainWindow::queryCloseColors () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::queryCloseColors() colorCells.modified=" << colorCells ()->isModified (); #endif toolEndShape (); if (!colorCells ()->isModified ()) { return true; // ok to close } int result = KMessageBox::Cancel; if (!colorCells ()->url ().isEmpty ()) { result = KMessageBox::warningYesNoCancel (this, i18n ("The color palette \"%1\" has been modified.\n" "Do you want to save it?", kpUrlFormatter::PrettyFilename (colorCells ()->url ())), QString ()/*caption*/, KStandardGuiItem::save (), KStandardGuiItem::discard ()); } else { const QString name = colorCells ()->colorCollection ()->name (); if (!name.isEmpty ()) { result = KMessageBox::warningYesNoCancel (this, i18n ("The KDE color palette \"%1\" has been modified.\n" "Do you want to save it to a file?", name), QString ()/*caption*/, KStandardGuiItem::save (), KStandardGuiItem::discard ()); } else { result = KMessageBox::warningYesNoCancel (this, i18n ("The default color palette has been modified.\n" "Do you want to save it to a file?"), QString ()/*caption*/, KStandardGuiItem::save (), KStandardGuiItem::discard ()); } } switch (result) { case KMessageBox::Yes: return slotColorsSave (); // close only if save succeeds case KMessageBox::No: return true; // close without saving default: return false; // don't close current doc } } //--------------------------------------------------------------------- // private void kpMainWindow::openDefaultColors () { colorCells ()->setColorCollection ( kpColorCells::DefaultColorCollection ()); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotColorsDefault () { // Call just in case. toolEndShape (); if (!queryCloseColors ()) { return; } openDefaultColors (); deselectActionColorsKDE (); } //--------------------------------------------------------------------- // private bool kpMainWindow::openKDEColors (const QString &name) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::openKDEColors(" << name << ")"; #endif kpColorCollection colorCol; if (colorCol.openKDE (name, this)) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "opened"; #endif colorCells ()->setColorCollection (colorCol); return true; } else { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "failed to open"; #endif return false; } } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotColorsKDE () { // Call in case an error dialog appears. toolEndShape (); const int curItem = d->actionColorsKDE->currentItem (); if (!queryCloseColors ()) { deselectActionColorsKDE (); return; } // queryCloseColors() calls slotColorSave(), which can call // slotColorSaveAs(), which can call deselectActionColorsKDE(). d->actionColorsKDE->setCurrentItem (curItem); const QStringList colNames = ::KDEColorCollectionNames (); const int selected = d->actionColorsKDE->currentItem (); Q_ASSERT (selected >= 0 && selected < colNames.size ()); if (!openKDEColors (colNames [selected])) { deselectActionColorsKDE (); } } //--------------------------------------------------------------------- // private bool kpMainWindow::openColors (const QUrl &url) { return colorCells ()->openColorCollection (url); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotColorsOpen () { // Call due to dialog. toolEndShape (); QFileDialog fd(this); fd.setDirectoryUrl(colorCells ()->url()); fd.setWindowTitle(i18nc ("@title:window", "Open Color Palette")); if (fd.exec ()) { if (!queryCloseColors ()) { return; } QList selected = fd.selectedUrls(); if ( selected.count() && openColors(selected[0]) ) { deselectActionColorsKDE(); } } } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotColorsReload () { toolEndShape (); if (colorCells ()->isModified ()) { int result = KMessageBox::Cancel; if (!colorCells ()->url ().isEmpty ()) { result = KMessageBox::warningContinueCancel (this, i18n ("The color palette \"%1\" has been modified.\n" "Reloading will lose all changes since you last saved it.\n" "Are you sure?", kpUrlFormatter::PrettyFilename (colorCells ()->url ())), QString ()/*caption*/, KGuiItem(i18n ("&Reload"))); } else { const QString name = colorCells ()->colorCollection ()->name (); if (!name.isEmpty ()) { result = KMessageBox::warningContinueCancel (this, i18n ("The KDE color palette \"%1\" has been modified.\n" "Reloading will lose all changes.\n" "Are you sure?", colorCells ()->colorCollection ()->name ()), QString ()/*caption*/, KGuiItem (i18n ("&Reload"))); } else { result = KMessageBox::warningContinueCancel (this, i18n ("The default color palette has been modified.\n" "Reloading will lose all changes.\n" "Are you sure?"), QString ()/*caption*/, KGuiItem (i18n ("&Reload"))); } } #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "result=" << result << "vs KMessageBox::Continue" << KMessageBox::Continue; #endif if (result != KMessageBox::Continue) { return; } } if (!colorCells ()->url ().isEmpty ()) { openColors (colorCells ()->url ()); } else { const QString name = colorCells ()->colorCollection ()->name (); if (!name.isEmpty ()) { openKDEColors (name); } else { openDefaultColors (); } } } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotColorsSave () { // Call due to dialog. toolEndShape (); if (colorCells ()->url ().isEmpty ()) { return slotColorsSaveAs (); } return colorCells ()->saveColorCollection (); } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotColorsSaveAs () { // Call due to dialog. toolEndShape (); QFileDialog fd(this); fd.setDirectoryUrl(colorCells ()->url()); fd.setWindowTitle(i18n("Save Color Palette As")); fd.setAcceptMode(QFileDialog::AcceptSave); // Note that QFileDialog takes care of asking the user to confirm overwriting. if (fd.exec ()) { QList selected = fd.selectedUrls(); if ( !selected.count() || !colorCells ()->saveColorCollectionAs(selected[0]) ) { return false; } // We're definitely using our own color collection now. deselectActionColorsKDE (); return true; } return false; } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotColorsAppendRow () { // Call just in case. toolEndShape (); kpColorCells *colorCells = d->colorToolBar->colorCells (); colorCells->appendRow (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotColorsDeleteRow () { // Call just in case. toolEndShape (); kpColorCells *colorCells = d->colorToolBar->colorCells (); colorCells->deleteLastRow (); } diff --git a/tools/selection/kpAbstractSelectionTool.h b/tools/selection/kpAbstractSelectionTool.h index 915ea258..db88eb68 100644 --- a/tools/selection/kpAbstractSelectionTool.h +++ b/tools/selection/kpAbstractSelectionTool.h @@ -1,600 +1,600 @@ /* 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. */ #ifndef kpAbstractSelectionTool_H #define kpAbstractSelectionTool_H #include "tools/kpTool.h" #include class QKeyEvent; class QPoint; class QRect; class kpAbstractSelection; class kpAbstractSelectionContentCommand; class kpCommand; class kpToolSelectionEnvironment; // The abstract base for all selection tools. // // // This provides methods to: // // 1. handle selection commands and the command history. // // 2. implement the kpTool drawing methods. // // 3. implement the "Create", "Move" and "Resize/Scale" selection // draw types. // // "Drags" that consist of a single click generally have no effect in -// order to prevent accidently creating a 1x1 selection, doing a NOP move +// order to prevent accidentally creating a 1x1 selection, doing a NOP move // or doing a NOP resize/scale. However, doing a bigger drag and then // dragging the mouse back to create a 1x1 selection or the NOP // effects are allowed and are recorded in the command history. // // Additionally, the "Create" draw type is fitted with "accidental drag // detection" which will not create a selection in response in small and // quick drags. // // // The internal architecture is as follows and is of interest for subclasses // and for changing the existing implementation of the above selection // draw types: // // beginDraw() initiates the action by determining the current draw type // by calling the virtual calculateDrawType(). Later, all of this class' // implementations of kpTool drawing methods (e.g. beginDraw(), draw(), // endShape() etc.) call the virtual operation(), which is passed: // // 1. the current draw type (e.g. "Create" or "Move") // // 2. the operation, corresponding to the calling method (e.g. // "BeginDraw" of called by beginDraw()). // // [Note: the documentation in these source files sometimes uses "operation" // where "draw type" is meant and vice-versa] // // Note that these kpTool drawing methods do some other work before and after // calling operation(). // // The default implementation of operation() is to dispatch the operation // to draw-type-specific methods e.g. createOperation() handles all // operations for the "Create" draw type. createOperation() will then // call the method that corresponds to the operation e.g. beginDrawCreate() // corresponds to the "BeginDraw" operation. // // For each draw type, all methods are grouped in a single source file // e.g. kpAbstractSelectionTool_Create.cpp. // // To introduce a custom draw type, not implemented by code in this // class (e.g. "SelectText"), you must: // // 1. Add it to "enum DrawType" below. // // 2. Override calculateDrawType() to determine the situations in which // the new draw type is active. // // 3. Override operation() to catch all situations in which the new draw // type is being used, and to implement the appropriate logic. // class kpAbstractSelectionTool : public kpTool { Q_OBJECT public: kpAbstractSelectionTool (const QString &text, const QString &description, int key, kpToolSelectionEnvironment *environ, QObject *parent, const QString &name); ~kpAbstractSelectionTool () override; // Inform kpTool to call draw() when CTRL, SHIFT and friends are // pressed. CTRL is used for copying, instead of moving, the // selection. SHIFT is used for sweeping. bool careAboutModifierState () const override { return true; } // // Drawing - Subclass Accessors // protected: friend struct kpAbstractSelectionToolPrivate; enum DrawType { None, Create, Move, SelectText, ResizeScale }; // The return value is not "None" during a drawing operation. // // The returned value is set by beginDraw(), after being determined // by calculateDrawType(). It is cleared in cancelShape() and endDraw(). DrawType drawType () const; bool hadSelectionBeforeDraw () const; // // Drawing // protected: // (overrides non-virtual method in kpTool) kpToolSelectionEnvironment *environ () const; // Returns whether a CTRL or SHIFT key is currently pressed. // Convenience method. bool controlOrShiftPressed () const; protected: // Deselects the current selection: // // 1. If it has no content, it is simply deleted. // 2. If it has content, it pushes it onto the document, adding the // necessary commands to the command history. // // ASSUMPTIONS: // 1. There is a current selection. // 2. You have not called giveContentIfNeeded() nor // addNeedingContentCommand() on the current selection. void pushOntoDocument (); // // The command lifecycle is as follows: // // 1. Ensure that the document has a selection, with or without content. // // 2. Call giveContentIfNeeded(). // // 3. Create the command. // // 4. Process user input, mutate the selection directly and update the // command with the user's input. // -// 5. When the drawing operatinon is complete, call addNeedingContentCommand() +// 5. When the drawing operation is complete, call addNeedingContentCommand() // with the command created in Step 3. // protected: // Returns a new instance of the give-the-selection-content command // that matches the current selection type. The command should not be // executed by this method. virtual kpAbstractSelectionContentCommand *newGiveContentCommand () const = 0; // Before changing a selection (e.g. moving or resizing), you must // ensure that it has content. Call this method to ensure that. // // If the selection has no content, this calls newGiveContentCommand() // and executes it. If the selection already has content, this does // nothing. // // ASSUMPTION: There is a selection. void giveContentIfNeeded (); // The name that should be given to command that is constructed in // response to a drag that creates a new selection. virtual QString nameOfCreateCommand () const = 0; // Add a command to the command history. // The command is not executed. // // If the prior call to giveContentIfNeeded() created content, this // will, in line with KolourPaint selection convention: // // 1. Adds a selection border creation command (this is a bit clever // and may overwrite the last "Undo" command instead -- see // kpCommandHistory::addCreateSelectionCommand()). // // 2. Group the content command created by giveContentIfNeeded() // with , as a kpMacroCommand also named . // // ASSUMPTION: giveContentIfNeeded() must have been called before // creating . void addNeedingContentCommand (kpCommand *cmd); protected: // Sets the selection border mode when no drawing operation is active. // // Subclasses may wish to reimplement but should still call the base // implementation. Reimplementations should wrap the whole // reimplementation in a kpViewManager::setQueueUpdates() block. virtual void setSelectionBorderForHaventBegunDraw (); private: // Returns the statusbar message from when no draw operation is in // progress. Calls operation() with "HaventBegunDrawUserMessage". // // (not const due to purely syntactic issue: it calls the non-const // operation(); it really acts like a const method though) QString haventBegunDrawUserMessage (); public: void begin () override; void end () override; public: void reselect () override; // // Drawing - Beginning a Drag // protected: // Called by calculateDrawType() to determine what type of draw type // is being started in response to a drag inside the bounding rectangle of // a selection. // // This implementation returns "Move". // // You are free to reimplement this and may choose to call this base // implementation or not. virtual DrawType calculateDrawTypeInsideSelection () const; // Called by beginDraw() to determine what type of draw type is // being started. The returned draw type is passed to operation(). // // This implementation behaves according to the first rule that matches: // // 1. If the cursor is on top of a selection resize handle and no modifiers // are held (i.e. not a smearing move draw type), it returns "ResizeScale". // // 2. If the cursor is inside the bounding rectangle of a selection, it // calls calculateDrawTypeInsideSelection(). // // 3. Otherwise, it returns "Create". // // You are free to reimplement this and may choose to call this base // implementation or not. Reimplementing allows you to support new // draw types for different types of selections (e.g. kpToolText // supports "SelectText"). It also allows you to make certain // drags (e.g. dragging in the middle of a selection) do nothing by // returning "None" instead of calling the base implementation. virtual DrawType calculateDrawType () const; public: void beginDraw () override; // // Drawing - Mouse Movement // public: void hover (const QPoint &point) override; void draw (const QPoint &thisPoint, const QPoint &lastPoint, const QRect &normalizedRect) override; // // Drawing - Ending a Drag // public: void cancelShape () override; void releasedAllButtons () override; protected: // Displays the right-mouse-button-triggered selection menu, re-entering // the event loop and blocking until the menu closes. // // This menu is a subset of the main window's Edit and Selection menus. // // WARNING: This may cause a re-entry of view/tool event handlers. // If you are calling this from a view/tool event handler, // either make all your handlers re-entrant or do not put any // code in your handler after the call. void popupRMBMenu (); public: void endDraw (const QPoint &thisPoint, const QRect &normalizedRect) override; // // Drawing - Operation Dispatching // protected: enum Operation { // // These may be called outside of a drawing operation where // drawType() will return None. // // Returns the message for the given draw type and operation. HaventBegunDrawUserMessage, SetCursor, // // Called to start, to end, or inside, a drawing operation. // BeginDraw, Draw, Cancel, EndDraw }; // (See the class API Doc for a description). virtual QVariant operation (DrawType drawType, Operation op, const QVariant &data1 = QVariant (), const QVariant &data2 = QVariant ()); // // Create // private: // Called by constructor to initialize the "Create" draw type. void initCreate (); // Called by destructor to uninitialize the "Create" draw type. void uninitCreate (); private: void beginCreate (); void endCreate (); protected: virtual QString haventBegunDrawUserMessageCreate () const = 0; private: void setCursorCreate (); protected: // Sets the selection border mode when beginning to drag to create a // selection. // // Subclasses may wish to reimplement but should still call the base // implementation. Reimplementations should wrap the whole // reimplementation in a kpViewManager::setQueueUpdates() block. virtual void setSelectionBorderForBeginDrawCreate (); private: void beginDrawCreate (); protected: // // If the drag has already been substantial enough to be considered as a // non-NOP drag (), you must return "true". // // If it has not, you should return whether you think the drag should // be started. This criteria usually includes "if // is not equal to startPoint()". // // If you are returning true, you must: // // 1. Set the document's selection (which may not have previously // existed) to the specified size. // // 2. Update the status bar by calling kpTool::setUserShapePoints(). // // If you return false, you are still permitted to do the above, // although it would be unusual (kpToolText does the above to allow a // single click -- with no dragging -- to create a new text box). // // The return value will be fed into the next call as . // // Arguments: // // 1. : // This is the same as currentPoint() but is set to startPoint() // if the mouse has not been moved much (6 manhatten length pixels // from startPoint() within a short period of time (200ms)). // This provides the accidental drag detection, referred to in the // class' API Doc. // // 2. : // This is as passed to kpTool::draw(). // virtual bool drawCreateMoreSelectionAndUpdateStatusBar ( bool dragAccepted, const QPoint &accidentalDragAdjustedPoint, const QRect &normalizedRect) = 0; void drawCreate (const QPoint &thisPoint, const QRect &normalizedRect); private slots: void delayedDrawCreate (); private: void cancelCreate (); void endDrawCreate (); private: QVariant operationCreate (Operation op, const QVariant &data1, const QVariant &data2); // // Move // private: // Called by constructor to initialize the "Move" draw type. void initMove (); // Called by destructor to uninitialize the "Move" draw type. void uninitMove (); private: void beginMove (); void endMove (); protected: virtual QString haventBegunDrawUserMessageMove () const = 0; private: void setCursorMove (); protected: // Sets the selection border mode when beginning to drag to move a // selection. // // Subclasses may wish to reimplement but should still call the base // implementation. Reimplementations should wrap the whole // reimplementation in a kpViewManager::setQueueUpdates() block. virtual void setSelectionBorderForBeginDrawMove (); private: void beginDrawMove (); private slots: void slotRMBMoveUpdateGUI (); private: void drawMove (const QPoint &thisPoint, const QRect &normalizedRect); private: void cancelMove (); protected: // Returns what the name of the command that moves -- but does not smear // (not holding SHIFT) -- the selection, should be. virtual QString nonSmearMoveCommandName () const; private: void endDrawMove (); private: QVariant operationMove (Operation op, const QVariant &data1, const QVariant &data2); // // Resize/Scale // private: int onSelectionResizeHandle () const; private: // Called by constructor to initialize the "Resize/Scale" draw type. void initResizeScale (); // Called by destructor to uninitialize the "Resize/Scale" draw type. void uninitResizeScale (); private: void beginResizeScale (); void endResizeScale (); protected: virtual QString haventBegunDrawUserMessageResizeScale () const = 0; private: void setCursorResizeScale (); protected: // Sets the selection border mode when beginning to drag to resize or // scale a selection. // // Subclasses may wish to reimplement but should still call the base // implementation. Reimplementations should wrap the whole // reimplementation in a kpViewManager::setQueueUpdates() block. virtual void setSelectionBorderForBeginDrawResizeScale (); private: void beginDrawResizeScale (); private: // drawResizeScaleCalculateNewSelectionPosSize() calls us with what the // x should be, but before any aspect maintenance // operations. // // specifies whether a horizontal grip is being // dragged. specifies whether a vertical grip is // being dragged. // // The selection before any resizing/scaling (before the sequence of // drags, where the mouse has been held down) is . // // The method should output its attempt at maintaining the aspect ratio. // We say "attempt" because it is constrained by the minimum allowed // size of the selection. void drawResizeScaleTryKeepAspect (int newWidth, int newHeight, bool horizontalGripDragged, bool verticalGripDragged, const kpAbstractSelection &originalSelection, int *newWidthOut, int *newHeightOut); void drawResizeScaleCalculateNewSelectionPosSize ( const kpAbstractSelection &originalSelection, int *newX, int *newY, int *newWidth, int *newHeight); void drawResizeScale (const QPoint &thisPoint, const QRect &normalizedRect); private: void cancelResizeScale (); void endDrawResizeScale (); private: QVariant operationResizeScale (Operation op, const QVariant &data1, const QVariant &data2); // // User Setting Selection Options // protected slots: virtual void slotIsOpaqueChanged (bool isOpaque) = 0; // // Keyboard Events // protected: // Reimplemented to trap Esc presses for deselecting the selection. // All other keypresses are passed to the base implementation. void keyPressEvent (QKeyEvent *e) override; private: struct kpAbstractSelectionToolPrivate * const d; }; #endif // kpAbstractSelectionTool_H diff --git a/tools/selection/kpAbstractSelectionToolPrivate.h b/tools/selection/kpAbstractSelectionToolPrivate.h index f949240a..58e737c8 100644 --- a/tools/selection/kpAbstractSelectionToolPrivate.h +++ b/tools/selection/kpAbstractSelectionToolPrivate.h @@ -1,91 +1,91 @@ /* 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. */ #ifndef kpAbstractSelectionToolPrivate_H #define kpAbstractSelectionToolPrivate_H #include "kpAbstractSelectionTool.h" #include class QTimer; class kpAbstractSelectionContentCommand; class kpToolSelectionMoveCommand; class kpToolSelectionResizeScaleCommand; class kpToolWidgetOpaqueOrTransparent; struct kpAbstractSelectionToolPrivate { kpAbstractSelectionTool::DrawType drawType; kpAbstractSelectionContentCommand *currentSelContentCommand; // Whether the drag has been substantial enough to be considered as a - // non-NOP drag. The "substantial enough" critera is draw-type + // non-NOP drag. The "substantial enough" criteria is draw-type // dependent and is usually based on how far the mouse has been // dragged. See kpAbstractSelectionTool's API Doc for details. bool dragAccepted; bool hadSelectionBeforeDraw; bool cancelledShapeButStillHoldingButtons; kpToolWidgetOpaqueOrTransparent *toolWidgetOpaqueOrTransparent; // // Create // QTimer *createNOPTimer; // // Move // kpToolSelectionMoveCommand *currentMoveCommand; bool currentMoveCommandIsSmear; QPoint startMoveDragFromSelectionTopLeft; QTimer *RMBMoveUpdateGUITimer; // // Resize / Scale // kpToolSelectionResizeScaleCommand *currentResizeScaleCommand; int resizeScaleType; }; #endif // kpAbstractSelectionToolPrivate_H diff --git a/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp b/tools/selection/kpAbstractSelectionTool_ResizeScale.cpp index f5e0609f..8fd1a56a 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. + // Calculate new width. *newWidth = originalSelection.width () + userXSign * (currentPoint ().x () - startPoint ().x ()); // Don't allow new width to be less than that kind of selection type's // minimum. *newWidth = qMax (originalSelection.minimumWidth (), *newWidth); // // Determine new height. // // Dragging top or bottom grip? // If top, positive Y drags decrease height. // If bottom, positive Y drags increase height. int userYSign = 0; if (d->resizeScaleType & kpView::Top) { userYSign = -1; } else if (d->resizeScaleType & kpView::Bottom) { userYSign = +1; } - // Calcluate new height. + // Calculate new height. *newHeight = originalSelection.height () + userYSign * (currentPoint ().y () - startPoint ().y ()); // Don't allow new height to be less than that kind of selection type's // minimum. *newHeight = qMax (originalSelection.minimumHeight (), *newHeight); // Keep aspect ratio? if (shiftPressed ()) { drawResizeScaleTryKeepAspect (*newWidth, *newHeight, (userXSign != 0)/*X or XY grip dragged*/, (userYSign != 0)/*Y or XY grip dragged*/, originalSelection, newWidth/*ptr*/, newHeight/*ptr*/); } *newX = originalSelection.x (); *newY = originalSelection.y (); // // Adjust x/y to new width/height for left/top resizes. // if (d->resizeScaleType & kpView::Left) { *newX -= (*newWidth - originalSelection.width ()); } if (d->resizeScaleType & kpView::Top) { *newY -= (*newHeight - originalSelection.height ()); } #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tnewX=" << *newX << " newY=" << *newY << " newWidth=" << *newWidth << " newHeight=" << *newHeight; #endif } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::drawResizeScale ( const QPoint &thisPoint, const QRect &/*normalizedRect*/) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\tresize/scale"; #endif kpAbstractSelection *sel = document ()->selection (); if (!d->dragAccepted && thisPoint == startPoint ()) { #if DEBUG_KP_TOOL_SELECTION && 1 qCDebug(kpLogTools) << "\t\tnop"; #endif setUserShapePoints (QPoint (sel->width (), sel->height ())); return; } giveContentIfNeeded (); if (!d->currentResizeScaleCommand) { d->currentResizeScaleCommand = new kpToolSelectionResizeScaleCommand (environ ()->commandEnvironment ()); } const kpAbstractSelection *originalSelection = d->currentResizeScaleCommand->originalSelection (); // There is nothing illegal about position (-1,-1) but why not. int newX = -1, newY = -1, newWidth = 0, newHeight = 0; // This should change all of the above values. drawResizeScaleCalculateNewSelectionPosSize ( *originalSelection, &newX, &newY, &newWidth, &newHeight); viewManager ()->setFastUpdates (); { d->currentResizeScaleCommand->resizeAndMoveTo ( newWidth, newHeight, QPoint (newX, newY), true/*smooth scale delayed*/); } viewManager ()->restoreFastUpdates (); setUserShapePoints (QPoint (originalSelection->width (), originalSelection->height ()), QPoint (newWidth, newHeight), false/*don't set size*/); setUserShapeSize (newWidth - originalSelection->width (), newHeight - originalSelection->height ()); d->dragAccepted = true; } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::cancelResizeScale () { #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\twas resize/scale sel - kill"; #endif // NOP drag? if (!d->currentResizeScaleCommand) { return; } #if DEBUG_KP_TOOL_SELECTION qCDebug(kpLogTools) << "\t\tundo currentResizeScaleCommand"; #endif d->currentResizeScaleCommand->finalize (); // (unneeded but let's be safe) d->currentResizeScaleCommand->unexecute (); delete d->currentResizeScaleCommand; d->currentResizeScaleCommand = nullptr; } //--------------------------------------------------------------------- // private void kpAbstractSelectionTool::endDrawResizeScale () { // NOP drag? if (!d->currentResizeScaleCommand) { return; } d->currentResizeScaleCommand->finalize (); addNeedingContentCommand (d->currentResizeScaleCommand); d->currentResizeScaleCommand = nullptr; } //--------------------------------------------------------------------- // private QVariant kpAbstractSelectionTool::operationResizeScale (Operation op, const QVariant &data1, const QVariant &data2) { (void) data1; (void) data2; switch (op) { case HaventBegunDrawUserMessage: return /*virtual*/haventBegunDrawUserMessageResizeScale (); case SetCursor: setCursorResizeScale (); break; case BeginDraw: beginDrawResizeScale (); break; case Draw: drawResizeScale (currentPoint (), normalizedRect ()); break; case Cancel: cancelResizeScale (); break; case EndDraw: endDrawResizeScale (); break; default: Q_ASSERT (!"Unhandled operation"); break; } return {}; } //--------------------------------------------------------------------- diff --git a/tools/selection/text/kpToolText_KeyboardEvents.cpp b/tools/selection/text/kpToolText_KeyboardEvents.cpp index 246fee8f..7b80cda9 100644 --- a/tools/selection/text/kpToolText_KeyboardEvents.cpp +++ b/tools/selection/text/kpToolText_KeyboardEvents.cpp @@ -1,212 +1,212 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_TOOL_TEXT 0 #include "tools/selection/text/kpToolText.h" #include "kpToolTextPrivate.h" #include #include #include "kpLogCategories.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "commands/tools/selection/text/kpToolTextBackspaceCommand.h" #include "commands/tools/selection/text/kpToolTextChangeStyleCommand.h" #include "commands/tools/selection/kpToolSelectionCreateCommand.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "commands/tools/selection/text/kpToolTextDeleteCommand.h" #include "commands/tools/selection/text/kpToolTextEnterCommand.h" #include "commands/tools/selection/text/kpToolTextInsertCommand.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #include // protected virtual [base kpTool] bool kpToolText::viewEvent (QEvent *e) { const bool isShortcutOverrideEvent = (e->type () == QEvent::ShortcutOverride); const bool haveTextSelection = document ()->textSelection (); #if DEBUG_KP_TOOL_TEXT && 0 qCDebug(kpLogTools) << "kpToolText::viewEvent() type=" << e->type () << " isShortcutOverrideEvent=" << isShortcutOverrideEvent << " haveTextSel=" << haveTextSelection; #endif if (!isShortcutOverrideEvent || !haveTextSelection) { return kpAbstractSelectionTool::viewEvent (e); } auto *ke = dynamic_cast (e); #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::viewEvent() key=" << ke->key () << " modifiers=" << ke->modifiers () << " QChar.isPrint()=" << QChar (ke->key ()).isPrint (); #endif // Can't be shortcut? if (ke->key () == 0 && ke->key () == Qt::Key_unknown) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tcan't be shortcut - safe to not react"; #endif } // Normal letter (w/ or w/o shift, keypad button ok)? // TODO: don't like this check else if ((ke->modifiers () & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) == 0 && !( ke->text ().isEmpty ())) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tis text - grab"; #endif e->accept (); } else { - // Strickly speaking, we should grab stuff like the arrow keys + // Strictly speaking, we should grab stuff like the arrow keys // and enter. In any case, should be done down in kpTool (as that // uses arrow keys too). } return kpAbstractSelectionTool::event (e); } // protected virtual [base kpAbstractSelectionTool] void kpToolText::keyPressEvent (QKeyEvent *e) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "kpToolText::keyPressEvent(e->text='" << e->text () << "')"; #endif e->ignore (); if (hasBegunDraw ()) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\talready began draw with mouse - passing on event to kpTool"; #endif kpAbstractSelectionTool::keyPressEvent (e); return; } kpTextSelection * const textSel = document ()->textSelection (); if (!textSel) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tno text sel - passing on event to kpTool"; #endif //if (hasBegunShape ()) // endShape (currentPoint (), normalizedRect ()); kpAbstractSelectionTool::keyPressEvent (e); return; } // (All handle.+()'s require this info) const QList textLines = textSel->textLines (); const int cursorRow = viewManager ()->textCursorRow (); const int cursorCol = viewManager ()->textCursorCol (); // TODO: KTextEdit::keyPressEvent() uses KStandardShortcut instead of hardcoding; same fix for kpTool? switch (e->key ()) { case Qt::Key_Up: handleUpKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Down: handleDownKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Left: handleLeftKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Right: handleRightKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Home: handleHomeKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_End: handleEndKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Backspace: handleBackspaceKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Delete: handleDeleteKeyPress (e, textLines, cursorRow, cursorCol); break; case Qt::Key_Enter: case Qt::Key_Return: handleEnterKeyPress (e, textLines, cursorRow, cursorCol); break; default: handleTextTyped (e, textLines, cursorRow, cursorCol); break; } if (!e->isAccepted ()) { #if DEBUG_KP_TOOL_TEXT qCDebug(kpLogTools) << "\tkey processing did not accept (text was '" << e->text () << "') - passing on event to kpAbstractSelectionTool"; #endif //if (hasBegunShape ()) // endShape (currentPoint (), normalizedRect ()); kpAbstractSelectionTool::keyPressEvent (e); return; } } diff --git a/views/kpViewPrivate.h b/views/kpViewPrivate.h index b3877368..565f0394 100644 --- a/views/kpViewPrivate.h +++ b/views/kpViewPrivate.h @@ -1,77 +1,77 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef kpViewPrivate_H #define kpViewPrivate_H #include #include #include #include class kpDocument; class kpToolToolBar; class kpView; class kpViewScrollableContainer; struct kpViewPrivate { // sync: kpView::paintEvent() // // Normally, these pointers must be valid while the kpView is alive. // Generally, the objects they point to are deleted only after kpView // is deleted. // // However, sometimes we use deleteLater() for the kpView. // Before the delayed deletion is executed, those objects are deleted // and then our paintEvent() is called. paintEvent() must therefore // have some way of realising that those objects have been deleted so - // we use guardded pointers. + // we use guarded pointers. // // For more details, see SVN commit: // "r385274 | dang | 2005-02-02 22:08:27 +1100 (Wed, 02 Feb 2005) | 21 lines". QPointer document; QPointer toolToolBar; QPointer viewManager; QPointer buddyView; QPointer scrollableContainer; int hzoom, vzoom; QPoint origin; bool showGrid; bool isBuddyViewScrollableContainerRectangleShown; QRect buddyViewScrollableContainerRectangle; QRegion queuedUpdateArea; }; #endif // kpViewPrivate_H