2005-08-05 [colin] 1.9.13cvs16
authorColin Leroy <colin@colino.net>
Fri, 5 Aug 2005 19:57:42 +0000 (19:57 +0000)
committerColin Leroy <colin@colino.net>
Fri, 5 Aug 2005 19:57:42 +0000 (19:57 +0000)
* src/Makefile.am
* src/prefs_folder_column.c
* src/prefs_folder_column.h
Add prefs for folder columns
* src/folderview.c
* src/folderview.h
* src/mainwindow.c
* src/mainwindow.h
* src/prefs_common.c
* src/prefs_common.h
* src/prefs_summaries.c
Use them
* src/prefs_summary_column.c
* src/summaryview.c
* src/summaryview.h
Add a To column
* src/textview.c
* src/common/utils.c
* src/common/utils.h
Move uri colourisation functions
to utils
* src/compose.c
Colourise URIs
* src/imap.c
Fix bug with LIST where we'd try
to get Folder//////[...]///

21 files changed:
ChangeLog-gtk2.claws
PATCHSETS
configure.ac
src/Makefile.am
src/common/utils.c
src/common/utils.h
src/compose.c
src/folderview.c
src/folderview.h
src/imap.c
src/mainwindow.c
src/mainwindow.h
src/prefs_common.c
src/prefs_common.h
src/prefs_folder_column.c [new file with mode: 0644]
src/prefs_folder_column.h [new file with mode: 0644]
src/prefs_summaries.c
src/prefs_summary_column.c
src/summaryview.c
src/summaryview.h
src/textview.c

index 084863b..7343350 100644 (file)
@@ -1,3 +1,32 @@
+2005-08-05 [colin]     1.9.13cvs16
+
+       * src/Makefile.am
+       * src/prefs_folder_column.c
+       * src/prefs_folder_column.h
+               Add prefs for folder columns
+       * src/folderview.c
+       * src/folderview.h
+       * src/mainwindow.c
+       * src/mainwindow.h
+       * src/prefs_common.c
+       * src/prefs_common.h
+       * src/prefs_summaries.c
+               Use them
+       * src/prefs_summary_column.c
+       * src/summaryview.c
+       * src/summaryview.h
+               Add a To column
+       * src/textview.c
+       * src/common/utils.c
+       * src/common/utils.h
+               Move uri colourisation functions
+               to utils
+       * src/compose.c
+               Colourise URIs
+       * src/imap.c
+               Fix bug with LIST where we'd try
+               to get Folder//////[...]/// 
+
 2005-08-05 [paul]      1.9.13cvs15
 
        * src/prefs_summaries.c
index f5f592b..823fd91 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( cvs diff -u -r 1.382.2.145 -r 1.382.2.146 src/compose.c;  cvs diff -u -r 1.207.2.50 -r 1.207.2.51 src/folderview.c;  cvs diff -u -r 1.94.2.58 -r 1.94.2.59 src/messageview.c;  cvs diff -u -r 1.101.2.11 -r 1.101.2.12 src/news.c;  cvs diff -u -r 1.2.2.7 -r 1.2.2.8 src/news_gtk.c;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/news_gtk.h;  cvs diff -u -r 1.1.2.19 -r 1.1.2.20 src/gtk/quicksearch.c;  ) > 1.9.13cvs13.patchset
 ( cvs diff -u -r 1.204.2.47 -r 1.204.2.48 src/prefs_common.c;  cvs diff -u -r 1.103.2.21 -r 1.103.2.22 src/prefs_common.h;  cvs diff -u -r 1.1.2.6 -r 1.1.2.7 src/prefs_summaries.c;  cvs diff -u -r 1.395.2.100 -r 1.395.2.101 src/summaryview.c;  ) > 1.9.13cvs14.patchset
 ( cvs diff -u -r 1.1.2.7 -r 1.1.2.8 src/prefs_summaries.c;  cvs diff -u -r 1.395.2.101 -r 1.395.2.102 src/summaryview.c;  ) > 1.9.13cvs15.patchset
+( cvs diff -u -r 1.155.2.29 -r 1.155.2.30 src/Makefile.am;  cvs diff -u -r 1.382.2.146 -r 1.382.2.147 src/compose.c;  cvs diff -u -r 1.207.2.51 -r 1.207.2.52 src/folderview.c;  cvs diff -u -r 1.20.2.5 -r 1.20.2.6 src/folderview.h;  cvs diff -u -r 1.179.2.56 -r 1.179.2.57 src/imap.c;  cvs diff -u -r 1.274.2.48 -r 1.274.2.49 src/mainwindow.c;  cvs diff -u -r 1.39.2.3 -r 1.39.2.4 src/mainwindow.h;  cvs diff -u -r 1.204.2.48 -r 1.204.2.49 src/prefs_common.c;  cvs diff -u -r 1.103.2.22 -r 1.103.2.23 src/prefs_common.h;  diff -u /dev/null src/prefs_folder_column.c;  diff -u /dev/null src/prefs_folder_column.h;  cvs diff -u -r 1.1.2.8 -r 1.1.2.9 src/prefs_summaries.c;  cvs diff -u -r 1.10.2.8 -r 1.10.2.9 src/prefs_summary_column.c;  cvs diff -u -r 1.395.2.102 -r 1.395.2.103 src/summaryview.c;  cvs diff -u -r 1.68.2.10 -r 1.68.2.11 src/summaryview.h;  cvs diff -u -r 1.96.2.68 -r 1.96.2.69 src/textview.c;  cvs diff -u -r 1.36.2.38 -r 1.36.2.39 src/common/utils.c;  cvs diff -u -r 1.20.2.20 -r 1.20.2.21 src/common/utils.h;  ) > 1.9.13cvs16.patchset
index 3a44de5..1946057 100644 (file)
@@ -11,7 +11,7 @@ MINOR_VERSION=9
 MICRO_VERSION=13
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=15
+EXTRA_VERSION=16
 EXTRA_RELEASE=
 EXTRA_GTK2_VERSION=
 
index ab23921..e75b586 100644 (file)
@@ -106,6 +106,7 @@ sylpheed_SOURCES = \
        prefs_spelling.c \
        prefs_summaries.c \
        prefs_summary_column.c \
+       prefs_folder_column.c \
        prefs_template.c \
        prefs_themes.c \
        prefs_toolbar.c \
@@ -244,6 +245,7 @@ sylpheedinclude_HEADERS = \
        prefs_spelling.h \
        prefs_summaries.h \
        prefs_summary_column.h \
+       prefs_folder_column.h \
        prefs_template.h \
        prefs_themes.h \
        prefs_toolbar.h \
index c9f6d79..646788d 100644 (file)
@@ -3956,3 +3956,322 @@ void replace_returns(gchar *str)
                *strstr(str, "\r") = ' ';
        }
 }
+
+/* get_uri_part() - retrieves a URI starting from scanpos.
+                   Returns TRUE if succesful */
+gboolean get_uri_part(const gchar *start, const gchar *scanpos,
+                            const gchar **bp, const gchar **ep)
+{
+       const gchar *ep_;
+
+       g_return_val_if_fail(start != NULL, FALSE);
+       g_return_val_if_fail(scanpos != NULL, FALSE);
+       g_return_val_if_fail(bp != NULL, FALSE);
+       g_return_val_if_fail(ep != NULL, FALSE);
+
+       *bp = scanpos;
+
+       /* find end point of URI */
+       for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
+               if (!isgraph(*(const guchar *)ep_) ||
+                   !IS_ASCII(*(const guchar *)ep_) ||
+                   strchr("[]{}()<>\"", *ep_))
+                       break;
+       }
+
+       /* no punctuation at end of string */
+
+       /* FIXME: this stripping of trailing punctuations may bite with other URIs.
+        * should pass some URI type to this function and decide on that whether
+        * to perform punctuation stripping */
+
+#define IS_REAL_PUNCT(ch)      (ispunct(ch) && ((ch) != '/')) 
+
+       for (; ep_ - 1 > scanpos + 1 &&
+              IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
+            ep_--)
+               ;
+
+#undef IS_REAL_PUNCT
+
+       *ep = ep_;
+
+       return TRUE;            
+}
+
+gchar *make_uri_string(const gchar *bp, const gchar *ep)
+{
+       return g_strndup(bp, ep - bp);
+}
+
+/* valid mail address characters */
+#define IS_RFC822_CHAR(ch) \
+       (IS_ASCII(ch) && \
+        (ch) > 32   && \
+        (ch) != 127 && \
+        !g_ascii_isspace(ch) && \
+        !strchr("(),;<>\"", (ch)))
+
+/* alphabet and number within 7bit ASCII */
+#define IS_ASCII_ALNUM(ch)     (IS_ASCII(ch) && g_ascii_isalnum(ch))
+#define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
+
+static GHashTable *create_domain_tab(void)
+{
+       static const gchar *toplvl_domains [] = {
+           "museum", "aero",
+           "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
+           "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
+           "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
+           "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
+           "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
+           "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
+           "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
+           "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
+           "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
+           "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
+           "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
+           "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
+           "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
+           "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
+           "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
+           "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
+           "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
+           "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
+           "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
+           "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
+           "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
+           "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
+           "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
+           "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
+           "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
+            "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw" 
+       };
+       gint n;
+       GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
+       
+       g_return_val_if_fail(htab, NULL);
+       for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++) 
+               g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
+       return htab;
+}
+
+static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
+{
+       const gint MAX_LVL_DOM_NAME_LEN = 6;
+       gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
+       const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
+       register gchar *p;
+       
+       if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
+               return FALSE;
+
+       for (p = buf; p < m &&  first < last; *p++ = *first++)
+               ;
+       *p = 0;
+
+       return g_hash_table_lookup(tab, buf) != NULL;
+}
+
+/* get_email_part() - retrieves an email address. Returns TRUE if succesful */
+gboolean get_email_part(const gchar *start, const gchar *scanpos,
+                              const gchar **bp, const gchar **ep)
+{
+       /* more complex than the uri part because we need to scan back and forward starting from
+        * the scan position. */
+       gboolean result = FALSE;
+       const gchar *bp_ = NULL;
+       const gchar *ep_ = NULL;
+       static GHashTable *dom_tab;
+       const gchar *last_dot = NULL;
+       const gchar *prelast_dot = NULL;
+       const gchar *last_tld_char = NULL;
+       
+       /* the informative part of the email address (describing the name
+        * of the email address owner) may contain quoted parts. the
+        * closure stack stores the last encountered quotes. */
+       gchar closure_stack[128];
+       gchar *ptr = closure_stack;
+
+       g_return_val_if_fail(start != NULL, FALSE);
+       g_return_val_if_fail(scanpos != NULL, FALSE);
+       g_return_val_if_fail(bp != NULL, FALSE);
+       g_return_val_if_fail(ep != NULL, FALSE);
+
+       if (!dom_tab)
+               dom_tab = create_domain_tab();
+       g_return_val_if_fail(dom_tab, FALSE);   
+
+       /* scan start of address */
+       for (bp_ = scanpos - 1;
+            bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
+               ;
+
+       /* TODO: should start with an alnum? */
+       bp_++;
+       for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
+               ;
+
+       if (bp_ != scanpos) {
+               /* scan end of address */
+               for (ep_ = scanpos + 1;
+                    *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
+                       if (*ep_ == '.') {
+                               prelast_dot = last_dot;
+                               last_dot = ep_;
+                               if (*(last_dot + 1) == '.') {
+                                       if (prelast_dot == NULL)
+                                               return FALSE;
+                                       last_dot = prelast_dot;
+                                       break;
+                               }
+                       }
+
+               /* TODO: really should terminate with an alnum? */
+               for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
+                    --ep_)
+                       ;
+               ep_++;
+
+               if (last_dot == NULL)
+                       return FALSE;
+               if (last_dot >= ep_)
+                       last_dot = prelast_dot;
+               if (last_dot == NULL || (scanpos + 1 >= last_dot))
+                       return FALSE;
+               last_dot++;
+
+               for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
+                       if (*last_tld_char == '?')
+                               break;
+
+               if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
+                       result = TRUE;
+
+               *ep = ep_;
+               *bp = bp_;
+       }
+
+       if (!result) return FALSE;
+
+       if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"' 
+       && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
+       && IS_RFC822_CHAR(*(ep_ + 3))) {
+               /* this informative part with an @ in it is 
+                * followed by the email address */
+               ep_ += 3;
+               
+               /* go to matching '>' (or next non-rfc822 char, like \n) */
+               for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
+                       ;
+                       
+               /* include the bracket */
+               if (*ep_ == '>') ep_++;
+               
+               /* include the leading quote */         
+               bp_--;
+
+               *ep = ep_;
+               *bp = bp_;
+               return TRUE;
+       }
+
+       /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
+       if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
+               return FALSE;
+
+       /* see if this is <bracketed>; in this case we also scan for the informative part. */
+       if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
+               return TRUE;
+
+#define FULL_STACK()   ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
+#define IN_STACK()     (ptr > closure_stack)
+/* has underrun check */
+#define POP_STACK()    if(IN_STACK()) --ptr
+/* has overrun check */
+#define PUSH_STACK(c)  if(!FULL_STACK()) *ptr++ = (c); else return TRUE
+/* has underrun check */
+#define PEEK_STACK()   (IN_STACK() ? *(ptr - 1) : 0)
+
+       ep_++;
+
+       /* scan for the informative part. */
+       for (bp_ -= 2; bp_ >= start; bp_--) {
+               /* if closure on the stack keep scanning */
+               if (PEEK_STACK() == *bp_) {
+                       POP_STACK();
+                       continue;
+               }
+               if (*bp_ == '\'' || *bp_ == '"') {
+                       PUSH_STACK(*bp_);
+                       continue;
+               }
+
+               /* if nothing in the closure stack, do the special conditions
+                * the following if..else expression simply checks whether 
+                * a token is acceptable. if not acceptable, the clause
+                * should terminate the loop with a 'break' */
+               if (!PEEK_STACK()) {
+                       if (*bp_ == '-'
+                       && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
+                       && (((bp_ + 1) < ep_)    && isalnum(*(bp_ + 1)))) {
+                               /* hyphens are allowed, but only in
+                                  between alnums */
+                       } else if (!strchr(",;:=?./+<>!&\r\n\t", *bp_)) {
+                               /* but anything not being a punctiation
+                                  is ok */
+                       } else {
+                               break; /* anything else is rejected */
+                       }
+               }
+       }
+
+       bp_++;
+
+#undef PEEK_STACK
+#undef PUSH_STACK
+#undef POP_STACK
+#undef IN_STACK
+#undef FULL_STACK
+
+       /* scan forward (should start with an alnum) */
+       for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
+               ;
+
+       *ep = ep_;
+       *bp = bp_;
+       
+       return result;
+}
+
+#undef IS_QUOTE
+#undef IS_ASCII_ALNUM
+#undef IS_RFC822_CHAR
+
+gchar *make_email_string(const gchar *bp, const gchar *ep)
+{
+       /* returns a mailto: URI; mailto: is also used to detect the
+        * uri type later on in the button_pressed signal handler */
+       gchar *tmp;
+       gchar *result;
+
+       tmp = g_strndup(bp, ep - bp);
+       result = g_strconcat("mailto:", tmp, NULL);
+       g_free(tmp);
+
+       return result;
+}
+
+gchar *make_http_string(const gchar *bp, const gchar *ep)
+{
+       /* returns an http: URI; */
+       gchar *tmp;
+       gchar *result;
+
+       tmp = g_strndup(bp, ep - bp);
+       result = g_strconcat("http://", tmp, NULL);
+       g_free(tmp);
+
+       return result;
+}
+
index 1300f4d..0dae156 100644 (file)
@@ -502,6 +502,15 @@ gpointer g_auto_pointer_get_ptr            (GAuto *auto_ptr);
 GAuto *g_auto_pointer_copy             (GAuto *auto_ptr);
 void g_auto_pointer_free               (GAuto *auto_ptr);
 void replace_returns                   (gchar *str);
