kpMainWindow_Edit.cpp 28.5 KB
Newer Older
1
/*
2
   Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
3
   Copyright (c) 2011 Martin Koller <kollix@aon.at>
4
   All rights reserved.
Clarence Dang's avatar
 
Clarence Dang committed
5

6 7 8
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:
Clarence Dang's avatar
 
Clarence Dang committed
9

10 11 12 13 14
   1. Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
   2. Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
Clarence Dang's avatar
 
Clarence Dang committed
15

16 17 18 19 20 21
   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 23
   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 25
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27

Clarence Dang's avatar
Clarence Dang committed
28

29
#include <kpMainWindow.h>
30
#include <kpMainWindowPrivate.h>
31 32 33

#include <qapplication.h>
#include <qclipboard.h>
Clarence Dang's avatar
+debug  
Clarence Dang committed
34
#include <qdatetime.h>
35
#include <qfontmetrics.h>
Clarence Dang's avatar
Clarence Dang committed
36
#include <qimage.h>
37
#include <qlist.h>
Clarence Dang's avatar
Clarence Dang committed
38
#include <qmenu.h>
39
#include <QDesktopWidget>
40
#include <QScrollBar>
41 42 43 44

#include <kaction.h>
#include <kdebug.h>
#include <klocale.h>
45
#include <kmessagebox.h>
Aaron J. Seigo's avatar
Aaron J. Seigo committed
46
#include <kstandardaction.h>
47
#include <kactioncollection.h>
48
#include <kxmlguifactory.h>
49

50 51
#include <kpAbstractImageSelection.h>
#include <kpColorToolBar.h>
52 53 54 55
#include <kpCommandHistory.h>
#include <kpDocument.h>
#include <kpDocumentMetaInfo.h>
#include <kpDocumentSaveOptions.h>
56
#include <kpImageSelectionTransparency.h>
57
#include <kpMacroCommand.h>
58
#include <kpPixmapFX.h>
59
#include <kpRectangularImageSelection.h>
60
#include <kpSelectionDrag.h>
Clarence Dang's avatar
Clarence Dang committed
61
#include <kpSetOverrideCursorSaver.h>
62
#include <kpTextSelection.h>
63
#include <kpTool.h>
64
#include <kpToolTextGiveContentCommand.h>
65 66
#include <kpToolSelectionCreateCommand.h>
#include <kpToolSelectionDestroyCommand.h>
67 68
#include <kpToolTextEnterCommand.h>
#include <kpToolTextInsertCommand.h>
69 70
#include <kpTransformCrop.h>
#include <kpTransformResizeScaleCommand.h>
71 72 73
#include <kpViewManager.h>
#include <kpViewScrollableContainer.h>
#include <kpZoomedView.h>
74

75
//---------------------------------------------------------------------
76

77 78 79 80 81
// private
void kpMainWindow::setupEditMenuActions ()
{
    KActionCollection *ac = actionCollection ();

82

83
    // Undo/Redo
Clarence Dang's avatar
Clarence Dang committed
84
    // CONFIG: Need GUI for config history size.
85
    d->commandHistory = new kpCommandHistory (true/*read config*/, this);
86

87
    if (d->configFirstTime)
88 89
    {
        // (so that cfg-file-editing user can modify in the meantime)
90
        d->commandHistory->writeConfig ();
91 92
    }

93

94 95 96 97 98 99
    d->actionCut = KStandardAction::cut (this, SLOT (slotCut ()), ac);
    d->actionCopy = KStandardAction::copy (this, SLOT (slotCopy ()), ac);
    d->actionPaste = KStandardAction::paste (this, SLOT (slotPaste ()), ac);
    d->actionPasteInNewWindow = ac->addAction ("edit_paste_in_new_window");
    d->actionPasteInNewWindow->setText (i18n ("Paste in &New Window"));
    connect (d->actionPasteInNewWindow, SIGNAL (triggered (bool)),
100
        SLOT (slotPasteInNewWindow ()));
101
    d->actionPasteInNewWindow->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
102

