there is no moc file generated for this class
[kdegraphics.git] / kolourpaint / mainWindow / kpMainWindow_Edit.cpp
blob587c1f7c3adf16a83d93a0a723d7a8b47742d0c8
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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.
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.
29 #include <kpMainWindow.h>
30 #include <kpMainWindowPrivate.h>
32 #include <qapplication.h>
33 #include <qclipboard.h>
34 #include <qdatetime.h>
35 #include <qfontmetrics.h>
36 #include <qimage.h>
37 #include <qlist.h>
38 #include <qmenu.h>
39 #include <qpixmap.h>
41 #include <kaction.h>
42 #include <kdebug.h>
43 #include <klocale.h>
44 #include <kmessagebox.h>
45 #include <kstandardaction.h>
46 #include <kactioncollection.h>
47 #include <kxmlguifactory.h>
49 #include <kpAbstractImageSelection.h>
50 #include <kpColorToolBar.h>
51 #include <kpCommandHistory.h>
52 #include <kpDocument.h>
53 #include <kpDocumentMetaInfo.h>
54 #include <kpDocumentSaveOptions.h>
55 #include <kpImageSelectionTransparency.h>
56 #include <kpMacroCommand.h>
57 #include <kpPixmapFX.h>
58 #include <kpRectangularImageSelection.h>
59 #include <kpSelectionDrag.h>
60 #include <kpSetOverrideCursorSaver.h>
61 #include <kpTextSelection.h>
62 #include <kpTool.h>
63 #include <kpToolTextGiveContentCommand.h>
64 #include <kpToolSelectionCreateCommand.h>
65 #include <kpToolSelectionDestroyCommand.h>
66 #include <kpToolTextEnterCommand.h>
67 #include <kpToolTextInsertCommand.h>
68 #include <kpTransformCrop.h>
69 #include <kpTransformResizeScaleCommand.h>
70 #include <kpViewManager.h>
71 #include <kpViewScrollableContainer.h>
72 #include <kpZoomedView.h>
75 // private
76 kpPixmapFX::WarnAboutLossInfo kpMainWindow::pasteWarnAboutLossInfo ()
78 return kpPixmapFX::WarnAboutLossInfo (
79 ki18n ("<qt><p>The image to be pasted"
80 " may have more colors than the current screen mode can support."
81 " In order to display it, some color information may be removed.</p>"
83 "<p><b>If you save this image, any color loss will become"
84 " permanent.</b></p>"
86 "<p>To avoid this issue, increase your screen depth to at"
87 " least %1bpp and then restart KolourPaint.</p>"
89 "<hr/>"
91 "<p>It also"
93 " contains translucency which is not fully"
94 " supported. The translucency data will be"
95 " approximated with a 1-bit transparency mask.</p>"
97 "<p><b>If you save this image, this loss of translucency will"
98 " become permanent.</b></p></qt>"),
99 ki18n ("<qt><p>The image to be pasted"
100 " may have more colors than the current screen mode can support."
101 " In order to display it, some color information may be removed.</p>"
103 "<p><b>If you save this image, any color loss will become"
104 " permanent.</b></p>"
106 "<p>To avoid this issue, increase your screen depth to at"
107 " least %1bpp and then restart KolourPaint.</p></qt>"),
108 i18n ("<qt><p>The image to be pasted"
109 " contains translucency which is not fully"
110 " supported. The translucency data will be"
111 " approximated with a 1-bit transparency mask.</p>"
113 "<p><b>If you save this image, this loss of translucency will"
114 " become permanent.</b></p></qt>"),
115 "paste",
116 this);
120 // private
121 void kpMainWindow::setupEditMenuActions ()
123 KActionCollection *ac = actionCollection ();
126 // Undo/Redo
127 // CONFIG: Need GUI for config history size.
128 d->commandHistory = new kpCommandHistory (true/*read config*/, this);
130 if (d->configFirstTime)
132 // (so that cfg-file-editing user can modify in the meantime)
133 d->commandHistory->writeConfig ();
137 d->actionCut = KStandardAction::cut (this, SLOT (slotCut ()), ac);
138 d->actionCopy = KStandardAction::copy (this, SLOT (slotCopy ()), ac);
139 d->actionPaste = KStandardAction::paste (this, SLOT (slotPaste ()), ac);
140 d->actionPasteInNewWindow = ac->addAction ("edit_paste_in_new_window");
141 d->actionPasteInNewWindow->setText (i18n ("Paste in &New Window"));
142 connect (d->actionPasteInNewWindow, SIGNAL (triggered (bool)),
143 SLOT (slotPasteInNewWindow ()));
144 d->actionPasteInNewWindow->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
146 //d->actionDelete = KStandardAction::clear (this, SLOT (slotDelete ()), ac);
147 d->actionDelete = ac->addAction ("edit_clear");
148 d->actionDelete->setText (i18n ("&Delete Selection"));
149 connect (d->actionDelete, SIGNAL (triggered (bool)), SLOT (slotDelete ()));
151 d->actionSelectAll = KStandardAction::selectAll (this, SLOT (slotSelectAll ()), ac);
152 d->actionDeselect = KStandardAction::deselect (this, SLOT (slotDeselect ()), ac);
155 d->actionCopyToFile = ac->addAction ("edit_copy_to_file");
156 d->actionCopyToFile->setText (i18n ("C&opy to File..."));
157 connect (d->actionCopyToFile, SIGNAL (triggered (bool)),
158 SLOT (slotCopyToFile ()));
159 d->actionPasteFromFile = ac->addAction ("edit_paste_from_file");
160 d->actionPasteFromFile->setText (i18n ("Paste &From File..."));
161 connect (d->actionPasteFromFile, SIGNAL (triggered (bool)),
162 SLOT (slotPasteFromFile ()));
165 d->editMenuDocumentActionsEnabled = false;
166 enableEditMenuDocumentActions (false);
168 // Paste should always be enabled, as long as there is something paste
169 // (independent of whether we have a document or not)
170 connect (QApplication::clipboard (), SIGNAL (dataChanged ()),
171 this, SLOT (slotEnablePaste ()));
172 slotEnablePaste ();
175 // private
176 void kpMainWindow::enableEditMenuDocumentActions (bool enable)
178 // d->actionCut
179 // d->actionCopy
180 // d->actionPaste
181 // d->actionPasteInNewWindow
183 // d->actionDelete
185 d->actionSelectAll->setEnabled (enable);
186 // d->actionDeselect
188 d->editMenuDocumentActionsEnabled = enable;
190 // d->actionCopyToFile
192 // Unlike d->actionPaste, we disable this if there is no document.
193 // This is because "File / Open" would do the same thing, if there is
194 // no document.
195 d->actionPasteFromFile->setEnabled (enable);
199 // public
200 QMenu *kpMainWindow::selectionToolRMBMenu ()
202 return qobject_cast <QMenu *> (guiFactory ()->container ("selectionToolRMBMenu", this));
206 // private slot
207 void kpMainWindow::slotCut ()
209 #if DEBUG_KP_MAIN_WINDOW && 1
210 kDebug () << "kpMainWindow::slotCut() CALLED";
211 #endif
213 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
215 Q_ASSERT (d->document && d->document->selection ());
217 toolEndShape ();
219 slotCopy ();
220 slotDelete ();
223 static QMimeData *NewTextMimeData (const QString &text)
225 QMimeData *md = new QMimeData ();
226 md->setText (text);
227 return md;
230 // private slot
231 void kpMainWindow::slotCopy ()
233 #if DEBUG_KP_MAIN_WINDOW && 1
234 kDebug () << "kpMainWindow::slotCopy() CALLED";
235 #endif
237 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
239 Q_ASSERT (d->document && d->document->selection ());
241 toolEndShape ();
243 kpAbstractSelection *sel = d->document->selection ()->clone ();
245 if (dynamic_cast <kpTextSelection *> (sel))
247 kpTextSelection *textSel = static_cast <kpTextSelection *> (sel);
248 if (!textSel->text ().isEmpty ())
250 QApplication::clipboard ()->setMimeData (
251 ::NewTextMimeData (textSel->text ()),
252 QClipboard::Clipboard);
254 // SYNC: Normally, users highlight text and press CTRL+C.
255 // Highlighting text copies it to the X11 "middle
256 // mouse button" clipboard. CTRL+C copies it to the
257 // separate, Windows-like "CTRL+V" clipboard.
259 // However, KolourPaint doesn't support highlighting.
260 // So when they press CTRL+C to copy all text, simulate
261 // the highlighting by copying the text to the "middle
262 // mouse button" clipboard. We don't do this for images
263 // as no one ever middle-mouse-pastes images.
265 // Note that we don't share the QMimeData pointer with
266 // the above in case Qt doesn't expect it.
268 // Once we change KolourPaint to support highlighted text
269 // and CTRL+C to copy only the highlighted text, delete
270 // this code.
271 QApplication::clipboard ()->setMimeData (
272 ::NewTextMimeData (textSel->text ()),
273 QClipboard::Selection);
276 else if (dynamic_cast <kpAbstractImageSelection *> (sel))
278 kpAbstractImageSelection *imageSel =
279 static_cast <kpAbstractImageSelection *> (sel);
281 // Transparency doesn't get sent across the aether so nuke it now
282 // so that transparency mask doesn't get needlessly recalculated
283 // if we ever call sel.setBaseImage().
284 imageSel->setTransparency (kpImageSelectionTransparency ());
286 kpImage rawImage;
288 if (imageSel->hasContent ())
289 rawImage = imageSel->baseImage ();
290 else
291 rawImage = d->document->getSelectedBaseImage ();
293 // Some apps, such as OpenOffice.org 2.0.4, ignore the image mask
294 // when pasting. For transparent pixels, the uninitialized RGB
295 // values are used. Fix this by initializing those values to a
296 // neutral color -- white.
298 // Strangely enough, OpenOffice.org respects the mask when inserting
299 // an image from a file, as opposed to pasting one from the clipboard.
301 // REFACTOR: This logic should be moved into kpSelectionDrag as that
302 // would centralize this responsibility.
304 // TODO: This does not work under Qt4 (we get black, instead of white,
305 // which is still OK, just not intended).
306 // See kpPixmapFX::pixmapWithDefinedTransparentPixels() API Doc.
307 imageSel->setBaseImage (
308 kpPixmapFX::pixmapWithDefinedTransparentPixels (
309 rawImage,
310 Qt::white)); // CONFIG
312 QApplication::clipboard ()->setMimeData (
313 new kpSelectionDrag (*imageSel),
314 QClipboard::Clipboard);
316 else
317 Q_ASSERT (!"Unknown selection type");
319 delete sel;
323 static bool HasSomethingToPaste (kpMainWindow *mw)
325 #if DEBUG_KP_MAIN_WINDOW
326 kDebug () << "kpMainWindow(" << mw->objectName () << "):HasSomethingToPaste()";
327 QTime timer;
328 timer.start ();
329 #else
330 (void) mw;
331 #endif
333 const QMimeData *md =
334 QApplication::clipboard ()->mimeData (QClipboard::Clipboard);
335 Q_ASSERT (md);
337 // It's faster to test for QMimeData::hasText() first due to the
338 // lazy evaluation of the '||' operator.
339 const bool hasSomething =
340 (md->hasText () || kpSelectionDrag::canDecode (md));
341 #if DEBUG_KP_MAIN_WINDOW
342 kDebug () << "\t" << mw->objectName () << "***canDecode=" << hasSomething
343 << "(time=" << timer.restart () << ")"
344 << "formats=" << md->formats ();
345 #endif
347 return hasSomething;
350 // HACK: SYNC: Non-Qt apps do not cause QApplication::clipboard() to
351 // emit dataChanged(). We don't want to have our paste
352 // action disabled when we can actually paste something.
354 // So we make sure the paste action is always enabled and
355 // before any paste, we check if there's actually something
356 // to paste (HasSomethingToPasteWithDialogIfNot ()).
358 #if 1 // Hack code path
361 // Call before any paste only.
362 static bool HasSomethingToPasteWithDialogIfNot (kpMainWindow *mw)
364 if (::HasSomethingToPaste (mw))
365 return true;
367 kpSetOverrideCursorSaver cursorSaver (Qt::ArrowCursor);
369 KMessageBox::sorry (mw,
370 i18n ("<qt><p>There is nothing in the clipboard to paste.</p></qt>"),
371 i18n ("Cannot Paste"));
372 return false;
375 // private slot
376 void kpMainWindow::slotEnablePaste ()
378 const bool shouldEnable = true;
379 d->actionPasteInNewWindow->setEnabled (shouldEnable);
380 d->actionPaste->setEnabled (shouldEnable);
384 #else // No hack
387 // Call before any paste only.
388 static bool HasSomethingToPasteWithDialogIfNot (kpMainWindow *)
390 // We will not be called if there's nothing to paste, as the paste
391 // action would have been disabled. But do _not_ assert that
392 // (see the slotPaste() "data unexpectedly disappeared" KMessageBox).
393 return true;
396 // private slot
397 void kpMainWindow::slotEnablePaste ()
399 const bool shouldEnable = ::HasSomethingToPaste (this);
400 d->actionPasteInNewWindow->setEnabled (shouldEnable);
401 d->actionPaste->setEnabled (shouldEnable);
405 #endif
408 // private
409 QRect kpMainWindow::calcUsefulPasteRect (int imageWidth, int imageHeight)
411 #if DEBUG_KP_MAIN_WINDOW && 1
412 kDebug () << "kpMainWindow::calcUsefulPasteRect("
413 << imageWidth << "," << imageHeight
414 << ")"
415 << endl;
416 #endif
417 Q_ASSERT (d->document);
419 // TODO: 1st choice is to paste sel near but not overlapping last deselect point
421 if (d->mainView && d->scrollView)
423 const QPoint viewTopLeft (d->scrollView->contentsX (),
424 d->scrollView->contentsY ());
426 const QPoint docTopLeft = d->mainView->transformViewToDoc (viewTopLeft);
428 if ((docTopLeft.x () + imageWidth <= d->document->width () &&
429 docTopLeft.y () + imageHeight <= d->document->height ()) ||
430 imageWidth <= docTopLeft.x () ||
431 imageHeight <= docTopLeft.y ())
433 return QRect (docTopLeft.x (), docTopLeft.y (),
434 imageWidth, imageHeight);
438 return QRect (0, 0, imageWidth, imageHeight);
441 // private
442 void kpMainWindow::paste (const kpAbstractSelection &sel, bool forceTopLeft)
444 #if DEBUG_KP_MAIN_WINDOW && 1
445 kDebug () << "kpMainWindow::paste(forceTopLeft=" << forceTopLeft << ")"
446 << endl;
447 #endif
449 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
451 toolEndShape ();
455 // Make sure we've got a document (esp. with File/Close)
458 if (!d->document)
460 kpDocument *newDoc = new kpDocument (
461 sel.width (), sel.height (), documentEnvironment ());
463 // will also create viewManager
464 setDocument (newDoc);
469 // Paste as new selection
472 const kpAbstractImageSelection *imageSel =
473 dynamic_cast <const kpAbstractImageSelection *> (&sel);
474 if (imageSel && imageSel->hasContent () && imageSel->transparency ().isTransparent ())
476 d->colorToolBar->flashColorSimilarityToolBarItem ();
479 kpAbstractSelection *selInUsefulPos = sel.clone ();
480 if (!forceTopLeft)
481 selInUsefulPos->moveTo (calcUsefulPasteRect (sel.width (), sel.height ()).topLeft ());
482 // TODO: Should use kpCommandHistory::addCreateSelectionCommand(),
483 // as well, to really support pasting selection borders.
484 addDeselectFirstCommand (new kpToolSelectionCreateCommand (
485 dynamic_cast <kpTextSelection *> (selInUsefulPos) ?
486 i18n ("Text: Create Box") :
487 i18n ("Selection: Create"),
488 *selInUsefulPos,
489 commandEnvironment ()));
490 delete selInUsefulPos;
493 #if DEBUG_KP_MAIN_WINDOW && 1
494 kDebug () << "sel.size=" << QSize (sel.width (), sel.height ())
495 << " document.size="
496 << QSize (d->document->width (), d->document->height ())
497 << endl;
498 #endif
500 // If the selection is bigger than the document, automatically
501 // resize the document (with the option of Undo'ing) to fit
502 // the selection.
504 // No annoying dialog necessary.
506 if (sel.width () > d->document->width () ||
507 sel.height () > d->document->height ())
509 d->commandHistory->addCommand (
510 new kpTransformResizeScaleCommand (
511 false/*act on doc, not sel*/,
512 qMax (sel.width (), d->document->width ()),
513 qMax (sel.height (), d->document->height ()),
514 kpTransformResizeScaleCommand::Resize,
515 commandEnvironment ()));
519 // public
520 void kpMainWindow::pasteText (const QString &text,
521 bool forceNewTextSelection,
522 const QPoint &newTextSelectionTopLeft)
524 #if DEBUG_KP_MAIN_WINDOW && 1
525 kDebug () << "kpMainWindow::pasteText(" << text
526 << ",forceNewTextSelection=" << forceNewTextSelection
527 << ",newTextSelectionTopLeft=" << newTextSelectionTopLeft
528 << ")" << endl;
529 #endif
531 if (text.isEmpty ())
532 return;
535 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
537 toolEndShape ();
540 QList <QString> textLines;
541 textLines.append (QString ());
543 for (int i = 0; i < (int) text.length (); i++)
545 if (text [i] == '\n')
546 textLines.push_back (QString::null); //krazy:exclude=nullstrassign for old broken gcc
547 else
548 textLines [textLines.size () - 1].append (text [i]);
552 if (!forceNewTextSelection &&
553 d->document && d->document->textSelection () &&
554 d->commandHistory && d->viewManager)
556 #if DEBUG_KP_MAIN_WINDOW && 1
557 kDebug () << "\treusing existing Text Selection";
558 #endif
560 kpTextSelection *textSel = d->document->textSelection ();
561 if (!textSel->hasContent ())
563 #if DEBUG_KP_MAIN_WINDOW && 1
564 kDebug () << "\t\tneeds content";
565 #endif
566 commandHistory ()->addCreateSelectionCommand (
567 new kpToolSelectionCreateCommand (
568 i18n ("Text: Create Box"),
569 *textSel,
570 commandEnvironment ()),
571 false/*no exec*/);
574 kpMacroCommand *macroCmd = new kpMacroCommand (i18n ("Text: Paste"),
575 commandEnvironment ());
576 // (yes, this is the same check as the previous "if")
577 if (!textSel->hasContent ())
579 kpCommand *giveContentCmd = new kpToolTextGiveContentCommand (
580 *textSel,
581 QString ()/*uninteresting child of macro cmd*/,
582 commandEnvironment ());
583 giveContentCmd->execute ();
585 macroCmd->addCommand (giveContentCmd);
588 for (int i = 0; i < (int) textLines.size (); i++)
590 if (i > 0)
592 macroCmd->addCommand (
593 new kpToolTextEnterCommand (
594 QString::null/*uninteresting child of macroCmd*/, //krazy:exclude=nullstrassign for old broken gcc
595 d->viewManager->textCursorRow (),
596 d->viewManager->textCursorCol (),
597 kpToolTextEnterCommand::AddEnterNow,
598 commandEnvironment ()));
601 macroCmd->addCommand (
602 new kpToolTextInsertCommand (
603 QString::null/*uninteresting child of macroCmd*/, //krazy:exclude=nullstrassign for old broken gcc
604 d->viewManager->textCursorRow (),
605 d->viewManager->textCursorCol (),
606 textLines [i],
607 commandEnvironment ()));
610 d->commandHistory->addCommand (macroCmd, false/*no exec*/);
612 else
614 #if DEBUG_KP_MAIN_WINDOW && 1
615 kDebug () << "\tcreating Text Selection";
616 #endif
618 const kpTextStyle ts = textStyle ();
619 const QFontMetrics fontMetrics = ts.fontMetrics ();
621 int height = textLines.size () * fontMetrics.height ();
622 if (textLines.size () >= 1)
623 height += (textLines.size () - 1) * fontMetrics.leading ();
625 int width = 0;
626 for (QList <QString>::const_iterator it = textLines.constBegin ();
627 it != textLines.constEnd ();
628 it++)
630 const int w = fontMetrics.width (*it);
631 if (w > width)
632 width = w;
636 const int selWidth = qMax (kpTextSelection::MinimumWidthForTextStyle (ts),
637 width + kpTextSelection::TextBorderSize () * 2);
638 const int selHeight = qMax (kpTextSelection::MinimumHeightForTextStyle (ts),
639 height + kpTextSelection::TextBorderSize () * 2);
640 kpTextSelection newTextSel (QRect (0, 0, selWidth, selHeight),
641 textLines,
642 ts);
644 if (newTextSelectionTopLeft != KP_INVALID_POINT)
646 newTextSel.moveTo (newTextSelectionTopLeft);
647 paste (newTextSel, true/*force topLeft*/);
649 else
651 paste (newTextSel);
656 // public
657 void kpMainWindow::pasteTextAt (const QString &text, const QPoint &point,
658 bool allowNewTextSelectionPointShift)
660 #if DEBUG_KP_MAIN_WINDOW && 1
661 kDebug () << "kpMainWindow::pasteTextAt(" << text
662 << ",point=" << point
663 << ",allowNewTextSelectionPointShift="
664 << allowNewTextSelectionPointShift
665 << ")" << endl;
666 #endif
668 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
670 toolEndShape ();
673 if (d->document &&
674 d->document->textSelection () &&
675 d->document->textSelection ()->pointIsInTextArea (point))
677 kpTextSelection *textSel = d->document->textSelection ();
679 int row, col;
681 if (textSel->hasContent ())
683 row = textSel->closestTextRowForPoint (point);
684 col = textSel->closestTextColForPoint (point);
686 else
688 row = col = 0;
691 d->viewManager->setTextCursorPosition (row, col);
693 pasteText (text);
695 else
697 QPoint pointToUse = point;
699 if (allowNewTextSelectionPointShift)
701 // TODO: In terms of doc pixels, would be inconsistent behaviour
702 // based on zoomLevel of view.
703 // pointToUse -= QPoint (-view->selectionResizeHandleAtomicSize (),
704 // -view->selectionResizeHandleAtomicSize ());
707 pasteText (text, true/*force new text selection*/, pointToUse);
711 // public slot
712 void kpMainWindow::slotPaste ()
714 #if DEBUG_KP_MAIN_WINDOW && 1
715 kDebug () << "kpMainWindow::slotPaste() CALLED";
716 #endif
718 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
720 toolEndShape ();
723 if (!::HasSomethingToPasteWithDialogIfNot (this))
724 return;
728 // Acquire the image.
731 const QMimeData *md = QApplication::clipboard ()->mimeData (QClipboard::Clipboard);
732 Q_ASSERT (md);
734 kpAbstractImageSelection *sel = kpSelectionDrag::decode (md,
735 pasteWarnAboutLossInfo ());
736 if (sel)
738 sel->setTransparency (imageSelectionTransparency ());
739 paste (*sel);
740 delete sel;
742 else if (md->hasText ())
744 pasteText (md->text ());
746 else
748 kpSetOverrideCursorSaver cursorSaver (Qt::ArrowCursor);
750 kDebug () << "kpMainWindow::slotPaste() could not decode selection";
751 kDebug () << "\tFormats supported:" << md->formats ();
753 // TODO: fix Klipper
754 KMessageBox::sorry (this,
755 i18n ("<qt><p>KolourPaint cannot paste the contents of"
756 " the clipboard as the data unexpectedly disappeared.</p>"
758 "<p>This usually occurs if the application which was"
759 " responsible"
760 " for the clipboard contents has been closed.</p></qt>"),
761 i18n ("Cannot Paste"));
763 // TODO: PROPAGATE: interprocess
764 // TODO: Is this loop safe since a KMainWindow later along in the list,
765 // could be closed as the code in the body almost certainly re-enters
766 // the event loop? Problem for KDE 3 as well, I think.
767 foreach (KMainWindow *kmw, KMainWindow::memberList ())
769 Q_ASSERT (dynamic_cast <kpMainWindow *> (kmw));
770 kpMainWindow *mw = static_cast <kpMainWindow *> (kmw);
772 #if DEBUG_KP_MAIN_WINDOW
773 kDebug () << "\t\tmw=" << mw;
774 #endif
776 mw->slotEnablePaste ();
779 return;
783 // private slot
784 void kpMainWindow::slotPasteInNewWindow ()
786 #if DEBUG_KP_MAIN_WINDOW && 1
787 kDebug () << "kpMainWindow::slotPasteInNewWindow() CALLED";
788 #endif
790 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
792 toolEndShape ();
795 if (!::HasSomethingToPasteWithDialogIfNot (this))
796 return;
800 // Pasting must ensure that:
802 // Requirement 1. the document is the same size as the image to be pasted.
803 // Requirement 2. transparent pixels in the image must remain as transparent.
806 kpMainWindow *win = new kpMainWindow (0/*no document*/);
807 win->show ();
809 // Make "Edit / Paste in New Window" always paste white pixels as white.
810 // Don't let selection transparency get in the way and paste them as
811 // transparent.
812 kpImageSelectionTransparency transparency = win->imageSelectionTransparency ();
813 if (transparency.isTransparent ())
815 #if DEBUG_KP_MAIN_WINDOW && 1
816 kDebug () << "\tchanging image selection transparency to opaque";
817 #endif
818 transparency.setOpaque ();
819 // Since we are setting selection transparency programmatically
820 // -- as opposed to in response to user input -- this will not
821 // affect the selection transparency tool option widget's "last used"
822 // config setting.
823 win->setImageSelectionTransparency (transparency);
826 // (this handles Requirement 1. above)
827 win->slotPaste ();
829 // (this handles Requirement 2. above;
830 // slotDeselect() is not enough unless the document is filled with the
831 // transparent color in advance)
832 win->slotCrop ();
835 // public slot
836 void kpMainWindow::slotDelete ()
838 #if DEBUG_KP_MAIN_WINDOW && 1
839 kDebug () << "kpMainWindow::slotDelete() CALLED";
840 #endif
841 if (!d->actionDelete->isEnabled ())
843 #if DEBUG_KP_MAIN_WINDOW && 1
844 kDebug () << "\taction not enabled - was probably called from kpTool::keyPressEvent()";
845 #endif
846 return;
849 Q_ASSERT (d->document && d->document->selection ());
851 toolEndShape ();
853 addImageOrSelectionCommand (new kpToolSelectionDestroyCommand (
854 d->document->textSelection () ?
855 i18n ("Text: Delete Box") : // not to be confused with i18n ("Text: Delete")
856 i18n ("Selection: Delete"),
857 false/*no push onto doc*/,
858 commandEnvironment ()));
862 // private slot
863 void kpMainWindow::slotSelectAll ()
865 #if DEBUG_KP_MAIN_WINDOW && 1
866 kDebug () << "kpMainWindow::slotSelectAll() CALLED";
867 #endif
868 Q_ASSERT (d->document);
870 toolEndShape ();
872 if (d->document->selection ())
873 slotDeselect ();
875 // just the border - don't actually pull image from doc yet
876 d->document->setSelection (
877 kpRectangularImageSelection (d->document->rect (),
878 imageSelectionTransparency ()));
880 if (tool ())
881 tool ()->somethingBelowTheCursorChanged ();
885 // private
886 void kpMainWindow::addDeselectFirstCommand (kpCommand *cmd)
888 #if DEBUG_KP_MAIN_WINDOW && 1
889 kDebug () << "kpMainWindow::addDeselectFirstCommand("
890 << cmd
891 << ")"
892 << endl;
893 #endif
896 kpAbstractSelection *sel = d->document->selection ();
898 #if DEBUG_KP_MAIN_WINDOW && 1
899 kDebug () << "\tsel=" << sel;
900 #endif
902 if (sel)
904 // if you just dragged out something with no action then
905 // forget the drag
906 if (!sel->hasContent ())
908 #if DEBUG_KP_MAIN_WINDOW && 1
909 kDebug () << "\tjust a fresh border - was nop - delete";
910 #endif
911 d->document->selectionDelete ();
912 if (tool ())
913 tool ()->somethingBelowTheCursorChanged ();
915 if (cmd)
916 d->commandHistory->addCommand (cmd);
918 else
920 #if DEBUG_KP_MAIN_WINDOW && 1
921 kDebug () << "\treal selection with image - push onto doc cmd";
922 #endif
923 kpCommand *deselectCommand = new kpToolSelectionDestroyCommand (
924 dynamic_cast <kpTextSelection *> (sel) ?
925 i18n ("Text: Finish") :
926 i18n ("Selection: Deselect"),
927 true/*push onto document*/,
928 commandEnvironment ());
930 if (cmd)
932 kpMacroCommand *macroCmd = new kpMacroCommand (cmd->name (),
933 commandEnvironment ());
934 macroCmd->addCommand (deselectCommand);
935 macroCmd->addCommand (cmd);
936 d->commandHistory->addCommand (macroCmd);
938 else
939 d->commandHistory->addCommand (deselectCommand);
942 else
944 if (cmd)
945 d->commandHistory->addCommand (cmd);
950 // public slot
951 void kpMainWindow::slotDeselect ()
953 #if DEBUG_KP_MAIN_WINDOW && 1
954 kDebug () << "kpMainWindow::slotDeselect() CALLED";
955 #endif
956 Q_ASSERT (d->document && d->document->selection ());
958 toolEndShape ();
960 addDeselectFirstCommand (0);
964 // private slot
965 void kpMainWindow::slotCopyToFile ()
967 #if DEBUG_KP_MAIN_WINDOW
968 kDebug () << "kpMainWindow::slotCopyToFile()";
969 #endif
971 toolEndShape ();
974 if (!d->document->selection ())
975 return;
977 kpImage imageToSave;
979 if (d->document->imageSelection ())
981 kpAbstractImageSelection *imageSel = d->document->imageSelection ();
982 if (!imageSel->hasContent ())
984 // Not a floating selection - user has just selected a region;
985 // haven't pulled it off yet so probably don't expect and can't
986 // visualize selection transparency so give opaque, not transparent
987 // image.
988 imageToSave = d->document->getSelectedBaseImage ();
990 else
991 imageToSave = imageSel->transparentImage ();
993 else if (d->document->textSelection ())
995 imageToSave = d->document->textSelection ()->approximateImage ();
997 else
998 Q_ASSERT (!"Unknown selection type");
1001 kpDocumentSaveOptions chosenSaveOptions;
1002 bool allowOverwritePrompt, allowLossyPrompt;
1003 KUrl chosenURL = askForSaveURL (i18n ("Copy to File"),
1004 d->lastCopyToURL.url (),
1005 imageToSave,
1006 d->lastCopyToSaveOptions,
1007 kpDocumentMetaInfo (),
1008 kpSettingsGroupEditCopyTo,
1009 false/*allow remote files*/,
1010 &chosenSaveOptions,
1011 d->copyToFirstTime,
1012 &allowOverwritePrompt,
1013 &allowLossyPrompt);
1015 if (chosenURL.isEmpty ())
1016 return;
1019 if (!kpDocument::savePixmapToFile (imageToSave,
1020 chosenURL,
1021 chosenSaveOptions, kpDocumentMetaInfo (),
1022 allowOverwritePrompt,
1023 allowLossyPrompt,
1024 this))
1026 return;
1030 addRecentURL (chosenURL);
1033 d->lastCopyToURL = chosenURL;
1034 d->lastCopyToSaveOptions = chosenSaveOptions;
1036 d->copyToFirstTime = false;
1039 // private slot
1040 void kpMainWindow::slotPasteFromFile ()
1042 #if DEBUG_KP_MAIN_WINDOW
1043 kDebug () << "kpMainWindow::slotPasteFromFile()";
1044 #endif
1046 toolEndShape ();
1049 KUrl::List urls = askForOpenURLs (i18n ("Paste From File"),
1050 d->lastPasteFromURL.url (),
1051 false/*only 1 URL*/);
1053 if (urls.count () != 1)
1054 return;
1056 KUrl url = urls.first ();
1057 d->lastPasteFromURL = url;
1060 kpImage image = kpDocument::getPixmapFromFile (url,
1061 false/*show error message if doesn't exist*/,
1062 this);
1065 if (image.isNull ())
1066 return;
1069 addRecentURL (url);
1071 paste (kpRectangularImageSelection (
1072 QRect (0, 0, image.width (), image.height ()),
1073 image,
1074 imageSelectionTransparency ()));