+
+gboolean get_uri_part(const gchar *start, const gchar *scanpos,
+                            const gchar **bp, const gchar **ep);
+gchar *make_uri_string(const gchar *bp, const gchar *ep);
+gboolean get_email_part(const gchar *start, const gchar *scanpos,
+                              const gchar **bp, const gchar **ep);
+gchar *make_email_string(const gchar *bp, const gchar *ep);
+gchar *make_http_string(const gchar *bp, const gchar *ep);
+
 #ifdef __cplusplus
 }
 #endif
index 64f3aaa..41dcbe0 100644 (file)
@@ -239,7 +239,8 @@ static void compose_attach_parts            (Compose        *compose,
                                                 MsgInfo        *msginfo);
 
 static void compose_wrap_paragraph             (Compose        *compose,
-                                                GtkTextIter    *par_iter);
+                                                GtkTextIter    *par_iter,
+                                                gboolean        force);
 static void compose_wrap_all                   (Compose        *compose);
 static void compose_wrap_all_full              (Compose        *compose,
                                                 gboolean        autowrap);
@@ -768,6 +769,13 @@ static GdkColor signature_color = {
        (gushort)0x7fff
 };
 
+static GdkColor uri_color = {
+       (gulong)0,
+       (gushort)0,
+       (gushort)0,
+       (gushort)0
+};
+
 static void compose_create_tags(GtkTextView *text, Compose *compose)
 {
        GtkTextBuffer *buffer;
@@ -781,8 +789,10 @@ static void compose_create_tags(GtkTextView *text, Compose *compose)
                                               &quote_color);
                gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
                                               &signature_color);
+               gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
+                                              &uri_color);
        } else {
-               signature_color = quote_color = black;
+               signature_color = quote_color = uri_color = black;
        }
 
        gtk_text_buffer_create_tag(buffer, "quote",
@@ -791,6 +801,9 @@ static void compose_create_tags(GtkTextView *text, Compose *compose)
        gtk_text_buffer_create_tag(buffer, "signature",
                                   "foreground-gdk", &signature_color,
                                   NULL);
+       gtk_text_buffer_create_tag(buffer, "link",
+                                        "foreground-gdk", &uri_color,
+                                        NULL);
 }
 
 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
@@ -1658,10 +1671,10 @@ void compose_toolbar_cb(gint action, gpointer data)
                compose_ext_editor_cb(compose, 0, NULL);
                break;
        case A_LINEWRAP_CURRENT:
-               compose_wrap_paragraph(compose, NULL);
+               compose_wrap_paragraph(compose, NULL, TRUE);
                break;
        case A_LINEWRAP_ALL:
-               compose_wrap_all(compose);
+               compose_wrap_all_full(compose, TRUE);
                break;
        case A_ADDRBOOK:
                compose_address_cb(compose, 0, NULL);
@@ -2933,16 +2946,27 @@ static gboolean compose_join_next_line(Compose *compose,
        return TRUE;
 }
 
-static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
+#define ADD_TXT_POS(bp_, ep_, pti_) \
+       if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
+               last = last->next; \
+               last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
+               last->next = NULL; \
+       } else { \
+               g_warning("alloc error scanning URIs\n"); \
+       }
+
+static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
 {
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
        GtkTextBuffer *buffer;
-       GtkTextIter iter, break_pos;
+       GtkTextIter iter, break_pos, end_of_line;
        gchar *quote_str = NULL;
        gint quote_len;
        gboolean wrap_quote = prefs_common.linewrap_quote;
        gboolean prev_autowrap = compose->autowrap;
        gint startq_offset = -1, noq_offset = -1;
+       gint uri_start = -1, uri_stop = -1;
+       gint nouri_start = -1, nouri_stop = -1;
 
        compose->autowrap = FALSE;
 
@@ -2975,14 +2999,46 @@ static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
 
        /* go until paragraph end (empty line) */
        while (!gtk_text_iter_ends_line(&iter)) {
+               gchar *scanpos = NULL;
+               /* parse table - in order of priority */
+               struct table {
+                       const gchar *needle; /* token */
+
+                       /* token search function */
+                       gchar    *(*search)     (const gchar *haystack,
+                                                const gchar *needle);
+                       /* part parsing function */
+                       gboolean  (*parse)      (const gchar *start,
+                                                const gchar *scanpos,
+                                                const gchar **bp_,
+                                                const gchar **ep_);
+                       /* part to URI function */
+                       gchar    *(*build_uri)  (const gchar *bp,
+                                                const gchar *ep);
+               };
+
+               static struct table parser[] = {
+                       {"http://",  strcasestr, get_uri_part,   make_uri_string},
+                       {"https://", strcasestr, get_uri_part,   make_uri_string},
+                       {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
+                       {"www.",     strcasestr, get_uri_part,   make_http_string},
+                       {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
+                       {"@",        strcasestr, get_email_part, make_email_string}
+               };
+               const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
+               gint last_index = PARSE_ELEMS;
+               gint  n;
+               gchar *o_walk, *walk, *bp, *ep;
+               gint walk_pos;
+
+               uri_start = uri_stop = -1;
                quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
+
                if (quote_str) {
                        if (!wrap_quote) {
                                if (startq_offset == -1) {
                                        startq_offset = gtk_text_iter_get_offset(&iter);
                                }
-                               gtk_text_iter_forward_line(&iter);
-                               g_free(quote_str);
                                goto colorize;
                        }
                        debug_print("compose_wrap_paragraph(): quote_str = '%s'\n", quote_str);
@@ -2992,9 +3048,7 @@ static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
                                noq_offset = gtk_text_iter_get_offset(&iter);
                }
 
-               if (prev_autowrap == FALSE) {
-                       gtk_text_iter_forward_line(&iter);
-                       g_free(quote_str);
+               if (prev_autowrap == FALSE && !force) {
                        goto colorize;
                }
                if (compose_get_line_break_pos(buffer, &iter, &break_pos,
@@ -3032,14 +3086,53 @@ static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
 
                        /* move iter to current line start */
                        gtk_text_iter_set_line_offset(&iter, 0);
+                       continue;
                } else {
                        /* move iter to next line start */
                        iter = break_pos;
-                       gtk_text_iter_forward_line(&iter);
                }
 
-               g_free(quote_str);
 colorize:
+               end_of_line = iter;
+               while (!gtk_text_iter_ends_line(&end_of_line)) {
+                       gtk_text_iter_forward_char(&end_of_line);
+               }
+               o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
+
+               nouri_start = gtk_text_iter_get_offset(&iter);
+               nouri_stop = gtk_text_iter_get_offset(&end_of_line);
+
+               walk_pos = gtk_text_iter_get_offset(&iter);
+               /* FIXME: this looks phony. scanning for anything in the parse table */
+               for (n = 0; n < PARSE_ELEMS; n++) {
+                       gchar *tmp;
+
+                       tmp = parser[n].search(walk, parser[n].needle);
+                       if (tmp) {
+                               if (scanpos == NULL || tmp < scanpos) {
+                                       scanpos = tmp;
+                                       last_index = n;
+                               }
+                       }                                       
+               }
+
+               bp = ep = 0;
+               if (scanpos) {
+                       /* check if URI can be parsed */
+                       if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,(const gchar **)&ep)
+                           && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
+                                       walk = ep;
+                       } else
+                               walk = scanpos +
+                                       strlen(parser[last_index].needle);
+               } 
+               if (bp && ep) {
+                       uri_start = walk_pos + (bp - o_walk);
+                       uri_stop  = walk_pos + (ep - o_walk);
+               }
+               g_free(o_walk);
+               gtk_text_iter_forward_line(&iter);
+               g_free(quote_str);
                if (startq_offset != -1) {
                        GtkTextIter startquote, endquote;
                        gtk_text_buffer_get_iter_at_offset(
@@ -3057,6 +3150,25 @@ colorize:
                                buffer, "quote", &startnoquote, &endnoquote);
                        noq_offset = -1;
                }
+               
+               /* always */ {
+                       GtkTextIter nouri_start_iter, nouri_end_iter;
+                       gtk_text_buffer_get_iter_at_offset(
+                               buffer, &nouri_start_iter, nouri_start);
+                       gtk_text_buffer_get_iter_at_offset(
+                               buffer, &nouri_end_iter, nouri_stop);
+                       gtk_text_buffer_remove_tag_by_name(
+                               buffer, "link", &nouri_start_iter, &nouri_end_iter);
+               }
+               if (uri_start > 0 && uri_stop > 0) {
+                       GtkTextIter uri_start_iter, uri_end_iter;
+                       gtk_text_buffer_get_iter_at_offset(
+                               buffer, &uri_start_iter, uri_start);
+                       gtk_text_buffer_get_iter_at_offset(
+                               buffer, &uri_end_iter, uri_stop);
+                       gtk_text_buffer_apply_tag_by_name(
+                               buffer, "link", &uri_start_iter, &uri_end_iter);
+               }
        }
 
        if (par_iter)
@@ -3071,7 +3183,7 @@ static void compose_wrap_all(Compose *compose)
        compose_wrap_all_full(compose, FALSE);
 }
 
-static void compose_wrap_all_full(Compose *compose, gboolean autowrap)
+static void compose_wrap_all_full(Compose *compose, gboolean force)
 {
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);
        GtkTextBuffer *buffer;
@@ -3083,7 +3195,7 @@ static void compose_wrap_all_full(Compose *compose, gboolean autowrap)
 
        gtk_text_buffer_get_start_iter(buffer, &iter);
        while (!gtk_text_iter_is_end(&iter))
-               compose_wrap_paragraph(compose, &iter);
+               compose_wrap_paragraph(compose, &iter, force);
 
        undo_unblock(compose->undostruct);
 }
@@ -6986,9 +7098,9 @@ static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
        Compose *compose = (Compose *)data;
 
        if (action == 1)
-               compose_wrap_all(compose);
+               compose_wrap_all_full(compose, TRUE);
        else
-               compose_wrap_paragraph(compose, NULL);
+               compose_wrap_paragraph(compose, NULL, TRUE);
 }
 
 static void compose_toggle_autowrap_cb(gpointer data, guint action,
@@ -7262,7 +7374,7 @@ static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
                gtk_text_buffer_insert(buffer, iter, text, len);
 
        mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
-       compose_wrap_all_full(compose, TRUE);
+       compose_wrap_all_full(compose, FALSE);
        gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
        gtk_text_buffer_delete_mark(buffer, mark);
 
index 10e6d91..affb74e 100644 (file)
 #include "hooks.h"
 #include "folderutils.h"
 #include "partial_download.h"
+#include "prefs_folder_column.h"
 
-typedef enum
-{
-       COL_FOLDER      = 0,
-       COL_NEW         = 1,
-       COL_UNREAD      = 2,
-       COL_TOTAL       = 3
-} FolderColumnPos;
-
-#define N_FOLDER_COLS          4
 #define COL_FOLDER_WIDTH       150
 #define COL_NUM_WIDTH          32
 
@@ -361,64 +353,109 @@ static void create_ifactories(gpointer key, gpointer value, gpointer data)
        g_hash_table_insert(folderview->popups, fpopup->klass, factory);
 }
 
-FolderView *folderview_create(void)
+static void folderview_column_set_titles(FolderView *folderview)
+{
+       GtkWidget *ctree = folderview->ctree;
+       GtkWidget *label_new;
+       GtkWidget *label_unread;
+       GtkWidget *label_total;
+       GtkWidget *hbox_new;
+       GtkWidget *hbox_unread;
+       GtkWidget *hbox_total;
+       gint *col_pos = folderview->col_pos;
+       
+       gtk_widget_realize(folderview->ctree);
+       gtk_widget_show_all(folderview->scrolledwin);
+       
+       /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
+        * instead text (text overflows making them unreadable and ugly) */
+        stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
+                        &newxpm, &newxpmmask);
+       stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
+                        &unreadxpm, &unreadxpmmask);
+       stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
+                        &readxpm, &readxpmmask);
+               
+       label_new = gtk_pixmap_new(newxpm, newxpmmask);
+       label_unread = gtk_pixmap_new(unreadxpm, unreadxpmmask);
+       label_total = gtk_pixmap_new(readxpm, readxpmmask);
+
+       hbox_new = gtk_hbox_new(FALSE, 4);
+       hbox_unread = gtk_hbox_new(FALSE, 4);
+       hbox_total = gtk_hbox_new(FALSE, 4);
+
+       /* left justified */
+       gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
+       gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
+       gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
+       gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
+       gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
+       gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
+
+       gtk_widget_show_all(hbox_new);
+       gtk_widget_show_all(hbox_unread);
+       gtk_widget_show_all(hbox_total);
+
+       gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_NEW],hbox_new);
+       gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
+       gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
+}
+
+GtkWidget *folderview_ctree_create(FolderView *folderview)
 {
-       FolderView *folderview;
-       GtkWidget *scrolledwin;
        GtkWidget *ctree;
+       gint *col_pos;
+       FolderColumnState *col_state;
+       FolderColumnType type;
        gchar *titles[N_FOLDER_COLS];
        gint i;
+       GtkWidget *scrolledwin = folderview->scrolledwin;
 
-       debug_print("Creating folder view...\n");
-       folderview = g_new0(FolderView, 1);
+       memset(titles, 0, sizeof(titles));
 
-       titles[COL_FOLDER] = _("Folder");
-       titles[COL_NEW]    = _("New");
-       titles[COL_UNREAD] = _("Unread");
-       titles[COL_TOTAL]  = _("#");
+       col_state = prefs_folder_column_get_config();
+       memset(titles, 0, sizeof(titles));
 
-       scrolledwin = gtk_scrolled_window_new(NULL, NULL);
-       gtk_scrolled_window_set_policy
-               (GTK_SCROLLED_WINDOW(scrolledwin),
-                GTK_POLICY_AUTOMATIC,
-                prefs_common.folderview_vscrollbar_policy);
-       gtk_widget_set_size_request(scrolledwin,
-                            prefs_common.folderview_width,
-                            prefs_common.folderview_height);
+       col_pos = folderview->col_pos;
+
+       for (i = 0; i < N_FOLDER_COLS; i++) {
+               folderview->col_state[i] = col_state[i];
+               type = col_state[i].type;
+               col_pos[type] = i;
+       }
 
-       ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, COL_FOLDER, titles);
+       titles[col_pos[F_COL_FOLDER]] = _("Folder");
+       titles[col_pos[F_COL_NEW]]    = _("New");
+       titles[col_pos[F_COL_UNREAD]] = _("Unread");
+       titles[col_pos[F_COL_TOTAL]]  = _("#");
+
+       ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
+                                          titles);
        
-       gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
-       gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_NEW,
+       gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[F_COL_NEW],
                                           GTK_JUSTIFY_RIGHT);
-       gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_UNREAD,
+       gtk_clist_set_column_justification(GTK_CLIST(ctree),
+                                          col_pos[F_COL_UNREAD],
                                           GTK_JUSTIFY_RIGHT);
-       gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_TOTAL,
+       gtk_clist_set_column_justification(GTK_CLIST(ctree), 
+                                          col_pos[F_COL_TOTAL],
                                           GTK_JUSTIFY_RIGHT);
-       gtk_clist_set_column_width(GTK_CLIST(ctree), COL_FOLDER,
-                                  prefs_common.folder_col_folder);
-       gtk_clist_set_column_width(GTK_CLIST(ctree), COL_NEW,
-                                  prefs_common.folder_col_new);
-       gtk_clist_set_column_width(GTK_CLIST(ctree), COL_UNREAD,        
-                                  prefs_common.folder_col_unread);
-       gtk_clist_set_column_width(GTK_CLIST(ctree), COL_TOTAL,
-                                  prefs_common.folder_col_total);
        gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
        gtk_ctree_set_expander_style(GTK_CTREE(ctree),
                                     GTK_CTREE_EXPANDER_SQUARE);
        gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
        gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
 
-       /* create popup factories */
-       folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
-       g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
-
        /* don't let title buttons take key focus */
-       for (i = 0; i < N_FOLDER_COLS; i++)
+       for (i = 0; i < N_FOLDER_COLS; i++) {
                GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
                                       GTK_CAN_FOCUS);
-
+               gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[i],
+                                  prefs_common.folder_col_size[i]);
+               gtk_clist_set_column_visibility
+                       (GTK_CLIST(ctree), i, col_state[i].visible);
+       }
 
        g_signal_connect(G_OBJECT(ctree), "key_press_event",
                         G_CALLBACK(folderview_key_pressed),
@@ -465,7 +502,57 @@ FolderView *folderview_create(void)
                         G_CALLBACK(folderview_drag_end_cb),
                         folderview);
 
+       gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
+
+       return ctree;
+}
+
+void folderview_set_column_order(FolderView *folderview)
+{
+       GtkWidget *ctree;
+       FolderItem *item = folderview_get_selected_item(folderview);
+       GtkWidget *scrolledwin = folderview->scrolledwin;
+
+       gtk_widget_destroy(folderview->ctree);
+
+       folderview->ctree = ctree = folderview_ctree_create(folderview);
+       gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
+                                           GTK_CLIST(ctree)->hadjustment);
+       gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
+                                           GTK_CLIST(ctree)->vadjustment);
+       gtk_widget_show(ctree);
+       
+       folderview_set(folderview);
+       folderview_column_set_titles(folderview);
+
+       folderview_select(folderview,item);
+}
+
+FolderView *folderview_create(void)
+{
+       FolderView *folderview;
+       GtkWidget *scrolledwin;
+       GtkWidget *ctree;
+
+       debug_print("Creating folder view...\n");
+       folderview = g_new0(FolderView, 1);
+
+       scrolledwin = gtk_scrolled_window_new(NULL, NULL);
+       gtk_scrolled_window_set_policy
+               (GTK_SCROLLED_WINDOW(scrolledwin),
+                GTK_POLICY_AUTOMATIC,
+                prefs_common.folderview_vscrollbar_policy);
+       gtk_widget_set_size_request(scrolledwin,
+                            prefs_common.folderview_width,
+                            prefs_common.folderview_height);
+
        folderview->scrolledwin  = scrolledwin;
+       ctree = folderview_ctree_create(folderview);
+       
+       /* create popup factories */
+       folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
+       g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
+
        folderview->ctree        = ctree;
 
        folderview->folder_update_callback_id =
@@ -474,7 +561,7 @@ FolderView *folderview_create(void)
                hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
 
        gtk_widget_show_all(scrolledwin);
-
+       
        folderview->target_list = gtk_target_list_new(folderview_drag_types, 1);
        folderview_list = g_list_append(folderview_list, folderview);
 
@@ -484,14 +571,7 @@ FolderView *folderview_create(void)
 void folderview_init(FolderView *folderview)
 {
        GtkWidget *ctree = folderview->ctree;
-       GtkWidget *label_new;
-       GtkWidget *label_unread;
-       GtkWidget *label_total;
-       GtkWidget *hbox_new;
-       GtkWidget *hbox_unread;
-       GtkWidget *hbox_total;
-               
-       gtk_widget_realize(ctree);
+
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
@@ -538,39 +618,6 @@ void folderview_init(FolderView *folderview)
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
-
-       /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
-        * instead text (text overflows making them unreadable and ugly) */
-        stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
-                        &newxpm, &newxpmmask);
-       stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
-                        &unreadxpm, &unreadxpmmask);
-       stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
-                        &readxpm, &readxpmmask);
-               
-       label_new = gtk_pixmap_new(newxpm, newxpmmask);
-       label_unread = gtk_pixmap_new(unreadxpm, unreadxpmmask);
-       label_total = gtk_pixmap_new(readxpm, readxpmmask);
-
-       hbox_new = gtk_hbox_new(FALSE, 4);
-       hbox_unread = gtk_hbox_new(FALSE, 4);
-       hbox_total = gtk_hbox_new(FALSE, 4);
-
-       /* left justified */
-       gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
-       gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
-       gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
-       gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
-       gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
-       gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
-
-       gtk_widget_show_all(hbox_new);
-       gtk_widget_show_all(hbox_unread);
-       gtk_widget_show_all(hbox_total);
-
-       gtk_clist_set_column_widget(GTK_CLIST(ctree),COL_NEW,hbox_new);
-       gtk_clist_set_column_widget(GTK_CLIST(ctree),COL_UNREAD,hbox_unread);
-       gtk_clist_set_column_widget(GTK_CLIST(ctree),COL_TOTAL,hbox_total);
                        
        if (!normal_style) {
                PangoFontDescription *font_desc;
@@ -756,15 +803,16 @@ void folderview_update_msg_num(FolderView *folderview, GtkCTreeNode *row)
        FolderItem *item;
        gint new, unread, total;
        gchar *new_str, *unread_str, *total_str;
+       gint *col_pos = folderview->col_pos;
 
        if (!row) return;
 
        item = gtk_ctree_node_get_row_data(ctree, row);
        if (!item) return;
 
-       gtk_ctree_node_get_text(ctree, row, COL_NEW, &new_str);
-       gtk_ctree_node_get_text(ctree, row, COL_UNREAD, &unread_str);
-       gtk_ctree_node_get_text(ctree, row, COL_TOTAL, &total_str);
+       gtk_ctree_node_get_text(ctree, row, col_pos[F_COL_NEW], &new_str);
+       gtk_ctree_node_get_text(ctree, row, col_pos[F_COL_UNREAD], &unread_str);
+       gtk_ctree_node_get_text(ctree, row, col_pos[F_COL_TOTAL], &total_str);
        new = atoi(new_str);
        unread = atoi(unread_str);
        total = atoi(total_str);
@@ -777,7 +825,7 @@ void folderview_update_msg_num(FolderView *folderview, GtkCTreeNode *row)
 void folderview_append_item(FolderItem *item)
 {
        GList *list;
-
+       
        g_return_if_fail(item != NULL);
        g_return_if_fail(item->folder != NULL);
        if (folder_item_parent(item)) return;
@@ -786,6 +834,7 @@ void folderview_append_item(FolderItem *item)
                FolderView *folderview = (FolderView *)list->data;
                GtkCTree *ctree = GTK_CTREE(folderview->ctree);
                GtkCTreeNode *node, *child;
+               gint *col_pos = folderview->col_pos;
 
                node = gtk_ctree_find_by_row_data(ctree, NULL, 
                                                  folder_item_parent(item));
@@ -797,7 +846,7 @@ void folderview_append_item(FolderItem *item)
 
                                gtk_clist_freeze(GTK_CLIST(ctree));
 
-                               text[COL_FOLDER] = item->name;
+                               text[col_pos[F_COL_FOLDER]] = item->name;
                                child = gtk_ctree_insert_node
                                        (ctree, node, NULL, text,
                                         FOLDER_SPACING,
@@ -1118,6 +1167,7 @@ static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
        gboolean add_unread_mark;
        gboolean add_sub_match_mark;
        gboolean use_bold, use_color;
+       gint *col_pos = folderview->col_pos;
 
        item = gtk_ctree_node_get_row_data(ctree, node);
        g_return_if_fail(item != NULL);
@@ -1257,13 +1307,13 @@ static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
        g_free(name);
 
        if (!folder_item_parent(item)) {
-               gtk_ctree_node_set_text(ctree, node, COL_NEW,    "-");
-               gtk_ctree_node_set_text(ctree, node, COL_UNREAD, "-");
-               gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  "-");
+               gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW],    "-");
+               gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
+               gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL],  "-");
        } else {
-               gtk_ctree_node_set_text(ctree, node, COL_NEW,    itos(item->new_msgs));
-               gtk_ctree_node_set_text(ctree, node, COL_UNREAD, itos(item->unread_msgs));
-               gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  itos(item->total_msgs));
+               gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW],    itos(item->new_msgs));
+               gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], itos(item->unread_msgs));
+               gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL],  itos(item->total_msgs));
        }
 
        if (item->stype == F_OUTBOX || item->stype == F_DRAFT ||
@@ -1806,22 +1856,9 @@ static void folderview_popup_close(GtkMenuShell *menu_shell,
 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
                                   FolderView *folderview)
 {
-       switch (column) {
-       case COL_FOLDER:
-               prefs_common.folder_col_folder = width;
-               break;
-       case COL_NEW:
-               prefs_common.folder_col_new = width;
-               break;
-       case COL_UNREAD:
-               prefs_common.folder_col_unread = width;
-               break;
-       case COL_TOTAL:
-               prefs_common.folder_col_total = width;
-               break;
-       default:
-               break;
-       }
+       FolderColumnType type = folderview->col_state[column].type;
+
+       prefs_common.folder_col_size[type] = width;
 }
 
 void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
@@ -1850,14 +1887,15 @@ void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
        GtkCTree *ctree = GTK_CTREE(folderview->ctree);
        gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
        GtkCTreeNode *node, *parent_node;
-       
+       gint *col_pos = folderview->col_pos;
+
        parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
        if (parent_node == NULL)
                return;
 
        gtk_clist_freeze(GTK_CLIST(ctree));
 
-       text[COL_FOLDER] = item->name;
+       text[col_pos[F_COL_FOLDER]] = item->name;
        node = gtk_ctree_insert_node(ctree, parent_node, NULL, text,
                                     FOLDER_SPACING,
                                     folderxpm, folderxpmmask,
@@ -2087,9 +2125,12 @@ void folderview_reflect_prefs(void)
        FolderView *folderview = mainwindow_get_mainwindow()->folderview;
        normal_style = normal_color_style = bold_style = 
                bold_color_style = bold_tgtfold_style = NULL;
-
+       FolderItem *item = folderview_get_selected_item(folderview);
        folderview_init(folderview);
+       folderview_column_set_titles(folderview);
        folderview_set_all();
+       if (item)
+               folderview_select(folderview, item);
 }
 
 static void drag_state_stop(FolderView *folderview)
index d4c633f..2081595 100644 (file)
@@ -22,6 +22,7 @@
 
 typedef struct _FolderView     FolderView;
 typedef struct _FolderViewPopup        FolderViewPopup;
+typedef struct _FolderColumnState      FolderColumnState;
 
 #include <glib.h>
 #include <gtk/gtkwidget.h>
@@ -31,6 +32,22 @@ typedef struct _FolderViewPopup      FolderViewPopup;
 #include "summaryview.h"
 #include "folder.h"
 
+typedef enum
+{
+       F_COL_FOLDER,
+       F_COL_NEW,
+       F_COL_UNREAD,
+       F_COL_TOTAL
+} FolderColumnType;
+
+#define N_FOLDER_COLS  4
+
+struct _FolderColumnState
+{
+       FolderColumnType type;
+       gboolean visible;
+};
+
 struct _FolderView
 {
        GtkWidget *scrolledwin;
@@ -59,6 +76,8 @@ struct _FolderView
        GtkCTreeNode *drag_node;        /* drag node */
        
        GtkTargetList *target_list; /* DnD */
+       FolderColumnState col_state[N_FOLDER_COLS];
+       gint col_pos[N_FOLDER_COLS];
 };
 
 struct _FolderViewPopup
@@ -110,5 +129,6 @@ void folderview_register_popup              (FolderViewPopup        *fpopup);
 void folderview_unregister_popup       (FolderViewPopup        *fpopup);
 void folderview_update_search_icon     (FolderItem             *item,  
                                         gboolean                matches);
+void folderview_set_column_order       (FolderView             *folderview);
 
 #endif /* __FOLDERVIEW_H__ */
index 6c1a54f..f50c54f 100644 (file)
@@ -3562,6 +3562,12 @@ static GSList * imap_list_from_lep(IMAPFolder * folder,
                        free(dup_name);
                        continue;
                }
+
+               if (dup_name[strlen(dup_name)-1] == '/') {
+                       g_free(base);
+                       free(dup_name);
+                       continue;
+               }
                
                loc_name = imap_modified_utf7_to_utf8(base);
                loc_path = imap_modified_utf7_to_utf8(dup_name);
index 0359db7..53bd46e 100644 (file)
@@ -65,6 +65,7 @@
 #include "prefs_filtering.h"
 #include "prefs_account.h"
 #include "prefs_summary_column.h"
+#include "prefs_folder_column.h"
 #include "prefs_template.h"
 #include "action.h"
 #include "account.h"
@@ -284,7 +285,10 @@ static void collapse_threads_cb     (MainWindow    *mainwin,
                                  guint          action,
                                  GtkWidget     *widget);
 
-static void set_display_item_cb         (MainWindow    *mainwin,
+static void set_summary_display_item_cb         (MainWindow    *mainwin,
+                                 guint          action,
+                                 GtkWidget     *widget);
+static void set_folder_display_item_cb  (MainWindow    *mainwin,
                                  guint          action,
                                  GtkWidget     *widget);
 static void sort_summary_cb     (MainWindow    *mainwin,
@@ -520,7 +524,9 @@ static GtkItemFactoryEntry mainwin_entries[] =
        {N_("/_View/E_xpand all threads"),      NULL, expand_threads_cb, 0, NULL},
        {N_("/_View/Co_llapse all threads"),    NULL, collapse_threads_cb, 0, NULL},
        {N_("/_View/_Hide read messages"),      NULL, hide_read_messages, 0, "<ToggleItem>"},
-       {N_("/_View/Set displayed _items..."),  NULL, set_display_item_cb, 0, NULL},
+       {N_("/_View/Set displayed _items"),     NULL, NULL, 0, "<Branch>"},
+       {N_("/_View/Set displayed _items/ in _Summary view..."),NULL, set_summary_display_item_cb, 0, NULL},
+       {N_("/_View/Set displayed _items/ in _Folder view..."), NULL, set_folder_display_item_cb, 0, NULL},
 
        {N_("/_View/---"),                      NULL, NULL, 0, "<Separator>"},
        {N_("/_View/_Go to"),                   NULL, NULL, 0, "<Branch>"},
@@ -1287,6 +1293,17 @@ void main_window_set_summary_column(void)
        }
 }
 
+void main_window_set_folder_column(void)
+{
+       GList *cur;
+       MainWindow *mainwin;
+
+       for (cur = mainwin_list; cur != NULL; cur = cur->next) {
+               mainwin = (MainWindow *)cur->data;
+               folderview_set_column_order(mainwin->folderview);
+       }
+}
+
 static void main_window_set_account_selector_menu(MainWindow *mainwin,
                                                  GList *account_list)
 {
@@ -2815,12 +2832,18 @@ static void collapse_threads_cb(MainWindow *mainwin, guint action,
        summary_collapse_threads(mainwin->summaryview);
 }
 
-static void set_display_item_cb(MainWindow *mainwin, guint action,
+static void set_summary_display_item_cb(MainWindow *mainwin, guint action,
                                GtkWidget *widget)
 {
        prefs_summary_column_open();
 }
 
+static void set_folder_display_item_cb(MainWindow *mainwin, guint action,
+                               GtkWidget *widget)
+{
+       prefs_folder_column_open();
+}
+
 static void sort_summary_cb(MainWindow *mainwin, guint action,
                            GtkWidget *widget)
 {
index 3c8fd07..374116c 100644 (file)
@@ -150,6 +150,7 @@ void main_window_unlock                     (MainWindow     *mainwin);
 void main_window_reflect_prefs_all_real                (gboolean pixmap_theme_changed);
 void main_window_reflect_prefs_all     (void);
 void main_window_set_summary_column    (void);
+void main_window_set_folder_column     (void);
 void main_window_set_account_menu      (GList          *account_list);
 
 GtkWidget *main_window_get_folder_window       (MainWindow     *mainwin);
index 602d072..ebdb351 100644 (file)
 #include "prefs_common.h"
 #include "prefs_display_header.h"
 #include "prefs_summary_column.h"
+#include "prefs_folder_column.h"
 #include "mainwindow.h"
 #include "summaryview.h"
+#include "folderview.h"
 #include "messageview.h"
 #include "manage_window.h"
 #include "inc.h"
@@ -237,8 +239,6 @@ static PrefParam param[] = {
         NULL, NULL, NULL},
 
        /* Display: Summary View */
-       {"enable_swap_from", "FALSE", &prefs_common.swap_from, P_BOOL,
-        NULL, NULL, NULL},
        {"use_address_book", "FALSE", &prefs_common.use_addr_book, P_BOOL,
         NULL, NULL, NULL},
        {"thread_by_subject", "TRUE", &prefs_common.thread_by_subject, P_BOOL,
@@ -278,6 +278,8 @@ static PrefParam param[] = {
         &prefs_common.summary_col_visible[S_COL_SUBJECT], P_BOOL, NULL, NULL, NULL},
        {"summary_col_show_from", "TRUE",
         &prefs_common.summary_col_visible[S_COL_FROM], P_BOOL, NULL, NULL, NULL},
+       {"summary_col_show_to", "FALSE",
+        &prefs_common.summary_col_visible[S_COL_TO], P_BOOL, NULL, NULL, NULL},
        {"summary_col_show_date", "TRUE",
         &prefs_common.summary_col_visible[S_COL_DATE], P_BOOL, NULL, NULL, NULL},
        {"summary_col_show_size", "TRUE",
@@ -309,6 +311,8 @@ static PrefParam param[] = {
         &prefs_common.summary_col_pos[S_COL_SCORE], P_INT, NULL, NULL, NULL},
        {"summary_col_pos_locked", "9",
         &prefs_common.summary_col_pos[S_COL_LOCKED], P_INT, NULL, NULL, NULL},
+       {"summary_col_pos_to", "10",
+         &prefs_common.summary_col_pos[S_COL_TO], P_INT, NULL, NULL, NULL},
 
        {"summary_col_size_mark", "10",
         &prefs_common.summary_col_size[S_COL_MARK], P_INT, NULL, NULL, NULL},
@@ -320,6 +324,8 @@ static PrefParam param[] = {
         &prefs_common.summary_col_size[S_COL_SUBJECT], P_INT, NULL, NULL, NULL},
        {"summary_col_size_from", "120",
         &prefs_common.summary_col_size[S_COL_FROM], P_INT, NULL, NULL, NULL},
+       {"summary_col_size_to", "120",
+        &prefs_common.summary_col_size[S_COL_TO], P_INT, NULL, NULL, NULL},
        {"summary_col_size_date", "118",
         &prefs_common.summary_col_size[S_COL_DATE], P_INT, NULL, NULL, NULL},
        {"summary_col_size_size", "45",
@@ -343,14 +349,32 @@ static PrefParam param[] = {
        {"folderview_visible", "TRUE", &prefs_common.folderview_visible, P_BOOL,
         NULL, NULL, NULL},
 
-       {"folder_col_folder", "150", &prefs_common.folder_col_folder, P_INT,
-        NULL, NULL, NULL},
-       {"folder_col_new", "32", &prefs_common.folder_col_new, P_INT,
-        NULL, NULL, NULL},
-       {"folder_col_unread", "32", &prefs_common.folder_col_unread, P_INT,
-        NULL, NULL, NULL},
-       {"folder_col_total", "32", &prefs_common.folder_col_total, P_INT,
-        NULL, NULL, NULL},
+       {"folder_col_show_folder", "TRUE",
+        &prefs_common.folder_col_visible[F_COL_FOLDER], P_BOOL, NULL, NULL, NULL},
+       {"folder_col_show_new", "TRUE",
+        &prefs_common.folder_col_visible[F_COL_NEW], P_BOOL, NULL, NULL, NULL},
+       {"folder_col_show_unread", "TRUE",
+        &prefs_common.folder_col_visible[F_COL_UNREAD], P_BOOL, NULL, NULL, NULL},
+       {"folder_col_show_total", "TRUE",
+        &prefs_common.folder_col_visible[F_COL_TOTAL], P_BOOL, NULL, NULL, NULL},
+
+       {"folder_col_pos_folder", "0",
+        &prefs_common.folder_col_pos[F_COL_FOLDER], P_INT, NULL, NULL, NULL},
+       {"folder_col_pos_new", "1",
+        &prefs_common.folder_col_pos[F_COL_NEW], P_INT, NULL, NULL, NULL},
+       {"folder_col_pos_unread", "2",
+        &prefs_common.folder_col_pos[F_COL_UNREAD], P_INT, NULL, NULL, NULL},
+       {"folder_col_pos_total", "3",
+        &prefs_common.folder_col_pos[F_COL_TOTAL], P_INT, NULL, NULL, NULL},
+
+       {"folder_col_size_folder", "150",
+        &prefs_common.folder_col_size[F_COL_FOLDER], P_INT, NULL, NULL, NULL},
+       {"folder_col_size_new", "32",
+        &prefs_common.folder_col_size[F_COL_NEW], P_INT, NULL, NULL, NULL},
+       {"folder_col_size_unread", "32",
+        &prefs_common.folder_col_size[F_COL_UNREAD], P_INT, NULL, NULL, NULL},
+       {"folder_col_size_total", "32",
+        &prefs_common.folder_col_size[F_COL_TOTAL], P_INT, NULL, NULL, NULL},
 
        {"summaryview_width", "600", &prefs_common.summaryview_width, P_INT,
         NULL, NULL, NULL},
index ef32263..68bb40a 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "mainwindow.h"
 #include "summaryview.h"
+#include "folderview.h"
 #include "codeconv.h"
 #include "textview.h"
 #include "procmime.h"
@@ -153,7 +154,6 @@ struct _PrefsCommon
        gint ng_abbrev_len;
 
        gboolean show_searchbar;
-       gboolean swap_from;
        gboolean expand_thread;
        gboolean use_addr_book;
        gchar *date_format;
@@ -185,6 +185,10 @@ struct _PrefsCommon
        gint summary_col_pos[N_SUMMARY_COLS];
        gint summary_col_size[N_SUMMARY_COLS];
 
+       gboolean folder_col_visible[N_FOLDER_COLS];
+       gint folder_col_pos[N_FOLDER_COLS];
+       gint folder_col_size[N_FOLDER_COLS];
+
        /* Widget visibility, position and size */
        gint folderwin_x;
        gint folderwin_y;
@@ -192,11 +196,6 @@ struct _PrefsCommon
        gint folderview_height;
        gboolean folderview_visible;
 
-       gint folder_col_folder;
-       gint folder_col_new;
-       gint folder_col_unread;
-       gint folder_col_total;
-
        gint summaryview_width;
        gint summaryview_height;
 
diff --git a/src/prefs_folder_column.c b/src/prefs_folder_column.c
new file mode 100644 (file)
index 0000000..1c15c9e
--- /dev/null
@@ -0,0 +1,885 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2005 Hiroyuki Yamamoto & the Sylpheed-Claws team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkbutton.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "prefs_gtk.h"
+#include "prefs_common.h"
+#include "prefs_folder_column.h"
+#include "manage_window.h"
+#include "folderview.h"
+#include "mainwindow.h"
+#include "inc.h"
+#include "gtkutils.h"
+#include "utils.h"
+
+enum {
+       SUMCOL_NAME,
+       SUMCOL_TYPE,
+       N_SUMCOL_COLUMNS
+};
+
+#define TARGET_INFO_SUMCOL  (0xFEEDBABE)
+
+static const GtkTargetEntry row_targets[] = {
+       { "PREFS_SUM_COL_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_INFO_SUMCOL }
+};
+
+
+static struct _FolderColumnDialog
+{
+       GtkWidget *window;
+
+       GtkWidget *stock_list_view;
+       GtkWidget *shown_list_view;
+
+       GtkWidget *add_btn;
+       GtkWidget *remove_btn;
+       GtkWidget *up_btn;
+       GtkWidget *down_btn;
+
+       GtkWidget *default_btn;
+
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+
+       gboolean finished;
+} folder_col;
+
+static const gchar *const col_name[N_FOLDER_COLS] = {
+       N_("Folder"),           /* F_COL_FOLDER  */
+       N_("New"),              /* F_COL_NEW     */
+       N_("Unread"),           /* F_COL_UNREAD  */
+       N_("Total"),            /* F_COL_TOTAL   */
+};
+
+static FolderColumnState default_state[N_FOLDER_COLS] = {
+       { F_COL_FOLDER   , TRUE  },
+       { F_COL_NEW      , TRUE  },
+       { F_COL_UNREAD   , TRUE  },
+       { F_COL_TOTAL    , TRUE  },
+};
+
+static void prefs_folder_column_create (void);
+
+static void prefs_folder_column_set_dialog     (FolderColumnState *state);
+static void prefs_folder_column_set_view       (void);
+
+/* callback functions */
+static void prefs_folder_column_add    (void);
+static void prefs_folder_column_remove (void);
+
+static void prefs_folder_column_up     (void);
+static void prefs_folder_column_down   (void);
+
+static void prefs_folder_column_set_to_default (void);
+
+static void prefs_folder_column_ok     (void);
+static void prefs_folder_column_cancel (void);
+
+static gint prefs_folder_column_delete_event   (GtkWidget      *widget,
+                                                GdkEventAny    *event,
+                                                gpointer        data);
+static gboolean prefs_folder_column_key_pressed(GtkWidget      *widget,
+                                                GdkEventKey    *event,
+                                                gpointer        data);
+
+static GtkListStore *prefs_folder_column_create_store  (void);
+
+static void prefs_folder_column_insert_column  (GtkListStore *store,
+                                                gint row,
+                                                const gchar *name,
+                                                FolderColumnType type);
+                                              
+static FolderColumnType prefs_folder_column_get_column (GtkWidget *list, 
+                                                                gint row);
+
+static GtkWidget *prefs_folder_column_list_view_create (const gchar *name);
+
+static void prefs_filtering_create_list_view_columns   (GtkWidget *list_view, 
+                                                        const gchar *name);
+
+static void drag_data_get      (GtkTreeView *tree_view, 
+                                GdkDragContext *context, 
+                                GtkSelectionData *data, 
+                                guint info, 
+                                guint time, 
+                                GtkTreeModel *model);
+                         
+static void drag_data_received (GtkTreeView *tree_view, 
+                                GdkDragContext *context,
+                                gint x, gint y, 
+                                GtkSelectionData *data,
+                                guint info, 
+                                guint time, 
+                                GtkTreeModel *model);
+
+void prefs_folder_column_open(void)
+{
+       inc_lock();
+
+       if (!folder_col.window)
+               prefs_folder_column_create();
+
+       manage_window_set_transient(GTK_WINDOW(folder_col.window));
+       gtk_widget_grab_focus(folder_col.ok_btn);
+
+       prefs_folder_column_set_dialog(NULL);
+
+       gtk_widget_show(folder_col.window);
+
+       folder_col.finished = FALSE;
+       while (folder_col.finished == FALSE)
+               gtk_main_iteration();
+
+       gtk_widget_hide(folder_col.window);
+
+       inc_unlock();
+}
+
+static void prefs_folder_column_create(void)
+{
+       GtkWidget *window;
+       GtkWidget *vbox;
+
+       GtkWidget *label_hbox;
+       GtkWidget *label;
+
+       GtkWidget *vbox1;
+
+       GtkWidget *hbox1;
+       GtkWidget *clist_hbox;
+       GtkWidget *scrolledwin;
+       GtkWidget *stock_list_view;
+       GtkWidget *shown_list_view;
+
+       GtkWidget *btn_vbox;
+       GtkWidget *btn_vbox1;
+       GtkWidget *add_btn;
+       GtkWidget *remove_btn;
+       GtkWidget *up_btn;
+       GtkWidget *down_btn;
+
+       GtkWidget *btn_hbox;
+       GtkWidget *default_btn;
+       GtkWidget *confirm_area;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+
+       debug_print("Creating folder column setting window...\n");
+
+       window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       gtk_container_set_border_width(GTK_CONTAINER(window), 8);
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+       gtk_window_set_modal(GTK_WINDOW(window), TRUE);
+       gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
+       gtk_window_set_title(GTK_WINDOW(window),
+                            _("Displayed items configuration"));
+       g_signal_connect(G_OBJECT(window), "delete_event",
+                        G_CALLBACK(prefs_folder_column_delete_event),
+                        NULL);
+       g_signal_connect(G_OBJECT(window), "key_press_event",
+                        G_CALLBACK(prefs_folder_column_key_pressed),
+                        NULL);
+
+       vbox = gtk_vbox_new(FALSE, 6);
+       gtk_widget_show(vbox);
+       gtk_container_add(GTK_CONTAINER(window), vbox);
+
+       label_hbox = gtk_hbox_new(FALSE, 0);
+       gtk_widget_show(label_hbox);
+       gtk_box_pack_start(GTK_BOX(vbox), label_hbox, FALSE, FALSE, 4);
+
+       label = gtk_label_new
+               (_("Select items to be displayed in the folder view. You can modify\n"
+                  "the order by using the Up / Down buttons or by dragging the items."));
+       gtk_widget_show(label);
+       gtk_box_pack_start(GTK_BOX(label_hbox), label, FALSE, FALSE, 4);
+       gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
+
+       vbox1 = gtk_vbox_new(FALSE, VSPACING);
+       gtk_widget_show(vbox1);
+       gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
+       gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);
+
+       hbox1 = gtk_hbox_new(FALSE, 8);
+       gtk_widget_show(hbox1);
+       gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0);
+
+       clist_hbox = gtk_hbox_new(FALSE, 8);
+       gtk_widget_show(clist_hbox);
+       gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
+
+       scrolledwin = gtk_scrolled_window_new(NULL, NULL);
+       gtk_widget_set_size_request(scrolledwin, 180, 210);
+       gtk_widget_show(scrolledwin);
+       gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
+                                      GTK_POLICY_AUTOMATIC,
+                                      GTK_POLICY_AUTOMATIC);
+
+                                      
+       stock_list_view = prefs_folder_column_list_view_create
+                               (_("Available items"));
+       gtk_widget_show(stock_list_view);
+       gtk_container_add(GTK_CONTAINER(scrolledwin), stock_list_view);
+
+       /* add/remove button */
+       btn_vbox = gtk_vbox_new(FALSE, 0);
+       gtk_widget_show(btn_vbox);
+       gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
+
+       btn_vbox1 = gtk_vbox_new(FALSE, 8);
+       gtk_widget_show(btn_vbox1);
+       gtk_box_pack_start(GTK_BOX(btn_vbox), btn_vbox1, TRUE, FALSE, 0);
+
+       add_btn = gtk_button_new_with_label(_("  ->  "));
+       gtk_widget_show(add_btn);
+       gtk_box_pack_start(GTK_BOX(btn_vbox1), add_btn, FALSE, FALSE, 0);
+
+       remove_btn = gtk_button_new_with_label(_("  <-  "));
+       gtk_widget_show(remove_btn);
+       gtk_box_pack_start(GTK_BOX(btn_vbox1), remove_btn, FALSE, FALSE, 0);
+
+       g_signal_connect(G_OBJECT(add_btn), "clicked",
+                        G_CALLBACK(prefs_folder_column_add), NULL);
+       g_signal_connect(G_OBJECT(remove_btn), "clicked",
+                        G_CALLBACK(prefs_folder_column_remove), NULL);
+
+       clist_hbox = gtk_hbox_new(FALSE, 8);
+       gtk_widget_show(clist_hbox);
+       gtk_box_pack_start(GTK_BOX(hbox1), clist_hbox, TRUE, TRUE, 0);
+
+       scrolledwin = gtk_scrolled_window_new(NULL, NULL);
+       gtk_widget_set_size_request(scrolledwin, 180, 210);
+       gtk_widget_show(scrolledwin);
+       gtk_box_pack_start(GTK_BOX(clist_hbox), scrolledwin, TRUE, TRUE, 0);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
+                                      GTK_POLICY_AUTOMATIC,
+                                      GTK_POLICY_AUTOMATIC);
+
+       shown_list_view = prefs_folder_column_list_view_create
+                               (_("Displayed items"));
+       gtk_widget_show(shown_list_view);
+       gtk_container_add(GTK_CONTAINER(scrolledwin), shown_list_view);
+
+       /* up/down button */
+       btn_vbox = gtk_vbox_new(FALSE, 0);
+       gtk_widget_show(btn_vbox);
+       gtk_box_pack_start(GTK_BOX(hbox1), btn_vbox, FALSE, FALSE, 0);
+
+       btn_vbox1 = gtk_vbox_new(FALSE, 8);
+       gtk_widget_show(btn_vbox1);
+       gtk_box_pack_start(GTK_BOX(btn_vbox), btn_vbox1, TRUE, FALSE, 0);
+
+       up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
+       gtk_widget_show(up_btn);
+       gtk_box_pack_start(GTK_BOX(btn_vbox1), up_btn, FALSE, FALSE, 0);
+
+       down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
+       gtk_widget_show(down_btn);
+       gtk_box_pack_start(GTK_BOX(btn_vbox1), down_btn, FALSE, FALSE, 0);
+
+       g_signal_connect(G_OBJECT(up_btn), "clicked",
+                        G_CALLBACK(prefs_folder_column_up), NULL);
+       g_signal_connect(G_OBJECT(down_btn), "clicked",
+                        G_CALLBACK(prefs_folder_column_down), NULL);
+
+       btn_hbox = gtk_hbox_new(FALSE, 8);
+       gtk_widget_show(btn_hbox);
+       gtk_box_pack_end(GTK_BOX(vbox), btn_hbox, FALSE, FALSE, 0);
+
+       btn_vbox = gtk_vbox_new(FALSE, 0);
+       gtk_widget_show(btn_vbox);
+       gtk_box_pack_start(GTK_BOX(btn_hbox), btn_vbox, FALSE, FALSE, 0);
+
+       default_btn = gtk_button_new_with_label(_(" Use default "));
+       gtk_widget_show(default_btn);
+       gtk_box_pack_start(GTK_BOX(btn_vbox), default_btn, TRUE, FALSE, 0);
+       g_signal_connect(G_OBJECT(default_btn), "clicked",
+                        G_CALLBACK(prefs_folder_column_set_to_default),
+                        NULL);
+
+       gtkut_stock_button_set_create(&confirm_area, &ok_btn, GTK_STOCK_OK,
+                                     &cancel_btn, GTK_STOCK_CANCEL,
+                                     NULL, NULL);
+       gtk_widget_show(confirm_area);
+       gtk_box_pack_end(GTK_BOX(btn_hbox), confirm_area, FALSE, FALSE, 0);
+       gtk_widget_grab_default(ok_btn);
+
+       g_signal_connect(G_OBJECT(ok_btn), "clicked",
+                        G_CALLBACK(prefs_folder_column_ok), NULL);
+       g_signal_connect(G_OBJECT(cancel_btn), "clicked",
+                        G_CALLBACK(prefs_folder_column_cancel), NULL);
+
+
+       folder_col.window      = window;
+       folder_col.add_btn     = add_btn;
+       folder_col.remove_btn  = remove_btn;
+       folder_col.up_btn      = up_btn;
+       folder_col.down_btn    = down_btn;
+       folder_col.ok_btn      = ok_btn;
+       folder_col.cancel_btn  = cancel_btn;
+       folder_col.stock_list_view = stock_list_view;
+       folder_col.shown_list_view = shown_list_view;
+}
+
+FolderColumnState *prefs_folder_column_get_config(void)
+{
+       static FolderColumnState state[N_FOLDER_COLS];
+       FolderColumnType type;
+       gint pos;
+
+       for (pos = 0; pos < N_FOLDER_COLS; pos++)
+               state[pos].type = -1;
+
+       for (type = 0; type < N_FOLDER_COLS; type++) {
+               pos = prefs_common.folder_col_pos[type];
+               if (pos < 0 || pos >= N_FOLDER_COLS ||
+                   state[pos].type != -1) {
+                       g_warning("Wrong column position\n");
+                       prefs_folder_column_set_config(default_state);
+                       return default_state;
+               }
+
+               state[pos].type = type;
+               state[pos].visible = prefs_common.folder_col_visible[type];
+       }
+
+       return state;
+}
+
+void prefs_folder_column_set_config(FolderColumnState *state)
+{
+       FolderColumnType type;
+       gint pos;
+
+       for (pos = 0; pos < N_FOLDER_COLS; pos++) {
+               type = state[pos].type;
+               prefs_common.folder_col_visible[type] = state[pos].visible;
+               prefs_common.folder_col_pos[type] = pos;
+       }
+}
+
+static void prefs_folder_column_set_dialog(FolderColumnState *state)
+{
+       GtkListStore *stock_store, *shown_store;
+       gint pos;
+       FolderColumnType type;
+       gchar *name;
+
+       stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
+                       (GTK_TREE_VIEW(folder_col.stock_list_view)));
+       shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
+                       (GTK_TREE_VIEW(folder_col.shown_list_view)));
+
+       gtk_list_store_clear(stock_store);
+       gtk_list_store_clear(shown_store);
+
+       if (!state)
+               state = prefs_folder_column_get_config();
+
+       for (pos = 0; pos < N_FOLDER_COLS; pos++) {
+               type = state[pos].type;
+               name = gettext(col_name[type]);
+
+               if (state[pos].visible)
+                       prefs_folder_column_insert_column(shown_store,
+                                                          -1, name,
+                                                          type);
+               else
+                       prefs_folder_column_insert_column(stock_store,
+                                                           -1, name,
+                                                           type);
+       }
+}
+
+static void prefs_folder_column_set_view(void)
+{
+       gint stock_n_rows, shown_n_rows;
+       FolderColumnState state[N_FOLDER_COLS];
+       FolderColumnType type;
+       gint row, pos = 0;
+
+       stock_n_rows = gtk_tree_model_iter_n_children
+               (gtk_tree_view_get_model(GTK_TREE_VIEW
+                       (folder_col.stock_list_view)), NULL);
+       shown_n_rows = gtk_tree_model_iter_n_children
+               (gtk_tree_view_get_model(GTK_TREE_VIEW
+                       (folder_col.shown_list_view)), NULL);
+
+       g_return_if_fail
+               (stock_n_rows + shown_n_rows == N_FOLDER_COLS);
+
+       for (row = 0; row < stock_n_rows; row++) {
+               type = prefs_folder_column_get_column
+                       (folder_col.stock_list_view, row);
+               state[row].type = type;
+               state[row].visible = FALSE;
+       }
+
+       pos = row;
+       for (row = 0; row < shown_n_rows; row++) {
+               type = prefs_folder_column_get_column
+                       (folder_col.shown_list_view, row);
+               state[pos + row].type = type;
+               state[pos + row].visible = TRUE;
+       }
+
+       prefs_folder_column_set_config(state);
+       main_window_set_folder_column();
+}
+
+static void prefs_folder_column_add(void)
+{
+       GtkListStore *stock_store, *shown_store;
+       GtkTreeIter stock_sel, shown_sel, shown_add;
+       gboolean shown_sel_valid;
+       gchar *name;
+       FolderColumnType type;
+       
+       stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
+               (GTK_TREE_VIEW(folder_col.stock_list_view)));
+       shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
+               (GTK_TREE_VIEW(folder_col.shown_list_view)));
+       
+       if (!gtk_tree_selection_get_selected
+               (gtk_tree_view_get_selection
+                       (GTK_TREE_VIEW(folder_col.stock_list_view)),
+                NULL,
+                &stock_sel))
+               return;
+
+       shown_sel_valid = gtk_tree_selection_get_selected
+               (gtk_tree_view_get_selection
+                       (GTK_TREE_VIEW(folder_col.shown_list_view)),
+                NULL,
+                &shown_sel);
+                        
+       gtk_tree_model_get(GTK_TREE_MODEL(stock_store), &stock_sel,
+                          SUMCOL_TYPE, &type,
+                          -1);
+                       
+       gtk_list_store_remove(stock_store, &stock_sel);
+
+       gtk_list_store_insert_after(shown_store, &shown_add, 
+                                   shown_sel_valid ? &shown_sel : NULL);
+
+       name = gettext(col_name[type]);                             
+                                   
+       gtk_list_store_set(shown_store, &shown_add,
+                          SUMCOL_NAME, name,
+                          SUMCOL_TYPE, type,
+                          -1);
+       
+       gtk_tree_selection_select_iter(gtk_tree_view_get_selection
+               (GTK_TREE_VIEW(folder_col.shown_list_view)),
+                &shown_add);
+}
+
+static void prefs_folder_column_remove(void)
+{
+       GtkListStore *stock_store, *shown_store;
+       GtkTreeIter shown_sel, stock_sel, stock_add;
+       gboolean stock_sel_valid;
+       gchar *name;
+       FolderColumnType type;
+       
+       stock_store = GTK_LIST_STORE(gtk_tree_view_get_model
+               (GTK_TREE_VIEW(folder_col.stock_list_view)));
+       shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
+               (GTK_TREE_VIEW(folder_col.shown_list_view)));
+               
+       if (!gtk_tree_selection_get_selected
+               (gtk_tree_view_get_selection
+                       (GTK_TREE_VIEW(folder_col.shown_list_view)),
+                NULL,
+                &shown_sel))
+               return;
+
+       stock_sel_valid = gtk_tree_selection_get_selected
+               (gtk_tree_view_get_selection
+                       (GTK_TREE_VIEW(folder_col.stock_list_view)),
+                NULL,
+                &stock_sel);
+       
+       gtk_tree_model_get(GTK_TREE_MODEL(shown_store), &shown_sel,
+                          SUMCOL_TYPE, &type,
+                          -1);
+                       
+       gtk_list_store_remove(shown_store, &shown_sel);
+
+       gtk_list_store_insert_after(stock_store, &stock_add, 
+                                   stock_sel_valid ? &stock_sel : NULL);
+
+       name = gettext(col_name[type]);                             
+                                   
+       gtk_list_store_set(stock_store, &stock_add,
+                          SUMCOL_NAME, name,
+                          SUMCOL_TYPE, type,
+                          -1);
+       
+       gtk_tree_selection_select_iter(gtk_tree_view_get_selection
+               (GTK_TREE_VIEW(folder_col.stock_list_view)),
+               &stock_add);
+}
+
+static void prefs_folder_column_up(void)
+{
+       GtkTreePath *prev, *sel;
+       GtkTreeIter isel;
+       GtkListStore *shown_store;
+       GtkTreeIter iprev;
+       
+       if (!gtk_tree_selection_get_selected
+               (gtk_tree_view_get_selection
+                       (GTK_TREE_VIEW(folder_col.shown_list_view)),
+                NULL,
+                &isel))
+               return;
+
+       shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
+               (GTK_TREE_VIEW(folder_col.shown_list_view)));
+
+       sel = gtk_tree_model_get_path(GTK_TREE_MODEL(shown_store), 
+                                     &isel);
+       if (!sel)
+               return;
+
+       prev = gtk_tree_path_copy(sel);         
+       if (!gtk_tree_path_prev(prev)) {
+               gtk_tree_path_free(prev);
+               gtk_tree_path_free(sel);
+               return;
+       }
+
+       gtk_tree_model_get_iter(GTK_TREE_MODEL(shown_store),
+                               &iprev, prev);
+       gtk_tree_path_free(sel);
+       gtk_tree_path_free(prev);
+
+       gtk_list_store_swap(shown_store, &iprev, &isel);
+}
+
+static void prefs_folder_column_down(void)
+{
+       GtkListStore *shown_store;
+       GtkTreeIter next, sel;
+       
+       if (!gtk_tree_selection_get_selected
+               (gtk_tree_view_get_selection
+                       (GTK_TREE_VIEW(folder_col.shown_list_view)),
+                NULL,
+                &sel))
+               return;
+
+       shown_store = GTK_LIST_STORE(gtk_tree_view_get_model
+               (GTK_TREE_VIEW(folder_col.shown_list_view)));
+
+       next = sel;
+       if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(shown_store), &next)) 
+               return;
+
+       gtk_list_store_swap(shown_store, &next, &sel);
+}
+
+static void prefs_folder_column_set_to_default(void)
+{
+       prefs_folder_column_set_dialog(default_state);
+}
+
+static void prefs_folder_column_ok(void)
+{
+       if (!folder_col.finished) {
+               folder_col.finished = TRUE;
+               prefs_folder_column_set_view();
+       }
+}
+
+static void prefs_folder_column_cancel(void)
+{
+       folder_col.finished = TRUE;
+}
+
+static gint prefs_folder_column_delete_event(GtkWidget *widget,
+                                             GdkEventAny *event,
+                                             gpointer data)
+{
+       folder_col.finished = TRUE;
+       return TRUE;
+}
+
+static gboolean prefs_folder_column_key_pressed(GtkWidget *widget,
+                                                GdkEventKey *event,
+                                                gpointer data)
+{
+       if (event && event->keyval == GDK_Escape)
+               folder_col.finished = TRUE;
+       return FALSE;
+}
+
+static GtkListStore *prefs_folder_column_create_store(void)
+{
+       return gtk_list_store_new(N_SUMCOL_COLUMNS,
+                                 G_TYPE_STRING,
+                                 G_TYPE_INT,
+                                 -1);
+}
+
+static void prefs_folder_column_insert_column(GtkListStore *store,
+                                              gint row,
+                                              const gchar *name,
+                                              FolderColumnType type)
+{
+       GtkTreeIter iter;
+
+       if (row >= 0) {
+               if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store),
+                                                  &iter, NULL, row))
+                       row = -1;                                                  
+       }
+       if (row < 0) {
+               /* add new */
+               gtk_list_store_append(store, &iter);
+               gtk_list_store_set(store, &iter,
+                                  SUMCOL_NAME, name,
+                                  SUMCOL_TYPE, type,
+                                  -1);
+               return;
+       } else {
+               /* change existing */
+               gtk_list_store_set(store, &iter, 
+                                  SUMCOL_NAME, name,
+                                  SUMCOL_TYPE, type,
+                                  -1);
+       }
+}
+
+/*!
+ *\brief       Return the columnn type for a row
+ */
+static FolderColumnType prefs_folder_column_get_column(GtkWidget *list, gint row)
+{      
+       GtkTreeView *list_view = GTK_TREE_VIEW(list);
+       GtkTreeModel *model = gtk_tree_view_get_model(list_view);
+       GtkTreeIter iter;
+       FolderColumnType result;
+
+       if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row))
+               return -1;
+       
+       gtk_tree_model_get(model, &iter, 
+                          SUMCOL_TYPE, &result,
+                          -1);
+       
+       return result;
+}
+
+static GtkWidget *prefs_folder_column_list_view_create(const gchar *name)
+{
+       GtkWidget *list_view;
+       GtkTreeSelection *selector;
+       GtkTreeModel *model;
+
+       model = GTK_TREE_MODEL(prefs_folder_column_create_store());
+       list_view = gtk_tree_view_new_with_model(model);
+       g_object_unref(G_OBJECT(model));
+       
+       gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view),
+                                    prefs_common.enable_rules_hint);
+       
+       selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
+       gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
+
+       prefs_filtering_create_list_view_columns(GTK_WIDGET(list_view), name);
+
+       gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(list_view),
+                                              GDK_BUTTON1_MASK,
+                                              row_targets,
+                                              G_N_ELEMENTS(row_targets), 
+                                              GDK_ACTION_MOVE);
+                                           
+       gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(list_view), 
+                                            row_targets, 
+                                            G_N_ELEMENTS(row_targets), 
+                                            GDK_ACTION_MOVE);
+               
+       g_signal_connect(G_OBJECT(list_view), "drag_data_get",
+                        G_CALLBACK(drag_data_get),
+                        model);
+
+       g_signal_connect(G_OBJECT(list_view), "drag_data_received",
+                        G_CALLBACK(drag_data_received),
+                        model);
+
+       return list_view;
+}
+
+static void prefs_filtering_create_list_view_columns(GtkWidget *list_view, 
+                                                    const gchar *name)
+{
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *renderer;
+
+       renderer = gtk_cell_renderer_text_new();
+       column = gtk_tree_view_column_new_with_attributes
+               (name, renderer, "text", SUMCOL_NAME, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);          
+}
+
+static void drag_data_get(GtkTreeView *tree_view, GdkDragContext *context, 
+                         GtkSelectionData *data, guint info, 
+                         guint time, GtkTreeModel *model)
+{
+       GtkTreeIter iter;
+       FolderColumnType type;
+       GtkTreeModel *source_model;
+
+       if (info != TARGET_INFO_SUMCOL) 
+               return;
+
+       if (!gtk_tree_selection_get_selected
+                       (gtk_tree_view_get_selection(tree_view),
+                        &source_model, &iter)) 
+               return;                  
+       
+       gtk_tree_model_get(source_model, &iter, 
+                          SUMCOL_TYPE, &type,
+                          -1);
+
+       /* send the type */
+       gtk_selection_data_set(data, data->target, 8, (gchar *) &type, sizeof type);
+}
+
+static void drag_data_received(GtkTreeView *tree_view, GdkDragContext *context,
+                              gint x, gint y, GtkSelectionData *data,
+                              guint info, guint time, GtkTreeModel *model)
+{
+       GtkWidget *source;
+       GtkTreePath *dst = NULL, *sel = NULL;
+       GtkTreeIter isel, idst;
+       GtkTreeViewDropPosition pos;
+       FolderColumnType type;
+       GtkTreeModel *sel_model;
+       gchar *name;
+       
+       source = gtk_drag_get_source_widget(context);
+       
+       if (source == GTK_WIDGET(tree_view)) {
+       
+               /*
+                * Same widget: re-order
+                */
+                
+               gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view),
+                                          NULL, &isel);
+               sel = gtk_tree_model_get_path(model, &isel);
+               gtk_tree_view_get_dest_row_at_pos(tree_view, x, y,
+                                                 &dst, &pos);
+
+               /* NOTE: dst is invalid if selection beyond last row, in that
+                * case move beyond last one (XXX_move_before(..., NULL)) */                                              
+
+               if (dst)                                                  
+                       gtk_tree_model_get_iter(model, &idst, dst);
+               else 
+                       gtk_list_store_move_before(GTK_LIST_STORE(model),
+                                                  &isel,
+                                                  NULL);
+
+               /* we do not drag if no valid dst and sel, and when
+                * dst and sel are the same (moving after or before
+                * itself doesn't change order...) */
+               if ((dst && sel) && gtk_tree_path_compare(sel, dst) != 0) {
+                       if (pos == GTK_TREE_VIEW_DROP_BEFORE
+                       ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
+                               gtk_list_store_move_before(GTK_LIST_STORE(model),
+                                                          &isel,
+                                                          &idst);
+                       else
+                               gtk_list_store_move_after(GTK_LIST_STORE(model),
+                                                         &isel,
+                                                         &idst);
+                       
+               } 
+               gtk_tree_path_free(dst);                                          
+               gtk_tree_path_free(sel);
+               gtk_drag_finish(context, TRUE, FALSE, time);
+               
+       } else if (source == folder_col.stock_list_view 
+       ||         source == folder_col.shown_list_view) {
+       
+               /*
+                * Other widget: change and update
+                */
+
+               
+               /* get source information and remove */
+               gtk_tree_selection_get_selected(gtk_tree_view_get_selection(
+                                               GTK_TREE_VIEW(source)),
+                                               &sel_model, &isel);
+               type = *((gint *) data->data);
+               name = gettext(col_name[type]);
+               gtk_list_store_remove(GTK_LIST_STORE(sel_model), &isel);
+
+               /* get insertion position */
+               gtk_tree_view_get_dest_row_at_pos(tree_view, x, y, &dst, &pos);
+
+               /* NOTE: dst is invalid if insertion point beyond last row, 
+                * just append to list in that case (XXX_store_append()) */
+
+               if (dst) {
+                       gtk_tree_model_get_iter(model, &idst, dst);
+
+                       if (pos == GTK_TREE_VIEW_DROP_BEFORE
+                       ||  pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
+                               gtk_list_store_insert_before(GTK_LIST_STORE(model),
+                                                            &isel,
+                                                            &idst);
+                       else
+                               gtk_list_store_insert_after(GTK_LIST_STORE(model),
+                                                           &isel,
+                                                           &idst);
+               } else
+                       gtk_list_store_append(GTK_LIST_STORE(model),
+                                             &isel);
+               
+               gtk_list_store_set(GTK_LIST_STORE(model), &isel,
+                                  SUMCOL_NAME, name,
+                                  SUMCOL_TYPE, type, -1);
+               gtk_tree_path_free(dst);
+               gtk_drag_finish(context, TRUE, FALSE, time);
+       }
+
+       /* XXXX: should we call gtk_drag_finish() for other code paths? */
+}
+
diff --git a/src/prefs_folder_column.h b/src/prefs_folder_column.h
new file mode 100644 (file)
index 0000000..13b8e14
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2005 Hiroyuki Yamamoto & the Sylpheed-Claws team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PREFS_FOLDER_COLUMN_H__
+#define __PREFS_FOLDER_COLUMN_H__
+
+#include "folderview.h"
+
+void prefs_folder_column_open(void);
+
+FolderColumnState *prefs_folder_column_get_config(void);
+void prefs_folder_column_set_config(FolderColumnState *state);
+
+#endif /* __PREFS_FOLDER_COLUMN_H__ */
index 5756170..96c21cf 100644 (file)
@@ -34,6 +34,7 @@
 #include "prefs_common.h"
 #include "prefs_gtk.h"
 #include "prefs_summary_column.h"
