Logo Search packages:      
Sourcecode: pan version File versions  Download package

prefs.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Pan - A Newsreader for Gtk+
 * Copyright (C) 2002  Charles Kerr <charles@rebelbase.com>
 *
 * 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; version 2 of the License.
 *
 * 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
 */

#include <config.h>

#include <glib.h>
#include <gtk/gtk.h>

#include <stdlib.h>
#include <string.h>

#include <pan/base/acache.h>
#include <pan/base/base-prefs.h>
#include <pan/base/debug.h>
#include <pan/base/pan-config.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/serverlist.h>
#include <pan/base/text-massager.h>
#include <pan/base/util-file.h>

#include <pan/filters/filter-score.h>

#include <pan/articlelist.h>
#include <pan/globals.h>
#include <pan/grouplist.h>
#include <pan/gui.h>
#include <pan/gui-headers.h>
#include <pan/gui-paned.h>
#include <pan/header-pane-renderer.h>
#include <pan/nntp.h>
#include <pan/pan-color-picker.h>
#include <pan/pan-font-picker.h>
#include <pan/pan-file-entry.h>
#include <pan/prefs.h>
#include <pan/queue.h>
#include <pan/server-menu-ui.h>
#include <pan/task-headers.h>
#include <pan/text.h>
#include <pan/util.h>

#include <pan/xpm/pan-pixbufs.h>

#define NORMAL_PANGO_DESCRIPTION "sans 11"
#define MONOSPACE_PANGO_DESCRIPTION  "courier 11"

#define DEFAULT_VALUE_CACHE_MAX_MEGS                        10
#define DEFAULT_VALUE_CACHE_FLUSH_ON_EXIT                   FALSE
#define DEFAULT_VALUE_COLOR_SUBJECT_COLUMN                  FALSE
#define DEFAULT_VALUE_COLOR_SCORE_COLUMN                    TRUE
#define DEFAULT_VALUE_GROUP_PANE_CUSTOM_FONT_ENABLED        FALSE
#define DEFAULT_VALUE_HEADER_PANE_CUSTOM_FONT_ENABLED       FALSE
#define DEFAULT_VALUE_HEADER_PANE_COLUMNS                   "1|2|4|3|5|6|7"
#define DEFAULT_VALUE_HEADER_PANE_HIDE_MULTIPARTS           TRUE
#define DEFAULT_VALUE_HEADER_PANE_THREADS_EXPANDED          FALSE
#define DEFAULT_VALUE_HEADER_PANE_DATE_FORMAT               "%x %X"
#define DEFAULT_VALUE_BODY_PANE_HEADERS                     243
#define DEFAULT_VALUE_BODY_PANE_CUSTOM_FONT_ENABLED         FALSE
#define DEFAULT_VALUE_BODY_PANE_SMOOTH_SCROLLING_ENABLED    TRUE
#define DEFAULT_VALUE_BODY_PANE_SMOOTH_SCROLLING_SPEED      10
#define DEFAULT_VALUE_BREAK_THREAD_WHEN_SUBJECT_CHANGES     FALSE
#define DEFAULT_VALUE_FETCH_NEW_AND_BODIES_ON_STARTUP       FALSE
#define DEFAULT_VALUE_FETCH_NEW_ON_GROUP_ENTER              TRUE
#define DEFAULT_VALUE_MARK_READ_ON_GROUP_EXIT               FALSE
#define DEFAULT_VALUE_FETCH_NEW_FROM_SUBSCRIBED_ON_STARTUP  FALSE
#define DEFAULT_VALUE_SINGLE_CLICK_SELECTS_GROUPS           FALSE
#define DEFAULT_VALUE_SINGLE_CLICK_SELECTS_HEADERS          FALSE
#define DEFAULT_VALUE_SMTP_PORT                             25
#define DEFAULT_VALUE_SMTP_ADDRESS                          NULL
#define DEFAULT_VALUE_QUOTE_CHARS                           ":>|}"

#define DEFAULT_VALUE_COLOR_WATCHED_BG                      "#FFFF00" /* Yellow */
#define DEFAULT_VALUE_COLOR_WATCHED_FG                      "#FF0000" /* Red */
#define DEFAULT_VALUE_COLOR_HIGH_FG                         "#00FF00" /* Green */
#define DEFAULT_VALUE_COLOR_HIGH_BG                         "#000000" /* Black */
#define DEFAULT_VALUE_COLOR_MEDIUM_BG                       "#FFFF00" /* Yellow */
#define DEFAULT_VALUE_COLOR_MEDIUM_FG                       "#000000" /* Black */
#define DEFAULT_VALUE_COLOR_LOW_BG                          "#545454" /* Dim Slate Grey */
#define DEFAULT_VALUE_COLOR_LOW_FG                          "#A8A8A8" /* Light Grey */
#define DEFAULT_VALUE_COLOR_IGNORED_BG                      "#000000" /* Black */
#define DEFAULT_VALUE_COLOR_IGNORED_FG                      "#A8A8A8" /* Light Grey */

#define DEFAULT_VALUE_COLOR_READ                            "#A8A8A8" /* Light Grey*/
#define DEFAULT_VALUE_COLOR_UNREAD                          "#000000" /* Black */
#define DEFAULT_VALUE_COLOR_URL                             "#0000FF" /* Blue */
#define DEFAULT_VALUE_COLOR_QUOTED_1                        "#660066" 
#define DEFAULT_VALUE_COLOR_QUOTED_2                        "#990000" /* Forest Green */
#define DEFAULT_VALUE_COLOR_QUOTED_3                        "#000099" /* Dark Purple */
#define DEFAULT_VALUE_COLOR_SIGNATURE                       "#007777" 

#ifdef G_OS_WIN32
#define DEFAULT_WEB_BROWSER "\"c:\\program files\\internet explorer\\iexplore\" %s"
#define DEFAULT_EXTERNAL_EDITOR "notepad %t"
#define DEFAULT_SCORE_EDITOR_COMMAND "notepad %t"
#else
#define DEFAULT_WEB_BROWSER "mozilla %s"
#define DEFAULT_EXTERNAL_EDITOR "xterm -e vi %t"
#define DEFAULT_SCORE_EDITOR_COMMAND "xterm -e vi +%n %t"
#endif

typedef struct
{
      GtkWidget * dialog;
      GtkWidget * notebook;

      /* smtp server */
      GtkWidget * smtp_address;
      GtkWidget * smtp_port;

      /* cache */
      GtkWidget * cache_megs_sb;
      GtkWidget * flush_cache_on_exit_check;

      /* general */
      GtkWidget * online_on_startup_cbutton;
      GtkWidget * single_click_selects_groups_cbutton;
      GtkWidget * single_click_selects_headers_cbutton;
      GtkWidget * remove_failed_tasks_cbutton;
      GtkWidget * fetch_new_on_group_enter_cbutton;
      GtkWidget * mark_read_on_group_exit_cbutton;
      GtkWidget * fetch_new_on_startup_cbutton;
      GtkWidget * fetch_new_and_bodies_on_startup_cbutton;
      GtkWidget * break_thread_when_subject_changes_cbutton;
      GtkWidget * expand_all_threads_by_default_cbutton;
      GtkWidget * hide_mpart_child_nodes_cbutton;
      GtkWidget * external_editor_combo;
      GtkWidget * external_browser_combo;

      /* directories */
      GtkWidget * dir_download;
      GtkWidget * dir_temp;
      GtkWidget * dir_data;

      /* fonts */
      GtkWidget * header_pane_custom_font_enabled_check;
      GtkWidget * header_pane_custom_font_gfp;
      GtkWidget * group_pane_custom_font_enabled_check;
      GtkWidget * group_pane_custom_font_gfp;
      GtkWidget * body_pane_custom_font_enabled_check;
      GtkWidget * body_pane_custom_font_gfp;
      GtkWidget * body_pane_monospace_font_gfp;

      /* colors */
      GtkWidget * read_pcp;
      GtkWidget * unread_pcp;
      GtkWidget * text_fg_pcp;
      GtkWidget * text_bg_pcp;
      GtkWidget * text_url_pcp;
      GtkWidget * text_quoted_pcp[3];
      GtkWidget * signature_pcp;
      GtkWidget * text_quoted_chars_entry;
      GtkWidget * thread_date_entry;
      GtkWidget * smooth_scrolling_check;
      GtkWidget * smooth_scrolling_speed_sb;

      /* layout */
      GtkWidget * layout_page;
      GtkWidget * body_pane_page;

      /* scoring */
      GtkWidget * scorefile_pfe;
      GtkWidget * score_editor_command_combo;

      GtkWidget * color_subject_column_check;
      GtkWidget * score_pcp[SCORE_COLOR_QTY][2];

      /* column order */
      GtkListStore * column_store;
      GtkWidget * column_tree_view;
      GtkWidget * column_up_cbutton;
      GtkWidget * column_down_cbutton;
}
PrefsWindow;


static char* layout_get_new_string (GtkWidget * layout_page);
static gulong get_header_flags (void);

static PrefsWindow * win;

extern GtkTooltips * ttips;

gboolean collapse_group_names                 = DEFAULT_VALUE_GROUP_PANE_COLLAPSE_NAMES;
gboolean text_window_smooth_scrolling         = DEFAULT_VALUE_BODY_PANE_SMOOTH_SCROLLING_ENABLED;
int      text_window_smooth_scrolling_speed   = DEFAULT_VALUE_BODY_PANE_SMOOTH_SCROLLING_SPEED;
gboolean do_spellcheck                        = DEFAULT_VALUE_POST_SPELLCHECK_ENABLED;

char * header_pane_date_format                = NULL;
char * layout_str                             = NULL;
int mail_server_port                          = DEFAULT_VALUE_SMTP_PORT;
gboolean expand_all_threads_by_default        = DEFAULT_VALUE_HEADER_PANE_THREADS_EXPANDED ;
gboolean hide_mpart_child_nodes               = DEFAULT_VALUE_HEADER_PANE_HIDE_MULTIPARTS;
gboolean pan_mute                             = FALSE;
gboolean fetch_new_on_group_enter             = DEFAULT_VALUE_FETCH_NEW_ON_GROUP_ENTER;
gboolean mark_read_on_group_exit              = DEFAULT_VALUE_MARK_READ_ON_GROUP_EXIT;
gboolean fetch_new_on_startup                 = DEFAULT_VALUE_FETCH_NEW_FROM_SUBSCRIBED_ON_STARTUP;
gboolean fetch_new_and_bodies_on_startup      = DEFAULT_VALUE_FETCH_NEW_AND_BODIES_ON_STARTUP;
gboolean header_pane_is_threaded              = DEFAULT_VALUE_HEADER_PANE_IS_THREADED;
gboolean single_click_selects_groups          = DEFAULT_VALUE_SINGLE_CLICK_SELECTS_GROUPS;
gboolean single_click_selects_headers         = DEFAULT_VALUE_SINGLE_CLICK_SELECTS_HEADERS;
gboolean show_group_pane                      = DEFAULT_VALUE_GROUP_PANE_ENABLED;
gboolean show_header_pane                     = DEFAULT_VALUE_HEADER_PANE_ENABLED;
gboolean show_body_pane                       = DEFAULT_VALUE_BODY_PANE_ENABLED;
gboolean header_pane_color_subject_column     = FALSE;
gboolean header_pane_color_score_column       = TRUE;

