Update of /cvsroot/mahogany/M/src/modules
In directory sc8-pr-cvs1:/tmp/cvs-serv10991/src/modules
Modified Files:
Filters.cpp
Log Message:
patch from Robert to avoid losing messages when opening the target folder was cancelled
Index: Filters.cpp
===================================================================
RCS file: /cvsroot/mahogany/M/src/modules/Filters.cpp,v
retrieving revision 1.147
retrieving revision 1.148
diff -b -u -2 -r1.147 -r1.148
--- Filters.cpp 22 Jul 2003 22:01:42 -0000 1.147
+++ Filters.cpp 3 Aug 2003 23:55:19 -0000 1.148
@@ -95,4 +95,5 @@
class Token;
class Value;
+class FilterRuleApply;
/// Type for functions to be called.
@@ -335,4 +336,6 @@
m_hasHeaderFunc;
+ friend class FilterRuleApply;
+
GCC_DTOR_WARN_OFF
};
@@ -431,5 +434,5 @@
// Can't use union here because m_String needs constructor.
long m_Num;
- const String m_String;
+ String m_String;
// if this flag is set, it means that the filter evaluation must be aborted
@@ -437,10 +440,57 @@
bool m_abort;
- // not needed nor implemented for now
- Value& operator=(const Value&);
-
MOBJECT_NAME(Value)
};
+// an object of this class is created to apply the given rule to the specified
+// messages, it is a sort of "filter runner"
+class FilterRuleApply
+{
+public:
+ FilterRuleApply(FilterRuleImpl *parent, UIdArray& msgs);
+ ~FilterRuleApply();
+
+ int Run();
+
+private:
+ bool LoopEvaluate();
+ bool LoopCopy();
+ bool DeleteAll();
+
+ void CreateProgressDialog();
+ bool GetMessage();
+ bool UpdateProgressDialog();
+ void HeaderCacheHints();
+ bool Evaluate();
+ bool ProgressResults();
+ bool ProgressCopy();
+ bool CopyToOneFolder();
+ void CollectForDelete();
+ void ProgressDelete();
+ void IndicateDeleted();
+
+ FilterRuleImpl *const m_parent;
+ UIdArray& m_msgs;
+
+ MProgressDialog *m_pd;
+ wxArrayInt m_allOperations;
+ wxArrayString m_destinations;
+ bool m_doExpunge;
+ UIdArray m_uidsToDelete;
+
+ // the array holding the indices of the messages deleted by filters
+ wxArrayLong m_indicesDeleted;
+
+ // Index of actual message in m_messageList
+ size_t m_idx;
+
+ // the text for the progress dialog (verbose) and for the log (terse)
+ String m_textPD,
+ m_textLog;
+
+ // Result of evaluating filter
+ Value m_retval;
+};
+
// ----------------------------------------------------------------------------
// ABC for syntax tree nodes.
@@ -3071,5 +3121,4 @@
// ----------------------------------------------------------------------------
-// TODO: this function should be factorized into several smaller ones
int
FilterRuleImpl::Apply(MailFolder *mf, UIdArray& msgs)
@@ -3089,23 +3138,224 @@
m_MailFolder->IncRef();
- // the array holding the indices of the messages deleted by filters
- wxArrayLong indicesDeleted;
+ FilterRuleApply apply(this, msgs);
+
+ rc = apply.Run();
+
+ m_MailFolder->DecRef();
+ m_MailFolder = NULL;
+ }
+#endif // !TEST
+
+ return rc;
+}
+
+FilterRuleImpl::FilterRuleImpl(const String &filterrule,
+ MInterface *minterface,
+ MModule_Filters *mod)
+ : m_FilterModule(mod),
+ m_MInterface(minterface)
+{
+#ifndef TEST
+ // we cannot allow the module to disappear while we exist
+ m_FilterModule->IncRef();
+#endif
+
+ m_hasToFunc =
+ m_hasRcptFunc =
+ m_hasHdrLineFunc =
+ m_hasHeaderFunc = false;
+
+ m_Program = Parse(filterrule);
+ m_MessageUId = UID_ILLEGAL;
+ m_MailMessage = NULL;
+ m_MailFolder = NULL;
+}
+
+FilterRuleImpl::~FilterRuleImpl()
+{
+ SafeDecRef(m_MailFolder);
+ delete m_Program;
+#ifndef TEST
+ m_FilterModule->DecRef();
+#endif
+}
+
+#ifdef DEBUG
+
+void FilterRuleImpl::Debug(void)
+{
+ Output(m_Program->Debug());
+}
+
+#endif // DEBUG
+
+// ----------------------------------------------------------------------------
+// FilterRuleApply
+// ----------------------------------------------------------------------------
+
+FilterRuleApply::FilterRuleApply(FilterRuleImpl *parent, UIdArray& msgs)
+ : m_parent(parent), m_msgs(msgs)
+{
+ m_pd = NULL;
+ m_doExpunge = false;
+}
+
+FilterRuleApply::~FilterRuleApply()
+{
+ delete m_pd;
+}
+
+int
+FilterRuleApply::Run()
+{
+ // no error yet
+ int rc = 0;
+
+ CreateProgressDialog();
+
+ if( !LoopEvaluate() )
+ {
+ rc |= FilterRule::Error;
+ }
+
+ // check if Cancel wasn't pressed (we'd exit the loop above by break then)
+ if ( m_idx == m_msgs.GetCount() &&
+ (!m_pd || m_pd->Update(m_msgs.GetCount(),
+ _("Executing filter actions..."))) )
+ {
+ if ( !LoopCopy() )
+ {
+ rc |= FilterRule::Error;
+ }
+
+ // again, stop right now if we were cancelled
+ if ( m_idx == m_msgs.GetCount() )
+ {
+ if ( !DeleteAll() )
+ {
+ rc |= FilterRule::Error;
+ }
+ else // deleted successfully
+ {
+ rc |= FilterRule::Deleted;
+ }
+
+ // actual expunging will be done by the calling code
+ if ( m_doExpunge )
+ {
+ rc |= FilterRule::Expunged;
+ }
+ }
+ }
+ //else: cancelled by user
+
+ return rc;
+}
+
+bool
+FilterRuleApply::LoopEvaluate()
+{
+ bool allOk = true;
+
+ // first decide what should we do with the messages: fill the arrays with
+ // the operations to perform and the destination folder if the operation
+ // involves copying the message
+ for ( m_idx = 0; m_idx < m_msgs.GetCount(); m_idx++ )
+ {
+ // do it first so that the arrays have the right size even if we hit
+ // "continue" below
+ m_allOperations.Add(FilterRuleImpl::None);
+ m_destinations.Add("");
+
+ if ( !GetMessage() )
+ {
+ continue;
+ }
+
+ if ( !UpdateProgressDialog() )
+ {
+ m_parent->m_MailMessage->DecRef();
+
+ break;
+ }
+
+ HeaderCacheHints();
+
+ if ( !Evaluate() )
+ {
+ allOk = false;
+ }
+
+ if ( !ProgressResults() )
+ {
+ break;
+ }
+ }
+
+ return allOk;
+}
+
+bool
+FilterRuleApply::LoopCopy()
+{
+ bool allOk = true;
+
+ for ( m_idx = 0; m_idx < m_msgs.GetCount(); m_idx++ )
+ {
+ if ( m_allOperations[m_idx] & FilterRuleImpl::Copy )
+ {
+ if ( !ProgressCopy() )
+ {
+ // cancelled by user
+ break;
+ }
+
+ if ( !CopyToOneFolder() )
+ {
+ allOk = false;
+ }
+ }
+ }
+
+ return allOk;
+}
+
+bool
+FilterRuleApply::DeleteAll()
+{
+ CollectForDelete();
+
+ if ( !m_uidsToDelete.IsEmpty() )
+ {
+ ProgressDelete();
+
+ if ( !m_parent->m_MailFolder->DeleteMessages(&m_uidsToDelete) )
+ {
+ return false;
+ }
+
+ // deleted successfully
+ IndicateDeleted();
+ }
- // show the progress dialog while filtering
- size_t count = msgs.GetCount();
+ return true;
+}
- wxFrame *frame = m_MailFolder->GetInteractiveFrame();
- MProgressDialog *pd;
+void
+FilterRuleApply::CreateProgressDialog()
+{
+ wxFrame *frame = m_parent->m_MailFolder->GetInteractiveFrame();
if ( frame )
{
- pd = new MProgressDialog
+ m_pd = new MProgressDialog
(
wxString::Format
(
_("Filtering %u messages in folder '%s'..."),
- count, m_MailFolder->GetName().c_str()
+ m_msgs.GetCount(),
+ m_parent->m_MailFolder->GetName().c_str()
),
"\n\n", // must be tall enough for 3 lines
- 2*count,
+ 2*m_msgs.GetCount(),
frame,
false, // !"disable parent only"
@@ -3113,48 +3363,38 @@
);
}
- else // don't show the progress dialog, just log in the status bar
- {
- pd = NULL;
- }
-
- // first decide what should we do with the messages: fill the arrays with
- // the operations to perform and the destination folder if the operation
- // involves copying the message
- wxArrayInt operations;
- wxArrayString destinations;
-
- bool doExpunge = false;
-
- // the text for the progress dialog (verbose) and for the log (terse)
- String textPD,
- textLog;
- size_t idx;
- for ( idx = 0; idx < count; idx++ )
- {
- // do it first so that the arrays have the right size even if we hit
- // "continue" below
- operations.Add(None);
- destinations.Add("");
+ // else: don't show the progress dialog, just log in the status bar
+}
- m_MessageUId = msgs[idx];
+bool
+FilterRuleApply::GetMessage()
+{
+ m_parent->m_MessageUId = m_msgs[m_idx];
- if ( m_MessageUId == UID_ILLEGAL )
+ if ( m_parent->m_MessageUId == UID_ILLEGAL )
{
FAIL_MSG( _T("invalid UID in FilterRule::Apply()!") );
- continue;
+ return false;
}
- m_MailMessage = m_MailFolder->GetMessage(m_MessageUId);
+ m_parent->m_MailMessage
+ = m_parent->m_MailFolder->GetMessage(m_parent->m_MessageUId);
- if ( !m_MailMessage )
+ if ( !m_parent->m_MailMessage )
{
// maybe another session deleted it?
wxLogDebug(
_T("Filter error: message with UID %ld in folder '%s' doesn't exist
any more."),
- m_MessageUId, m_MailFolder->GetName().c_str());
- continue;
+ m_parent->m_MessageUId,
+ m_parent->m_MailFolder->GetName().c_str());
+ return false;
}
+ return true;
+}
+
+bool
+FilterRuleApply::UpdateProgressDialog()
+{
// update the GUI
@@ -3162,46 +3402,46 @@
// configurable
- String subject = MailFolder::DecodeHeader(m_MailMessage->Subject()),
- from = MailFolder::DecodeHeader(m_MailMessage->From());
+ String subject = MailFolder::DecodeHeader(
+ m_parent->m_MailMessage->Subject());
+ String from = MailFolder::DecodeHeader(m_parent->m_MailMessage->From());
- textLog.Printf(_("Filtering message %u/%u"), idx + 1, count);
+ m_textLog.Printf(_("Filtering message %u/%u"),
+ m_idx + 1, m_msgs.GetCount());
// make a multiline label for the progress dialog and a more concise
// one for the status bar
- if ( pd )
+ m_textPD.clear();
+ if ( m_pd )
{
- textPD.clear();
- textPD << textLog << '\n'
+ m_textPD << m_textLog << '\n'
<< _("From: ") << from << '\n'
<< _("Subject: ") << subject;
}
- textLog << " (";
+ m_textLog << " (";
if ( !from.empty() )
{
- textLog << _("from ") << from << ' ';
+ m_textLog << _("from ") << from << ' ';
}
if ( !subject.empty() )
{
- textLog << _("about '") << subject << '\'';
+ m_textLog << _("about '") << subject << '\'';
}
else
{
- textLog << _("without subject");
+ m_textLog << _("without subject");
}
- textLog << ')';
+ m_textLog << ')';
// and use both of them
- if ( pd )
+ if ( m_pd )
{
- if ( !pd->Update(idx, textPD) )
+ if ( !m_pd->Update(m_idx, m_textPD) )
{
// cancelled by user
- m_MailMessage->DecRef();
-
- break;
+ return false;
}
}
@@ -3210,7 +3450,13 @@
// don't pass it as the first argument because the string might
// contain '%' characters!
- wxLogStatus("%s", textLog.c_str());
+ wxLogStatus("%s", m_textLog.c_str());
}
+ return true;
+}
+
+void
+FilterRuleApply::HeaderCacheHints()
+{
// do some heuristic optimizations: if our program contains requests
// for the entire message header, get it first because like this it
@@ -3221,43 +3467,54 @@
// in the same way, retrieve all the recipients if we need them anyhow
// before retrieving "To" &c
- if ( m_hasHeaderFunc )
+ if ( m_parent->m_hasHeaderFunc )
{
- if ( m_hasToFunc || m_hasRcptFunc || m_hasHdrLineFunc )
+ if ( m_parent->m_hasToFunc || m_parent->m_hasRcptFunc
+ || m_parent->m_hasHdrLineFunc )
{
// pre-retrieve the whole header
- (void)m_MailMessage->GetHeader();
+ (void)m_parent->m_MailMessage->GetHeader();
}
}
- else if ( m_hasRcptFunc )
+ else if ( m_parent->m_hasRcptFunc )
{
- if ( m_hasToFunc )
+ if ( m_parent->m_hasToFunc )
{
// pre-fetch the recipient headers which include "To"
- (void)m_MailMessage->GetHeaderLines(headersRecipients);
+ (void)m_parent->m_MailMessage->GetHeaderLines(headersRecipients);
}
}
+}
+bool
+FilterRuleApply::Evaluate()
+{
// reset the result flags
- m_operation = None;
+ m_parent->m_operation = FilterRuleImpl::None;
- const Value retval = m_Program->Evaluate();
+ m_retval = m_parent->m_Program->Evaluate();
// remember the result
- operations[idx] = m_operation;
- destinations[idx] = m_copiedTo;
+ m_allOperations[m_idx] = m_parent->m_operation;
+ m_destinations[m_idx] = m_parent->m_copiedTo;
- if ( m_operation & Expunge )
+ if ( m_parent->m_operation & FilterRuleImpl::Expunge )
{
- doExpunge = true;
+ m_doExpunge = true;
}
+ m_parent->m_MailMessage->DecRef();
+
+ return m_retval.IsNumber();
+}
+
+bool
+FilterRuleApply::ProgressResults()
+{
// and show the result in the progress dialog
String textExtra = " - ";
- if ( !retval.IsNumber() )
+ if ( !m_retval.IsNumber() )
{
textExtra << _("error!");
-
- rc |= FilterRule::Error;
}
else // filter executed ok
@@ -3274,11 +3531,12 @@
}
- bool wasDeleted = (m_operation & Deleted) != 0;
- if ( !m_copiedTo.empty() )
+ bool wasDeleted = (m_parent->m_operation
+ & FilterRuleImpl::Deleted) != 0;
+ if ( !m_parent->m_copiedTo.empty() )
{
textExtra << (wasDeleted ? _("moved to ") : _("copied to "))
- << m_copiedTo;
+ << m_parent->m_copiedTo;
- m_copiedTo.clear();
+ m_parent->m_copiedTo.clear();
}
else if ( wasDeleted )
@@ -3292,14 +3550,12 @@
}
- textLog += textExtra;
-
- m_MailMessage->DecRef();
+ m_textLog += textExtra;
- if ( pd )
+ if ( m_pd )
{
- if ( !pd->Update(idx, textPD + textExtra) )
+ if ( !m_pd->Update(m_idx, m_textPD + textExtra) )
{
// cancelled by user
- break;
+ return false;
}
@@ -3309,23 +3565,18 @@
// NB: textLog may contain '%'s itself, so don't let it be
// interpreted as a format string
- wxLogGeneric(M_LOG_WINONLY, "%s", textLog.c_str());
+ wxLogGeneric(M_LOG_WINONLY, "%s", m_textLog.c_str());
}
else // no progress dialog
{
// see comment above
- wxLogStatus("%s", textLog.c_str());
- }
+ wxLogStatus("%s", m_textLog.c_str());
}
- // check if Cancel wasn't pressed (we'd exit the loop above by break then)
- wxString pdExecText = _("Executing filter actions...");
- if ( idx == count &&
- (!pd || pd->Update(count, pdExecText)) )
- {
- UIdArray uidsToDelete;
- wxArrayLong indicesDeleted;
+ return true;
+}
- for ( idx = 0; idx < count; idx++ )
- {
+bool
+FilterRuleApply::ProgressCopy()
+{
// note that our progress meter may accelerate towards the end as
// we may copy more than one message initially - but it's ok, it's
@@ -3333,127 +3584,95 @@
// this!
- if ( pd && !pd->Update(count + idx) )
+ if ( m_pd )
{
- // cancelled by user
- break;
- }
+ wxString message = _("Executing filter actions...") + '\n'
+ + wxString::Format(_("Copying messages to '%s'..."),
+ m_destinations[m_idx].c_str());
- if ( operations[idx] & Copy )
+ if( !m_pd->Update(m_msgs.GetCount() + m_idx, message) )
{
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+FilterRuleApply::CopyToOneFolder()
+{
// copy all messages we are copying to this destination at once
UIdArray uidsToCopy;
- uidsToCopy.Add(msgs[idx]);
+ wxArrayLong indexesToCopy;
- String dst = destinations[idx];
+ uidsToCopy.Add(m_msgs[m_idx]);
+ indexesToCopy.Add(m_idx);
- for ( size_t n = idx + 1; n < count; n++ )
+ for ( size_t n = m_idx + 1; n < m_msgs.GetCount(); n++ )
{
- if ( (operations[n] & Copy) && destinations[n] == dst )
+ if ( (m_allOperations[n] & FilterRuleImpl::Copy)
+ && m_destinations[n] == m_destinations[m_idx] )
{
- uidsToCopy.Add(msgs[n]);
-
- // don't try to copy it when we reach it
- operations[n] &= ~Copy;
+ uidsToCopy.Add(m_msgs[n]);
+ indexesToCopy.Add(n);
}
}
- if ( pd )
- {
- pd->Update(count + idx, pdExecText + '\n' +
- wxString::Format(_("Copying messages to '%s'..."),
- dst.c_str()));
- }
+ bool copyOk = m_parent->m_MailFolder->SaveMessages(
+ &uidsToCopy, m_destinations[m_idx]);
- if ( !m_MailFolder->SaveMessages(&uidsToCopy, dst) )
+ for ( size_t copiedIndex = 0; copiedIndex < indexesToCopy.GetCount();
+ ++copiedIndex )
{
- rc |= FilterRule::Error;
- }
- }
+ // don't try to copy it when we reach it
+ m_allOperations[copiedIndex] &= ~FilterRuleImpl::Copy;
- if ( operations[idx] & Delete )
+ if ( !copyOk )
{
- indicesDeleted.Add(idx);
- uidsToDelete.Add(msgs[idx]);
+ m_allOperations[copiedIndex] &= ~FilterRuleImpl::Delete;
}
}
- // again, stop right now if we were cancelled
- if ( idx == count )
- {
- if ( !uidsToDelete.IsEmpty() )
+ return copyOk;
+}
+
+void
+FilterRuleApply::CollectForDelete()
+{
+ m_uidsToDelete.Empty();
+ m_indicesDeleted.Empty();
+
+ for ( m_idx = 0; m_idx < m_msgs.GetCount(); m_idx++ )
{
- if ( pd )
+ if ( m_allOperations[m_idx] & FilterRuleImpl::Delete )
{
- pd->Update(2*count, pdExecText + '\n' +
- wxString::Format(_("Deleting moved messages...")));
+ m_indicesDeleted.Add(m_idx);
+ m_uidsToDelete.Add(m_msgs[m_idx]);
+ }
}
+}
- if ( !m_MailFolder->DeleteMessages(&uidsToDelete) )
+void
+FilterRuleApply::ProgressDelete()
+{
+ if ( m_pd )
{
- rc |= FilterRule::Error;
+ m_pd->Update(2*m_msgs.GetCount(),
+ String(_("Executing filter actions...")) + '\n'
+ + _("Deleting moved messages..."));
}
- else // deleted successfully
- {
- rc |= FilterRule::Deleted;
+}
+void
+FilterRuleApply::IndicateDeleted()
+{
// remove the deleted messages from msgs
// iterate from end because otherwise the indices would shift
// while we traverse them
- size_t count = indicesDeleted.GetCount();
+ size_t count = m_indicesDeleted.GetCount();
for ( size_t n = count; n > 0; n-- )
{
- msgs.RemoveAt(indicesDeleted[n - 1]);
- }
- }
- }
-
- // actual expunging will be done by the calling code
- if ( doExpunge )
- {
- rc |= FilterRule::Expunged;
- }
+ m_msgs.RemoveAt(m_indicesDeleted[n - 1]);
}
- }
- //else: cancelled by user
-
- delete pd;
-
- m_MailFolder->DecRef();
- m_MailFolder = NULL;
- }
-#endif // !TEST
-
- return rc;
-}
-
-FilterRuleImpl::FilterRuleImpl(const String &filterrule,
- MInterface *minterface,
- MModule_Filters *mod)
- : m_FilterModule(mod),
- m_MInterface(minterface)
-{
-#ifndef TEST
- // we cannot allow the module to disappear while we exist
- m_FilterModule->IncRef();
-#endif
-
- m_hasToFunc =
- m_hasRcptFunc =
- m_hasHdrLineFunc =
- m_hasHeaderFunc = false;
-
- m_Program = Parse(filterrule);
- m_MessageUId = UID_ILLEGAL;
- m_MailMessage = NULL;
- m_MailFolder = NULL;
-}
-
-FilterRuleImpl::~FilterRuleImpl()
-{
- SafeDecRef(m_MailFolder);
- delete m_Program;
-#ifndef TEST
- m_FilterModule->DecRef();
-#endif
}
@@ -3461,11 +3680,4 @@
// MModule_FiltersImpl - the module
// ----------------------------------------------------------------------------
-
-#ifdef DEBUG
-void FilterRuleImpl::Debug(void)
-{
- Output(m_Program->Debug());
-}
-#endif
/** Filtering Module class. */
-------------------------------------------------------
This SF.Net email sponsored by: Free pre-built ASP.NET sites including
Data Reports, E-commerce, Portals, and Forums are available now.
Download today and enter to win an XBOX or Visual Studio .NET.
http://aspnet.click-url.com/go/psa00100003ave/direct;at.aspnet_072303_01/01
_______________________________________________
Mahogany-cvsupdates mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/mahogany-cvsupdates