[M-git] Mahogany soruces repository. branch master updated. v0.67-623-g1f28a4e

"Vadim Zeitlin" <[email protected]> Mon, 9 Jan 2012 13:25:46 +0000
Newsgroups gmane.mail.mahogany.cvs
Message-ID <[email protected]>
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Mahogany soruces repository.".

The branch, master has been updated
       via  1f28a4eac0f225c28ba534f27939be2737877633 (commit)
       via  0e86174823d8e6b5d4e63125135c42e4fd647f03 (commit)
       via  4d94be133b2745b255a1da862f2e80da462aa55b (commit)
       via  2bf2614d39d8b943bf8248c905594da480fda2e8 (commit)
       via  e726af460110158cf85cbc8dc018ac3b4e1ebccc (commit)
       via  ed3cc84b2a23b228924a8713ae4d2a013333d59b (commit)
       via  505d356859a136c926112c0576f19c090ce3a661 (commit)
       via  3cb56e222271c6f9fdb4e04e15f9e945f7f45969 (commit)
       via  5a34a59fca7029a9161c66b6d0a0bd0d63f32eae (commit)
       via  7f2041d78bd6c72e9e7cc4e735271bfa8c28488a (commit)
       via  f7e73ff5f864c394fc5f3e27263e2e31df951c6d (commit)
       via  797a401af8801d494f3504c29665e8ba91863e43 (commit)
       via  a1827a1d89b7e136bf8cf4b29e5e9c93ef2211e8 (commit)
      from  d027ffd124fd73aef5dc24dcbe4b10f0e6e71c1b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 1f28a4eac0f225c28ba534f27939be2737877633
Author: Vadim Zeitlin <[email protected]>
Date:   Sun Dec 25 17:27:45 2011 +0100

    Reimplement reopening folders after resume from sleep.
    
    Instead of reopening the folders at the UI level, e.g. in the main frame
    itself, reopen them at MailFolder level. This seems to make sense, can
    potentially make things that are broken now (such as suspending with an option
    reply in the composer and then sending the message after resuming won't mark
    the original as answered) work in the future and fixes the bug when we were
    losing connection to the folder reopened in the main frame on idle timeout
    because we didn't restart its keep alive timer after reopening it.
    
    Also make MailFolder::Close() public to allow calling it when suspending for
    the folders that we don't need to keep open.

diff --git a/include/MailFolder.h b/include/MailFolder.h
index 88fa35f..7b4d988 100644
--- a/include/MailFolder.h
+++ b/include/MailFolder.h
@@ -251,6 +251,15 @@ public:
    static bool CloseFolder(const MFolder *mfolder, bool mayLinger = true);
 
    /**
+      Close the folder.
+
+      If mayLinger parameter is true (default), we can keep the network
+      connection so that it could be reused later. If it is false, the
+      connection should be closed as well.
+    */
+   virtual void Close(bool mayLinger = true) = 0;
+
+   /**
      Check the folder status without opening it (if possible).
 
      @param frame if not NULL, some feedback is given
@@ -258,6 +267,26 @@ public:
     */
    static bool CheckFolder(const MFolder *mfolder, wxFrame *frame = NULL);
 
+   /**
+       Suspend the folder by temporarily closing it.
+
+       This is called when the system is entering the sleep state to close all
+       network connections. Resume() is called after wake up to reopen them.
+
+       @return true if the folder was suspended and Resume() should be called,
+         false if there is no need to suspend this folder (e.g. it's a local
+         file) -- notice that this doesn't indicate an error
+    */
+   virtual bool Suspend() = 0;
+
+   /**
+       Reopen the folder after it was suspended.
+
+       Tries to reopen the method with the same parameters that were used when
+       it had been first opened by OpenFolder().
+    */
+   virtual bool Resume() = 0;
+
    //@}
 
    /** @name Operations on all folders at once */
@@ -977,15 +1006,6 @@ public:
 
 protected:
    /**
-      Close the folder.
-
-      If mayLinger parameter is true (default), we can keep the network
-      connection so that it could be reused later. If it is false, the
-      connection should be closed as well.
-    */
-   virtual void Close(bool mayLinger = true) = 0;
-
-   /**
      Check if the network connectivity is up if the given folder requires it
 
      @param folder the folder we're going to access
diff --git a/include/MailFolderCC.h b/include/MailFolderCC.h
index 0cbe304..6309da7 100644
--- a/include/MailFolderCC.h
+++ b/include/MailFolderCC.h
@@ -79,6 +79,11 @@ public:
    */
    static bool CanExit(String *which);
 
+   virtual void Close(bool mayLinger = true);
+
+   virtual bool Suspend();
+   virtual bool Resume();
+
    virtual bool IsOpened(void) const { return m_MailStream != NULL; }
 
    virtual bool IsReadOnly(void) const;
@@ -289,9 +294,6 @@ private:
    /// check (and delete if requested) for the lock on this file folder
    bool CheckForFileLock();
 
-   /// Close the folder
-   virtual void Close(bool mayLinger = true);
-
    /**
        Check if we still have a connection to the folder.
 
diff --git a/include/MailFolderCmn.h b/include/MailFolderCmn.h
index 2e05ecd..0952bf8 100644
--- a/include/MailFolderCmn.h
+++ b/include/MailFolderCmn.h
@@ -43,6 +43,9 @@ DECLARE_REF_COUNTER(FilterRule)
 class MailFolderCmn : public MailFolder
 {
 public:
+   /// remove the folder from our "closer" list
+   virtual void Close(bool mayLinger = true);
+
    /// do status caching and call DoCountMessages() to do the real work
    virtual bool CountAllMessages(MailFolderStatus *status) const;
 
@@ -170,6 +173,8 @@ public:
    virtual void ResumeUpdates();
    virtual void RequestUpdate();
 
+   virtual bool Resume();
+
    /** @name Delayed folder closing
 
        We may want to keep alive the folder for a while instead of closing it
@@ -185,9 +190,6 @@ private:
    //@}
 
 protected:
-   /// remove the folder from our "closer" list
-   virtual void Close(bool mayLinger = true);
-
    /// is updating currently suspended?
    bool IsUpdateSuspended() const { return m_suspendUpdates != 0; }
 
diff --git a/include/gui/wxMainFrame.h b/include/gui/wxMainFrame.h
index 1a08d21..477599d 100644
--- a/include/gui/wxMainFrame.h
+++ b/include/gui/wxMainFrame.h
@@ -23,8 +23,10 @@
 
 #include <wx/power.h>
 
+#include <vector>
+
+class MailFolder;
 class MFolder;
-class MFolderList;
 class wxFolderView;
 class wxFolderTree;
 
@@ -126,11 +128,9 @@ protected:
 
 
 #ifdef wxHAS_POWER_EVENTS
-   // the list of folders automatically closed when we suspended or NULL
-   MFolderList *m_foldersToReopen;
-
-   // the name of the folder which was opened in the main frame
-   String m_folderToReopenHere;
+   // the list of folders which were opened when we were suspended
+   typedef std::vector<MailFolder*> MailFolders;
+   MailFolders m_foldersToResume;
 #endif // wxHAS_POWER_EVENTS
 
 private:
diff --git a/include/mail/VFolder.h b/include/mail/VFolder.h
index 1466e38..027b8da 100644
--- a/include/mail/VFolder.h
+++ b/include/mail/VFolder.h
@@ -20,9 +20,19 @@
 #  include <wx/dynarray.h>
 #endif // USE_PCH
 
+#include <set>
+
 class MailFolderVirt : public MailFolderCmn
 {
 public:
+   /** @name Suspending and resuming */
+   //@{
+
+   virtual bool Suspend();
+   virtual bool Resume();
+
+   //@}
+
    /** @name Accessors */
    //@{
 
@@ -187,6 +197,13 @@ protected:
    /// the array of messages in the folder
    MsgArray m_messages;
 
+   /// All physical folders our messages belong to.
+   typedef std::set<MailFolder*> MailFoldersSet;
+   MailFoldersSet m_underlyingMFs;
+
+   /// Set of folders that should be resumed, possibly NULL.
+   MailFoldersSet m_foldersToResume;
+
    //@}
 
    /** @name folder properties */
diff --git a/src/gui/wxMainFrame.cpp b/src/gui/wxMainFrame.cpp
index 869de93..58525ea 100644
--- a/src/gui/wxMainFrame.cpp
+++ b/src/gui/wxMainFrame.cpp
@@ -634,9 +634,6 @@ wxMainFrame::wxMainFrame(const String &iname, wxFrame *parent)
    m_searchData = NULL;
    m_FolderTree = NULL;
    m_FolderView = NULL;
-#ifdef wxHAS_POWER_EVENTS
-   m_foldersToReopen = NULL;
-#endif // wxHAS_POWER_EVENTS
 
    // set frame icon/title, create status bar
    SetIcon(ICON(_T("MainFrame")));
@@ -1258,27 +1255,35 @@ wxMainFrame::OnCommandEvent(wxCommandEvent &event)
 
 void wxMainFrame::OnPowerSuspended(wxPowerEvent& WXUNUSED(event))
 {
-   ASSERT_MSG( !m_foldersToReopen, _T("didn't resume from last suspend?") );
+   ASSERT_MSG( m_foldersToResume.empty(), "didn't resume from last suspend?" );
 
-   m_folderToReopenHere = m_folderName;
-   m_foldersToReopen = new MFolderList;
-   int nClosed = MailFolder::CloseAll(m_foldersToReopen);
-   if ( nClosed < 0 )
-   {
-      wxLogWarning(_("Failed to gracefully close some opened folders on "
-                     "system suspend."));
-   }
-   else if ( nClosed )
+   MFPool::Cookie cookie;
+   for ( MailFolder *mf = MFPool::GetFirst(cookie); mf; mf = MFPool::GetNext(cookie) )
    {
-      wxLogStatus(_("Closed %lu folders which will be reopened on resume."),
-                  (unsigned long)m_foldersToReopen->size());
+      MailFolder_obj mfObj(mf);
+
+      // We only need to reopen the folders that the user is working with and
+      // those that need to be constantly monitored.
+      if ( mf->GetInteractiveFrame() || (mf->GetFlags() & MF_FLAGS_MONITOR))
+      {
+         if ( mf->Suspend() )
+         {
+            // Pass ownership to the list of folders to resume.
+            m_foldersToResume.push_back(mfObj.Detach());
+         }
+         //else: this (probably local) folder will survive resume.
+      }
+      else
+      {
+         // All the others can be simply closed.
+         mf->Close(false /* don't linger */);
+      }
    }
 
-   if ( nClosed <= 0 )
+   if ( !m_foldersToResume.empty() )
    {
-      // we don't need it finally
-      delete m_foldersToReopen;
-      m_foldersToReopen = NULL;
+      wxLogStatus(_("Closed %lu folders which will be reopened on resume."),
+                  (unsigned long)m_foldersToResume.size());
    }
 
    // save all options just in case
@@ -1287,28 +1292,28 @@ void wxMainFrame::OnPowerSuspended(wxPowerEvent& WXUNUSED(event))
 
 void wxMainFrame::OnPowerResume(wxPowerEvent& WXUNUSED(event))
 {
-   if ( !m_foldersToReopen )
+   if ( m_foldersToResume.empty() )
       return;
 
    // copy to a temporary variable to avoid problems in case we get several
    // resume messages (currently happens under Windows sometimes)
-   MFolderList *foldersToReopen = m_foldersToReopen;
-   m_foldersToReopen = NULL;
+   MailFolders foldersToResume;
+   foldersToResume.swap(m_foldersToResume);
 
    wxLogStatus(_("Reopening %lu folders on system resume"),
-               (unsigned long)foldersToReopen->size());
+               (unsigned long)foldersToResume.size());
 
 #ifdef CAN_CHECK_NETWORK_STATE
    bool checkedNetwork = false;
 #endif // CAN_CHECK_NETWORK_STATE
-   for ( MFolderList::iterator i = foldersToReopen->begin();
-         i != foldersToReopen->end();
+   for ( MailFolders::iterator i = foldersToResume.begin();
+         i != foldersToResume.end();
          ++i )
    {
-      MFolder *folder = *i;
+      MailFolder* const mf = *i;
 
 #ifdef CAN_CHECK_NETWORK_STATE
-      if ( !checkedNetwork && folder->NeedsNetwork() )
+      if ( !checkedNetwork && mf->NeedsNetwork() )
       {
          // We can get a resume event before the system got reconnected so stay
          // here for a few seconds to give it a chance to do it as otherwise
@@ -1330,35 +1335,22 @@ void wxMainFrame::OnPowerResume(wxPowerEvent& WXUNUSED(event))
       }
 #endif // CAN_CHECK_NETWORK_STATE
 
-      if ( folder->GetFullName() == m_folderToReopenHere )
+      if ( !mf->Resume() )
       {
-         OpenFolder(folder);
+         ERRORMESSAGE((_("Failed to reopen folder \"%s\" after resuming from sleep."),
+                      mf->GetName()));
       }
-      else // a folder opened elsewhere
+      else
       {
-         // only reopen it this folder should be permanently opened,
-         // otherwise it will be reopened from the folder view which uses it
-         if ( folder->GetFlags() & MF_FLAGS_KEEPOPEN )
-         {
-            MailFolder *mf = MailFolder::OpenFolder(folder);
-            if ( !mf )
-            {
-               ERRORMESSAGE((_("Failed to reopen folder \"%s\""),
-                             folder->GetFullName().c_str()));
-            }
-            else
-            {
-               // it won't be really closed but will be kept open in the
-               // background
-               mf->DecRef();
-            }
-         }
+         // Notify everybody about the folder reopening. This is an abuse of
+         // this event as there are not necessarily any real updates but it's
+         // the simplest way to make all folder listings in all the existing
+         // folder views to refresh.
+         MEventManager::Send(new MEventFolderUpdateData(mf));
       }
-   }
-
-   delete foldersToReopen;
 
-   m_folderToReopenHere.clear();
+      mf->DecRef();
+   }
 }
 
 #endif // wxHAS_POWER_EVENTS
diff --git a/src/mail/MailFolderCC.cpp b/src/mail/MailFolderCC.cpp
index c1eb0ec..650a844 100644
--- a/src/mail/MailFolderCC.cpp
+++ b/src/mail/MailFolderCC.cpp
@@ -2407,6 +2407,34 @@ MailFolderCC::ForceClose()
 }
 
 // ----------------------------------------------------------------------------
+// MailFolderCC suspending and resuming
+// ----------------------------------------------------------------------------
+
+bool MailFolderCC::Suspend()
+{
+   if ( !NeedsNetwork() )
+      return false;
+
+   Close(false /* don't linger */);
+
+   return true;
+}
+
+bool MailFolderCC::Resume()
+{
+   ASSERT_MSG( !IsOpened(), "reopening already open folder?" );
+
+   if ( !Open() )
+      return false;
+
+   // This is done from MailFolder::OpenFolder() for the newly opened folders
+   // but MailFolderCmn::Close() undoes it, so we need to redo it here.
+   MFPool::Add(MFDriver::Get(m_mfolder->GetClass()), this, m_mfolder, m_login);
+
+   return MailFolderCmn::Resume();
+}
+
+// ----------------------------------------------------------------------------
 // MailFolderCC timeouts
 // ----------------------------------------------------------------------------
 
diff --git a/src/mail/MailFolderCmn.cpp b/src/mail/MailFolderCmn.cpp
index 9d6709e..d6f9fae 100644
--- a/src/mail/MailFolderCmn.cpp
+++ b/src/mail/MailFolderCmn.cpp
@@ -343,7 +343,7 @@ void MailFolderKeepAliveTimer::Notify(void)
               m_mf->GetName(),
               TimestampWithMS());
 
-   Start();
+   Start(-1, true /* one shot */);
 }
 
 // ----------------------------------------------------------------------------
@@ -536,6 +536,22 @@ void MailFolderCmn::Close(bool /* mayLinger */)
    }
 }
 
+bool MailFolderCmn::Resume()
+{
+   if ( m_keepAliveTimer )
+   {
+      // restart the timer that we stopped in Close()
+      wxLogTrace(TRACE_MF_KEEPALIVE,
+                 "Restarting keep alive timer for \"%s\" at %s",
+                 GetName(),
+                 TimestampWithMS());
+
+      m_keepAliveTimer->Start(-1 /* same interval */, true /* one shot */);
+   }
+
+   return true;
+}
+
 bool
 MailFolderCmn::DecRef()
 {
diff --git a/src/mail/VFolder.cpp b/src/mail/VFolder.cpp
index c608627..f310452 100644
--- a/src/mail/VFolder.cpp
+++ b/src/mail/VFolder.cpp
@@ -168,6 +168,51 @@ MailFolderVirt::GetFullImapSpec(const MFolder *folder,
 }
 
 // ----------------------------------------------------------------------------
+// Opening and closing
+// ----------------------------------------------------------------------------
+
+bool MailFolderVirt::Suspend()
+{
+   ASSERT_MSG( m_foldersToResume.empty(),
+               "suspending twice without intervening Resume()?" );
+
+   for ( MailFoldersSet::const_iterator i = m_underlyingMFs.begin();
+         i != m_underlyingMFs.end();
+         ++i )
+   {
+      if ( (*i)->Suspend() )
+         m_foldersToResume.insert(*i);
+   }
+
+   // We only need to be resumed if we suspended anything.
+   return !m_foldersToResume.empty();
+}
+
+bool MailFolderVirt::Resume()
+{
+   MailFoldersSet foldersToResume;
+   foldersToResume.swap(m_foldersToResume);
+
+   bool rc = false;
+   for ( MailFoldersSet::const_iterator i = foldersToResume.begin();
+         i != foldersToResume.end();
+         ++i )
+   {
+      if ( (*i)->Resume() )
+         rc = true;
+
+      // Continue reopening the folders in any case, if at least one of them
+      // was reopened it's already partially successful as we will at least be
+      // able to provide access to the messages from it.
+   }
+
+   if ( !MailFolderCmn::Resume() )
+      rc = false;
+
+   return rc;
+}
+
+// ----------------------------------------------------------------------------
 // trivial MailFolderVirt accessors
 // ----------------------------------------------------------------------------
 
@@ -268,6 +313,8 @@ void MailFolderVirt::AddMsg(MailFolderVirt::Msg *msg)
 {
    CHECK_RET( msg, _T("NULL Msg in MailFolderVirt?") );
 
+   m_underlyingMFs.insert(msg->mf);
+
    m_messages.Add(msg);
 }
 

commit 0e86174823d8e6b5d4e63125135c42e4fd647f03
Author: Vadim Zeitlin <[email protected]>
Date:   Thu Dec 29 17:27:10 2011 +0100

    Don't call MFPool::Remove() from MailFolderCmn dtor.
    
    This is dangerous as if the folder was indeed left in the pool, due to some
    bug, we're going to crash when calling MailFolder::GetName() from inside
    MFPool::Remove() as it's pure virtual in MailFolderCmn and the derived object,
    which did define it, is already destroyed by now.
    
    So replace this check with a less precise but still good enough in practice
    check for m_headers == NULL as if this is the case, the folder must have been
    closed.

diff --git a/src/mail/MailFolderCmn.cpp b/src/mail/MailFolderCmn.cpp
index df9289c..9d6709e 100644
--- a/src/mail/MailFolderCmn.cpp
+++ b/src/mail/MailFolderCmn.cpp
@@ -651,7 +651,7 @@ MailFolderCmn::~MailFolderCmn()
    ASSERT_MSG( !m_suspendUpdates,
                _T("mismatch between Suspend/ResumeUpdates()") );
 
-   ASSERT_MSG( !MFPool::Remove(this), _T("folder shouldn't be left in the pool!") );
+   ASSERT_MSG( !m_headers, _T("folder destroyed without being closed?") );
 
    // this must have been cleared by SendMsgStatusChangeEvent() call earlier
    if ( m_statusChangeData )

commit 4d94be133b2745b255a1da862f2e80da462aa55b
Author: Vadim Zeitlin <[email protected]>
Date:   Tue Dec 27 20:08:08 2011 +0100

    No real changes, just change milliseconds separator in debug messages.
    
    Use '.' for milliseconds and not ':' which was unusual and confusing.

diff --git a/src/mail/MailFolderCmn.cpp b/src/mail/MailFolderCmn.cpp
index 1345bd4..df9289c 100644
--- a/src/mail/MailFolderCmn.cpp
+++ b/src/mail/MailFolderCmn.cpp
@@ -124,7 +124,7 @@ long GetProgressThreshold(Profile *profile)
 /// Return a timestamp string including milliseconds.
 wxString TimestampWithMS()
 {
-   return wxDateTime::UNow().Format("%Y-%m-%d %H:%M:%S:%l");
+   return wxDateTime::UNow().Format("%Y-%m-%d %H:%M:%S.%l");
 }
 
 } // anonymous namespace

commit 2bf2614d39d8b943bf8248c905594da480fda2e8
Author: Vadim Zeitlin <[email protected]>
Date:   Mon Dec 26 23:16:37 2011 +0100

    Enhance the "Quick filter" dialog by allowing to set up new folder from it.
    
    Create the folder specified in the dialog rule if it doesn't exist and also
    allow to set the new folder up sensibly by reusing the filter rule sender and
    recipient.
    
    This makes it as simple as possible to set up a new folder for all messages
    from some sender which is probably the most common task for which the filters
    are used.

diff --git a/CHANGES b/CHANGES
index 0bf53ad..8013d3e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,7 @@
 Release 0.68 'Cynthia' September xx, 2010
 -----------------------------------------
 
+2011-12-26 VZ: Allow to create and configure new folders in "Quick filter".
 2010-08-01 VZ: Show hidden folders if new mail arrives into them.
 2010-07-05 VZ: Send messages from a separate background thread.
 2009-12-27 VZ: Pre-fill composer recipient with address from clipboard if any.
diff --git a/doc/Manual.htex b/doc/Manual.htex
index 69ddc03..b1d66c5 100644
--- a/doc/Manual.htex
+++ b/doc/Manual.htex
@@ -73,6 +73,11 @@ skip them unless you're updating from a very old version of Mahogany.
          the new options in the composer options page (\ref{ComposePage}).
    \item Added ``Remove attachments'' command which can be used to strip the
          unwanted attachments from a message in a local or IMAP folder.
+   \item Made the ``Quick filter'' dialog more useful by allowing to specify
+         a folder that doesn't exist yet in it. Moreover, the new folder can
+         be configured to use the recipient and sender corresponding to the
+         rule itself, e.g. the default recipient can be set up to be the same
+         as the sender address tested by the rule.
    \item Added the possibility to treat different addresses as equivalent,
          this is useful to avoid sending duplicate replies to the different
          addresses of the same person, for example.
