compile
[kdegraphics.git] / kolourpaint / mainWindow / kpMainWindow.cpp
blob48a4458c23bd62dd0c50d602172fda02ed5781ce
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <kpMainWindow.h>
30 #include <kpMainWindowPrivate.h>
32 #include <qevent.h>
33 #include <qpainter.h>
34 #include <qtimer.h>
36 #include <kapplication.h>
37 #include <kconfig.h>
38 #include <kconfiggroup.h>
39 #include <kdebug.h>
40 #include <kglobal.h>
41 #include <klocale.h>
42 #include <KMenu>
43 #include <KMenuBar>
44 #include <kmessagebox.h>
45 #include <krecentfilesaction.h>
47 #include <kpAbstractImageSelection.h>
48 #include <kpCommandEnvironment.h>
49 #include <kpColorCells.h>
50 #include <kpColorToolBar.h>
51 #include <kpCommandHistory.h>
52 #include <kpDefs.h>
53 #include <kpDocument.h>
54 #include <kpDocumentEnvironment.h>
55 #include <kpSelectionDrag.h>
56 #include <kpThumbnail.h>
57 #include <kpTool.h>
58 #include <kpToolToolBar.h>
59 #include <kpViewManager.h>
60 #include <kpViewScrollableContainer.h>
61 #include <kpWidgetMapper.h>
62 #include <kpZoomedThumbnailView.h>
63 #include <kpZoomedView.h>
65 #if DEBUG_KP_MAIN_WINDOW
66 #include <qdatetime.h>
67 #endif
70 kpMainWindow::kpMainWindow ()
71 : KXmlGuiWindow (0/*parent*/)
73 init ();
74 open (KUrl (), true/*create an empty doc*/);
76 d->isFullyConstructed = true;
79 kpMainWindow::kpMainWindow (const KUrl &url)
80 : KXmlGuiWindow (0/*parent*/)
82 init ();
83 open (url, true/*create an empty doc with the same url if url !exist*/);
85 d->isFullyConstructed = true;
88 kpMainWindow::kpMainWindow (kpDocument *newDoc)
89 : KXmlGuiWindow (0/*parent*/)
91 init ();
92 setDocument (newDoc);
94 d->isFullyConstructed = true;
98 // TODO: Move into appropriate kpMainWindow_*.cpp or another class
100 // private
101 void kpMainWindow::readGeneralSettings ()
103 #if DEBUG_KP_MAIN_WINDOW
104 kDebug () << "\tkpMainWindow(" << objectName () << ")::readGeneralSettings()";
105 #endif
107 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupGeneral);
109 d->configFirstTime = cfg.readEntry (kpSettingFirstTime, true);
110 d->configShowGrid = cfg.readEntry (kpSettingShowGrid, false);
111 d->configShowPath = cfg.readEntry (kpSettingShowPath, false);
112 d->moreEffectsDialogLastEffect = cfg.readEntry (kpSettingMoreEffectsLastEffect, 0);
113 d->resizeScaleDialogLastKeepAspect = cfg.readEntry (kpSettingResizeScaleLastKeepAspect, false);
115 if (cfg.hasKey (kpSettingOpenImagesInSameWindow))
117 d->configOpenImagesInSameWindow = cfg.readEntry (kpSettingOpenImagesInSameWindow, false);
119 else
121 d->configOpenImagesInSameWindow = false;
122 #if DEBUG_KP_MAIN_WINDOW
123 kDebug () << "\tconfigOpenImagesInSameWindow: first time"
124 << " - writing default: " << d->configOpenImagesInSameWindow
125 << endl;
126 #endif
127 // TODO: More hidden options have to write themselves out on startup,
128 // not on use, to be discoverable (e.g. printing centered on page).
129 cfg.writeEntry (kpSettingOpenImagesInSameWindow,
130 d->configOpenImagesInSameWindow);
131 cfg.sync ();
134 d->configPrintImageCenteredOnPage = cfg.readEntry (kpSettingPrintImageCenteredOnPage, true);
137 #if DEBUG_KP_MAIN_WINDOW
138 kDebug () << "\t\tGeneral Settings: firstTime=" << d->configFirstTime
139 << " showGrid=" << d->configShowGrid
140 << " showPath=" << d->configShowPath
141 << " moreEffectsDialogLastEffect=" << d->moreEffectsDialogLastEffect
142 << " resizeScaleDialogLastKeepAspect=" << d->resizeScaleDialogLastKeepAspect
143 << " openImagesInSameWindow=" << d->configOpenImagesInSameWindow
144 << " printImageCenteredOnPage=" << d->configPrintImageCenteredOnPage;
145 #endif
148 // private
149 void kpMainWindow::readThumbnailSettings ()
151 #if DEBUG_KP_MAIN_WINDOW
152 kDebug () << "\tkpMainWindow(" << objectName () << ")::readThumbnailSettings()";
153 #endif
155 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupThumbnail);
157 d->configThumbnailShown = cfg.readEntry (kpSettingThumbnailShown, false);
158 d->configThumbnailGeometry = cfg.readEntry (kpSettingThumbnailGeometry, QRect ());
159 d->configZoomedThumbnail = cfg.readEntry (kpSettingThumbnailZoomed, true);
160 d->configThumbnailShowRectangle = cfg.readEntry (kpSettingThumbnailShowRectangle, true);
162 #if DEBUG_KP_MAIN_WINDOW
163 kDebug () << "\t\tThumbnail Settings: shown=" << d->configThumbnailShown
164 << " geometry=" << d->configThumbnailGeometry
165 << " zoomed=" << d->configZoomedThumbnail
166 << " showRectangle=" << d->configThumbnailShowRectangle
167 << endl;
168 #endif
171 // private [override KXmlGuiWindow]
172 void kpMainWindow::createGUI ()
174 KXmlGuiWindow::createGUI ();
176 // HACK: Until we have a proper kpToolToolBar where tool actions are
177 // plugged into it properly, Qt4 will not recognise our actions'
178 // shortcuts.
180 // In kolourpaintui.rc, we get around this by plugging them into
181 // a fake menu. Here, we hide that fake menu.
182 const QList<KMenu *> menuToHide = findChildren<KMenu *>("toolToolBarHiddenMenu");
183 // should only contain one but...
184 foreach (KMenu *menu, menuToHide)
186 menu->menuAction()->setVisible(false);
190 // private
191 void kpMainWindow::init ()
193 #if DEBUG_KP_MAIN_WINDOW
194 kDebug () << "kpMainWindow(" << objectName () << ")::init()";
195 QTime totalTime; totalTime.start ();
196 QTime time; time.start ();
197 #endif
199 d = new kpMainWindowPrivate;
202 // Set fields that must be set early on.
204 // For all other fields that don't need to be set as urgently, please
205 // set them in their respective kpMainWindow_*.cpp.
208 d->isFullyConstructed = false;
210 d->scrollView = 0;
211 d->mainView = 0;
212 d->thumbnail = 0;
213 d->thumbnailView = 0;
214 d->document = 0;
215 d->viewManager = 0;
216 d->colorToolBar = 0;
217 d->toolToolBar = 0;
218 d->commandHistory = 0;
219 d->statusBarCreated = false;
220 d->settingImageSelectionTransparency = 0;
221 d->settingTextStyle = 0;
223 d->docResizeToBeCompleted = false;
225 d->documentEnvironment = 0;
226 d->commandEnvironment = 0;
227 d->toolSelectionEnvironment = 0;
228 d->toolsActionGroup = 0;
229 d->transformDialogEnvironment = 0;
233 // set mainwindow properties
236 setMinimumSize (320, 260);
237 setAcceptDrops (true);
238 #if DEBUG_KP_MAIN_WINDOW
239 kDebug () << "\tTIME: little init = " << time.restart () << "msec";
240 #endif
244 // read config
247 // KConfig::readEntry() does not actually reread from disk, hence doesn't
248 // realize what other processes have done e.g. Settings / Show Path
249 KGlobal::config ()->reparseConfiguration ();
250 #if DEBUG_KP_MAIN_WINDOW
251 kDebug () << "\tTIME: reparseConfig = " << time.restart () << "msec";
252 #endif
254 readGeneralSettings ();
255 #if DEBUG_KP_MAIN_WINDOW
256 kDebug () << "\tTIME: readGeneralSettings = " << time.restart () << "msec";
257 #endif
259 readThumbnailSettings ();
260 #if DEBUG_KP_MAIN_WINDOW
261 kDebug () << "\tTIME: readThumbnailSettings = " << time.restart () << "msec";
262 #endif
266 // create GUI
269 setupActions ();
270 #if DEBUG_KP_MAIN_WINDOW
271 kDebug () << "\tTIME: setupActions = " << time.restart () << "msec";
272 #endif
274 createStatusBar ();
275 #if DEBUG_KP_MAIN_WINDOW
276 kDebug () << "\tTIME: createStatusBar = " << time.restart () << "msec";
277 #endif
279 createGUI ();
280 #if DEBUG_KP_MAIN_WINDOW
281 kDebug () << "\tTIME: createGUI = " << time.restart () << "msec";
282 #endif
286 // Create more GUI.
289 createColorBox ();
290 #if DEBUG_KP_MAIN_WINDOW
291 kDebug () << "\tTIME: createColorBox = " << time.restart () << "msec";
292 #endif
294 createToolBox ();
295 #if DEBUG_KP_MAIN_WINDOW
296 kDebug () << "\tTIME: createToolBox = " << time.restart () << "msec";
297 #endif
300 // Let the Tool Box take all the vertical space, since it can be quite
301 // tall with all its tool option widgets. This also avoids occasional
302 // bugs like the Tool Box overlapping the Color Tool Bar.
303 setCorner (Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
306 // HACK: We couldn't get toolbar orientations and undocking to work
307 // properly so we fix their positions for the time being.
309 // Must be called before setAutoSaveSettings() or there are
310 // massive redraw errors (don't know why).
311 addDockWidget (Qt::LeftDockWidgetArea, d->toolToolBar, Qt::Vertical);
312 d->toolToolBar->setFeatures (QDockWidget::NoDockWidgetFeatures);
314 addDockWidget (Qt::BottomDockWidgetArea, d->colorToolBar, Qt::Horizontal);
315 d->colorToolBar->setFeatures (QDockWidget::NoDockWidgetFeatures);
318 d->scrollView = new kpViewScrollableContainer (this);
319 d->scrollView->setObjectName ("scrollView");
320 connect (d->scrollView, SIGNAL (beganDocResize ()),
321 this, SLOT (slotBeganDocResize ()));
322 connect (d->scrollView, SIGNAL (continuedDocResize (const QSize &)),
323 this, SLOT (slotContinuedDocResize (const QSize &)));
324 connect (d->scrollView, SIGNAL (cancelledDocResize ()),
325 this, SLOT (slotCancelledDocResize ()));
326 connect (d->scrollView, SIGNAL (endedDocResize (const QSize &)),
327 this, SLOT (slotEndedDocResize (const QSize &)));
329 connect (d->scrollView, SIGNAL (statusMessageChanged (const QString &)),
330 this, SLOT (slotDocResizeMessageChanged (const QString &)));
332 connect (d->scrollView, SIGNAL (contentsMoving (int, int)),
333 this, SLOT (slotScrollViewAboutToScroll ()));
334 setCentralWidget (d->scrollView);
335 d->scrollView->show ();
336 #if DEBUG_KP_MAIN_WINDOW
337 kDebug () << "\tTIME: d->scrollView = " << time.restart () << "msec";
338 #endif
342 // set initial pos/size of GUI
345 setAutoSaveSettings ();
347 // Put our non-XMLGUI toolbars in a sane place, the first time around
348 // (have to do this _after_ setAutoSaveSettings as that applies default
349 // (i.e. random) settings to the toolbars)
350 if (d->configFirstTime)
352 #if DEBUG_KP_MAIN_WINDOW
353 kDebug () << "\tfirstTime: positioning toolbars";
354 #endif
356 // Not needed due to above toolbar HACK.
357 #if 0
358 addToolBar (Qt::LeftToolBarArea, d->toolToolBar);
359 addToolBar (Qt::BottomToolBarArea, d->colorToolBar);
360 #endif
362 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupGeneral);
364 cfg.writeEntry (kpSettingFirstTime, d->configFirstTime = false);
365 cfg.sync ();
369 #if DEBUG_KP_MAIN_WINDOW
370 kDebug () << "\tall done in " << totalTime.elapsed () << "msec";
371 #endif
375 // private virtual [base KMainWindow]
376 void kpMainWindow::readProperties (const KConfigGroup &configGroup)
378 #if DEBUG_KP_MAIN_WINDOW
379 kDebug () << "kpMainWindow<" << this << ">::readProperties()";
380 #endif
382 // No document at all?
383 if (!configGroup.hasKey (kpSessionSettingDocumentUrl))
385 #if DEBUG_KP_MAIN_WINDOW
386 kDebug () << "\tno url - no document";
387 #endif
388 setDocument (0);
390 // Have a document.
391 else
393 const KUrl url = configGroup.readEntry (kpSessionSettingDocumentUrl,
394 QString ());
395 #if DEBUG_KP_MAIN_WINDOW
396 kDebug () << "\turl=" << url;
397 #endif
399 const QSize notFromURLDocSize =
400 configGroup.readEntry (kpSessionSettingNotFromUrlDocumentSize,
401 QSize ());
403 // Is from URL?
404 if (notFromURLDocSize.isEmpty ())
406 // If this fails, the empty document that kpMainWindow::kpMainWindow()
407 // created is left untouched.
408 openInternal (url, defaultDocSize (),
409 false/*show error message if url !exist*/);
411 // Not from URL?
412 else
414 #if DEBUG_KP_MAIN_WINDOW
415 kDebug () << "\tnot from url; doc size=" << notFromURLDocSize;
416 #endif
417 // Either we have an empty URL or we have a "kolourpaint doesnotexist.png"
418 // URL. Regarding the latter case, if a file now actually exists at that
419 // URL, we do open it - ignoring notFromURLDocSize - to avoid putting
420 // the user in a situation where he might accidentally overwrite an
421 // existing file.
422 openInternal (url, notFromURLDocSize,
423 true/*create an empty doc with the same url if url !exist*/);
429 // private virtual [base KMainWindow]
430 // WARNING: KMainWindow API Doc says "No user interaction is allowed
431 // in this function!"
432 void kpMainWindow::saveProperties (KConfigGroup &configGroup)
434 #if DEBUG_KP_MAIN_WINDOW
435 kDebug () << "kpMainWindow<" << this << ">::saveProperties()";
436 #endif
438 // No document at all?
439 if (!d->document)
441 #if DEBUG_KP_MAIN_WINDOW
442 kDebug () << "\tno url - no document";
443 #endif
445 // Have a document.
446 else
448 // Save URL in all cases:
450 // a) d->document->isFromURL()
451 // b) !d->document->isFromURL() [save size in this case]
452 // i) No URL
453 // ii) URL (from "kolourpaint doesnotexist.png")
455 const KUrl url = d->document->url ();
456 #if DEBUG_KP_MAIN_WINDOW
457 kDebug () << "\turl=" << url;
458 #endif
459 configGroup.writeEntry (kpSessionSettingDocumentUrl, url.url ());
461 // Not from URL e.g. "kolourpaint doesnotexist.png"?
463 // Note that "kolourpaint doesexist.png" is considered to be from
464 // a URL even if it was deleted in the background (hence the
465 // "false" arg to isFromURL()). This is because the user expects
466 // it to be from a URL, so when we session restore, we pop up a
467 // "cannot find file" dialog, instead of silently creating a new,
468 // blank document.
469 if (!d->document->isFromURL (false/*don't bother checking exists*/))
471 // If we don't have a URL either:
473 // a) it was not modified - so we can use either width() or
474 // constructorWidth() (they'll be equal).
475 // b) the changes were discarded so we use the initial width,
476 // constructorWidth().
478 // Similarly for height() and constructorHeight().
479 const QSize docSize (d->document->constructorWidth (),
480 d->document->constructorHeight ());
481 #if DEBUG_KP_MAIN_WINDOW
482 kDebug () << "\tnot from url; doc size=" << docSize;
483 #endif
484 configGroup.writeEntry (kpSessionSettingNotFromUrlDocumentSize, docSize);
488 // Local session save i.e. queryClose() was not called beforehand
489 // (see QApplication::saveState())?
490 #if 0
491 if (d->document->isModified ())
493 // TODO: Implement by saving the current image to a persistent file.
494 // We do this instead of saving/mutating the backing image file
495 // as no one expects a file save on a session save without a
496 // "do you want to save" dialog first.
498 // I don't think any KDE application implements local session saving.
500 // --- The below code does not compile but shows you want to do ---
502 // Create unique name for the document in this main window.
503 const KUrl tempURL = homeDir +
504 "kolourpaint session " + sessionID +
505 mainWindowPtrToString + ".png";
506 // TODO: Use lossless PNG saving options.
507 kpDocumentSaveOptions pngSaveOptions;
509 if (kpDocument::savePixmapToFile (d->document->pixmapWithSelection (),
510 tempURL,
511 pngSaveOptions, *d->document->metaInfo (),
512 false/*no overwrite prompt*/,
513 false/*no lossy prompt*/,
514 this))
516 // readProperties() will still open kpSessionSettingDocumentUrl
517 // (as that's the expected URL) and will then add commands to:
519 // 1. Resize the document to the size of image at
520 // kpSessionSettingDocumentUnsavedContentsUrl, if the sizes
521 // differ.
522 // 2. Paste the kpSessionSettingDocumentUnsavedContentsUrl image
523 // (setting the main window's selection mode to opaque beforehand).
525 // It will then delete the file at
526 // kpSessionSettingDocumentUnsavedContentsUrl.
527 configGroup.writeEntry (kpSessionSettingDocumentUnsavedContentsUrl,
528 tempURL.url ());
530 else
532 // Not much we can do - we aren't allowed to throw up a dialog.
535 #endif
540 kpMainWindow::~kpMainWindow ()
542 d->isFullyConstructed = false;
544 // Get the kpTool to finish up. This makes sure that the kpTool destructor
545 // will not need to access any other class (that might be deleted before
546 // the destructor is called by the QObject child-deletion mechanism).
547 if (tool ())
548 tool ()->endInternal ();
550 // Delete document & views.
551 // Note: This will disconnects signals from the current kpTool, so kpTool
552 // must not be destructed yet.
553 setDocument (0);
555 delete d->commandHistory; d->commandHistory = 0;
556 delete d->scrollView; d->scrollView = 0;
558 delete d; d = 0;
562 // public
563 kpDocument *kpMainWindow::document () const
565 return d->document;
568 // public
569 kpDocumentEnvironment *kpMainWindow::documentEnvironment ()
571 if (!d->documentEnvironment)
572 d->documentEnvironment = new kpDocumentEnvironment (this);
574 return d->documentEnvironment;
577 // public
578 kpViewManager *kpMainWindow::viewManager () const
580 return d->viewManager;
583 // public
584 kpColorToolBar *kpMainWindow::colorToolBar () const
586 return d->colorToolBar;
589 // public
590 kpColorCells *kpMainWindow::colorCells () const
592 return d->colorToolBar ? d->colorToolBar->colorCells () : 0;
595 // public
596 kpToolToolBar *kpMainWindow::toolToolBar () const
598 return d->toolToolBar;
601 // public
602 kpCommandHistory *kpMainWindow::commandHistory () const
604 return d->commandHistory;
607 kpCommandEnvironment *kpMainWindow::commandEnvironment ()
609 if (!d->commandEnvironment)
610 d->commandEnvironment = new kpCommandEnvironment (this);
612 return d->commandEnvironment;
616 // private
617 void kpMainWindow::setupActions ()
619 setupFileMenuActions ();
620 setupEditMenuActions ();
621 setupViewMenuActions ();
622 setupImageMenuActions ();
623 setupColorsMenuActions ();
624 setupSettingsMenuActions ();
625 setupHelpMenuActions ();
627 setupTextToolBarActions ();
628 setupToolActions ();
631 // private
632 void kpMainWindow::enableDocumentActions (bool enable)
634 enableFileMenuDocumentActions (enable);
635 enableEditMenuDocumentActions (enable);
636 enableViewMenuDocumentActions (enable);
637 enableImageMenuDocumentActions (enable);
638 enableColorsMenuDocumentActions (enable);
639 enableSettingsMenuDocumentActions (enable);
640 enableHelpMenuDocumentActions (enable);
644 // private
645 void kpMainWindow::setDocument (kpDocument *newDoc)
647 #if DEBUG_KP_MAIN_WINDOW
648 kDebug () << "kpMainWindow::setDocument (" << newDoc << ")";
649 #endif
651 // is it a close operation?
652 if (!newDoc)
654 #if DEBUG_KP_MAIN_WINDOW
655 kDebug () << "\tdisabling actions";
656 #endif
658 // sync with the bit marked "sync" below
660 // TODO: Never disable the Color Box because the user should be
661 // able to manipulate the colors, even without a currently
662 // open document.
664 // We just have to make sure that signals from the Color
665 // Box aren't fired and received unexpectedly when there's
666 // no document.
667 Q_ASSERT (d->colorToolBar);
668 d->colorToolBar->setEnabled (false);
670 enableTextToolBarActions (false);
673 // Always disable the tools.
674 // If we decide to open a new document/mainView we want
675 // kpTool::begin() to be called again e.g. in case it sets the cursor.
676 // kpViewManager won't do this because we nuke it to avoid stale state.
677 enableToolsDocumentActions (false);
679 if (!newDoc)
681 enableDocumentActions (false);
684 #if DEBUG_KP_MAIN_WINDOW
685 kDebug () << "\tdestroying views";
686 #endif
688 delete d->mainView; d->mainView = 0;
689 slotDestroyThumbnail ();
691 #if DEBUG_KP_MAIN_WINDOW
692 kDebug () << "\tdestroying viewManager";
693 #endif
695 // viewManager will die and so will the selection
696 d->actionCopy->setEnabled (false);
697 d->actionCut->setEnabled (false);
698 d->actionDelete->setEnabled (false);
699 d->actionDeselect->setEnabled (false);
700 d->actionCopyToFile->setEnabled (false);
702 delete d->viewManager; d->viewManager = 0;
704 #if DEBUG_KP_MAIN_WINDOW
705 kDebug () << "\tdestroying document";
706 kDebug () << "\t\td->document=" << d->document;
707 #endif
708 // destroy current document
709 delete d->document;
710 d->document = newDoc;
713 if (!d->lastCopyToURL.isEmpty ())
714 d->lastCopyToURL.setFileName (QString());
715 d->copyToFirstTime = true;
717 if (!d->lastExportURL.isEmpty ())
718 d->lastExportURL.setFileName (QString());
719 d->exportFirstTime = true;
722 // not a close operation?
723 if (d->document)
725 #if DEBUG_KP_MAIN_WINDOW
726 kDebug () << "\treparenting doc that may have been created into a"
727 << " different mainWindiow" << endl;
728 #endif
729 d->document->setEnviron (documentEnvironment ());
731 #if DEBUG_KP_MAIN_WINDOW
732 kDebug () <<"\tcreating viewManager";
733 #endif
734 d->viewManager = new kpViewManager (this);
736 #if DEBUG_KP_MAIN_WINDOW
737 kDebug () << "\tcreating views";
738 #endif
739 d->mainView = new kpZoomedView (d->document, d->toolToolBar, d->viewManager,
740 0/*buddyView*/,
741 d->scrollView,
742 d->scrollView->viewport ());
743 d->mainView->setObjectName ("mainView");
745 d->scrollView->addChild (d->mainView);
746 d->viewManager->registerView (d->mainView);
747 d->mainView->show ();
749 #if DEBUG_KP_MAIN_WINDOW
750 kDebug () << "\thooking up document signals";
751 #endif
753 // Copy/Cut/Deselect/Delete
754 connect (d->document, SIGNAL (selectionEnabled (bool)),
755 d->actionCut, SLOT (setEnabled (bool)));
756 connect (d->document, SIGNAL (selectionEnabled (bool)),
757 d->actionCopy, SLOT (setEnabled (bool)));
758 connect (d->document, SIGNAL (selectionEnabled (bool)),
759 d->actionDelete, SLOT (setEnabled (bool)));
760 connect (d->document, SIGNAL (selectionEnabled (bool)),
761 d->actionDeselect, SLOT (setEnabled (bool)));
762 connect (d->document, SIGNAL (selectionEnabled (bool)),
763 d->actionCopyToFile, SLOT (setEnabled (bool)));
765 // this code won't actually enable any actions at this stage
766 // (fresh document) but better safe than sorry
767 d->actionCopy->setEnabled (d->document->selection ());
768 d->actionCut->setEnabled (d->document->selection ());
769 d->actionDeselect->setEnabled (d->document->selection ());
770 d->actionDelete->setEnabled (d->document->selection ());
771 d->actionCopyToFile->setEnabled (d->document->selection ());
773 connect (d->document, SIGNAL (selectionEnabled (bool)),
774 this, SLOT (slotImageMenuUpdateDueToSelection ()));
775 connect (d->document, SIGNAL (selectionIsTextChanged (bool)),
776 this, SLOT (slotImageMenuUpdateDueToSelection ()));
778 // Status bar
779 connect (d->document, SIGNAL (documentOpened ()),
780 this, SLOT (recalculateStatusBar ()));
782 connect (d->document, SIGNAL (sizeChanged (const QSize &)),
783 this, SLOT (setStatusBarDocSize (const QSize &)));
785 // Caption (url, modified)
786 connect (d->document, SIGNAL (documentModified ()),
787 this, SLOT (slotUpdateCaption ()));
788 connect (d->document, SIGNAL (documentOpened ()),
789 this, SLOT (slotUpdateCaption ()));
790 connect (d->document, SIGNAL (documentSaved ()),
791 this, SLOT (slotUpdateCaption ()));
793 // File/Reload action only available with non-empty URL
794 connect (d->document, SIGNAL (documentSaved ()),
795 this, SLOT (slotEnableReload ()));
797 connect (d->document, SIGNAL (documentSaved ()),
798 this, SLOT (slotEnableSettingsShowPath ()));
800 // Command history
801 Q_ASSERT (d->commandHistory);
802 connect (d->commandHistory, SIGNAL (documentRestored ()),
803 this, SLOT (slotDocumentRestored ())); // caption "!modified"
804 connect (d->document, SIGNAL (documentSaved ()),
805 d->commandHistory, SLOT (documentSaved ()));
807 // Sync document -> views
808 connect (d->document, SIGNAL (contentsChanged (const QRect &)),
809 d->viewManager, SLOT (updateViews (const QRect &)));
810 connect (d->document, SIGNAL (sizeChanged (int, int)),
811 d->viewManager, SLOT (adjustViewsToEnvironment ()));
813 #if DEBUG_KP_MAIN_WINDOW
814 kDebug () << "\tenabling actions";
815 #endif
817 // sync with the bit marked "sync" above
819 Q_ASSERT (d->colorToolBar);
820 d->colorToolBar->setEnabled (true);
823 // Hide the text toolbar - it will be shown by kpToolText::begin()
824 enableTextToolBarActions (false);
826 enableToolsDocumentActions (true);
828 enableDocumentActions (true);
830 // TODO: The thumbnail auto zoom doesn't work because it thinks its
831 // width == 1 when !this->isShown(). So for consistency,
832 // never create the thumbnail.
833 #if 0
834 if (d->configThumbnailShown)
836 if (isShown ())
838 #if DEBUG_KP_MAIN_WINDOW
839 kDebug () << "\tcreating thumbnail immediately";
840 #endif
841 slotCreateThumbnail ();
843 // this' geometry is weird ATM
844 else
846 #if DEBUG_KP_MAIN_WINDOW
847 kDebug () << "\tcreating thumbnail LATER";
848 #endif
849 QTimer::singleShot (0, this, SLOT (slotCreateThumbnail ()));
852 #endif
855 #if DEBUG_KP_MAIN_WINDOW
856 kDebug () << "\tupdating mainWindow elements";
857 #endif
859 slotImageMenuUpdateDueToSelection ();
860 recalculateStatusBar ();
861 slotUpdateCaption (); // Untitled to start with
862 slotEnableReload ();
863 slotEnableSettingsShowPath ();
865 if (d->commandHistory)
866 d->commandHistory->clear ();
868 #if DEBUG_KP_MAIN_WINDOW
869 kDebug () << "\tdocument and views ready to go!";
870 #endif
874 // private virtual [base QWidget]
875 void kpMainWindow::dragEnterEvent (QDragEnterEvent *e)
877 // It's faster to test for QMimeData::hasText() first due to the
878 // lazy evaluation of the '||' operator.
879 e->setAccepted (e->mimeData ()->hasText () ||
880 KUrl::List::canDecode (e->mimeData ()) ||
881 kpSelectionDrag::canDecode (e->mimeData ()));
884 // private virtual [base QWidget]
885 void kpMainWindow::dropEvent (QDropEvent *e)
887 #if DEBUG_KP_MAIN_WINDOW
888 kDebug () << "kpMainWindow::dropEvent" << e->pos ();
889 #endif
891 KUrl::List urls;
893 kpAbstractImageSelection *sel = kpSelectionDrag::decode (e->mimeData (),
894 pasteWarnAboutLossInfo ());
895 if (sel)
897 // TODO: How do you actually drop a selection or ordinary images on
898 // the clipboard)? Will this code path _ever_ execute?
899 sel->setTransparency (imageSelectionTransparency ());
900 // TODO: drop at point like with QTextDrag below?
901 paste (*sel);
902 delete sel;
904 else if (!(urls = KUrl::List::fromMimeData (e->mimeData ())).isEmpty ())
906 // LOTODO: kpSetOverrideCursorSaver cursorSaver (Qt::waitCursor);
908 // However, you would need to prefix all possible error/warning
909 // dialogs that might be called, with Qt::arrowCursor e.g. in
910 // kpDocument, kpPixmapFX::convertToPixmapAsLosslessAsPossible()
911 // and probably a lot more places.
912 foreach (const KUrl &u, urls)
913 open (u);
915 else if (e->mimeData ()->hasText ())
917 const QString text = e->mimeData ()->text ();
919 QPoint selTopLeft = KP_INVALID_POINT;
920 const QPoint globalPos = QWidget::mapToGlobal (e->pos ());
921 #if DEBUG_KP_MAIN_WINDOW
922 kDebug () << "\tpos toGlobal=" << globalPos;
923 #endif
925 kpView *view = 0;
927 if (d->viewManager)
929 view = d->viewManager->viewUnderCursor ();
930 #if DEBUG_KP_MAIN_WINDOW
931 kDebug () << "\t\tviewUnderCursor=" << view;
932 #endif
933 if (!view)
935 // HACK: see kpViewManager::setViewUnderCursor() to see why
936 // it's not reliable
937 #if DEBUG_KP_MAIN_WINDOW
938 kDebug () << "\t\tattempting to discover view";
940 if (d->mainView && d->scrollView)
942 kDebug () << "\t\t\tmainView->globalRect="
943 << kpWidgetMapper::toGlobal (d->mainView, d->mainView->rect ())
944 << " scrollView->globalRect="
945 << kpWidgetMapper::toGlobal (d->scrollView,
946 QRect (0, 0,
947 d->scrollView->visibleWidth (),
948 d->scrollView->visibleHeight ()))
949 << endl;
951 #endif
952 if (d->thumbnailView &&
953 kpWidgetMapper::toGlobal (d->thumbnailView, d->thumbnailView->rect ())
954 .contains (globalPos))
956 // TODO: Code will never get executed.
957 // Thumbnail doesn't accept drops.
958 view = d->thumbnailView;
960 else if (d->mainView &&
961 kpWidgetMapper::toGlobal (d->mainView, d->mainView->rect ())
962 .contains (globalPos) &&
963 d->scrollView &&
964 kpWidgetMapper::toGlobal (d->scrollView,
965 QRect (0, 0,
966 d->scrollView->visibleWidth (),
967 d->scrollView->visibleHeight ()))
968 .contains (globalPos))
970 view = d->mainView;
975 if (view)
977 const QPoint viewPos = view->mapFromGlobal (globalPos);
978 const QPoint docPoint = view->transformViewToDoc (viewPos);
980 // viewUnderCursor() is hacky and can return a view when we aren't
981 // over one thanks to drags.
982 if (d->document && d->document->rect ().contains (docPoint))
984 selTopLeft = docPoint;
986 // TODO: In terms of doc pixels, would be inconsistent behaviour
987 // based on zoomLevel of view.
988 // selTopLeft -= QPoint (-view->selectionResizeHandleAtomicSize (),
989 // -view->selectionResizeHandleAtomicSize ());
993 pasteText (text, true/*force new text selection*/, selTopLeft);
998 // private slot
999 void kpMainWindow::slotScrollViewAboutToScroll ()
1001 #if DEBUG_KP_MAIN_WINDOW && 1
1002 kDebug () << "kpMainWindow::slotScrollViewAboutToScroll() tool="
1003 << tool () << "viewManager=" << viewManager ();
1004 if (viewManager ())
1006 kDebug () << "\tfastUpdates=" << viewManager ()->fastUpdates ()
1007 << " queueUpdates=" << viewManager ()->queueUpdates ();
1009 else
1011 // We're getting a late signal from the scrollview (thanks to
1012 // a timer inside the Q3ScrollView). By now, setDocument() has
1013 // already killed the document(), tool() and viewManager().
1015 #endif
1017 QTimer::singleShot (0, this, SLOT (slotScrollViewAfterScroll ()));
1020 // private slot
1021 void kpMainWindow::slotScrollViewAfterScroll ()
1023 #if DEBUG_KP_MAIN_WINDOW && 1
1024 kDebug () << "kpMainWindow::slotScrollViewAfterScroll() tool="
1025 << tool () << endl;
1026 #endif
1028 // OPT: Why can't this be moved into kpViewScrollableContainer::slotDragScroll(),
1029 // grouping all drag-scroll-related repaints, which would potentially avoid
1030 // double repainting?
1031 if (tool ())
1033 #if DEBUG_KP_MAIN_WINDOW && 1
1034 kDebug () << "calling somethingBelowTheCursorChanged()";
1035 #endif
1036 tool ()->somethingBelowTheCursorChanged ();
1039 #if DEBUG_KP_MAIN_WINDOW && 1
1040 kDebug () << "DONE";
1041 #endif
1045 // private virtual [base QWidget]
1046 void kpMainWindow::moveEvent (QMoveEvent * /*e*/)
1048 if (d->thumbnail)
1050 // Disabled because it lags too far behind the mainWindow
1051 // d->thumbnail->move (d->thumbnail->pos () + (e->pos () - e->oldPos ()));
1053 notifyThumbnailGeometryChanged ();
1058 // private slot
1059 void kpMainWindow::slotUpdateCaption ()
1061 if (d->document)
1063 setCaption (d->configShowPath ? d->document->prettyUrl ()
1064 : d->document->prettyFilename (),
1065 d->document->isModified ());
1067 else
1069 setCaption (QString::null, false); //krazy:exclude=nullstrassign for old broken gcc
1073 // private slot
1074 void kpMainWindow::slotDocumentRestored ()
1076 if (d->document)
1077 d->document->setModified (false);
1078 slotUpdateCaption ();
1082 #include <kpMainWindow.moc>