Bug 94651 - KolourPaint fails to remember the opened document when saving KDE session
Summary: KolourPaint fails to remember the opened document when saving KDE session
Status: RESOLVED FIXED
Alias: None
Product: kolourpaint
Classification: Applications
Component: general-kde4 (show other bugs)
Version: unspecified
Platform: Gentoo Packages Linux
: NOR normal
Target Milestone: ---
Assignee: kolourpaint-support
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-12-08 06:44 UTC by Luke-Jr
Modified: 2007-05-13 14:35 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Luke-Jr 2004-12-08 06:44:15 UTC
Version:            (using KDE KDE 3.3.1)
Installed from:    Gentoo Packages

When I restore my session, KolourPaint re-opens, but doesn't load the document it had when the session saved.
Comment 1 Clarence Dang 2004-12-20 12:38:41 UTC
Session restore is not currently implemented.  I plan to implement it eventually but at the moment, it is not high on my priority list.  Is this feature important to you?  If so, I can prioritise it above some other goals I have in mind (mainly new features for 1.4).
Comment 2 Clarence Dang 2006-12-17 04:26:49 UTC
Reassigning bugs to KolourPaint support email address. 
Comment 3 Clarence Dang 2006-12-18 01:35:20 UTC
http://developer.kde.org/documentation/library/kdeqt/kde3arch/session.html provides the needed programming info, including a link to Qt Session Management.  We should implement this for KDE 4.  If it's not too complicated, will also backport to KDE 3.5.
Comment 4 Clarence Dang 2006-12-23 12:46:50 UTC
Reassigning bugs to KolourPaint support email address (again).
Comment 5 Clarence Dang 2007-05-11 13:49:12 UTC
SVN commit 663465 by dang:

Implement rudimentary global session management.  It saves the URLs, which
is the most I dare implement in the stable branch so as to not break anything.

Local session save/restore is not implemented, although some psuedo-code exists for it.
I don't intend to implement this since I don't think any other KDE app does.

CCMAIL: 94651@bugs.kde.org


 M  +4 -0      kolourpaint.cpp  
 M  +15 -0     kpdefs.h  
 M  +164 -1    kpmainwindow.cpp  
 M  +24 -0     kpmainwindow.h  
 M  +27 -11    kpmainwindow_file.cpp  


--- branches/KDE/3.5/kdegraphics/kolourpaint/kolourpaint.cpp #663464:663465
@@ -220,7 +220,11 @@
 
 
     if (app.isRestored ())
+    {
+        // Creates a kpMainWindow using the default constructor and then
+        // calls kpMainWindow::readProperties().
         RESTORE (kpMainWindow)
+    }
     else
     {
         kpMainWindow *mainWindow;
--- branches/KDE/3.5/kdegraphics/kolourpaint/kpdefs.h #663464:663465
@@ -132,5 +132,20 @@
 #define kpSettingFlattenEffectColor2 QString::fromLatin1 ("Color2")
 
 
+//
+// Session Restore Setting
+//
+
+// URL of the document in the main window.
+//
+// This key only exists if the document does.  If it exists, it can be empty.
+// The URL need not point to a file that exists e.g. "kolourpaint doesnotexist.png".
+#define kpSessionSettingDocumentUrl QString::fromLatin1 ("Session Document Url")
+
+// The size of a document which is not from a URL e.g. "kolourpaint doesnotexist.png".
+// This key does not exist for documents from URLs.
+#define kpSessionSettingNotFromUrlDocumentSize QString::fromLatin1 ("Session Not-From-Url Document Size")
+
+
 #endif  // __kp_defs_h__
 
--- branches/KDE/3.5/kdegraphics/kolourpaint/kpmainwindow.cpp #663464:663465
@@ -310,6 +310,170 @@
 #endif
 }
 