+#include "prefs_folder_column.h"
 
 #include "gtk/menu.h"
 #include "gtk/gtkutils.h"
@@ -50,7 +51,6 @@ typedef struct _SummariesPage
        GtkWidget *chkbtn_transhdr;
        GtkWidget *chkbtn_folder_unread;
        GtkWidget *spinbtn_ng_abbrev_len;
-       GtkWidget *chkbtn_swapfrom;
        GtkWidget *chkbtn_useaddrbook;
        GtkWidget *chkbtn_threadsubj;
        GtkWidget *button_datefmt;
@@ -695,9 +695,7 @@ void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window,
        GtkWidget *label_ng_abbrev;
        GtkWidget *spinbtn_ng_abbrev_len;
        GtkObject *spinbtn_ng_abbrev_len_adj;
-       GtkWidget *frame_summary;
        GtkWidget *vbox2;
-       GtkWidget *chkbtn_swapfrom;
        GtkWidget *chkbtn_useaddrbook;
        GtkWidget *chkbtn_threadsubj;
        GtkWidget *vbox3;
@@ -756,23 +754,17 @@ void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window,
        gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_ng_abbrev_len),
                                     TRUE);
 
-       label_ng_abbrev = gtk_label_new
-               (_("letters"));
+       label_ng_abbrev = gtk_label_new (_("letters"));
        gtk_widget_show (label_ng_abbrev);
        gtk_box_pack_start (GTK_BOX (hbox1), label_ng_abbrev, FALSE, FALSE, 0);
 
        /* ---- Summary ---- */
 
-       PACK_FRAME(vbox1, frame_summary, _("Summary View"));
-
        vbox2 = gtk_vbox_new (FALSE, 0);
        gtk_widget_show (vbox2);
-       gtk_container_add (GTK_CONTAINER (frame_summary), vbox2);
-       gtk_container_set_border_width (GTK_CONTAINER (vbox2), 8);
+       gtk_container_add (GTK_CONTAINER (vbox1), vbox2);
+       gtk_container_set_border_width (GTK_CONTAINER (vbox2), 0);
 
-       PACK_CHECK_BUTTON
-               (vbox2, chkbtn_swapfrom,
-                _("Display recipient in 'From' column if sender is yourself"));
        PACK_CHECK_BUTTON
                (vbox2, chkbtn_useaddrbook,
                 _("Display sender using address book"));
@@ -815,6 +807,14 @@ void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window,
                          G_CALLBACK (prefs_summary_column_open),
                          NULL);
 
+       button_dispitem = gtk_button_new_with_label
+               (_(" Set displayed items in folder view... "));
+       gtk_widget_show (button_dispitem);
+       gtk_box_pack_start (GTK_BOX (hbox1), button_dispitem, FALSE, TRUE, 0);
+       g_signal_connect (G_OBJECT (button_dispitem), "clicked",
+                         G_CALLBACK (prefs_folder_column_open),
+                         NULL);
+
        vbox2 = gtk_vbox_new (FALSE, 0);
        gtk_widget_show (vbox2);
        gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0);