103 104 105 106
    //d->actionDelete = KStandardAction::clear (this, SLOT (slotDelete ()), ac);
    d->actionDelete = ac->addAction ("edit_clear");
    d->actionDelete->setText (i18n ("&Delete Selection"));
    connect (d->actionDelete, SIGNAL (triggered (bool)), SLOT (slotDelete ()));
107

108 109
    d->actionSelectAll = KStandardAction::selectAll (this, SLOT (slotSelectAll ()), ac);
    d->actionDeselect = KStandardAction::deselect (this, SLOT (slotDeselect ()), ac);
110

111

112 113 114
    d->actionCopyToFile = ac->addAction ("edit_copy_to_file");
    d->actionCopyToFile->setText (i18n ("C&opy to File..."));
    connect (d->actionCopyToFile, SIGNAL (triggered (bool)),
115
        SLOT (slotCopyToFile ()));
116
    d->actionPasteFromFile = ac->addAction ("edit_paste_from_file");
117 118
    d->actionPasteFromFile->setText (i18n ("Paste &From File..."));
    connect (d->actionPasteFromFile, SIGNAL (triggered (bool)),
119
        SLOT (slotPasteFromFile ()));
120 121


122
    d->editMenuDocumentActionsEnabled = false;
Clarence Dang's avatar
Clarence Dang committed
123 124
    enableEditMenuDocumentActions (false);

125
    // Paste should always be enabled, as long as there is something to paste
Clarence Dang's avatar
Clarence Dang committed
126 127 128 129
    // (independent of whether we have a document or not)
    connect (QApplication::clipboard (), SIGNAL (dataChanged ()),
             this, SLOT (slotEnablePaste ()));
    slotEnablePaste ();
130 131
}

132 133
//---------------------------------------------------------------------

Clarence Dang's avatar
Clarence Dang committed
134 135 136
// private
void kpMainWindow::enableEditMenuDocumentActions (bool enable)
{
137 138 139 140
    // d->actionCut
    // d->actionCopy
    // d->actionPaste
    // d->actionPasteInNewWindow
141

142
    // d->actionDelete
143

144 145
    d->actionSelectAll->setEnabled (enable);
    // d->actionDeselect
146

147
    d->editMenuDocumentActionsEnabled = enable;
148

149
    // d->actionCopyToFile
150 151 152 153

    // Unlike d->actionPaste, we disable this if there is no document.
    // This is because "File / Open" would do the same thing, if there is
    // no document.
154
    d->actionPasteFromFile->setEnabled (enable);
Clarence Dang's avatar
Clarence Dang committed
155
}
156

157
//---------------------------------------------------------------------
Clarence Dang's avatar
Clarence Dang committed
158

159
// public
Clarence Dang's avatar
Clarence Dang committed
160
QMenu *kpMainWindow::selectionToolRMBMenu ()
161
{
Clarence Dang's avatar
Clarence Dang committed
162
    return qobject_cast <QMenu *> (guiFactory ()->container ("selectionToolRMBMenu", this));
163 164
}

165
//---------------------------------------------------------------------
166

167 168
// private slot
void kpMainWindow::slotCut ()
169
{
170
#if DEBUG_KP_MAIN_WINDOW && 1
171
    kDebug () << "kpMainWindow::slotCut() CALLED";
172
#endif
173

Urs Wolfer's avatar
Urs Wolfer committed
174
    kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
175

Clarence Dang's avatar
Clarence Dang committed
176
    Q_ASSERT (d->document && d->document->selection ());
Clarence Dang's avatar
Clarence Dang committed
177

Clarence Dang's avatar
Clarence Dang committed
178
    toolEndShape ();
179

180 181
    slotCopy ();
    slotDelete ();
182 183
}

184 185
//---------------------------------------------------------------------

186 187 188 189 190 191 192
static QMimeData *NewTextMimeData (const QString &text)
{
    QMimeData *md = new QMimeData ();
    md->setText (text);
    return md;
}

193 194
//---------------------------------------------------------------------