diff --git a/include/MFolderDialogs.h b/include/MFolderDialogs.h
index 22354b4..f486dea 100644
--- a/include/MFolderDialogs.h
+++ b/include/MFolderDialogs.h
@@ -61,6 +61,20 @@ enum FolderCreatePage
 extern MFolder* AskUserToCreateFolder(wxWindow* parent, MFolder* parentFolder);
 
 /**
+    Try to create a folder with the given name, asking the user only if needed.
+
+    If the required parameter of the new folder can be deduced automatically
+    from its path, the folder is created without user intervention. If this
+    can't be done, AskUserToCreateFolder() is used.
+
+    @param parent The window to use as the parent for the various dialogs.
+    @param fullname The full name of the folder to create.
+    @return The new folder to be DecRef()'d by called or NULL.
+ */
+extern
+MFolder* TryToCreateFolderOrAskUser(wxWindow* parent, const String& fullname);
+
+/**
   Shows folder creation dialog, returns a pointer to created folder or NULL.
 
   Use AskUserToCreateFolder() unless it's really the folder creation dialog and
diff --git a/include/MpersIds.h b/include/MpersIds.h
index 69f3362..30ab530 100644
--- a/include/MpersIds.h
+++ b/include/MpersIds.h
@@ -86,6 +86,7 @@ DECL_OR_DEF(CONFIG_SAVED_REMOTELY);
 DECL_OR_DEF(EXPLAIN_GLOBALPASSWD);
 DECL_OR_DEF(FILTER_NOT_USED_YET);
 DECL_OR_DEF(FILTER_CONFIRM_OVERWRITE);
+DECL_OR_DEF(FILTER_CREATE_TARGET);
 DECL_OR_DEF(IMPORT_FOLDERS_UNDER_ROOT);
 DECL_OR_DEF(MOVE_EXPUNGE_CONFIRM);
 DECL_OR_DEF(APPLY_QUICK_FILTER_NOW);
diff --git a/src/classes/Mpers.cpp b/src/classes/Mpers.cpp
index c5ce316..a3823e0 100644
--- a/src/classes/Mpers.cpp
+++ b/src/classes/Mpers.cpp
@@ -138,6 +138,7 @@ static const struct
    { "ExplainGlobalPasswd",      gettext_noop("show explanation before asking for global password") },
    { "FilterNotUsedYet",         gettext_noop("warn that newly created filter is unused") },
    { "FilterOverwrite",          gettext_noop("ask confirmation before overwriting a filter with another one") },
+   { "FilterCreateTarget",       gettext_noop("propose to create filter target folder if it doesn't exist") },
    { "ImportUnderRoot",          gettext_noop("ask where do you want to import folders") },
    { "MoveExpungeConfirm",       gettext_noop("confirm expunging messages after moving") },
    { "ApplyQuickFilter",         gettext_noop("propose to apply quick filter after creation") },
diff --git a/src/gui/wxFiltersDialog.cpp b/src/gui/wxFiltersDialog.cpp
index 089c2be..067f78a 100644
--- a/src/gui/wxFiltersDialog.cpp
+++ b/src/gui/wxFiltersDialog.cpp
@@ -39,6 +39,7 @@
 #include "MFilter.h"
 #include "MFolder.h"
 #include "MailFolder.h"
+#include "MFolderDialogs.h"
 #include "MModule.h"
 #include "SpamFilter.h"
 
@@ -51,11 +52,19 @@
 class WXDLLIMPEXP_FWD_CORE wxWindow;
 
 // ----------------------------------------------------------------------------
+// options we use here
+// ----------------------------------------------------------------------------
+
+extern const MOption MP_COMPOSE_TO;
+extern const MOption MP_FROM_ADDRESS;
+
+// ----------------------------------------------------------------------------
 // persistent msgboxes we use here
 // ----------------------------------------------------------------------------
 
 extern const MPersMsgBox *M_MSGBOX_FILTER_NOT_USED_YET;
 extern const MPersMsgBox *M_MSGBOX_FILTER_CONFIRM_OVERWRITE;
+extern const MPersMsgBox *M_MSGBOX_FILTER_CREATE_TARGET;
 extern const MPersMsgBox *M_MSGBOX_FILTER_REPLACE;
 
 // ---------------------------------------------------------------------------
@@ -944,6 +953,19 @@ public:
          m_Argument->SetValue(argument);
    }
 
+   /**
+       Validate the user-entered action.
+
+       Currently this just checks that the folder entered by the user exists
+       and proposes to create it if it doesn't. Does nothing for the actions
+       not using folders.
+
+       If this method return false, the action shouldn't be used, i.e. the
+       filter creation should be cancelled.
+    */
+   bool Validate();
+
+
    /// get the action
    MFDialogAction GetAction() const
       { return MFDialogAction_fromSelect(m_Type->GetSelection()); }
@@ -1118,6 +1140,56 @@ OneActionControl::LayoutControls(wxWindow **last,
    *last = m_Argument;
 }
 