+
+// private virtual [base KMainWindow]
+void kpMainWindow::readProperties (KConfig *cfg)
+{
+#if DEBUG_KP_MAIN_WINDOW
+    kdDebug () << "kpMainWindow<" << this << ">::readProperties()" << endl;
+#endif
+
+    // No document at all?
+    if (!cfg->hasKey (kpSessionSettingDocumentUrl))
+    {
+    #if DEBUG_KP_MAIN_WINDOW
+        kdDebug () << "\tno url - no document" << endl;
+    #endif
+        setDocument (0);
+    }
+    // Have a document.
+    else
+    {
+        const KURL url (cfg->readEntry (kpSessionSettingDocumentUrl));
+    #if DEBUG_KP_MAIN_WINDOW
+        kdDebug () << "\turl=" << url << endl;
+    #endif
+
+        const QSize notFromURLDocSize =
+            cfg->readSizeEntry (kpSessionSettingNotFromUrlDocumentSize);
+
+        // Is from URL?
+        if (notFromURLDocSize.isEmpty ())
+        {
+            // If this fails, the empty document that kpMainWindow::kpMainWindow()
+            // created is left untouched.
+            openInternal (url, defaultDocSize (),
+                false/*show error message if url !exist*/);
+        }
+        // Not from URL?
+        else
+        {
+        #if DEBUG_KP_MAIN_WINDOW
+            kdDebug () << "\tnot from url; doc size=" << notFromURLDocSize << endl;
+        #endif
+            // Either we have an empty URL or we have a "kolourpaint doesnotexist.png"
+            // URL.  Regarding the latter case, if a file now actually exists at that
+            // URL, we do open it - ignoring notFromURLDocSize - to avoid putting
+            // the user in a situation where he might accidentally overwrite an
+            // existing file.
+            openInternal (url, notFromURLDocSize,
+                true/*create an empty doc with the same url if url !exist*/);
+        }
+    }
+
+}
+
+// private virtual [base KMainWindow]
+// WARNING: KMainWindow API Doc says "No user interaction is allowed
+//          in this function!"
+void kpMainWindow::saveProperties (KConfig *cfg)
+{
+#if DEBUG_KP_MAIN_WINDOW
+    kdDebug () << "kpMainWindow<" << this << ">::saveProperties()" << endl;
+#endif
+
+    // No document at all?
+    if (!m_document)
+    {
+    #if DEBUG_KP_MAIN_WINDOW
+        kdDebug () << "\tno url - no document" << endl;
+    #endif
+    }
+    // Have a document.
+    else
+    {
+        // Save URL in all cases:
+        //
+        //    a) m_document->isFromURL()
+        //    b) !m_document->isFromURL() [save size in this case]
+        //       i) No URL
+        //       ii) URL (from "kolourpaint doesnotexist.png")
+
+        const KURL url = m_document->url ();
+    #if DEBUG_KP_MAIN_WINDOW
+        kdDebug () << "\turl=" << url << endl;
+    #endif
+        cfg->writeEntry (kpSessionSettingDocumentUrl, url.url ());
+
+        // Not from URL e.g. "kolourpaint doesnotexist.png"?
+        //
+        // Note that "kolourpaint doesexist.png" is considered to be from
+        // a URL even if it was deleted in the background (hence the
+        // "false" arg to isFromURL()).  This is because the user expects
+        // it to be from a URL, so when we session restore, we pop up a
+        // "cannot find file" dialog, instead of silently creating a new,
+        // blank document.
+        if (!m_document->isFromURL (false/*don't bother checking exists*/))
+        {
+            // If we don't have a URL either:
+            //
+            // a) it was not modified - so we can use either width() or
+            //    constructorWidth() (they'll be equal).
+            // b) the changes were discarded so we use the initial width,
+            //    constructorWidth().
+            //
+            // Similarly for height() and constructorHeight().
+            const QSize docSize (m_document->constructorWidth (),
+                                 m_document->constructorHeight ());
+        #if DEBUG_KP_MAIN_WINDOW
+            kdDebug () << "\tnot from url; doc size=" << docSize << endl;
+        #endif
+            cfg->writeEntry (kpSessionSettingNotFromUrlDocumentSize, docSize);
+        }
+
+
+        // Local session save i.e. queryClose() was not called beforehand
+        // (see QApplication::saveState())?
+    #if 0
+        if (m_document->isModified ())
+        {
+            // TODO: Implement by saving the current image to a persistent file.
+            //       We do this instead of saving/mutating the backing image file
+            //       as no one expects a file save on a session save without a
+            //       "do you want to save" dialog first.
+            //
+            //       I don't think any KDE application implements local session saving.
+            //
+            //       --- The below code does not compile but shows you want to do ---
+
+            // Create unique name for the document in this main window.
+            const KURL tempURL = homeDir +
+                "kolourpaint session " + sessionID +
+                mainWindowPtrToString + ".png";
+            // TODO: Use lossless PNG saving options.
+            kpDocumentSaveOptions pngSaveOptions;
+
+            if (kpDocument::savePixmapToFile (m_document->pixmapWithSelection (),
+                    tempURL,
+                    pngSaveOptions, *m_document->metaInfo (),
+                    false/*no overwrite prompt*/,
+                    false/*no lossy prompt*/,
+                    this))
+            {
+                // readProperties() will still open kpSessionSettingDocumentUrl
+                // (as that's the expected URL) and will then add commands to:
+                //
+                // 1. Resize the document to the size of image at
+                //    kpSessionSettingDocumentUnsavedContentsUrl, if the sizes
+                //    differ.
+                // 2. Paste the kpSessionSettingDocumentUnsavedContentsUrl image
+                //    (setting the main window's selection mode to opaque beforehand).
+                //
+                // It will then delete the file at
+                // kpSessionSettingDocumentUnsavedContentsUrl.
+                cfg->writeEntry (kpSessionSettingDocumentUnsavedContentsUrl,
+                    tempURL.url ());
+            }
+            else
+            {
+                // Not much we can do - we aren't allowed to throw up a dialog.
+            }
+        }
+    #endif
+    }
+}
+
+
 kpMainWindow::~kpMainWindow ()
 {
     m_isFullyConstructed = false;
@@ -885,4 +1049,3 @@
 
 
 #include <kpmainwindow.moc>
-
--- branches/KDE/3.5/kdegraphics/kolourpaint/kpmainwindow.h #663464:663465
@@ -120,6 +120,15 @@
     void readThumbnailSettings ();
     void init ();
 
+    // (only called for restoring a previous session e.g. starting KDE with
+    //  a previously saved session; it's not called on normal KolourPaint
+    //  startup)
+    virtual void readProperties (KConfig *cfg);
+    // (only called for saving the current session e.g. logging out of KDE
+    //  with the KolourPaint window open; it's not called on normal KolourPaint
+    //  exit)
+    virtual void saveProperties (KConfig *cfg);
+
 public:
     ~kpMainWindow ();
 
@@ -310,7 +319,22 @@
     void setDocumentChoosingWindow (kpDocument *doc);
 
 private:
+    kpDocument *openInternal (const KURL &url,
+        const QSize &fallbackDocSize,
+        bool newDocSameNameIfNotExist);
+    // Same as above except that it:
+    //
+    // 1. Assumes a default fallback document size.
+    // 2. If the URL is successfully opened (with the special exception of
+    //    the "kolourpaint doesnotexist.png" case), it is bubbled up to the
+    //    top in the Recent Files Action.
+    //
+    // As a result of this behavior, this should only be called in response
+    // to a user open request e.g. File / Open or "kolourpaint doesexist.png".
+    // It should not be used for session restore - in that case, it does not
+    // make sense to bubble the Recent Files list.
     bool open (const KURL &url, bool newDocSameNameIfNotExist = false);
+
     KURL::List askForOpenURLs (const QString &caption,
                                const QString &startURL,
                                bool allowMultipleURLs = true);
--- branches/KDE/3.5/kdegraphics/kolourpaint/kpmainwindow_file.cpp #663464:663465
@@ -283,29 +283,45 @@
 
 
 // private
-bool kpMainWindow::open (const KURL &url, bool newDocSameNameIfNotExist)
+kpDocument *kpMainWindow::openInternal (const KURL &url,
+        const QSize &fallbackDocSize,
+        bool newDocSameNameIfNotExist)
 {
-    QSize docSize = defaultDocSize ();
-
     // create doc
-    kpDocument *newDoc = new kpDocument (docSize.width (), docSize.height (), this);
-    if (newDoc->open (url, newDocSameNameIfNotExist))
+    kpDocument *newDoc = new kpDocument (fallbackDocSize.width (),
+                                         fallbackDocSize.height (),
+                                         this);
+    if (!newDoc->open (url, newDocSameNameIfNotExist))
     {
+        delete newDoc;
+        return 0;
+    }
+
+    // Send document to current or new window.
+    setDocumentChoosingWindow (newDoc);
+
+    return newDoc;
+}
+
+// private
+bool kpMainWindow::open (const KURL &url, bool newDocSameNameIfNotExist)
+{
+    kpDocument *newDoc = openInternal (url,
+                                       defaultDocSize (),
+                                       newDocSameNameIfNotExist);
+    if (newDoc)
+    {
         if (newDoc->isFromURL (false/*don't bother checking exists*/))
             addRecentURL (url);
+        return true;
     }
     else
     {
-        delete newDoc;
         return false;
     }
+}
 
-    // Send document to current or new window.
-    setDocumentChoosingWindow (newDoc);
 
-    return true;
-}
-
 // private
 KURL::List kpMainWindow::askForOpenURLs (const QString &caption, const QString &startURL,
                                          bool allowMultipleURLs)