195 196 197
// private slot
void kpMainWindow::slotCopy ()
{
198
#if DEBUG_KP_MAIN_WINDOW && 1
199
    kDebug () << "kpMainWindow::slotCopy() CALLED";
200
#endif
201

Urs Wolfer's avatar
Urs Wolfer committed
202
    kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
203

Clarence Dang's avatar
Clarence Dang committed
204
    Q_ASSERT (d->document && d->document->selection ());
205

Clarence Dang's avatar
Clarence Dang committed
206
    toolEndShape ();
207

208
    kpAbstractSelection *sel = d->document->selection ()->clone ();
209

210
    if (dynamic_cast <kpTextSelection *> (sel))
211
    {
212 213
        kpTextSelection *textSel = static_cast <kpTextSelection *> (sel);
        if (!textSel->text ().isEmpty ())
214
        {
215 216 217
            QApplication::clipboard ()->setMimeData (
                ::NewTextMimeData (textSel->text ()),
                QClipboard::Clipboard);
218 219 220 221 222 223 224 225 226 227 228 229

            // SYNC: Normally, users highlight text and press CTRL+C.
            //       Highlighting text copies it to the X11 "middle
            //       mouse button" clipboard.  CTRL+C copies it to the
            //       separate, Windows-like "CTRL+V" clipboard.
            //
            //       However, KolourPaint doesn't support highlighting.
            //       So when they press CTRL+C to copy all text, simulate
            //       the highlighting by copying the text to the "middle
            //       mouse button" clipboard.  We don't do this for images
            //       as no one ever middle-mouse-pastes images.
            //
230
            //       Note that we don't share the QMimeData pointer with
231 232 233 234 235
            //       the above in case Qt doesn't expect it.
            //
            //       Once we change KolourPaint to support highlighted text
            //       and CTRL+C to copy only the highlighted text, delete
            //       this code.
236 237 238
            QApplication::clipboard ()->setMimeData (
                ::NewTextMimeData (textSel->text ()),
                QClipboard::Selection);
239
        }
240
    }
241
    else if (dynamic_cast <kpAbstractImageSelection *> (sel))
242
    {
243 244 245 246 247 248 249 250
        kpAbstractImageSelection *imageSel =
            static_cast <kpAbstractImageSelection *> (sel);

        // Transparency doesn't get sent across the aether so nuke it now
        // so that transparency mask doesn't get needlessly recalculated
        // if we ever call sel.setBaseImage().
        imageSel->setTransparency (kpImageSelectionTransparency ());

251 252 253 254 255 256 257
        kpImage rawImage;

        if (imageSel->hasContent ())
            rawImage = imageSel->baseImage ();
        else
            rawImage = d->document->getSelectedBaseImage ();

258
        imageSel->setBaseImage ( rawImage );
259

260 261 262
        QApplication::clipboard ()->setMimeData (
            new kpSelectionDrag (*imageSel),
            QClipboard::Clipboard);
263
    }
264 265 266 267
    else
        Q_ASSERT (!"Unknown selection type");

    delete sel;
Clarence Dang's avatar
Clarence Dang committed
268
}
269

270
//---------------------------------------------------------------------
271 272 273 274

// private slot
void kpMainWindow::slotEnablePaste ()
{
275 276
    const QMimeData *md =
        QApplication::clipboard()->mimeData(QClipboard::Clipboard);
277

278 279 280
    // It's faster to test for QMimeData::hasText() first due to the
    // lazy evaluation of the '||' operator.
    const bool shouldEnable = (md->hasText() || kpSelectionDrag::canDecode(md));
281

282 283
    d->actionPasteInNewWindow->setEnabled(shouldEnable);
    d->actionPaste->setEnabled(shouldEnable);
284 285
}

286
//---------------------------------------------------------------------
287