+bool OneActionControl::Validate()
+{
+   if ( FilterActionUsesFolder(GetAction()) )
+   {
+      const wxString folderName = GetArgument();
+      MFolder_obj folder(folderName);
+      if ( !folder.IsOk() )
+      {
+         switch ( MDialog_YesNoCancel
+                  (
+                     wxString::Format
+                     (
+                        _("The folder \"%s\" specified by the filter action "
+                          "doesn't exist, would you like to create it now?"),
+                        folderName
+                     ),
+                     m_Parent,
+                     _("Filter target folder doesn't exist"),
+                     M_DLG_YES_DEFAULT,
+                     M_MSGBOX_FILTER_CREATE_TARGET
+                 ) )
+         {
+            case MDlg_Cancel:
+               // Don't use this filter target at all.
+               return false;
+
+            case MDlg_Yes:
+               // Create the new folder with the given name.
+               if ( !MFolder_obj(
+                        TryToCreateFolderOrAskUser(m_Parent, folderName)
+                     ) )
+               {
+                  return false;
+               }
+               break;
+
+            default:
+               FAIL_MSG( "Unexpected MDialog_YesNoCancel() return value" );
+               // fall through
+
+            case MDlg_No:
+               // Nothing to do, keep the inexistent target folder, presumably
+               // they're going to create it later.
+               break;
+         }
+      }
+   }
+
+   return true;
+}
 
 /*
   Contains several lines of "OneCritControl" and "OneActionControl":
@@ -2338,10 +2410,35 @@ protected:
                 FilterControl which);
 
 private:
+   // Handlers for m_checkSetRecipient and m_checkSetSender.
+   void OnSetRecipientCheck(wxCommandEvent& event);
+   void OnSetSenderCheck(wxCommandEvent& event);
+
+   // And the common part of their implementations.
+   void DoUpdateTextOnCheck(wxTextCtrl* textCopyFrom, wxTextCtrl* textCopyTo);
+
+   // This handler is for updating all controls depending on whether the action
+   // uses a target.
+   void OnUpdateDepOnTargetFolder(wxUpdateUIEvent& event);
+
+   // And these two are for the target- and checkbox-depending text controls.
+   void OnUpdateTextRecipient(wxUpdateUIEvent& event);
+   void OnUpdateTextSender(wxUpdateUIEvent& event);
+
+   // The common implementation of the two above handlers.
+   void DoUpdateTextFromCheckbox(wxCheckBox* checkbox, wxUpdateUIEvent& event);
+
+
    // GUI controls
    wxCheckBox *m_check[Filter_Max];
    wxTextCtrl *m_text[Filter_Max];
 
+   wxCheckBox* m_checkSetRecipient;
+   wxTextCtrl* m_textRecipient;
+
+   wxCheckBox* m_checkSetSender;
+   wxTextCtrl* m_textSender;
+
    OneActionControl *m_action;
 
    MFolder *m_folder;
@@ -2384,7 +2481,7 @@ wxQuickFilterDialog::wxQuickFilterDialog(MFolder *folder,
 
    CreateAllControls(ProfileEdit_WithoutApply);
 
-   SetDefaultSize(8*wBtn, 13*hBtn);
+   SetDefaultSize(8*wBtn, 16*hBtn);
 
    DoUpdateUI();
 }
@@ -2468,6 +2565,81 @@ wxWindow *wxQuickFilterDialog::CreateMainWindow(wxPanel *panel)
    wxWindow *last = msg;
    m_action->LayoutControls(&last, 2*LAYOUT_X_MARGIN, 3*LAYOUT_X_MARGIN);
 
+
+   msg = new wxStaticText(panel, wxID_ANY,
+                          _("Additionally, configure the folder "
+                            "chosen above to:"));
+   c = new wxLayoutConstraints;
+   c->top.Below(last, 4*LAYOUT_Y_MARGIN);
+   c->left.SameAs(box, wxLeft, 2*LAYOUT_X_MARGIN);
+   c->right.SameAs(box, wxRight, 2*LAYOUT_X_MARGIN);
+   c->height.AsIs();
+   msg->SetConstraints(c);
+
+   msg->Bind(wxEVT_UPDATE_UI,
+      &wxQuickFilterDialog::OnUpdateDepOnTargetFolder, this
+   );
+
+   labels.Clear();
+   labels.Add(_("use the following recipient by default:"));
+   labels.Add(_("use the following return address:"));
+   widthMax = GetMaxLabelWidth(labels, panel) + 4*LAYOUT_X_MARGIN;
+
+   m_checkSetRecipient = new wxCheckBox(panel, wxID_ANY, labels[0]);
+   m_textRecipient = new wxTextCtrl(panel, wxID_ANY);
+
+   m_checkSetSender = new wxCheckBox(panel, wxID_ANY, labels[1]);
+   m_textSender = new wxTextCtrl(panel, wxID_ANY);
+
+   c = new wxLayoutConstraints;
+   c->top.Below(msg, LAYOUT_Y_MARGIN);
+   c->right.SameAs(box, wxRight, 2*LAYOUT_X_MARGIN);
+   c->left.SameAs(box, wxLeft,
+                  2*LAYOUT_X_MARGIN + widthMax + LAYOUT_X_MARGIN);
+   c->height.AsIs();
+   m_textRecipient->SetConstraints(c);
+
+   c = new wxLayoutConstraints;
+   c->centreY.SameAs(m_textRecipient, wxCentreY);
+   c->left.SameAs(box, wxLeft, 2*LAYOUT_X_MARGIN);
+   c->width.Absolute(widthMax);
+   c->height.AsIs();
+   m_checkSetRecipient->SetConstraints(c);
+
+   c = new wxLayoutConstraints;
+   c->top.Below(m_textRecipient, LAYOUT_Y_MARGIN);
+   c->right.SameAs(box, wxRight, 2*LAYOUT_X_MARGIN);
+   c->left.SameAs(box, wxLeft,
+                  2*LAYOUT_X_MARGIN + widthMax + LAYOUT_X_MARGIN);
+   c->height.AsIs();
+   m_textSender->SetConstraints(c);
+
+   c = new wxLayoutConstraints;
+   c->centreY.SameAs(m_textSender, wxCentreY);
+   c->left.SameAs(box, wxLeft, 2*LAYOUT_X_MARGIN);
+   c->width.Absolute(widthMax);
+   c->height.AsIs();
+   m_checkSetSender->SetConstraints(c);
+
+   m_checkSetRecipient->Bind(wxEVT_UPDATE_UI,
+      &wxQuickFilterDialog::OnUpdateDepOnTargetFolder, this
+   );
+   m_checkSetRecipient->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED,
+      &wxQuickFilterDialog::OnSetRecipientCheck, this
+   );
+   m_textRecipient->Bind(wxEVT_UPDATE_UI,
+      &wxQuickFilterDialog::OnUpdateTextRecipient, this
+   );
+   m_checkSetSender->Bind(wxEVT_UPDATE_UI,
+      &wxQuickFilterDialog::OnUpdateDepOnTargetFolder, this
+   );
+   m_checkSetSender->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED,
+      &wxQuickFilterDialog::OnSetSenderCheck, this
+   );
+   m_textSender->Bind(wxEVT_UPDATE_UI,
+      &wxQuickFilterDialog::OnUpdateTextSender, this
+   );
+
    return box;
 }
 
@@ -2523,6 +2695,9 @@ bool wxQuickFilterDialog::TransferDataToWindow()
 
 bool wxQuickFilterDialog::TransferDataFromWindow()
 {
+   if ( !m_action->Validate() )
+      return false;
+
    // construct the object we use to initialize the filter
    MFDialogSettings *settings = NULL;
    String name = _("quick filter ");
@@ -2535,7 +2710,9 @@ bool wxQuickFilterDialog::TransferDataFromWindow()
    CHECK( settings, false,
           _T("the [Ok] button not supposed to be enabled in this case") );
 
-   settings->SetAction(m_action->GetAction(), m_action->GetArgument());
+   const MFDialogAction action = m_action->GetAction();
+   const wxString arg = m_action->GetArgument();
+   settings->SetAction(action, arg);
 
    MFilter_obj filter(name);
    MFilterDesc fd;
@@ -2558,6 +2735,29 @@ bool wxQuickFilterDialog::TransferDataFromWindow()
    // other (presumably more generic) filters
    m_folder->PrependFilter(name);
 
+
+   // Also update the folder options if requested:
+   if ( FilterActionUsesFolder(action) )
+   {
+      wxString recipient;
+      if ( m_checkSetRecipient->IsChecked() )
+         recipient = m_textRecipient->GetValue();
+
+      wxString sender;
+      if ( m_checkSetSender->IsChecked() )
+         sender = m_textSender->GetValue();
+
+      if ( !recipient.empty() || !sender.empty() )
+      {
+         Profile_obj profileTargetFolder(arg);
+
+         if ( !recipient.empty() )
+            profileTargetFolder->writeEntry(MP_COMPOSE_TO, recipient);
+         if ( !sender.empty() )
+            profileTargetFolder->writeEntry(MP_FROM_ADDRESS, sender);
+      }
+   }
+
    return true;
 }
 
@@ -2567,6 +2767,28 @@ wxQuickFilterDialog::~wxQuickFilterDialog()
    delete m_action;
 }
 
+void wxQuickFilterDialog::OnUpdateDepOnTargetFolder(wxUpdateUIEvent& event)
+{
+   event.Enable( FilterActionUsesFolder(m_action->GetAction()) );
+}
+
+void
+wxQuickFilterDialog::DoUpdateTextFromCheckbox(wxCheckBox* checkbox,
+                                              wxUpdateUIEvent& event)
+{
+   event.Enable( checkbox->IsThisEnabled() && checkbox->IsChecked() );
+}
+
+void wxQuickFilterDialog::OnUpdateTextRecipient(wxUpdateUIEvent& event)
+{
+   DoUpdateTextFromCheckbox(m_checkSetRecipient, event);
+}
+
+void wxQuickFilterDialog::OnUpdateTextSender(wxUpdateUIEvent& event)
+{
+   DoUpdateTextFromCheckbox(m_checkSetSender, event);
+}
+
 void wxQuickFilterDialog::OnText(wxCommandEvent& event)
 {
    if ( !m_action )
@@ -2592,6 +2814,29 @@ void wxQuickFilterDialog::OnText(wxCommandEvent& event)
    // some other button, e.g. the browse button of the folder entry: ignore it
 }
 
+void wxQuickFilterDialog::OnSetRecipientCheck(wxCommandEvent&)
+{
+   DoUpdateTextOnCheck(m_text[Filter_From], m_textRecipient);
+}
+
+void wxQuickFilterDialog::OnSetSenderCheck(wxCommandEvent& event)
+{
+   DoUpdateTextOnCheck(m_text[Filter_To], m_textSender);
+}
+
+void
+wxQuickFilterDialog::DoUpdateTextOnCheck(wxTextCtrl* textCopyFrom,
+                                         wxTextCtrl* textCopyTo)
+{
+   // To make life easier for the user, copy the value of the control that is
+   // likely to be used as a default for the new folder when the corresponding
+   // checkbox is clicked.
+
+   // Don't overwrite the user entry, if any.
+   if ( textCopyTo->IsEmpty() )
+      textCopyTo->SetValue(textCopyFrom->GetValue());
+}
+
 void wxQuickFilterDialog::OnUpdateOk(wxUpdateUIEvent& event)
 {
    // only enable the ok button if we have some condition
diff --git a/src/gui/wxMFolderDialogs.cpp b/src/gui/wxMFolderDialogs.cpp
index 2c3247c..02cef7c 100644
--- a/src/gui/wxMFolderDialogs.cpp
+++ b/src/gui/wxMFolderDialogs.cpp
@@ -2763,6 +2763,48 @@ MFolder* AskUserToCreateFolder(wxWindow *parent, MFolder* parentFolder)
    return newfolder;
 }
 
+MFolder* TryToCreateFolderOrAskUser(wxWindow* parent, const String& fullname)
+{
+   // First, determine the new folder parent.
+   wxString name;
+   wxString path = fullname.BeforeLast('/', &name);
+   MFolder_obj parentFolder(path);
+   if ( parentFolder )
+   {
+      // We could support automatic creation of folders of other types too but
+      // it's not very useful for MF_NNTP (for which it's simple) and it's not
+      // simple for MF_FILE (for which it could be useful) so don't bother.
+      if ( parentFolder->GetType() == MF_IMAP )
+      {
+         MFolder* const newFolder = MFolder::Create(fullname, MF_IMAP);
+         if ( newFolder )
+         {
+            // Set the path correctly for the new folder.
+            String fullpath = parentFolder->GetPath();
+            fullpath = MailFolder::GetLogicalMailboxName(fullpath);
+            fullpath += MailFolder::GetFolderDelimiter(parentFolder);
+            fullpath += name;
+
+            newFolder->SetPath(name);
+
+            // Notify all observers about the new folder creation.
+            MEventManager::Send(
+               new MEventFolderTreeChangeData(
+                  fullname,
+                  MEventFolderTreeChangeData::Create
+               )
+            );
+
+            return newFolder;
+         }
+      }
+   }
+   //else: We could try to find an existing parent of the parent and so on but
+   //      this doesn't seem very useful in practice so just give up.
+
+   return AskUserToCreateFolder(parent, parentFolder);
+}
+
 bool ShowFolderPropertiesDialog(MFolder *folder, wxWindow *parent)
 {
    wxFolderPropertiesDialog dlg(parent, folder);

commit e726af460110158cf85cbc8dc018ac3b4e1ebccc
Author: Vadim Zeitlin <[email protected]>
Date:   Mon Dec 26 23:15:15 2011 +0100

    Ensure that saved dialog geometry is not too small for new dialog size.
    
    When a dialog becomes better in a later version of M, its size saved by an
    older version may be insufficient for the new one. If this is the case,
    increase it to be big enough to avoid badly laid out dialogs after upgrade.

diff --git a/src/gui/wxDialogLayout.cpp b/src/gui/wxDialogLayout.cpp
index f9ad1c4..fe626cc 100644
--- a/src/gui/wxDialogLayout.cpp
+++ b/src/gui/wxDialogLayout.cpp
@@ -1133,17 +1133,35 @@ wxManuallyLaidOutDialog::wxManuallyLaidOutDialog(wxWindow *parent,
 void wxManuallyLaidOutDialog::SetDefaultSize(int width, int height,
                                              bool setAsMinimalSizeToo)
 {
-   if ( !LastSizeRestored() || setAsMinimalSizeToo )
+   // First of all, adjust the height before using it below to be not bigger
+   // than screen size as some of our dialogs may be quite tall.
+   int heightScreen = (9*wxGetDisplaySize().y) / 10;
+   if ( height > heightScreen )
    {
-      int heightScreen = (9*wxGetDisplaySize().y) / 10;
-      if ( height > heightScreen )
+      // don't create dialogs taller than the screen
+      height = heightScreen;
+   }
+
+   // Normally the previously size used the last time should be used in
+   // preference to the default size passed to this function.
+   bool useActualSize = LastSizeRestored();
+
+   // However there is an exception: if the dialog has become bigger, requiring
+   // a larger minimal size, in a later version of M, we don't want to show it
+   // using the previously saved too small size as it wouldn't allow to show it
+   // correctly.
+   if ( useActualSize && setAsMinimalSizeToo )
+   {
+      const wxSize size = GetClientSize();
+      if ( size.x < width || size.y < height )
       {
-         // don't create dialogs taller than the screen
-         height = heightScreen;
+         // Still try to preserve at least one user-set dimension, if possible.
+         SetClientSize(size.x < width ? width : size.x,
+                       size.y < height ? height : size.y);
       }
    }
 
-   if ( !LastSizeRestored() )
+   if ( !useActualSize )
    {
       SetClientSize(width, height);
 

commit ed3cc84b2a23b228924a8713ae4d2a013333d59b
Author: Vadim Zeitlin <[email protected]>
Date:   Mon Dec 26 17:16:46 2011 +0100

    No changes, just factor out duplicated code in AskUserToCreateFolder().
    
    Add new AskUserToCreateFolder() function that shows the folder creation wizard
    and, if the user dismisses it, the folder creation dialog. Reuse it in both
    wxFolderTree and wxMainFrame instead of duplicating the same code in both
    places.

diff --git a/include/MFolderDialogs.h b/include/MFolderDialogs.h
index 3028ec6..22354b4 100644
--- a/include/MFolderDialogs.h
+++ b/include/MFolderDialogs.h
@@ -52,7 +52,19 @@ enum FolderCreatePage
 // -----------------------------------------------------------------------------
 
 /**
-  shows folder creation dialog, returns a pointer to created folder or NULL.
+    Proposes to create a new folder with the given parent folder.
+
+    @param parent The window to use as the parent for the various dialogs.
+    @param parentFolder The folder under which the new one should be created.
+    @return The new folder to be DecRef()'d by called or NULL.
+ */
+extern MFolder* AskUserToCreateFolder(wxWindow* parent, MFolder* parentFolder);
+
+/**
+  Shows folder creation dialog, returns a pointer to created folder or NULL.
+
+  Use AskUserToCreateFolder() unless it's really the folder creation dialog and
+  not a higher level wizard that must be used.
 
   @param parentFolder is the default parent folder or NULL
   @return the returned folder object must be DecRef()d by the caller (if !NULL)
diff --git a/src/gui/wxFolderTree.cpp b/src/gui/wxFolderTree.cpp
index bcaac6c..a5124f5 100644
--- a/src/gui/wxFolderTree.cpp
+++ b/src/gui/wxFolderTree.cpp
@@ -1013,13 +1013,7 @@ MFolder *wxFolderTree::OnCreate(MFolder *parent)
 {
    wxWindow *winTop = ((wxMApp *)mApplication)->GetTopWindow();
 
-   bool wantsDialog;
-   MFolder *newfolder = RunCreateFolderWizard(&wantsDialog, parent, winTop);
-   if ( wantsDialog )
-   {
-      // users wants to use the dialog directly instead of the wizard
-      newfolder = ShowFolderCreateDialog(winTop, FolderCreatePage_Default, parent);
-   }
+   MFolder* const newfolder = AskUserToCreateFolder(winTop, parent);
 
    if ( parent )
       parent->DecRef();
diff --git a/src/gui/wxMFolderDialogs.cpp b/src/gui/wxMFolderDialogs.cpp
index b3b2ae0..2c3247c 100644
--- a/src/gui/wxMFolderDialogs.cpp
+++ b/src/gui/wxMFolderDialogs.cpp
@@ -2746,6 +2746,23 @@ MFolder *ShowFolderCreateDialog(wxWindow *parent,
    return DoShowFolderDialog(dlg, page);
 }
 
+MFolder* AskUserToCreateFolder(wxWindow *parent, MFolder* parentFolder)
+{
+   bool wantsDialog;
+   MFolder* newfolder = RunCreateFolderWizard(&wantsDialog,
+                                              parentFolder,
+                                              parent);
+   if ( wantsDialog )
+   {
+      // user wants to use the dialog directly instead of the wizard
+      newfolder = ShowFolderCreateDialog(parent,
+                                         FolderCreatePage_Default,
+                                         parentFolder);
+   }
+
+   return newfolder;
+}
+
 bool ShowFolderPropertiesDialog(MFolder *folder, wxWindow *parent)
 {
    wxFolderPropertiesDialog dlg(parent, folder);
diff --git a/src/gui/wxMainFrame.cpp b/src/gui/wxMainFrame.cpp
index 3c1c178..869de93 100644
--- a/src/gui/wxMainFrame.cpp
+++ b/src/gui/wxMainFrame.cpp
@@ -974,21 +974,7 @@ wxMainFrame::OnCommandEvent(wxCommandEvent &event)
             {
                MFolder_obj parent(m_FolderTree->GetSelection());
 
-               wxWindow *winTop = ((wxMApp *)mApplication)->GetTopWindow();
-               bool wantsDialog;
-               MFolder *newfolder = RunCreateFolderWizard(&wantsDialog,
-                                                          parent,
-                                                          winTop);
-               if ( wantsDialog )
-               {
-                  // users wants to use the dialog directly instead of the
-                  // wizard
-                  newfolder = ShowFolderCreateDialog(winTop,
-                                                     FolderCreatePage_Default,
-                                                     parent);
-               }
-
-               SafeDecRef(newfolder);
+               SafeDecRef(AskUserToCreateFolder(this, parent));
             }
             break;
 

commit 505d356859a136c926112c0576f19c090ce3a661
Author: Vadim Zeitlin <[email protected]>
Date:   Mon Dec 26 16:42:07 2011 +0100

    Fix TAB order in the "Quick filter" dialog.
    
    The checkbox should precede the corresponding text controls in TAB order as
    they're on the left hand side of the dialogs and the text controls are on the
    right hand side.

diff --git a/src/gui/wxFiltersDialog.cpp b/src/gui/wxFiltersDialog.cpp
index 96272d1..089c2be 100644
--- a/src/gui/wxFiltersDialog.cpp
+++ b/src/gui/wxFiltersDialog.cpp
@@ -2421,7 +2421,13 @@ wxWindow *wxQuickFilterDialog::CreateMainWindow(wxPanel *panel)
 
    for ( size_t n = 0; n < Filter_Max; n++ )
    {
+      // Notice that we should create the controls in the correct, i.e.
+      // corresponding to TAB navigation, order, so create the checkbox first,
+      // even if it's more convenient to set the constraints for the text
+      // control first.
+      m_check[n] = new wxCheckBox(panel, -1, labels[n]);
       m_text[n] = new wxTextCtrl(panel, -1, text[n]);
+
       c = new wxLayoutConstraints;
 
       if ( n == 0 )
@@ -2441,7 +2447,6 @@ wxWindow *wxQuickFilterDialog::CreateMainWindow(wxPanel *panel)
       c->height.AsIs();
       m_text[n]->SetConstraints(c);
 
-      m_check[n] = new wxCheckBox(panel, -1, labels[n]);
       c = new wxLayoutConstraints;
       c->centreY.SameAs(m_text[n], wxCentreY);
       c->left.SameAs(box, wxLeft, 2*LAYOUT_X_MARGIN);

commit 3cb56e222271c6f9fdb4e04e15f9e945f7f45969
Author: Vadim Zeitlin <[email protected]>
Date:   Mon Dec 26 16:38:56 2011 +0100

    Ensure that the MailFolder opened in the main frame is associated with it.
    
    Call MailFolder::SetInteractiveFrame() explicitly when opening a folder in the
    main frame. While the interactive frame was set correctly when the folder was
    opened the first time, it could remain NULL if a folder was opened, then the
    view switched to another one and returned to the original folder without
    reopening it because it remained open in the background because of the "keep
    open" option set for it.

diff --git a/src/gui/wxMainFrame.cpp b/src/gui/wxMainFrame.cpp
index 9d63a00..3c1c178 100644
--- a/src/gui/wxMainFrame.cpp
+++ b/src/gui/wxMainFrame.cpp
@@ -796,8 +796,13 @@ wxMainFrame::OpenFolder(MFolder *pFolder, bool readonly)
 
       m_folderName.clear();
    }
-   else // select the folder on screen as well
+   else // folder opened successfully
    {
+      // Associate it with this frame to e.g. let it use our status bar for any
+      // messages.
+      MailFolder_obj(m_FolderView->GetMailFolder())->SetInteractiveFrame(this);
+
+      // Select the folder in the tree as well.
       m_FolderTree->SelectFolder(folder);
    }
 

commit 5a34a59fca7029a9161c66b6d0a0bd0d63f32eae
Author: Vadim Zeitlin <[email protected]>
Date:   Sun Dec 25 17:41:31 2011 +0100

    Fix memory leaks in wxQuickFilterDialog.
    
    m_action was never freed, do delete it now.

diff --git a/src/gui/wxFiltersDialog.cpp b/src/gui/wxFiltersDialog.cpp
index 1444b70..96272d1 100644
--- a/src/gui/wxFiltersDialog.cpp
+++ b/src/gui/wxFiltersDialog.cpp
@@ -2559,6 +2559,7 @@ bool wxQuickFilterDialog::TransferDataFromWindow()
 wxQuickFilterDialog::~wxQuickFilterDialog()
 {
    m_folder->DecRef();
+   delete m_action;
 }
 
 void wxQuickFilterDialog::OnText(wxCommandEvent& event)

commit 7f2041d78bd6c72e9e7cc4e735271bfa8c28488a
Author: Vadim Zeitlin <[email protected]>
Date:   Sun Dec 25 17:40:17 2011 +0100

    Don't show "Apply" button in the "Quick filter" dialog.
    
    This button doesn't make any sense there as we either create the filter or do
    not.
    
    Add ProfileEdit_WithoutApply flag to wxProfileSettingsEditDialog and use it
    from wxQuickFilterDialog.

diff --git a/include/gui/wxDialogLayout.h b/include/gui/wxDialogLayout.h
index 0623e2b..d69647b 100644
--- a/include/gui/wxDialogLayout.h
+++ b/include/gui/wxDialogLayout.h
@@ -215,7 +215,12 @@ protected:
    // must call this from the derived class ctor to create the main window and
    // the standard Ok/Cancel/Apply buttons, calls CreateControlsAbove/Below()
    // and CreateMainWindow() which may be overridden in the derived classes
-   void CreateAllControls();
+   enum
+   {
+      ProfileEdit_WithoutApply = 0,
+      ProfileEdit_WithApply = 1,
+   };
+   void CreateAllControls(int flags = ProfileEdit_WithApply);
 
    // call this after creating a new profile (as is done by the folder creation
    // dialog which only creates the profile when the folder itself is being
diff --git a/src/gui/wxDialogLayout.cpp b/src/gui/wxDialogLayout.cpp
index f1ca15e..f9ad1c4 100644
--- a/src/gui/wxDialogLayout.cpp
+++ b/src/gui/wxDialogLayout.cpp
@@ -1308,7 +1308,7 @@ wxControl *wxProfileSettingsEditDialog::CreateControlsBelow(wxPanel *panel)
    return m_chcSources;
 }
 
-void wxProfileSettingsEditDialog::CreateAllControls()
+void wxProfileSettingsEditDialog::CreateAllControls(int flags)
 {
    wxLayoutConstraints *c;
 
@@ -1367,10 +1367,13 @@ void wxProfileSettingsEditDialog::CreateAllControls()
    c->bottom.SameAs(panel, wxBottom, LAYOUT_Y_MARGIN);
    m_btnHelp->SetConstraints(c);
 
+   // "OK" and "Cancel" are always shown but "Apply" is optional.
+   int numButtons = flags & ProfileEdit_WithApply ? 3 : 2;
+
    m_btnOk = new wxButton(panel, wxID_OK, _("OK"));
    m_btnOk->SetDefault();
    c = new wxLayoutConstraints;
-   c->left.SameAs(panel, wxRight, -3*(LAYOUT_X_MARGIN + wBtn));
+   c->left.SameAs(panel, wxRight, -numButtons*(LAYOUT_X_MARGIN + wBtn));
    c->width.Absolute(wBtn);
    c->height.Absolute(hBtn);
    c->bottom.SameAs(panel, wxBottom, LAYOUT_Y_MARGIN);
@@ -1378,19 +1381,26 @@ void wxProfileSettingsEditDialog::CreateAllControls()
 
    wxButton *btn = new wxButton(panel, wxID_CANCEL, _("Cancel"));
    c = new wxLayoutConstraints;
-   c->left.SameAs(panel, wxRight, -2*(LAYOUT_X_MARGIN + wBtn));
+   c->left.SameAs(panel, wxRight, -(numButtons - 1)*(LAYOUT_X_MARGIN + wBtn));
    c->width.Absolute(wBtn);
    c->height.Absolute(hBtn);
    c->bottom.SameAs(panel, wxBottom, LAYOUT_Y_MARGIN);
    btn->SetConstraints(c);
 
-   m_btnApply = new wxButton(panel, wxID_APPLY, _("&Apply"));
-   c = new wxLayoutConstraints;
-   c->left.SameAs(panel, wxRight, -(LAYOUT_X_MARGIN + wBtn));
-   c->width.Absolute(wBtn);
-   c->height.Absolute(hBtn);
-   c->bottom.SameAs(panel, wxBottom, LAYOUT_Y_MARGIN);
-   m_btnApply->SetConstraints(c);
+   if ( flags & ProfileEdit_WithApply )
+   {
+      m_btnApply = new wxButton(panel, wxID_APPLY, _("&Apply"));
+      c = new wxLayoutConstraints;
+      c->left.SameAs(panel, wxRight, -(LAYOUT_X_MARGIN + wBtn));
+      c->width.Absolute(wBtn);
+      c->height.Absolute(hBtn);
+      c->bottom.SameAs(panel, wxBottom, LAYOUT_Y_MARGIN);
+      m_btnApply->SetConstraints(c);
+   }
+   else
+   {
+      m_btnApply = NULL;
+   }
 
    // set dialog size (FIXME these are more or less arbitrary numbers)
    SetDefaultSize(6*wBtn, 27*hBtn, TRUE /* set as min size too */);
