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

Reply via email to