gboolean  group_pane_custom_font_enabled      = FALSE;
char *   group_pane_custom_font               = NULL;
gboolean header_pane_custom_font_enabled      = FALSE;
char *   header_pane_custom_font              = NULL;
gboolean body_pane_custom_font_enabled        = FALSE;
char *   body_pane_custom_font                = NULL;
gboolean body_pane_monospace_font_enabled     = DEFAULT_VALUE_BODY_PANE_MONOSPACE_FONT_ENABLED;
char *   body_pane_monospace_font             = NULL;

char * external_editor = NULL;
char * score_editor_command = NULL;
char * external_web_browser = NULL;
char * mail_server_address = NULL;

static void
set_color (GdkColor      * color,
           const char    * key,
           const char    * default_value)
{
      char * value = pan_config_get_string (key, default_value);

      if (!gdk_color_parse (value, color))
            g_warning ("couldn't parse color \"%s\"", value);
      else if (!gdk_color_alloc (cmap, color))
            g_error ("couldn't allocate color \"%s\"", key);

      g_free (value);
}

static void
date_help_clicked_cb (void)
{
      const char * str = _("%a - abbreviated weekday name\n"
                           "%A - full weekday name\n"
                           "%b - abbreviated month name\n"
                           "%B - full month name\n"
                           "%c - local date & time\n"
                           "%d - day of the month\n"
                           "%H - hour (24-hour clock)\n"
                           "%I - hour (12-hour clock)\n"
                           "%j - day of the year (001-366)\n"
                           "%m - month (01-12)\n"
                           "%M - minute (00-59)\n"
                           "%p - local equivalent of AM/PM\n"
                           "%S - second (00-61)\n"
                           "%x - local date\n"
                           "%X - local time\n"
                           "%y - two-digit year\n"
                           "%Y - four-digit year\n"
                           "%% - %");
      GtkWidget * w = gtk_message_dialog_new (GTK_WINDOW(win->dialog),
                                              GTK_DIALOG_DESTROY_WITH_PARENT,
                                              GTK_MESSAGE_INFO,
                                              GTK_BUTTONS_CLOSE, "%s", str);
      g_signal_connect_swapped (GTK_OBJECT(w), "response",
                                G_CALLBACK(gtk_widget_destroy), GTK_OBJECT (w));
      gtk_widget_show_all (w);
}

static void
pan_prefs_changed (GtkDialog * dialog)
{
        gtk_dialog_set_response_sensitive (GTK_DIALOG(win->dialog), GTK_RESPONSE_OK, TRUE);
        gtk_dialog_set_response_sensitive (GTK_DIALOG(win->dialog), GTK_RESPONSE_APPLY, TRUE);
}

#define connect_signal_to_prefs_changed(object,signal_name) \
      g_signal_connect_swapped (object, signal_name, G_CALLBACK(pan_prefs_changed), win->dialog)

/**
***  UPDATE UTILS
**/

static gboolean
update_entry_and_bool_from_toggle_button (gboolean     * setme,
                                          gboolean       default_value,
                                          const char   * key,
                                          GtkWidget    * toggle)
{
      const gboolean old_value = *setme != 0;
      const gboolean new_value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(toggle)) != 0;

      *setme = new_value;
      pan_config_set_bool_if_different (key, new_value, default_value);

      return old_value != new_value;
}

static void
set_config_from_editable (const char   * key,
                          GtkWidget    * entry)
{
      char * text = gtk_editable_get_chars (GTK_EDITABLE(entry), 0, -1);
      g_strstrip (text);
      if (is_nonempty_string(text))
            pan_config_set_string (key, text);
      else
            pan_config_clean_key (key);
      g_free (text);
}

static void
handle_editable (GtkWidget * e, char ** setme_value, const char * key, const char * default_value)
{
      char * new_value = gtk_editable_get_chars (GTK_EDITABLE(e), 0, -1);
      g_strstrip (new_value);
      pan_config_set_string_if_different (key, new_value, default_value);
      replace_gstr (setme_value, new_value);
}

static gboolean
handle_spin (GtkWidget * spin, const char * config_key, int * setme_value, int default_value)
{
      int old_value;
      int new_value;

      /* sanity clause */
      g_return_val_if_fail (GTK_IS_SPIN_BUTTON(spin), FALSE);
      g_return_val_if_fail (setme_value!=NULL, FALSE);
      g_return_val_if_fail (is_nonempty_string(config_key), FALSE);

      old_value = *setme_value;
      new_value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));

      *setme_value = new_value;
      pan_config_set_int_if_different (config_key, new_value, default_value);

      return old_value != new_value;
}

static gboolean
handle_font_picker (GtkWidget * pfp, char ** p_old_font, const char * config_key)
{
      gboolean changed = FALSE;

      /* get new... */
      char * new_font = pan_font_picker_get_font (pfp);

      /* compare old with new... */
      if (pan_strcmp (*p_old_font, new_font)) {
            replace_gstr (p_old_font, g_strdup(new_font));
            pan_config_set_string (config_key, new_font);
            changed = TRUE;
      }

      g_free (new_font);
      return changed;
}

static gboolean
handle_color_picker (const char    * key,
                     GdkColor      * color,
                     GtkWidget     * w)
{
      GdkColor picker_color;
      gboolean color_changed = FALSE;

      g_return_val_if_fail (w!=NULL, FALSE);

      pan_color_picker_get_color (w, &picker_color);

      color_changed = (color->red != picker_color.red)
                 || (color->green != picker_color.green)
                 || (color->blue != picker_color.blue);

      if (color_changed)
      {
            char value[1024];

            color_changed = TRUE;

            /* update the color */
            gdk_colormap_free_colors (cmap, color, 1);
            color->red = picker_color.red;
            color->green = picker_color.green;
            color->blue = picker_color.blue;
            if (!gdk_color_alloc (cmap, color))
                  g_error ("couldn't allocate color \"%s\"", key);

            /* update the config */
            g_snprintf (value, sizeof(value), "#%04x%04x%04x",
                        picker_color.red,
                        picker_color.green,
                        picker_color.blue);
            pan_config_set_string (key, value);
      }

      return color_changed;
}

static gboolean
handle_header_pane_columns (GtkTreeModel * model,
                        int          * articlelist_columns,
                      int            articlelist_column_qty,
                      const char   * key)
{
      gboolean      columns_changed = FALSE;
      gboolean      ok;
      GtkTreeIter   iter;
      int           idx;
      GString     * val = g_string_new (NULL);

      ok = gtk_tree_model_get_iter_first (model, &iter);

      for (idx=0; ok && idx < articlelist_column_qty ; ++idx)
      {
            int col;

            gtk_tree_model_get (model, &iter, 1, &col, -1);

            columns_changed|= articlelist_columns[idx] != col;
            articlelist_columns[idx] = col;

            g_string_append_printf (val, idx > 0 ? "|%u" : "%u", col+1);

            ok = gtk_tree_model_iter_next (model, &iter);
      }

      if (val->len > 0)
            pan_config_set_string_if_different (KEY_HEADER_PANE_COLUMNS, val->str, DEFAULT_VALUE_HEADER_PANE_COLUMNS);

      g_string_free (val, TRUE);

      return columns_changed;
}

/**
***  UPDATE EVERYTHING
**/