diff --git a/src/gui/wxFiltersDialog.cpp b/src/gui/wxFiltersDialog.cpp
index 800830e..1444b70 100644
--- a/src/gui/wxFiltersDialog.cpp
+++ b/src/gui/wxFiltersDialog.cpp
@@ -2382,7 +2382,7 @@ wxQuickFilterDialog::wxQuickFilterDialog(MFolder *folder,
    // the dialog
    m_action = NULL;
 
-   CreateAllControls();
+   CreateAllControls(ProfileEdit_WithoutApply);
 
    SetDefaultSize(8*wBtn, 13*hBtn);
 

commit f7e73ff5f864c394fc5f3e27263e2e31df951c6d
Author: Vadim Zeitlin <[email protected]>
Date:   Sat Dec 24 23:53:49 2011 +0100

    Work around MSVC bug with std::set and map and CRT memory debugging.
    
    Including <set> or <map> standard headers after including <crtdbg.h> results
    in compilation errors, so we must include the former headers before including
    wx/msw/msvcrt.h that includes the latter one if we want to be able to use them
    at all.

diff --git a/include/Mpch.h b/include/Mpch.h
index 5dfe13a..468333d 100644
--- a/include/Mpch.h
+++ b/include/Mpch.h
@@ -63,7 +63,12 @@
    // reported by MSVC CRT much easier.
    //
    // Notice that this must be done after including the standard headers which
-   // sometimes redefine operator new themselves.
+   // sometimes redefine operator new themselves and, worse, map and set can't
+   // be included after doing this as it redefines some of the identifiers used
+   // in them, so include them from here proactively.
+   #include <map>
+   #include <set>
+
    #include <wx/msw/msvcrt.h>
 #endif
 

commit 797a401af8801d494f3504c29665e8ba91863e43
Author: Vadim Zeitlin <[email protected]>
Date:   Sat Dec 24 22:49:43 2011 +0100

    No real changes, just added more debug/tracing code.
    
    Add debug menu items to simulate suspend/resume and to view the currently
    opened folders.
    
    Also add yet another trace message to the folder keep alive code.

diff --git a/src/gui/wxMainFrame.cpp b/src/gui/wxMainFrame.cpp
index f540948..9d63a00 100644
--- a/src/gui/wxMainFrame.cpp
+++ b/src/gui/wxMainFrame.cpp
@@ -55,6 +55,10 @@
 // view in the frame - with current wxGTK it doesn't work at all, so disabling
 #undef HAS_DYNAMIC_MENU_SUPPORT
 
+#ifdef DEBUG
+   #include "mail/FolderPool.h"
+#endif // DEBUG
+
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
@@ -67,7 +71,12 @@ enum
    WXMENU_DEBUG_SHOW_LICENCE,
    WXMENU_DEBUG_TRACE,
    WXMENU_DEBUG_TOGGLE_LOG,
-   WXMENU_DEBUG_CRASH
+   WXMENU_DEBUG_CRASH,
+#ifdef wxHAS_POWER_EVENTS
+   WXMENU_DEBUG_SUSPEND,
+   WXMENU_DEBUG_RESUME,
+#endif // wxHAS_POWER_EVENTS
+   WXMENU_DEBUG_VIEW_OPENED
 };
 
 #endif // DEBUG