@@ -916,8 +916,6 @@ void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window,
                        prefs_common.trans_hdr);
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chkbtn_folder_unread),
                        prefs_common.display_folder_unread);
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chkbtn_swapfrom),
-                       prefs_common.swap_from);
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chkbtn_useaddrbook),
                        prefs_common.use_addr_book);
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chkbtn_threadsubj),
@@ -944,7 +942,6 @@ void prefs_summaries_create_widget(PrefsPage *_page, GtkWindow *window,
        prefs_summaries->chkbtn_transhdr = chkbtn_transhdr;
        prefs_summaries->chkbtn_folder_unread = chkbtn_folder_unread;
        prefs_summaries->spinbtn_ng_abbrev_len = spinbtn_ng_abbrev_len;
-       prefs_summaries->chkbtn_swapfrom = chkbtn_swapfrom;
        prefs_summaries->chkbtn_useaddrbook = chkbtn_useaddrbook;
        prefs_summaries->chkbtn_threadsubj = chkbtn_threadsubj;
        prefs_summaries->entry_datefmt = entry_datefmt;
@@ -969,8 +966,6 @@ void prefs_summaries_save(PrefsPage *_page)
                        GTK_TOGGLE_BUTTON(page->chkbtn_transhdr));
        prefs_common.display_folder_unread = gtk_toggle_button_get_active(
                        GTK_TOGGLE_BUTTON(page->chkbtn_folder_unread));