/* Ok or Apply pressed in the Preferences dialog, save all information */
static void
prefs_apply (void)
{
      gboolean group_pane_changed = FALSE;
      gboolean header_pane_changed = FALSE;
      gboolean color_changed = FALSE;
      gboolean b;

      /* Commit all of the data to the config file */

      /* thread date format */
      if (1) {
            char * tmp = g_strdup (header_pane_date_format);
            handle_editable (win->thread_date_entry, &header_pane_date_format, KEY_HEADER_PANE_DATE_FORMAT, DEFAULT_VALUE_HEADER_PANE_DATE_FORMAT);
            if (pan_strcmp (tmp, header_pane_date_format))
                  header_pane_changed = TRUE;
            g_free (tmp);
      }

      set_config_from_editable (
            "/Pan/Paths/download_dir",
            pan_file_entry_gtk_entry(win->dir_download));

      /**
      ***  Mail Server
      **/

      handle_editable (win->smtp_address, &mail_server_address, "/Pan/Mail/smtp_address", DEFAULT_VALUE_SMTP_ADDRESS);

      handle_spin (win->smtp_port, "/Pan/Mail/smtp_port", &mail_server_port, DEFAULT_VALUE_SMTP_PORT);

      /**
      ***  Scoring
      **/

      pan_config_set_string (KEY_SCOREFILE, pan_file_entry_get (win->scorefile_pfe));

      handle_editable (GTK_COMBO(win->score_editor_command_combo)->entry, &score_editor_command, KEY_APP_SCOREFILE_EDITOR, DEFAULT_SCORE_EDITOR_COMMAND);

      header_pane_changed |= update_entry_and_bool_from_toggle_button (&header_pane_color_subject_column,
                                                                       DEFAULT_VALUE_COLOR_SUBJECT_COLUMN,
                                                                       KEY_COLOR_SUBJECT_COLUMN,
                                                                       win->color_subject_column_check);
      header_pane_changed |= handle_color_picker (KEY_COLOR_WATCHED_BG,
                                                  &score_color[SCORE_COLOR_WATCHED][0],
                                                  win->score_pcp[SCORE_COLOR_WATCHED][0]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_WATCHED_FG,
                                                  &score_color[SCORE_COLOR_WATCHED][1],
                                                  win->score_pcp[SCORE_COLOR_WATCHED][1]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_HIGH_BG,
                                                  &score_color[SCORE_COLOR_HIGH][0],
                                                  win->score_pcp[SCORE_COLOR_HIGH][0]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_HIGH_FG,
                                                  &score_color[SCORE_COLOR_HIGH][1],
                                                  win->score_pcp[SCORE_COLOR_HIGH][1]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_MEDIUM_BG,
                                                  &score_color[SCORE_COLOR_MEDIUM][0],
                                                  win->score_pcp[SCORE_COLOR_MEDIUM][0]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_MEDIUM_FG,
                                                  &score_color[SCORE_COLOR_MEDIUM][1],
                                                  win->score_pcp[SCORE_COLOR_MEDIUM][1]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_LOW_BG,
                                                  &score_color[SCORE_COLOR_LOW][0],
                                                  win->score_pcp[SCORE_COLOR_LOW][0]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_LOW_FG,
                                                  &score_color[SCORE_COLOR_LOW][1],
                                                  win->score_pcp[SCORE_COLOR_LOW][1]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_IGNORED_BG,
                                                  &score_color[SCORE_COLOR_IGNORED][0],
                                                  win->score_pcp[SCORE_COLOR_IGNORED][0]);
      header_pane_changed |= handle_color_picker (KEY_COLOR_IGNORED_FG,
                                                  &score_color[SCORE_COLOR_IGNORED][1],
                                                  win->score_pcp[SCORE_COLOR_IGNORED][1]);
      /**
      ***  Display
      **/

      /* header pane colors */
      header_pane_changed |= handle_color_picker (KEY_COLOR_READ, &read_color, win->read_pcp);
      header_pane_changed |= handle_color_picker (KEY_COLOR_UNREAD, &unread_color, win->unread_pcp);

      /* body pane colors */
      b = 0;
      b |= handle_color_picker (KEY_COLOR_URL, &text_url_color, win->text_url_pcp);
      b |= handle_color_picker (KEY_COLOR_QUOTED_1, &text_quoted_color[0], win->text_quoted_pcp[0]);
      b |= handle_color_picker (KEY_COLOR_QUOTED_2, &text_quoted_color[1], win->text_quoted_pcp[1]);
      b |= handle_color_picker (KEY_COLOR_QUOTED_3, &text_quoted_color[2], win->text_quoted_pcp[2]);
      b |= handle_color_picker (KEY_COLOR_SIGNATURE, &signature_color, win->signature_pcp);
      color_changed = b;

      /* quote prefix characters */
      if (1)
      {
            char * old_quote_chars = NULL;
            char * new_quote_chars = NULL;
            TextMassager * tm;
             
            tm = text_pane_get_text_massager ();
                  old_quote_chars = text_massager_get_quote_chars (tm);
            handle_editable (win->text_quoted_chars_entry, &new_quote_chars, KEY_QUOTE_CHARS, DEFAULT_VALUE_QUOTE_CHARS);

            if (pan_strcmp (old_quote_chars, new_quote_chars))
            {
                  text_massager_set_quote_chars (tm, new_quote_chars);
                  color_changed = TRUE;
            }

            g_free (new_quote_chars);
            g_free (old_quote_chars);
      }

      if (color_changed)
      {
            GtkStyle * rc_style;
            GtkStyle * style;

            rc_style = gtk_rc_get_style (Pan.text);

            style = rc_style ? gtk_style_copy (rc_style) 
                         : gtk_style_new ();

            gtk_widget_set_style (Pan.text, style);

            text_set_font ();
            if (Pan.text)
            {
                  GtkTextBuffer * text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(Pan.text));
                  text_set_text_buffer_tags (text_buffer);
            }

            gtk_style_unref (style);
      }

      if (update_entry_and_bool_from_toggle_button (&group_pane_custom_font_enabled,
                                                    DEFAULT_VALUE_GROUP_PANE_CUSTOM_FONT_ENABLED,
                                                    KEY_GROUP_PANE_CUSTOM_FONT_ENABLED,
                                                    win->group_pane_custom_font_enabled_check))
            group_pane_changed = TRUE;

      if (update_entry_and_bool_from_toggle_button (&header_pane_custom_font_enabled,
                                                    DEFAULT_VALUE_HEADER_PANE_CUSTOM_FONT_ENABLED,
                                                    KEY_HEADER_PANE_CUSTOM_FONT_ENABLED,
                                                    win->header_pane_custom_font_enabled_check))
            header_pane_changed = TRUE;

      if (update_entry_and_bool_from_toggle_button (&body_pane_custom_font_enabled,
                                                    DEFAULT_VALUE_BODY_PANE_CUSTOM_FONT_ENABLED,
                                                    KEY_BODY_PANE_CUSTOM_FONT_ENABLED,
                                                    win->body_pane_custom_font_enabled_check))
            text_set_font ();

      if (handle_font_picker (win->group_pane_custom_font_gfp,
                              &group_pane_custom_font,
                              KEY_GROUP_PANE_CUSTOM_FONT))
            group_pane_changed = TRUE;

      if (handle_font_picker (win->header_pane_custom_font_gfp,
                              &header_pane_custom_font,
                              KEY_HEADER_PANE_CUSTOM_FONT))
            header_pane_changed = TRUE;

      update_entry_and_bool_from_toggle_button (&text_window_smooth_scrolling,
                                                DEFAULT_VALUE_BODY_PANE_SMOOTH_SCROLLING_ENABLED,
                                                KEY_BODY_PANE_SMOOTH_SCROLLING_ENABLED,
                                                win->smooth_scrolling_check);

      if (1) {
            GtkSpinButton* w = GTK_SPIN_BUTTON(
                  win->smooth_scrolling_speed_sb);
            const gint i = gtk_spin_button_get_value_as_int (w);
            if (i != text_window_smooth_scrolling_speed) {
                  text_window_smooth_scrolling_speed = i;
                  pan_config_set_int (KEY_BODY_PANE_SMOOTH_SCROLLING_SPEED, i);

            }
      }

      b = handle_font_picker (win->body_pane_custom_font_gfp,
                              &body_pane_custom_font,
                              KEY_BODY_PANE_CUSTOM_FONT);

      b |= handle_font_picker (win->body_pane_monospace_font_gfp,
                              &body_pane_monospace_font,
                              KEY_BODY_PANE_MONOSPACE_FONT);

      if (b) {
            text_set_font ();
            if (Pan.text)
            {
                  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(Pan.text));
                  text_set_text_buffer_tags (text_buffer);
            }
      }

      if (1) {
            char * str = layout_get_new_string (win->layout_page);
            if (pan_strcmp (str, layout_str)) {
                  replace_gstr (&layout_str, g_strdup(str));
                  pan_config_set_string (KEY_PANE_LAYOUT, layout_str);
                  gui_layout_refresh ();
            }
            g_free (str);
      }

      /**
      ***  General
      **/

      update_entry_and_bool_from_toggle_button (&fetch_new_on_group_enter,
                                                DEFAULT_VALUE_FETCH_NEW_ON_GROUP_ENTER,
                                                KEY_FETCH_NEW_ON_GROUP_ENTER,
                                                win->fetch_new_on_group_enter_cbutton);

      update_entry_and_bool_from_toggle_button (&mark_read_on_group_exit,
                                                DEFAULT_VALUE_MARK_READ_ON_GROUP_EXIT,
                                                KEY_MARK_READ_ON_GROUP_EXIT,
                                                win->mark_read_on_group_exit_cbutton);

      update_entry_and_bool_from_toggle_button (&fetch_new_on_startup,
                                                DEFAULT_VALUE_FETCH_NEW_FROM_SUBSCRIBED_ON_STARTUP,
                                                KEY_FETCH_NEW_FROM_SUBSCRIBED_ON_STARTUP,
                                                win->fetch_new_on_startup_cbutton);

      update_entry_and_bool_from_toggle_button (&single_click_selects_groups,
                                                DEFAULT_VALUE_SINGLE_CLICK_SELECTS_GROUPS,
                                                KEY_SINGLE_CLICK_SELECTS_GROUPS,
                                                win->single_click_selects_groups_cbutton);

      update_entry_and_bool_from_toggle_button (&single_click_selects_headers,
                                                DEFAULT_VALUE_SINGLE_CLICK_SELECTS_HEADERS,
                                                KEY_SINGLE_CLICK_SELECTS_HEADERS,
                                                win->single_click_selects_headers_cbutton);

      update_entry_and_bool_from_toggle_button (&fetch_new_and_bodies_on_startup,
                                                DEFAULT_VALUE_FETCH_NEW_AND_BODIES_ON_STARTUP,
                                                KEY_FETCH_NEW_AND_BODIES_ON_STARTUP,
                                                win->fetch_new_and_bodies_on_startup_cbutton);

      if (update_entry_and_bool_from_toggle_button (&break_thread_when_subject_changes,
                                                    DEFAULT_VALUE_BREAK_THREAD_WHEN_SUBJECT_CHANGES,
                                                    KEY_BREAK_THREAD_WHEN_SUBJECT_CHANGES,
                                                    win->break_thread_when_subject_changes_cbutton))
            header_pane_changed = TRUE;

      if (update_entry_and_bool_from_toggle_button (&expand_all_threads_by_default,
                                                    DEFAULT_VALUE_HEADER_PANE_THREADS_EXPANDED,
                                                    KEY_HEADER_PANE_THREADS_EXPANDED,
                                                    win->expand_all_threads_by_default_cbutton))
            header_pane_changed = TRUE;

      if (update_entry_and_bool_from_toggle_button (&hide_mpart_child_nodes,
                                                    DEFAULT_VALUE_HEADER_PANE_HIDE_MULTIPARTS,
                                                    KEY_HEADER_PANE_HIDE_MULTIPARTS,
                                                    win->hide_mpart_child_nodes_cbutton))
            header_pane_changed = TRUE;

      if (handle_header_pane_columns (GTK_TREE_MODEL (win->column_store),
                                      articlelist_columns,
                                      COLUMN_TYPE_QTY,
                                      KEY_HEADER_PANE_COLUMNS))
            header_pane_changed = TRUE;


      /* remove failed tasks */
      if (1) {
            const gboolean old_val = queue_get_remove_failed_tasks ();
            gboolean new_val = old_val;
            update_entry_and_bool_from_toggle_button (&new_val,
                                                      DEFAULT_VALUE_REMOVE_FAILED_TASKS,
                                                      KEY_REMOVE_FAILED_TASKS,
                                                      win->remove_failed_tasks_cbutton);
            if (old_val != new_val)
                  queue_set_remove_failed_tasks (new_val);
      }

      /**
      ***  Cache
      **/

      /* cache */
      if (1) {
            acache_max_megs = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(win->cache_megs_sb));
            pan_config_set_int_if_different (KEY_CACHE_MAXIMUM_SIZE_MEGS, acache_max_megs, DEFAULT_VALUE_CACHE_MAX_MEGS);
            acache_expire ();
      }

      pan_config_set_bool_if_different (KEY_CACHE_FLUSH_ON_EXIT,
                                        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON( win->flush_cache_on_exit_check)),
                                        DEFAULT_VALUE_CACHE_FLUSH_ON_EXIT);


      /* article headers */
      if (1) {
            gulong flags = get_header_flags ( );
            if (header_flags != flags) {
                  pan_config_set_ulong (KEY_BODY_PANE_HEADERS, flags);
                  header_flags = flags;
                  text_refresh ();
            }
      }
      


      handle_editable (GTK_COMBO(win->external_editor_combo)->entry,
                       &external_editor,
                       KEY_APP_EDITOR,
                       DEFAULT_EXTERNAL_EDITOR);

      handle_editable (GTK_COMBO(win->external_browser_combo)->entry,
                       &external_web_browser,
                       KEY_APP_BROWSER,
                       DEFAULT_WEB_BROWSER);

      /**
      **/

      if (group_pane_changed)
            grouplist_update_font ();

      if (header_pane_changed) {
            articlelist_reset_style_nolock ();
            articlelist_update_columns ();
      }

      pan_config_sync ();

      prefs_init ();
      gtk_dialog_set_response_sensitive (GTK_DIALOG(win->dialog), GTK_RESPONSE_CANCEL, FALSE);
      gtk_dialog_set_response_sensitive (GTK_DIALOG(win->dialog), GTK_RESPONSE_APPLY, FALSE);
}


static void
ensure_trailing_slash (char ** pch)
{
      char * m = strrchr (*pch, G_DIR_SEPARATOR);
      if (!m || m[1]!='\0')
            replace_gstr (pch, g_strdup_printf ("%s%c", *pch, G_DIR_SEPARATOR));
}

/**
 * This is where all the user preferences get loaded in.
 */
void
prefs_init (void)
{
      char * pch;
      const char * march;
      int idx, col;
      debug_enter ("prefs_init");

      /* get the layout string */
      replace_gstr (&layout_str, pan_config_get_string (KEY_PANE_LAYOUT, "4gta"));

      /* get download directory */
      replace_gstr (&download_dir, pan_config_get_string ("/Pan/Paths/download_dir", NULL));
      if (is_nonempty_string (download_dir))
            ensure_trailing_slash (&download_dir);
      else
            download_dir = g_build_filename (pan_get_home_dir(), "News", "Pan", NULL);


      acache_max_megs = pan_config_get_int (KEY_CACHE_MAXIMUM_SIZE_MEGS, DEFAULT_VALUE_CACHE_MAX_MEGS);

      /* scoring preferences */
      replace_gstr (&score_editor_command, pan_config_get_string (KEY_APP_SCOREFILE_EDITOR, DEFAULT_SCORE_EDITOR_COMMAND));
      header_pane_color_subject_column = pan_config_get_bool (KEY_COLOR_SUBJECT_COLUMN,       DEFAULT_VALUE_COLOR_SUBJECT_COLUMN);
      header_pane_color_score_column   = pan_config_get_bool (KEY_COLOR_SCORE_COLUMN,         DEFAULT_VALUE_COLOR_SCORE_COLUMN);

      /* display preferences */
      text_set_wrap (pan_config_get_bool(KEY_BODY_PANE_WRAP_ENABLED, DEFAULT_VALUE_BODY_PANE_WRAP_ENABLED));
      collapse_group_names                = pan_config_get_bool (KEY_GROUP_PANE_COLLAPSE_GROUP_NAMES,     DEFAULT_VALUE_GROUP_PANE_COLLAPSE_NAMES);
      show_group_pane                     = pan_config_get_bool (KEY_GROUP_PANE_ENABLED,                  DEFAULT_VALUE_GROUP_PANE_ENABLED);
      show_header_pane                    = pan_config_get_bool (KEY_HEADER_PANE_ENABLED,                 DEFAULT_VALUE_HEADER_PANE_ENABLED);
      show_body_pane                      = pan_config_get_bool (KEY_BODY_PANE_ENABLED,                   DEFAULT_VALUE_BODY_PANE_ENABLED);
      header_pane_is_threaded             = pan_config_get_bool (KEY_HEADER_PANE_THREADING_ENABLED,       DEFAULT_VALUE_HEADER_PANE_IS_THREADED);
      text_window_smooth_scrolling        = pan_config_get_bool (KEY_BODY_PANE_SMOOTH_SCROLLING_ENABLED,  DEFAULT_VALUE_BODY_PANE_SMOOTH_SCROLLING_ENABLED);
      text_window_smooth_scrolling_speed  = pan_config_get_int  (KEY_BODY_PANE_SMOOTH_SCROLLING_SPEED,    DEFAULT_VALUE_BODY_PANE_SMOOTH_SCROLLING_SPEED);
      body_pane_monospace_font_enabled    = pan_config_get_bool (KEY_BODY_PANE_MONOSPACE_FONT_ENABLED,    DEFAULT_VALUE_BODY_PANE_MONOSPACE_FONT_ENABLED);
      group_pane_custom_font_enabled      = pan_config_get_bool (KEY_GROUP_PANE_CUSTOM_FONT_ENABLED,      DEFAULT_VALUE_GROUP_PANE_CUSTOM_FONT_ENABLED);
      header_pane_custom_font_enabled     = pan_config_get_bool (KEY_HEADER_PANE_CUSTOM_FONT_ENABLED,     DEFAULT_VALUE_HEADER_PANE_CUSTOM_FONT_ENABLED);
      body_pane_custom_font_enabled       = pan_config_get_bool (KEY_BODY_PANE_CUSTOM_FONT_ENABLED,       DEFAULT_VALUE_BODY_PANE_CUSTOM_FONT_ENABLED);

      /* set the quote characters column */
      pch = pan_config_get_string (KEY_QUOTE_CHARS, DEFAULT_VALUE_QUOTE_CHARS);
      text_massager_set_quote_chars (text_pane_get_text_massager(), pch);
      g_free (pch);

      /* don't let the user turn off _all_ the panes; that's too confusing. */
      if (!show_group_pane && !show_header_pane && !show_body_pane)
            show_group_pane = show_header_pane = show_body_pane = TRUE;

      /* behavior */
      text_massager_set_wrap_column (text_pane_get_text_massager(), pan_config_get_int (KEY_WRAP_COLUMN,   DEFAULT_VALUE_WRAP_COLUMN));
      hide_mpart_child_nodes             = pan_config_get_bool (KEY_HEADER_PANE_HIDE_MULTIPARTS,           DEFAULT_VALUE_HEADER_PANE_HIDE_MULTIPARTS);
      expand_all_threads_by_default      = pan_config_get_bool (KEY_HEADER_PANE_THREADS_EXPANDED,          DEFAULT_VALUE_HEADER_PANE_THREADS_EXPANDED);
      break_thread_when_subject_changes  = pan_config_get_bool (KEY_BREAK_THREAD_WHEN_SUBJECT_CHANGES,     DEFAULT_VALUE_BREAK_THREAD_WHEN_SUBJECT_CHANGES);
      fetch_new_and_bodies_on_startup    = pan_config_get_bool (KEY_FETCH_NEW_AND_BODIES_ON_STARTUP,       DEFAULT_VALUE_FETCH_NEW_AND_BODIES_ON_STARTUP);
      fetch_new_on_group_enter           = pan_config_get_bool (KEY_FETCH_NEW_ON_GROUP_ENTER,              DEFAULT_VALUE_FETCH_NEW_ON_GROUP_ENTER);
      mark_read_on_group_exit            = pan_config_get_bool (KEY_MARK_READ_ON_GROUP_EXIT,               DEFAULT_VALUE_MARK_READ_ON_GROUP_EXIT);
      fetch_new_on_startup               = pan_config_get_bool (KEY_FETCH_NEW_FROM_SUBSCRIBED_ON_STARTUP,  DEFAULT_VALUE_FETCH_NEW_FROM_SUBSCRIBED_ON_STARTUP);
      single_click_selects_groups        = pan_config_get_bool (KEY_SINGLE_CLICK_SELECTS_GROUPS,           DEFAULT_VALUE_SINGLE_CLICK_SELECTS_GROUPS);
      single_click_selects_headers       = pan_config_get_bool (KEY_SINGLE_CLICK_SELECTS_HEADERS,          DEFAULT_VALUE_SINGLE_CLICK_SELECTS_HEADERS);


      header_flags = pan_config_get_ulong (KEY_BODY_PANE_HEADERS, DEFAULT_VALUE_BODY_PANE_HEADERS);

      /**
      ***  Unsorted
      **/

      do_spellcheck = pan_config_get_bool (KEY_POST_SPELLCHECK_ENABLED, DEFAULT_VALUE_POST_SPELLCHECK_ENABLED);

      replace_gstr (&header_pane_date_format, pan_config_get_string (KEY_HEADER_PANE_DATE_FORMAT, DEFAULT_VALUE_HEADER_PANE_DATE_FORMAT));

      replace_gstr (&external_editor, pan_config_get_string (KEY_APP_EDITOR, DEFAULT_EXTERNAL_EDITOR));

      replace_gstr (&external_web_browser, pan_config_get_string (KEY_APP_BROWSER, NULL));
      if (!external_web_browser) {
            const char * browser = g_getenv ("BROWSER");
            if (!is_nonempty_string(browser))
                  browser = DEFAULT_WEB_BROWSER;
            external_web_browser = g_strdup (browser);
      }

      /* mail server preferences */

      replace_gstr (&mail_server_address, pan_config_get_string ("/Pan/Mail/smtp_address", DEFAULT_VALUE_SMTP_ADDRESS));

      mail_server_port = pan_config_get_int ("/Pan/Mail/smtp_port", DEFAULT_VALUE_SMTP_PORT);

      /**
      ***  Fonts
      **/

      replace_gstr (&group_pane_custom_font, pan_config_get_string (KEY_GROUP_PANE_CUSTOM_FONT,  NORMAL_PANGO_DESCRIPTION));

      replace_gstr (&header_pane_custom_font, pan_config_get_string (KEY_HEADER_PANE_CUSTOM_FONT,  NORMAL_PANGO_DESCRIPTION));

      replace_gstr (&body_pane_custom_font, pan_config_get_string (KEY_BODY_PANE_CUSTOM_FONT, NORMAL_PANGO_DESCRIPTION));

      replace_gstr (&body_pane_monospace_font, pan_config_get_string (KEY_BODY_PANE_MONOSPACE_FONT, MONOSPACE_PANGO_DESCRIPTION));

      /**
      ***  Colors
      **/

      set_color (&score_color[SCORE_COLOR_WATCHED][0],   KEY_COLOR_WATCHED_BG,    DEFAULT_VALUE_COLOR_WATCHED_BG);
      set_color (&score_color[SCORE_COLOR_WATCHED][1],   KEY_COLOR_WATCHED_FG,    DEFAULT_VALUE_COLOR_WATCHED_FG);
      set_color (&score_color[SCORE_COLOR_HIGH][0],      KEY_COLOR_HIGH_BG,       DEFAULT_VALUE_COLOR_HIGH_FG);
      set_color (&score_color[SCORE_COLOR_HIGH][1],      KEY_COLOR_HIGH_FG,       DEFAULT_VALUE_COLOR_HIGH_BG);
      set_color (&score_color[SCORE_COLOR_MEDIUM][0],    KEY_COLOR_MEDIUM_BG,     DEFAULT_VALUE_COLOR_MEDIUM_BG);
      set_color (&score_color[SCORE_COLOR_MEDIUM][1],    KEY_COLOR_MEDIUM_FG,     DEFAULT_VALUE_COLOR_MEDIUM_FG);
      set_color (&score_color[SCORE_COLOR_LOW][0],       KEY_COLOR_LOW_BG,        DEFAULT_VALUE_COLOR_LOW_BG);
      set_color (&score_color[SCORE_COLOR_LOW][1],       KEY_COLOR_LOW_FG,        DEFAULT_VALUE_COLOR_LOW_FG);
      set_color (&score_color[SCORE_COLOR_IGNORED][0],   KEY_COLOR_IGNORED_BG,    DEFAULT_VALUE_COLOR_IGNORED_BG);
      set_color (&score_color[SCORE_COLOR_IGNORED][1],   KEY_COLOR_IGNORED_FG,    DEFAULT_VALUE_COLOR_IGNORED_FG);
      set_color (&read_color,                            KEY_COLOR_READ,          DEFAULT_VALUE_COLOR_READ);
      set_color (&unread_color,                          KEY_COLOR_UNREAD,        DEFAULT_VALUE_COLOR_UNREAD);
      set_color (&text_url_color,                        KEY_COLOR_URL,           DEFAULT_VALUE_COLOR_URL);
      set_color (&text_quoted_color[0],                  KEY_COLOR_QUOTED_1,      DEFAULT_VALUE_COLOR_QUOTED_1); 
      set_color (&text_quoted_color[1],                  KEY_COLOR_QUOTED_2,      DEFAULT_VALUE_COLOR_QUOTED_2);
      set_color (&text_quoted_color[2],                  KEY_COLOR_QUOTED_3,      DEFAULT_VALUE_COLOR_QUOTED_3);
      set_color (&signature_color,                     KEY_COLOR_SIGNATURE,       DEFAULT_VALUE_COLOR_SIGNATURE); 

      /**
      ***
      **/

      march = pch = pan_config_get_string (KEY_HEADER_PANE_COLUMNS, DEFAULT_VALUE_HEADER_PANE_COLUMNS);
      for (idx=0; (col = get_next_token_int (march, '|', &march)) != 0; ++idx)
      {
            --col;
            if (col < COLUMN_ACTION_STATE || col >= COLUMN_TYPE_QTY)
                  continue;

            articlelist_columns[idx] = col;
      }
      g_free (pch);

      base_prefs_init (download_dir,
                       acache_max_megs,
                   pan_config_get_bool(KEY_CACHE_FLUSH_ON_EXIT, FALSE),
                       break_thread_when_subject_changes);

      debug_exit ("prefs_init");
}

