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 |
83 | void 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 |
145 | void 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 |
175 | void 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. |
261 | void 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 |
283 | QSize 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 |
311 | void 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 |
326 | bool 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 |
349 | void 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 |
369 | kpDocument *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 |
403 | bool 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 |
429 | KUrl::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 |
459 | void 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 |
476 | void 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 |
503 | void 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 |
597 | void 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 | |
642 | void 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 | |
680 | void 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 |
698 | void 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 |
717 | bool 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 |
746 | bool kpMainWindow::slotSave () |
747 | { |
748 | toolEndShape (); |
749 | |
750 | return save (); |
751 | } |
752 | |
753 | //--------------------------------------------------------------------- |
754 | |
755 | // private |
756 | KUrl 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 |
939 | bool 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 |
976 | bool kpMainWindow::slotSaveAs () |
977 | { |
978 | toolEndShape (); |
979 | |
980 | return saveAs (); |
981 | } |
982 | |
983 | //--------------------------------------------------------------------- |
984 | |
985 | // private slot |
986 | bool 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 |
1032 | void kpMainWindow::slotEnableReload () |
1033 | { |
1034 | d->actionReload->setEnabled (d->document); |
1035 | } |
1036 | |
1037 | //--------------------------------------------------------------------- |
1038 | |
1039 | // private slot |
1040 | bool 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 |
1119 | void 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 |
1146 | void 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 |
1361 | void kpMainWindow::slotPrint () |
1362 | { |
1363 | toolEndShape (); |
1364 | |
1365 | QPrinter printer; |
1366 | |
1367 | sendImageToPrinter (&printer, true/*showPrinterSetupDialog*/); |
1368 | } |
1369 | |
1370 | //--------------------------------------------------------------------- |
1371 | |
1372 | // private slot |
1373 | void 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 |
1389 | void 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 |
1431 | bool 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] |
1459 | bool 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 |
1478 | void kpMainWindow::slotClose () |
1479 | { |
1480 | toolEndShape (); |
1481 | |
1482 | if (!queryCloseDocument ()) |
1483 | return; |
1484 | |
1485 | setDocument (0); |
1486 | } |
1487 | |
1488 | //--------------------------------------------------------------------- |
1489 | |
1490 | // private slot |
1491 | void kpMainWindow::slotQuit () |
1492 | { |
1493 | toolEndShape (); |
1494 | |
1495 | close (); // will call queryClose() |
1496 | } |
1497 | |
1498 | //--------------------------------------------------------------------- |
1499 | |