288
// private
289
QRect kpMainWindow::calcUsefulPasteRect (int imageWidth, int imageHeight)
290 291
{
#if DEBUG_KP_MAIN_WINDOW && 1
Laurent Montel's avatar
Laurent Montel committed
292
    kDebug () << "kpMainWindow::calcUsefulPasteRect("
293
               << imageWidth << "," << imageHeight
294 295 296
               << ")"
               << endl;
#endif
Clarence Dang's avatar
Clarence Dang committed
297
    Q_ASSERT (d->document);
298 299 300

    // TODO: 1st choice is to paste sel near but not overlapping last deselect point

301
    if (d->mainView && d->scrollView)
302
    {
303 304
        const QPoint viewTopLeft (d->scrollView->horizontalScrollBar()->value (),
                                  d->scrollView->verticalScrollBar()->value ());
305

306
        const QPoint docTopLeft = d->mainView->transformViewToDoc (viewTopLeft);
307

308 309 310 311
        if ((docTopLeft.x () + imageWidth <= d->document->width () &&
             docTopLeft.y () + imageHeight <= d->document->height ()) ||
            imageWidth <= docTopLeft.x () ||
            imageHeight <= docTopLeft.y ())
312 313
        {
            return QRect (docTopLeft.x (), docTopLeft.y (),
314
                          imageWidth, imageHeight);
315 316 317
        }
    }

318
    return QRect (0, 0, imageWidth, imageHeight);
319 320
}

321 322
//---------------------------------------------------------------------

323
// private
324
void kpMainWindow::paste(const kpAbstractSelection &sel, bool forceTopLeft)
325
{
326
#if DEBUG_KP_MAIN_WINDOW && 1
Clarence Dang's avatar
Clarence Dang committed
327
    kDebug () << "kpMainWindow::paste(forceTopLeft=" << forceTopLeft << ")"
328 329 330
               << endl;
#endif

Urs Wolfer's avatar
Urs Wolfer committed
331
    kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
Clarence Dang's avatar
Clarence Dang committed
332

Clarence Dang's avatar
Clarence Dang committed
333
    toolEndShape ();
334

335 336 337 338
    //
    // Make sure we've got a document (esp. with File/Close)
    //

339
    if (!d->document)
340 341
    {
        kpDocument *newDoc = new kpDocument (
342
            sel.width (), sel.height (), documentEnvironment ());
343 344 345 346 347 348 349 350 351

        // will also create viewManager
        setDocument (newDoc);
    }

    //
    // Paste as new selection
    //

352 353 354 355
    const kpAbstractImageSelection *imageSel =
        dynamic_cast <const kpAbstractImageSelection *> (&sel);
    if (imageSel && imageSel->hasContent () && imageSel->transparency ().isTransparent ())
    {
356
        d->colorToolBar->flashColorSimilarityToolBarItem ();
357 358 359
    }

    kpAbstractSelection *selInUsefulPos = sel.clone ();
360
    if (!forceTopLeft)
361
        selInUsefulPos->moveTo (calcUsefulPasteRect (sel.width (), sel.height ()).topLeft ());
362 363
    // TODO: Should use kpCommandHistory::addCreateSelectionCommand(),
    //       as well, to really support pasting selection borders.
364
    addDeselectFirstCommand (new kpToolSelectionCreateCommand (
365
        dynamic_cast <kpTextSelection *> (selInUsefulPos) ?
366 367
            i18n ("Text: Create Box") :
            i18n ("Selection: Create"),
368 369 370
        *selInUsefulPos,
        commandEnvironment ()));
    delete selInUsefulPos;
371 372


373
#if DEBUG_KP_MAIN_WINDOW && 1
Clarence Dang's avatar
Clarence Dang committed
374
    kDebug () << "sel.size=" << QSize (sel.width (), sel.height ())
375
               << " document.size="
Clarence Dang's avatar
Clarence Dang committed
376
               << QSize (d->document->width (), d->document->height ())
377 378 379
               << endl;
#endif

380 381 382 383 384 385
    // If the selection is bigger than the document, automatically
    // resize the document (with the option of Undo'ing) to fit
    // the selection.
    //
    // No annoying dialog necessary.
    //
386 387
    if (sel.width () > d->document->width () ||
        sel.height () > d->document->height ())
388
    {
389
        d->commandHistory->addCommand (
390
            new kpTransformResizeScaleCommand (
391
                false/*act on doc, not sel*/,
392 393
                qMax (sel.width (), d->document->width ()),
                qMax (sel.height (), d->document->height ()),
394
                kpTransformResizeScaleCommand::Resize,
395
                commandEnvironment ()));
396 397 398
    }
}

