3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 Copyright (c) 2007 Martin Koller <m.koller@surfeu.at>
5 Copyright (c) 2007 John Layt <john@layt.net>
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
12 1. Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright
15 notice, this list of conditions and the following disclaimer in the
16 documentation and/or other materials provided with the distribution.
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <kpMainWindow.h>
32 #include <kpMainWindowPrivate.h>
34 #include <qdatastream.h>
35 #include <QDBusInterface>
36 #include <QDesktopWidget>
41 #include <QtGui/QPrinter>
42 #include <QtGui/QPrintDialog>
44 #include <kapplication.h>
46 #include <kactioncollection.h>
48 #include <kconfiggroup.h>
50 #include <kfiledialog.h>
52 #include <kiconloader.h>
53 #include <kimagefilepreview.h>
55 #include <kio/netaccess.h>
57 #include <kmessagebox.h>
58 #include <krecentfilesaction.h>
60 #include <kstandardshortcut.h>
61 #include <kstandardaction.h>
62 #include <ktoolinvocation.h>
63 #include <kdeprintdialog.h>
64 #include <kprintpreview.h>
66 #include <kpCommandHistory.h>
68 #include <kpDocument.h>
69 #include <kpDocumentMetaInfoCommand.h>
70 #include <kpDocumentMetaInfoDialog.h>
71 #include <kpDocumentSaveOptionsWidget.h>
72 #include <kpPixmapFX.h>
73 #include <kpPrintDialogPage.h>
75 #include <kpViewManager.h>
79 void kpMainWindow::setupFileMenuActions ()
81 #if DEBUG_KP_MAIN_WINDOW
82 kDebug () << "kpMainWindow::setupFileMenuActions()";
84 KActionCollection
*ac
= actionCollection ();
86 d
->actionNew
= KStandardAction::openNew (this, SLOT (slotNew ()), ac
);
87 d
->actionOpen
= KStandardAction::open (this, SLOT (slotOpen ()), ac
);
89 d
->actionOpenRecent
= KStandardAction::openRecent (this, SLOT (slotOpenRecent (const KUrl
&)), ac
);
90 d
->actionOpenRecent
->loadEntries (KGlobal::config ()->group (kpSettingsGroupRecentFiles
));
91 #if DEBUG_KP_MAIN_WINDOW
92 kDebug () << "\trecent URLs=" << d
->actionOpenRecent
->items ();
95 d
->actionSave
= KStandardAction::save (this, SLOT (slotSave ()), ac
);
96 d
->actionSaveAs
= KStandardAction::saveAs (this, SLOT (slotSaveAs ()), ac
);
98 d
->actionExport
= ac
->addAction("file_export");
99 d
->actionExport
->setText (i18n ("E&xport..."));
100 d
->actionExport
->setIcon (KIcon ("document-export"));
101 connect(d
->actionExport
, SIGNAL(triggered(bool) ), SLOT (slotExport ()));
103 d
->actionScan
= ac
->addAction("file_scan");
104 d
->actionScan
->setText (i18n ("Scan..."));
105 d
->actionScan
->setIcon (SmallIcon ("scanner"));
106 connect(d
->actionScan
, SIGNAL(triggered(bool) ), SLOT (slotScan ()));
108 d
->actionProperties
= ac
->addAction ("file_properties");
109 d
->actionProperties
->setText (i18n ("Properties"));
110 d
->actionProperties
->setIcon (KIcon ("document-properties"));
111 connect (d
->actionProperties
, SIGNAL (triggered (bool)), SLOT (slotProperties ()));
113 //d->actionRevert = KStandardAction::revert (this, SLOT (slotRevert ()), ac);
114 d
->actionReload
= ac
->addAction ("file_revert");
115 d
->actionReload
->setText (i18n ("Reloa&d"));
116 d
->actionReload
->setIcon (KIcon ("view-refresh"));
117 connect(d
->actionReload
, SIGNAL(triggered(bool) ), SLOT (slotReload ()));
118 d
->actionReload
->setShortcuts(KStandardShortcut::reload ());
121 d
->actionPrint
= KStandardAction::print (this, SLOT (slotPrint ()), ac
);
122 d
->actionPrintPreview
= KStandardAction::printPreview (this, SLOT (slotPrintPreview ()), ac
);
124 d
->actionMail
= KStandardAction::mail (this, SLOT (slotMail ()), ac
);
126 d
->actionSetAsWallpaperCentered
= ac
->addAction ("file_set_as_wallpaper_centered");
127 d
->actionSetAsWallpaperCentered
->setText (i18n ("Set as Wa&llpaper (Centered)"));
128 connect(d
->actionSetAsWallpaperCentered
, SIGNAL(triggered(bool) ), SLOT (slotSetAsWallpaperCentered ()));
129 d
->actionSetAsWallpaperTiled
= ac
->addAction ("file_set_as_wallpaper_tiled");
130 d
->actionSetAsWallpaperTiled
->setText (i18n ("Set as Wallpaper (&Tiled)"));
131 connect(d
->actionSetAsWallpaperTiled
, SIGNAL(triggered(bool) ), SLOT (slotSetAsWallpaperTiled ()));
133 d
->actionClose
= KStandardAction::close (this, SLOT (slotClose ()), ac
);
134 d
->actionQuit
= KStandardAction::quit (this, SLOT (slotQuit ()), ac
);
138 enableFileMenuDocumentActions (false);
142 void kpMainWindow::enableFileMenuDocumentActions (bool enable
)
147 // d->actionOpenRecent
149 d
->actionSave
->setEnabled (enable
);
150 d
->actionSaveAs
->setEnabled (enable
);
152 d
->actionExport
->setEnabled (enable
);
156 d
->actionProperties
->setEnabled (enable
);
160 d
->actionPrint
->setEnabled (enable
);
161 d
->actionPrintPreview
->setEnabled (enable
);
163 d
->actionMail
->setEnabled (enable
);
165 d
->actionSetAsWallpaperCentered
->setEnabled (enable
);
166 d
->actionSetAsWallpaperTiled
->setEnabled (enable
);
168 d
->actionClose
->setEnabled (enable
);
169 // d->actionQuit->setEnabled (enable);
173 void kpMainWindow::addRecentURL (const KUrl
&url_
)
175 // HACK: KRecentFilesAction::loadEntries() clears the KRecentFilesAction::d->urls
178 // So afterwards, the URL ref, our method is given, points to an
179 // element in this now-cleared map (see KRecentFilesAction::urlSelected(QAction*)).
180 // Accessing it would result in a crash.
182 // To avoid the crash, make a copy of it before calling
183 // loadEntries() and use this copy, instead of the to-be-dangling
185 const KUrl url
= url_
;
187 #if DEBUG_KP_MAIN_WINDOW
188 kDebug () << "kpMainWindow::addRecentURL(" << url
<< ")";
194 KSharedConfig::Ptr cfg
= KGlobal::config();
196 // KConfig::readEntry() does not actually reread from disk, hence doesn't
197 // realize what other processes have done e.g. Settings / Show Path
198 cfg
->reparseConfiguration ();
200 #if DEBUG_KP_MAIN_WINDOW
201 kDebug () << "\trecent URLs=" << d
->actionOpenRecent
->items ();
203 // HACK: Something might have changed interprocess.
204 // If we could PROPAGATE: interprocess, then this wouldn't be required.
205 d
->actionOpenRecent
->loadEntries (cfg
->group (kpSettingsGroupRecentFiles
));
206 #if DEBUG_KP_MAIN_WINDOW
207 kDebug () << "\tafter loading config=" << d
->actionOpenRecent
->items ();
210 d
->actionOpenRecent
->addUrl (url
);
212 d
->actionOpenRecent
->saveEntries (cfg
->group (kpSettingsGroupRecentFiles
));
215 #if DEBUG_KP_MAIN_WINDOW
216 kDebug () << "\tnew recent URLs=" << d
->actionOpenRecent
->items ();
220 // TODO: PROPAGATE: interprocess
221 // TODO: Is this loop safe since a KMainWindow later along in the list,
222 // could be closed as the code in the body almost certainly re-enters
223 // the event loop? Problem for KDE 3 as well, I think.
224 foreach (KMainWindow
*kmw
, KMainWindow::memberList ())
226 Q_ASSERT (dynamic_cast <kpMainWindow
*> (kmw
));
227 kpMainWindow
*mw
= static_cast <kpMainWindow
*> (kmw
);
229 #if DEBUG_KP_MAIN_WINDOW
230 kDebug () << "\t\tmw=" << mw
;
235 // WARNING: Do not use KRecentFilesAction::setItems()
236 // - it does not work since only its superclass,
237 // KSelectAction, implements setItems() and can't
238 // update KRecentFilesAction's URL list.
240 // Avoid URL memory leak in KRecentFilesAction::loadEntries().
241 mw
->d
->actionOpenRecent
->clear ();
243 mw
->d
->actionOpenRecent
->loadEntries (cfg
->group (kpSettingsGroupRecentFiles
));
244 #if DEBUG_KP_MAIN_WINDOW
245 kDebug () << "\t\t\tcheck recent URLs="
246 << mw
->d
->actionOpenRecent
->items () << endl
;
254 // TODO: Disable action if
255 // (d->configOpenImagesInSameWindow && d->document && d->document->isEmpty())
256 // as it does nothing if this is true.
257 void kpMainWindow::slotNew ()
261 if (d
->document
&& !d
->configOpenImagesInSameWindow
)
263 // A document -- empty or otherwise -- is open.
264 // Force open a new window. In contrast, open() might not open
265 // a new window in this case.
266 kpMainWindow
*win
= new kpMainWindow ();
271 open (KUrl (), true/*create an empty doc*/);
277 QSize
kpMainWindow::defaultDocSize () const
279 // KConfig::readEntry() does not actually reread from disk, hence doesn't
280 // realize what other processes have done e.g. Settings / Show Path
281 KGlobal::config ()->reparseConfiguration ();
283 KConfigGroup
cfg (KGlobal::config (), kpSettingsGroupGeneral
);
285 QSize docSize
= cfg
.readEntry (kpSettingLastDocSize
, QSize ());
287 if (docSize
.isEmpty ())
289 docSize
= QSize (400, 300);
293 // Don't get too big or you'll thrash (or even lock up) the computer
294 // just by opening a window
295 docSize
= QSize (qMin (2048, docSize
.width ()),
296 qMin (2048, docSize
.height ()));
303 void kpMainWindow::saveDefaultDocSize (const QSize
&size
)
305 #if DEBUG_KP_MAIN_WINDOW
306 kDebug () << "\tCONFIG: saving Last Doc Size = " << size
;
309 KConfigGroup
cfg (KGlobal::config (), kpSettingsGroupGeneral
);
311 cfg
.writeEntry (kpSettingLastDocSize
, size
);
317 bool kpMainWindow::shouldOpen ()
319 if (d
->configOpenImagesInSameWindow
)
321 #if DEBUG_KP_MAIN_WINDOW
322 kDebug () << "\topenImagesInSameWindow";
324 // (this brings up a dialog and might save the current doc)
325 if (!queryCloseDocument ())
327 #if DEBUG_KP_MAIN_WINDOW
328 kDebug () << "\t\tqueryCloseDocument() aborts open";
338 void kpMainWindow::setDocumentChoosingWindow (kpDocument
*doc
)
341 if (d
->document
&& !d
->document
->isEmpty () &&
342 !d
->configOpenImagesInSameWindow
)
344 #if DEBUG_KP_MAIN_WINDOW
345 kDebug () << "\topen in new window";
347 // Send doc to new window.
348 kpMainWindow
*win
= new kpMainWindow (doc
);
353 #if DEBUG_KP_MAIN_WINDOW
354 kDebug () << "\topen in same window";
356 // (sets up views, doc signals)
363 kpDocument
*kpMainWindow::openInternal (const KUrl
&url
,
364 const QSize
&fallbackDocSize
,
365 bool newDocSameNameIfNotExist
)
367 // If using OpenImagesInSameWindow mode, ask whether to close the
373 kpDocument
*newDoc
= new kpDocument (fallbackDocSize
.width (),
374 fallbackDocSize
.height (),
375 documentEnvironment ());
376 if (!newDoc
->open (url
, newDocSameNameIfNotExist
))
378 #if DEBUG_KP_MAIN_WINDOW
379 kDebug () << "\topen failed";
385 #if DEBUG_KP_MAIN_WINDOW
386 kDebug () << "\topen OK";
388 // Send document to current or new window.
389 setDocumentChoosingWindow (newDoc
);
395 bool kpMainWindow::open (const KUrl
&url
, bool newDocSameNameIfNotExist
)
397 #if DEBUG_KP_MAIN_WINDOW
398 kDebug () << "kpMainWindow::open(" << url
399 << ",newDocSameNameIfNotExist=" << newDocSameNameIfNotExist
403 kpDocument
*newDoc
= openInternal (url
,
405 newDocSameNameIfNotExist
);
408 if (newDoc
->isFromURL (false/*don't bother checking exists*/))
420 KUrl::List
kpMainWindow::askForOpenURLs (const QString
&caption
, const QString
&startURL
,
421 bool allowMultipleURLs
)
423 QStringList mimeTypes
= KImageIO::mimeTypes (KImageIO::Reading
);
424 #if DEBUG_KP_MAIN_WINDOW
425 QStringList sortedMimeTypes
= mimeTypes
;
426 sortedMimeTypes
.sort ();
427 kDebug () << "kpMainWindow::askForURLs(allowMultiple="
430 << "\tmimeTypes=" << mimeTypes
<< endl
431 << "\tsortedMimeTypes=" << sortedMimeTypes
<< endl
;
433 QString filter
= mimeTypes
.join (" ");
435 KFileDialog
fd (startURL
, filter
, this);
436 fd
.setCaption (caption
);
437 fd
.setOperationMode (KFileDialog::Opening
);
439 if (allowMultipleURLs
)
440 fd
.setMode (KFile::Files
);
442 // COMPAT: Doesn't work. KFileDialog bug.
443 fd
.setPreviewWidget (new KImageFilePreview (&fd
));
446 return fd
.selectedUrls ();
448 return KUrl::List ();
452 void kpMainWindow::slotOpen ()
457 const KUrl::List urls
= askForOpenURLs (i18n ("Open Image"),
458 d
->document
? d
->document
->url ().url () : QString());
460 for (KUrl::List::const_iterator it
= urls
.begin ();
469 void kpMainWindow::slotOpenRecent (const KUrl
&url
)
471 #if DEBUG_KP_MAIN_WINDOW
472 kDebug () << "kpMainWindow::slotOpenRecent(" << url
<< ")";
473 kDebug () << "\titems=" << d
->actionOpenRecent
->items ();
480 // If the open is successful, addRecentURL() would have bubbled up the
481 // URL in the File / Open Recent action. As a side effect, the URL is
484 // If the open fails, we should deselect the URL:
486 // 1. for consistency
488 // 2. because it has not been opened.
490 d
->actionOpenRecent
->setCurrentItem (-1);
495 void kpMainWindow::slotScan ()
497 #if DEBUG_KP_MAIN_WINDOW
498 kDebug () << "kpMainWindow::slotScan() scanDialog=" << d
->scanDialog
;
506 // Create scan dialog by looking for plugin.
507 // [takes about 500ms on 350Mhz]
508 d
->scanDialog
= KScanDialog::getScanDialog (this);
510 // No scanning support (kdegraphics/libkscan) installed?
511 // [Remove $KDEDIR/share/servicetypes/kscan.desktop and
512 // $KDEDIR/share/services/scanservice.desktop to simulate this]
515 #if DEBUG_KP_MAIN_WINDOW
516 kDebug () << "\tcould not create scan dialog";
518 // Instead, we could try to create the scan dialog in the ctor
519 // and just disable the action in the first place, removing
520 // the need for this dialog.
522 // But this increases startup time and is a bit risky e.g. if
523 // the scan support hangs, KolourPaint would not be able to be
526 // Also, disabling the action is bad because the scan support
527 // can be installed while KolourPaint is still running.
528 KMessageBox::sorry (this,
529 i18n ("Scanning support is not installed."),
530 i18n ("No Scanning Support"));
534 #if DEBUG_KP_MAIN_WINDOW
535 kDebug () << "\tcreated scanDialog=" << d
->scanDialog
;
537 connect (d
->scanDialog
, SIGNAL (finalImage (const QImage
&, int)),
538 SLOT (slotScanned (const QImage
&, int)));
542 // If using OpenImagesInSameWindow mode, ask whether to close the
545 // Do this after scan support is detected. Because if it's not, what
546 // would be the point of closing the document?
548 // Ideally, we would do this after the user presses "Final Scan" in
549 // the scan dialog and before the scan begins (if the user wants to
550 // cancel the scan operation, it would be annoying to offer this choice
551 // only after the slow scan is completed) but the KScanDialog API does
552 // not allow this. So we settle for doing this before any
553 // scan dialogs are shown. We don't do this between KScanDialog::setup()
554 // and KScanDialog::exec() as it could be confusing alternating between
555 // scanning and KolourPaint dialogs.
560 #if DEBUG_KP_MAIN_WINDOW
561 kDebug () << "\tcalling setup";
563 // Bring up dialog to select scan device.
564 // If there is no scanner, we find that this does not bring up a dialog
565 // but still returns true.
566 if (d
->scanDialog
->setup ())
568 #if DEBUG_KP_MAIN_WINDOW
569 kDebug () << "\t\tOK - showing dialog";
571 // Called only if scanner configured/available.
573 // In reality, this seems to be called even if you press "Cancel" in
574 // the KScanDialog::setup() dialog!
576 // We use exec() to make sure it's modal. show() seems to work too
577 // but better safe than sorry.
578 d
->scanDialog
->exec ();
582 // Have never seen this code path execute even if "Cancel" is pressed.
583 #if DEBUG_KP_MAIN_WINDOW
584 kDebug () << "\t\tFAIL";
590 void kpMainWindow::slotScanned (const QImage
&image
, int)
592 #if DEBUG_KP_MAIN_WINDOW
593 kDebug () << "kpMainWindow::slotScanned() image.rect=" << image
.rect ();
596 #if DEBUG_KP_MAIN_WINDOW
597 kDebug () << "\thiding dialog";
599 // (KScanDialog does not close itself after a scan is made)
601 // Close the dialog, first thing:
603 // 1. This means that any dialogs we bring up won't be nested on top.
605 // 2. We don't want to return from this method but forget to close
606 // the dialog. So do it before anything else.
607 d
->scanDialog
->hide ();
609 // (just in case there's some drawing between slotScan() exiting and
614 // TODO: Maybe this code should be moved into kpdocument.cpp -
615 // since it resembles the responsibilities of kpDocument::open().
617 // Convert QImage to kpDocument's image format, gathering meta info
619 kpDocumentSaveOptions saveOptions
;
620 kpDocumentMetaInfo metaInfo
;
621 const QPixmap pixmap
= kpDocument::convertToPixmapAsLosslessAsPossible (
623 kpMainWindow::pasteWarnAboutLossInfo (),
627 if (pixmap
.isNull ())
629 #if DEBUG_KP_MAIN_WINDOW
630 kDebug () << "\tcould not convert to pixmap";
632 KMessageBox::sorry (this,
633 i18n ("Cannot scan - out of graphics memory."),
634 i18n ("Cannot Scan"));
639 // Create document from image and meta info.
640 kpDocument
*doc
= new kpDocument (pixmap
.width (), pixmap
.height (),
641 documentEnvironment ());
642 doc
->setImage (pixmap
);
643 doc
->setSaveOptions (saveOptions
);
644 doc
->setMetaInfo (metaInfo
);
647 // Send document to current or new window.
648 setDocumentChoosingWindow (doc
);
653 void kpMainWindow::slotProperties ()
657 kpDocumentMetaInfoDialog
dialog (document ()->metaInfo (), this);
659 if (dialog
.exec () && !dialog
.isNoOp ())
661 commandHistory ()->addCommand (
662 new kpDocumentMetaInfoCommand (
663 i18n ("Document Properties"),
664 dialog
.metaInfo ()/*new*/, *document ()->metaInfo ()/*old*/,
665 commandEnvironment ()));
671 bool kpMainWindow::save (bool localOnly
)
673 if (d
->document
->url ().isEmpty () ||
674 !KImageIO::mimeTypes (KImageIO::Writing
)
675 .contains (d
->document
->saveOptions ()->mimeType ()) ||
676 // SYNC: kpDocument::getPixmapFromFile() can't determine quality
677 // from file so it has been set initially to an invalid value.
678 (d
->document
->saveOptions ()->mimeTypeHasConfigurableQuality () &&
679 d
->document
->saveOptions ()->qualityIsInvalid ()) ||
680 (localOnly
&& !d
->document
->url ().isLocalFile ()))
682 return saveAs (localOnly
);
686 if (d
->document
->save (false/*no overwrite prompt*/,
687 !d
->document
->savedAtLeastOnceBefore ()/*lossy prompt*/))
689 addRecentURL (d
->document
->url ());
698 bool kpMainWindow::slotSave ()
706 KUrl
kpMainWindow::askForSaveURL (const QString
&caption
,
707 const QString
&startURL
,
708 const kpImage
&imageToBeSaved
,
709 const kpDocumentSaveOptions
&startSaveOptions
,
710 const kpDocumentMetaInfo
&docMetaInfo
,
711 const QString
&forcedSaveOptionsGroup
,
713 kpDocumentSaveOptions
*chosenSaveOptions
,
714 bool isSavingForFirstTime
,
715 bool *allowOverwritePrompt
,
716 bool *allowLossyPrompt
)
718 #if DEBUG_KP_MAIN_WINDOW
719 kDebug () << "kpMainWindow::askForURL() startURL=" << startURL
;
720 startSaveOptions
.printDebug ("\tstartSaveOptions");
723 bool reparsedConfiguration
= false;
725 // KConfig::readEntry() does not actually reread from disk, hence doesn't
726 // realize what other processes have done e.g. Settings / Show Path
727 // so reparseConfiguration() must be called
728 #define SETUP_READ_CFG() \
729 if (!reparsedConfiguration) \
731 KGlobal::config ()->reparseConfiguration (); \
732 reparsedConfiguration = true; \
735 KConfigGroup cfg (KGlobal::config (), forcedSaveOptionsGroup); \
738 if (chosenSaveOptions)
739 *chosenSaveOptions
= kpDocumentSaveOptions ();
741 if (allowOverwritePrompt
)
742 *allowOverwritePrompt
= true; // play it safe for now
744 if (allowLossyPrompt
)
745 *allowLossyPrompt
= true; // play it safe for now
748 kpDocumentSaveOptions fdSaveOptions
= startSaveOptions
;
750 QStringList mimeTypes
= KImageIO::mimeTypes (KImageIO::Writing
);
751 #if DEBUG_KP_MAIN_WINDOW
752 QStringList sortedMimeTypes
= mimeTypes
;
753 sortedMimeTypes
.sort ();
754 kDebug () << "\tmimeTypes=" << mimeTypes
755 << "\tsortedMimeTypes=" << sortedMimeTypes
<< endl
;
757 if (mimeTypes
.isEmpty ())
759 kError () << "No KImageIO output mimetypes!" << endl
;
763 #define MIME_TYPE_IS_VALID() (!fdSaveOptions.mimeTypeIsInvalid () && \
764 mimeTypes.contains (fdSaveOptions.mimeType ()))
765 if (!MIME_TYPE_IS_VALID ())
767 #if DEBUG_KP_MAIN_WINDOW
768 kDebug () << "\tmimeType=" << fdSaveOptions
.mimeType ()
769 << " not valid, get default" << endl
;
774 fdSaveOptions
.setMimeType (kpDocumentSaveOptions::defaultMimeType (cfg
));
777 if (!MIME_TYPE_IS_VALID ())
779 #if DEBUG_KP_MAIN_WINDOW
780 kDebug () << "\tmimeType=" << fdSaveOptions
.mimeType ()
781 << " not valid, get hardcoded" << endl
;
783 if (mimeTypes
.contains ("image/png"))
784 fdSaveOptions
.setMimeType ("image/png");
785 else if (mimeTypes
.contains ("image/bmp"))
786 fdSaveOptions
.setMimeType ("image/bmp");
788 fdSaveOptions
.setMimeType (mimeTypes
.first ());
791 #undef MIME_TYPE_IN_LIST
793 if (fdSaveOptions
.colorDepthIsInvalid ())
797 fdSaveOptions
.setColorDepth (kpDocumentSaveOptions::defaultColorDepth (cfg
));
798 fdSaveOptions
.setDither (kpDocumentSaveOptions::defaultDither (cfg
));
801 if (fdSaveOptions
.qualityIsInvalid ())
805 fdSaveOptions
.setQuality (kpDocumentSaveOptions::defaultQuality (cfg
));
807 #if DEBUG_KP_MAIN_WINDOW
808 fdSaveOptions
.printDebug ("\tcorrected saveOptions passed to fileDialog");
811 kpDocumentSaveOptionsWidget
*saveOptionsWidget
=
812 new kpDocumentSaveOptionsWidget (imageToBeSaved
,
817 KFileDialog
fd (startURL
, QString(), this,
819 saveOptionsWidget
->setVisualParent (&fd
);
820 fd
.setCaption (caption
);
821 fd
.setOperationMode (KFileDialog::Saving
);
822 #if DEBUG_KP_MAIN_WINDOW
823 kDebug () << "\tmimeTypes=" << mimeTypes
;
825 fd
.setMimeFilter (mimeTypes
, fdSaveOptions
.mimeType ());
827 fd
.setMode (KFile::File
| KFile::LocalOnly
);
829 connect (&fd
, SIGNAL (filterChanged (const QString
&)),
830 saveOptionsWidget
, SLOT (setMimeType (const QString
&)));
835 kpDocumentSaveOptions newSaveOptions
= saveOptionsWidget
->documentSaveOptions ();
836 #if DEBUG_KP_MAIN_WINDOW
837 newSaveOptions
.printDebug ("\tnewSaveOptions");
840 KConfigGroup
cfg (KGlobal::config (), forcedSaveOptionsGroup
);
842 // Save options user forced - probably want to use them in future
843 kpDocumentSaveOptions::saveDefaultDifferences (cfg
,
844 fdSaveOptions
, newSaveOptions
);
848 if (chosenSaveOptions
)
849 *chosenSaveOptions
= newSaveOptions
;
852 bool shouldAllowOverwritePrompt
=
853 (fd
.selectedUrl () != startURL
||
854 newSaveOptions
.mimeType () != startSaveOptions
.mimeType ());
855 if (allowOverwritePrompt
)
857 *allowOverwritePrompt
= shouldAllowOverwritePrompt
;
858 #if DEBUG_KP_MAIN_WINDOW
859 kDebug () << "\tallowOverwritePrompt=" << *allowOverwritePrompt
;
863 if (allowLossyPrompt
)
865 // SYNC: kpDocumentSaveOptions elements - everything except quality
866 // (one quality setting is "just as lossy" as another so no
867 // need to continually warn due to quality change)
869 (isSavingForFirstTime
||
870 shouldAllowOverwritePrompt
||
871 newSaveOptions
.mimeType () != startSaveOptions
.mimeType () ||
872 newSaveOptions
.colorDepth () != startSaveOptions
.colorDepth () ||
873 newSaveOptions
.dither () != startSaveOptions
.dither ());
874 #if DEBUG_KP_MAIN_WINDOW
875 kDebug () << "\tallowLossyPrompt=" << *allowLossyPrompt
;
880 #if DEBUG_KP_MAIN_WINDOW
881 kDebug () << "\tselectedUrl=" << fd
.selectedUrl ();
883 return fd
.selectedUrl ();
887 #undef SETUP_READ_CFG
892 bool kpMainWindow::saveAs (bool localOnly
)
894 #if DEBUG_KP_MAIN_WINDOW
895 kDebug () << "kpMainWindow::saveAs URL=" << d
->document
->url ();
898 kpDocumentSaveOptions chosenSaveOptions
;
899 bool allowOverwritePrompt
, allowLossyPrompt
;
900 KUrl chosenURL
= askForSaveURL (i18n ("Save Image As"),
901 d
->document
->url ().url (),
902 d
->document
->imageWithSelection (),
903 *d
->document
->saveOptions (),
904 *d
->document
->metaInfo (),
905 kpSettingsGroupFileSaveAs
,
908 !d
->document
->savedAtLeastOnceBefore (),
909 &allowOverwritePrompt
,
913 if (chosenURL
.isEmpty ())
917 if (!d
->document
->saveAs (chosenURL
, chosenSaveOptions
,
918 allowOverwritePrompt
,
925 addRecentURL (chosenURL
);
931 bool kpMainWindow::slotSaveAs ()
939 bool kpMainWindow::slotExport ()
941 #if DEBUG_KP_MAIN_WINDOW
942 kDebug () << "kpMainWindow::slotExport()";
948 kpDocumentSaveOptions chosenSaveOptions
;
949 bool allowOverwritePrompt
, allowLossyPrompt
;
950 KUrl chosenURL
= askForSaveURL (i18n ("Export"),
951 d
->lastExportURL
.url (),
952 d
->document
->imageWithSelection (),
953 d
->lastExportSaveOptions
,
954 *d
->document
->metaInfo (),
955 kpSettingsGroupFileExport
,
956 false/*allow remote files*/,
959 &allowOverwritePrompt
,
963 if (chosenURL
.isEmpty ())
967 if (!kpDocument::savePixmapToFile (d
->document
->imageWithSelection (),
969 chosenSaveOptions
, *d
->document
->metaInfo (),
970 allowOverwritePrompt
,
978 addRecentURL (chosenURL
);
981 d
->lastExportURL
= chosenURL
;
982 d
->lastExportSaveOptions
= chosenSaveOptions
;
984 d
->exportFirstTime
= false;
991 void kpMainWindow::slotEnableReload ()
993 d
->actionReload
->setEnabled (d
->document
);
997 bool kpMainWindow::slotReload ()
1001 Q_ASSERT (d
->document
);
1004 KUrl oldURL
= d
->document
->url ();
1007 if (d
->document
->isModified ())
1009 int result
= KMessageBox::Cancel
;
1011 if (d
->document
->isFromURL (false/*don't bother checking exists*/) && !oldURL
.isEmpty ())
1013 result
= KMessageBox::warningContinueCancel (this,
1014 i18n ("The document \"%1\" has been modified.\n"
1015 "Reloading will lose all changes since you last saved it.\n"
1017 d
->document
->prettyFilename ()),
1018 QString()/*caption*/,
1019 KGuiItem(i18n ("&Reload")));
1023 result
= KMessageBox::warningContinueCancel (this,
1024 i18n ("The document \"%1\" has been modified.\n"
1025 "Reloading will lose all changes.\n"
1027 d
->document
->prettyFilename ()),
1028 QString()/*caption*/,
1029 KGuiItem(i18n ("&Reload")));
1032 if (result
!= KMessageBox::Continue
)
1037 kpDocument
*doc
= 0;
1039 // If it's _supposed to_ come from a URL or it exists
1040 if (d
->document
->isFromURL (false/*don't bother checking exists*/) ||
1041 (!oldURL
.isEmpty () && KIO::NetAccess::exists (oldURL
, KIO::NetAccess::SourceSide
/*open*/, this)))
1043 #if DEBUG_KP_MAIN_WINDOW
1044 kDebug () << "kpMainWindow::slotReload() reloading from disk!";
1047 doc
= new kpDocument (1, 1, documentEnvironment ());
1048 if (!doc
->open (oldURL
))
1050 delete doc
; doc
= 0;
1054 addRecentURL (oldURL
);
1058 #if DEBUG_KP_MAIN_WINDOW
1059 kDebug () << "kpMainWindow::slotReload() create doc";
1062 doc
= new kpDocument (d
->document
->constructorWidth (),
1063 d
->document
->constructorHeight (),
1064 documentEnvironment ());
1065 doc
->setURL (oldURL
, false/*not from URL*/);
1076 void kpMainWindow::sendDocumentNameToPrinter (QPrinter
*printer
)
1078 KUrl url
= d
->document
->url ();
1079 if (!url
.isEmpty ())
1083 QString fileName
= url
.fileName ();
1084 dot
= fileName
.lastIndexOf ('.');
1086 // file.ext but not .hidden-file?
1088 fileName
.truncate (dot
);
1090 #if DEBUG_KP_MAIN_WINDOW
1091 kDebug () << "kpMainWindow::sendDocumentNameToPrinter() fileName="
1097 printer
->setDocName (fileName
);
1103 void kpMainWindow::sendImageToPrinter (QPrinter
*printer
,
1104 bool showPrinterSetupDialog
)
1106 // Get image to be printed.
1107 kpImage image
= d
->document
->imageWithSelection ();
1111 double imageDotsPerMeterX
=
1112 double (d
->document
->metaInfo ()->dotsPerMeterX ());
1113 double imageDotsPerMeterY
=
1114 double (d
->document
->metaInfo ()->dotsPerMeterY ());
1115 #if DEBUG_KP_MAIN_WINDOW
1116 kDebug () << "kpMainWindow::sendImageToPrinter() image:"
1117 << " width=" << image
.width ()
1118 << " height=" << image
.height ()
1119 << " dotsPerMeterX=" << imageDotsPerMeterX
1120 << " dotsPerMeterY=" << imageDotsPerMeterY
1124 // Image DPI invalid (e.g. new image, could not read from file
1125 // or Qt3 doesn't implement DPI for JPEG)?
1126 if (imageDotsPerMeterX
<= 0 || imageDotsPerMeterY
<= 0)
1128 // Even if just one DPI dimension is invalid, mutate both DPI
1129 // dimensions as we have no information about the intended
1130 // aspect ratio anyway (and other dimension likely to be invalid).
1132 // When rendering text onto a document, the fonts are rasterised
1133 // according to the screen's DPI.
1134 // TODO: I think we should use the image's DPI. Technically
1137 // So no matter what computer you draw text on, you get
1140 // So we must print at the screen's DPI to get the right text size.
1142 // Unfortunately, this means that moving to a different screen DPI
1143 // affects printing. If you edited the image at a different screen
1144 // DPI than when you print, you get incorrect results. Furthermore,
1145 // this is bogus if you don't have text in your image. Worse still,
1146 // what if you have multiple screens connected to the same computer
1147 // with different DPIs?
1148 // TODO: mysteriously, someone else is setting this to 96dpi always.
1149 QPixmap arbitraryScreenElement
;
1150 const QPaintDevice
*screenDevice
= &arbitraryScreenElement
;
1151 const int dpiX
= screenDevice
->logicalDpiX (),
1152 dpiY
= screenDevice
->logicalDpiY ();
1153 #if DEBUG_KP_MAIN_WINDOW
1154 kDebug () << "\tusing screen dpi: x=" << dpiX
<< " y=" << dpiY
;
1157 imageDotsPerMeterX
= dpiX
* KP_INCHES_PER_METER
;
1158 imageDotsPerMeterY
= dpiY
* KP_INCHES_PER_METER
;
1162 // Get page size (excluding margins).
1163 // Coordinate (0,0) is the X here:
1169 const int printerWidthMM
= printer
->widthMM ();
1170 const int printerHeightMM
= printer
->heightMM ();
1171 #if DEBUG_KP_MAIN_WINDOW
1172 kDebug () << "\tprinter: widthMM=" << printerWidthMM
1173 << " heightMM=" << printerHeightMM
1178 double dpiX
= imageDotsPerMeterX
/ KP_INCHES_PER_METER
;
1179 double dpiY
= imageDotsPerMeterY
/ KP_INCHES_PER_METER
;
1180 #if DEBUG_KP_MAIN_WINDOW
1181 kDebug () << "\timage: dpiX=" << dpiX
<< " dpiY=" << dpiY
;
1186 // If image doesn't fit on page at intended DPI, change the DPI.
1189 const double scaleDpiX
=
1190 (image
.width () / (printerWidthMM
/ KP_MILLIMETERS_PER_INCH
))
1192 const double scaleDpiY
=
1193 (image
.height () / (printerHeightMM
/ KP_MILLIMETERS_PER_INCH
))
1195 const double scaleDpi
= qMax (scaleDpiX
, scaleDpiY
);
1196 #if DEBUG_KP_MAIN_WINDOW
1197 kDebug () << "\t\tscaleDpi: x=" << scaleDpiX
<< " y=" << scaleDpiY
1198 << " --> scale at " << scaleDpi
<< " to fit?"
1202 // Need to increase resolution to fit page?
1207 #if DEBUG_KP_MAIN_WINDOW
1208 kDebug () << "\t\t\tto fit page, scaled to:"
1209 << " dpiX=" << dpiX
<< " dpiY=" << dpiY
<< endl
;
1214 // Make sure DPIs are equal as that's all QPrinter::setResolution()
1215 // supports. We do this in such a way that we only ever stretch an
1216 // image, to avoid losing information. Don't antialias as the printer
1217 // will do that to translate our DPI to its physical resolution and
1218 // double-antialiasing looks bad.
1221 #if DEBUG_KP_MAIN_WINDOW
1222 kDebug () << "\tdpiX > dpiY; stretching image height to equalise DPIs to dpiX="
1225 kpPixmapFX::scale (&image
,
1227 qMax (1, qRound (image
.height () * dpiX
/ dpiY
)),
1228 false/*don't antialias*/);
1232 else if (dpiY
> dpiX
)
1234 #if DEBUG_KP_MAIN_WINDOW
1235 kDebug () << "\tdpiY > dpiX; stretching image width to equalise DPIs to dpiY="
1238 kpPixmapFX::scale (&image
,
1239 qMax (1, qRound (image
.width () * dpiY
/ dpiX
)),
1241 false/*don't antialias*/);
1246 Q_ASSERT (dpiX
== dpiY
);
1249 // QPrinter::setResolution() has to be called before QPrinter::setup().
1250 printer
->setResolution (qMax (1, qRound (dpiX
)));
1253 sendDocumentNameToPrinter (printer
);
1256 if (showPrinterSetupDialog
)
1258 kpPrintDialogPage
*optionsPage
= new kpPrintDialogPage (this);
1259 optionsPage
->setPrintImageCenteredOnPage (d
->configPrintImageCenteredOnPage
);
1261 QPrintDialog
*printDialog
=
1262 KdePrint::createPrintDialog (
1264 QList
<QWidget
*> () << optionsPage
,
1266 printDialog
->setWindowTitle (i18n ("Print Image"));
1269 const bool wantToPrint
= printDialog
->exec ();
1271 if (optionsPage
->printImageCenteredOnPage () !=
1272 d
->configPrintImageCenteredOnPage
)
1274 // Save config option even if the dialog was cancelled.
1275 d
->configPrintImageCenteredOnPage
= optionsPage
->printImageCenteredOnPage ();
1277 KConfigGroup
cfg (KGlobal::config (), kpSettingsGroupGeneral
);
1278 cfg
.writeEntry (kpSettingPrintImageCenteredOnPage
,
1279 d
->configPrintImageCenteredOnPage
);
1290 double originX
= 0, originY
= 0;
1292 // Center image on page?
1293 if (d
->configPrintImageCenteredOnPage
)
1296 (printerWidthMM
* dpiX
/ KP_MILLIMETERS_PER_INCH
- image
.width ())
1299 (printerHeightMM
* dpiY
/ KP_MILLIMETERS_PER_INCH
- image
.height ())
1303 #if DEBUG_KP_MAIN_WINDOW
1304 kDebug () << "\torigin: x=" << originX
<< " y=" << originY
;
1308 // Send image to printer.
1310 painter
.begin (printer
);
1311 painter
.drawPixmap (qRound (originX
), qRound (originY
), image
);
1317 void kpMainWindow::slotPrint ()
1323 sendImageToPrinter (&printer
, true/*showPrinterSetupDialog*/);
1327 void kpMainWindow::slotPrintPreview ()
1333 KPrintPreview
printPreview (&printer
);
1335 sendImageToPrinter (&printer
, false/*don't showPrinterSetupDialog*/);
1337 printPreview
.exec ();
1342 void kpMainWindow::slotMail ()
1346 if (d
->document
->url ().isEmpty ()/*no name*/ ||
1347 !d
->document
->isFromURL () ||
1348 d
->document
->isModified ()/*needs to be saved*/)
1350 int result
= KMessageBox::questionYesNo (this,
1351 i18n ("You must save this image before sending it.\n"
1352 "Do you want to save it?"),
1354 KStandardGuiItem::save (), KStandardGuiItem::cancel ());
1356 if (result
== KMessageBox::Yes
)
1360 // save failed or aborted - don't email
1366 // don't want to save - don't email
1371 KToolInvocation::invokeMailer (
1372 QString::null
/*to*/, //krazy:exclude=nullstrassign for old broken gcc
1375 d
->document
->prettyFilename()/*subject*/,
1377 QString()/*messageFile*/,
1378 QStringList (d
->document
->url ().url ())/*attachments*/);
1383 void kpMainWindow::setAsWallpaper (bool centered
)
1385 if (d
->document
->url ().isEmpty ()/*no name*/ ||
1386 !d
->document
->url ().isLocalFile ()/*remote file*/ ||
1387 !d
->document
->isFromURL () ||
1388 d
->document
->isModified ()/*needs to be saved*/)
1392 if (!d
->document
->url ().isLocalFile ())
1394 question
= i18n ("Before this image can be set as the wallpaper, "
1395 "you must save it as a local file.\n"
1396 "Do you want to save it?");
1400 question
= i18n ("Before this image can be set as the wallpaper, "
1401 "you must save it.\n"
1402 "Do you want to save it?");
1405 int result
= KMessageBox::questionYesNo (this,
1406 question
, QString(),
1407 KStandardGuiItem::save (), KStandardGuiItem::cancel ());
1409 if (result
== KMessageBox::Yes
)
1411 // save() is smart enough to pop up a filedialog if it's a
1412 // remote file that should be saved locally
1413 if (!save (true/*localOnly*/))
1415 // save failed or aborted - don't set the wallpaper
1421 // don't want to save - don't set wallpaper
1428 QDataStream
dataStream (&data
, QIODevice::WriteOnly
);
1431 #if DEBUG_KP_MAIN_WINDOW
1432 kDebug () << "kpMainWindow::setAsWallpaper() path="
1433 << d
->document
->url ().path () << endl
;
1435 dataStream
<< QString (d
->document
->url ().path ());
1439 // SYNC: kdebase/kcontrol/background/bgsettings.h:
1443 // 9 = lastWallpaperMode
1445 // Why restrict the user to Centered & Tiled?
1446 // Why don't we let the user choose if it should be common to all desktops?
1447 // Why don't we rewrite the Background control page?
1449 // Answer: This is supposed to be a quick & convenient feature.
1451 // If you want more options, go to kcontrol for that kind of
1452 // flexiblity. We don't want to slow down average users, who see way too
1453 // many dialogs already and probably haven't even heard of "Centered Maxpect"...
1455 dataStream
<< int (centered
? 1 : 2);
1458 // I'm going to all this trouble because the user might not have kdebase
1459 // installed so kdebase/kdesktop/KBackgroundIface.h might not be around
1460 // to be compiled in (where user == developer :))
1464 int konq_screen_number
= KApplication::desktop()->primaryScreen();
1466 if (konq_screen_number
== 0)
1467 appname
= "org.kde.kdesktop";
1469 appname
= "org.kde.kdesktop-screen-" + QByteArray::number(konq_screen_number
);
1470 QDBusInterface
kdesktop(appname
, "/Background", "org.kde.kdesktop.Background");
1471 QDBusReply
<void> retVal
= kdesktop
.call( "setWallpaper", QString (d
->document
->url ().path ()), int (centered
? 1 : 2) );
1472 if (!retVal
.isValid())
1474 KMessageBox::sorry (this, i18n ("Could not change wallpaper."));
1478 #warning "Setting wallpaper not implemented in non-X11"
1484 void kpMainWindow::slotSetAsWallpaperCentered ()
1488 setAsWallpaper (true/*centered*/);
1492 void kpMainWindow::slotSetAsWallpaperTiled ()
1496 setAsWallpaper (false/*tiled*/);
1501 bool kpMainWindow::queryCloseDocument ()
1505 if (!d
->document
|| !d
->document
->isModified ())
1506 return true; // ok to close current doc
1508 int result
= KMessageBox::warningYesNoCancel (this,
1509 i18n ("The document \"%1\" has been modified.\n"
1510 "Do you want to save it?",
1511 d
->document
->prettyFilename ()),
1512 QString()/*caption*/,
1513 KStandardGuiItem::save (), KStandardGuiItem::discard ());
1517 case KMessageBox::Yes
:
1518 return slotSave (); // close only if save succeeds
1519 case KMessageBox::No
:
1520 return true; // close without saving
1522 return false; // don't close current doc
1526 // private virtual [base KMainWindow]
1527 bool kpMainWindow::queryClose ()
1529 #if DEBUG_KP_MAIN_WINDOW
1530 kDebug () << "kpMainWindow::queryClose()";
1534 if (!queryCloseDocument ())
1537 if (!queryCloseColors ())
1544 void kpMainWindow::slotClose ()
1548 #if DEBUG_KP_MAIN_WINDOW
1549 kDebug () << "kpMainWindow::slotClose()";
1552 if (!queryCloseDocument ())
1559 void kpMainWindow::slotQuit ()
1563 #if DEBUG_KP_MAIN_WINDOW
1564 kDebug () << "kpMainWindow::slotQuit()";
1567 close (); // will call queryClose()