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 //---------------------------------------------------------------------