399 400
//---------------------------------------------------------------------

401
// public
402 403 404
void kpMainWindow::pasteText (const QString &text,
                              bool forceNewTextSelection,
                              const QPoint &newTextSelectionTopLeft)
405 406
{
#if DEBUG_KP_MAIN_WINDOW && 1
Laurent Montel's avatar
Laurent Montel committed
407
    kDebug () << "kpMainWindow::pasteText(" << text
408
               << ",forceNewTextSelection=" << forceNewTextSelection
409
               << ",newTextSelectionTopLeft=" << newTextSelectionTopLeft
410 411 412
               << ")" << endl;
#endif

413
    if ( text.isEmpty() )
414 415
        return;

Urs Wolfer's avatar
Urs Wolfer committed
416
    kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
417

Clarence Dang's avatar
Clarence Dang committed
418
    toolEndShape ();
419

420
    QStringList textLines = text.split('\n');
421

422
    if (!forceNewTextSelection &&
423 424
        d->document && d->document->textSelection () &&
        d->commandHistory && d->viewManager)
425 426
    {
    #if DEBUG_KP_MAIN_WINDOW && 1
427
        kDebug () << "\treusing existing Text Selection";
428
    #endif
429

430 431
        d->viewManager->setQueueUpdates();

432 433 434 435
        kpTextSelection *textSel = d->document->textSelection ();
        if (!textSel->hasContent ())
        {
        #if DEBUG_KP_MAIN_WINDOW && 1
436
            kDebug () << "\t\tneeds content";
437 438 439 440 441 442 443 444 445
        #endif
            commandHistory ()->addCreateSelectionCommand (
                new kpToolSelectionCreateCommand (
                    i18n ("Text: Create Box"),
                    *textSel,
                    commandEnvironment ()),
                false/*no exec*/);
        }

446
        kpMacroCommand *macroCmd = new kpMacroCommand (i18n ("Text: Paste"),
447
            commandEnvironment ());
448 449 450 451 452 453 454 455 456 457 458
        // (yes, this is the same check as the previous "if")
        if (!textSel->hasContent ())
        {
            kpCommand *giveContentCmd = new kpToolTextGiveContentCommand (
                *textSel,
                QString ()/*uninteresting child of macro cmd*/,
                commandEnvironment ());
            giveContentCmd->execute ();

            macroCmd->addCommand (giveContentCmd);
        }
459 460 461 462 463 464 465

        for (int i = 0; i < (int) textLines.size (); i++)
        {
            if (i > 0)
            {
                macroCmd->addCommand (
                    new kpToolTextEnterCommand (
Martin Koller's avatar
Martin Koller committed
466
                        QString()/*uninteresting child of macroCmd*/,
467 468
                        d->viewManager->textCursorRow (),
                        d->viewManager->textCursorCol (),
469
                        kpToolTextEnterCommand::AddEnterNow,
470
                        commandEnvironment ()));
471 472 473 474
            }

            macroCmd->addCommand (
                new kpToolTextInsertCommand (
Martin Koller's avatar
Martin Koller committed
475
                    QString()/*uninteresting child of macroCmd*/,
476 477
                    d->viewManager->textCursorRow (),
                    d->viewManager->textCursorCol (),
478
                    textLines [i],
479
                    commandEnvironment ()));
480 481
        }

482
        d->commandHistory->addCommand (macroCmd, false/*no exec*/);
483 484

        d->viewManager->restoreQueueUpdates();
485 486 487 488
    }
    else
    {
    #if DEBUG_KP_MAIN_WINDOW && 1
489
        kDebug () << "\tcreating Text Selection";
490 491 492 493 494 495 496 497 498 499
    #endif

        const kpTextStyle ts = textStyle ();
        const QFontMetrics fontMetrics = ts.fontMetrics ();

        int height = textLines.size () * fontMetrics.height ();
        if (textLines.size () >= 1)
            height += (textLines.size () - 1) * fontMetrics.leading ();

        int width = 0;
Laurent Montel's avatar
Laurent Montel committed
500 501
        for (QList <QString>::const_iterator it = textLines.constBegin ();
             it != textLines.constEnd ();
Martin Koller's avatar
Martin Koller committed
502
             ++it)
503 504 505 506 507 508
        {
            const int w = fontMetrics.width (*it);
            if (w > width)
                width = w;
        }

509
        // limit the size to avoid memory overflow
510 511
        width = qMin(qMax(QApplication::desktop()->width(), d->document ? d->document->width() : 0), width);
        height = qMin(qMax(QApplication::desktop()->height(), d->document ? d->document->height() : 0), height);
512

513 514 515 516
        const int selWidth = qMax (kpTextSelection::MinimumWidthForTextStyle (ts),
                                   width + kpTextSelection::TextBorderSize () * 2);
        const int selHeight = qMax (kpTextSelection::MinimumHeightForTextStyle (ts),
                                    height + kpTextSelection::TextBorderSize () * 2);
517
        kpTextSelection newTextSel (QRect (0, 0, selWidth, selHeight),
518 519
            textLines,
            ts);
520

521 522
        if (newTextSelectionTopLeft != KP_INVALID_POINT)
        {
523 524
            newTextSel.moveTo (newTextSelectionTopLeft);
            paste (newTextSel, true/*force topLeft*/);
525 526 527
        }
        else
        {
528
            paste (newTextSel);
529
        }
530 531 532
    }
}

