Allow Mutt-like patterns in quick search.
authorDarko Koruga <darko@users.sourceforge.net>
Thu, 5 Dec 2002 10:31:11 +0000 (10:31 +0000)
committerDarko Koruga <darko@users.sourceforge.net>
Thu, 5 Dec 2002 10:31:11 +0000 (10:31 +0000)
ChangeLog.claws
README.claws
configure.in
src/common/utils.c
src/common/utils.h
src/summaryview.c

index 337da4695754176333444fbd45140aa9797ff68a..54b4eae92717e6da2a9b001d1c86342bbbae2874 100644 (file)
@@ -1,3 +1,15 @@
+2002-12-05 [darko]     0.8.6claws62
+
+       * src/utils.[ch]
+               expand_search_string(): new function
+               converts Mutt-like patterns to Sylpheed's
+               filtering engine
+       * src/summaryview.c
+               use expand_search_string() to allow Mutt-like
+               patterns in extended search
+       * README.claws
+               document extended option in quick search
+
 2002-12-05 [paul]      0.8.6claws61
 
        * sync with 0.8.6cvs16
 2002-12-05 [paul]      0.8.6claws61
 
        * sync with 0.8.6cvs16
index 92413327c636fd5947c0fc4925128663968dae05..878dae0872a970af7098771ac7d4a3b8d34c16de 100644 (file)
@@ -19,6 +19,7 @@ Summary:
    * spell checking (with installation instructions)
    * new cache
    * selective download, delete messages on server
    * spell checking (with installation instructions)
    * new cache
    * selective download, delete messages on server
+   * extended search in quick search
 4. How to contribute
 5. How to request features
 6. Installing Claws from CVS
 4. How to contribute
 5. How to request features
 6. Installing Claws from CVS
@@ -470,6 +471,67 @@ mention it.
                - select "Dillo: dillo %p&" from drop down list
                - choose an icon and click ok
        
                - select "Dillo: dillo %p&" from drop down list
                - choose an icon and click ok
        
+* quick search
+---------------------------------
+    This feature allows one to define criteria that messages have
+    to match in order to be displayed in the summary view pane.
+    Search types titled From, Subject and To are self explanatory.
+    Search type extended allows one to use Sylpheed's powerful
+    filtering engine to select messages. Examples:
+    from regexpcase "foo"
+    subject regexp "Bug" & to regexp "sylpheed-claws"
+
+    Additionally, it is possible to use simpler yet equally
+    powerfull patterns for message selections. Mutt users will
+    immediately recognize most of the available patterns:
+
+    Pattern  Parameter  Selects
+    ----------------------------------------------------
+    a                   all messages
+    ag       #          messages whose age is greater than #
+    al       #          messages whose age is lower than #
+    b        S          messages which contain S in the message body
+    B        S          messages which contain S in the whole message
+    c        S          messages carbon-copied to S
+    C        S          message is either to: or cc: to S
+    D                   deleted messages
+    e        S          messages which contain S in the Sender field
+    E        S          true if execute "S" succeeds
+    f        S          messages originating from user S
+    F                   forwarded messages
+    h        S          messages which contain header S
+    i        S          messages which contain S in Message-Id header
+    I        S          messages which contain S in inreplyto header
+    n        S          messages which are in newsgroup S
+    N                   new messages
+    O                   old messages
+    r                   messages which have been replied to
+    R                   read messages
+    s        S          messages which contain S in subject
+    se       #          messages whose score is equal to #
+    sg       #          messages whose score is greater than #
+    sl       #          messages whose score is lower than #
+    Se       #          messages whose size is equal to #
+    Sg       #          messages whose size is greater than #
+    Ss       #          messages whose size is smaller than #
+    t        S          messages which have been sent to S
+    T                   marked marked
+    U                   unread messages
+    x        S          messages which contain S in References header
+    y        S          messages which contain S in X-Label header
+
+    # means number
+    S means regexp string
+
+    It is possible to use logical operators AND (&), OR (|) and
+    NOT (! or ~). Case sensitive search is achieved with %.
+    Examples:
+    T                  marked messages
+    U                  unread messages
+    f "john beavis"    messages from john beavis
+    %f "John Beavis"   messages from John Beavis (case sensitive)
+    ~s foo             messages which do not have foo in the subject
+    f foo & s bar      messages from foo that do not have bar in thesubject
 
 
 4. How to contribute
 
 
 4. How to contribute
index 1dcd57d366e6b20f41717db280958d4acf86b969..884ac943a3254860ba39ea14c2cb7e5a97cc51b8 100644 (file)
@@ -11,7 +11,7 @@ MINOR_VERSION=8
 MICRO_VERSION=6
 INTERFACE_AGE=0
 BINARY_AGE=0
 MICRO_VERSION=6
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=claws61
+EXTRA_VERSION=claws62
 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
 
 dnl set $target
 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
 
 dnl set $target