void
show_group_substitution_help_dialog (gpointer window)
{
      const char * str = _("%g - group as one directory (alt.binaries.pictures.trains)\n"
                           "%G - group as nested directory (/alt/binaries/pictures/trains)\n"
                           " \n"
                           "\"/home/user/News/Pan/%g\" becomes\n"
                           "\"/home/user/News/Pan/alt.binaries.pictures.trains\", and\n"
                           "\"/home/user/News/Pan/%G\" becomes\n"
                           "\"/home/user/News/Pan/alt/binaries/pictures/trains\",");
      GtkWidget * w = gtk_message_dialog_new (GTK_WINDOW(window),
                                              GTK_DIALOG_DESTROY_WITH_PARENT,
                                              GTK_MESSAGE_INFO,
                                              GTK_BUTTONS_CLOSE, "%s", str);
      g_signal_connect_swapped (GTK_OBJECT(w), "response",
                                G_CALLBACK(gtk_widget_destroy), GTK_OBJECT (w));
      gtk_widget_show_all (w);
}

/****
*****
*****   THE CONFIGURE PAGES
*****
*****   * font_page
*****   * color_page
*****   * layout_page
*****   * header_pane_page
*****   * body_pane_page
*****   * behavior_page
*****   * apps_and_mail_page
*****
****/

static void
add_wide_check_button (GtkWidget     * t,
                       int           * row,
                       const char    * mnemonic_string,
                       gboolean        is_active,
                       GtkWidget   ** setme_cbutton)
{
      GtkWidget * w = pan_hig_workarea_add_wide_checkbutton (t, row, mnemonic_string, is_active);
      if (setme_cbutton != NULL)
            *setme_cbutton = w;
      connect_signal_to_prefs_changed (w, "toggled");
}

/**
***  Font Page
**/

static void
custom_font_check_toggled (GtkToggleButton * tb, gpointer user_data)
{
      GtkWidget * font_picker = GTK_WIDGET(user_data);
      const gboolean custom_font_enabled = gtk_toggle_button_get_active (tb);
      gtk_widget_set_sensitive (font_picker, custom_font_enabled);
}

static void
font_page_add_font_row (GtkWidget    * t,
                        int          * row,
                        const char   * mnemonic_string,
                        gboolean       enabled,
                        const char   * font_name,
                        GtkWidget   ** setme_label,
                        GtkWidget   ** setme_font_picker)
{
      GtkWidget * l;
      GtkWidget * w;

      l = gtk_check_button_new_with_mnemonic (mnemonic_string);
      *setme_label = l;
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(l), enabled);
      connect_signal_to_prefs_changed (l, "toggled");
      gtk_table_attach (GTK_TABLE(t), l, 1, 2, *row, *row+1, GTK_FILL, 0, 0, 0);

      w = pan_font_picker_new ();
      *setme_font_picker = w;
      g_signal_connect (l, "toggled", G_CALLBACK(custom_font_check_toggled), w);
      gtk_widget_set_sensitive (w, enabled);
      pan_font_picker_set_font (w, font_name);
      connect_signal_to_prefs_changed (w, "clicked");
      gtk_table_attach_defaults (GTK_TABLE(t), w, 3, 4, *row, *row+1);

      ++*row;
}

static GtkWidget*
font_page (void)
{
      GtkWidget * w;
      GtkWidget * t;
      int row;

      row = 0;
      
      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_title (t, &row, _("Fonts"));

            pan_hig_workarea_add_section_spacer (t, row, 4);

            font_page_add_font_row (t, &row, _("Use custom font in Group Pane:"),
                              group_pane_custom_font_enabled,
                              group_pane_custom_font,
                              &win->group_pane_custom_font_enabled_check,
                              &win->group_pane_custom_font_gfp);

            font_page_add_font_row (t, &row, _("Use custom font in Header Pane:"),
                              header_pane_custom_font_enabled,
                              header_pane_custom_font,
                              &win->header_pane_custom_font_enabled_check,
                              &win->header_pane_custom_font_gfp);

            font_page_add_font_row (t, &row, _("Use custom font in Body Pane:"),
                              body_pane_custom_font_enabled,
                              body_pane_custom_font,
                              &win->body_pane_custom_font_enabled_check,
                              &win->body_pane_custom_font_gfp);

            w = win->body_pane_monospace_font_gfp = pan_font_picker_new ();
            pan_font_picker_set_font (w, body_pane_monospace_font);
            connect_signal_to_prefs_changed (w, "clicked");
            pan_hig_workarea_add_row (t, &row, _("Monospac_e font:"), w, NULL);

      return t;
}

/**
***  Colors Page
**/

static void
add_color_picker (GtkWidget * t, const char * label, GtkWidget ** setme_pcp, GdkColor * color,  int * row)
{
      GtkWidget * w;

      w = *setme_pcp = pan_color_picker_new ();
      pan_color_picker_set_color (w, color);
      connect_signal_to_prefs_changed (w, "clicked");
      pan_hig_workarea_add_row (t, row, label, w, NULL);
}

static void
add_fg_bg_color_picker (GtkWidget * t, int * row, const char * mnemonic, 
                        GtkWidget ** setme_fg_picker,  const GdkColor * fg,
                        GtkWidget ** setme_bg_picker,  const GdkColor * bg)
{
      GtkWidget * l;
      GtkWidget * h;
      GtkWidget * w;

      h = gtk_hbox_new (FALSE, GUI_PAD_SMALL);
      pan_hig_workarea_add_row (t, row, mnemonic, h, NULL);

      l = gtk_label_new (_("Text:"));
      gtk_box_pack_start_defaults (GTK_BOX(h), l);
      w = *setme_fg_picker = pan_color_picker_new ();
      pan_color_picker_set_color (w, fg);
      connect_signal_to_prefs_changed (w, "clicked");
      gtk_box_pack_start_defaults (GTK_BOX(h), w);

      l = gtk_label_new (_("Background:"));
      gtk_box_pack_start_defaults (GTK_BOX(h), l);
      w = *setme_bg_picker = pan_color_picker_new ();
      pan_color_picker_set_color (w, bg);
      connect_signal_to_prefs_changed (w, "clicked");
      gtk_box_pack_start_defaults (GTK_BOX(h), w);
}

static GtkWidget*
color_page (void)
{
      GtkWidget * w;
      GtkWidget * h;
      GtkWidget * t;
      GtkWidget * l;
      int row;

      row = 0;
      
      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_title (t, &row, _("Header Pane"));

            pan_hig_workarea_add_section_spacer (t, row, 4);

            add_color_picker (t, _("_Read Threads:"), &win->read_pcp, &read_color, &row);

            add_color_picker (t, _("_Unread Threads:"), &win->unread_pcp, &unread_color, &row);

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Header Pane Scoring"));

            pan_hig_workarea_add_section_spacer (t, row, 6);

            add_wide_check_button (t, &row, _("Color the \"sub_ject\" column"),
                                   header_pane_color_subject_column,
                                   &win->color_subject_column_check);

            add_fg_bg_color_picker (t, &row, _("Articles with a Score of 9999 or higher:"),
                                        &win->score_pcp[SCORE_COLOR_WATCHED][1], &score_color[SCORE_COLOR_WATCHED][1],
                                        &win->score_pcp[SCORE_COLOR_WATCHED][0], &score_color[SCORE_COLOR_WATCHED][0]);

            add_fg_bg_color_picker (t, &row, _("Articles with a Score between 5000 and 9998:"),
                                        &win->score_pcp[SCORE_COLOR_HIGH][1], &score_color[SCORE_COLOR_HIGH][1],
                                        &win->score_pcp[SCORE_COLOR_HIGH][0], &score_color[SCORE_COLOR_HIGH][0]);

            add_fg_bg_color_picker (t, &row, _("Articles with a Score between 1 and 4999:"),
                                        &win->score_pcp[SCORE_COLOR_MEDIUM][1], &score_color[SCORE_COLOR_MEDIUM][1],
                                        &win->score_pcp[SCORE_COLOR_MEDIUM][0], &score_color[SCORE_COLOR_MEDIUM][0]);

            add_fg_bg_color_picker (t, &row, _("Articles with a Score between -9998 and -1:"),
                                        &win->score_pcp[SCORE_COLOR_LOW][1], &score_color[SCORE_COLOR_LOW][1],
                                        &win->score_pcp[SCORE_COLOR_LOW][0], &score_color[SCORE_COLOR_LOW][0]);

            add_fg_bg_color_picker (t, &row, _("Articles with a Score of -9999 or lower:"),
                                        &win->score_pcp[SCORE_COLOR_IGNORED][1], &score_color[SCORE_COLOR_IGNORED][1],
                                        &win->score_pcp[SCORE_COLOR_IGNORED][0], &score_color[SCORE_COLOR_IGNORED][0]);

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Body Pane"));

            pan_hig_workarea_add_section_spacer (t, row, 3);

            /* quoted text */
            h = gtk_hbox_new (FALSE, GUI_PAD_SMALL);
            pan_hig_workarea_add_row (t, &row, _("Quoted Text:"), h, NULL);

                  /* quoted text 1 label */
                  l = gtk_label_new_with_mnemonic (_("_1:"));
                  gtk_box_pack_start_defaults (GTK_BOX(h), l);

                  /* quoted text 1 colorbutton */
                  w = win->text_quoted_pcp[0] = pan_color_picker_new ();
                  gtk_label_set_mnemonic_widget (GTK_LABEL(l), w);
                  pan_color_picker_set_color (w, text_quoted_color+0);
                  connect_signal_to_prefs_changed (w, "clicked");
                  gtk_box_pack_start_defaults (GTK_BOX(h), w);

                  /* quoted text 2 label */
                  l = gtk_label_new_with_mnemonic (_("_2:"));
                  gtk_box_pack_start_defaults (GTK_BOX(h), l);

                  /* quoted text 2 colorbutton */
                  w = win->text_quoted_pcp[1] = pan_color_picker_new ();
                  gtk_label_set_mnemonic_widget (GTK_LABEL(l), w);
                  pan_color_picker_set_color (w, text_quoted_color+1);
                  connect_signal_to_prefs_changed (w, "clicked");
                  gtk_box_pack_start_defaults (GTK_BOX(h), w);

                  /* quoted text 3 label */
                  l = gtk_label_new_with_mnemonic (_("_3:"));
                  gtk_box_pack_start_defaults (GTK_BOX(h), l);

                  /* quoted text 3 colorbutton */
                  w = win->text_quoted_pcp[2] = pan_color_picker_new ();
                  gtk_label_set_mnemonic_widget (GTK_LABEL(l), w);
                  pan_color_picker_set_color (w, text_quoted_color+2);
                  connect_signal_to_prefs_changed (w, "clicked");
                  gtk_box_pack_start_defaults (GTK_BOX(h), w);

            add_color_picker (t, _("Signature:"), &win->signature_pcp, &signature_color, &row);

            add_color_picker (t, _("URLs:"), &win->text_url_pcp, &text_url_color, &row);

      return t;
}