533 534
//---------------------------------------------------------------------

535
// public
536 537
void kpMainWindow::pasteTextAt (const QString &text, const QPoint &point,
                                bool allowNewTextSelectionPointShift)
538 539
{
#if DEBUG_KP_MAIN_WINDOW && 1
Laurent Montel's avatar
Laurent Montel committed
540
    kDebug () << "kpMainWindow::pasteTextAt(" << text
541
               << ",point=" << point
542 543
               << ",allowNewTextSelectionPointShift="
               << allowNewTextSelectionPointShift
544 545 546
               << ")" << endl;
#endif

Urs Wolfer's avatar
Urs Wolfer committed
547
    kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
548

Clarence Dang's avatar
Clarence Dang committed
549
    toolEndShape ();
550 551


552 553 554
    if (d->document &&
        d->document->textSelection () &&
        d->document->textSelection ()->pointIsInTextArea (point))
555
    {
556
        kpTextSelection *textSel = d->document->textSelection ();
557

558 559 560 561 562 563 564 565 566 567 568
        int row, col;

        if (textSel->hasContent ())
        {
            row = textSel->closestTextRowForPoint (point);
            col = textSel->closestTextColForPoint (point);
        }
        else
        {
            row = col = 0;
        }
569

570
        d->viewManager->setTextCursorPosition (row, col);
571 572 573 574 575

        pasteText (text);
    }
    else
    {
576 577 578 579
        QPoint pointToUse = point;

        if (allowNewTextSelectionPointShift)
        {
580 581 582
            // TODO: In terms of doc pixels, would be inconsistent behaviour
            //       based on zoomLevel of view.
            // pointToUse -= QPoint (-view->selectionResizeHandleAtomicSize (),
583
            //                       -view->selectionResizeHandleAtomicSize ());
584 585 586
        }

        pasteText (text, true/*force new text selection*/, pointToUse);
587 588 589
    }
}

590
//---------------------------------------------------------------------
Clarence Dang's avatar
Clarence Dang committed
591
// public slot
592