@@ -679,6 +688,14 @@ wxMainFrame::wxMainFrame(const String &iname, wxFrame *parent)
    menuDebug->AppendCheckItem(WXMENU_DEBUG_TOGGLE_LOG,
                               _T("Toggle &debug logging\tCtrl-Alt-D"));
    menuDebug->Append(WXMENU_DEBUG_CRASH, _T("Provoke a c&rash"));
+#ifdef wxHAS_POWER_EVENTS
+   menuDebug->AppendSeparator();
+   menuDebug->Append(WXMENU_DEBUG_SUSPEND, "Simulate &suspend");
+   menuDebug->Append(WXMENU_DEBUG_RESUME, "Simulate &resume");
+#endif // wxHAS_POWER_EVENTS
+   menuDebug->AppendSeparator();
+   menuDebug->Append(WXMENU_DEBUG_VIEW_OPENED, "View &opened folders");
+
    GetMenuBar()->Append(menuDebug, _T("&Debug"));
 #endif // debug
 
@@ -1180,6 +1197,57 @@ wxMainFrame::OnCommandEvent(wxCommandEvent &event)
             *(char *)17 = '!';
             break;
 
+#ifdef wxHAS_POWER_EVENTS
+         case WXMENU_DEBUG_SUSPEND:
+            {
+               wxPowerEvent dummyEvent;
+               OnPowerSuspended(dummyEvent);
+            }
+            break;
+
+         case WXMENU_DEBUG_RESUME:
+            {
+               wxPowerEvent dummyEvent;
+               OnPowerResume(dummyEvent);
+            }
+            break;
+#endif // wxHAS_POWER_EVENTS
+
+         case WXMENU_DEBUG_VIEW_OPENED:
+            {
+               wxArrayString folderNames;
+
+               MFPool::Cookie cookie;
+               MFolder *folder = NULL;
+               for ( MailFolder *mf = MFPool::GetFirst(cookie, NULL, &folder);
+                     mf;
+                     mf = MFPool::GetNext(cookie, NULL, &folder) )
+               {
+                  folderNames.push_back(folder->GetFullName());
+
+                  folder->DecRef();
+                  mf->DecRef();
+               }
+
+               if ( folderNames.empty() )
+               {
+                  wxLogMessage("No folders are currently opened.");
+               }
+               else
+               {
+                  const unsigned count = folderNames.size();
+                  wxString msg;
+                  msg.Printf("The following %u folders are opened:", count);
+                  for ( unsigned n = 0; n < count; n++ )
+                  {
+                     msg << "\n    " << folderNames[n];
+                  }
+
+                  wxLogMessage("%s", msg);
+               }
+            }
+            break;
+
          default:
             FAIL_MSG( _T("unknown debug menu command?") );
       }