/**
***  Layout Page
**/

#define LAYOUT_QTY 6

static char*
layout_get_new_string (GtkWidget * page)
{
      int i;
      int layout = -1;
      const char * order = "tag";
      GtkWidget ** layout_buttons = (GtkWidget**) g_object_get_data (G_OBJECT(page), "layout_buttons");
      GSList * l = (GSList*) g_object_get_data (G_OBJECT(page), "layout_radio_group");

      /* get the layout number */
      for (i=0; i<LAYOUT_QTY; ++i) {
            GtkToggleButton * tb = GTK_TOGGLE_BUTTON(layout_buttons[i]);
            if (gtk_toggle_button_get_active(tb)) {
                  layout = i + 1;
                  break;
            }
      }

      /* get the ordering string */
      for (; l!=NULL; l=l->next) {
            GtkRadioButton * rb = GTK_RADIO_BUTTON(l->data);
            if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rb))) {
                  order = (const char*) g_object_get_data (G_OBJECT(rb), "layout");
                  g_assert (order != NULL);
                  g_assert (strlen(order) == 3);
                  break;
            }
      }

      /* build & return the string */
      return g_strdup_printf ("%d%s", layout, order);
}

static void
layout_changed_cb (GtkToggleButton * togglebutton, gpointer user_data)
{
      GtkWidget ** layout_buttons = (GtkWidget**) user_data;
      static gboolean dampen_feedback_loop = FALSE;

      if (!dampen_feedback_loop) {
            int i;
            dampen_feedback_loop = TRUE;
            for (i=0; i<LAYOUT_QTY; ++i) {
                  GtkToggleButton * tb = GTK_TOGGLE_BUTTON(layout_buttons[i]);
                  gboolean active = togglebutton==tb;
                  if (gtk_toggle_button_get_active(tb) != active)
                        gtk_toggle_button_set_active (tb, active);
            }
            dampen_feedback_loop = FALSE;
      }
}


static GtkWidget*
layout_page (void)
{
      int i;
      GtkWidget * h;
      GtkWidget * w;
      GtkWidget * radio;
      GtkWidget * t;
      const char * pch_group;
      const char * pch_header;
      const char * pch_body;
      const char lch = *layout_str;
      int row;
      char buf [256];
      const guint8 * inline_txt [LAYOUT_QTY] = {
            icon_layout_1, icon_layout_2, icon_layout_3,
            icon_layout_4, icon_layout_5, icon_layout_6 };
      GtkWidget ** layout_buttons = g_malloc (sizeof(GtkWidget*) * LAYOUT_QTY);

      row = 0;
      
      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_title (t, &row, _("Pane Layout"));

            pan_hig_workarea_add_section_spacer (t, row, 1);

            /* pane layout */
            h = gtk_hbox_new (FALSE, 6);
            gtk_container_set_border_width (GTK_CONTAINER (h), 0);
            for (i=0; i<LAYOUT_QTY; ++i)
            {
                  char buf[64];
                  GdkPixbuf * pixbuf;
                  GdkPixmap * pixmap;
                  GdkBitmap * bitmap;

                  g_snprintf (buf, sizeof(buf), "%d", i+1);
                  w = gtk_toggle_button_new ();
                  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), *buf==lch);

                  pixbuf = gdk_pixbuf_new_from_inline (-1, inline_txt[i], FALSE, NULL);
                  gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, gtk_widget_get_colormap(w), &pixmap, &bitmap, 128);
                  gtk_container_add (GTK_CONTAINER(w), gtk_image_new_from_pixmap (pixmap, bitmap));

                  g_object_unref (pixbuf);
                  g_object_unref (pixmap);
                  g_object_unref (bitmap);

                  g_signal_connect (w, "toggled", G_CALLBACK(layout_changed_cb), layout_buttons);
                  connect_signal_to_prefs_changed (w, "toggled");
                  layout_buttons[i] = w;
                  gtk_box_pack_start (GTK_BOX(h), w, FALSE, FALSE, 0);
            }
            gtk_table_attach_defaults (GTK_TABLE(t), h, 1, 2, row, row+1);
            ++row;

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Pane Order"));

            pan_hig_workarea_add_section_spacer (t, row, 6);

            /* pane order */
            pch_group = _("Group Pane");
            pch_header = _("Header Pane");
            pch_body = _("Body Pane");
            g_snprintf (buf, sizeof(buf), "1=%s, 2=%s, 3=%s", pch_group, pch_header, pch_body);
            w = radio = gtk_radio_button_new_with_mnemonic_from_widget (NULL, buf);
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), !strcmp(layout_str+1,"gta"));
            g_object_set_data (G_OBJECT(w), "layout", "gta");
            connect_signal_to_prefs_changed (w, "toggled");
            gtk_table_attach_defaults (GTK_TABLE(t), w, 1, 2, row, row+1);
            ++row;
            g_snprintf (buf, sizeof(buf), "1=%s, 2=%s, 3=%s", pch_group, pch_body, pch_header);
            w = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON(w), buf);
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), !strcmp(layout_str+1,"gat"));
            g_object_set_data (G_OBJECT(w), "layout", "gat");
            connect_signal_to_prefs_changed (w, "toggled");
            gtk_table_attach_defaults (GTK_TABLE(t), w, 1, 2, row, row+1);
            ++row;
            g_snprintf (buf, sizeof(buf), "1=%s, 2=%s, 3=%s", pch_header, pch_group, pch_body);
            w = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON(w), buf);
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), !strcmp(layout_str+1,"tga"));
            g_object_set_data (G_OBJECT(w), "layout", "tga");
            connect_signal_to_prefs_changed (w, "toggled");
            gtk_table_attach_defaults (GTK_TABLE(t), w, 1, 2, row, row+1);
            ++row;
            g_snprintf (buf, sizeof(buf), "1=%s, 2=%s, 3=%s", pch_header, pch_body, pch_group);
            w = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON(w), buf);
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), !strcmp(layout_str+1,"tag"));
            g_object_set_data (G_OBJECT(w), "layout", "tag");
            connect_signal_to_prefs_changed (w, "toggled");
            gtk_table_attach_defaults (GTK_TABLE(t), w, 1, 2, row, row+1);
            ++row;
            g_snprintf (buf, sizeof(buf), "1=%s, 2=%s, 3=%s", pch_body, pch_group, pch_header);
            w = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON(w), buf);
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), !strcmp(layout_str+1,"agt"));
            g_object_set_data (G_OBJECT(w), "layout", "agt");
            connect_signal_to_prefs_changed (w, "toggled");
            gtk_table_attach_defaults (GTK_TABLE(t), w, 1, 2, row, row+1);
            ++row;
            g_snprintf (buf, sizeof(buf), "1=%s, 2=%s, 3=%s", pch_body, pch_header, pch_group);
            w = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON(w), buf);
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), !strcmp(layout_str+1,"atg"));
            g_object_set_data (G_OBJECT(w), "layout", "atg");
            connect_signal_to_prefs_changed (w, "toggled");
            gtk_table_attach_defaults (GTK_TABLE(t), w, 1, 2, row, row+1);
            ++row;

      g_object_set_data_full (G_OBJECT(t), "layout_buttons", layout_buttons, g_free);
      g_object_set_data (G_OBJECT(t), "layout_radio_group", gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)));
      return t;
}

/**
***  Header Pane Page
**/

static const char *
articlelist_column_to_prefs_label (int type)
{
      switch (type)
      {
            case COLUMN_ACTION_STATE: return _("Action State");
            case COLUMN_ARTICLE_STATE: return _("Article State");
            case COLUMN_SCORE: return _("Score");
            case COLUMN_SUBJECT: return _("Subject");
            case COLUMN_LINES: return _("Lines");
            case COLUMN_AUTHOR: return _("Author");
            case COLUMN_DATE: return _("Date");
            default: break;
      }

      pan_warn_if_reached();
      return "BUG!!!";
}


static void
articlelist_column_move (int up)
{
      GtkTreeModel *model;
      GtkTreeIter iter1, iter2;
      GtkTreeSelection *selection;
      
      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(win->column_tree_view));
      if (gtk_tree_selection_get_selected (selection, &model, &iter1))
      {
            GtkTreePath *old_path;
            GtkTreePath *new_path;

            /* ARG! Why doesn't gtk_tree_model have */
            /* an API to move up AND down easily??  */

            old_path = gtk_tree_model_get_path (model, &iter1);
            new_path = gtk_tree_model_get_path (model, &iter1);

            if (up)
                  gtk_tree_path_prev (new_path);
            else
                  gtk_tree_path_next (new_path);

            if (gtk_tree_path_compare (old_path, new_path) != 0 &&
                gtk_tree_model_get_iter (model, &iter2, new_path))
            {
                  int col;

                  /* In GTK2.2, we could call gtk_list_store_swap () */

                  gtk_tree_model_get (model, &iter1, 1, &col, -1);
                  gtk_list_store_remove (win->column_store, &iter1);
            
                  if (up)
                        gtk_list_store_insert_before (win->column_store, &iter1, &iter2);
                  else
                        gtk_list_store_insert_after (win->column_store, &iter1, &iter2);

                  gtk_list_store_set (win->column_store, &iter1,
                        0, articlelist_column_to_prefs_label(col),
                        1, col,
                        -1);

                  /* Keep the row selected */
                  gtk_tree_selection_select_iter (selection, &iter1);
                  gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(win->column_tree_view), new_path, NULL, TRUE, 0.5, 0.5);
            }

            gtk_tree_path_free (old_path);
            gtk_tree_path_free (new_path);
      }
}