593 594 595
void kpMainWindow::slotPaste()
{
    kpSetOverrideCursorSaver cursorSaver(Qt::WaitCursor);
596

597
    toolEndShape();
598

599
    const QMimeData *mimeData = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
600

601 602
    kpAbstractImageSelection *sel = kpSelectionDrag::decode(mimeData);
    if ( sel )
603
    {
604 605
        sel->setTransparency(imageSelectionTransparency());
        paste(*sel);
606
        delete sel;
607
    }
608
    else if ( mimeData->hasText() )
609
    {
610
        pasteText(mimeData->text());
611 612
    }
    else
Clarence Dang's avatar
Clarence Dang committed
613
    {
614
        kpSetOverrideCursorSaver cursorSaver(Qt::ArrowCursor);
615

616 617 618 619
        KMessageBox::sorry(this,
            i18n("<qt>KolourPaint cannot paste the contents of"
                 " the clipboard as it has an unknown format.</qt>"),
            i18n("Cannot Paste"));
620 621 622
    }
}

623 624
//---------------------------------------------------------------------

Clarence Dang's avatar
Clarence Dang committed
625 626 627 628
// private slot
void kpMainWindow::slotPasteInNewWindow ()
{
#if DEBUG_KP_MAIN_WINDOW && 1
629
    kDebug () << "kpMainWindow::slotPasteInNewWindow() CALLED";
Clarence Dang's avatar
Clarence Dang committed
630 631
#endif

Urs Wolfer's avatar
Urs Wolfer committed
632
    kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
Clarence Dang's avatar
Clarence Dang committed
633

Clarence Dang's avatar
Clarence Dang committed
634
    toolEndShape ();
Clarence Dang's avatar
Clarence Dang committed
635

636 637 638 639 640 641 642
    //
    // Pasting must ensure that:
    //
    // Requirement 1. the document is the same size as the image to be pasted.
    // Requirement 2. transparent pixels in the image must remain as transparent.
    //

Clarence Dang's avatar
Clarence Dang committed
643 644 645
    kpMainWindow *win = new kpMainWindow (0/*no document*/);
    win->show ();

646 647 648 649 650 651 652
    // Make "Edit / Paste in New Window" always paste white pixels as white.
    // Don't let selection transparency get in the way and paste them as
    // transparent.
    kpImageSelectionTransparency transparency = win->imageSelectionTransparency ();
    if (transparency.isTransparent ())
    {
    #if DEBUG_KP_MAIN_WINDOW && 1
653
        kDebug () << "\tchanging image selection transparency to opaque";
654 655 656 657 658 659 660 661 662 663
    #endif
        transparency.setOpaque ();
        // Since we are setting selection transparency programmatically
        // -- as opposed to in response to user input -- this will not
        // affect the selection transparency tool option widget's "last used"
        // config setting.
        win->setImageSelectionTransparency (transparency);
    }

    // (this handles Requirement 1. above)
Clarence Dang's avatar
Clarence Dang committed
664
    win->slotPaste ();
665

666 667 668 669 670 671 672 673
    // if slotPaste could not decode clipboard data, no document was created
    if ( win->document() )
    {
      // (this handles Requirement 2. above;
      //  slotDeselect() is not enough unless the document is filled with the
      //  transparent color in advance)
      win->slotCrop();
    }
Clarence Dang's avatar
Clarence Dang committed
674 675
}

676 677
//---------------------------------------------------------------------

678
// public slot
679 680
void kpMainWindow::slotDelete ()
{
681
#if DEBUG_KP_MAIN_WINDOW && 1
682
    kDebug () << "kpMainWindow::slotDelete() CALLED";
683
#endif
684
    if (!d->actionDelete->isEnabled ())
685 686
    {
    #if DEBUG_KP_MAIN_WINDOW && 1
687
        kDebug () << "\taction not enabled - was probably called from kpTool::keyPressEvent()";
688 689 690 691
    #endif
        return;
    }

Clarence Dang's avatar
Clarence Dang committed
692
    Q_ASSERT (d->document && d->document->selection ());
693

Clarence Dang's avatar
Clarence Dang committed
694
    toolEndShape ();
695

696
    addImageOrSelectionCommand (new kpToolSelectionDestroyCommand (
697
        d->document->textSelection () ?
698 699
            i18n ("Text: Delete Box") :  // not to be confused with i18n ("Text: Delete")
            i18n ("Selection: Delete"),
700
        false/*no push onto doc*/,
701
        commandEnvironment ()));
702 703
}

704
//---------------------------------------------------------------------
705 706 707 708

