1
2/*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 Copyright (c) 2007 Martin Koller <kollix@aon.at>
5 Copyright (c) 2007 John Layt <john@layt.net>
6 Copyright (c) 2011 Martin Koller <kollix@aon.at>
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15 2. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29*/
30
31
32#include <kpMainWindow.h>
33#include <kpMainWindowPrivate.h>
34
35#include <qdatastream.h>
36#include <QDesktopWidget>
37#include <qpainter.h>
38#include <qpixmap.h>
39#include <qsize.h>
40#include <QPrinter>
41#include <QPrintDialog>
42#include <QApplication>
43#include <QTimer>
44#include <QLabel>
45#include <QCheckBox>
46#include <QVBoxLayout>
47
48#include <kdialog.h>
49#include <kaction.h>
50#include <kactioncollection.h>
51#include <kconfig.h>
52#include <kconfiggroup.h>
53#include <kdebug.h>
54#include <KIntSpinBox>
55#include <kfiledialog.h>
56#include <kglobal.h>
57#include <kiconloader.h>
58#include <kimageio.h>
59#include <kio/netaccess.h>
60#include <klocale.h>
61#include <kmessagebox.h>
62#include <krecentfilesaction.h>
63#include <kscan.h>
64#include <kstandardshortcut.h>
65#include <kstandardaction.h>
66#include <ktoolinvocation.h>
67#include <kdeprintdialog.h>
68#include <kprintpreview.h>
69#include <kurlcombobox.h>
70
71#include <kpCommandHistory.h>
72#include <kpDefs.h>
73#include <kpDocument.h>
74#include <kpDocumentMetaInfoCommand.h>
75#include <kpDocumentMetaInfoDialog.h>
76#include <kpDocumentSaveOptionsWidget.h>
77#include <kpPixmapFX.h>
78#include <kpPrintDialogPage.h>
79#include <kpView.h>
80#include <kpViewManager.h>
81
82// private
83void kpMainWindow::setupFileMenuActions ()
84{
85#if DEBUG_KP_MAIN_WINDOW
86 kDebug () << "kpMainWindow::setupFileMenuActions()";
87#endif
88 KActionCollection *ac = actionCollection ();
89
90 d->actionNew = KStandardAction::openNew (this, SLOT (slotNew ()), ac);
91 d->actionOpen = KStandardAction::open (this, SLOT (slotOpen ()), ac);
92
93 d->actionOpenRecent = KStandardAction::openRecent (this, SLOT (slotOpenRecent (const KUrl &)), ac);
94 d->actionOpenRecent->loadEntries (KGlobal::config ()->group (kpSettingsGroupRecentFiles));
95#if DEBUG_KP_MAIN_WINDOW
96 kDebug () << "\trecent URLs=" << d->actionOpenRecent->items ();
97#endif
98
99 d->actionSave = KStandardAction::save (this, SLOT (slotSave ()), ac);
100 d->actionSaveAs = KStandardAction::saveAs (this, SLOT (slotSaveAs ()), ac);
101
102 d->actionExport = ac->addAction("file_export");
103 d->actionExport->setText (i18n ("E&xport..."));
104 d->actionExport->setIcon (KIcon ("document-export"));
105 connect(d->actionExport, SIGNAL(triggered(bool) ), SLOT (slotExport ()));
106
107 d->actionScan = ac->addAction("file_scan");
108 d->actionScan->setText(i18n ("Scan..."));
109 d->actionScan->setIcon(SmallIcon("scanner"));
110 connect(d->actionScan, SIGNAL(triggered(bool)), SLOT(slotScan()));
111
112 d->actionScreenshot = ac->addAction("file_screenshot");
113 d->actionScreenshot->setText(i18n("Acquire Screenshot"));
114 connect(d->actionScreenshot, SIGNAL(triggered(bool)), SLOT(slotScreenshot()));
115
116 d->actionProperties = ac->addAction ("file_properties");
117 d->actionProperties->setText (i18n ("Properties"));
118 d->actionProperties->setIcon (KIcon ("document-properties"));
119 connect (d->actionProperties, SIGNAL (triggered (bool)), SLOT (slotProperties ()));
120
121 //d->actionRevert = KStandardAction::revert (this, SLOT (slotRevert ()), ac);
122 d->actionReload = ac->addAction ("file_revert");
123 d->actionReload->setText (i18n ("Reloa&d"));
124 d->actionReload->setIcon (KIcon ("view-refresh"));
125 connect(d->actionReload, SIGNAL(triggered(bool) ), SLOT (slotReload ()));
126 d->actionReload->setShortcuts(KStandardShortcut::reload ());
127 slotEnableReload ();
128
129 d->actionPrint = KStandardAction::print (this, SLOT (slotPrint ()), ac);
130 d->actionPrintPreview = KStandardAction::printPreview (this, SLOT (slotPrintPreview ()), ac);
131
132 d->actionMail = KStandardAction::mail (this, SLOT (slotMail ()), ac);
133
134 d->actionClose = KStandardAction::close (this, SLOT (slotClose ()), ac);
135 d->actionQuit = KStandardAction::quit (this, SLOT (slotQuit ()), ac);
136
137 d->scanDialog = 0;
138
139 enableFileMenuDocumentActions (false);
140}
141
142//---------------------------------------------------------------------
143
144// private
145void kpMainWindow::enableFileMenuDocumentActions (bool enable)
146{
147 // d->actionNew
148 // d->actionOpen
149
150 // d->actionOpenRecent
151
152 d->actionSave->setEnabled (enable);
153 d->actionSaveAs->setEnabled (enable);
154
155 d->actionExport->setEnabled (enable);
156
157 // d->actionScan
158
159 d->actionProperties->setEnabled (enable);
160
161 // d->actionReload
162
163 d->actionPrint->setEnabled (enable);
164 d->actionPrintPreview->setEnabled (enable);
165
166 d->actionMail->setEnabled (enable);
167
168 d->actionClose->setEnabled (enable);
169 // d->actionQuit->setEnabled (enable);
170}
171
172//---------------------------------------------------------------------
173
174// private
175void kpMainWindow::addRecentURL (const KUrl &url_)
176{
177 // HACK: KRecentFilesAction::loadEntries() clears the KRecentFilesAction::d->urls
178 // map.
179 //
180 // So afterwards, the URL ref, our method is given, points to an
181 // element in this now-cleared map (see KRecentFilesAction::urlSelected(QAction*)).
182 // Accessing it would result in a crash.
183 //
184 // To avoid the crash, make a copy of it before calling
185 // loadEntries() and use this copy, instead of the to-be-dangling
186 // ref.
187 const KUrl url = url_;
188
189#if DEBUG_KP_MAIN_WINDOW
190 kDebug () << "kpMainWindow::addRecentURL(" << url << ")";
191#endif
192 if (url.isEmpty ())
193 return;
194
195
196 KSharedConfig::Ptr cfg = KGlobal::config();
197
198 // KConfig::readEntry() does not actually reread from disk, hence doesn't
199 // realize what other processes have done e.g. Settings / Show Path
200 cfg->reparseConfiguration ();
201
202#if DEBUG_KP_MAIN_WINDOW
203 kDebug () << "\trecent URLs=" << d->actionOpenRecent->items ();
204#endif
205 // HACK: Something might have changed interprocess.
206 // If we could PROPAGATE: interprocess, then this wouldn't be required.
207 d->actionOpenRecent->loadEntries (cfg->group (kpSettingsGroupRecentFiles));
208#if DEBUG_KP_MAIN_WINDOW
209 kDebug () << "\tafter loading config=" << d->actionOpenRecent->items ();
210#endif
211
212 d->actionOpenRecent->addUrl (url);
213
214 d->actionOpenRecent->saveEntries (cfg->group (kpSettingsGroupRecentFiles));
215 cfg->sync ();
216
217#if DEBUG_KP_MAIN_WINDOW
218 kDebug () << "\tnew recent URLs=" << d->actionOpenRecent->items ();
219#endif
220
221
222 // TODO: PROPAGATE: interprocess
223 // TODO: Is this loop safe since a KMainWindow later along in the list,
224 // could be closed as the code in the body almost certainly re-enters
225 // the event loop? Problem for KDE 3 as well, I think.
226 foreach (KMainWindow *kmw, KMainWindow::memberList ())
227 {
228 Q_ASSERT (dynamic_cast <kpMainWindow *> (kmw));
229 kpMainWindow *mw = static_cast <kpMainWindow *> (kmw);
230
231 #if DEBUG_KP_MAIN_WINDOW
232 kDebug () << "\t\tmw=" << mw;
233 #endif
234
235 if (mw != this)
236 {
237 // WARNING: Do not use KRecentFilesAction::setItems()
238 // - it does not work since only its superclass,
239 // KSelectAction, implements setItems() and can't
240 // update KRecentFilesAction's URL list.
241
242 // Avoid URL memory leak in KRecentFilesAction::loadEntries().
243 mw->d->actionOpenRecent->clear ();
244
245 mw->d->actionOpenRecent->loadEntries (cfg->group (kpSettingsGroupRecentFiles));
246 #if DEBUG_KP_MAIN_WINDOW
247 kDebug () << "\t\t\tcheck recent URLs="
248 << mw->d->actionOpenRecent->items () << endl;
249 #endif
250 }
251 }
252}
253
254//---------------------------------------------------------------------
255
256
257// private slot
258// TODO: Disable action if
259// (d->configOpenImagesInSameWindow && d->document && d->document->isEmpty())
260// as it does nothing if this is true.
261void kpMainWindow::slotNew ()
262{
263 toolEndShape ();
264
265 if (d->document && !d->configOpenImagesInSameWindow)
266 {
267 // A document -- empty or otherwise -- is open.
268 // Force open a new window. In contrast, open() might not open
269 // a new window in this case.
270 kpMainWindow *win = new kpMainWindow ();
271 win->show ();
272 }
273 else
274 {
275 open (KUrl (), true/*create an empty doc*/);
276 }
277}
278
279//---------------------------------------------------------------------
280
281
282// private
283QSize kpMainWindow::defaultDocSize () const
284{
285 // KConfig::readEntry() does not actually reread from disk, hence doesn't
286 // realize what other processes have done e.g. Settings / Show Path
287 KGlobal::config ()->reparseConfiguration ();
288
289 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupGeneral);
290
291 QSize docSize = cfg.readEntry (kpSettingLastDocSize, QSize ());
292
293 if (docSize.isEmpty ())
294 {
295 docSize = QSize (400, 300);
296 }
297 else
298 {
299 // Don't get too big or you'll thrash (or even lock up) the computer
300 // just by opening a window
301 docSize = QSize (qMin (2048, docSize.width ()),
302 qMin (2048, docSize.height ()));
303 }
304
305 return docSize;
306}
307
308//---------------------------------------------------------------------
309
310// private
311void kpMainWindow::saveDefaultDocSize (const QSize &size)
312{
313#if DEBUG_KP_MAIN_WINDOW
314 kDebug () << "\tCONFIG: saving Last Doc Size = " << size;
315#endif
316
317 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupGeneral);
318
319 cfg.writeEntry (kpSettingLastDocSize, size);
320 cfg.sync ();
321}
322
323//---------------------------------------------------------------------
324
325// private
326bool kpMainWindow::shouldOpen ()
327{
328 if (d->configOpenImagesInSameWindow)
329 {
330 #if DEBUG_KP_MAIN_WINDOW
331 kDebug () << "\topenImagesInSameWindow";
332 #endif
333 // (this brings up a dialog and might save the current doc)
334 if (!queryCloseDocument ())
335 {
336 #if DEBUG_KP_MAIN_WINDOW
337 kDebug () << "\t\tqueryCloseDocument() aborts open";
338 #endif
339 return false;
340 }
341 }
342
343 return true;
344}
345
346//---------------------------------------------------------------------
347
348// private
349void kpMainWindow::setDocumentChoosingWindow (kpDocument *doc)
350{
351 // Want new window?
352 if (d->document && !d->document->isEmpty () &&
353 !d->configOpenImagesInSameWindow)
354 {
355 // Send doc to new window.
356 kpMainWindow *win = new kpMainWindow (doc);
357 win->show ();
358 }
359 else
360 {
361 // (sets up views, doc signals)
362 setDocument (doc);
363 }
364}
365
366//---------------------------------------------------------------------
367
368// private
369kpDocument *kpMainWindow::openInternal (const KUrl &url,
370 const QSize &fallbackDocSize,
371 bool newDocSameNameIfNotExist)
372{
373 // If using OpenImagesInSameWindow mode, ask whether to close the
374 // current document.
375 if (!shouldOpen ())
376 return 0;
377
378 // Create/open doc.
379 kpDocument *newDoc = new kpDocument (fallbackDocSize.width (),
380 fallbackDocSize.height (),
381 documentEnvironment ());
382 if (!newDoc->open (url, newDocSameNameIfNotExist))
383 {
384 #if DEBUG_KP_MAIN_WINDOW
385 kDebug () << "\topen failed";
386 #endif
387 delete newDoc;
388 return 0;
389 }
390
391#if DEBUG_KP_MAIN_WINDOW
392 kDebug () << "\topen OK";
393#endif
394 // Send document to current or new window.
395 setDocumentChoosingWindow (newDoc);
396
397 return newDoc;
398}
399
400//---------------------------------------------------------------------
401
402// private
403bool kpMainWindow::open (const KUrl &url, bool newDocSameNameIfNotExist)
404{
405#if DEBUG_KP_MAIN_WINDOW
406 kDebug () << "kpMainWindow::open(" << url
407 << ",newDocSameNameIfNotExist=" << newDocSameNameIfNotExist
408 << ")" << endl;
409#endif
410
411 kpDocument *newDoc = openInternal (url,
412 defaultDocSize (),
413 newDocSameNameIfNotExist);
414 if (newDoc)
415 {
416 if (newDoc->isFromURL (false/*don't bother checking exists*/))
417 addRecentURL (url);
418 return true;
419 }
420 else
421 {
422 return false;
423 }
424}
425
426//---------------------------------------------------------------------
427
428// private
429KUrl::List kpMainWindow::askForOpenURLs(const QString &caption, bool allowMultipleURLs)
430{
431 QStringList mimeTypes = KImageIO::mimeTypes (KImageIO::Reading);
432#if DEBUG_KP_MAIN_WINDOW
433 QStringList sortedMimeTypes = mimeTypes;
434 sortedMimeTypes.sort ();
435 kDebug () << "kpMainWindow::askForURLs(allowMultiple="
436 << allowMultipleURLs
437 << ")" << endl
438 << "\tmimeTypes=" << mimeTypes << endl
439 << "\tsortedMimeTypes=" << sortedMimeTypes << endl;
440#endif
441 QString filter = mimeTypes.join (" ");
442
443 KFileDialog fd(KUrl("kfiledialog:///dir/"), filter, this);
444 fd.setCaption(caption);
445 fd.setOperationMode(KFileDialog::Opening);
446
447 if (allowMultipleURLs)
448 fd.setMode (KFile::Files);
449
450 if (fd.exec ())
451 return fd.selectedUrls ();
452 else
453 return KUrl::List ();
454}
455
456//---------------------------------------------------------------------
457
458// private slot
459void kpMainWindow::slotOpen ()
460{
461 toolEndShape ();
462
463 const KUrl::List urls = askForOpenURLs(i18nc("@title:window", "Open Image"));
464
465 for (KUrl::List::const_iterator it = urls.begin ();
466 it != urls.end ();
467 ++it)
468 {
469 open (*it);
470 }
471}
472
473//---------------------------------------------------------------------
474
475// private slot
476void kpMainWindow::slotOpenRecent (const KUrl &url)
477{
478#if DEBUG_KP_MAIN_WINDOW
479 kDebug () << "kpMainWindow::slotOpenRecent(" << url << ")";
480 kDebug () << "\titems=" << d->actionOpenRecent->items ();
481#endif
482
483 toolEndShape ();
484
485 open (url);
486
487 // If the open is successful, addRecentURL() would have bubbled up the
488 // URL in the File / Open Recent action. As a side effect, the URL is
489 // deselected.
490 //
491 // If the open fails, we should deselect the URL:
492 //
493 // 1. for consistency
494 //
495 // 2. because it has not been opened.
496 //
497 d->actionOpenRecent->setCurrentItem (-1);
498}
499
500//---------------------------------------------------------------------
501
502// private slot
503void kpMainWindow::slotScan ()
504{
505#if DEBUG_KP_MAIN_WINDOW
506 kDebug () << "kpMainWindow::slotScan() scanDialog=" << d->scanDialog;
507#endif
508
509 toolEndShape ();
510
511 if (!d->scanDialog)
512 {
513 // Create scan dialog by looking for plugin.
514 // [takes about 500ms on 350Mhz]
515 d->scanDialog = KScanDialog::getScanDialog (this);
516
517 // No scanning support (kdegraphics/libkscan) installed?
518 // [Remove $KDEDIR/share/servicetypes/kscan.desktop and
519 // $KDEDIR/share/services/scanservice.desktop to simulate this]
520 if (!d->scanDialog)
521 {
522 // Instead, we could try to create the scan dialog in the ctor
523 // and just disable the action in the first place, removing
524 // the need for this dialog.
525 //
526 // But this increases startup time and is a bit risky e.g. if
527 // the scan support hangs, KolourPaint would not be able to be
528 // started at all.
529 //
530 // Also, disabling the action is bad because the scan support
531 // can be installed while KolourPaint is still running.
532 KMessageBox::sorry (this,
533 i18n ("No plugin was found which provides the scanner dialog.\n"
534 "This usually means that the package providing the ksaneplugin is not installed."),
535 i18n ("No Scanning Support"));
536 return;
537 }
538
539 #if DEBUG_KP_MAIN_WINDOW
540 kDebug () << "\tcreated scanDialog=" << d->scanDialog;
541 #endif
542 connect (d->scanDialog, SIGNAL (finalImage (const QImage &, int)),
543 SLOT (slotScanned (const QImage &, int)));
544 }
545
546
547 // If using OpenImagesInSameWindow mode, ask whether to close the
548 // current document.
549 //
550 // Do this after scan support is detected. Because if it's not, what
551 // would be the point of closing the document?
552 //
553 // Ideally, we would do this after the user presses "Final Scan" in
554 // the scan dialog and before the scan begins (if the user wants to
555 // cancel the scan operation, it would be annoying to offer this choice
556 // only after the slow scan is completed) but the KScanDialog API does
557 // not allow this. So we settle for doing this before any
558 // scan dialogs are shown. We don't do this between KScanDialog::setup()
559 // and KScanDialog::exec() as it could be confusing alternating between
560 // scanning and KolourPaint dialogs.
561 if (!shouldOpen ())
562 return;
563
564
565#if DEBUG_KP_MAIN_WINDOW
566 kDebug () << "\tcalling setup";
567#endif
568 // Bring up dialog to select scan device.
569 // If there is no scanner, we find that this does not bring up a dialog
570 // but still returns true.
571 if (d->scanDialog->setup ())
572 {
573 #if DEBUG_KP_MAIN_WINDOW
574 kDebug () << "\t\tOK - showing dialog";
575 #endif
576 // Called only if scanner configured/available.
577 //
578 // In reality, this seems to be called even if you press "Cancel" in
579 // the KScanDialog::setup() dialog!
580 //
581 // We use exec() to make sure it's modal. show() seems to work too
582 // but better safe than sorry.
583 d->scanDialog->exec ();
584 }
585 else
586 {
587 // Have never seen this code path execute even if "Cancel" is pressed.
588 #if DEBUG_KP_MAIN_WINDOW
589 kDebug () << "\t\tFAIL";
590 #endif
591 }
592}
593
594//---------------------------------------------------------------------
595
596// private slot
597void kpMainWindow::slotScanned (const QImage &image, int)
598{
599#if DEBUG_KP_MAIN_WINDOW
600 kDebug () << "kpMainWindow::slotScanned() image.rect=" << image.rect ();
601#endif
602
603#if DEBUG_KP_MAIN_WINDOW
604 kDebug () << "\thiding dialog";
605#endif
606 // (KScanDialog does not close itself after a scan is made)
607 //
608 // Close the dialog, first thing:
609 //
610 // 1. This means that any dialogs we bring up won't be nested on top.
611 //
612 // 2. We don't want to return from this method but forget to close
613 // the dialog. So do it before anything else.
614 d->scanDialog->hide ();
615
616 // (just in case there's some drawing between slotScan() exiting and
617 // us being called)
618 toolEndShape ();
619
620
621 // TODO: Maybe this code should be moved into kpdocument.cpp -
622 // since it resembles the responsibilities of kpDocument::open().
623
624 kpDocumentSaveOptions saveOptions;
625 kpDocumentMetaInfo metaInfo;
626
627 kpDocument::getDataFromImage(image, saveOptions, metaInfo);
628
629 // Create document from image and meta info.
630 kpDocument *doc = new kpDocument (image.width (), image.height (),
631 documentEnvironment ());
632 doc->setImage (image);
633 doc->setSaveOptions (saveOptions);
634 doc->setMetaInfo (metaInfo);
635
636 // Send document to current or new window.
637 setDocumentChoosingWindow (doc);
638}
639
640//---------------------------------------------------------------------
641
642void kpMainWindow::slotScreenshot()
643{
644 toolEndShape();
645
646 KDialog *dialog = new KDialog(this);
647 dialog->setButtons(KDialog::Ok | KDialog::Cancel);
648
649 QLabel *label = new QLabel(i18n("Snapshot Delay"));
650 KIntSpinBox *seconds = new KIntSpinBox;
651 seconds->setRange(0, 99);
652 seconds->setSuffix(ki18np(" second", " seconds"));
653 seconds->setSpecialValueText(i18n("No delay"));
654
655 QCheckBox *hideWindow = new QCheckBox(i18n("Hide Main Window"));
656 hideWindow->setChecked(true);
657
658 QVBoxLayout *vbox = new QVBoxLayout(dialog->mainWidget());
659 vbox->addWidget(label);
660 vbox->addWidget(seconds);
661 vbox->addWidget(hideWindow);
662
663 if ( dialog->exec() == KDialog::Rejected )
664 {
665 delete dialog;
666 return;
667 }
668
669 if ( hideWindow->isChecked() )
670 hide();
671
672 // at least 1 seconds to make sure the window is hidden and the hide effect already stopped
673 QTimer::singleShot((seconds->value() + 1) * 1000, this, SLOT(slotMakeScreenshot()));
674
675 delete dialog;
676}
677
678//---------------------------------------------------------------------
679
680void kpMainWindow::slotMakeScreenshot()
681{
682 QCoreApplication::processEvents();
683 QPixmap pixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
684
685 kpDocument *doc = new kpDocument(pixmap.width(), pixmap.height(),
686 documentEnvironment());
687 doc->setImage(pixmap.toImage());
688
689 // Send document to current or new window.
690 setDocumentChoosingWindow(doc);
691
692 show(); // in case we hid the mainwindow, show it again
693}
694
695//---------------------------------------------------------------------
696
697// private slot
698void kpMainWindow::slotProperties ()
699{
700 toolEndShape ();
701
702 kpDocumentMetaInfoDialog dialog (document ()->metaInfo (), this);
703
704 if (dialog.exec () && !dialog.isNoOp ())
705 {
706 commandHistory ()->addCommand (
707 new kpDocumentMetaInfoCommand (
708 i18n ("Document Properties"),
709 dialog.metaInfo ()/*new*/, *document ()->metaInfo ()/*old*/,
710 commandEnvironment ()));
711 }
712}
713
714//---------------------------------------------------------------------
715
716// private slot
717bool kpMainWindow::save (bool localOnly)
718{
719 if (d->document->url ().isEmpty () ||
720 !KImageIO::mimeTypes (KImageIO::Writing)
721 .contains (d->document->saveOptions ()->mimeType ()) ||
722 // SYNC: kpDocument::getPixmapFromFile() can't determine quality
723 // from file so it has been set initially to an invalid value.
724 (d->document->saveOptions ()->mimeTypeHasConfigurableQuality () &&
725 d->document->saveOptions ()->qualityIsInvalid ()) ||
726 (localOnly && !d->document->url ().isLocalFile ()))
727 {
728 return saveAs (localOnly);
729 }
730 else
731 {
732 if (d->document->save (false/*no overwrite prompt*/,
733 !d->document->savedAtLeastOnceBefore ()/*lossy prompt*/))
734 {
735 addRecentURL (d->document->url ());
736 return true;
737 }
738 else
739 return false;
740 }
741}
742
743//---------------------------------------------------------------------
744
745// private slot
746bool kpMainWindow::slotSave ()
747{
748 toolEndShape ();
749
750 return save ();
751}
752
753//---------------------------------------------------------------------
754
755// private
756KUrl kpMainWindow::askForSaveURL (const QString &caption,
757 const QString &startURL,
758 const kpImage &imageToBeSaved,
759 const kpDocumentSaveOptions &startSaveOptions,
760 const kpDocumentMetaInfo &docMetaInfo,
761 const QString &forcedSaveOptionsGroup,
762 bool localOnly,
763 kpDocumentSaveOptions *chosenSaveOptions,
764 bool isSavingForFirstTime,
765 bool *allowOverwritePrompt,
766 bool *allowLossyPrompt)
767{
768#if DEBUG_KP_MAIN_WINDOW
769 kDebug () << "kpMainWindow::askForURL() startURL=" << startURL;
770 startSaveOptions.printDebug ("\tstartSaveOptions");
771#endif
772
773 bool reparsedConfiguration = false;
774
775 // KConfig::readEntry() does not actually reread from disk, hence doesn't
776 // realize what other processes have done e.g. Settings / Show Path
777 // so reparseConfiguration() must be called
778#define SETUP_READ_CFG() \
779 if (!reparsedConfiguration) \
780 { \
781 KGlobal::config ()->reparseConfiguration (); \
782 reparsedConfiguration = true; \
783 } \
784 \
785 KConfigGroup cfg (KGlobal::config (), forcedSaveOptionsGroup); \
786
787
788 if (chosenSaveOptions)
789 *chosenSaveOptions = kpDocumentSaveOptions ();
790
791 if (allowOverwritePrompt)
792 *allowOverwritePrompt = true; // play it safe for now
793
794 if (allowLossyPrompt)
795 *allowLossyPrompt = true; // play it safe for now
796
797
798 kpDocumentSaveOptions fdSaveOptions = startSaveOptions;
799
800 QStringList mimeTypes = KImageIO::mimeTypes (KImageIO::Writing);
801#if DEBUG_KP_MAIN_WINDOW
802 QStringList sortedMimeTypes = mimeTypes;
803 sortedMimeTypes.sort ();
804 kDebug () << "\tmimeTypes=" << mimeTypes
805 << "\tsortedMimeTypes=" << sortedMimeTypes << endl;
806#endif
807 if (mimeTypes.isEmpty ())
808 {
809 kError () << "No KImageIO output mimetypes!" << endl;
810 return KUrl ();
811 }
812
813#define MIME_TYPE_IS_VALID() (!fdSaveOptions.mimeTypeIsInvalid () && \
814 mimeTypes.contains (fdSaveOptions.mimeType ()))
815 if (!MIME_TYPE_IS_VALID ())
816 {
817 #if DEBUG_KP_MAIN_WINDOW
818 kDebug () << "\tmimeType=" << fdSaveOptions.mimeType ()
819 << " not valid, get default" << endl;
820 #endif
821
822 SETUP_READ_CFG ();
823
824 fdSaveOptions.setMimeType (kpDocumentSaveOptions::defaultMimeType (cfg));
825
826
827 if (!MIME_TYPE_IS_VALID ())
828 {
829 #if DEBUG_KP_MAIN_WINDOW
830 kDebug () << "\tmimeType=" << fdSaveOptions.mimeType ()
831 << " not valid, get hardcoded" << endl;
832 #endif
833 if (mimeTypes.contains ("image/png"))
834 fdSaveOptions.setMimeType ("image/png");
835 else if (mimeTypes.contains ("image/bmp"))
836 fdSaveOptions.setMimeType ("image/bmp");
837 else
838 fdSaveOptions.setMimeType (mimeTypes.first ());
839 }
840 }
841#undef MIME_TYPE_IS_VALID
842
843 if (fdSaveOptions.colorDepthIsInvalid ())
844 {
845 SETUP_READ_CFG ();
846
847 fdSaveOptions.setColorDepth (kpDocumentSaveOptions::defaultColorDepth (cfg));
848 fdSaveOptions.setDither (kpDocumentSaveOptions::defaultDither (cfg));
849 }
850
851 if (fdSaveOptions.qualityIsInvalid ())
852 {
853 SETUP_READ_CFG ();
854
855 fdSaveOptions.setQuality (kpDocumentSaveOptions::defaultQuality (cfg));
856 }
857#if DEBUG_KP_MAIN_WINDOW
858 fdSaveOptions.printDebug ("\tcorrected saveOptions passed to fileDialog");
859#endif
860
861 kpDocumentSaveOptionsWidget *saveOptionsWidget =
862 new kpDocumentSaveOptionsWidget (imageToBeSaved,
863 fdSaveOptions,
864 docMetaInfo,
865 this);
866
867 KFileDialog fd (startURL, QString(), this,
868 saveOptionsWidget);
869 saveOptionsWidget->setVisualParent (&fd);
870 fd.setCaption (caption);
871 fd.setOperationMode (KFileDialog::Saving);
872 fd.setMimeFilter (mimeTypes, fdSaveOptions.mimeType ());
873 if (localOnly)
874 fd.setMode (KFile::File | KFile::LocalOnly);
875
876 connect (&fd, SIGNAL (filterChanged (const QString &)),
877 saveOptionsWidget, SLOT (setMimeType (const QString &)));
878
879 if ( fd.exec() == QDialog::Accepted )
880 {
881 kpDocumentSaveOptions newSaveOptions = saveOptionsWidget->documentSaveOptions ();
882 #if DEBUG_KP_MAIN_WINDOW
883 newSaveOptions.printDebug ("\tnewSaveOptions");
884 #endif
885
886 KConfigGroup cfg (KGlobal::config (), forcedSaveOptionsGroup);
887
888 // Save options user forced - probably want to use them in future
889 kpDocumentSaveOptions::saveDefaultDifferences (cfg,
890 fdSaveOptions, newSaveOptions);
891 cfg.sync ();
892
893
894 if (chosenSaveOptions)
895 *chosenSaveOptions = newSaveOptions;
896
897
898 bool shouldAllowOverwritePrompt =
899 (fd.selectedUrl () != startURL ||
900 newSaveOptions.mimeType () != startSaveOptions.mimeType ());
901 if (allowOverwritePrompt)
902 {
903 *allowOverwritePrompt = shouldAllowOverwritePrompt;
904 #if DEBUG_KP_MAIN_WINDOW
905 kDebug () << "\tallowOverwritePrompt=" << *allowOverwritePrompt;
906 #endif
907 }
908
909 if (allowLossyPrompt)
910 {
911 // SYNC: kpDocumentSaveOptions elements - everything except quality
912 // (one quality setting is "just as lossy" as another so no
913 // need to continually warn due to quality change)
914 *allowLossyPrompt =
915 (isSavingForFirstTime ||
916 shouldAllowOverwritePrompt ||
917 newSaveOptions.mimeType () != startSaveOptions.mimeType () ||
918 newSaveOptions.colorDepth () != startSaveOptions.colorDepth () ||
919 newSaveOptions.dither () != startSaveOptions.dither ());
920 #if DEBUG_KP_MAIN_WINDOW
921 kDebug () << "\tallowLossyPrompt=" << *allowLossyPrompt;
922 #endif
923 }
924
925
926 #if DEBUG_KP_MAIN_WINDOW
927 kDebug () << "\tselectedUrl=" << fd.selectedUrl ();
928 #endif
929 return fd.selectedUrl ();
930 }
931 else
932 return KUrl ();
933#undef SETUP_READ_CFG
934}
935
936//---------------------------------------------------------------------
937
938// private slot
939bool kpMainWindow::saveAs (bool localOnly)
940{
941 kpDocumentSaveOptions chosenSaveOptions;
942 bool allowOverwritePrompt, allowLossyPrompt;
943 KUrl chosenURL = askForSaveURL (i18nc ("@title:window", "Save Image As"),
944 d->document->url ().url (),
945 d->document->imageWithSelection (),
946 *d->document->saveOptions (),
947 *d->document->metaInfo (),
948 kpSettingsGroupFileSaveAs,
949 localOnly,
950 &chosenSaveOptions,
951 !d->document->savedAtLeastOnceBefore (),
952 &allowOverwritePrompt,
953 &allowLossyPrompt);
954
955
956 if (chosenURL.isEmpty ())
957 return false;
958
959
960 if (!d->document->saveAs (chosenURL, chosenSaveOptions,
961 allowOverwritePrompt,
962 allowLossyPrompt))
963 {
964 return false;
965 }
966
967
968 addRecentURL (chosenURL);
969
970 return true;
971}
972
973//---------------------------------------------------------------------
974
975// private slot
976bool kpMainWindow::slotSaveAs ()
977{
978 toolEndShape ();
979
980 return saveAs ();
981}
982
983//---------------------------------------------------------------------
984
985// private slot
986bool kpMainWindow::slotExport ()
987{
988 toolEndShape ();
989
990 kpDocumentSaveOptions chosenSaveOptions;
991 bool allowOverwritePrompt, allowLossyPrompt;
992 KUrl chosenURL = askForSaveURL (i18nc ("@title:window", "Export"),
993 d->lastExportURL.url (),
994 d->document->imageWithSelection (),
995 d->lastExportSaveOptions,
996 *d->document->metaInfo (),
997 kpSettingsGroupFileExport,
998 false/*allow remote files*/,
999 &chosenSaveOptions,
1000 d->exportFirstTime,
1001 &allowOverwritePrompt,
1002 &allowLossyPrompt);
1003
1004
1005 if (chosenURL.isEmpty ())
1006 return false;
1007
1008 if (!kpDocument::savePixmapToFile (d->document->imageWithSelection (),
1009 chosenURL,
1010 chosenSaveOptions, *d->document->metaInfo (),
1011 allowOverwritePrompt,
1012 allowLossyPrompt,
1013 this))
1014 {
1015 return false;
1016 }
1017
1018
1019 addRecentURL (chosenURL);
1020
1021 d->lastExportURL = chosenURL;
1022 d->lastExportSaveOptions = chosenSaveOptions;
1023
1024 d->exportFirstTime = false;
1025
1026 return true;
1027}
1028
1029//---------------------------------------------------------------------
1030
1031// private slot
1032void kpMainWindow::slotEnableReload ()
1033{
1034 d->actionReload->setEnabled (d->document);
1035}
1036
1037//---------------------------------------------------------------------
1038
1039// private slot
1040bool kpMainWindow::slotReload ()
1041{
1042 toolEndShape ();
1043
1044 Q_ASSERT (d->document);
1045
1046
1047 KUrl oldURL = d->document->url ();
1048
1049
1050 if (d->document->isModified ())
1051 {
1052 int result = KMessageBox::Cancel;
1053
1054 if (d->document->isFromURL (false/*don't bother checking exists*/) && !oldURL.isEmpty ())
1055 {
1056 result = KMessageBox::warningContinueCancel (this,
1057 i18n ("The document \"%1\" has been modified.\n"
1058 "Reloading will lose all changes since you last saved it.\n"
1059 "Are you sure?",
1060 d->document->prettyFilename ()),
1061 QString()/*caption*/,
1062 KGuiItem(i18n ("&Reload")));
1063 }
1064 else
1065 {
1066 result = KMessageBox::warningContinueCancel (this,
1067 i18n ("The document \"%1\" has been modified.\n"
1068 "Reloading will lose all changes.\n"
1069 "Are you sure?",
1070 d->document->prettyFilename ()),
1071 QString()/*caption*/,
1072 KGuiItem(i18n ("&Reload")));
1073 }
1074
1075 if (result != KMessageBox::Continue)
1076 return false;
1077 }
1078
1079
1080 kpDocument *doc = 0;
1081
1082 // If it's _supposed to_ come from a URL or it exists
1083 if (d->document->isFromURL (false/*don't bother checking exists*/) ||
1084 (!oldURL.isEmpty () && KIO::NetAccess::exists (oldURL, KIO::NetAccess::SourceSide/*open*/, this)))
1085 {
1086 #if DEBUG_KP_MAIN_WINDOW
1087 kDebug () << "kpMainWindow::slotReload() reloading from disk!";
1088 #endif
1089
1090 doc = new kpDocument (1, 1, documentEnvironment ());
1091 if (!doc->open (oldURL))
1092 {
1093 delete doc; doc = 0;
1094 return false;
1095 }
1096
1097 addRecentURL (oldURL);
1098 }
1099 else
1100 {
1101 #if DEBUG_KP_MAIN_WINDOW
1102 kDebug () << "kpMainWindow::slotReload() create doc";
1103 #endif
1104
1105 doc = new kpDocument (d->document->constructorWidth (),
1106 d->document->constructorHeight (),
1107 documentEnvironment ());
1108 doc->setURL (oldURL, false/*not from URL*/);
1109 }
1110
1111
1112 setDocument (doc);
1113
1114 return true;
1115}
1116
1117
1118// private
1119void kpMainWindow::sendDocumentNameToPrinter (QPrinter *printer)
1120{
1121 KUrl url = d->document->url ();
1122 if (!url.isEmpty ())
1123 {
1124 int dot;
1125
1126 QString fileName = url.fileName ();
1127 dot = fileName.lastIndexOf ('.');
1128
1129 // file.ext but not .hidden-file?
1130 if (dot > 0)
1131 fileName.truncate (dot);
1132
1133 #if DEBUG_KP_MAIN_WINDOW
1134 kDebug () << "kpMainWindow::sendDocumentNameToPrinter() fileName="
1135 << fileName
1136 << " dir="
1137 << url.directory ()
1138 << endl;
1139 #endif
1140 printer->setDocName (fileName);
1141 }
1142}
1143
1144
1145// private
1146void kpMainWindow::sendImageToPrinter (QPrinter *printer,
1147 bool showPrinterSetupDialog)
1148{
1149 // Get image to be printed.
1150 kpImage image = d->document->imageWithSelection ();
1151
1152
1153 // Get image DPI.
1154 double imageDotsPerMeterX =
1155 double (d->document->metaInfo ()->dotsPerMeterX ());
1156 double imageDotsPerMeterY =
1157 double (d->document->metaInfo ()->dotsPerMeterY ());
1158#if DEBUG_KP_MAIN_WINDOW
1159 kDebug () << "kpMainWindow::sendImageToPrinter() image:"
1160 << " width=" << image.width ()
1161 << " height=" << image.height ()
1162 << " dotsPerMeterX=" << imageDotsPerMeterX
1163 << " dotsPerMeterY=" << imageDotsPerMeterY
1164 << endl;
1165#endif
1166
1167 // Image DPI invalid (e.g. new image, could not read from file
1168 // or Qt3 doesn't implement DPI for JPEG)?
1169 if (imageDotsPerMeterX <= 0 || imageDotsPerMeterY <= 0)
1170 {
1171 // Even if just one DPI dimension is invalid, mutate both DPI
1172 // dimensions as we have no information about the intended
1173 // aspect ratio anyway (and other dimension likely to be invalid).
1174
1175 // When rendering text onto a document, the fonts are rasterised
1176 // according to the screen's DPI.
1177 // TODO: I think we should use the image's DPI. Technically
1178 // possible?
1179 //
1180 // So no matter what computer you draw text on, you get
1181 // the same pixels.
1182 //
1183 // So we must print at the screen's DPI to get the right text size.
1184 //
1185 // Unfortunately, this means that moving to a different screen DPI
1186 // affects printing. If you edited the image at a different screen
1187 // DPI than when you print, you get incorrect results. Furthermore,
1188 // this is bogus if you don't have text in your image. Worse still,
1189 // what if you have multiple screens connected to the same computer
1190 // with different DPIs?
1191 // TODO: mysteriously, someone else is setting this to 96dpi always.
1192 QPixmap arbitraryScreenElement(1, 1);
1193 const QPaintDevice *screenDevice = &arbitraryScreenElement;
1194 const int dpiX = screenDevice->logicalDpiX (),
1195 dpiY = screenDevice->logicalDpiY ();
1196 #if DEBUG_KP_MAIN_WINDOW
1197 kDebug () << "\tusing screen dpi: x=" << dpiX << " y=" << dpiY;
1198 #endif
1199
1200 imageDotsPerMeterX = dpiX * KP_INCHES_PER_METER;
1201 imageDotsPerMeterY = dpiY * KP_INCHES_PER_METER;
1202 }
1203
1204
1205 // Get page size (excluding margins).
1206 // Coordinate (0,0) is the X here:
1207 // mmmmm
1208 // mX m
1209 // m m m = margin
1210 // m m
1211 // mmmmm
1212 const int printerWidthMM = printer->widthMM ();
1213 const int printerHeightMM = printer->heightMM ();
1214#if DEBUG_KP_MAIN_WINDOW
1215 kDebug () << "\tprinter: widthMM=" << printerWidthMM
1216 << " heightMM=" << printerHeightMM
1217 << endl;
1218#endif
1219
1220
1221 double dpiX = imageDotsPerMeterX / KP_INCHES_PER_METER;
1222 double dpiY = imageDotsPerMeterY / KP_INCHES_PER_METER;
1223#if DEBUG_KP_MAIN_WINDOW
1224 kDebug () << "\timage: dpiX=" << dpiX << " dpiY=" << dpiY;
1225#endif
1226
1227
1228 //
1229 // If image doesn't fit on page at intended DPI, change the DPI.
1230 //
1231
1232 const double scaleDpiX =
1233 (image.width () / (printerWidthMM / KP_MILLIMETERS_PER_INCH))
1234 / dpiX;
1235 const double scaleDpiY =
1236 (image.height () / (printerHeightMM / KP_MILLIMETERS_PER_INCH))
1237 / dpiY;
1238 const double scaleDpi = qMax (scaleDpiX, scaleDpiY);
1239#if DEBUG_KP_MAIN_WINDOW
1240 kDebug () << "\t\tscaleDpi: x=" << scaleDpiX << " y=" << scaleDpiY
1241 << " --> scale at " << scaleDpi << " to fit?"
1242 << endl;
1243#endif
1244
1245 // Need to increase resolution to fit page?
1246 if (scaleDpi > 1.0)
1247 {
1248 dpiX *= scaleDpi;
1249 dpiY *= scaleDpi;
1250 #if DEBUG_KP_MAIN_WINDOW
1251 kDebug () << "\t\t\tto fit page, scaled to:"
1252 << " dpiX=" << dpiX << " dpiY=" << dpiY << endl;
1253 #endif
1254 }
1255
1256
1257 // Make sure DPIs are equal as that's all QPrinter::setResolution()
1258 // supports. We do this in such a way that we only ever stretch an
1259 // image, to avoid losing information. Don't antialias as the printer
1260 // will do that to translate our DPI to its physical resolution and
1261 // double-antialiasing looks bad.
1262 if (dpiX > dpiY)
1263 {
1264 #if DEBUG_KP_MAIN_WINDOW
1265 kDebug () << "\tdpiX > dpiY; stretching image height to equalise DPIs to dpiX="
1266 << dpiX << endl;
1267 #endif
1268 kpPixmapFX::scale (&image,
1269 image.width (),
1270 qMax (1, qRound (image.height () * dpiX / dpiY)),
1271 false/*don't antialias*/);
1272
1273 dpiY = dpiX;
1274 }
1275 else if (dpiY > dpiX)
1276 {
1277 #if DEBUG_KP_MAIN_WINDOW
1278 kDebug () << "\tdpiY > dpiX; stretching image width to equalise DPIs to dpiY="
1279 << dpiY << endl;
1280 #endif
1281 kpPixmapFX::scale (&image,
1282 qMax (1, qRound (image.width () * dpiY / dpiX)),
1283 image.height (),
1284 false/*don't antialias*/);
1285
1286 dpiX = dpiY;
1287 }
1288
1289 Q_ASSERT (dpiX == dpiY);
1290
1291
1292 // QPrinter::setResolution() has to be called before QPrinter::setup().
1293 printer->setResolution (qMax (1, qRound (dpiX)));
1294
1295
1296 sendDocumentNameToPrinter (printer);
1297
1298
1299 if (showPrinterSetupDialog)
1300 {
1301 kpPrintDialogPage *optionsPage = new kpPrintDialogPage (this);
1302 optionsPage->setPrintImageCenteredOnPage (d->configPrintImageCenteredOnPage);
1303
1304 QPrintDialog *printDialog =
1305 KdePrint::createPrintDialog (
1306 printer,
1307 QList <QWidget *> () << optionsPage,
1308 this);
1309 printDialog->setWindowTitle (i18nc ("@title:window", "Print Image"));
1310
1311 // Display dialog.
1312 const bool wantToPrint = printDialog->exec ();
1313
1314 if (optionsPage->printImageCenteredOnPage () !=
1315 d->configPrintImageCenteredOnPage)
1316 {
1317 // Save config option even if the dialog was cancelled.
1318 d->configPrintImageCenteredOnPage = optionsPage->printImageCenteredOnPage ();
1319
1320 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupGeneral);
1321 cfg.writeEntry (kpSettingPrintImageCenteredOnPage,
1322 d->configPrintImageCenteredOnPage);
1323 cfg.sync ();
1324 }
1325
1326 delete printDialog;
1327
1328 if (!wantToPrint)
1329 return;
1330 }
1331
1332
1333 double originX = 0, originY = 0;
1334
1335 // Center image on page?
1336 if (d->configPrintImageCenteredOnPage)
1337 {
1338 originX =
1339 (printerWidthMM * dpiX / KP_MILLIMETERS_PER_INCH - image.width ())
1340 / 2;
1341 originY =
1342 (printerHeightMM * dpiY / KP_MILLIMETERS_PER_INCH - image.height ())
1343 / 2;
1344 }
1345
1346#if DEBUG_KP_MAIN_WINDOW
1347 kDebug () << "\torigin: x=" << originX << " y=" << originY;
1348#endif
1349
1350
1351 // Send image to printer.
1352 QPainter painter;
1353 painter.begin (printer);
1354 painter.drawImage (qRound (originX), qRound (originY), image);
1355 painter.end ();
1356}
1357
1358//---------------------------------------------------------------------
1359
1360// private slot
1361void kpMainWindow::slotPrint ()
1362{
1363 toolEndShape ();
1364
1365 QPrinter printer;
1366
1367 sendImageToPrinter (&printer, true/*showPrinterSetupDialog*/);
1368}
1369
1370//---------------------------------------------------------------------
1371
1372// private slot
1373void kpMainWindow::slotPrintPreview ()
1374{
1375 toolEndShape ();
1376
1377 QPrinter printer;
1378
1379 KPrintPreview printPreview (&printer);
1380
1381 sendImageToPrinter (&printer, false/*don't showPrinterSetupDialog*/);
1382
1383 printPreview.exec ();
1384}
1385
1386//---------------------------------------------------------------------
1387
1388// private slot
1389void kpMainWindow::slotMail ()
1390{
1391 toolEndShape ();
1392
1393 if (d->document->url ().isEmpty ()/*no name*/ ||
1394 !d->document->isFromURL () ||
1395 d->document->isModified ()/*needs to be saved*/)
1396 {
1397 int result = KMessageBox::questionYesNo (this,
1398 i18n ("You must save this image before sending it.\n"
1399 "Do you want to save it?"),
1400 QString(),
1401 KStandardGuiItem::save (), KStandardGuiItem::cancel ());
1402
1403 if (result == KMessageBox::Yes)
1404 {
1405 if (!save ())
1406 {
1407 // save failed or aborted - don't email
1408 return;
1409 }
1410 }
1411 else
1412 {
1413 // don't want to save - don't email
1414 return;
1415 }
1416 }
1417
1418 KToolInvocation::invokeMailer (
1419 QString()/*to*/,
1420 QString()/*cc*/,
1421 QString()/*bcc*/,
1422 d->document->prettyFilename()/*subject*/,
1423 QString()/*body*/,
1424 QString()/*messageFile*/,
1425 QStringList(d->document->url().url())/*attachments*/);
1426}
1427
1428//---------------------------------------------------------------------
1429
1430// private
1431bool kpMainWindow::queryCloseDocument ()
1432{
1433 toolEndShape ();
1434
1435 if (!d->document || !d->document->isModified ())
1436 return true; // ok to close current doc
1437
1438 int result = KMessageBox::warningYesNoCancel (this,
1439 i18n ("The document \"%1\" has been modified.\n"
1440 "Do you want to save it?",
1441 d->document->prettyFilename ()),
1442 QString()/*caption*/,
1443 KStandardGuiItem::save (), KStandardGuiItem::discard ());
1444
1445 switch (result)
1446 {
1447 case KMessageBox::Yes:
1448 return slotSave (); // close only if save succeeds
1449 case KMessageBox::No:
1450 return true; // close without saving
1451 default:
1452 return false; // don't close current doc
1453 }
1454}
1455
1456//---------------------------------------------------------------------
1457
1458// private virtual [base KMainWindow]
1459bool kpMainWindow::queryClose ()
1460{
1461#if DEBUG_KP_MAIN_WINDOW
1462 kDebug () << "kpMainWindow::queryClose()";
1463#endif
1464 toolEndShape ();
1465
1466 if (!queryCloseDocument ())
1467 return false;
1468
1469 if (!queryCloseColors ())
1470 return false;
1471
1472 return true;
1473}
1474
1475//---------------------------------------------------------------------
1476
1477// private slot
1478void kpMainWindow::slotClose ()
1479{
1480 toolEndShape ();
1481
1482 if (!queryCloseDocument ())
1483 return;
1484
1485 setDocument (0);
1486}
1487
1488//---------------------------------------------------------------------
1489
1490// private slot
1491void kpMainWindow::slotQuit ()
1492{
1493 toolEndShape ();
1494
1495 close (); // will call queryClose()
1496}
1497
1498//---------------------------------------------------------------------
1499