File indexing completed on 2024-04-28 04:20:20

0001 /*
0002    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0003    Copyright (c) 2011 Martin Koller <kollix@aon.at>
0004    All rights reserved.
0005 
0006    Redistribution and use in source and binary forms, with or without
0007    modification, are permitted provided that the following conditions
0008    are met:
0009 
0010    1. Redistributions of source code must retain the above copyright
0011       notice, this list of conditions and the following disclaimer.
0012    2. Redistributions in binary form must reproduce the above copyright
0013       notice, this list of conditions and the following disclaimer in the
0014       documentation and/or other materials provided with the distribution.
0015 
0016    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0017    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0018    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0019    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0020    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0021    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0022    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0023    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0024    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0025    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0026 */
0027 
0028 
0029 #include "kpMainWindow.h"
0030 #include "kpMainWindowPrivate.h"
0031 
0032 #include <QApplication>
0033 #include <QClipboard>
0034 #include <QFontMetrics>
0035 #include <QImage>
0036 #include <QList>
0037 #include <QMenu>
0038 #include <QScrollBar>
0039 #include <QScreen>
0040 
0041 #include "kpLogCategories.h"
0042 #include <KMessageBox>
0043 #include <KStandardAction>
0044 #include <KActionCollection>
0045 #include <KXMLGUIFactory>
0046 #include <KLocalizedString>
0047 
0048 #include "layers/selections/image/kpAbstractImageSelection.h"
0049 #include "widgets/toolbars/kpColorToolBar.h"
0050 #include "commands/kpCommandHistory.h"
0051 #include "document/kpDocument.h"
0052 #include "imagelib/kpDocumentMetaInfo.h"
0053 #include "document/kpDocumentSaveOptions.h"
0054 #include "layers/selections/image/kpImageSelectionTransparency.h"
0055 #include "commands/kpMacroCommand.h"
0056 #include "pixmapfx/kpPixmapFX.h"
0057 #include "layers/selections/image/kpRectangularImageSelection.h"
0058 #include "layers/selections/kpSelectionDrag.h"
0059 #include "generic/kpSetOverrideCursorSaver.h"
0060 #include "layers/selections/text/kpTextSelection.h"
0061 #include "tools/kpTool.h"
0062 #include "commands/tools/selection/text/kpToolTextGiveContentCommand.h"
0063 #include "commands/tools/selection/kpToolSelectionCreateCommand.h"
0064 #include "commands/tools/selection/kpToolSelectionDestroyCommand.h"
0065 #include "commands/tools/selection/text/kpToolTextEnterCommand.h"
0066 #include "commands/tools/selection/text/kpToolTextInsertCommand.h"
0067 #include "imagelib/transforms/kpTransformCrop.h"
0068 #include "commands/imagelib/transforms/kpTransformResizeScaleCommand.h"
0069 #include "views/manager/kpViewManager.h"
0070 #include "kpViewScrollableContainer.h"
0071 #include "views/kpZoomedView.h"
0072 
0073 //---------------------------------------------------------------------
0074 
0075 // private
0076 void kpMainWindow::setupEditMenuActions ()
0077 {
0078     KActionCollection *ac = actionCollection ();
0079 
0080 
0081     // Undo/Redo
0082     // CONFIG: Need GUI for config history size.
0083     d->commandHistory = new kpCommandHistory (true/*read config*/, this);
0084 
0085     if (d->configFirstTime)
0086     {
0087         // (so that cfg-file-editing user can modify in the meantime)
0088         d->commandHistory->writeConfig ();
0089     }
0090 
0091 
0092     d->actionCut = KStandardAction::cut (this, SLOT (slotCut()), ac);
0093     d->actionCopy = KStandardAction::copy (this, SLOT (slotCopy()), ac);
0094     d->actionPaste = KStandardAction::paste (this, SLOT (slotPaste()), ac);
0095     d->actionPasteInNewWindow = ac->addAction (QStringLiteral("edit_paste_in_new_window"));
0096     d->actionPasteInNewWindow->setText (i18n ("Paste in &New Window"));
0097     connect (d->actionPasteInNewWindow, &QAction::triggered,
0098              this, &kpMainWindow::slotPasteInNewWindow);
0099     ac->setDefaultShortcut (d->actionPasteInNewWindow, Qt::CTRL | Qt::SHIFT | Qt::Key_V);
0100 
0101     //d->actionDelete = KStandardAction::clear (this, SLOT (slotDelete()), ac);
0102     d->actionDelete = ac->addAction (QStringLiteral("edit_clear"));
0103     d->actionDelete->setText (i18n ("&Delete Selection"));
0104     connect (d->actionDelete, &QAction::triggered, this, &kpMainWindow::slotDelete);
0105 
0106     d->actionSelectAll = KStandardAction::selectAll (this, SLOT (slotSelectAll()), ac);
0107     d->actionDeselect = KStandardAction::deselect (this, SLOT (slotDeselect()), ac);
0108 
0109 
0110     d->actionCopyToFile = ac->addAction (QStringLiteral("edit_copy_to_file"));
0111     d->actionCopyToFile->setText (i18n ("C&opy to File..."));
0112     connect (d->actionCopyToFile, &QAction::triggered, this, &kpMainWindow::slotCopyToFile);
0113 
0114     d->actionPasteFromFile = ac->addAction (QStringLiteral("edit_paste_from_file"));
0115     d->actionPasteFromFile->setText (i18n ("Paste &From File..."));
0116     connect (d->actionPasteFromFile, &QAction::triggered, this, &kpMainWindow::slotPasteFromFile);
0117 
0118 
0119     d->editMenuDocumentActionsEnabled = false;
0120     enableEditMenuDocumentActions (false);
0121 
0122     // Paste should always be enabled, as long as there is something to paste
0123     // (independent of whether we have a document or not)
0124     connect (QApplication::clipboard(), &QClipboard::dataChanged,
0125              this, &kpMainWindow::slotEnablePaste);
0126 
0127     slotEnablePaste ();
0128 }
0129 
0130 //---------------------------------------------------------------------
0131 
0132 // private
0133 void kpMainWindow::enableEditMenuDocumentActions (bool enable)
0134 {
0135     // d->actionCut
0136     // d->actionCopy
0137     // d->actionPaste
0138     // d->actionPasteInNewWindow
0139 
0140     // d->actionDelete
0141 
0142     d->actionSelectAll->setEnabled (enable);
0143     // d->actionDeselect
0144 
0145     d->editMenuDocumentActionsEnabled = enable;
0146 
0147     // d->actionCopyToFile
0148 
0149     // Unlike d->actionPaste, we disable this if there is no document.
0150     // This is because "File / Open" would do the same thing, if there is
0151     // no document.
0152     d->actionPasteFromFile->setEnabled (enable);
0153 }
0154 
0155 //---------------------------------------------------------------------
0156 
0157 // public
0158 QMenu *kpMainWindow::selectionToolRMBMenu ()
0159 {
0160     return qobject_cast <QMenu *> (guiFactory ()->container (QStringLiteral("selectionToolRMBMenu"), this));
0161 }
0162 
0163 //---------------------------------------------------------------------
0164 
0165 // private slot
0166 void kpMainWindow::slotCut ()
0167 {
0168 #if DEBUG_KP_MAIN_WINDOW && 1
0169     qCDebug(kpLogMainWindow) << "kpMainWindow::slotCut() CALLED";
0170 #endif
0171 
0172     kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
0173 
0174     Q_ASSERT (d->document && d->document->selection ());
0175 
0176     toolEndShape ();
0177 
0178     slotCopy ();
0179     slotDelete ();
0180 }
0181 
0182 //---------------------------------------------------------------------
0183 
0184 static QMimeData *NewTextMimeData (const QString &text)
0185 {
0186     auto *md = new QMimeData ();
0187     md->setText (text);
0188     return md;
0189 }
0190 
0191 //---------------------------------------------------------------------
0192 
0193 // private slot
0194 void kpMainWindow::slotCopy ()
0195 {
0196 #if DEBUG_KP_MAIN_WINDOW && 1
0197     qCDebug(kpLogMainWindow) << "kpMainWindow::slotCopy() CALLED";
0198 #endif
0199 
0200     kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
0201 
0202     Q_ASSERT (d->document && d->document->selection ());
0203 
0204     toolEndShape ();
0205 
0206     kpAbstractSelection *sel = d->document->selection ()->clone ();
0207 
0208     if (dynamic_cast <kpTextSelection *> (sel))
0209     {
0210         auto *textSel = dynamic_cast <kpTextSelection *> (sel);
0211         if (!textSel->text ().isEmpty ())
0212         {
0213             QApplication::clipboard ()->setMimeData (
0214                 ::NewTextMimeData (textSel->text ()),
0215                 QClipboard::Clipboard);
0216 
0217             // SYNC: Normally, users highlight text and press CTRL+C.
0218             //       Highlighting text copies it to the X11 "middle
0219             //       mouse button" clipboard.  CTRL+C copies it to the
0220             //       separate, Windows-like "CTRL+V" clipboard.
0221             //
0222             //       However, KolourPaint doesn't support highlighting.
0223             //       So when they press CTRL+C to copy all text, simulate
0224             //       the highlighting by copying the text to the "middle
0225             //       mouse button" clipboard.  We don't do this for images
0226             //       as no one ever middle-mouse-pastes images.
0227             //
0228             //       Note that we don't share the QMimeData pointer with
0229             //       the above in case Qt doesn't expect it.
0230             //
0231             //       Once we change KolourPaint to support highlighted text
0232             //       and CTRL+C to copy only the highlighted text, delete
0233             //       this code.
0234             QApplication::clipboard ()->setMimeData (
0235                 ::NewTextMimeData (textSel->text ()),
0236                 QClipboard::Selection);
0237         }
0238     }
0239     else if (dynamic_cast <kpAbstractImageSelection *> (sel))
0240     {
0241         auto *imageSel = dynamic_cast <kpAbstractImageSelection *> (sel);
0242 
0243         // Transparency doesn't get sent across the aether so nuke it now
0244         // so that transparency mask doesn't get needlessly recalculated
0245         // if we ever call sel.setBaseImage().
0246         imageSel->setTransparency (kpImageSelectionTransparency ());
0247 
0248         kpImage rawImage;
0249 
0250         if (imageSel->hasContent ()) {
0251             rawImage = imageSel->baseImage ();
0252         }
0253         else {
0254             rawImage = d->document->getSelectedBaseImage ();
0255         }
0256 
0257         imageSel->setBaseImage ( rawImage );
0258 
0259         QApplication::clipboard ()->setMimeData (
0260             new kpSelectionDrag (*imageSel),
0261             QClipboard::Clipboard);
0262     }
0263     else {
0264         Q_ASSERT (!"Unknown selection type");
0265     }
0266 
0267     delete sel;
0268 }
0269 
0270 //---------------------------------------------------------------------
0271 
0272 // private slot
0273 void kpMainWindow::slotEnablePaste ()
0274 {
0275     const QMimeData *md =
0276         QApplication::clipboard()->mimeData(QClipboard::Clipboard);
0277 
0278     // It's faster to test for QMimeData::hasText() first due to the
0279     // lazy evaluation of the '||' operator.
0280     const bool shouldEnable = md && (md->hasText() || kpSelectionDrag::canDecode(md));
0281 
0282     d->actionPasteInNewWindow->setEnabled(shouldEnable);
0283     d->actionPaste->setEnabled(shouldEnable);
0284 }
0285 
0286 //---------------------------------------------------------------------
0287 
0288 // private
0289 QRect kpMainWindow::calcUsefulPasteRect (int imageWidth, int imageHeight)
0290 {
0291 #if DEBUG_KP_MAIN_WINDOW && 1
0292     qCDebug(kpLogMainWindow) << "kpMainWindow::calcUsefulPasteRect("
0293                << imageWidth << "," << imageHeight
0294                << ")";
0295 #endif
0296     Q_ASSERT (d->document);
0297 
0298     // TODO: 1st choice is to paste sel near but not overlapping last deselect point
0299 
0300     if (d->mainView && d->scrollView)
0301     {
0302         const QPoint viewTopLeft (d->scrollView->horizontalScrollBar()->value (),
0303                                   d->scrollView->verticalScrollBar()->value ());
0304 
0305         const QPoint docTopLeft = d->mainView->transformViewToDoc (viewTopLeft);
0306 
0307         if ((docTopLeft.x () + imageWidth <= d->document->width () &&
0308              docTopLeft.y () + imageHeight <= d->document->height ()) ||
0309             imageWidth <= docTopLeft.x () ||
0310             imageHeight <= docTopLeft.y ())
0311         {
0312             return  {docTopLeft.x (), docTopLeft.y (),  imageWidth, imageHeight};
0313         }
0314     }
0315 
0316     return  {0, 0, imageWidth, imageHeight};
0317 }
0318 
0319 //---------------------------------------------------------------------
0320 
0321 // private
0322 void kpMainWindow::paste(const kpAbstractSelection &sel, bool forceTopLeft)
0323 {
0324 #if DEBUG_KP_MAIN_WINDOW && 1
0325     qCDebug(kpLogMainWindow) << "kpMainWindow::paste(forceTopLeft=" << forceTopLeft << ")";
0326 #endif
0327 
0328     kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
0329 
0330     toolEndShape ();
0331 
0332     //
0333     // Make sure we've got a document (esp. with File/Close)
0334     //
0335 
0336     if (!d->document)
0337     {
0338         auto *newDoc = new kpDocument (
0339             sel.width (), sel.height (), documentEnvironment ());
0340 
0341         // will also create viewManager
0342         setDocument (newDoc);
0343     }
0344 
0345     //
0346     // Paste as new selection
0347     //
0348 
0349     const auto *imageSel = dynamic_cast <const kpAbstractImageSelection *> (&sel);
0350 
0351     if (imageSel && imageSel->hasContent () && imageSel->transparency ().isTransparent ())
0352     {
0353         d->colorToolBar->flashColorSimilarityToolBarItem ();
0354     }
0355 
0356     kpAbstractSelection *selInUsefulPos = sel.clone ();
0357     if (!forceTopLeft) {
0358         selInUsefulPos->moveTo (calcUsefulPasteRect (sel.width (), sel.height ()).topLeft ());
0359     }
0360     // TODO: Should use kpCommandHistory::addCreateSelectionCommand(),
0361     //       as well, to really support pasting selection borders.
0362     addDeselectFirstCommand (new kpToolSelectionCreateCommand (
0363         dynamic_cast <kpTextSelection *> (selInUsefulPos) ?
0364             i18n ("Text: Create Box") :
0365             i18n ("Selection: Create"),
0366         *selInUsefulPos,
0367         commandEnvironment ()));
0368     delete selInUsefulPos;
0369 
0370 
0371 #if DEBUG_KP_MAIN_WINDOW && 1
0372     qCDebug(kpLogMainWindow) << "sel.size=" << QSize (sel.width (), sel.height ())
0373                << " document.size="
0374                << QSize (d->document->width (), d->document->height ());
0375 #endif
0376 
0377     // If the selection is bigger than the document, automatically
0378     // resize the document (with the option of Undo'ing) to fit
0379     // the selection.
0380     //
0381     // No annoying dialog necessary.
0382     //
0383     if (sel.width () > d->document->width () ||
0384         sel.height () > d->document->height ())
0385     {
0386         d->commandHistory->addCommand (
0387             new kpTransformResizeScaleCommand (
0388                 false/*act on doc, not sel*/,
0389                 qMax (sel.width (), d->document->width ()),
0390                 qMax (sel.height (), d->document->height ()),
0391                 kpTransformResizeScaleCommand::Resize,
0392                 commandEnvironment ()));
0393     }
0394 }
0395 
0396 //---------------------------------------------------------------------
0397 
0398 // public
0399 void kpMainWindow::pasteText (const QString &text,
0400                               bool forceNewTextSelection,
0401                               const QPoint &newTextSelectionTopLeft)
0402 {
0403 #if DEBUG_KP_MAIN_WINDOW && 1
0404     qCDebug(kpLogMainWindow) << "kpMainWindow::pasteText(" << text
0405                << ",forceNewTextSelection=" << forceNewTextSelection
0406                << ",newTextSelectionTopLeft=" << newTextSelectionTopLeft
0407                << ")";
0408 #endif
0409 
0410     if ( text.isEmpty() ) {
0411         return;
0412     }
0413 
0414     kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
0415 
0416     toolEndShape ();
0417 
0418     const QStringList textLines = text.split(QLatin1Char('\n'));
0419 
0420     if (!forceNewTextSelection &&
0421         d->document && d->document->textSelection () &&
0422         d->commandHistory && d->viewManager)
0423     {
0424     #if DEBUG_KP_MAIN_WINDOW && 1
0425         qCDebug(kpLogMainWindow) << "\treusing existing Text Selection";
0426     #endif
0427 
0428         d->viewManager->setQueueUpdates();
0429 
0430         kpTextSelection *textSel = d->document->textSelection ();
0431         if (!textSel->hasContent ())
0432         {
0433         #if DEBUG_KP_MAIN_WINDOW && 1
0434             qCDebug(kpLogMainWindow) << "\t\tneeds content";
0435         #endif
0436             commandHistory ()->addCreateSelectionCommand (
0437                 new kpToolSelectionCreateCommand (
0438                     i18n ("Text: Create Box"),
0439                     *textSel,
0440                     commandEnvironment ()),
0441                 false/*no exec*/);
0442         }
0443 
0444         kpMacroCommand *macroCmd = new kpMacroCommand (i18n ("Text: Paste"),
0445             commandEnvironment ());
0446         // (yes, this is the same check as the previous "if")
0447         if (!textSel->hasContent ())
0448         {
0449             kpCommand *giveContentCmd = new kpToolTextGiveContentCommand (
0450                 *textSel,
0451                 QString ()/*uninteresting child of macro cmd*/,
0452                 commandEnvironment ());
0453             giveContentCmd->execute ();
0454 
0455             macroCmd->addCommand (giveContentCmd);
0456         }
0457 
0458         for (int i = 0; i < textLines.size(); i++)
0459         {
0460             if (i > 0)
0461             {
0462                 macroCmd->addCommand (
0463                     new kpToolTextEnterCommand (
0464                         QString()/*uninteresting child of macroCmd*/,
0465                         d->viewManager->textCursorRow (),
0466                         d->viewManager->textCursorCol (),
0467                         kpToolTextEnterCommand::AddEnterNow,
0468                         commandEnvironment ()));
0469             }
0470 
0471             macroCmd->addCommand (
0472                 new kpToolTextInsertCommand (
0473                     QString()/*uninteresting child of macroCmd*/,
0474                     d->viewManager->textCursorRow (),
0475                     d->viewManager->textCursorCol (),
0476                     textLines [i],
0477                     commandEnvironment ()));
0478         }
0479 
0480         d->commandHistory->addCommand (macroCmd, false/*no exec*/);
0481 
0482         d->viewManager->restoreQueueUpdates();
0483     }
0484     else
0485     {
0486     #if DEBUG_KP_MAIN_WINDOW && 1
0487         qCDebug(kpLogMainWindow) << "\tcreating Text Selection";
0488     #endif
0489 
0490         const kpTextStyle ts = textStyle ();
0491         const QFontMetrics fontMetrics = ts.fontMetrics ();
0492 
0493         int height = textLines.size () * fontMetrics.height ();
0494         if (textLines.size () >= 1) {
0495             height += (textLines.size () - 1) * fontMetrics.leading ();
0496         }
0497 
0498         int width = 0;
0499         for (const QString &str : textLines)
0500           width = std::max(width, fontMetrics.horizontalAdvance(str));
0501 
0502         // limit the size to avoid memory overflow
0503         const QSize desktopSize = QApplication::primaryScreen()->virtualSize();
0504         width = qMin(qMax(desktopSize.width(), d->document ? d->document->width() : 0), width);
0505         height = qMin(qMax(desktopSize.height(), d->document ? d->document->height() : 0), height);
0506 
0507         const int selWidth = qMax (kpTextSelection::MinimumWidthForTextStyle (ts),
0508                                    width + kpTextSelection::TextBorderSize () * 2);
0509         const int selHeight = qMax (kpTextSelection::MinimumHeightForTextStyle (ts),
0510                                     height + kpTextSelection::TextBorderSize () * 2);
0511         kpTextSelection newTextSel (QRect (0, 0, selWidth, selHeight),
0512             textLines,
0513             ts);
0514 
0515         if (newTextSelectionTopLeft != KP_INVALID_POINT)
0516         {
0517             newTextSel.moveTo (newTextSelectionTopLeft);
0518             paste (newTextSel, true/*force topLeft*/);
0519         }
0520         else
0521         {
0522             paste (newTextSel);
0523         }
0524     }
0525 }
0526 
0527 //---------------------------------------------------------------------
0528 
0529 // public
0530 void kpMainWindow::pasteTextAt (const QString &text, const QPoint &point,
0531                                 bool allowNewTextSelectionPointShift)
0532 {
0533 #if DEBUG_KP_MAIN_WINDOW && 1
0534     qCDebug(kpLogMainWindow) << "kpMainWindow::pasteTextAt(" << text
0535                << ",point=" << point
0536                << ",allowNewTextSelectionPointShift="
0537                << allowNewTextSelectionPointShift
0538                << ")";
0539 #endif
0540 
0541     kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
0542 
0543     toolEndShape ();
0544 
0545 
0546     if (d->document &&
0547         d->document->textSelection () &&
0548         d->document->textSelection ()->pointIsInTextArea (point))
0549     {
0550         kpTextSelection *textSel = d->document->textSelection ();
0551 
0552         int row, col;
0553 
0554         if (textSel->hasContent ())
0555         {
0556             row = textSel->closestTextRowForPoint (point);
0557             col = textSel->closestTextColForPoint (point);
0558         }
0559         else
0560         {
0561             row = col = 0;
0562         }
0563 
0564         d->viewManager->setTextCursorPosition (row, col);
0565 
0566         pasteText (text);
0567     }
0568     else
0569     {
0570         QPoint pointToUse = point;
0571 
0572         if (allowNewTextSelectionPointShift)
0573         {
0574             // TODO: In terms of doc pixels, would be inconsistent behaviour
0575             //       based on zoomLevel of view.
0576             // pointToUse -= QPoint (-view->selectionResizeHandleAtomicSize (),
0577             //                       -view->selectionResizeHandleAtomicSize ());
0578         }
0579 
0580         pasteText (text, true/*force new text selection*/, pointToUse);
0581     }
0582 }
0583 
0584 //---------------------------------------------------------------------
0585 // public slot
0586 
0587 void kpMainWindow::slotPaste()
0588 {
0589     kpSetOverrideCursorSaver cursorSaver(Qt::WaitCursor);
0590 
0591     toolEndShape();
0592 
0593     const QMimeData *mimeData = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
0594 
0595     kpAbstractImageSelection *sel = kpSelectionDrag::decode(mimeData);
0596     if ( sel )
0597     {
0598         sel->setTransparency(imageSelectionTransparency());
0599         paste(*sel);
0600         delete sel;
0601     }
0602     else if ( mimeData->hasText() )
0603     {
0604         pasteText(mimeData->text());
0605     }
0606     else
0607     {
0608         kpSetOverrideCursorSaver cursorSaver(Qt::ArrowCursor);
0609 
0610         KMessageBox::error(this,
0611             i18n("<qt>KolourPaint cannot paste the contents of"
0612                  " the clipboard as it has an unknown format.</qt>"),
0613             i18n("Cannot Paste"));
0614     }
0615 }
0616 
0617 //---------------------------------------------------------------------
0618 
0619 // private slot
0620 void kpMainWindow::slotPasteInNewWindow ()
0621 {
0622 #if DEBUG_KP_MAIN_WINDOW && 1
0623     qCDebug(kpLogMainWindow) << "kpMainWindow::slotPasteInNewWindow() CALLED";
0624 #endif
0625 
0626     kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
0627 
0628     toolEndShape ();
0629 
0630     //
0631     // Pasting must ensure that:
0632     //
0633     // Requirement 1. the document is the same size as the image to be pasted.
0634     // Requirement 2. transparent pixels in the image must remain as transparent.
0635     //
0636 
0637     auto *win = new kpMainWindow (nullptr/*no document*/);
0638     win->show ();
0639 
0640     // Make "Edit / Paste in New Window" always paste white pixels as white.
0641     // Don't let selection transparency get in the way and paste them as
0642     // transparent.
0643     kpImageSelectionTransparency transparency = win->imageSelectionTransparency ();
0644     if (transparency.isTransparent ())
0645     {
0646     #if DEBUG_KP_MAIN_WINDOW && 1
0647         qCDebug(kpLogMainWindow) << "\tchanging image selection transparency to opaque";
0648     #endif
0649         transparency.setOpaque ();
0650         // Since we are setting selection transparency programmatically
0651         // -- as opposed to in response to user input -- this will not
0652         // affect the selection transparency tool option widget's "last used"
0653         // config setting.
0654         win->setImageSelectionTransparency (transparency);
0655     }
0656 
0657     // (this handles Requirement 1. above)
0658     win->slotPaste ();
0659 
0660     // if slotPaste could not decode clipboard data, no document was created
0661     if ( win->document() )
0662     {
0663       // (this handles Requirement 2. above;
0664       //  slotDeselect() is not enough unless the document is filled with the
0665       //  transparent color in advance)
0666       win->slotCrop();
0667     }
0668 }
0669 
0670 //---------------------------------------------------------------------
0671 
0672 // public slot
0673 void kpMainWindow::slotDelete ()
0674 {
0675 #if DEBUG_KP_MAIN_WINDOW && 1
0676     qCDebug(kpLogMainWindow) << "kpMainWindow::slotDelete() CALLED";
0677 #endif
0678     if (!d->actionDelete->isEnabled ())
0679     {
0680     #if DEBUG_KP_MAIN_WINDOW && 1
0681         qCDebug(kpLogMainWindow) << "\taction not enabled - was probably called from kpTool::keyPressEvent()";
0682     #endif
0683         return;
0684     }
0685 
0686     Q_ASSERT (d->document && d->document->selection ());
0687 
0688     toolEndShape ();
0689 
0690     addImageOrSelectionCommand (new kpToolSelectionDestroyCommand (
0691         d->document->textSelection () ?
0692             i18n ("Text: Delete Box") :  // not to be confused with i18n ("Text: Delete")
0693             i18n ("Selection: Delete"),
0694         false/*no push onto doc*/,
0695         commandEnvironment ()));
0696 }
0697 
0698 //---------------------------------------------------------------------
0699 
0700 // private slot
0701 void kpMainWindow::slotSelectAll ()
0702 {
0703 #if DEBUG_KP_MAIN_WINDOW && 1
0704     qCDebug(kpLogMainWindow) << "kpMainWindow::slotSelectAll() CALLED";
0705 #endif
0706     Q_ASSERT (d->document);
0707 
0708     toolEndShape ();
0709 
0710     if (d->document->selection ()) {
0711         slotDeselect ();
0712     }
0713 
0714     // just the border - don't actually pull image from doc yet
0715     d->document->setSelection (
0716         kpRectangularImageSelection (d->document->rect (),
0717             imageSelectionTransparency ()));
0718 
0719     if (tool ()) {
0720         tool ()->somethingBelowTheCursorChanged ();
0721     }
0722 }
0723 
0724 //---------------------------------------------------------------------
0725 
0726 // private
0727 void kpMainWindow::addDeselectFirstCommand (kpCommand *cmd)
0728 {
0729 #if DEBUG_KP_MAIN_WINDOW && 1
0730     qCDebug(kpLogMainWindow) << "kpMainWindow::addDeselectFirstCommand("
0731                << cmd
0732                << ")";
0733 #endif
0734 
0735 
0736     kpAbstractSelection *sel = d->document->selection ();
0737 
0738 #if DEBUG_KP_MAIN_WINDOW && 1
0739     qCDebug(kpLogMainWindow) << "\tsel=" << sel;
0740 #endif
0741 
0742     if (sel)
0743     {
0744         // if you just dragged out something with no action then
0745         // forget the drag
0746         if (!sel->hasContent ())
0747         {
0748         #if DEBUG_KP_MAIN_WINDOW && 1
0749             qCDebug(kpLogMainWindow) << "\tjust a fresh border - was nop - delete";
0750         #endif
0751             d->document->selectionDelete ();
0752             if (tool ()) {
0753                 tool ()->somethingBelowTheCursorChanged ();
0754             }
0755 
0756             if (cmd) {
0757                 d->commandHistory->addCommand (cmd);
0758             }
0759         }
0760         else
0761         {
0762         #if DEBUG_KP_MAIN_WINDOW && 1
0763             qCDebug(kpLogMainWindow) << "\treal selection with image - push onto doc cmd";
0764         #endif
0765             kpCommand *deselectCommand = new kpToolSelectionDestroyCommand (
0766                 dynamic_cast <kpTextSelection *> (sel) ?
0767                     i18n ("Text: Finish") :
0768                     i18n ("Selection: Deselect"),
0769                 true/*push onto document*/,
0770                 commandEnvironment ());
0771 
0772             if (cmd)
0773             {
0774                 kpMacroCommand *macroCmd = new kpMacroCommand (cmd->name (),
0775                     commandEnvironment ());
0776                 macroCmd->addCommand (deselectCommand);
0777                 macroCmd->addCommand (cmd);
0778                 d->commandHistory->addCommand (macroCmd);
0779             }
0780             else {
0781                 d->commandHistory->addCommand (deselectCommand);
0782             }
0783         }
0784     }
0785     else
0786     {
0787         if (cmd) {
0788             d->commandHistory->addCommand (cmd);
0789         }
0790     }
0791 }
0792 
0793 //---------------------------------------------------------------------
0794 
0795 // public slot
0796 void kpMainWindow::slotDeselect ()
0797 {
0798 #if DEBUG_KP_MAIN_WINDOW && 1
0799     qCDebug(kpLogMainWindow) << "kpMainWindow::slotDeselect() CALLED";
0800 #endif
0801     Q_ASSERT (d->document && d->document->selection ());
0802 
0803     toolEndShape ();
0804 
0805     addDeselectFirstCommand (nullptr);
0806 }
0807 
0808 //---------------------------------------------------------------------
0809 
0810 // private slot
0811 void kpMainWindow::slotCopyToFile ()
0812 {
0813 #if DEBUG_KP_MAIN_WINDOW
0814     qCDebug(kpLogMainWindow) << "kpMainWindow::slotCopyToFile()";
0815 #endif
0816 
0817     toolEndShape ();
0818 
0819 
0820     if (!d->document->selection ()) {
0821         return;
0822     }
0823 
0824     kpImage imageToSave;
0825 
0826     if (d->document->imageSelection ())
0827     {
0828         kpAbstractImageSelection *imageSel = d->document->imageSelection ();
0829         if (!imageSel->hasContent ())
0830         {
0831             // Not a floating selection - user has just selected a region;
0832             // haven't pulled it off yet so probably don't expect and can't
0833             // visualize selection transparency so give opaque, not transparent
0834             // image.
0835             imageToSave = d->document->getSelectedBaseImage ();
0836         }
0837         else {
0838             imageToSave = imageSel->transparentImage ();
0839         }
0840     }
0841     else if (d->document->textSelection ())
0842     {
0843         imageToSave = d->document->textSelection ()->approximateImage ();
0844     }
0845     else {
0846         Q_ASSERT (!"Unknown selection type");
0847     }
0848 
0849 
0850     kpDocumentSaveOptions chosenSaveOptions;
0851     bool allowLossyPrompt;
0852     QUrl chosenURL = askForSaveURL (i18nc ("@title:window", "Copy to File"),
0853                                     d->lastCopyToURL.url (),
0854                                     imageToSave,
0855                                     d->lastCopyToSaveOptions,
0856                                     kpDocumentMetaInfo (),
0857                                     QLatin1String(kpSettingsGroupEditCopyTo),
0858                                     false/*allow remote files*/,
0859                                     &chosenSaveOptions,
0860                                     d->copyToFirstTime,
0861                                     &allowLossyPrompt);
0862 
0863     if (chosenURL.isEmpty ()) {
0864         return;
0865     }
0866 
0867 
0868     if (!kpDocument::savePixmapToFile (imageToSave,
0869                                        chosenURL,
0870                                        chosenSaveOptions, kpDocumentMetaInfo (),
0871                                        allowLossyPrompt,
0872                                        this))
0873     {
0874         return;
0875     }
0876 
0877 
0878     addRecentURL (chosenURL);
0879 
0880 
0881     d->lastCopyToURL = chosenURL;
0882     d->lastCopyToSaveOptions = chosenSaveOptions;
0883 
0884     d->copyToFirstTime = false;
0885 }
0886 
0887 //---------------------------------------------------------------------
0888 
0889 // private slot
0890 void kpMainWindow::slotPasteFromFile ()
0891 {
0892 #if DEBUG_KP_MAIN_WINDOW
0893     qCDebug(kpLogMainWindow) << "kpMainWindow::slotPasteFromFile()";
0894 #endif
0895 
0896     toolEndShape ();
0897 
0898 
0899     QList<QUrl> urls = askForOpenURLs(i18nc ("@title:window", "Paste From File"),
0900                                      false/*only 1 URL*/);
0901 
0902     if (urls.count () != 1) {
0903         return;
0904     }
0905 
0906     QUrl url = urls.first ();
0907 
0908     kpImage image = kpDocument::getPixmapFromFile (url,
0909         false/*show error message if doesn't exist*/,
0910         this);
0911 
0912     if (image.isNull ()) {
0913         return;
0914     }
0915 
0916     addRecentURL (url);
0917 
0918     paste (kpRectangularImageSelection (
0919         QRect (0, 0, image.width (), image.height ()),
0920         image,
0921         imageSelectionTransparency ()));
0922 }
0923 
0924 //---------------------------------------------------------------------