// private slot
void kpMainWindow::slotSelectAll ()
{
709
#if DEBUG_KP_MAIN_WINDOW && 1
710
    kDebug () << "kpMainWindow::slotSelectAll() CALLED";
711
#endif
Clarence Dang's avatar
Clarence Dang committed
712
    Q_ASSERT (d->document);
Clarence Dang's avatar
Clarence Dang committed
713

Clarence Dang's avatar
Clarence Dang committed
714
    toolEndShape ();
715

716
    if (d->document->selection ())
717 718
        slotDeselect ();

719
    // just the border - don't actually pull image from doc yet
720 721
    d->document->setSelection (
        kpRectangularImageSelection (d->document->rect (),
722
            imageSelectionTransparency ()));
723 724 725

    if (tool ())
        tool ()->somethingBelowTheCursorChanged ();
726 727
}

728
//---------------------------------------------------------------------
729 730

// private
731
void kpMainWindow::addDeselectFirstCommand (kpCommand *cmd)
732
{
733
#if DEBUG_KP_MAIN_WINDOW && 1
Laurent Montel's avatar
Laurent Montel committed
734
    kDebug () << "kpMainWindow::addDeselectFirstCommand("
735 736 737
               << cmd
               << ")"
               << endl;
738
#endif
739

740

741
    kpAbstractSelection *sel = d->document->selection ();
742

743
#if DEBUG_KP_MAIN_WINDOW && 1
744
    kDebug () << "\tsel=" << sel;
745 746
#endif

747 748 749 750
    if (sel)
    {
        // if you just dragged out something with no action then
        // forget the drag
751
        if (!sel->hasContent ())
752 753
        {
        #if DEBUG_KP_MAIN_WINDOW && 1
754
            kDebug () << "\tjust a fresh border - was nop - delete";
755
        #endif
756
            d->document->selectionDelete ();
757 758 759
            if (tool ())
                tool ()->somethingBelowTheCursorChanged ();

760
            if (cmd)
761
                d->commandHistory->addCommand (cmd);
762 763 764 765
        }
        else
        {
        #if DEBUG_KP_MAIN_WINDOW && 1
766
            kDebug () << "\treal selection with image - push onto doc cmd";
767
        #endif
768
            kpCommand *deselectCommand = new kpToolSelectionDestroyCommand (
769
                dynamic_cast <kpTextSelection *> (sel) ?
770 771
                    i18n ("Text: Finish") :
                    i18n ("Selection: Deselect"),
772
                true/*push onto document*/,
773
                commandEnvironment ());
774 775 776

            if (cmd)
            {
777 778
                kpMacroCommand *macroCmd = new kpMacroCommand (cmd->name (),
                    commandEnvironment ());
779 780
                macroCmd->addCommand (deselectCommand);
                macroCmd->addCommand (cmd);
781
                d->commandHistory->addCommand (macroCmd);
782 783
            }
            else
784
                d->commandHistory->addCommand (deselectCommand);
785 786 787 788
        }
    }
    else
    {
789
        if (cmd)
790
            d->commandHistory->addCommand (cmd);
791
    }
792
}
793

794
//---------------------------------------------------------------------
795 796 797 798 799

// public slot
void kpMainWindow::slotDeselect ()
{
#if DEBUG_KP_MAIN_WINDOW && 1
800
    kDebug () << "kpMainWindow::slotDeselect() CALLED";
801
#endif
Clarence Dang's avatar
Clarence Dang committed
802
    Q_ASSERT (d->document && d->document->selection ());
803

Clarence Dang's avatar
Clarence Dang committed
804
    toolEndShape ();
805

806 807
    addDeselectFirstCommand (0);
}
808

809
//---------------------------------------------------------------------
810 811 812 813 814

// private slot
void kpMainWindow::slotCopyToFile ()
{
#if DEBUG_KP_MAIN_WINDOW
815
    kDebug () << "kpMainWindow::slotCopyToFile()";
816 817
#endif

Clarence Dang's avatar
Clarence Dang committed
818
    toolEndShape ();