Revision: 7431
http://mahogany.svn.sourceforge.net/mahogany/?rev=7431&view=rev
Author: vadz
Date: 2008-04-24 18:12:58 -0700 (Thu, 24 Apr 2008)
Log Message:
-----------
add support for filtering on individual headers to the GUI (bug 176)
Modified Paths:
--------------
trunk/M/CHANGES
trunk/M/doc/Manual.htex
trunk/M/include/MFilter.h
trunk/M/src/classes/MFilter.cpp
trunk/M/src/gui/wxFiltersDialog.cpp
Modified: trunk/M/CHANGES
===================================================================
--- trunk/M/CHANGES 2008-04-24 22:55:22 UTC (rev 7430)
+++ trunk/M/CHANGES 2008-04-25 01:12:58 UTC (rev 7431)
@@ -9,6 +9,7 @@
Release 0.68 '' September xx, 2007
---------------------------------------
+2008-04-25 VZ: Add support for filtering on individual headers to the GUI.
2008-04-23 VZ: Allow using wildcards in the headers selected to be seen.
2008-04-22 VZ: Added support for "Face:" header, similar to "X-Face:"
2008-04-21 VZ: Allow adding custom headers to show in the message view
Modified: trunk/M/doc/Manual.htex
===================================================================
--- trunk/M/doc/Manual.htex 2008-04-24 22:55:22 UTC (rev 7430)
+++ trunk/M/doc/Manual.htex 2008-04-25 01:12:58 UTC (rev 7431)
@@ -74,6 +74,7 @@
\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.
+ \item Allow creation of filters testing individual headers in the GUI.
\end{itemize}
\subsubsection{0.67 against 0.66}
@@ -3133,7 +3134,7 @@
\subsubsection{\label{FiltersDialog}The Filters Dialog\_4260\_}
This dialog allows you to define any number of filter rules available
-to Mahogany. In a seaparate dialog (\ref{FolderFiltersDialog}) you
+to Mahogany. In a separate dialog (\ref{FolderFiltersDialog}) you
can then pick any rule from the list and assign it to a folder. As
you can have different sets of rules for each folder and might want
to share rules for some folders, this dialog simply sets up rules
@@ -3152,8 +3153,8 @@
the list of rules in the main Filters Dialog, but is without any further
significance.
\item Underneath the name, you find the text {}``If Message...{}'' followed
-by at least one row of conditioncontrols.
-\item Under the condition controls you find the text {}``Then do this:{}``
+by at least one row of condition controls.
+\item Under the condition controls you find the text ``Then do this:``
followed by some action controls.
\end{itemize}
You can add more pattern matching controls by pressing the {[}More{]}
@@ -3167,14 +3168,13 @@
\begin{itemize}
\item Always - this rule will always be executed
-\item Contains - check if the text next to it is contained in the message
-component selected
-\item Match - check if the message component selected is exactly this text
+\item Contains - check if the target of the test contains the selected text
+\item Match - check if the target of the test is exactly this text
(case independent)
-\item Match Case - check if the message compent selected is exactly this
+\item Match Case - check if the target of the test is exactly this
text (case dependent)
-\item Match RegExp - check if the message compent selected matches the regular
-expression specified
+\item Match RegExp - check if the target of the test matches the specified
+regular expression
\item Larger Than - check if the message is larger than this in KByte
\item Smaller Than - check if the message is smaller than this in KByte
\item Older Than - check if the message is older than this many days
@@ -3184,6 +3184,20 @@
\item Python - execute the text in the box in the built-in Python interpreter
and proceed if it returns a non-0 result.
\end{itemize}
+
+The target of the text can be selected in the control next to the last one. The
+choices are mostly self-explanatory:
+\begin{itemize}
+\item ``From'', ``To'' and ``Subject'' correspond to the message headers with
+the same names
+\item ``Sender'' is the SMTP sender of the message
+\item ``Any recipient'' corresponds to the union of ``To'', ``Cc'' and ``Bcc''
+\item ``Headers'' corresponds to the entire message header while ``Header''
+selects the contents of the given (in the nearby text control) header only
+\item ``Body'' corresponds to the message body, i.e. everything except headers
+\item ``Message'' corresponds to the full message, including headers and body
+\end{itemize}
+
The possible actions which can be performed, are:
\begin{itemize}
Modified: trunk/M/include/MFilter.h
===================================================================
--- trunk/M/include/MFilter.h 2008-04-24 22:55:22 UTC (rev 7430)
+++ trunk/M/include/MFilter.h 2008-04-25 01:12:58 UTC (rev 7431)
@@ -72,13 +72,14 @@
{
ORC_W_Illegal = -1,
ORC_W_Subject = 0,
- ORC_W_Header,
+ ORC_W_Headers,
ORC_W_From,
ORC_W_Body,
ORC_W_Message,
ORC_W_To,
ORC_W_Sender,
ORC_W_Recipients,
+ ORC_W_Header,
ORC_W_Max
};
@@ -168,6 +169,8 @@
virtual MFDialogLogical GetLogical(size_t n) const = 0;
/// Return where to apply the n-th test
virtual MFDialogTarget GetTestTarget(size_t n) const = 0;
+ /// Return the argument of the n-th test target, if any
+ virtual String GetTestTargetArgument(size_t n) const = 0;
/// Return the n-th test's argument if any:
virtual String GetTestArgument(size_t n) const = 0;
/// Return the action component
@@ -181,12 +184,14 @@
@param test the actual test enum
@param target where to apply the test to
@param argument empty string or optional string argument
+ @param targetArg empty string or target argument for ORC_W_Header
*/
virtual void AddTest(MFDialogLogical l,
bool isInverted,
MFDialogTest test,
MFDialogTarget target,
- String argument = wxEmptyString
+ String argument = wxEmptyString,
+ String targetArg = wxEmptyString
) = 0;
/// sets the action and its argument
Modified: trunk/M/src/classes/MFilter.cpp
===================================================================
--- trunk/M/src/classes/MFilter.cpp 2008-04-24 22:55:22 UTC (rev 7430)
+++ trunk/M/src/classes/MFilter.cpp 2008-04-25 01:12:58 UTC (rev 7431)
@@ -80,6 +80,7 @@
MFDialogLogical m_Logical;
MFDialogTarget m_Target;
String m_Argument;
+ String m_TargetArgument;
/// reads settings from a string
bool ReadSettings(String *str);
@@ -97,6 +98,7 @@
m_Test == other.m_Test &&
m_Logical == other.m_Logical &&
m_Target == other.m_Target &&
+ m_TargetArgument == other.m_TargetArgument &&
m_Argument == other.m_Argument;
}
@@ -135,18 +137,28 @@
return FALSE;
m_Target = (MFDialogTarget) number;
+ if ( m_Target == ORC_W_Header )
+ {
+ m_TargetArgument = strutil_readString(*str, &success);
+ if(!success)
+ return FALSE;
+ }
+
return TRUE;
}
String
MFDialogComponent::WriteSettings(void)
{
- return wxString::Format(_T("%d %d %d \"%s\" %d"),
- (int) m_Logical,
- (int) m_Inverted,
- (int) m_Test,
- strutil_escapeString(m_Argument).c_str(),
- (int) m_Target);
+ String s = wxString::Format(_T("%d %d %d \"%s\" %d"),
+ (int) m_Logical,
+ (int) m_Inverted,
+ (int) m_Test,
+ strutil_escapeString(m_Argument).c_str(),
+ (int) m_Target);
+ if ( m_Target == ORC_W_Header )
+ s << " \"" << strutil_escapeString(m_TargetArgument) << '"';
+ return s;
}
static
@@ -239,13 +251,14 @@
const wxChar * ORC_W_Names[] =
{
_T("subject()"), // ORC_W_Subject
- _T("header()"), // ORC_W_Header
+ _T("header()"), // ORC_W_Headers
_T("from()"), // ORC_W_From
_T("body()"), // ORC_W_Body
_T("message()"), // ORC_W_Message
_T("to()"), // ORC_W_To
- _T("header(\"Sender\")"), // ORC_W_Sender
+ _T("headerline(\"Sender\")"),// ORC_W_Sender
_T("recipients()"), // ORC_W_Recipients
+ _T("headerline"), // ORC_W_Header: parentheses treated specially
NULL
};
@@ -397,13 +410,14 @@
bool needsTarget = FilterTestNeedsTarget(m_Test);
if(needsTarget)
{
- if(m_Target != ORC_W_Illegal && m_Target < ORC_W_Max)
- program << ORC_W_Names[ m_Target ];
- else
- {
- FAIL_MSG(_T("This must not happen!"));
- return wxEmptyString;
- }
+ CHECK(m_Target != ORC_W_Illegal && m_Target < ORC_W_Max, wxEmptyString,
+ "invalid target value");
+
+ program << ORC_W_Names[m_Target];
+
+ // this one is special as it has an extra argument of its own
+ if ( m_Target == ORC_W_Header )
+ program << "(\"" << m_TargetArgument << "\")";
}
bool needsArgument = FilterTestNeedsArgument(m_Test);
@@ -453,27 +467,51 @@
// now we need to find the test to be applied:
m_Test = ORC_T_Illegal;
for(size_t i = 0; ORC_T_Names[i]; i++)
- if(wxStrncmp(cptr, ORC_T_Names[i], wxStrlen(ORC_T_Names[i])) == 0)
+ {
+ const size_t len = wxStrlen(ORC_T_Names[i]);
+ if(wxStrncmp(cptr, ORC_T_Names[i], len) == 0)
{
- cptr += wxStrlen(ORC_T_Names[i]);
+ cptr += len;
m_Test = (MFDialogTest) i;
break;
}
+ }
if(m_Test == ORC_T_Illegal)
return FALSE;
+
bool needsTarget = FilterTestNeedsTarget(m_Test);
bool needsArgument = FilterTestNeedsArgument(m_Test);
m_Target = ORC_W_Illegal;
if(needsTarget)
{
for(size_t i = 0; ORC_W_Names[i]; i++)
- if(wxStrncmp(cptr, ORC_W_Names[i], wxStrlen(ORC_W_Names[i])) == 0)
+ {
+ const size_t len = wxStrlen(ORC_W_Names[i]);
+ if(wxStrncmp(cptr, ORC_W_Names[i], len) == 0)
{
m_Target = (MFDialogTarget) i;
+ cptr += len;
break;
}
+ }
+
if(m_Target == ORC_W_Illegal)
return FALSE;
+ if(m_Target == ORC_W_Header)
+ {
+ // special case: this one has an extra argument which we must extract
+ if (*cptr++ != '(')
+ return FALSE;
+ bool success;
+ String tmp(cptr);
+ const size_t lenOrig = tmp.length();
+ m_TargetArgument = strutil_readString(tmp, &success);
+ if ( !success )
+ return FALSE;
+ cptr += lenOrig - tmp.length();
+ if (*cptr++ != ')')
+ return FALSE;
+ }
}
// comma between target and argument:
if(needsTarget && needsArgument
@@ -484,14 +522,16 @@
if(needsArgument)
{
if(*cptr != '"') return FALSE;
- String tmp = *cptr;
+ String tmp(cptr);
+ const size_t lenOrig = tmp.length();
bool success;
m_Argument = strutil_readString(tmp, &success);
if(! success) return FALSE;
+ cptr += lenOrig - tmp.length();
}
if(*cptr++ != ')')
return FALSE;
- // assing remaining bit
+ // assign remaining bit
rule = cptr;
return TRUE;
}
@@ -544,6 +584,15 @@
return m_Tests[n].m_Target;
}
+ virtual String GetTestTargetArgument(size_t n) const
+ {
+ MOcheck();
+ ASSERT_MSG( GetTestTarget(n) == ORC_W_Header,
+ "only \"header\" target has an argument" );
+
+ return m_Tests[n].m_TargetArgument;
+ }
+
/// Return the action component
virtual MFDialogAction GetAction() const
{
@@ -561,7 +610,8 @@
bool isInverted,
MFDialogTest test,
MFDialogTarget target,
- String argument = wxEmptyString
+ String argument = wxEmptyString,
+ String targetArg = wxEmptyString
)
{
MOcheck();
@@ -570,6 +620,7 @@
c.m_Logical = (m_Tests.Count() == 0) ? ORC_L_None : l;
c.m_Test = test;
c.m_Target = target;
+ c.m_TargetArgument = targetArg;
c.m_Argument = argument;
m_Tests.Add(c);
}
Modified: trunk/M/src/gui/wxFiltersDialog.cpp
===================================================================
--- trunk/M/src/gui/wxFiltersDialog.cpp 2008-04-24 22:55:22 UTC (rev 7430)
+++ trunk/M/src/gui/wxFiltersDialog.cpp 2008-04-25 01:12:58 UTC (rev 7431)
@@ -206,9 +206,10 @@
gettext_noop("in subject"), // ORC_W_Subject
gettext_noop("in sender"), // ORC_W_Sender
gettext_noop("in any recipient"), // ORC_W_Recipients
+ gettext_noop("in headers"), // ORC_W_Headers
+ gettext_noop("in body"), // ORC_W_Body
+ gettext_noop("in message"), // ORC_W_Message
gettext_noop("in header"), // ORC_W_Header
- gettext_noop("in body"), // ORC_W_Body
- gettext_noop("in message") // ORC_W_Message
};
static const
@@ -225,9 +226,10 @@
ORC_W_Subject,
ORC_W_Sender,
ORC_W_Recipients,
- ORC_W_Header,
+ ORC_W_Headers,
ORC_W_Body,
- ORC_W_Message
+ ORC_W_Message,
+ ORC_W_Header
};
ENUM_fromSelect(MFDialogTarget, ORC_W_Swap, ORC_WhereCountS, ORC_W_Subject)
ENUM_toSelect( MFDialogTarget, ORC_W_Swap, ORC_WhereCountS, ORC_W_Subject)
@@ -486,6 +488,13 @@
return MFDialogTarget_fromSelect(m_Where->GetSelection());
}
+ String GetTargetArgument() const
+ {
+ CHECK( m_WhereArg, "", "no target argument control" );
+
+ return m_WhereArg->GetValue();
+ }
+
String GetArgument()
{
MFDialogTest test = GetTest();
@@ -533,6 +542,7 @@
// create the spam button and position it correctly
void CreateSpamButton();
+
// the controls which are always shown
wxCheckBox *m_Not; // invert the condition if checked
wxChoice *m_Logical; // corresponds to ORC_Logical
@@ -545,7 +555,8 @@
// special controls
wxChoice *m_choiceFlags; // used for ORC_T_HasFlag
- wxButton *m_btnSpam; // configure the ORC_T_IsSpam test
+ wxButton *m_btnSpam; // configure the ORC_T_IsSpam test
+ wxTextCtrl *m_WhereArg; // extra argument for some ORC_Where values
// the parent for all these controls
wxWindow *m_Parent;
@@ -610,7 +621,7 @@
m_choiceFlags = new wxChoice(parent, -1, wxDefaultPosition,
wxDefaultSize, ORC_Msg_Flag_Count, msgflagsTrans);
- m_Argument = new wxTextCtrl(parent,-1, wxEmptyString, wxDefaultPosition);
+ m_Argument = new wxTextCtrl(parent,-1, wxEmptyString);
wxString whereTrans[ORC_WhereCount];
for ( size_t nWhere = 0; nWhere < ORC_WhereCountS; nWhere++ )
@@ -620,6 +631,7 @@
m_Where = new wxChoice(parent, -1, wxDefaultPosition,
wxDefaultSize, ORC_WhereCountS, whereTrans);
+ m_WhereArg = new wxTextCtrl(parent, -1, wxEmptyString);
m_helpText = new wxStaticText(parent, -1, wxEmptyString);
@@ -644,6 +656,7 @@
delete m_choiceFlags;
delete m_btnSpam;
+ delete m_WhereArg;
}
void
@@ -682,6 +695,13 @@
c->width.AsIs();
c->centreY.SameAs(m_Not, wxCentreY);
c->height.AsIs();
+ m_WhereArg->SetConstraints(c);
+
+ c = new wxLayoutConstraints;
+ c->right.LeftOf(m_WhereArg, LAYOUT_X_MARGIN);
+ c->width.AsIs();
+ c->centreY.SameAs(m_Not, wxCentreY);
+ c->height.AsIs();
m_Where->SetConstraints(c);
c = new wxLayoutConstraints;
@@ -796,6 +816,7 @@
EnableAndShow(m_choiceFlags, enableMsgFlag);
EnableAndShow(m_Argument, enableArg);
EnableAndShow(m_Where, enableTarget);
+ EnableAndShow(m_WhereArg, enableTarget && GetTarget() == ORC_W_Header);
EnableAndShow(m_helpText, enableHelpText);
if ( enableSpam && !m_btnSpam )
@@ -822,6 +843,8 @@
m_helpText->Disable();
if ( m_Logical )
m_Logical->Disable();
+ if ( m_WhereArg )
+ m_WhereArg->Disable();
}
void
@@ -834,7 +857,13 @@
MFDialogTest test = settings.GetTest(n);
m_Not->SetValue(settings.IsInverted(n));
m_Type->SetSelection(MFDialogTest_toSelect(test));
- m_Where->SetSelection(MFDialogTarget_toSelect(settings.GetTestTarget(n)));
+
+ const MFDialogTarget target = settings.GetTestTarget(n);
+ m_Where->SetSelection(MFDialogTarget_toSelect(target));
+
+ if ( target == ORC_W_Header )
+ m_WhereArg->SetValue(settings.GetTestTargetArgument(n));
+
String argument = settings.GetTestArgument(n);
if ( test == ORC_T_HasFlag )
{
@@ -1480,11 +1509,17 @@
for ( size_t idx = 0; idx < m_nControls; idx++ )
{
OneCritControl *ctrl = m_CritControl[idx];
+ const MFDialogTarget target = ctrl->GetTarget();
+ String targetArg;
+ if ( target == ORC_W_Header )
+ targetArg = ctrl->GetTargetArgument();
+
settings->AddTest(ctrl->GetLogical(),
ctrl->IsInverted(),
ctrl->GetTest(),
- ctrl->GetTarget(),
- ctrl->GetArgument());
+ target,
+ ctrl->GetArgument(),
+ targetArg);
}
settings->SetAction(m_ActionControl->GetAction(),
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference
Don't miss this year's exciting event. There's still time to save $100.
Use priority code J8TL2D2.
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
Mahogany-cvsupdates mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mahogany-cvsupdates