static void
articlelist_column_up_button_clicked_cb (GtkButton * button, gpointer data)
{
      articlelist_column_move (1);

}

static void
articlelist_column_down_button_clicked_cb (GtkButton * button, gpointer data)
{
      articlelist_column_move (0);
}

static GtkWidget*
header_pane_page (void)
{
      GtkWidget * w;
      GtkWidget * h;
      GtkWidget * t;
      GtkWidget * hbox;
      GtkWidget * bbox;
      GtkCellRenderer * renderer;
      GtkTreeViewColumn *column;
      GtkTreeSelection * selection;
      int idx;
      int row;

      row = 0;
      
      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_title (t, &row, _("Column Layout"));

            pan_hig_workarea_add_section_spacer (t, row, 1);

            /* column ordering list */
            /* work area */
            hbox = gtk_hbox_new (FALSE, GUI_PAD);
            gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);
            gtk_table_attach_defaults (GTK_TABLE(t), hbox, 1, 2, row, row+1);

            /* create list store & tree view */
            win->column_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
            win->column_tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(win->column_store));
            gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(win->column_tree_view), FALSE);

            /* add the column to the view */
            renderer = gtk_cell_renderer_text_new ();
            column = gtk_tree_view_column_new_with_attributes (_("Column"),
                        renderer, "text", 0, NULL);
            gtk_tree_view_append_column (GTK_TREE_VIEW (win->column_tree_view), column);
            
            /* set the selection mode */
            selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(win->column_tree_view));
            gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);

            /* populate the list store */

            for (idx=0; idx<COLUMN_TYPE_QTY; ++idx)
            {
                  GtkTreeIter iter;

                  gtk_list_store_append (win->column_store, &iter);
                  gtk_list_store_set (win->column_store, &iter,
                        0, articlelist_column_to_prefs_label (articlelist_columns[idx]),
                        1, articlelist_columns[idx], 
                        -1);
            }

            /* containing widget */
            w = gtk_scrolled_window_new (NULL, NULL);
            gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
            gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
            gtk_container_add (GTK_CONTAINER(w), win->column_tree_view);
            gtk_widget_set_usize (w, 150u, 150u);
            gtk_box_pack_start (GTK_BOX(hbox), w, TRUE, TRUE, 0);

            /* controlling buttons */
            bbox = gtk_vbox_new (FALSE, GUI_PAD_SMALL);
            gtk_box_pack_start (GTK_BOX(hbox), bbox, FALSE, FALSE, 0);

            /* up button */
            w = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
            gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
            g_signal_connect (w, "clicked", 
                  G_CALLBACK(articlelist_column_up_button_clicked_cb), 
                  NULL);
            connect_signal_to_prefs_changed (w, "clicked");
            win->column_up_cbutton = w;

            /* down button */
            w = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
            gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
            g_signal_connect (w, "clicked", 
                  G_CALLBACK(articlelist_column_down_button_clicked_cb), 
                  NULL);
            connect_signal_to_prefs_changed (w, "clicked");
            win->column_down_cbutton = w;
            ++row;

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Other Settings"));

            pan_hig_workarea_add_section_spacer (t, row, 4);

            add_wide_check_button (t, &row, _("When a Followup subject header changes, show as _new thread"),
                                   break_thread_when_subject_changes,
                                   &win->break_thread_when_subject_changes_cbutton);

            add_wide_check_button (t, &row, _("E_xpand all threads by default"),
                                   expand_all_threads_by_default,
                                   &win->expand_all_threads_by_default_cbutton);

            add_wide_check_button (t, &row, _("Show complete _multipart posts as a single article"),
                                   hide_mpart_child_nodes,
                                   &win->hide_mpart_child_nodes_cbutton);

            /* date format */
            h = gtk_hbox_new (FALSE, GUI_PAD_SMALL);
            w = win->thread_date_entry = gtk_entry_new_with_max_length (128);
            pan_gtk_entry_set_text (w, header_pane_date_format);
            connect_signal_to_prefs_changed (w, "changed");
            gtk_box_pack_start (GTK_BOX(h), w, TRUE, TRUE, 0);
            w = gtk_button_new_with_mnemonic (_("_Help"));
            g_signal_connect (w, "clicked", G_CALLBACK (date_help_clicked_cb), NULL);
            gtk_box_pack_start (GTK_BOX(h), w, FALSE, FALSE, 0);
            pan_hig_workarea_add_row (t, &row, _("Date Forma_t:"), h, w);

      return t;
}

/**
***  Body Pane Page
**/

static void
smooth_scrolling_check_toggled (GtkToggleButton * tb, gpointer user_data)
{
      gtk_widget_set_sensitive (GTK_WIDGET(win->smooth_scrolling_speed_sb), gtk_toggle_button_get_active(tb));
}

static gulong
get_header_flags (void)
{
      guint i;
      gulong flags = 0;
      GPtrArray * header_toggles = (GPtrArray*) g_object_get_data (G_OBJECT(win->body_pane_page), "header_toggles");

      for (i=0; i<header_toggles->len; ++i) {
            GtkWidget * w = GTK_WIDGET (g_ptr_array_index (header_toggles, i));
            if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(w)))
                  flags |= GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(w), "bit"));
      }

      return flags;
}

static GtkWidget*
body_pane_page (void)
{
      GtkAdjustment * adj;
      GtkWidget * w;
      GtkWidget * t;
      GPtrArray * header_toggles = g_ptr_array_new ();
      int row = 0;
      char * pch;
      char buf[512];
      
      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_title (t, &row, _("Headers"));

            pan_hig_workarea_add_section_spacer (t, row, 8);

#define ADD_HEADER_TOGGLE(name,bit) \
            g_snprintf (buf, sizeof(buf), _("Show \"%s\" header"), name); \
            add_wide_check_button (t, &row, buf, (header_flags & bit?1:0), &w); \
            g_object_set_data (G_OBJECT(w), "bit", GUINT_TO_POINTER(bit)); \
            g_ptr_array_add (header_toggles, w);
            ADD_HEADER_TOGGLE(_("From"), UI_HEADER_AUTHOR)
            ADD_HEADER_TOGGLE(_("Subject"), UI_HEADER_SUBJECT)
            ADD_HEADER_TOGGLE(_("Date"), UI_HEADER_DATE)
            ADD_HEADER_TOGGLE(_("Reply-To"), UI_HEADER_REPLY_TO)
            ADD_HEADER_TOGGLE(_("Followup-To"), UI_HEADER_FOLLOWUP_TO)
            ADD_HEADER_TOGGLE(_("Newsgroups"), UI_HEADER_NEWSGROUPS)
            ADD_HEADER_TOGGLE(_("Message-ID"), UI_HEADER_MESSAGE_ID)
            ADD_HEADER_TOGGLE(_("Newsreader"), UI_HEADER_NEWSREADER)
#undef ADD_HEADER_TOGGLE

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Other Settings"));

            pan_hig_workarea_add_section_spacer (t, row, 2);

            /* quoted text */
            w = win->text_quoted_chars_entry = gtk_entry_new_with_max_length (20);
            pch = text_massager_get_quote_chars (text_pane_get_text_massager());
            gtk_entry_set_text (GTK_ENTRY(w), pch);
            g_free (pch);
            connect_signal_to_prefs_changed (w, "changed");
            pan_hig_workarea_add_row (t, &row, _("Characters denoting _quoted text:"), w, NULL);

            /* smooth scrolling label */
            w = gtk_check_button_new_with_mnemonic (_("S_mooth scrolling at speed:"));
            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), text_window_smooth_scrolling);
            connect_signal_to_prefs_changed (w, "toggled");
            gtk_signal_connect (GTK_OBJECT(w), "toggled", GTK_SIGNAL_FUNC(smooth_scrolling_check_toggled), 0);
            win->smooth_scrolling_check = w;
            gtk_table_attach (GTK_TABLE(t), w, 1, 2, row, row+1, GTK_FILL, 0, 0, 0);

            /* smooth scrolling control */
            adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 10.0, 1.0, 1.0, 0.0);
            w = gtk_spin_button_new (adj, 0, 0);
            gtk_spin_button_set_value (GTK_SPIN_BUTTON(w), text_window_smooth_scrolling_speed);
            connect_signal_to_prefs_changed (w, "changed");
            win->smooth_scrolling_speed_sb = w;
            gtk_widget_set_sensitive (w, text_window_smooth_scrolling);
            gtk_table_attach_defaults (GTK_TABLE(t), w, 3, 4, row, row+1);
            ++row;


      g_object_set_data (G_OBJECT(t), "header_toggles", header_toggles);
      return t;
}

/**
***  Behavior Page
**/

