1 /*
2 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
3 Copyright (c) 2011 Martin Koller <kollix@aon.at>
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28
29 #include "kpMainWindow.h"
30 #include "kpMainWindowPrivate.h"
31
32 #include <QApplication>
33 #include <QClipboard>
34 #include <QFontMetrics>
35 #include <QImage>
36 #include <QList>
37 #include <QMenu>
38 #include <QDesktopWidget>
39 #include <QScrollBar>
40
41 #include "kpLogCategories.h"
42 #include <KMessageBox>
43 #include <KStandardAction>
44 #include <KActionCollection>
45 #include <KXMLGUIFactory>
46 #include <KLocalizedString>
47
48 #include "layers/selections/image/kpAbstractImageSelection.h"
49 #include "widgets/toolbars/kpColorToolBar.h"
50 #include "commands/kpCommandHistory.h"
51 #include "document/kpDocument.h"
52 #include "imagelib/kpDocumentMetaInfo.h"
53 #include "document/kpDocumentSaveOptions.h"
54 #include "layers/selections/image/kpImageSelectionTransparency.h"
55 #include "commands/kpMacroCommand.h"
56 #include "pixmapfx/kpPixmapFX.h"
57 #include "layers/selections/image/kpRectangularImageSelection.h"
58 #include "layers/selections/kpSelectionDrag.h"
59 #include "generic/kpSetOverrideCursorSaver.h"
60 #include "layers/selections/text/kpTextSelection.h"
61 #include "tools/kpTool.h"
62 #include "commands/tools/selection/text/kpToolTextGiveContentCommand.h"
63 #include "commands/tools/selection/kpToolSelectionCreateCommand.h"
64 #include "commands/tools/selection/kpToolSelectionDestroyCommand.h"
65 #include "commands/tools/selection/text/kpToolTextEnterCommand.h"
66 #include "commands/tools/selection/text/kpToolTextInsertCommand.h"
67 #include "imagelib/transforms/kpTransformCrop.h"
68 #include "commands/imagelib/transforms/kpTransformResizeScaleCommand.h"
69 #include "views/manager/kpViewManager.h"
70 #include "kpViewScrollableContainer.h"
71 #include "views/kpZoomedView.h"
72
73 //---------------------------------------------------------------------
74
75 // private
setupEditMenuActions()76 void kpMainWindow::setupEditMenuActions ()
77 {
78 KActionCollection *ac = actionCollection ();
79
80
81 // Undo/Redo
82 // CONFIG: Need GUI for config history size.
83 d->commandHistory = new kpCommandHistory (true/*read config*/, this);
84
85 if (d->configFirstTime)
86 {
87 // (so that cfg-file-editing user can modify in the meantime)
88 d->commandHistory->writeConfig ();
89 }
90
91
92 d->actionCut = KStandardAction::cut (this, SLOT (slotCut()), ac);
93 d->actionCopy = KStandardAction::copy (this, SLOT (slotCopy()), ac);
94 d->actionPaste = KStandardAction::paste (this, SLOT (slotPaste()), ac);
95 d->actionPasteInNewWindow = ac->addAction (QStringLiteral("edit_paste_in_new_window"));
96 d->actionPasteInNewWindow->setText (i18n ("Paste in &New Window"));
97 connect (d->actionPasteInNewWindow, &QAction::triggered,
98 this, &kpMainWindow::slotPasteInNewWindow);
99 ac->setDefaultShortcut (d->actionPasteInNewWindow, Qt::CTRL | Qt::SHIFT | Qt::Key_V);
100
101 //d->actionDelete = KStandardAction::clear (this, SLOT (slotDelete()), ac);
102 d->actionDelete = ac->addAction (QStringLiteral("edit_clear"));
103 d->actionDelete->setText (i18n ("&Delete Selection"));
104 connect (d->actionDelete, &QAction::triggered, this, &kpMainWindow::slotDelete);
105
106 d->actionSelectAll = KStandardAction::selectAll (this, SLOT (slotSelectAll()), ac);
107 d->actionDeselect = KStandardAction::deselect (this, SLOT (slotDeselect()), ac);
108
109
110 d->actionCopyToFile = ac->addAction (QStringLiteral("edit_copy_to_file"));
111 d->actionCopyToFile->setText (i18n ("C&opy to File..."));
112 connect (d->actionCopyToFile, &QAction::triggered, this, &kpMainWindow::slotCopyToFile);
113
114 d->actionPasteFromFile = ac->addAction (QStringLiteral("edit_paste_from_file"));
115 d->actionPasteFromFile->setText (i18n ("Paste &From File..."));
116 connect (d->actionPasteFromFile, &QAction::triggered, this, &kpMainWindow::slotPasteFromFile);
117
118
119 d->editMenuDocumentActionsEnabled = false;
120 enableEditMenuDocumentActions (false);
121
122 // Paste should always be enabled, as long as there is something to paste
123 // (independent of whether we have a document or not)
124 connect (QApplication::clipboard(), &QClipboard::dataChanged,
125 this, &kpMainWindow::slotEnablePaste);
126
127 slotEnablePaste ();
128 }
129
130 //---------------------------------------------------------------------
131
132 // private
enableEditMenuDocumentActions(bool enable)133 void kpMainWindow::enableEditMenuDocumentActions (bool enable)
134 {
135 // d->actionCut
136 // d->actionCopy
137 // d->actionPaste
138 // d->actionPasteInNewWindow
139
140 // d->actionDelete
141
142 d->actionSelectAll->setEnabled (enable);
143 // d->actionDeselect
144
145 d->editMenuDocumentActionsEnabled = enable;
146
147 // d->actionCopyToFile
148
149 // Unlike d->actionPaste, we disable this if there is no document.
150 // This is because "File / Open" would do the same thing, if there is
151 // no document.
152 d->actionPasteFromFile->setEnabled (enable);
153 }
154
155 //---------------------------------------------------------------------
156
157 // public
selectionToolRMBMenu()158 QMenu *kpMainWindow::selectionToolRMBMenu ()
159 {
160 return qobject_cast <QMenu *> (guiFactory ()->container (QStringLiteral("selectionToolRMBMenu"), this));
161 }
162
163 //---------------------------------------------------------------------
164
165 // private slot
slotCut()166 void kpMainWindow::slotCut ()
167 {
168 #if DEBUG_KP_MAIN_WINDOW && 1
169 qCDebug(kpLogMainWindow) << "kpMainWindow::slotCut() CALLED";
170 #endif
171
172 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
173
174 Q_ASSERT (d->document && d->document->selection ());
175
176 toolEndShape ();
177
178 slotCopy ();
179 slotDelete ();
180 }
181
182 //---------------------------------------------------------------------
183
NewTextMimeData(const QString & text)184 static QMimeData *NewTextMimeData (const QString &text)
185 {
186 auto *md = new QMimeData ();
187 md->setText (text);
188 return md;
189 }
190
191 //---------------------------------------------------------------------
192
193 // private slot
slotCopy()194 void kpMainWindow::slotCopy ()
195 {
196 #if DEBUG_KP_MAIN_WINDOW && 1
197 qCDebug(kpLogMainWindow) << "kpMainWindow::slotCopy() CALLED";
198 #endif
199
200 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
201
202 Q_ASSERT (d->document && d->document->selection ());
203
204 toolEndShape ();
205
206 kpAbstractSelection *sel = d->document->selection ()->clone ();
207
208 if (dynamic_cast <kpTextSelection *> (sel))
209 {
210 auto *textSel = dynamic_cast <kpTextSelection *> (sel);
211 if (!textSel->text ().isEmpty ())
212 {
213 QApplication::clipboard ()->setMimeData (
214 ::NewTextMimeData (textSel->text ()),
215 QClipboard::Clipboard);
216
217 // SYNC: Normally, users highlight text and press CTRL+C.
218 // Highlighting text copies it to the X11 "middle
219 // mouse button" clipboard. CTRL+C copies it to the
220 // separate, Windows-like "CTRL+V" clipboard.
221 //
222 // However, KolourPaint doesn't support highlighting.
223 // So when they press CTRL+C to copy all text, simulate
224 // the highlighting by copying the text to the "middle
225 // mouse button" clipboard. We don't do this for images
226 // as no one ever middle-mouse-pastes images.
227 //
228 // Note that we don't share the QMimeData pointer with
229 // the above in case Qt doesn't expect it.
230 //
231 // Once we change KolourPaint to support highlighted text
232 // and CTRL+C to copy only the highlighted text, delete
233 // this code.
234 QApplication::clipboard ()->setMimeData (
235 ::NewTextMimeData (textSel->text ()),
236 QClipboard::Selection);
237 }
238 }
239 else if (dynamic_cast <kpAbstractImageSelection *> (sel))
240 {
241 auto *imageSel = dynamic_cast <kpAbstractImageSelection *> (sel);
242
243 // Transparency doesn't get sent across the aether so nuke it now
244 // so that transparency mask doesn't get needlessly recalculated
245 // if we ever call sel.setBaseImage().
246 imageSel->setTransparency (kpImageSelectionTransparency ());
247
248 kpImage rawImage;
249
250 if (imageSel->hasContent ()) {
251 rawImage = imageSel->baseImage ();
252 }
253 else {
254 rawImage = d->document->getSelectedBaseImage ();
255 }
256
257 imageSel->setBaseImage ( rawImage );
258
259 QApplication::clipboard ()->setMimeData (
260 new kpSelectionDrag (*imageSel),
261 QClipboard::Clipboard);
262 }
263 else {
264 Q_ASSERT (!"Unknown selection type");
265 }
266
267 delete sel;
268 }
269
270 //---------------------------------------------------------------------
271
272 // private slot
slotEnablePaste()273 void kpMainWindow::slotEnablePaste ()
274 {
275 const QMimeData *md =
276 QApplication::clipboard()->mimeData(QClipboard::Clipboard);
277
278 // It's faster to test for QMimeData::hasText() first due to the
279 // lazy evaluation of the '||' operator.
280 const bool shouldEnable = md && (md->hasText() || kpSelectionDrag::canDecode(md));
281
282 d->actionPasteInNewWindow->setEnabled(shouldEnable);
283 d->actionPaste->setEnabled(shouldEnable);
284 }
285
286 //---------------------------------------------------------------------
287
288 // private
calcUsefulPasteRect(int imageWidth,int imageHeight)289 QRect kpMainWindow::calcUsefulPasteRect (int imageWidth, int imageHeight)
290 {
291 #if DEBUG_KP_MAIN_WINDOW && 1
292 qCDebug(kpLogMainWindow) << "kpMainWindow::calcUsefulPasteRect("
293 << imageWidth << "," << imageHeight
294 << ")";
295 #endif
296 Q_ASSERT (d->document);
297
298 // TODO: 1st choice is to paste sel near but not overlapping last deselect point
299
300 if (d->mainView && d->scrollView)
301 {
302 const QPoint viewTopLeft (d->scrollView->horizontalScrollBar()->value (),
303 d->scrollView->verticalScrollBar()->value ());
304
305 const QPoint docTopLeft = d->mainView->transformViewToDoc (viewTopLeft);
306
307 if ((docTopLeft.x () + imageWidth <= d->document->width () &&
308 docTopLeft.y () + imageHeight <= d->document->height ()) ||
309 imageWidth <= docTopLeft.x () ||
310 imageHeight <= docTopLeft.y ())
311 {
312 return {docTopLeft.x (), docTopLeft.y (), imageWidth, imageHeight};
313 }
314 }
315
316 return {0, 0, imageWidth, imageHeight};
317 }
318
319 //---------------------------------------------------------------------
320
321 // private
paste(const kpAbstractSelection & sel,bool forceTopLeft)322 void kpMainWindow::paste(const kpAbstractSelection &sel, bool forceTopLeft)
323 {
324 #if DEBUG_KP_MAIN_WINDOW && 1
325 qCDebug(kpLogMainWindow) << "kpMainWindow::paste(forceTopLeft=" << forceTopLeft << ")";
326 #endif
327
328 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
329
330 toolEndShape ();
331
332 //
333 // Make sure we've got a document (esp. with File/Close)
334 //
335
336 if (!d->document)
337 {
338 auto *newDoc = new kpDocument (
339 sel.width (), sel.height (), documentEnvironment ());
340
341 // will also create viewManager
342 setDocument (newDoc);
343 }
344
345 //
346 // Paste as new selection
347 //
348
349 const auto *imageSel = dynamic_cast <const kpAbstractImageSelection *> (&sel);
350
351 if (imageSel && imageSel->hasContent () && imageSel->transparency ().isTransparent ())
352 {
353 d->colorToolBar->flashColorSimilarityToolBarItem ();
354 }
355
356 kpAbstractSelection *selInUsefulPos = sel.clone ();
357 if (!forceTopLeft) {
358 selInUsefulPos->moveTo (calcUsefulPasteRect (sel.width (), sel.height ()).topLeft ());
359 }
360 // TODO: Should use kpCommandHistory::addCreateSelectionCommand(),
361 // as well, to really support pasting selection borders.
362 addDeselectFirstCommand (new kpToolSelectionCreateCommand (
363 dynamic_cast <kpTextSelection *> (selInUsefulPos) ?
364 i18n ("Text: Create Box") :
365 i18n ("Selection: Create"),
366 *selInUsefulPos,
367 commandEnvironment ()));
368 delete selInUsefulPos;
369
370
371 #if DEBUG_KP_MAIN_WINDOW && 1
372 qCDebug(kpLogMainWindow) << "sel.size=" << QSize (sel.width (), sel.height ())
373 << " document.size="
374 << QSize (d->document->width (), d->document->height ());
375 #endif
376
377 // If the selection is bigger than the document, automatically
378 // resize the document (with the option of Undo'ing) to fit
379 // the selection.
380 //
381 // No annoying dialog necessary.
382 //
383 if (sel.width () > d->document->width () ||
384 sel.height () > d->document->height ())
385 {
386 d->commandHistory->addCommand (
387 new kpTransformResizeScaleCommand (
388 false/*act on doc, not sel*/,
389 qMax (sel.width (), d->document->width ()),
390 qMax (sel.height (), d->document->height ()),
391 kpTransformResizeScaleCommand::Resize,
392 commandEnvironment ()));
393 }
394 }
395
396 //---------------------------------------------------------------------
397
398 // public
pasteText(const QString & text,bool forceNewTextSelection,const QPoint & newTextSelectionTopLeft)399 void kpMainWindow::pasteText (const QString &text,
400 bool forceNewTextSelection,
401 const QPoint &newTextSelectionTopLeft)
402 {
403 #if DEBUG_KP_MAIN_WINDOW && 1
404 qCDebug(kpLogMainWindow) << "kpMainWindow::pasteText(" << text
405 << ",forceNewTextSelection=" << forceNewTextSelection
406 << ",newTextSelectionTopLeft=" << newTextSelectionTopLeft
407 << ")";
408 #endif
409
410 if ( text.isEmpty() ) {
411 return;
412 }
413
414 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
415
416 toolEndShape ();
417
418 QStringList textLines = text.split('\n');
419
420 if (!forceNewTextSelection &&
421 d->document && d->document->textSelection () &&
422 d->commandHistory && d->viewManager)
423 {
424 #if DEBUG_KP_MAIN_WINDOW && 1
425 qCDebug(kpLogMainWindow) << "\treusing existing Text Selection";
426 #endif
427
428 d->viewManager->setQueueUpdates();
429
430 kpTextSelection *textSel = d->document->textSelection ();
431 if (!textSel->hasContent ())
432 {
433 #if DEBUG_KP_MAIN_WINDOW && 1
434 qCDebug(kpLogMainWindow) << "\t\tneeds content";
435 #endif
436 commandHistory ()->addCreateSelectionCommand (
437 new kpToolSelectionCreateCommand (
438 i18n ("Text: Create Box"),
439 *textSel,
440 commandEnvironment ()),
441 false/*no exec*/);
442 }
443
444 kpMacroCommand *macroCmd = new kpMacroCommand (i18n ("Text: Paste"),
445 commandEnvironment ());
446 // (yes, this is the same check as the previous "if")
447 if (!textSel->hasContent ())
448 {
449 kpCommand *giveContentCmd = new kpToolTextGiveContentCommand (
450 *textSel,
451 QString ()/*uninteresting child of macro cmd*/,
452 commandEnvironment ());
453 giveContentCmd->execute ();
454
455 macroCmd->addCommand (giveContentCmd);
456 }
457
458 for (int i = 0; i < textLines.size(); i++)
459 {
460 if (i > 0)
461 {
462 macroCmd->addCommand (
463 new kpToolTextEnterCommand (
464 QString()/*uninteresting child of macroCmd*/,
465 d->viewManager->textCursorRow (),
466 d->viewManager->textCursorCol (),
467 kpToolTextEnterCommand::AddEnterNow,
468 commandEnvironment ()));
469 }
470
471 macroCmd->addCommand (
472 new kpToolTextInsertCommand (
473 QString()/*uninteresting child of macroCmd*/,
474 d->viewManager->textCursorRow (),
475 d->viewManager->textCursorCol (),
476 textLines [i],
477 commandEnvironment ()));
478 }
479
480 d->commandHistory->addCommand (macroCmd, false/*no exec*/);
481
482 d->viewManager->restoreQueueUpdates();
483 }
484 else
485 {
486 #if DEBUG_KP_MAIN_WINDOW && 1
487 qCDebug(kpLogMainWindow) << "\tcreating Text Selection";
488 #endif
489
490 const kpTextStyle ts = textStyle ();
491 const QFontMetrics fontMetrics = ts.fontMetrics ();
492
493 int height = textLines.size () * fontMetrics.height ();
494 if (textLines.size () >= 1) {
495 height += (textLines.size () - 1) * fontMetrics.leading ();
496 }
497
498 int width = 0;
499 foreach (const QString &str, textLines)
500 width = std::max(width, fontMetrics.horizontalAdvance(str));
501
502 // limit the size to avoid memory overflow
503 width = qMin(qMax(QApplication::desktop()->width(), d->document ? d->document->width() : 0), width);
504 height = qMin(qMax(QApplication::desktop()->height(), d->document ? d->document->height() : 0), height);
505
506 const int selWidth = qMax (kpTextSelection::MinimumWidthForTextStyle (ts),
507 width + kpTextSelection::TextBorderSize () * 2);
508 const int selHeight = qMax (kpTextSelection::MinimumHeightForTextStyle (ts),
509 height + kpTextSelection::TextBorderSize () * 2);
510 kpTextSelection newTextSel (QRect (0, 0, selWidth, selHeight),
511 textLines,
512 ts);
513
514 if (newTextSelectionTopLeft != KP_INVALID_POINT)
515 {
516 newTextSel.moveTo (newTextSelectionTopLeft);
517 paste (newTextSel, true/*force topLeft*/);
518 }
519 else
520 {
521 paste (newTextSel);
522 }
523 }
524 }
525
526 //---------------------------------------------------------------------
527
528 // public
pasteTextAt(const QString & text,const QPoint & point,bool allowNewTextSelectionPointShift)529 void kpMainWindow::pasteTextAt (const QString &text, const QPoint &point,
530 bool allowNewTextSelectionPointShift)
531 {
532 #if DEBUG_KP_MAIN_WINDOW && 1
533 qCDebug(kpLogMainWindow) << "kpMainWindow::pasteTextAt(" << text
534 << ",point=" << point
535 << ",allowNewTextSelectionPointShift="
536 << allowNewTextSelectionPointShift
537 << ")";
538 #endif
539
540 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
541
542 toolEndShape ();
543
544
545 if (d->document &&
546 d->document->textSelection () &&
547 d->document->textSelection ()->pointIsInTextArea (point))
548 {
549 kpTextSelection *textSel = d->document->textSelection ();
550
551 int row, col;
552
553 if (textSel->hasContent ())
554 {
555 row = textSel->closestTextRowForPoint (point);
556 col = textSel->closestTextColForPoint (point);
557 }
558 else
559 {
560 row = col = 0;
561 }
562
563 d->viewManager->setTextCursorPosition (row, col);
564
565 pasteText (text);
566 }
567 else
568 {
569 QPoint pointToUse = point;
570
571 if (allowNewTextSelectionPointShift)
572 {
573 // TODO: In terms of doc pixels, would be inconsistent behaviour
574 // based on zoomLevel of view.
575 // pointToUse -= QPoint (-view->selectionResizeHandleAtomicSize (),
576 // -view->selectionResizeHandleAtomicSize ());
577 }
578
579 pasteText (text, true/*force new text selection*/, pointToUse);
580 }
581 }
582
583 //---------------------------------------------------------------------
584 // public slot
585
slotPaste()586 void kpMainWindow::slotPaste()
587 {
588 kpSetOverrideCursorSaver cursorSaver(Qt::WaitCursor);
589
590 toolEndShape();
591
592 const QMimeData *mimeData = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
593
594 kpAbstractImageSelection *sel = kpSelectionDrag::decode(mimeData);
595 if ( sel )
596 {
597 sel->setTransparency(imageSelectionTransparency());
598 paste(*sel);
599 delete sel;
600 }
601 else if ( mimeData->hasText() )
602 {
603 pasteText(mimeData->text());
604 }
605 else
606 {
607 kpSetOverrideCursorSaver cursorSaver(Qt::ArrowCursor);
608
609 KMessageBox::sorry(this,
610 i18n("<qt>KolourPaint cannot paste the contents of"
611 " the clipboard as it has an unknown format.</qt>"),
612 i18n("Cannot Paste"));
613 }
614 }
615
616 //---------------------------------------------------------------------
617
618 // private slot
slotPasteInNewWindow()619 void kpMainWindow::slotPasteInNewWindow ()
620 {
621 #if DEBUG_KP_MAIN_WINDOW && 1
622 qCDebug(kpLogMainWindow) << "kpMainWindow::slotPasteInNewWindow() CALLED";
623 #endif
624
625 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
626
627 toolEndShape ();
628
629 //
630 // Pasting must ensure that:
631 //
632 // Requirement 1. the document is the same size as the image to be pasted.
633 // Requirement 2. transparent pixels in the image must remain as transparent.
634 //
635
636 auto *win = new kpMainWindow (nullptr/*no document*/);
637 win->show ();
638
639 // Make "Edit / Paste in New Window" always paste white pixels as white.
640 // Don't let selection transparency get in the way and paste them as
641 // transparent.
642 kpImageSelectionTransparency transparency = win->imageSelectionTransparency ();
643 if (transparency.isTransparent ())
644 {
645 #if DEBUG_KP_MAIN_WINDOW && 1
646 qCDebug(kpLogMainWindow) << "\tchanging image selection transparency to opaque";
647 #endif
648 transparency.setOpaque ();
649 // Since we are setting selection transparency programmatically
650 // -- as opposed to in response to user input -- this will not
651 // affect the selection transparency tool option widget's "last used"
652 // config setting.
653 win->setImageSelectionTransparency (transparency);
654 }
655
656 // (this handles Requirement 1. above)
657 win->slotPaste ();
658
659 // if slotPaste could not decode clipboard data, no document was created
660 if ( win->document() )
661 {
662 // (this handles Requirement 2. above;
663 // slotDeselect() is not enough unless the document is filled with the
664 // transparent color in advance)
665 win->slotCrop();
666 }
667 }
668
669 //---------------------------------------------------------------------
670
671 // public slot
slotDelete()672 void kpMainWindow::slotDelete ()
673 {
674 #if DEBUG_KP_MAIN_WINDOW && 1
675 qCDebug(kpLogMainWindow) << "kpMainWindow::slotDelete() CALLED";
676 #endif
677 if (!d->actionDelete->isEnabled ())
678 {
679 #if DEBUG_KP_MAIN_WINDOW && 1
680 qCDebug(kpLogMainWindow) << "\taction not enabled - was probably called from kpTool::keyPressEvent()";
681 #endif
682 return;
683 }
684
685 Q_ASSERT (d->document && d->document->selection ());
686
687 toolEndShape ();
688
689 addImageOrSelectionCommand (new kpToolSelectionDestroyCommand (
690 d->document->textSelection () ?
691 i18n ("Text: Delete Box") : // not to be confused with i18n ("Text: Delete")
692 i18n ("Selection: Delete"),
693 false/*no push onto doc*/,
694 commandEnvironment ()));
695 }
696
697 //---------------------------------------------------------------------
698
699 // private slot
slotSelectAll()700 void kpMainWindow::slotSelectAll ()
701 {
702 #if DEBUG_KP_MAIN_WINDOW && 1
703 qCDebug(kpLogMainWindow) << "kpMainWindow::slotSelectAll() CALLED";
704 #endif
705 Q_ASSERT (d->document);
706
707 toolEndShape ();
708
709 if (d->document->selection ()) {
710 slotDeselect ();
711 }
712
713 // just the border - don't actually pull image from doc yet
714 d->document->setSelection (
715 kpRectangularImageSelection (d->document->rect (),
716 imageSelectionTransparency ()));
717
718 if (tool ()) {
719 tool ()->somethingBelowTheCursorChanged ();
720 }
721 }
722
723 //---------------------------------------------------------------------
724
725 // private
addDeselectFirstCommand(kpCommand * cmd)726 void kpMainWindow::addDeselectFirstCommand (kpCommand *cmd)
727 {
728 #if DEBUG_KP_MAIN_WINDOW && 1
729 qCDebug(kpLogMainWindow) << "kpMainWindow::addDeselectFirstCommand("
730 << cmd
731 << ")";
732 #endif
733
734
735 kpAbstractSelection *sel = d->document->selection ();
736
737 #if DEBUG_KP_MAIN_WINDOW && 1
738 qCDebug(kpLogMainWindow) << "\tsel=" << sel;
739 #endif
740
741 if (sel)
742 {
743 // if you just dragged out something with no action then
744 // forget the drag
745 if (!sel->hasContent ())
746 {
747 #if DEBUG_KP_MAIN_WINDOW && 1
748 qCDebug(kpLogMainWindow) << "\tjust a fresh border - was nop - delete";
749 #endif
750 d->document->selectionDelete ();
751 if (tool ()) {
752 tool ()->somethingBelowTheCursorChanged ();
753 }
754
755 if (cmd) {
756 d->commandHistory->addCommand (cmd);
757 }
758 }
759 else
760 {
761 #if DEBUG_KP_MAIN_WINDOW && 1
762 qCDebug(kpLogMainWindow) << "\treal selection with image - push onto doc cmd";
763 #endif
764 kpCommand *deselectCommand = new kpToolSelectionDestroyCommand (
765 dynamic_cast <kpTextSelection *> (sel) ?
766 i18n ("Text: Finish") :
767 i18n ("Selection: Deselect"),
768 true/*push onto document*/,
769 commandEnvironment ());
770
771 if (cmd)
772 {
773 kpMacroCommand *macroCmd = new kpMacroCommand (cmd->name (),
774 commandEnvironment ());
775 macroCmd->addCommand (deselectCommand);
776 macroCmd->addCommand (cmd);
777 d->commandHistory->addCommand (macroCmd);
778 }
779 else {
780 d->commandHistory->addCommand (deselectCommand);
781 }
782 }
783 }
784 else
785 {
786 if (cmd) {
787 d->commandHistory->addCommand (cmd);
788 }
789 }
790 }
791
792 //---------------------------------------------------------------------
793
794 // public slot
slotDeselect()795 void kpMainWindow::slotDeselect ()
796 {
797 #if DEBUG_KP_MAIN_WINDOW && 1
798 qCDebug(kpLogMainWindow) << "kpMainWindow::slotDeselect() CALLED";
799 #endif
800 Q_ASSERT (d->document && d->document->selection ());
801
802 toolEndShape ();
803
804 addDeselectFirstCommand (nullptr);
805 }
806
807 //---------------------------------------------------------------------
808
809 // private slot
slotCopyToFile()810 void kpMainWindow::slotCopyToFile ()
811 {
812 #if DEBUG_KP_MAIN_WINDOW
813 qCDebug(kpLogMainWindow) << "kpMainWindow::slotCopyToFile()";
814 #endif
815
816 toolEndShape ();
817
818
819 if (!d->document->selection ()) {
820 return;
821 }
822
823 kpImage imageToSave;
824
825 if (d->document->imageSelection ())
826 {
827 kpAbstractImageSelection *imageSel = d->document->imageSelection ();
828 if (!imageSel->hasContent ())
829 {
830 // Not a floating selection - user has just selected a region;
831 // haven't pulled it off yet so probably don't expect and can't
832 // visualize selection transparency so give opaque, not transparent
833 // image.
834 imageToSave = d->document->getSelectedBaseImage ();
835 }
836 else {
837 imageToSave = imageSel->transparentImage ();
838 }
839 }
840 else if (d->document->textSelection ())
841 {
842 imageToSave = d->document->textSelection ()->approximateImage ();
843 }
844 else {
845 Q_ASSERT (!"Unknown selection type");
846 }
847
848
849 kpDocumentSaveOptions chosenSaveOptions;
850 bool allowLossyPrompt;
851 QUrl chosenURL = askForSaveURL (i18nc ("@title:window", "Copy to File"),
852 d->lastCopyToURL.url (),
853 imageToSave,
854 d->lastCopyToSaveOptions,
855 kpDocumentMetaInfo (),
856 kpSettingsGroupEditCopyTo,
857 false/*allow remote files*/,
858 &chosenSaveOptions,
859 d->copyToFirstTime,
860 &allowLossyPrompt);
861
862 if (chosenURL.isEmpty ()) {
863 return;
864 }
865
866
867 if (!kpDocument::savePixmapToFile (imageToSave,
868 chosenURL,
869 chosenSaveOptions, kpDocumentMetaInfo (),
870 allowLossyPrompt,
871 this))
872 {
873 return;
874 }
875
876
877 addRecentURL (chosenURL);
878
879
880 d->lastCopyToURL = chosenURL;
881 d->lastCopyToSaveOptions = chosenSaveOptions;
882
883 d->copyToFirstTime = false;
884 }
885
886 //---------------------------------------------------------------------
887
888 // private slot
slotPasteFromFile()889 void kpMainWindow::slotPasteFromFile ()
890 {
891 #if DEBUG_KP_MAIN_WINDOW
892 qCDebug(kpLogMainWindow) << "kpMainWindow::slotPasteFromFile()";
893 #endif
894
895 toolEndShape ();
896
897
898 QList<QUrl> urls = askForOpenURLs(i18nc ("@title:window", "Paste From File"),
899 false/*only 1 URL*/);
900
901 if (urls.count () != 1) {
902 return;
903 }
904
905 QUrl url = urls.first ();
906
907 kpImage image = kpDocument::getPixmapFromFile (url,
908 false/*show error message if doesn't exist*/,
909 this);
910
911 if (image.isNull ()) {
912 return;
913 }
914
915 addRecentURL (url);
916
917 paste (kpRectangularImageSelection (
918 QRect (0, 0, image.width (), image.height ()),
919 image,
920 imageSelectionTransparency ()));
921 }
922
923 //---------------------------------------------------------------------
924