-       prefs_common.swap_from = gtk_toggle_button_get_active(
-                       GTK_TOGGLE_BUTTON(page->chkbtn_swapfrom));
        prefs_common.use_addr_book = gtk_toggle_button_get_active(
                        GTK_TOGGLE_BUTTON(page->chkbtn_useaddrbook));
        prefs_common.thread_by_subject = gtk_toggle_button_get_active(
index 4acc27c..6901a0c 100644 (file)
@@ -81,6 +81,7 @@ static const gchar *const col_name[N_SUMMARY_COLS] = {
        N_("Attachment"),       /* S_COL_MIME    */
        N_("Subject"),          /* S_COL_SUBJECT */
        N_("From"),             /* S_COL_FROM    */
+       N_("To"),               /* S_COL_TO      */
        N_("Date"),             /* S_COL_DATE    */
        N_("Size"),             /* S_COL_SIZE    */
        N_("Number"),           /* S_COL_NUMBER  */
@@ -94,11 +95,12 @@ static SummaryColumnState default_state[N_SUMMARY_COLS] = {
        { S_COL_MIME   , TRUE  },
        { S_COL_SUBJECT, TRUE  },
        { S_COL_FROM   , TRUE  },
+       { S_COL_TO     , FALSE },
        { S_COL_DATE   , TRUE  },
        { S_COL_SIZE   , TRUE  },
        { S_COL_NUMBER , FALSE },
         { S_COL_SCORE  , FALSE },
-       { S_COL_LOCKED , FALSE  },
+       { S_COL_LOCKED , FALSE },
 };
 
 static void prefs_summary_column_create        (void);
index 16744bf..13bd4a4 100644 (file)
@@ -312,6 +312,8 @@ static void summary_date_clicked    (GtkWidget              *button,
                                         SummaryView            *summaryview);
 static void summary_from_clicked       (GtkWidget              *button,
                                         SummaryView            *summaryview);
+static void summary_to_clicked         (GtkWidget              *button,
+                                        SummaryView            *summaryview);
 static void summary_subject_clicked    (GtkWidget              *button,
                                         SummaryView            *summaryview);
 static void summary_score_clicked      (GtkWidget              *button,
@@ -459,6 +461,7 @@ static const gchar *const col_label[N_SUMMARY_COLS] = {
        "",             /* S_COL_MIME    */
        N_("Subject"),  /* S_COL_SUBJECT */
        N_("From"),     /* S_COL_FROM    */
+       N_("To"),       /* S_COL_TO      */
        N_("Date"),     /* S_COL_DATE    */
        N_("Size"),     /* S_COL_SIZE    */
        N_("No."),      /* S_COL_NUMBER  */
@@ -1948,6 +1951,7 @@ static void summary_set_column_titles(SummaryView *summaryview)
                SORT_BY_MIME,
                SORT_BY_SUBJECT,
                SORT_BY_FROM,
+               SORT_BY_TO,
                SORT_BY_DATE,
                SORT_BY_SIZE,
                SORT_BY_NUMBER,
@@ -1966,6 +1970,7 @@ static void summary_set_column_titles(SummaryView *summaryview)
                switch (type) {
                case S_COL_SUBJECT:
                case S_COL_FROM:
+               case S_COL_TO:
                case S_COL_DATE:
                case S_COL_NUMBER:
                        if (prefs_common.trans_hdr)
@@ -2045,6 +2050,9 @@ void summary_sort(SummaryView *summaryview,
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
        GtkCListCompareFunc cmp_func = NULL;
+       g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
+                                      G_CALLBACK(summary_tree_expanded), summaryview);
+       gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
 
        switch (sort_key) {
        case SORT_BY_MARK:
@@ -2089,7 +2097,7 @@ void summary_sort(SummaryView *summaryview,
        case SORT_BY_NONE:
                break;
        default:
-               return;
+               goto unlock;
        }
 
        summaryview->sort_key = sort_key;
@@ -2100,7 +2108,7 @@ void summary_sort(SummaryView *summaryview,
 
        /* allow fallback to don't sort */
        if (summaryview->sort_key == SORT_BY_NONE)
-               return;
+               goto unlock;
 
        if (cmp_func != NULL) {
                debug_print("Sorting summary...");
@@ -2123,6 +2131,10 @@ void summary_sort(SummaryView *summaryview,
                debug_print("done.\n");
                STATUSBAR_POP(summaryview->mainwin);
        }
+unlock:
+       gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
+       g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
+                                      G_CALLBACK(summary_tree_expanded), summaryview);
 }
 
 gboolean summary_insert_gnode_func(GtkCTree *ctree, guint depth, GNode *gnode,
@@ -2149,6 +2161,7 @@ gboolean summary_insert_gnode_func(GtkCTree *ctree, guint depth, GNode *gnode,
        SET_TEXT(S_COL_SIZE);
        SET_TEXT(S_COL_DATE);
        SET_TEXT(S_COL_FROM);
+       SET_TEXT(S_COL_TO);
        SET_TEXT(S_COL_SUBJECT);
 
 #undef SET_TEXT
@@ -2275,35 +2288,10 @@ static void summary_set_ctree_from_list(SummaryView *summaryview,
        END_TIMING();
 }
 
-static gchar *summary_complete_address(const gchar *addr)
-{
-       gint count;
-       gchar *res, *tmp, *email_addr;
-
-       Xstrdup_a(email_addr, addr, return NULL);
-       extract_address(email_addr);
-       g_return_val_if_fail(*email_addr, NULL);
-
-       /*
-        * completion stuff must be already initialized
-        */
-       res = NULL;
-       if (1 < (count = complete_address(email_addr))) {
-               tmp = get_complete_address(1);
-/*     tmp = addressbook_lookup_name( email_addr );
-       if( tmp ) { */
-               res = procheader_get_fromname(tmp);
-               g_free(tmp);
-       }
-
-       return res;
-}
-
 static void summary_set_header(SummaryView *summaryview, gchar *text[],
                               MsgInfo *msginfo)
 {
        static gchar date_modified[80];
-       static gchar *to = NULL;
        static gchar col_score[11];
        static gchar buf[BUFFSIZE];
        gint *col_pos = summaryview->col_pos;
@@ -2329,46 +2317,12 @@ static void summary_set_header(SummaryView *summaryview, gchar *text[],
 
        text[col_pos[S_COL_FROM]] = msginfo->fromname ? msginfo->fromname :
                _("(No From)");
+       text[col_pos[S_COL_TO]] = msginfo->to ? msginfo->to :
+               _("(No Recipient)");
        
        if (msginfo->folder && msginfo->folder->folder)
                ftype = msginfo->folder->folder->klass->type; 
                
-       if (ftype != F_NEWS && prefs_common.swap_from && msginfo->from && msginfo->to) {
-               gchar *addr = NULL;
-
-               Xstrdup_a(addr, msginfo->from, return);
-               extract_address(addr);
-               if (prefs_common.use_addr_book) {
-                       if (account_find_from_address(addr)) {
-                               addr = summary_complete_address(msginfo->to);
-                               g_free(to);
-                               to   = g_strconcat("-->", addr == NULL ? msginfo->to : addr, NULL);
-                               text[col_pos[S_COL_FROM]] = to;
-                               g_free(addr);
-                       }
-               } else {
-                       if (account_find_from_address(addr)) {
-                               g_free(to);
-                               to = g_strconcat("-->", msginfo->to, NULL);
-                               text[col_pos[S_COL_FROM]] = to;
-                       }
-               }
-       }
-
-       /*
-        * CLAWS: note that the "text[col_pos[S_COL_FROM]] != to" is really a hack, 
-        * checking whether the above block (which handles the special case of
-        * the --> in sent boxes) was executed.
-        */
-       if (text[col_pos[S_COL_FROM]] != to && prefs_common.use_addr_book && msginfo->from) {
-               gchar *from = summary_complete_address(msginfo->from);
-               if (from) {
-                       g_free(to);
-                       to = from;
-                       text[col_pos[S_COL_FROM]] = to;
-               }                       
-       }
-
        if (summaryview->simplify_subject_preg != NULL)
                text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? 
                        string_remove_match(buf, BUFFSIZE, msginfo->subject, 
@@ -4322,6 +4276,8 @@ static GtkWidget *summary_ctree_create(SummaryView *summaryview)
                                   prefs_common.summary_col_size[S_COL_SUBJECT]);
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_FROM],
                                   prefs_common.summary_col_size[S_COL_FROM]);
+       gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_TO],
+                                  prefs_common.summary_col_size[S_COL_TO]);
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_DATE],
                                   prefs_common.summary_col_size[S_COL_DATE]);
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
@@ -4363,6 +4319,7 @@ static GtkWidget *summary_ctree_create(SummaryView *summaryview)
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked);
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked);
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked);
+       CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO     , summary_to_clicked);
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked);
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE,   summary_score_clicked);
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED,  summary_locked_clicked);
@@ -4803,9 +4760,6 @@ static void summary_sort_by_column_click(SummaryView *summaryview,
 {
        GtkCTreeNode *node = NULL;
        START_TIMING("summary_sort_by_column_click");
-       g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
-                                      G_CALLBACK(summary_tree_expanded), summaryview);
-       gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
        if (summaryview->sort_key == sort_key)
                summary_sort(summaryview, sort_key,
                             summaryview->sort_type == SORT_ASCENDING
@@ -4813,10 +4767,6 @@ static void summary_sort_by_column_click(SummaryView *summaryview,
        else
                summary_sort(summaryview, sort_key, SORT_ASCENDING);
 
-       gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
-       g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
-                                      G_CALLBACK(summary_tree_expanded), summaryview);
-
        node = GTK_CTREE_NODE(GTK_CLIST(summaryview->ctree)->row_list);
 
        while (prefs_common.bold_unread && node) {
@@ -4863,6 +4813,11 @@ static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
        summary_sort_by_column_click(summaryview, SORT_BY_FROM);
 }
 
+static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview)
+{
+       summary_sort_by_column_click(summaryview, SORT_BY_TO);
+}
+
 static void summary_subject_clicked(GtkWidget *button,
                                    SummaryView *summaryview)
 {
@@ -4974,24 +4929,6 @@ CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
 CMP_FUNC_DEF(summary_cmp_by_date, msginfo1->date_t - msginfo2->date_t)
 
-#undef CMP_FUNC_DEF
-#define CMP_FUNC_DEF(func_name, var_name)                               \
-static gint func_name(GtkCList *clist,                                  \
-                     gconstpointer ptr1, gconstpointer ptr2)            \
-{                                                                       \
-       MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;                 \
-       MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;                 \
-                                                                        \
-       if (!msginfo1->var_name)                                         \
-               return (msginfo2->var_name != NULL);                     \
-       if (!msginfo2->var_name)                                         \
-               return -1;                                               \
-                                                                        \
-       return g_utf8_collate(msginfo1->var_name, msginfo2->var_name);   \
-}
-
-CMP_FUNC_DEF(summary_cmp_by_to, to);
-
 #undef CMP_FUNC_DEF
 
 static gint summary_cmp_by_subject(GtkCList *clist,
@@ -5032,6 +4969,28 @@ static gint summary_cmp_by_from(GtkCList *clist, gconstpointer ptr1,
        return g_utf8_collate(str1, str2);
 }
  
+static gint summary_cmp_by_to(GtkCList *clist, gconstpointer ptr1,
+                               gconstpointer ptr2)
+{
+       const gchar *str1, *str2;
+       const GtkCListRow *r1 = (const GtkCListRow *) ptr1;
+       const GtkCListRow *r2 = (const GtkCListRow *) ptr2;
+       const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
+       
+       g_return_val_if_fail(sv, -1);
+       
+       str1 = GTK_CELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text;
+       str2 = GTK_CELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text;
+
+       if (!str1)
+               return str2 != NULL;
+       if (!str2)
+               return -1;
+       return g_utf8_collate(str1, str2);
+}
 static gint summary_cmp_by_simplified_subject
        (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
 {
index 921138b..b414ff3 100644 (file)
@@ -42,6 +42,7 @@ typedef enum
        S_COL_MIME,
        S_COL_SUBJECT,
        S_COL_FROM,
+       S_COL_TO,
        S_COL_DATE,
        S_COL_SIZE,
        S_COL_NUMBER,
@@ -49,7 +50,7 @@ typedef enum
        S_COL_LOCKED
 } SummaryColumnType;
 
-#define N_SUMMARY_COLS 10
+#define N_SUMMARY_COLS 11
 
 typedef enum
 {
index 48aeeb8..517e9a1 100644 (file)
@@ -830,324 +830,6 @@ static void textview_show_ertf(TextView *textview, FILE *fp,
        ertf_parser_destroy(parser);
 }
 
-/* get_uri_part() - retrieves a URI starting from scanpos.
-                   Returns TRUE if succesful */
-static gboolean get_uri_part(const gchar *start, const gchar *scanpos,
-                            const gchar **bp, const gchar **ep)
-{
-       const gchar *ep_;
-
-       g_return_val_if_fail(start != NULL, FALSE);
-       g_return_val_if_fail(scanpos != NULL, FALSE);
-       g_return_val_if_fail(bp != NULL, FALSE);
-       g_return_val_if_fail(ep != NULL, FALSE);
-
-       *bp = scanpos;
-
-       /* find end point of URI */
-       for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
-               if (!isgraph(*(const guchar *)ep_) ||
-                   !IS_ASCII(*(const guchar *)ep_) ||
-                   strchr("[]{}()<>\"", *ep_))
-                       break;
-       }
-
-       /* no punctuation at end of string */
-
-       /* FIXME: this stripping of trailing punctuations may bite with other URIs.
-        * should pass some URI type to this function and decide on that whether
-        * to perform punctuation stripping */
-
-#define IS_REAL_PUNCT(ch)      (ispunct(ch) && ((ch) != '/')) 
-
-       for (; ep_ - 1 > scanpos + 1 &&
-              IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
-            ep_--)
-               ;
-
-#undef IS_REAL_PUNCT
-
-       *ep = ep_;
-
-       return TRUE;            
-}
-
-static gchar *make_uri_string(const gchar *bp, const gchar *ep)
-{
-       return g_strndup(bp, ep - bp);
-}
-
-/* valid mail address characters */
-#define IS_RFC822_CHAR(ch) \
-       (IS_ASCII(ch) && \
-        (ch) > 32   && \
-        (ch) != 127 && \
-        !g_ascii_isspace(ch) && \
-        !strchr("(),;<>\"", (ch)))
-
-/* alphabet and number within 7bit ASCII */
-#define IS_ASCII_ALNUM(ch)     (IS_ASCII(ch) && g_ascii_isalnum(ch))
-#define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
-
-static GHashTable *create_domain_tab(void)
-{
-       static const gchar *toplvl_domains [] = {
-           "museum", "aero",
-           "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
-           "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
-           "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
-           "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
-           "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
-           "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
-           "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
-           "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
-           "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
-           "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
-           "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
-           "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
-           "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
-           "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
-           "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
-           "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
-           "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
-           "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
-           "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
-           "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
-           "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
-           "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
-           "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
-           "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
-           "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
-            "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw" 
-       };
-       gint n;
-       GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
-       
-       g_return_val_if_fail(htab, NULL);
-       for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++) 
-               g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
-       return htab;
-}
-
-static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
-{
-       const gint MAX_LVL_DOM_NAME_LEN = 6;
-       gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
-       const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
-       register gchar *p;
-       
-       if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
-               return FALSE;
-
-       for (p = buf; p < m &&  first < last; *p++ = *first++)
-               ;
-       *p = 0;
-
-       return g_hash_table_lookup(tab, buf) != NULL;
-}
-
-/* get_email_part() - retrieves an email address. Returns TRUE if succesful */
-static gboolean get_email_part(const gchar *start, const gchar *scanpos,
-                              const gchar **bp, const gchar **ep)
-{
-       /* more complex than the uri part because we need to scan back and forward starting from
-        * the scan position. */
-       gboolean result = FALSE;
-       const gchar *bp_ = NULL;
-       const gchar *ep_ = NULL;
-       static GHashTable *dom_tab;
-       const gchar *last_dot = NULL;
-       const gchar *prelast_dot = NULL;
-       const gchar *last_tld_char = NULL;
-       
-       /* the informative part of the email address (describing the name
-        * of the email address owner) may contain quoted parts. the
-        * closure stack stores the last encountered quotes. */
-       gchar closure_stack[128];
-       gchar *ptr = closure_stack;
-
-       g_return_val_if_fail(start != NULL, FALSE);
-       g_return_val_if_fail(scanpos != NULL, FALSE);
-       g_return_val_if_fail(bp != NULL, FALSE);
-       g_return_val_if_fail(ep != NULL, FALSE);
-
-       if (!dom_tab)
-               dom_tab = create_domain_tab();
-       g_return_val_if_fail(dom_tab, FALSE);   
-
-       /* scan start of address */
-       for (bp_ = scanpos - 1;
-            bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
-               ;
-
-       /* TODO: should start with an alnum? */
-       bp_++;
-       for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
-               ;
-
-       if (bp_ != scanpos) {
-               /* scan end of address */
-               for (ep_ = scanpos + 1;
-                    *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
-                       if (*ep_ == '.') {
-                               prelast_dot = last_dot;
-                               last_dot = ep_;
-                               if (*(last_dot + 1) == '.') {
-                                       if (prelast_dot == NULL)
-                                               return FALSE;
-                                       last_dot = prelast_dot;
-                                       break;
-                               }
-                       }
-
-               /* TODO: really should terminate with an alnum? */
-               for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
-                    --ep_)
-                       ;
-               ep_++;
-
-               if (last_dot == NULL)
-                       return FALSE;
-               if (last_dot >= ep_)
-                       last_dot = prelast_dot;
-               if (last_dot == NULL || (scanpos + 1 >= last_dot))
-                       return FALSE;
-               last_dot++;
-
-               for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
-                       if (*last_tld_char == '?')
-                               break;
-
-               if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
-                       result = TRUE;
-
-               *ep = ep_;
-               *bp = bp_;
-       }
-
-       if (!result) return FALSE;
-
-       if (*ep_ && *(bp_ - 1) == '"' && *(ep_) == '"' 
-       && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
-       && IS_RFC822_CHAR(*(ep_ + 3))) {
-               /* this informative part with an @ in it is 
-                * followed by the email address */
-               ep_ += 3;
-               
-               /* go to matching '>' (or next non-rfc822 char, like \n) */
-               for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
-                       ;
-                       
-               /* include the bracket */
-               if (*ep_ == '>') ep_++;
-               
-               /* include the leading quote */         
-               bp_--;
-
-               *ep = ep_;
-               *bp = bp_;
-               return TRUE;
-       }
-
-       /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
-       if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
-               return FALSE;
-
-       /* see if this is <bracketed>; in this case we also scan for the informative part. */
-       if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
-               return TRUE;
-
-#define FULL_STACK()   ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
-#define IN_STACK()     (ptr > closure_stack)
-/* has underrun check */
-#define POP_STACK()    if(IN_STACK()) --ptr
-/* has overrun check */
-#define PUSH_STACK(c)  if(!FULL_STACK()) *ptr++ = (c); else return TRUE
-/* has underrun check */
-#define PEEK_STACK()   (IN_STACK() ? *(ptr - 1) : 0)
-
-       ep_++;
-
-       /* scan for the informative part. */
-       for (bp_ -= 2; bp_ >= start; bp_--) {
-               /* if closure on the stack keep scanning */
-               if (PEEK_STACK() == *bp_) {
-                       POP_STACK();
-                       continue;
-               }
-               if (*bp_ == '\'' || *bp_ == '"') {
-                       PUSH_STACK(*bp_);
-                       continue;
-               }
-
-               /* if nothing in the closure stack, do the special conditions
-                * the following if..else expression simply checks whether 
-                * a token is acceptable. if not acceptable, the clause
-                * should terminate the loop with a 'break' */
-               if (!PEEK_STACK()) {
-                       if (*bp_ == '-'
-                       && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
-                       && (((bp_ + 1) < ep_)    && isalnum(*(bp_ + 1)))) {
-                               /* hyphens are allowed, but only in
-                                  between alnums */
-                       } else if (!strchr(",;:=?./+<>!&\r\n\t", *bp_)) {
-                               /* but anything not being a punctiation
-                                  is ok */
-                       } else {
-                               break; /* anything else is rejected */
-                       }
-               }
-       }
-
-       bp_++;
-
-#undef PEEK_STACK
-#undef PUSH_STACK
-#undef POP_STACK
-#undef IN_STACK
-#undef FULL_STACK
-
-       /* scan forward (should start with an alnum) */
-       for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
-               ;
-
-       *ep = ep_;
-       *bp = bp_;
-       
-       return result;
-}
-
-#undef IS_QUOTE
-#undef IS_ASCII_ALNUM
-#undef IS_RFC822_CHAR
-
-static gchar *make_email_string(const gchar *bp, const gchar *ep)
-{
-       /* returns a mailto: URI; mailto: is also used to detect the
-        * uri type later on in the button_pressed signal handler */
-       gchar *tmp;
-       gchar *result;
-
-       tmp = g_strndup(bp, ep - bp);
-       result = g_strconcat("mailto:", tmp, NULL);
-       g_free(tmp);
-
-       return result;
-}
-
-static gchar *make_http_string(const gchar *bp, const gchar *ep)
-{
-       /* returns an http: URI; */
-       gchar *tmp;
-       gchar *result;
-
-       tmp = g_strndup(bp, ep - bp);
-       result = g_strconcat("http://", tmp, NULL);
-       g_free(tmp);
-
-       return result;
-}
-
 #define ADD_TXT_POS(bp_, ep_, pti_) \
        if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
                last = last->next; \