static GtkWidget*
behavior_page (void)
{
      int row;
      GtkAdjustment * adj;
      GtkWidget * t;
      GtkWidget * w;
      GtkWidget * h;
      char * p;
      const gboolean flush_on_exit = pan_config_get_bool (KEY_CACHE_FLUSH_ON_EXIT, DEFAULT_VALUE_CACHE_FLUSH_ON_EXIT);

      row = 0;

      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_title (t, &row, _("Mouse"));

            pan_hig_workarea_add_section_spacer (t, row, 2);

            add_wide_check_button (t, &row, _("Single-clic_k selects, rather than loads, groups"),
                                   single_click_selects_groups,
                                   &win->single_click_selects_groups_cbutton);

            add_wide_check_button (t, &row, _("Single-click selects, rather than loads, heade_rs"),
                                   single_click_selects_headers,
                                   &win->single_click_selects_headers_cbutton);

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Automatic Downloads"));

            pan_hig_workarea_add_section_spacer (t, row, 4);

            add_wide_check_button (t, &row, _("Download new headers when entering a _group"),
                                   fetch_new_on_group_enter,
                                   &win->fetch_new_on_group_enter_cbutton);

            add_wide_check_button (t, &row, _("_Mark group's articles read when leaving a group"),
                                   mark_read_on_group_exit,
                                   &win->mark_read_on_group_exit_cbutton);

            add_wide_check_button (t, &row, _("Download new headers from s_ubscribed groups when starting Pan"),
                                   fetch_new_on_startup,
                                   &win->fetch_new_on_startup_cbutton);

            add_wide_check_button (t, &row, _("Download new headers and bod_ies from subscribed groups when starting Pan"),
                                   fetch_new_and_bodies_on_startup,
                                   &win->fetch_new_and_bodies_on_startup_cbutton);

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Tasks"));

            pan_hig_workarea_add_section_spacer (t, row, 3);

            /* download directory */
            h = gtk_hbox_new (FALSE, GUI_PAD_SMALL);
            w = win->dir_download = pan_file_entry_new (_("Download Directory"));
            if ((p = pan_config_get_string ("/Pan/Paths/download_dir", NULL)) == NULL)
                  p = g_build_filename (pan_get_home_dir(), "News", "Pan", NULL);
            pan_file_entry_set (win->dir_download, p);
            g_free (p);
            connect_signal_to_prefs_changed (pan_file_entry_gtk_entry(w), "changed");
            gtk_box_pack_start (GTK_BOX(h), w, TRUE, TRUE, 0);
            w = gtk_button_new_with_mnemonic (_("_Help"));
            g_signal_connect_swapped (w, "clicked", G_CALLBACK (show_group_substitution_help_dialog), win->dialog);
            gtk_box_pack_start (GTK_BOX(h), w, FALSE, FALSE, 0);
            pan_hig_workarea_add_row (t, &row, _("Save a_ttachments in directory:"), h, pan_file_entry_gtk_entry(win->dir_download));


            add_wide_check_button (t, &row, _("Automatically remove _failed tasks from the task manager"),
                                   queue_get_remove_failed_tasks(),
                                   &win->remove_failed_tasks_cbutton);

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Article Cache"));

            pan_hig_workarea_add_section_spacer (t, row, 2);

            add_wide_check_button (t, &row, _("_Erase cache when exiting Pan"),
                                   flush_on_exit,
                                   &win->flush_cache_on_exit_check);

            /* max cache size */
            adj = (GtkAdjustment*) gtk_adjustment_new  (acache_max_megs, 1.0, 20000, 1.0, 1.0, 1.0);
            w = win->cache_megs_sb = gtk_spin_button_new (GTK_ADJUSTMENT(adj), 1.0, 0);
            connect_signal_to_prefs_changed (w, "changed");
            pan_hig_workarea_add_row (t, &row, _("Ma_ximum Cache Size (Megs):"), w, NULL);

      return t;
}

/**
***  Apps and Mail Page
**/

static GtkWidget*
apps_and_mail_page (void)
{
      int i;
      GtkWidget * w;
      GtkWidget * t;
      GList * s;
      int row;
      char * p;

      const char * browsers [] = {
#ifdef G_OS_WIN32
             DEFAULT_WEB_BROWSER,
             "netscape '%s'",
             "netscape -remote 'openURL(%s,raise)'",
             "mozilla -remote 'openURL(%s)'"
#endif
#ifdef G_OS_UNIX
             "gnome-moz-remote --raise --newwin '%s'",
             "netscape '%s'",
             "netscape -remote 'openURL(%s,raise)'",
             "mozilla -remote 'openURL(%s)'",
             "konqueror '%s'",
             "xterm -e w3m \"%s\"",
             "xterm -e lynx \"%s\"",
             "xterm -e links \"%s\"",
             "konsole -e w3m \"%s\"",
             "konsole -e lynx \"%s\"",
             "konsole -e links \"%s\"",
             "gnome-terminal -e w3m \"%s\"",
             "gnome-terminal -e lynx \"%s\"",
             "gnome-terminal -e links \"%s\""
#endif
      };
      const char * editors [] = {
#ifdef G_OS_WIN32
             "notepad %t",
             "pfe %t",
             "vim %t"
#endif
#ifdef G_OS_UNIX
             "gedit %t",
             "mgedit --no-fork %t",
             "emacs %t",
             "xemacs %t",
             "xterm -e jed %t",
             "xterm -e vi %t",
             "konsole -e jed %t",
             "konsole -e vi %t",
             "gnome-terminal -e jed %t",
             "gnome-terminal -e vi %t"
#endif
      };
      const char * scorefile_editors[] = {
#ifdef G_OS_WIN32
            "notepad %t",
            "pfe %t"
#endif
#ifdef G_OS_UNIX
            "xterm -e vi +%n %t",
            "gedit +%n %t",
            "emacs +%n %t",
            "xemacs +%n %t"
#endif
      };

      row = 0;
      
      t = pan_hig_workarea_create ();
      pan_hig_workarea_add_section_title (t, &row, _("Applications"));

            pan_hig_workarea_add_section_spacer (t, row, 4);

            /* external browser */
            w = win->external_browser_combo = gtk_combo_new ();
            gtk_widget_set_usize (w, 300u, 0u);
            for (i=0, s=NULL; i!=G_N_ELEMENTS(browsers); ++i)
                  s = g_list_append (s, (char*) browsers[i]);
            gtk_combo_set_popdown_strings (GTK_COMBO(w), s);
            g_list_free (s);
            pan_gtk_entry_set_text (GTK_COMBO(w)->entry, external_web_browser ? external_web_browser : NULL);
            connect_signal_to_prefs_changed (GTK_COMBO(w)->entry, "changed");
            pan_hig_workarea_add_row (t, &row, _("_Web Browser (%s is URL):"), w, GTK_COMBO(w)->entry);

            /* external editor */
            w = win->external_editor_combo = gtk_combo_new ();
            gtk_widget_set_usize (w, 300u, 0u);
            for (i=0, s=NULL; i!=G_N_ELEMENTS(editors); ++i)
                  s = g_list_append (s, (char*) editors[i]);
            gtk_combo_set_popdown_strings (GTK_COMBO(w), s);
            g_list_free (s);
            pan_gtk_entry_set_text (GTK_COMBO(w)->entry, external_editor);
            connect_signal_to_prefs_changed (GTK_COMBO(w)->entry, "changed");
            pan_hig_workarea_add_row (t, &row, _("_Editor (%t is filename):"), w, GTK_COMBO(w)->entry);

            /* external scorefile editor */
            w = win->score_editor_command_combo = gtk_combo_new ();
            for (i=0, s=NULL; i!=G_N_ELEMENTS(scorefile_editors); ++i)
                  s = g_list_append (s, (char*) scorefile_editors[i]);
            gtk_combo_set_popdown_strings (GTK_COMBO(w), s);
            g_list_free (s);
            pan_gtk_entry_set_text (GTK_COMBO(w)->entry, score_editor_command);
            connect_signal_to_prefs_changed (GTK_COMBO(w)->entry, "changed");
            pan_hig_workarea_add_row (t, &row, _("Scorefile _Editor:\n(%t is filename, %n is line number)"), w, GTK_COMBO(w)->entry);

            /* scorefile */
            w = win->scorefile_pfe = pan_file_entry_new (_("Scorefile"));
            p = pan_config_get_string (KEY_SCOREFILE, KEY_SCOREFILE_DEFAULT);
            replace_gstr (&p, pan_file_normalize (p, NULL));
            pan_file_entry_set (w, p);
            g_free (p);
            connect_signal_to_prefs_changed (pan_file_entry_gtk_entry(w), "changed");
            pan_hig_workarea_add_row (t, &row, _("Score_file"), w, pan_file_entry_gtk_entry(w));

      pan_hig_workarea_add_section_divider (t, &row);
      pan_hig_workarea_add_section_title (t, &row, _("Sending Mail"));

            pan_hig_workarea_add_section_spacer (t, row, 2);

            /* address */
            w = win->smtp_address = gtk_entry_new ();
            if (is_nonempty_string(mail_server_address))
                  pan_gtk_entry_set_text (w, mail_server_address);
            connect_signal_to_prefs_changed (w, "changed");
            pan_hig_workarea_add_row (t, &row, _("Mail Server Add_ress:"), w, NULL);

            /* address */
            w = win->smtp_port = gtk_spin_button_new (GTK_ADJUSTMENT(gtk_adjustment_new (mail_server_port, 0, 65536, 1, 1, 1)), 1, 0);
            connect_signal_to_prefs_changed (w, "changed");
            pan_hig_workarea_add_row (t, &row, _("Mail Server Por_t:"), w, NULL);

      return t;
}


/***
****  
****  
****  
***/

static void
prefs_destroy_cb (GtkObject * object, gpointer user_data)
{
      /* cleanup main window */
      g_free (win);
      win = NULL;
}

static void
prefs_response_cb (GtkDialog * dialog, int response, gpointer user_data)
{
      if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY)
            prefs_apply ();

      if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_CANCEL)
            gtk_widget_destroy (GTK_WIDGET(dialog));
}

static void
add_page (GtkWidget * notebook, const char * page_name, GtkWidget * page_widget)
{
      GtkWidget * v = gtk_vbox_new (FALSE, 0);
      gtk_box_pack_start (GTK_BOX(v), page_widget, FALSE, FALSE, 0);
      gtk_notebook_append_page (GTK_NOTEBOOK(notebook), v, gtk_label_new_with_mnemonic (page_name));
}

void
prefs_spawn (void)
{
      if (win != NULL)
      {
            /* there can be only one! */
            gdk_window_raise (GTK_WIDGET(win->dialog)->window);
      }
      else
      {
            GtkWidget * w;

            win = g_new0 (PrefsWindow, 1);

            win->dialog = w = gtk_dialog_new_with_buttons (_("Pan Preferences"),
                                                 GTK_WINDOW(Pan.window),
                                                 GTK_DIALOG_DESTROY_WITH_PARENT,
                                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                                 GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
                                                 GTK_STOCK_OK, GTK_RESPONSE_OK,
                                                 NULL);
            gtk_dialog_set_response_sensitive (GTK_DIALOG(w), GTK_RESPONSE_OK, FALSE);
            gtk_dialog_set_response_sensitive (GTK_DIALOG(w), GTK_RESPONSE_APPLY, FALSE);

            /* put a notebook in the dialog's workarea */
            w = win->notebook = gtk_notebook_new ();
            gtk_container_set_border_width (GTK_CONTAINER (w), 18);
            gtk_box_pack_start (GTK_BOX(GTK_DIALOG(win->dialog)->vbox), w, TRUE, TRUE, 0);

            /* add the tabs */
            add_page (w, _("Beha_vior"), behavior_page());
            add_page (w, _("Pane _Layout"), win->layout_page=layout_page());
            add_page (w, _("Hea_der Pane"), header_pane_page());
            add_page (w, _("Bod_y Pane"), win->body_pane_page=body_pane_page());
            add_page (w, _("Fonts"), font_page());
            add_page (w, _("Colors"), color_page());
            add_page (w, _("A_pps & Mail"), apps_and_mail_page());

            /* callbacks */
            g_signal_connect (win->dialog, "response", G_CALLBACK(prefs_response_cb), NULL);
            g_signal_connect (win->dialog, "destroy", G_CALLBACK(prefs_destroy_cb), NULL);

            gtk_widget_show_all (win->dialog);
      }
}

Generated by  Doxygen 1.6.0   Back to index