Comment 6 Clarence Dang 2007-05-13 14:35:11 UTC
SVN commit 664228 by dang:

Manual merge of branches/KDE/3.5/kdegraphics/kolourpaint/ -r662446:r663466:

Implement rudimentary global session management.  It saves the URLs - I'll
probably get it to save other things like zoom level later.

Local session save/restore is not implemented, although some psuedo-code exists for it.
I don't intend to implement this since I don't think any other KDE app does.

CCMAIL: 94651-done@bugs.kde.org

[FORWARD PORT]

Note: Global session saving crashes KolourPaint and XSM, at least on my build.
      I took a shortcut in the build process (timestamp-touched kpMainWindow.h
      so that I could override readProperties() and saveProperties() without
      a full rebuild) so this might be to blame.

      But if this is not the case, it's a null ptr deref in KMWSessionManager::commitData().
      Next time I update from kdelibs and do a clean build, I'll test again.
      Commit message ends here and afterwards is the valgrind output:

==7693==
==7693== Use of uninitialised value of size 4
==7693==    at 0x80CFEAD: QWidget::testAttribute(Qt::WidgetAttribute) const (in /home/kde4/dist/bin/kolourpaint)
==7693==    by 0x1C67CE12: KMWSessionManager::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1C5DC5C5: KApplication::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1CDD0BCC: sm_performSaveYourself(QSessionManagerPrivate*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDD0ED4: sm_saveYourselfCallback(_SmcConn*, void*, int, int, int, int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x4F6BAE: _SmcProcessMessage (in /usr/X11R6/lib/libSM.so.6.0)
==7693==    by 0x510CBA: IceProcessMessages (in /usr/X11R6/lib/libICE.so.6.3)
==7693==    by 0x1CDC100B: QSmSocketReceiver::socketActivated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDC10E9: QSmSocketReceiver::qt_metacall(QMetaObject::Call, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA60AB4: QMetaObject::activate(QObject*, int, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA60CB0: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA87DC2: QSocketNotifier::activated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA6C026: QSocketNotifier::event(QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD60741: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CD60A8B: QApplication::notify(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1C5DA78A: KApplication::notify(QObject*, QEvent*) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1BA4D111: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA510B4: QCoreApplication::sendEvent(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA78E34: socketNotifierSourceDispatch(_GSource*, int (*)(void*), void*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x6543ED: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6573F5: (within /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6578D7: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x1BA78CB2: QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CDF688E: QGuiEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA4BF9B: QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA4C236: QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA50E54: QCoreApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD5FBB4: QApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x80C080C: main (in /home/kde4/dist/bin/kolourpaint)
==7693==
==7693== Invalid read of size 4
==7693==    at 0x80CFEAD: QWidget::testAttribute(Qt::WidgetAttribute) const (in /home/kde4/dist/bin/kolourpaint)
==7693==    by 0x1C67CE12: KMWSessionManager::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1C5DC5C5: KApplication::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1CDD0BCC: sm_performSaveYourself(QSessionManagerPrivate*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDD0ED4: sm_saveYourselfCallback(_SmcConn*, void*, int, int, int, int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x4F6BAE: _SmcProcessMessage (in /usr/X11R6/lib/libSM.so.6.0)
==7693==    by 0x510CBA: IceProcessMessages (in /usr/X11R6/lib/libICE.so.6.3)
==7693==    by 0x1CDC100B: QSmSocketReceiver::socketActivated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDC10E9: QSmSocketReceiver::qt_metacall(QMetaObject::Call, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA60AB4: QMetaObject::activate(QObject*, int, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA60CB0: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA87DC2: QSocketNotifier::activated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA6C026: QSocketNotifier::event(QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD60741: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CD60A8B: QApplication::notify(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1C5DA78A: KApplication::notify(QObject*, QEvent*) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1BA4D111: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA510B4: QCoreApplication::sendEvent(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA78E34: socketNotifierSourceDispatch(_GSource*, int (*)(void*), void*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x6543ED: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6573F5: (within /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6578D7: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x1BA78CB2: QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CDF688E: QGuiEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA4BF9B: QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA4C236: QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA50E54: QCoreApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD5FBB4: QApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x80C080C: main (in /home/kde4/dist/bin/kolourpaint)
==7693==  Address 0x1D660010 is 16 bytes before a block of size 16 free'd
==7693==    at 0x1B9098CF: operator delete(void*) (vg_replace_malloc.c:155)
==7693==    by 0x1CEE3444: QRegion::cleanUp(QRegion::QRegionData*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CEE34F2: QRegion::~QRegion() (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CEEB41F: QWidgetBackingStore::copyToScreen(QRegion const&, QWidget*, QPoint const&, bool) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CEEC906: QWidgetBackingStore::cleanRegion(QRegion const&, QWidget*, bool) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CEECFA8: qt_syncBackingStore(QWidget*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDB5C47: QWidget::event(QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1D097F1F: QAbstractButton::event(QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1D1732CF: QToolButton::event(QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CD60741: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CD6258A: QApplication::notify(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1C5DA78A: KApplication::notify(QObject*, QEvent*) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1BA4D111: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA510B4: QCoreApplication::sendEvent(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA50BFC: QCoreApplication::sendPostedEvents(QObject*, int) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA78DAA: postEventSourceDispatch(_GSource*, int (*)(void*), void*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x6543ED: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6573F5: (within /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6578D7: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x1BA78CB2: QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CDF688E: QGuiEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA4BF9B: QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA4C236: QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA50E54: QCoreApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD5FBB4: QApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x80C080C: main (in /home/kde4/dist/bin/kolourpaint)
==7693==
==7693== Invalid read of size 4
==7693==    at 0x80CFEB7: QWidget::testAttribute(Qt::WidgetAttribute) const (in /home/kde4/dist/bin/kolourpaint)
==7693==    by 0x1C67CE12: KMWSessionManager::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1C5DC5C5: KApplication::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1CDD0BCC: sm_performSaveYourself(QSessionManagerPrivate*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDD0ED4: sm_saveYourselfCallback(_SmcConn*, void*, int, int, int, int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x4F6BAE: _SmcProcessMessage (in /usr/X11R6/lib/libSM.so.6.0)
==7693==    by 0x510CBA: IceProcessMessages (in /usr/X11R6/lib/libICE.so.6.3)
==7693==    by 0x1CDC100B: QSmSocketReceiver::socketActivated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDC10E9: QSmSocketReceiver::qt_metacall(QMetaObject::Call, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA60AB4: QMetaObject::activate(QObject*, int, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA60CB0: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA87DC2: QSocketNotifier::activated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA6C026: QSocketNotifier::event(QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD60741: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CD60A8B: QApplication::notify(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1C5DA78A: KApplication::notify(QObject*, QEvent*) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1BA4D111: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA510B4: QCoreApplication::sendEvent(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA78E34: socketNotifierSourceDispatch(_GSource*, int (*)(void*), void*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x6543ED: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6573F5: (within /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6578D7: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x1BA78CB2: QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CDF688E: QGuiEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA4BF9B: QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA4C236: QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA50E54: QCoreApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD5FBB4: QApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x80C080C: main (in /home/kde4/dist/bin/kolourpaint)
==7693==  Address 0x4 is not stack'd, malloc'd or (recently) free'd
==7693==
==7693== Process terminating with default action of signal 11 (SIGSEGV)
==7693==  Access not within mapped region at address 0x4
==7693==    at 0x80CFEB7: QWidget::testAttribute(Qt::WidgetAttribute) const (in /home/kde4/dist/bin/kolourpaint)
==7693==    by 0x1C67CE12: KMWSessionManager::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1C5DC5C5: KApplication::commitData(QSessionManager&) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1CDD0BCC: sm_performSaveYourself(QSessionManagerPrivate*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDD0ED4: sm_saveYourselfCallback(_SmcConn*, void*, int, int, int, int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x4F6BAE: _SmcProcessMessage (in /usr/X11R6/lib/libSM.so.6.0)
==7693==    by 0x510CBA: IceProcessMessages (in /usr/X11R6/lib/libICE.so.6.3)
==7693==    by 0x1CDC100B: QSmSocketReceiver::socketActivated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CDC10E9: QSmSocketReceiver::qt_metacall(QMetaObject::Call, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA60AB4: QMetaObject::activate(QObject*, int, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA60CB0: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA87DC2: QSocketNotifier::activated(int) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA6C026: QSocketNotifier::event(QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD60741: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1CD60A8B: QApplication::notify(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1C5DA78A: KApplication::notify(QObject*, QEvent*) (in /home/kde4/dist/lib/libkdeui.so.5.0.0)
==7693==    by 0x1BA4D111: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA510B4: QCoreApplication::sendEvent(QObject*, QEvent*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA78E34: socketNotifierSourceDispatch(_GSource*, int (*)(void*), void*) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x6543ED: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6573F5: (within /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x6578D7: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.600.4)
==7693==    by 0x1BA78CB2: QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CDF688E: QGuiEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x1BA4BF9B: QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA4C236: QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1BA50E54: QCoreApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtCore.so.4.3.0)
==7693==    by 0x1CD5FBB4: QApplication::exec() (in /home/kde4/celerysvn/qt-copy/lib/libQtGui.so.4.3.0)
==7693==    by 0x80C080C: main (in /home/kde4/dist/bin/kolourpaint)
==7693==
==7693== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 154 from 4)
==7693== malloc/free: in use at exit: 2757301 bytes in 55478 blocks.
==7693== malloc/free: 261781 allocs, 206303 frees, 175132418 bytes allocated.
==7693== For counts of detected errors, rerun with: -v
==7693== searching for pointers to 55478 not-freed blocks.
==7693== checked 4074156 bytes.
==7693==
==7693== LEAK SUMMARY:
==7693==    definitely lost: 268 bytes in 3 blocks.
==7693==      possibly lost: 70748 bytes in 2322 blocks.
==7693==    still reachable: 2686285 bytes in 53153 blocks.
==7693==         suppressed: 0 bytes in 0 blocks.
==7693== Use --leak-check=full to see details of leaked memory.





 M  +4 -1      NEWS  
 M  +4 -0      kolourpaint.cpp  
 M  +15 -0     kpDefs.h  
 M  +170 -3    mainwindow/kpMainWindow.cpp  
 M  +26 -1     mainwindow/kpMainWindow.h  
 M  +38 -20    mainwindow/kpMainWindow_File.cpp  


--- branches/work/~dang/kdegraphics/kolourpaint/NEWS #664227:664228
@@ -14,6 +14,9 @@
    * Add "File / Scan..." feature (Martin Koller)
      [also in branches/KDE/3.5/]
 
+   * Add global session save/restore (Bug #94651)
+     [also in branches/KDE/3.5/]
+
    * Rectangles, rounded rectangles and ellipsed are now bounded by
      the dimensions of the dragged out rectangle
 
@@ -27,7 +30,7 @@
      (Bug #125116)
 
    * Fix crash triggered by rapidly deselecting a selection after
-     drag-scaling the selection (Bug 117866)
+     drag-scaling the selection (Bug #117866)
      [also in branches/KDE/3.[345]/, branches/kolourpaint/1.2_kde3/]
 
    * Add "Rotate 90 Degrees Counterclockwise" (CTRL+L) and
--- branches/work/~dang/kdegraphics/kolourpaint/kolourpaint.cpp #664227:664228
@@ -203,7 +203,11 @@
 
 
     if (app.isSessionRestored ())
+    {
+        // Creates a kpMainWindow using the default constructor and then
+        // calls kpMainWindow::readProperties().
         RESTORE (kpMainWindow)
+    }
     else
     {
         kpMainWindow *mainWindow;
--- branches/work/~dang/kdegraphics/kolourpaint/kpDefs.h #664227:664228
@@ -133,5 +133,20 @@
 #define kpSettingFlattenEffectColor2 QString::fromLatin1 ("Color2")
 
 
+//
+// Session Restore Setting
+//
+
+// URL of the document in the main window.
+//
+// This key only exists if the document does.  If it exists, it can be empty.
+// The URL need not point to a file that exists e.g. "kolourpaint doesnotexist.png".
+#define kpSessionSettingDocumentUrl QString::fromLatin1 ("Session Document Url")
+
+// The size of a document which is not from a URL e.g. "kolourpaint doesnotexist.png".
+// This key does not exist for documents from URLs.
+#define kpSessionSettingNotFromUrlDocumentSize QString::fromLatin1 ("Session Not-From-Url Document Size")
+
+
 #endif  // KP_DEFS_H
 
--- branches/work/~dang/kdegraphics/kolourpaint/mainwindow/kpMainWindow.cpp #664227:664228
@@ -42,6 +42,7 @@
 #include <kmessagebox.h>
 #include <krecentfilesaction.h>
 #include <kconfiggroup.h>
+
 #include <kpColorToolBar.h>
 #include <kpCommandHistory.h>
 #include <kpDefs.h>
@@ -114,7 +115,7 @@
 void kpMainWindow::readGeneralSettings ()
 {
 #if DEBUG_KP_MAIN_WINDOW
-    kDebug () << "\tkpMainWindow(" << name () << ")::readGeneralSettings()" << endl;
+    kDebug () << "\tkpMainWindow(" << objectName () << ")::readGeneralSettings()" << endl;
 #endif
 
     KConfigGroup cfg (KGlobal::config (), kpSettingsGroupGeneral);
@@ -162,7 +163,7 @@
 void kpMainWindow::readThumbnailSettings ()
 {
 #if DEBUG_KP_MAIN_WINDOW
-    kDebug () << "\tkpMainWindow(" << name () << ")::readThumbnailSettings()" << endl;
+    kDebug () << "\tkpMainWindow(" << objectName () << ")::readThumbnailSettings()" << endl;
 #endif
 
     KConfigGroup cfg (KGlobal::config (), kpSettingsGroupThumbnail);
@@ -185,7 +186,7 @@
 void kpMainWindow::init ()
 {
 #if DEBUG_KP_MAIN_WINDOW
-    kDebug () << "kpMainWindow(" << name () << ")::init()" << endl;
+    kDebug () << "kpMainWindow(" << objectName () << ")::init()" << endl;
     QTime totalTime; totalTime.start ();
     QTime time; time.start ();
 #endif
@@ -328,6 +329,172 @@
 #endif
 }
 
+
+// private virtual [base KMainWindow]
+void kpMainWindow::readProperties (const KConfigGroup &configGroup)
+{
+#if DEBUG_KP_MAIN_WINDOW
+    kDebug () << "kpMainWindow<" << this << ">::readProperties()" << endl;
+#endif
+
+    // No document at all?
+    if (!configGroup.hasKey (kpSessionSettingDocumentUrl))
+    {
+    #if DEBUG_KP_MAIN_WINDOW
+        kDebug () << "\tno url - no document" << endl;
+    #endif
+        setDocument (0);
+    }
+    // Have a document.
+    else
+    {
+        const KUrl url = configGroup.readEntry (kpSessionSettingDocumentUrl,
+                                                QString ());
+    #if DEBUG_KP_MAIN_WINDOW
+        kDebug () << "\turl=" << url << endl;
+    #endif
+
+        const QSize notFromURLDocSize =
+            configGroup.readEntry (kpSessionSettingNotFromUrlDocumentSize,
+                                   QSize ());
+
+        // Is from URL?
+        if (notFromURLDocSize.isEmpty ())
+        {
+            // If this fails, the empty document that kpMainWindow::kpMainWindow()
+            // created is left untouched.
+            openInternal (url, defaultDocSize (),
+                false/*show error message if url !exist*/);
+        }
+        // Not from URL?
+        else
+        {
+        #if DEBUG_KP_MAIN_WINDOW
+            kDebug () << "\tnot from url; doc size=" << notFromURLDocSize << endl;
+        #endif
+            // Either we have an empty URL or we have a "kolourpaint doesnotexist.png"
+            // URL.  Regarding the latter case, if a file now actually exists at that
+            // URL, we do open it - ignoring notFromURLDocSize - to avoid putting
+            // the user in a situation where he might accidentally overwrite an
+            // existing file.
+            openInternal (url, notFromURLDocSize,
+                true/*create an empty doc with the same url if url !exist*/);
+        }
+    }
+
+}
+
+// private virtual [base KMainWindow]
+// WARNING: KMainWindow API Doc says "No user interaction is allowed
+//          in this function!"
+void kpMainWindow::saveProperties (KConfigGroup &configGroup)
+{
+#if DEBUG_KP_MAIN_WINDOW
+    kDebug () << "kpMainWindow<" << this << ">::saveProperties()" << endl;
+#endif
+
+    // No document at all?
+    if (!m_document)
+    {
+    #if DEBUG_KP_MAIN_WINDOW
+        kDebug () << "\tno url - no document" << endl;
+    #endif
+    }
+    // Have a document.
+    else
+    {
+        // Save URL in all cases:
+        //
+        //    a) m_document->isFromURL()
+        //    b) !m_document->isFromURL() [save size in this case]
+        //       i) No URL
+        //       ii) URL (from "kolourpaint doesnotexist.png")
+
+        const KUrl url = m_document->url ();
+    #if DEBUG_KP_MAIN_WINDOW
+        kDebug () << "\turl=" << url << endl;
+    #endif
+        configGroup.writeEntry (kpSessionSettingDocumentUrl, url.url ());
+
+        // Not from URL e.g. "kolourpaint doesnotexist.png"?
+        //
+        // Note that "kolourpaint doesexist.png" is considered to be from
+        // a URL even if it was deleted in the background (hence the
+        // "false" arg to isFromURL()).  This is because the user expects
+        // it to be from a URL, so when we session restore, we pop up a
+        // "cannot find file" dialog, instead of silently creating a new,
+        // blank document.
+        if (!m_document->isFromURL (false/*don't bother checking exists*/))
+        {
+            // If we don't have a URL either:
+            //
+            // a) it was not modified - so we can use either width() or
+            //    constructorWidth() (they'll be equal).
+            // b) the changes were discarded so we use the initial width,
+            //    constructorWidth().
+            //
+            // Similarly for height() and constructorHeight().
+            const QSize docSize (m_document->constructorWidth (),
+                                 m_document->constructorHeight ());
+        #if DEBUG_KP_MAIN_WINDOW
+            kDebug () << "\tnot from url; doc size=" << docSize << endl;
+        #endif
+            configGroup.writeEntry (kpSessionSettingNotFromUrlDocumentSize, docSize);
+        }
+
+
+        // Local session save i.e. queryClose() was not called beforehand
+        // (see QApplication::saveState())?
+    #if 0
+        if (m_document->isModified ())
+        {
+            // TODO: Implement by saving the current image to a persistent file.
+            //       We do this instead of saving/mutating the backing image file
+            //       as no one expects a file save on a session save without a
+            //       "do you want to save" dialog first.
+            //
+            //       I don't think any KDE application implements local session saving.
+            //
+            //       --- The below code does not compile but shows you want to do ---
+
+            // Create unique name for the document in this main window.
+            const KUrl tempURL = homeDir +
+                "kolourpaint session " + sessionID +
+                mainWindowPtrToString + ".png";
+            // TODO: Use lossless PNG saving options.
+            kpDocumentSaveOptions pngSaveOptions;
+
+            if (kpDocument::savePixmapToFile (m_document->pixmapWithSelection (),
+                    tempURL,
+                    pngSaveOptions, *m_document->metaInfo (),
+                    false/*no overwrite prompt*/,
+                    false/*no lossy prompt*/,
+                    this))
+            {
+                // readProperties() will still open kpSessionSettingDocumentUrl
+                // (as that's the expected URL) and will then add commands to:
+                //
+                // 1. Resize the document to the size of image at
+                //    kpSessionSettingDocumentUnsavedContentsUrl, if the sizes
+                //    differ.
+                // 2. Paste the kpSessionSettingDocumentUnsavedContentsUrl image
+                //    (setting the main window's selection mode to opaque beforehand).
+                //
+                // It will then delete the file at
+                // kpSessionSettingDocumentUnsavedContentsUrl.
+                configGroup.writeEntry (kpSessionSettingDocumentUnsavedContentsUrl,
+                    tempURL.url ());
+            }
+            else
+            {
+                // Not much we can do - we aren't allowed to throw up a dialog.
+            }
+        }
+    #endif
+    }
+}
+
+
 kpMainWindow::~kpMainWindow ()
 {
     m_isFullyConstructed = false;
--- branches/work/~dang/kdegraphics/kolourpaint/mainwindow/kpMainWindow.h #664227:664228
@@ -44,6 +44,7 @@
 #include <kpPixmapFX.h>
 
 
+class QAction;
 class QActionGroup;
 class QDragEnterEvent;
 class QDropEvent;
@@ -57,7 +58,7 @@
 class QStringList;
 class QUrl;
 
-class QAction;
+class KConfigGroup;
 class KFontAction;
 class KFontSizeAction;
 class KSelectAction;
@@ -124,6 +125,15 @@
     void readThumbnailSettings ();
     void init ();
 
+    // (only called for restoring a previous session e.g. starting KDE with
+    //  a previously saved session; it's not called on normal KolourPaint
+    //  startup)
+    virtual void readProperties (const KConfigGroup &configGroup);
+    // (only called for saving the current session e.g. logging out of KDE
+    //  with the KolourPaint window open; it's not called on normal KolourPaint
+    //  exit)
+    virtual void saveProperties (KConfigGroup &configGroup);
+
 public:
     ~kpMainWindow ();
 
@@ -314,7 +324,22 @@
     void setDocumentChoosingWindow (kpDocument *doc);
 
 private:
+    kpDocument *openInternal (const KUrl &url,
+        const QSize &fallbackDocSize,
+        bool newDocSameNameIfNotExist);
+    // Same as above except that it:
+    //
+    // 1. Assumes a default fallback document size.
+    // 2. If the URL is successfully opened (with the special exception of
+    //    the "kolourpaint doesnotexist.png" case), it is bubbled up to the
+    //    top in the Recent Files Action.
+    //
+    // As a result of this behavior, this should only be called in response
+    // to a user open request e.g. File / Open or "kolourpaint doesexist.png".
+    // It should not be used for session restore - in that case, it does not
+    // make sense to bubble the Recent Files list.
     bool open (const KUrl &url, bool newDocSameNameIfNotExist = false);
+
     KUrl::List askForOpenURLs (const QString &caption,
                                const QString &startURL,
                                bool allowMultipleURLs = true);
--- branches/work/~dang/kdegraphics/kolourpaint/mainwindow/kpMainWindow_File.cpp #664227:664228
@@ -331,6 +331,38 @@
 
 
 // private
+kpDocument *kpMainWindow::openInternal (const KUrl &url,
+        const QSize &fallbackDocSize,
+        bool newDocSameNameIfNotExist)
+{
+    // If using OpenImagesInSameWindow mode, ask whether to close the
+    // current document.
+    if (!shouldOpen ())
+        return 0;
+
+    // Create/open doc.
+    kpDocument *newDoc = new kpDocument (fallbackDocSize.width (),
+                                         fallbackDocSize.height (),
+                                         this);
+    if (!newDoc->open (url, newDocSameNameIfNotExist))
+    {
+    #if DEBUG_KP_MAIN_WINDOW
+        kDebug () << "\topen failed" << endl;
+    #endif
+        delete newDoc;
+        return 0;
+    }
+
+#if DEBUG_KP_MAIN_WINDOW
+    kDebug () << "\topen OK" << endl;
+#endif
+    // Send document to current or new window.
+    setDocumentChoosingWindow (newDoc);
+
+    return newDoc;
+}
+
+// private
 bool kpMainWindow::open (const KUrl &url, bool newDocSameNameIfNotExist)
 {
 #if DEBUG_KP_MAIN_WINDOW
@@ -339,37 +371,23 @@
               << ")" << endl;
 #endif
 
-    // If using OpenImagesInSameWindow mode, ask whether to close the
-    // current document.
-    if (!shouldOpen ())
-        return false;
-
-    // Create/open doc.
-    const QSize docSize = defaultDocSize ();
-    kpDocument *newDoc = new kpDocument (docSize.width (), docSize.height (), this);
-    if (newDoc->open (url, newDocSameNameIfNotExist))
+    kpDocument *newDoc = openInternal (url,
+                                       defaultDocSize (),
+                                       newDocSameNameIfNotExist);
+    if (newDoc)
     {
-    #if DEBUG_KP_MAIN_WINDOW
-        kDebug () << "\topen OK" << endl;
-    #endif
         if (newDoc->isFromURL (false/*don't bother checking exists*/))
             addRecentURL (url);
     }
     else
     {
-    #if DEBUG_KP_MAIN_WINDOW
-        kDebug () << "\topen failed" << endl;
-    #endif
-        delete newDoc;
         return false;
     }
-
-    // Send document to current or new window.
-    setDocumentChoosingWindow (newDoc);
-
+    
     return true;
 }
 
+
 // private
 KUrl::List kpMainWindow::askForOpenURLs (const QString &caption, const QString &startURL,
                                          bool allowMultipleURLs)