diff --git a/src/mail/MailFolderCmn.cpp b/src/mail/MailFolderCmn.cpp
index 2f8317f..1345bd4 100644
--- a/src/mail/MailFolderCmn.cpp
+++ b/src/mail/MailFolderCmn.cpp
@@ -527,7 +527,13 @@ void MailFolderCmn::Close(bool /* mayLinger */)
    }
 
    if ( m_keepAliveTimer )
+   {
+      wxLogTrace(TRACE_MF_KEEPALIVE,
+                 "Keep alive timer for \"%s\" stopped on folder close at %s",
+                 GetName(),
+                 TimestampWithMS());
       m_keepAliveTimer->Stop();
+   }
 }
 
 bool

commit a1827a1d89b7e136bf8cf4b29e5e9c93ef2211e8
Author: Vadim Zeitlin <[email protected]>
Date:   Sat Dec 24 22:25:43 2011 +0100

    Fix the size of the "About" dialog in splash screen bitmap absence.
    
    This is not supposed to happen, but if the splash screen bitmap couldn't be
    loaded, still make the about dialog of a reasonable size and not reduce it to
    the default 20*20 size.

diff --git a/src/gui/wxMSplash.cpp b/src/gui/wxMSplash.cpp
index 18f3f93..7074093 100644
--- a/src/gui/wxMSplash.cpp
+++ b/src/gui/wxMSplash.cpp
@@ -167,10 +167,14 @@ private:
 };
 
 AboutWindow::AboutWindow(wxFrame *parent, wxBitmap bmp, bool bCloseOnTimeout)
-           : wxWindow(parent, -1, wxDefaultPosition,
-                      wxSize(bmp.GetWidth(), 2*bmp.GetHeight()))
 {
-   const wxSize sizeBmp(bmp.GetWidth(), bmp.GetHeight());
+   // Use fall back size if the splash screen image is not available because
+   // otherwise the entire window would be too small.
+   const wxSize sizeBmp(bmp.IsOk() ? bmp.GetSize() : wxSize(400, 300));
+
+   wxWindow::Create(parent, -1, wxDefaultPosition,
+                    wxSize(sizeBmp.x, 2*sizeBmp.y));
+
    wxStaticBitmap *top = new wxStaticBitmap(this, wxID_ANY, bmp,
                                             wxPoint(0, 0), sizeBmp);
    wxHtmlWindow *bottom = new wxHtmlWindow(this, wxID_ANY,

-----------------------------------------------------------------------

Summary of changes:
 CHANGES                      |    1 +
 doc/Manual.htex              |    5 +
 include/MFolderDialogs.h     |   28 +++++-
 include/MailFolder.h         |   38 +++++--
 include/MailFolderCC.h       |    8 +-
 include/MailFolderCmn.h      |    8 +-
 include/Mpch.h               |    7 +-
 include/MpersIds.h           |    1 +
 include/gui/wxDialogLayout.h |    7 +-
 include/gui/wxMainFrame.h    |   12 +-
 include/mail/VFolder.h       |   17 +++
 src/classes/Mpers.cpp        |    1 +
 src/gui/wxDialogLayout.cpp   |   60 +++++++---
 src/gui/wxFiltersDialog.cpp  |  259 +++++++++++++++++++++++++++++++++++++++++-
 src/gui/wxFolderTree.cpp     |    8 +-
 src/gui/wxMFolderDialogs.cpp |   59 ++++++++++
 src/gui/wxMSplash.cpp        |   10 +-
 src/gui/wxMainFrame.cpp      |  187 +++++++++++++++++++-----------
 src/mail/MailFolderCC.cpp    |   28 +++++
 src/mail/MailFolderCmn.cpp   |   28 ++++-
 src/mail/VFolder.cpp         |   47 ++++++++
 21 files changed, 694 insertions(+), 125 deletions(-)


hooks/post-receive
-- 
Mahogany soruces repository.

------------------------------------------------------------------------------
Ridiculously easy VDI. With Citrix VDI-in-a-Box, you don't need a complex
infrastructure or vast IT resources to deliver seamless, secure access to
virtual desktops. With this all-in-one solution, easily deploy virtual 
desktops for less than the cost of PCs and save 60% on VDI infrastructure 
costs. Try it free! http://p.sf.net/sfu/Citrix-VDIinabox