index 7016e415e3d99af9a6a0f85a4067bbe2d5d1caa5..b0491f5512127021e37558e92c3dce4e235441ad 100644 (file)
@@ -3026,3 +3026,205 @@ FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
 
        return fdopen(fd, "w+");
 }
 
        return fdopen(fd, "w+");
 }
+
+/* allow Mutt-like patterns in quick search */
+gchar *expand_search_string(const gchar *search_string)
+{
+       int i, len, new_len = 0;
+       gchar term_char, save_char;
+       gchar *cmd_start, *cmd_end;
+       gchar *new_str = NULL;
+       gchar *copy_str;
+       gboolean casesens, dontmatch;
+       /* list of allowed pattern abbreviations */
+       struct {
+               gchar           *abbreviated;   /* abbreviation */
+               gchar           *command;       /* actual matcher command */ 
+               gint            numparams;      /* number of params for cmd */
+               gboolean        qualifier;      /* do we append regexpcase */
+               gboolean        quotes;         /* do we need quotes */
+       }
+       cmds[] = {
+               { "a",  "all",                          0,      FALSE,  FALSE },
+               { "ag", "age_greater",                  1,      FALSE,  FALSE },
+               { "al", "age_lower",                    1,      FALSE,  FALSE },
+               { "b",  "body_part",                    1,      TRUE,   TRUE  },
+               { "B",  "message",                      1,      TRUE,   TRUE  },
+               { "c",  "cc",                           1,      TRUE,   TRUE  },
+               { "C",  "to_or_cc",                     1,      TRUE,   TRUE  },
+               { "D",  "deleted",                      0,      FALSE,  FALSE },
+               { "e",  "header \"Sender\"",            1,      TRUE,   TRUE  },
+               { "E",  "execute",                      1,      FALSE,  TRUE  },
+               { "f",  "from",                         1,      TRUE,   TRUE  },
+               { "F",  "forwarded",                    0,      FALSE,  FALSE },
+               { "h",  "headers_part",                 1,      TRUE,   TRUE  },
+               { "i",  "header \"Message-Id\"",        1,      TRUE,   TRUE  },
+               { "I",  "inreplyto",                    1,      TRUE,   TRUE  },
+               { "n",  "newsgroups",                   1,      TRUE,   TRUE  },
+               { "N",  "new",                          0,      FALSE,  FALSE },
+               { "O",  "~new",                         0,      FALSE,  FALSE },
+               { "r",  "replied",                      0,      FALSE,  FALSE },
+               { "R",  "~unread",                      0,      FALSE,  FALSE },
+               { "s",  "subject",                      1,      TRUE,   TRUE  },
+               { "se", "score_equal",                  1,      FALSE,  FALSE },
+               { "sg", "score_greater",                1,      FALSE,  FALSE },
+               { "sl", "score_lower",                  1,      FALSE,  FALSE },
+               { "Se", "size_equal",                   1,      FALSE,  FALSE },
+               { "Sg", "size_greater",                 1,      FALSE,  FALSE },
+               { "Ss", "size_smaller",                 1,      FALSE,  FALSE },
+               { "t",  "to",                           1,      TRUE,   TRUE  },
+               { "T",  "marked",                       0,      FALSE,  FALSE },
+               { "U",  "unread",                       0,      FALSE,  FALSE },
+               { "x",  "header \"References\"",        1,      TRUE,   TRUE  },
+               { "y",  "header \"X-Label\"",           1,      TRUE,   TRUE  },
+               { "&",  "&",                            0,      FALSE,  FALSE },
+               { "|",  "|",                            0,      FALSE,  FALSE },
+               { NULL, NULL,                           0,      FALSE,  FALSE }
+       };
+
+       if (copy_str == NULL)
+               return NULL;
+
+       copy_str = g_strdup(search_string);
+
+       /* if it's a full command don't process it so users
+          can still do something like from regexpcase "foo" */
+       for (i = 0; cmds[i].command; i++) {
+               cmd_start = cmds[i].command;
+               /* allow logical NOT */
+               if (*cmd_start == '~')
+                       cmd_start++;
+               if (!strncmp(copy_str, cmd_start, strlen(cmd_start)))
+                       break;
+       }
+       if (cmds[i].command)
+               return copy_str;
+
+       cmd_start = cmd_end = copy_str;
+       while (cmd_end && *cmd_end) {
+               /* skip all white spaces */
+               while (*cmd_end && isspace(*cmd_end))
+                       cmd_end++;
+
+               /* extract a command */
+               while (*cmd_end && !isspace(*cmd_end))
+                       cmd_end++;
+
+               /* save character */
+               save_char = *cmd_end;
+               *cmd_end = '\0';
+
+               dontmatch = FALSE;
+               casesens = FALSE;
+
+               /* ~ and ! mean logical NOT */
+               if (*cmd_start == '~' || *cmd_start == '!')
+               {
+                       dontmatch = TRUE;
+                       cmd_start++;
+               }
+               /* % means case sensitive match */
+               if (*cmd_start == '%')
+               {
+                       casesens = TRUE;
+                       cmd_start++;
+               }
+
+               /* find matching abbreviation */
+               for (i = 0; cmds[i].command; i++) {
+                       if (!strcmp(cmd_start, cmds[i].abbreviated)) {
+                               /* restore character */
+                               *cmd_end = save_char;
+                               len = strlen(cmds[i].command) + 1;
+                               if (dontmatch)
+                                       len++;
+                               if (casesens)
+                                       len++;
+
+                               /* copy command */
+                               if (new_str) {
+                                       new_len += 1;
+                                       new_str = g_realloc(new_str, new_len);
+                                       strcat(new_str, " ");
+                               }
+                               new_len += (len + 1);
+                               new_str = g_realloc(new_str, new_len);
+                               if (new_len == len + 1)
+                                       *new_str = '\0';
+                               if (dontmatch)
+                                       strcat(new_str, "~");
+                               strcat(new_str, cmds[i].command);
+                               strcat(new_str, " ");
+
+                               /* stop if no params required */
+                               if (cmds[i].numparams == 0)
+                                       break;
+
+                               /* extract a parameter, allow quotes */
+                               cmd_end++;
+                               cmd_start = cmd_end;
+                               if (*cmd_start == '"') {
+                                       term_char = '"';
+                                       cmd_end++;
+                               }
+                               else
+                                       term_char = ' ';
+
+                               /* extract actual parameter */
+                               while ((*cmd_end) && (*cmd_end != term_char))
+                                       cmd_end++;
+
+                               if (*cmd_end && (*cmd_end != term_char))
+                                       break;
+
+                               if (*cmd_end == '"')
+                                       cmd_end++;
+
+                               save_char = *cmd_end;
+                               *cmd_end = '\0';
+
+                               new_len += strlen(cmd_start);
+
+                               /* do we need to add regexpcase ? */
+                               if (cmds[i].qualifier)
+                                       new_len += 10; /* "regexpcase " */
+
+                               if (term_char != '"')
+                                       new_len += 2;
+                               new_str = g_realloc(new_str, new_len);
+
+                               if (cmds[i].qualifier) {
+                                       if (casesens)
+                                               strcat(new_str, "regexp ");
+                                       else
+                                               strcat(new_str, "regexpcase ");
+                               }
+
+                               /* do we need to add quotes ? */
+                               if (cmds[i].quotes && term_char != '"')
+                                       strcat(new_str, "\"");
+
+                               /* copy actual parameter */
+                               strcat(new_str, cmd_start);
+
+                               /* do we need to add quotes ? */
+                               if (cmds[i].quotes && term_char != '"')
+                                       strcat(new_str, "\"");
+
+                               /* restore original character */
+                               *cmd_end = save_char;
+
+                               break;
+                       }
+               }
+
+               if (*cmd_end) {
+                       cmd_end++;
+                       cmd_start = cmd_end;
+               }
+       }
+
+       g_free(copy_str);
+       return new_str;
+}
+
index 260fbb3e8b2ee6ee65b845d5b0a7377fd74d0b8d..c33f201a5d3a99f8befd400ce9205ae4364712f0 100644 (file)
@@ -401,5 +401,7 @@ const gchar * line_has_quote_char   (const gchar *str,
 const gchar * line_has_quote_char_last (const gchar *str,
                                         const gchar *quote_chars);
 
 const gchar * line_has_quote_char_last (const gchar *str,
                                         const gchar *quote_chars);
 
+/* used in extended search */
+gchar * expand_search_string   (const gchar *str);
 
 #endif /* __UTILS_H__ */
 
 #endif /* __UTILS_H__ */
index ff87ecc68e9f2005314d79b6fab76d8b6cf0fe99..a4ea7dbaf673a0eaedb8d2683f758efa26be94e1 100644 (file)
@@ -921,8 +921,17 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                gchar *searched_header = NULL;
                MatcherList * tmp_list = NULL;
                
                gchar *searched_header = NULL;
                MatcherList * tmp_list = NULL;
                
-               if (search_type == S_SEARCH_EXTENDED)
-                       tmp_list = matcher_parser_get_cond(search_string);
+               if (search_type == S_SEARCH_EXTENDED) {
+                       char *newstr;
+
+                       newstr = expand_search_string(search_string);
+                       if (newstr) {
+                               tmp_list = matcher_parser_get_cond(newstr);
+                               g_free(newstr);
+                       }
+                       else
+                               tmp_list = NULL;
+               }
 
                not_killed = NULL;
                for (cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
 
                not_killed = NULL;
                for (cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {