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

gui.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 <ctype.h>
#include <string.h>
#include <stdlib.h>

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

#include <pan/base/acache.h>
#include <pan/base/argset.h>
#include <pan/base/debug.h>
#include <pan/base/file-grouplist.h>
#include <pan/base/file-headers.h>
#include <pan/base/gnksa.h>
#include <pan/base/log.h>
#include <pan/base/pan-config.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/serverlist.h>
#include <pan/base/status-item.h>
#include <pan/base/base-prefs.h>

#include <pan/rules/rule-ui.h>

#include <pan/identities/identity.h>
#include <pan/identities/identity-manager.h>
#include <pan/identities/identity-ui.h>

#include <pan/filters/score.h>

#include <pan/action.h>
#include <pan/article-actions.h>
#include <pan/article-find.h>
#include <pan/articlelist.h>
#include <pan/article-toolbar.h>
#include <pan/filter-ui.h>
#include <pan/filter-mediator.h>
#include <pan/flagset.h>
#include <pan/globals.h>
#include <pan/group-action.h>
#include <pan/group-ui.h>
#include <pan/gui.h>
#include <pan/gui-notebook.h>
#include <pan/gui-paned.h>
#include <pan/grouplist.h>
#include <pan/log-ui.h>
#include <pan/message-window.h>
#include <pan/pan.h>
#include <pan/prefs.h>
#include <pan/print.h>
#include <pan/save-ui.h>
#include <pan/score-add-ui.h>
#include <pan/score-view-ui.h>
#include <pan/server-menu-ui.h>
#include <pan/server-ui.h>
#include <pan/sockets.h>
#include <pan/status-item-view.h>
#include <pan/text.h>
#include <pan/task-bodies.h>
#include <pan/task-manager.h>
#include <pan/task-post.h>
#include <pan/util.h>
#include <pan/queue.h>

#include <pan/dialogs/dialogs.h>

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

GtkTooltips * ttips = NULL;
GdkColormap * cmap = NULL;

static void log_button_cb (void);

/**
***  Actions
**/

GtkWidget * _toolbar_buttons[ACTION_QTY];

/**
***  STATUS
**/

typedef enum
{
      STATUS_OK,
      STATUS_ERROR,
      STATUS_QTY
}
StatusType;
typedef struct
{
      const guint8 * inline_txt;
      GdkPixbuf * pixbuf;
      GdkPixmap * pixmap;
      GdkBitmap * bitmap;
}
StatusIcon;
static StatusType _current_status;
static GtkWidget * _status_image = NULL;
static StatusIcon _status_icons[STATUS_QTY] = {
      { icon_inform, NULL, NULL },
      { icon_caution, NULL, NULL }
};

GtkAccelGroup * _main_accel_group = NULL;

#define VIEW_QTY 3
static GtkWidget* views[VIEW_QTY];
static GStaticMutex active_tasks_mutex = G_STATIC_MUTEX_INIT;
static GSList * active_tasks;

/**
***  MENUS
**/

/* remember to subtract 1 for the Windows #ifdef! */
#define MAIN_MENU_ENTRIES_QTY 157
static GtkItemFactoryEntry main_menu_entries[MAIN_MENU_ENTRIES_QTY];
GtkItemFactory * _main_menu_factory;



static int current_layout = -1;

GtkWidget * groups_vbox;
GtkWidget * articlelist_ctree;
GtkWidget * text_box;

/****
*****
*****   SECTION:  GUI UPKEEP
*****
****/

static int
gui_set_title_idle (gpointer data)
{
      const Group * group;
      GString * s;
      debug_enter ("gui_set_title_idle");

            s = g_string_sized_new (128);

      /* add the group */
            group = GROUP(data);
      if (group != NULL) {
            const char * groupname = group_get_name (group);
            const guint selected_count = articlelist_get_selected_count_nolock ();
            g_string_printf (s, "%s %d/%d",
                             groupname,
                             group->article_qty-group->article_read_qty,
                             group->article_qty);
            if (selected_count > 1u) {
                  g_string_append_c (s, ' ');
                  g_string_append_printf (s, _("(%u selected)"), selected_count);
            }
            g_string_append (s, " - ");
      }

      /* add the server */
      if (1) {
            Server * server = serverlist_get_active_server ();
            if (server != NULL)
                  g_string_append_printf (s, " %s - ", server_get_name(server));
      }

      /* add application name */
      g_string_append (s, "Pan");

      /* set the title in the UI */
      pan_lock ();
      gtk_window_set_title (GTK_WINDOW (Pan.window), s->str);
      pan_unlock ();

      /* cleanup */
      g_string_free (s, TRUE);

      debug_exit ("gui_set_title_idle");
      return 0;
}

static void
gui_set_title (const Group * group)
{
      gui_queue_add (gui_set_title_idle, (gpointer)group);
}

static void
articles_changed_cb (gpointer call_obj, gpointer call_arg, gpointer user_data)
{
      Group * agroup = articlelist_get_group ();
      ArticleChangeEvent * e = (ArticleChangeEvent*) call_obj;
      if (agroup == e->group)
            gui_set_title (agroup);
}

/**
***  Status Log
**/

static void
set_current_status_nolock (StatusType status)
{
      g_return_if_fail (status==STATUS_OK || status==STATUS_ERROR);

      _current_status = status;
      gtk_image_set_from_pixmap (GTK_IMAGE(_status_image),
                                 _status_icons[_current_status].pixmap,
                                 _status_icons[_current_status].bitmap);
}

static int
set_log_button_status_idle (gpointer data)
{
      const int status = GPOINTER_TO_INT(data);

      pan_lock ();
      set_current_status_nolock (status);
      pan_unlock ();

      return 0;
}

static void
log_entry_added_cb (gpointer entry_data, gpointer unused1, gpointer unused2)
{
      LogEntry* e = (LogEntry*) entry_data;

      if (e != NULL)
      {
            if (e->severity & LOG_URGENT)
            {
                   if (e->severity & LOG_ERROR)
                        pan_error_dialog (e->message);
                  else
                        pan_info_dialog (e->message);
            }
            if (e->severity & LOG_ERROR)
                  gui_queue_add (set_log_button_status_idle, GINT_TO_POINTER(STATUS_ERROR));
      }
}

/**
***
**/

static int
update_menus_idle (gpointer data)
{
      gboolean have_selected_article;
      gboolean have_read_article;
      const Group * alist_group;
      const Group * glist_group;
      gboolean have_group;
      gboolean have_alist_folder;
      gboolean have_glist_folder;
      gboolean is_paned = current_layout == GUI_PANED;
      GtkItemFactory * gif = _main_menu_factory;
      const int visible_pane_qty = (show_group_pane?1:0) + (show_header_pane?1:0) + (show_body_pane?1:0);
      debug_enter ("update_menus_idle");

      have_selected_article = header_pane_has_selection ();
      have_read_article = text_pane_has_message ();
      alist_group = articlelist_get_group ();
      glist_group = grouplist_get_selected_group();
      have_group = alist_group!=NULL || glist_group!=NULL;
      have_alist_folder = alist_group!=NULL && group_is_folder(alist_group);
      have_glist_folder = glist_group!=NULL && group_is_folder(glist_group);

      pan_lock ();

      /* pane visibility.  don't let them turn off _all_ the panes; that's too confusing. */
      menu_set_sensitive (gif, "/View/Show Group Pane", is_paned && (!show_group_pane || visible_pane_qty>1));
      menu_set_sensitive (gif, "/View/Show Header Pane", is_paned && (!show_header_pane || visible_pane_qty>1));
      menu_set_sensitive (gif, "/View/Show Body Pane", is_paned && (!show_body_pane || visible_pane_qty>1));

      /* post menu */
      menu_set_sensitive (gif, "/Post/Followup to Newsgroup...", have_read_article);
      menu_set_sensitive (gif, "/Post/Reply by Email...", have_read_article);
      menu_set_sensitive (gif, "/Post/Forward by Email...", have_read_article);
      menu_set_sensitive (gif, "/Post/Send Pending Messages", TRUE);

      /* navigate menu */
      menu_set_sensitive (gif, "/Go/Next Article", have_group);
      menu_set_sensitive (gif, "/Go/Next Unread Article", have_group);
      menu_set_sensitive (gif, "/Go/Next New Article", have_group);
      menu_set_sensitive (gif, "/Go/Next Watched Article", have_group);
      menu_set_sensitive (gif, "/Go/Next Thread", have_group);
      menu_set_sensitive (gif, "/Go/Next Unread Thread", have_group);
      menu_set_sensitive (gif, "/Go/Next New Thread", have_group);
      menu_set_sensitive (gif, "/Go/Previous Article", have_group);
      menu_set_sensitive (gif, "/Go/Previous Thread", have_group);
      menu_set_sensitive (gif, "/Go/Previous Article Read", have_group && articlelist_has_prev_read());
      menu_set_sensitive (gif, "/Go/Parent Article", have_group);

      /* update group menu */
      menu_set_sensitive (gif, "/Newsgroups/Mark Group Read", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Delete Group's Articles", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Get New Headers", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Get New Headers and Bodies", have_group);
      menu_set_sensitive (gif, "/Newsgroups/More Download Options...", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Refresh Article Counts", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Subscribe", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Unsubscribe", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Group Properties...", have_group);
      menu_set_sensitive (gif, "/Newsgroups/Delete Group", have_group);

      /* update article_menus */
      menu_set_sensitive (gif, "/Articles/Mark Read", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Mark Unread", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Flag", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Unflag", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Download", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Watch Thread", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Ignore Thread", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Plonk Author", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Create Score...", have_selected_article);
      menu_set_sensitive (gif, "/Articles/View Article's Scores", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Cancel...", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Supersede...", have_selected_article);
      menu_set_sensitive (gif, "/Articles/Delete",  have_selected_article);

      /* update file menu */
      menu_set_sensitive (gif, "/File/Save Attachments", have_selected_article);
      menu_set_sensitive (gif, "/File/Save Attachments As...", have_selected_article);
      menu_set_sensitive (gif, "/File/Cancel Last Task", !queue_is_empty());
      menu_set_sensitive (gif, "/File/Print Article...",  have_selected_article);
      menu_set_sensitive (gif, "/File/Delete Folder...", have_glist_folder && strncmp(glist_group->name.str, "pan.", 4));
      menu_set_sensitive (gif, "/File/Edit Article in Folder", have_alist_folder && have_selected_article);

      pan_unlock ();

      /* update the title */
      gui_set_title_idle ((gpointer)alist_group);

      debug_exit ("update_menus_idle");
      return 0;
}

static void
update_menus_cb (gpointer call_object, gpointer call_arg, gpointer user_data)
{
      gui_queue_add (update_menus_idle, NULL);
}

static GtkWidget * connection_qty_label = NULL;
static int connection_size = -1;
static double KBps = 0.0;

static int
gui_refresh_connection_label_idle (gpointer data)
{
      char str[128];
      char tip[128];

      /* decide what to say */
      if (!queue_is_online()) {
            g_snprintf (str, sizeof(str), _("Offline"));
            if (connection_size != 0)
                  g_snprintf (tip, sizeof(tip), _("Closing %d connections"), connection_size);
            else
                  g_snprintf (tip, sizeof(tip), _("No Connections"));
      }
      else if (connection_size != 0) {
            g_snprintf (str, sizeof(str), "%d @ %.1f KB/s", connection_size, KBps);
            g_snprintf (tip, sizeof(tip), _("%d connections totaling %.1f KB per second"),
                  connection_size, KBps);
      }
      else {
            g_snprintf (str, sizeof(str), _("No Connections"));
            g_snprintf (tip, sizeof(tip), "%s", str);
      }

      /* update the tip */
      pan_lock ();
      if (GTK_IS_LABEL(connection_qty_label)) {
            gtk_label_set_text (GTK_LABEL(connection_qty_label), str);
            gtk_tooltips_set_tip (GTK_TOOLTIPS(ttips), connection_qty_label, tip, NULL);
      }
      pan_unlock ();

      return 0;
}

static void
gui_set_connection_size (int size)
{
      if (size != connection_size)
      {
            connection_size = size;
            log_add_va (LOG_INFO, _("News server connection count: %d"), size);
            gui_queue_add (gui_refresh_connection_label_idle, NULL);
            gui_queue_add (update_menus_idle, NULL);
      }
}

static void
queue_connection_size_changed_cb (gpointer call_obj, gpointer call_arg, gpointer user_data)
{
      const int size = GPOINTER_TO_INT (call_obj);
      gui_set_connection_size (size);
}


static int
gui_refresh_timer_cb (gpointer user_data)
{
      /* update KBps */
      if (1)
      {
            static GTimeVal last_time;
            static unsigned long last_KB = 0;
            static gboolean last_time_inited = FALSE;

            if (!last_time_inited)
            {
                  g_get_current_time (&last_time);
                  last_time_inited = TRUE;
            }
            else
            {
                  double time_diff;
                  double KB_diff;
                  GTimeVal new_time;
                  unsigned long new_KB;

                  g_get_current_time (&new_time);
                  time_diff = (double)(new_time.tv_sec - last_time.tv_sec) * G_USEC_PER_SEC;
                  time_diff += (new_time.tv_usec - last_time.tv_usec);
                  last_time = new_time;

                  new_KB = pan_socket_get_total_xfer_K ();
                  KB_diff = new_KB - last_KB;
                  last_KB = new_KB;

                  KB_diff *= (double)G_USEC_PER_SEC;
                  KBps = KB_diff / time_diff;
            }
      }

      gui_queue_add (gui_refresh_connection_label_idle, NULL);

      return 1;
}

static GtkWidget * queue_qty_label = NULL;
static GtkWidget * queue_qty_button = NULL;

static int
gui_set_queue_size_idle (gpointer data)
{
      char str[128];
      char tip[128];
      ArgSet * argset = (ArgSet*) data;
      const guint running = GPOINTER_TO_UINT (argset_get (argset, 0));
      const guint size = GPOINTER_TO_UINT (argset_get (argset, 1));

      /* build the format strings */
      if (size == 0u) {
            g_snprintf (str, sizeof(str), _("No Tasks"));
            g_snprintf (tip, sizeof(tip), _("The Task Manager is Empty"));
      } else {
            g_snprintf (str, sizeof(str), _("Tasks: %u/%u"), running, size);
            g_snprintf (tip, sizeof(tip), _("%u Tasks Running, %u Tasks Total"), running, size);
      }

      /* update the gui */
      pan_lock();
      gtk_label_set_text (GTK_LABEL(queue_qty_label), str);
      gtk_tooltips_set_tip (GTK_TOOLTIPS(ttips), queue_qty_button, tip, NULL);
      pan_unlock();

      /* clean up */
      argset_free (argset);
      return 0;
}

static void
gui_set_queue_size (guint running, guint size)
{
      gui_queue_add (gui_set_queue_size_idle,
                     argset_new2 (GUINT_TO_POINTER(running), GUINT_TO_POINTER(size)));
}

static void
queue_size_changed_cb (gpointer call_obj, gpointer call_arg, gpointer user_data)
{
      gui_set_queue_size (GPOINTER_TO_INT(call_obj), GPOINTER_TO_INT(call_arg));
}

static void
turn_off_status_item_views_nolock (void)
{
      int i;
      debug_enter ("turn_off_status_item_views");

      for (i=0; i<VIEW_QTY; ++i)
            status_item_view_set_item_nolock (views[i], NULL);

      debug_exit ("turn_off_status_item_views");
}

static int
status_item_active_changed_idle (gpointer unused)
{
      pan_lock ();

      g_static_mutex_lock (&active_tasks_mutex);
      {
            GSList * l;
            guint view_i;

            for (view_i=0, l=active_tasks; l!=NULL && view_i!=VIEW_QTY; l=l->next, ++view_i)
                  status_item_view_set_item_nolock (views[view_i], STATUS_ITEM(l->data));

            for ( ; view_i<VIEW_QTY; ++view_i)
                  status_item_view_set_item_nolock (views[view_i], NULL);
      }
      g_static_mutex_unlock (&active_tasks_mutex);

      pan_unlock ();

      return 0;
}

static void
status_item_active_changed_cb (gpointer call_obj, gpointer call_arg, gpointer user_data)
{
      StatusItem * item = STATUS_ITEM(call_obj);
      gboolean is_active = GPOINTER_TO_INT(call_arg) != 0;

      g_return_if_fail (item != NULL);

      g_static_mutex_lock (&active_tasks_mutex);
      {
            GSList * node;

            if (is_active)
            {
                  pan_object_ref (PAN_OBJECT(item));
                  active_tasks = g_slist_append (active_tasks, item);
            }
            else if (((node = g_slist_find (active_tasks, item))) != NULL)
            {
                  active_tasks = g_slist_delete_link (active_tasks, node);
                  pan_object_unref (PAN_OBJECT(item));
            }
      }
      g_static_mutex_unlock (&active_tasks_mutex);

      gui_queue_add (status_item_active_changed_idle, NULL);
}

static void
online_status_changed_cb (gpointer call_obj, gpointer call_arg, gpointer user_data)
{
      pan_lock ();
      menu_set_checked (_main_menu_factory, "/File/Work Online", call_obj!=NULL);
      pan_unlock ();
      gui_queue_add (gui_refresh_connection_label_idle, NULL);
}

static void
mute_quoted_text_changed_cb (gpointer call_obj, gpointer call_arg, gpointer client)
{
      menu_set_checked (_main_menu_factory, "/View/Mute Quoted Text", call_arg!=NULL);
}

static void
show_all_headers_changed_cb (gpointer call_obj, gpointer call_arg, gpointer client)
{
      menu_set_checked (_main_menu_factory, "/View/Show All Headers in Body Pane", call_arg!=NULL);
}

static void
text_fill_body_changed_cb (gpointer call_obj, gpointer call_arg, gpointer user_data)
{
      menu_set_checked (_main_menu_factory, "/View/Wrap Article Body", call_arg!=NULL);
}


/****
*****
*****   SECTION:  GUI LAYOUT (NOTEBOOK, PANED)
*****
****/

void
gui_layout_clear_workarea (void)
{
      /* remove the three panes from the workarea */
      gtk_widget_hide_all (GTK_WIDGET(Pan.workarea));
      if (groups_vbox->parent != NULL)
            gtk_container_remove (GTK_CONTAINER(groups_vbox->parent), groups_vbox);
      if (articlelist_ctree->parent != NULL)
            gtk_container_remove (GTK_CONTAINER(articlelist_ctree->parent), articlelist_ctree);
      if (text_box->parent != NULL)
            gtk_container_remove (GTK_CONTAINER(text_box->parent), text_box);

      /* clear the workarea */
      if (1) {
            GList * children = gtk_container_children (GTK_CONTAINER(Pan.workarea));
            GList * l;
            for (l=children; l!=NULL; l=l->next)
                  gtk_container_remove (GTK_CONTAINER(Pan.workarea), GTK_WIDGET(l->data));
            g_list_free (l);
      }
}

void
gui_layout_refresh (void)
{
      switch (current_layout)
      {
            case GUI_NOTEBOOK:
                  gui_notebook_construct ();
                  break;

            case GUI_PANED:
                  gui_paned_construct ();
                  break;

            default:
                  pan_warn_if_reached ();
                  gui_paned_construct ();
                  break;
      }

      menu_set_checked (_main_menu_factory, "/View/Tabbed Layout", current_layout==GUI_NOTEBOOK);
      update_menus_cb (NULL, NULL, NULL);
}

static void
gui_layout_set_mode (int new_layout)
{
      debug_enter ("gui_layout_set_mode");

      if (new_layout != current_layout)
      {
            current_layout = new_layout;

            gui_layout_refresh ();
      }

      debug_exit ("gui_layout_set_mode");
}

static GtkWidget*
gui_page_get_pane_nolock (int pane)
{
      GtkWidget * retval = NULL;

      switch (pane) {
            case GROUPS_PANE: retval = Pan.group_tree; break;
            case HEADERS_PANE: retval = Pan.article_ctree; break;
            case BODY_PANE: retval = Pan.text; break;
            default: pan_warn_if_reached ();
      }

      return retval;
}

static int
gui_page_set_idle (gpointer page_gpointer)
{
      int page = GPOINTER_TO_INT (page_gpointer);
      GtkWidget * focus_pane;
      debug_enter ("gui_page_set_idle");

      pan_lock ();
      focus_pane = gui_page_get_pane_nolock (page);
      switch (Pan.viewmode)
      {
            case GUI_PANED:
                  /* small tweak: it's not very useful to focus the message pane */
                  if (page==HEADERS_PANE || page==BODY_PANE)
                        focus_pane = gui_page_get_pane_nolock (HEADERS_PANE);
                  gui_paned_page_set_nolock (page, focus_pane);
                  break;

            case GUI_NOTEBOOK:
                  gui_notebook_page_set_nolock (page, focus_pane);
                  break;

            default:
                  pan_warn_if_reached ();
                  break;
      }
      pan_unlock ();

      debug_exit ("gui_page_set_idle");
      return 0;
}
void
gui_page_set (int page)
{
      debug_enter ("gui_page_set");

      gui_queue_add (gui_page_set_idle, GINT_TO_POINTER(page));

      debug_exit ("gui_page_set");
}

static int
gui_page_get_nolock (void)
{
      int retval = 0;

      switch (Pan.viewmode) {
            case GUI_PANED:    retval = gui_paned_get_current_pane_nolock();    break;
            case GUI_NOTEBOOK: retval = gui_notebook_get_current_pane_nolock(); break;
            default: pan_warn_if_reached(); break;
      }

      return retval;
}

static void
gui_select_all (void)
{
      const int pane = gui_page_get_nolock ();
      switch (pane) {
            case GROUPS_PANE:
                  grouplist_select_all ();
                  break;
            case HEADERS_PANE:
                  articlelist_select_all_nolock ();
                  break;
            case BODY_PANE:
                  text_select_all ();
                  break;
      }
}

static void
gui_deselect_all (void)
{
      const int pane = gui_page_get_nolock ();
      switch (pane) {
            case GROUPS_PANE:
                  grouplist_deselect_all ();
                  break;
            case HEADERS_PANE:
                  articlelist_deselect_all_nolock ();
                  break;
            case BODY_PANE:
                  text_deselect_all ();
                  break;
      }
}

static void
gui_page_activate_nolock (int pane)
{
      switch (pane)
      {
            case GROUPS_PANE:
            {
                  Group * g = grouplist_get_selected_group ();
                  if (g != NULL)
                        articlelist_set_group (g);
                  break;
            }

            case HEADERS_PANE:
                  articlelist_activate_selected ();
                  break;

            case BODY_PANE:
                  break;

            default:
                  pan_warn_if_reached ();
                  break;
      }
}

static void
gui_page_change_nolock (int change)
{
      int page = gui_page_get_nolock () + change;

      if (0<=page && page<=2)
            gui_page_set (page);
}

static gboolean
gui_key_press_cb (GtkWidget      * widget,
                  GdkEventKey    * event,
                  gpointer         data)
{
      gboolean retval = FALSE;

      if (event->keyval == GDK_Return)
      {
            gui_page_activate_nolock (gui_page_get_nolock ());
            gui_page_change_nolock (1);
            retval = TRUE;
      }

      return retval;
}


/****
*****
*****   SECTION:  SHUTDOWN
*****
****/

static unsigned int gui_refresh_timer_id = 0;

static void
get_accel_filename (char * buf, int buf_max)
{
      g_snprintf (buf, buf_max, "%s%caccels.txt", get_data_dir(), G_DIR_SEPARATOR);
}

static void
gui_shutdown (void)
{
      int i;
      char * pch;
      char fname[PATH_MAX];
      GtkWidget * w[3];
      debug_enter ("gui_shutdown");

      /**
      ***  Stop updating the UI
      **/

      pan_callback_remove (status_item_get_active_callback(), status_item_active_changed_cb, NULL);
      turn_off_status_item_views_nolock ();

      /**
      ***  Stop updating the rest of the UI
      **/

      pan_callback_remove (article_get_articles_changed_callback(), articles_changed_cb, NULL);
      pan_callback_remove (articlelist_get_group_changed_callback(), update_menus_cb, NULL);
      pan_callback_remove (articlelist_get_selection_changed_callback(), update_menus_cb, NULL);
      pan_callback_remove (current_article_changed, update_menus_cb, NULL);
      pan_callback_remove (grouplist_group_selection_changed, update_menus_cb, NULL);
      pan_callback_remove (serverlist_get_server_activated_callback(), update_menus_cb, NULL);
      pan_callback_remove (log_get_entry_added_callback(), log_entry_added_cb, NULL);
      pan_callback_remove (queue_get_size_changed_callback(), queue_size_changed_cb, NULL);
      pan_callback_remove (queue_get_connection_size_changed_callback(), queue_connection_size_changed_cb, NULL);
      pan_callback_remove (text_get_fill_body_changed_callback(), text_fill_body_changed_cb, NULL);
      pan_callback_remove (text_get_mute_quoted_changed_callback(), mute_quoted_text_changed_cb, NULL);
      pan_callback_remove (text_get_show_all_headers_changed_callback(), show_all_headers_changed_cb, NULL);
      pan_callback_remove (queue_get_online_status_changed_callback(), online_status_changed_cb, NULL);

      g_source_remove (gui_refresh_timer_id);
      gui_refresh_timer_id = 0;
      gui_queue_shutdown ();

      /**
      ***  Save settings
      **/

      pan_config_set_bool_if_different (KEY_GROUP_PANE_ENABLED,               show_group_pane,                  DEFAULT_VALUE_GROUP_PANE_ENABLED);
      pan_config_set_bool_if_different (KEY_GROUP_PANE_COLLAPSE_GROUP_NAMES,  collapse_group_names,             DEFAULT_VALUE_GROUP_PANE_COLLAPSE_NAMES);
      pan_config_set_bool_if_different (KEY_HEADER_PANE_ENABLED,              show_header_pane,                 DEFAULT_VALUE_HEADER_PANE_ENABLED);
      pan_config_set_bool_if_different (KEY_HEADER_PANE_THREADING_ENABLED,    header_pane_is_threaded,          DEFAULT_VALUE_HEADER_PANE_IS_THREADED);
      pan_config_set_bool_if_different (KEY_BODY_PANE_ENABLED,                show_body_pane,                   DEFAULT_VALUE_BODY_PANE_ENABLED);
      pan_config_set_bool_if_different (KEY_BODY_PANE_WRAP_ENABLED,           text_get_wrap(),                  DEFAULT_VALUE_BODY_PANE_WRAP_ENABLED);
      pan_config_set_bool_if_different (KEY_BODY_PANE_MONOSPACE_FONT_ENABLED, body_pane_monospace_font_enabled, DEFAULT_VALUE_BODY_PANE_MONOSPACE_FONT_ENABLED);
      pan_config_set_bool_if_different (KEY_BODY_PANE_SHOW_ALL_HEADERS,       text_get_show_all_headers(),      DEFAULT_VALUE_BODY_PANE_SHOW_ALL_HEADERS);
      pan_config_set_bool_if_different (KEY_BODY_PANE_MUTE_QUOTED_TEXT,       text_get_mute_quoted(),           DEFAULT_VALUE_BODY_PANE_MUTE_QUOTED);
      pan_config_set_int_if_different  (KEY_PANE_MODE,                        Pan.viewmode,                     DEFAULT_VALUE_PANE_MODE);

      grouplist_shutdown_module ();

      switch (Pan.viewmode)
      {
            case GUI_NOTEBOOK:
                  pan_config_set_int ("/Pan/State/page", gui_page_get_nolock ());
                  break;

            case GUI_PANED:
                  pch = layout_str + 1;
                  for (i=0; i<=2; ++i, ++pch)
                  {
                        if (tolower(*pch)=='g')
                              w[i] = groups_vbox;
                        else if (tolower(*pch)=='t')
                              w[i] = articlelist_ctree;
                        else
                              w[i] = text_box;
                  }
                  switch(*layout_str)
                  {
                        case '2':
                              pan_config_set_int ("/Pan/Geometry/vpaned", w[0]->allocation.height);
                              pan_config_set_int ("/Pan/Geometry/hpaned", w[1]->allocation.width);
                              break;
                        case '4':
                              pan_config_set_int ("/Pan/Geometry/vpaned", w[1]->allocation.height);
                              pan_config_set_int ("/Pan/Geometry/hpaned", w[0]->allocation.width);
                              break;
                        case '5':
                              pan_config_set_int ("/Pan/Geometry/vpaned", w[0]->allocation.height);
                              pan_config_set_int ("/Pan/Geometry/hpaned", w[0]->allocation.height + w[1]->allocation.height);
                              break;
                        case '1':
                        case '3':
                        default:
                              pan_config_set_int ("/Pan/Geometry/vpaned", w[0]->allocation.height);
                              pan_config_set_int ("/Pan/Geometry/hpaned", w[0]->allocation.width);
                              break;
                  }
      }

      gui_save_column_widths (Pan.group_tree, "group");
      gui_save_column_widths (Pan.article_ctree, "header_pane_2");
      gui_save_window_size (Pan.window, "main_window");

      /**
      *** Save accelerators
      **/

      get_accel_filename (fname, sizeof(fname));
      gtk_accel_map_save (fname);

      /**
      ***  Clear out the UI pieces
      **/

      gtk_widget_unref (groups_vbox);
      gtk_widget_unref (articlelist_ctree);
      gtk_widget_unref (text_box);

      articlelist_set_group (NULL);
      text_clear_nolock ();
      debug_exit ("gui_shutdown");
}

/****
*****
*****   SECTION:  STARTUP
*****
****/

static void
queue_button_clicked (GtkButton *button,
                      gpointer user_data)
{
      task_manager_spawn ();
}

static GtkWidget*
gui_create_appbar (GtkWidget* window)
{
      int i;
      GtkWidget * w;
      GtkWidget * frame;
      GtkWidget * taskbar;
      GtkWidget * appbar;

      appbar = gtk_hbox_new (FALSE, GUI_PAD_SMALL);
      gtk_container_set_border_width (GTK_CONTAINER(appbar), GUI_PAD_SMALL);

      /* connection status */
      w = connection_qty_label = gtk_label_new (NULL);
      gtk_misc_set_padding (GTK_MISC(w), GUI_PAD, 0);
      frame = gtk_frame_new (NULL);
      gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_IN);
      gtk_container_add (GTK_CONTAINER(frame), w);
      gtk_box_pack_start (GTK_BOX(appbar), frame, FALSE, FALSE, 0);

      /* task status */
      queue_qty_label = gtk_label_new (NULL);
      w = queue_qty_button = gtk_button_new();
      gtk_tooltips_set_tip (GTK_TOOLTIPS(ttips), w, _("Open the Task Manager"), NULL);
      gtk_button_set_relief (GTK_BUTTON(w), GTK_RELIEF_NONE);
      g_signal_connect (GTK_OBJECT(w), "clicked", G_CALLBACK(queue_button_clicked), NULL);
      gtk_container_add (GTK_CONTAINER(w), queue_qty_label);
      frame = gtk_frame_new (NULL);
      gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_IN);
      gtk_container_add (GTK_CONTAINER(frame), w);
      gtk_box_pack_start (GTK_BOX(appbar), frame, FALSE, FALSE, 0);

      /* status item views */
      taskbar = gtk_table_new (1, VIEW_QTY, TRUE);
      for (i=0; i<VIEW_QTY; ++i) {
            GtkWidget * w = views[i] = status_item_view_new ();
            gtk_table_attach (GTK_TABLE(taskbar), w, i, i+1, 0, 1, ~0, ~0, 0, 0);
      }
      gtk_box_pack_start (GTK_BOX(appbar), taskbar, TRUE, TRUE, 0);

      /* status icons */
      for (i=0; i<STATUS_QTY; ++i) {
            _status_icons[i].pixbuf = gdk_pixbuf_new_from_inline (-1, _status_icons[i].inline_txt,
                                                                  FALSE, NULL);
            gdk_pixbuf_render_pixmap_and_mask_for_colormap (_status_icons[i].pixbuf, cmap,
                                                            &_status_icons[i].pixmap,
                                                            &_status_icons[i].bitmap, 128);
      }

      /* status button */
      w = gtk_button_new ();
      _status_image = gtk_image_new ();
      gtk_container_add (GTK_CONTAINER(w), _status_image);
      set_current_status_nolock (STATUS_OK);
      gtk_tooltips_set_tip (GTK_TOOLTIPS(ttips), w, _("Open the Status Log"), NULL);
      gtk_button_set_relief (GTK_BUTTON(w), GTK_RELIEF_NONE);
      g_signal_connect (GTK_OBJECT(w), "clicked", G_CALLBACK(log_button_cb), NULL);
      gtk_box_pack_start (GTK_BOX(appbar), w, FALSE, FALSE, 0);

      gui_set_queue_size (0u, 0u);
      gui_set_connection_size (0);

      return appbar;
}

static int
window_delete_event_cb (GtkWidget *widget,
                  GdkEvent *event,
                  gpointer  data)
{
      gui_shutdown ();
      return FALSE; /* causes "destroy" signal to be fired */
}

static char*
menu_translate(const char* path, gpointer data)
{
      return (char*) gettext (path);
}

/***
****  Toolbar Utilities
***/

static GtkIconSet * online_icon_set = NULL;
static GtkIconSet * offline_icon_set = NULL;

typedef struct
{
      GtkWidget * button;
      GtkWidget * image;
      GtkIconSize icon_size;
}
ConnectionButtonCallbackStruct;

static int
online_status_changed_idle (gpointer user_data)
{
      ConnectionButtonCallbackStruct * cbs = (ConnectionButtonCallbackStruct *) user_data;
      const gboolean is_online = queue_is_online ();

      gtk_tooltips_set_tip (GTK_TOOLTIPS(ttips), GTK_WIDGET(cbs->button),
            is_online ? _("Pan is Online") : _("Pan is Offline"), NULL);
      gtk_image_set_from_icon_set (GTK_IMAGE(cbs->image),
            is_online ? online_icon_set : offline_icon_set, cbs->icon_size);

      return 0;
}
static void
toolbar_connection_online_status_changed_cb (gpointer call_obj,
                          gpointer call_arg,
                          gpointer user_data)
{
      gui_queue_add (online_status_changed_idle, user_data);
}
static void
online_clicked_cb (GtkToggleButton  * tb,
                   gpointer           unused)
{
      queue_set_online (!queue_is_online());
}
static void
network_button_destroy_cb (GtkWidget * w, gpointer user_data)
{
      pan_callback_remove (queue_get_online_status_changed_callback(),
                           toolbar_connection_online_status_changed_cb, user_data);

      g_free (user_data);
}
GtkWidget*
gui_add_network_button_to_toolbar (GtkWidget * toolbar)
{
      GtkWidget * image;
      GtkWidget * button;
      ConnectionButtonCallbackStruct * cbs;
      const gboolean is_online = queue_is_online ();
      const GtkIconSize icon_size = gtk_toolbar_get_icon_size (GTK_TOOLBAR(toolbar));

      if (online_icon_set == NULL)
      {
            GdkPixbuf * pixbuf;

              /* online icon set */
              pixbuf = gdk_pixbuf_new_from_inline (-1, icon_online, FALSE, NULL);
            online_icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
            g_object_unref (G_OBJECT(pixbuf));

            /* offline icon set */
            pixbuf = gdk_pixbuf_new_from_inline (-1, icon_offline, FALSE, NULL);
            offline_icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
            g_object_unref (G_OBJECT(pixbuf));
      }

      image = gtk_image_new_from_icon_set (is_online ? online_icon_set : offline_icon_set, icon_size);
      button = gtk_toolbar_append_element (GTK_TOOLBAR(toolbar),
                                           GTK_TOOLBAR_CHILD_BUTTON, NULL,
                                           _("Connection"),
                                           is_online ? _("Pan is Online") : _("Pan is Offline"),
                                           NULL,
                                           image,
                                           G_CALLBACK(online_clicked_cb), NULL);
      cbs = g_new (ConnectionButtonCallbackStruct, 1);
      cbs->image = image;
      cbs->button = button;
      cbs->icon_size = icon_size;
      pan_callback_add (queue_get_online_status_changed_callback(),
                        toolbar_connection_online_status_changed_cb, cbs); 
      g_signal_connect (button, "destroy", G_CALLBACK(network_button_destroy_cb), cbs);
      return button;
}

/**
***
**/

static void
toolbar_cb (GtkWidget * button, gpointer user_data)
{
      const PanAction action = (PanAction) user_data;
      pan_action_do (action);
}
static void
menu_cb (gpointer user_data, int iaction, GtkWidget * w)
{
      const PanAction action = (PanAction) iaction;
      pan_action_do (action);
}


/**
***
**/

static GtkWidget*
create_toolbar (void)
{
      GtkWidget * w;
      GtkWidget * toolbar_w;
      GtkToolbar * toolbar;
      char buf[512];
      const gboolean show_labels = pan_config_get_bool ("/Pan/Display/toolbar_labels_visible", TRUE);

      toolbar_w = gtk_toolbar_new ();
      toolbar = GTK_TOOLBAR(toolbar_w);
      gtk_toolbar_set_icon_size (toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
      gtk_toolbar_set_style (toolbar, GTK_TOOLBAR_ICONS);

      if (show_labels) {
            g_snprintf (buf, sizeof(buf), "%s:", _("Post"));
            w = gtk_label_new (buf);
            gtk_misc_set_alignment (GTK_MISC(w), 0.5f, 0.5f);
            gtk_misc_set_padding (GTK_MISC(w), GUI_PAD, 0);
            gtk_toolbar_append_widget (toolbar, w, NULL, NULL);
      }
      _toolbar_buttons[ACTION_COMPOSE_NEW] = gtk_toolbar_append_item (
            toolbar, "Post", _("Post to Newsgroup"), NULL,
            pan_gtk_image_new_from_inline_text (icon_compose_post, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_COMPOSE_NEW));
      _toolbar_buttons[ACTION_COMPOSE_FOLLOWUP] = gtk_toolbar_append_item (
            toolbar, "Followup", _("Followup to Newsgroup"), NULL,
            pan_gtk_image_new_from_inline_text (icon_compose_followup, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_COMPOSE_FOLLOWUP));
      gtk_toolbar_append_space (toolbar);

      if (show_labels) {
            g_snprintf (buf, sizeof(buf), "%s:", _("Get"));
            w = gtk_label_new (buf);
            gtk_misc_set_alignment (GTK_MISC(w), 0.5f, 0.5f);
            gtk_misc_set_padding (GTK_MISC(w), GUI_PAD, 0);
            gtk_toolbar_append_widget (toolbar, w, NULL, NULL);
      }
      _toolbar_buttons[ACTION_GET_NEW_HEADERS_FOR_SUBSCRIBED] = gtk_toolbar_append_item (
            toolbar, NULL, _("Get New Headers in Subscribed Groups"), NULL,
            pan_gtk_image_new_from_inline_text (icon_get_subscribed, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_GET_NEW_HEADERS_FOR_SUBSCRIBED));
      _toolbar_buttons[ACTION_GET_NEW_HEADERS_FOR_SELECTED] = gtk_toolbar_append_item (
            toolbar, NULL, _("Get New Headers in Selected Groups"), NULL,
            pan_gtk_image_new_from_inline_text (icon_get_new, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_GET_NEW_HEADERS_FOR_SELECTED));
      _toolbar_buttons[ACTION_GET_BODIES_FOR_SELECTED] = gtk_toolbar_append_item (
            toolbar, NULL, _("Get Selected Articles"), NULL,
            pan_gtk_image_new_from_inline_text (icon_get_selected, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_GET_BODIES_FOR_SELECTED));
      gtk_toolbar_append_space (toolbar);

      if (show_labels) {
            g_snprintf (buf, sizeof(buf), "%s:", _("Reading"));
            w = gtk_label_new (buf);
            gtk_misc_set_alignment (GTK_MISC(w), 0.5f, 0.5f);
            gtk_misc_set_padding (GTK_MISC(w), GUI_PAD, 0);
            gtk_toolbar_append_widget (toolbar, w, NULL, NULL);
      }
      _toolbar_buttons[ACTION_READ_MORE] = gtk_toolbar_append_item (
            toolbar, NULL, _("Read More"), NULL,
            pan_gtk_image_new_from_inline_text (icon_read_more, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_READ_MORE));
      _toolbar_buttons[ACTION_READ_NEXT_UNREAD_ARTICLE] = gtk_toolbar_append_item (
            toolbar, NULL, _("Read Next Unread Article"), NULL,
            pan_gtk_image_new_from_inline_text (icon_read_unread_article, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_READ_NEXT_UNREAD_ARTICLE));
      _toolbar_buttons[ACTION_READ_NEXT_UNREAD_THREAD] = gtk_toolbar_append_item (
            toolbar, NULL, _("Read Next Unread Thread"), NULL,
            pan_gtk_image_new_from_inline_text (icon_read_unread_thread, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_READ_NEXT_UNREAD_THREAD));
      _toolbar_buttons[ACTION_READ_NEXT_UNREAD_GROUP] = gtk_toolbar_append_item (
            toolbar, NULL, _("Read Next Unread Group"), NULL,
            pan_gtk_image_new_from_inline_text (icon_read_group, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_READ_NEXT_UNREAD_GROUP));
      _toolbar_buttons[ACTION_SAVE] = gtk_toolbar_insert_stock (
            toolbar, GTK_STOCK_SAVE, _("Save Attachments"), NULL,
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_SAVE), -1);
      _toolbar_buttons[ACTION_SAVE_AS] = gtk_toolbar_insert_stock (
            toolbar, GTK_STOCK_SAVE_AS, _("Save Attachments As..."), NULL,
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_SAVE_AS), -1);
      gtk_toolbar_append_space (toolbar);

      if (show_labels) {
            g_snprintf (buf, sizeof(buf), "%s:", _("Filters"));
            w = gtk_label_new (buf);
            gtk_misc_set_alignment (GTK_MISC(w), 0.5f, 0.5f);
            gtk_misc_set_padding (GTK_MISC(w), GUI_PAD, 0);
            gtk_toolbar_append_widget (toolbar, w, NULL, NULL);
      }
      _toolbar_buttons[ACTION_SHOW_ONLY_NEW] = gtk_toolbar_append_element (
            toolbar, GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, _("New"), _("Match Only New Articles"), NULL,
            pan_gtk_image_new_from_inline_text (icon_filter_only_new, GTK_ICON_SIZE_SMALL_TOOLBAR), NULL, NULL);
      _toolbar_buttons[ACTION_SHOW_ONLY_CACHED] = gtk_toolbar_append_element (
            toolbar, GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, _("Cached"), _("Match Only Cached Articles"), NULL,
            pan_gtk_image_new_from_inline_text (icon_filter_only_cached, GTK_ICON_SIZE_SMALL_TOOLBAR), NULL, NULL);
      _toolbar_buttons[ACTION_SHOW_ONLY_ATTACHMENTS] = gtk_toolbar_append_element (
            toolbar, GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, _("Files"), _("Match Only Complete Attachments"), NULL,
            pan_gtk_image_new_from_inline_text (icon_filter_only_attachments, GTK_ICON_SIZE_SMALL_TOOLBAR), NULL, NULL);
      _toolbar_buttons[ACTION_SHOW_ONLY_MINE] = gtk_toolbar_append_element (
            toolbar, GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, _("Mine"), _("Match Only My Articles"), NULL,
            pan_gtk_image_new_from_inline_text (icon_filter_only_me, GTK_ICON_SIZE_SMALL_TOOLBAR), NULL, NULL);
      _toolbar_buttons[ACTION_SHOW_ONLY_WATCHED] = gtk_toolbar_append_element (
            toolbar, GTK_TOOLBAR_CHILD_TOGGLEBUTTON, NULL, _("Score"), _("Match Only Watched Articles"), NULL,
            pan_gtk_image_new_from_inline_text (icon_filter_only_watched, GTK_ICON_SIZE_SMALL_TOOLBAR), NULL, NULL);
      gtk_toolbar_append_space (toolbar);

      if (show_labels) {
            g_snprintf (buf, sizeof(buf), "%s:", _("Net"));
            w = gtk_label_new (buf);
            gtk_misc_set_alignment (GTK_MISC(w), 0.5f, 0.5f);
            gtk_misc_set_padding (GTK_MISC(w), GUI_PAD, 0);
            gtk_toolbar_append_widget (toolbar, w, NULL, NULL);
      }
      _toolbar_buttons[ACTION_NET_ONLINE] = gui_add_network_button_to_toolbar (toolbar_w);
      _toolbar_buttons[ACTION_NET_CANCEL_LAST_TASK] = gtk_toolbar_append_item (
            toolbar, "Cancel", _("Cancel Last Task"), NULL,
            pan_gtk_image_new_from_inline_text (icon_cancel, GTK_ICON_SIZE_SMALL_TOOLBAR),
            G_CALLBACK(toolbar_cb), GINT_TO_POINTER(ACTION_NET_CANCEL_LAST_TASK));

      return toolbar_w;
}

static GtkWidget*
get_menu_widget (const char * key)
{
      GtkWidget * w = gtk_item_factory_get_item (_main_menu_factory, key);
      g_assert (w != NULL);
      return w;
}

void
gui_construct (const char * geometry)
{
      GtkWidget * w;
      GtkWidget * vbox;
      GtkWidget * menubar;
      char fname[PATH_MAX];
      debug_enter ("gui_construct");

      ttips = gtk_tooltips_new();
        cmap = gdk_colormap_get_system ();
      /*register_stock_icons ();*/

      /**
      ***  Make the Main Window
      **/

      /* create the main window */
      Pan.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title (GTK_WINDOW(Pan.window), "Pan");
      gtk_window_set_role (GTK_WINDOW(Pan.window), "pan-main-window");
      if (!gui_restore_window_size (Pan.window, "main_window"))
            gtk_window_set_default_size (GTK_WINDOW(Pan.window), 820, 700);
      g_signal_connect (GTK_OBJECT(Pan.window), "delete_event", 
                        G_CALLBACK (window_delete_event_cb), NULL);
      g_signal_connect (GTK_OBJECT(Pan.window), "destroy",
                        G_CALLBACK (pan_shutdown), NULL);

      /* set the window icon */
      if (1) {
            GdkPixbuf * icon = gdk_pixbuf_new_from_inline (-1, icon_pan, FALSE, NULL);
            GList * l = g_list_append (NULL, icon);
            gtk_window_set_default_icon_list (l);
            g_list_free (l);
      }

      /* create the table */
      vbox = gtk_vbox_new (FALSE, 0);
      gtk_container_add (GTK_CONTAINER(Pan.window), vbox);

      /* create the menubar */
      _main_accel_group = gtk_accel_group_new ();
      gtk_window_add_accel_group (GTK_WINDOW(Pan.window), _main_accel_group);
      g_object_unref (_main_accel_group);
      _main_menu_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", _main_accel_group);
      gtk_item_factory_set_translate_func (_main_menu_factory, menu_translate, NULL, NULL);
      gtk_item_factory_create_items (_main_menu_factory, MAIN_MENU_ENTRIES_QTY, main_menu_entries, NULL);
      menubar = gtk_item_factory_get_widget (_main_menu_factory, "<main>");
      gtk_box_pack_start (GTK_BOX(vbox), menubar, FALSE, FALSE, 0);

      /* load accelerators */
      get_accel_filename (fname, sizeof(fname));
      gtk_accel_map_load (fname);

      /* create the toolbar */
      w = create_toolbar ();
      if (pan_config_get_bool ("/Pan/Display/toolbar_visible", TRUE))
            gtk_box_pack_start (GTK_BOX(vbox), w, FALSE, FALSE, 0);

      /* create the workarea */
      Pan.workarea = gtk_vbox_new (TRUE, 0);
      gtk_box_pack_start (GTK_BOX(vbox), Pan.workarea, TRUE, TRUE, 0);

      /* create the statusbar */
      memset (views, '\0', sizeof(views));
      w = gui_create_appbar (Pan.window);
      gtk_box_pack_start (GTK_BOX(vbox), w, FALSE, FALSE, 0);

      /**
      ***  Make the Workarea Widgetry
      **/   

      /* group pane */
      groups_vbox = gtk_vbox_new (FALSE, 0);
      gtk_widget_set_usize (GTK_WIDGET (groups_vbox), 320, -1);
      gtk_box_pack_start (GTK_BOX (groups_vbox), GTK_WIDGET(grouplist_create()), TRUE, TRUE, 1);
      g_signal_connect (GTK_OBJECT (Pan.group_tree), "key_press_event",
                    G_CALLBACK (gui_key_press_cb),  NULL);

      /* article pane */
      articlelist_ctree = create_articlelist_ctree ();
        g_signal_connect (GTK_OBJECT (Pan.article_ctree), "key_press_event",
                          G_CALLBACK (gui_key_press_cb), NULL);

      /* text pane */
      text_box = text_create ();
        g_signal_connect (GTK_OBJECT (Pan.text), "key_press_event",
                          G_CALLBACK (gui_key_press_cb), NULL);

      /* set the workarea layout */
      gtk_widget_ref (groups_vbox);
      gtk_widget_ref (articlelist_ctree);
      gtk_widget_ref (text_box);

      /* this also updates menu sensitivity */
      gui_layout_set_mode (pan_config_get_int (KEY_PANE_MODE, DEFAULT_VALUE_PANE_MODE));

      pan_callback_add (article_get_articles_changed_callback(), articles_changed_cb, NULL);
      pan_callback_add (articlelist_get_group_changed_callback(), update_menus_cb, NULL);
      pan_callback_add (articlelist_get_selection_changed_callback(), update_menus_cb, NULL );
      pan_callback_add (current_article_changed, update_menus_cb, NULL);
      pan_callback_add (grouplist_group_selection_changed, update_menus_cb, NULL);
      pan_callback_add (serverlist_get_server_activated_callback(), update_menus_cb, NULL);
      pan_callback_add (log_get_entry_added_callback(), log_entry_added_cb, NULL);
      pan_callback_add (queue_get_size_changed_callback(), queue_size_changed_cb, NULL);
      pan_callback_add (queue_get_connection_size_changed_callback(), queue_connection_size_changed_cb, NULL);
      pan_callback_add (status_item_get_active_callback(), status_item_active_changed_cb, NULL);
      pan_callback_add (text_get_fill_body_changed_callback(), text_fill_body_changed_cb, NULL);
      pan_callback_add (text_get_show_all_headers_changed_callback(), show_all_headers_changed_cb, NULL);
      pan_callback_add (text_get_mute_quoted_changed_callback(), mute_quoted_text_changed_cb, NULL);
      pan_callback_add (queue_get_online_status_changed_callback(), online_status_changed_cb, NULL);

      menu_set_checked (_main_menu_factory, "/File/Work Online", queue_is_online());
      menu_set_checked (_main_menu_factory, "/View/Abbreviate Names in Group Pane", collapse_group_names);
      menu_set_checked (_main_menu_factory, "/View/Wrap Article Body", text_get_wrap());
      menu_set_checked (_main_menu_factory, "/View/Show All Headers in Body Pane", text_get_show_all_headers());
      menu_set_checked (_main_menu_factory, "/View/Use Monospace Font in Body Pane", body_pane_monospace_font_enabled);
      menu_set_checked (_main_menu_factory, "/View/Thread Header Pane", header_pane_is_threaded);
      menu_set_checked (_main_menu_factory, "/View/Show Group Pane", show_group_pane);
      menu_set_checked (_main_menu_factory, "/View/Show Header Pane", show_header_pane);
      menu_set_checked (_main_menu_factory, "/View/Show Body Pane", show_body_pane);

      text_set_show_all_headers (pan_config_get_bool(KEY_BODY_PANE_SHOW_ALL_HEADERS, DEFAULT_VALUE_BODY_PANE_SHOW_ALL_HEADERS));
      text_set_mute_quoted (pan_config_get_bool(KEY_BODY_PANE_MUTE_QUOTED_TEXT, DEFAULT_VALUE_BODY_PANE_MUTE_QUOTED));
      gui_refresh_timer_id = pan_timeout_add (3000, gui_refresh_timer_cb, NULL); 
      gui_queue_init ();
      gtk_widget_show_all (Pan.window);
      server_menu_update ();

      if (1)
      {
            FilterCurrentMediatorCtor ctor;

            ctor._match_only_new_ckm                = get_menu_widget ("/Filter/Match New Articles");
            ctor._match_only_unread_ckm             = get_menu_widget ("/Filter/Match Unread Articles");
            ctor._match_only_read_ckm               = get_menu_widget ("/Filter/Match Read Articles");
            ctor._match_only_new_tb                 = _toolbar_buttons[ACTION_SHOW_ONLY_NEW];

            ctor._match_only_complete_ckm           = get_menu_widget ("/Filter/Match Only Binary Attachment Articles");
            ctor._match_only_text_ckm               = get_menu_widget ("/Filter/Match Only Text Articles");
            ctor._match_only_complete_tb            = _toolbar_buttons[ACTION_SHOW_ONLY_ATTACHMENTS];

            ctor._match_only_cached_ckm             = get_menu_widget ("/Filter/Match Only Cached Articles");
            ctor._match_only_cached_tb              = _toolbar_buttons[ACTION_SHOW_ONLY_CACHED];

            ctor._match_only_mine_ckm               = get_menu_widget ("/Filter/Match Only My Articles");
            ctor._match_only_mine_tb                = _toolbar_buttons[ACTION_SHOW_ONLY_MINE];

              ctor._match_only_watched_rmi            = get_menu_widget ("/Filter/Match Scores of 9999 (Watched)");
              ctor._match_only_high_rmi               = get_menu_widget ("/Filter/Match Scores in 5000...9998 (High)");
              ctor._match_only_medium_rmi             = get_menu_widget ("/Filter/Match Scores in 1...4999 (Medium)");
              ctor._match_only_zero_rmi               = get_menu_widget ("/Filter/Match Scores of 0 (Normal)");
              ctor._match_only_low_rmi                = get_menu_widget ("/Filter/Match Scores in -9998...-1 (Low)");
              ctor._match_only_ignored_rmi            = get_menu_widget ("/Filter/Match Scores of -9999 (Ignored)");
            ctor._match_only_watched_tb             = _toolbar_buttons[ACTION_SHOW_ONLY_WATCHED];

            ctor._show_articles_rmi                 = get_menu_widget ("/Filter/Show Matching Articles");
            ctor._show_subthreads_rmi               = get_menu_widget ("/Filter/Show Matching Articles' Subthreads");
            ctor._show_threads_rmi                  = get_menu_widget ("/Filter/Show Matching Articles' Threads");

            filter_mediator_init (&ctor);
      }

      debug_exit ("gui_construct");
}

/****
*****
*****   SECTION:  NAVIGATION
*****
****/


/****
*****
*****   SECTION: MENU CALLBACKS
*****
****/

static void
pan_homepage_url (void)
{
      pan_url_show ("http://pan.rebelbase.com/");
}

static void
pan_bug_url (void)
{
      pan_url_show ("http://pan.rebelbase.com/bugs/");
}

static void
dialog_about_cb (void)
{
      dialog_about (Pan.window);
}

static void
thread_articles_cb (gpointer user_data, int action, GtkWidget * w)
{
      GtkCheckMenuItem * item = GTK_CHECK_MENU_ITEM(w);
      g_return_if_fail (item!=NULL);
      articlelist_set_threaded (item->active);
}

static void
rot13_cb (gpointer user_data, int action, GtkWidget * w)
{
      text_rot13_selected_text_nolock ();
}

static void
fill_body_cb (gpointer user_data, int action, GtkWidget * w)
{
      GtkCheckMenuItem * item = GTK_CHECK_MENU_ITEM(w);
      g_return_if_fail (item!=NULL);
      text_set_wrap (item->active);
}

static void
show_all_headers_cb (gpointer user_data, int action, GtkWidget * w)
{
      GtkCheckMenuItem * item = GTK_CHECK_MENU_ITEM(w);
      g_return_if_fail (item!=NULL);
      text_set_show_all_headers (item->active);
}

static void
mute_quoted_text_cb (gpointer user_data, int action, GtkWidget * w)
{
      GtkCheckMenuItem * item = GTK_CHECK_MENU_ITEM(w);
      g_return_if_fail (item!=NULL);
      text_set_mute_quoted (item->active);
}

static void
use_fixed_font_cb (gpointer user_data, int action, GtkWidget * w)
{
      GtkCheckMenuItem * item = GTK_CHECK_MENU_ITEM(w);
      g_return_if_fail (item!=NULL);
      body_pane_monospace_font_enabled = item->active;
      text_set_font ();
}

static void
collapse_group_names_cb (gpointer user_data, int action, GtkWidget * w)
{
      GtkCheckMenuItem * item = GTK_CHECK_MENU_ITEM(w);
      if (collapse_group_names != item->active) {
            collapse_group_names = item->active;
            grouplist_refresh_nolock ();
      }
}



static void
toggle_pane_visibility (gpointer user_data, int action, GtkWidget * w)
{
      GtkCheckMenuItem * item = GTK_CHECK_MENU_ITEM (w);

      if (current_layout == GUI_PANED)
      {
            gboolean * show_pane = NULL;

            switch (action) {
                  case GROUPS_PANE: show_pane = &show_group_pane; break;
                  case HEADERS_PANE: show_pane = &show_header_pane; break;
                  case BODY_PANE: show_pane = &show_body_pane; break;
                  default: pan_warn_if_reached ();
            }

            if (show_pane!=NULL && *show_pane!=item->active) {
                  *show_pane = item->active;
                  gui_layout_refresh ();
            }
      }
}

static void
zoom_page_cb (gpointer menu, gpointer page)
{
      debug_enter ("zoom_page_cb");
       
      gui_layout_set_mode (GUI_NOTEBOOK);
      gui_page_set (GPOINTER_TO_INT(page));

      debug_exit ("zoom_page_cb");
}

static void
zoom_cb (gpointer user_data, int action, GtkWidget * w)
{
      int new_layout;
      debug_enter ("zoom_cb");

        new_layout = GTK_CHECK_MENU_ITEM(w)->active ? GUI_NOTEBOOK : GUI_PANED;
      if (new_layout != current_layout)
      {
            const int page = gui_page_get_nolock ();
            gui_layout_set_mode (new_layout);
            gui_page_set (page);
      }

      debug_exit ("zoom_cb");
}

static void
work_online_cb (gpointer user_data, int action, GtkWidget * w)
{
      queue_set_online (GTK_CHECK_MENU_ITEM(w)->active);
}

static void
exit_cb (void)
{
      gui_shutdown ();
      gtk_object_destroy (GTK_OBJECT(Pan.window));
}

static void
widget_destroyed_cb (GtkObject * o, gpointer data)
{
      *((GtkWidget**)data) = NULL;
}

static void
identity_cb (void)
{
      static GtkWidget * identity_dialog = NULL;

      if (identity_dialog == NULL)
      {
            identity_dialog = identity_dialog_new (GTK_WINDOW(Pan.window));
            g_signal_connect (GTK_OBJECT(identity_dialog), "destroy",
                              G_CALLBACK(widget_destroyed_cb),
                              &identity_dialog);
      }

      gtk_widget_show_all (identity_dialog);
}

static void
server_custom_cb (void)
{
      static GtkWidget * server_dialog = NULL;

      if (server_dialog == NULL)
      {
            server_dialog = server_dialog_new (GTK_WINDOW(Pan.window));
            g_signal_connect (GTK_OBJECT(server_dialog), "destroy",
                              G_CALLBACK(widget_destroyed_cb),
                              &server_dialog);
      }

      gtk_widget_show_all (server_dialog);
}

static void
filter_custom_cb (void)
{
      static GtkWidget * filter_dialog = NULL;

      if (filter_dialog == NULL)
      {
            filter_dialog = filter_dialog_new (GTK_WINDOW(Pan.window));
            g_signal_connect (GTK_OBJECT(filter_dialog), "destroy",
                              G_CALLBACK(widget_destroyed_cb),
                              &filter_dialog);
      }

      gtk_widget_show_all (filter_dialog);
}

static void
log_button_cb (void)
{
      static GtkWidget * log_dialog = NULL;

      gui_queue_add (set_log_button_status_idle, GINT_TO_POINTER(STATUS_OK));

      if (log_dialog == NULL)
      {
            log_dialog = log_dialog_new (GTK_WINDOW(Pan.window));
            g_signal_connect (GTK_OBJECT(log_dialog), "destroy",
                              G_CALLBACK(widget_destroyed_cb), &log_dialog);
      }

      gtk_widget_show_all (log_dialog);
}

static void
rules_cb (void)
{
      static GtkWidget * rule_dialog = NULL;

      if (rule_dialog == NULL)
      {
            rule_dialog = rule_dialog_new (GTK_WINDOW(Pan.window));
            g_signal_connect (GTK_OBJECT(rule_dialog), "destroy",
                              G_CALLBACK(widget_destroyed_cb),
                              &rule_dialog);
      }

      gtk_widget_show_all (rule_dialog);
}

/****
*****
*****   SECTION:  MENU
*****
****/

static GtkItemFactoryEntry main_menu_entries[MAIN_MENU_ENTRIES_QTY] =
{
      /*   */ {N_("/_File"), NULL, NULL, 0, "<Branch>"},
      /* t */ {N_("/_File/Save A_ttachments"), "<shift>S", menu_cb, ACTION_SAVE, "<StockItem>", GTK_STOCK_SAVE},
      /* s */ {N_("/_File/_Save Attachments As..."), "<control>S", menu_cb, ACTION_SAVE_AS, "<StockItem>", GTK_STOCK_SAVE_AS},
      /* m */ {N_("/_File/_Manual Decode..."), "<control>M", menu_cb, ACTION_MANUAL_DECODE, "<StockItem>", GTK_STOCK_SAVE_AS},
      /*   */ {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
      /* l */ {N_("/_File/Work On_line"), "L", work_online_cb, 0, "<ToggleItem>"},
      /* c */ {N_("/_File/_Cancel Last Task"), "<control>Delete", menu_cb, ACTION_NET_CANCEL_LAST_TASK, "<ImageItem>", icon_cancel},
      /*   */ {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
      /* p */ {N_("/_File/_Print Article..."), "<Control>P", print_cb, 0, "<StockItem>", GTK_STOCK_PRINT},
      /*   */ {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
      /* n */ {N_("/_File/_New Folder..."), NULL, group_new_folder_dialog, 0, NULL, NULL},
      /* f */ {N_("/_File/Delete _Folder..."), NULL, group_action_selected_destroy, 0, "<StockItem>", GTK_STOCK_DELETE},
      /* e */ {N_("/_File/_Edit Article in Folder"), NULL, article_action_edit_selected, 0, NULL},
      /*   */ {N_("/_File/---"), NULL, NULL, 0, "<Separator>"},
#ifdef G_OS_WIN32
      /* x */ {N_("/_File/E_xit"), "<alt>F4", exit_cb, 0, "<StockItem>", GTK_STOCK_QUIT},
#else
      /* q */ {N_("/_File/_Quit"), NULL, exit_cb, 0, "<StockItem>", GTK_STOCK_QUIT},
#endif

      /*   */ {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
      /*   */ {N_("/_Edit/Select all"), "<control>A", gui_select_all, 0, NULL},
      /*   */ {N_("/_Edit/Deselect all"), "<shift><control>A", gui_deselect_all, 0, NULL},
      /*   */ {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
      /* g */ {N_("/_Edit/Select all _Groups"), "<control>B", grouplist_select_all, 0, NULL},
      /* u */ {N_("/_Edit/Add S_ubscribed to Selection"), "<shift>B", grouplist_add_subscribed_to_selection_nolock, 0, NULL},
      /* r */ {N_("/_Edit/Deselect all G_roups"), "<shift><control>B", grouplist_deselect_all, 0, NULL},
      /*   */ {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
      /* a */ {N_("/_Edit/Select all _Articles"), "<control>D", articlelist_select_all_nolock, 0, NULL},
      /* s */ {N_("/_Edit/Add _Subthreads to Selection"), "D", articlelist_add_replies_to_selection_nolock, 0, NULL},
      /* t */ {N_("/_Edit/Add _Threads to Selection"), "<shift>D", articlelist_add_thread_to_selection_nolock, 0, NULL},
      /* e */ {N_("/_Edit/Add S_et to Selection"), "E", articlelist_add_set_to_selection_nolock, 0, NULL},
      /* l */ {N_("/_Edit/Deselect a_ll Articles"), "<shift><control>D", articlelist_deselect_all_nolock, 0, NULL},
      /*   */ {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
      /* b */ {N_("/_Edit/Select Article _Body"), NULL, text_select_all, 0, NULL},
      /*   */ {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
      /* f */ {N_("/_Edit/_Find..."), "<control>F", articlelist_find_text_cb, 0, "<StockItem>", GTK_STOCK_FIND},
      /* n */ {N_("/_Edit/Find _Next"), "<control>G", articlelist_find_next_cb, 0, "<StockItem>", GTK_STOCK_FIND},
      /*   */ {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
      /* p */ {N_("/_Edit/_Preferences..."), NULL, prefs_spawn, 0, "<StockItem>", GTK_STOCK_PREFERENCES},

      /*   */ {N_("/_View"), NULL, NULL, 0, "<Branch>"},
      /* t */ {N_("/_View/_Tabbed Layout"), "Z", zoom_cb, 0, "<CheckItem>", GTK_STOCK_JUMP_TO},
      /* g */ {N_("/_View/Jump to _Group Tab"), "1", zoom_page_cb, GROUPS_PANE, "<StockItem>", GTK_STOCK_JUMP_TO},
      /* h */ {N_("/_View/Jump to _Header Tab"), "2", zoom_page_cb, HEADERS_PANE, "<StockItem>", GTK_STOCK_JUMP_TO},
      /* b */ {N_("/_View/Jump to _Body Tab"), "3", zoom_page_cb, BODY_PANE, "<StockItem>", GTK_STOCK_JUMP_TO},
      /*   */ {N_("/_View/---"), NULL, NULL, 0, "<Separator>"},
      /* p */ {N_("/_View/Show Group _Pane"), "<control>1", toggle_pane_visibility, GROUPS_PANE, "<ToggleItem>"},
      /* d */ {N_("/_View/Show Hea_der Pane"), "<control>2", toggle_pane_visibility, HEADERS_PANE, "<ToggleItem>"},
      /* y */ {N_("/_View/Show Bod_y Pane"), "<control>3", toggle_pane_visibility, BODY_PANE, "<ToggleItem>"},
      /*   */ {N_("/_View/---"), NULL, NULL, 0, "<Separator>"},
      /* n */ {N_("/_View/Abbreviate _Names in Group Pane"), "B", collapse_group_names_cb, 0, "<ToggleItem>"},
      /* e */ {N_("/_View/Thr_ead Header Pane"), "<control>E", thread_articles_cb, 0, "<ToggleItem>"},
      /* r */ {N_("/_View/_Rot13 Selected Text"), "<control><shift>R", rot13_cb, 0, NULL},
      /* w */ {N_("/_View/_Wrap Article Body"), "W", fill_body_cb, 0, "<ToggleItem>"},
      /* q */ {N_("/_View/Mute _Quoted Text"), "Q", mute_quoted_text_cb, 0, "<ToggleItem>"},
      /* h */ {N_("/_View/Show All _Headers in Body Pane"), "H", show_all_headers_cb, 0, "<ToggleItem>"},
      /* m */ {N_("/_View/Use _Monospace Font in Body Pane"), "C", use_fixed_font_cb, 0, "<ToggleItem>"},
      /*   */ {N_("/_View/---"), NULL, NULL, 0, "<Separator>"},
      /* x */ {N_("/_View/E_xpand Selected Threads"), "X", articlelist_expand_selected_threads, 0, NULL},
      /* o */ {N_("/_View/C_ollapse Selected Threads"), "<control>X", articlelist_collapse_selected_threads, 0, NULL},

      /*   */ {N_("/Filte_r"),                                          NULL, NULL, 0, "<Branch>"},
      /*   */ {N_("/Filte_r/Show Matching Articles"),                   NULL, NULL, 0, "<RadioItem>"},
      /*   */ {N_("/Filte_r/Show Matching Articles' Threads"),          NULL, NULL, 0, "/Filter/Show Matching Articles"},
      /*   */ {N_("/Filte_r/Show Matching Articles' Subthreads"),       NULL, NULL, 0, "/Filter/Show Matching Articles"},
      /*   */ {N_("/Filte_r/---"),                                      NULL, NULL, 0, "<Separator>"},
      /* c */ {N_("/Filte_r/Match Only _Cached Articles"),              NULL, NULL, 0, "<ToggleItem>"},
      /* t */ {N_("/Filte_r/Match Only _Text Articles"),                NULL, NULL, 0, "<ToggleItem>"},
      /* b */ {N_("/Filte_r/Match Only _Binary Attachment Articles"),   NULL, NULL, 0, "<ToggleItem>"},
      /* m */ {N_("/Filte_r/Match Only _My Articles"),                  NULL, NULL, 0, "<ToggleItem>"},
      /*   */ {N_("/Filte_r/---"),                                      NULL, NULL, 0, "<Separator>"},
      /* n */ {N_("/Filte_r/Match _New Articles"),                      NULL, NULL, 0, "<ToggleItem>"},
      /* u */ {N_("/Filte_r/Match _Unread Articles"),                   NULL, NULL, 0, "<ToggleItem>"},
      /* r */ {N_("/Filte_r/Match _Read Articles"),                     NULL, NULL, 0, "<ToggleItem>"},
      /*   */ {N_("/Filte_r/---"),                                      NULL, NULL, 0, "<Separator>"},
      /*   */ {N_("/Filte_r/Match Scores of 9999 (Watched)"),           NULL, NULL, 0, "<ToggleItem>"},
      /*   */ {N_("/Filte_r/Match Scores in 5000...9998 (High)"),       NULL, NULL, 0, "<ToggleItem>"},
      /*   */ {N_("/Filte_r/Match Scores in 1...4999 (Medium)"),        NULL, NULL, 0, "<ToggleItem>"},
      /*   */ {N_("/Filte_r/Match Scores of 0 (Normal)"),               NULL, NULL, 0, "<ToggleItem>"},
      /*   */ {N_("/Filte_r/Match Scores in -9998...-1 (Low)"),         NULL, NULL, 0, "<ToggleItem>"},
      /*   */ {N_("/Filte_r/Match Scores of -9999 (Ignored)"),          NULL, NULL, 0, "<ToggleItem>"},

      /*   */ {N_("/_Go"),                          NULL,                NULL, 0, "<Branch>"},
      /*   */ {N_("/_Go/Read More"),                "space",             menu_cb, ACTION_READ_MORE, "<ImageItem>", icon_read_more},
      /*   */ {N_("/_Go/Read Back"),                "BackSpace",         menu_cb, ACTION_READ_LESS, "<ImageItem>", icon_read_less},
      /*   */ {N_("/_Go/---"),                      NULL,                NULL, 0, "<Separator>"},
      /* u */ {N_("/_Go/Next _Unread Article"),     "N",                 menu_cb, ACTION_READ_NEXT_UNREAD_ARTICLE, "<ImageItem>", icon_read_unread_article},
      /* n */ {N_("/_Go/Next _New Article"),        "<shift>N",          menu_cb, ACTION_READ_NEXT_NEW_ARTICLE, "<ImageItem>", icon_read_new_article},
      /* a */ {N_("/_Go/Next _Article"),            "<control>N",        header_pane_read_next, 0, NULL},
      /* w */ {N_("/_Go/Next _Watched Article"),    "<control><shift>N", menu_cb, ACTION_READ_NEXT_SCORE_ARTICLE, 0, NULL},
      /*   */ {N_("/_Go/---"),                      NULL,                NULL, 0, "<Separator>"},
      /* t */ {N_("/_Go/Next Unread _Thread"),      "T",                 menu_cb, ACTION_READ_NEXT_UNREAD_THREAD, "<ImageItem>", icon_read_unread_thread},
      /* e */ {N_("/_Go/Next N_ew Thread"),         "<shift>T",          menu_cb, ACTION_READ_NEXT_NEW_THREAD, "<ImageItem>", icon_read_new_thread},
      /* d */ {N_("/_Go/Next Threa_d"),             "<control>T",        header_pane_read_next_thread, 0, NULL},
      /*   */ {N_("/_Go/---"),                      NULL,                NULL, 0, "<Separator>"},
      /* v */ {N_("/_Go/Pre_vious Article"),        "V",                 header_pane_read_prev, 0, NULL},
      /*   */ {N_("/_Go/Previous Thread"),          "<control>V",        header_pane_read_prev_thread, 0, NULL},
      /* r */ {N_("/_Go/Previous Article _Read"),   "<control><shift>V", header_pane_read_prev_read, 0, NULL},
      /* p */ {N_("/_Go/_Parent Article"),          "U",                 header_pane_read_parent, 0, NULL},
      /*   */ {N_("/_Go/---"),                      NULL,                NULL, 0, "<Separator>"},
      /* g */ {N_("/_Go/Next Unread _Group"),       "G",                 menu_cb, ACTION_READ_NEXT_UNREAD_GROUP, "<ImageItem>", icon_read_group},
      /* e */ {N_("/_Go/N_ext Group"),              "<shift>G",          grouplist_activate_next_group, 0, NULL},

      /*   */ {N_("/_Servers"), NULL, NULL, 0, "<Branch>"}, /* <-- the server list gets inserted here */
      /* a */ {N_("/_Servers/Get List of _All Groups"), NULL, grouplist_get_all, 0, NULL},
      /* n */ {N_("/_Servers/Get List of _New Groups"), NULL, grouplist_get_new, 0, NULL},
      /*   */ {N_("/_Servers/---"), NULL, NULL, 0, "<Separator>"},

      /*   */ {N_("/_Newsgroups"), NULL, NULL, 0, "<Branch>"},
      /* r */ {N_("/_Newsgroups/Mark Group _Read"), "<control><shift>M", group_action_selected_mark_read, 0, "<StockItem>", GTK_STOCK_CLEAR},
      /* d */ {N_("/_Newsgroups/_Delete Group's Articles"), "<shift><control>Delete", group_action_selected_empty, 0, "<StockItem>", GTK_STOCK_DELETE},
      /*   */ {N_("/_Newsgroups/---"), NULL, NULL, 0, "<Separator>"},
      /* h */ {N_("/_Newsgroups/Get New _Headers"), "A", menu_cb, ACTION_GET_NEW_HEADERS_FOR_SELECTED, "<StockItem>", GTK_STOCK_JUSTIFY_LEFT},
      /* b */ {N_("/_Newsgroups/Get New Headers and _Bodies"), "<shift>A", menu_cb, ACTION_GET_BODIES_FOR_SELECTED, "<StockItem>", GTK_STOCK_JUSTIFY_FILL},
      /* o */ {N_("/_Newsgroups/More Download _Options..."), "<shift>O", group_action_selected_download_dialog, 0, NULL},
      /* c */ {N_("/_Newsgroups/Refresh Article _Counts"), NULL, group_action_selected_update_count_info, 0, "<StockItem>", GTK_STOCK_REFRESH},
      /*   */ {N_("/_Newsgroups/---"), NULL, NULL, 0, "<Separator>"},
      /* g */ {N_("/_Newsgroups/Get New Headers in Subscribed _Groups"), "<shift><control>U", menu_cb, ACTION_GET_NEW_HEADERS_FOR_SUBSCRIBED, "<ImageItem>", icon_get_subscribed},
      /* s */ {N_("/_Newsgroups/_Subscribe"), NULL, group_action_selected_subscribe, 0, "<ImageItem>", icon_newsgroup},
      /* u */ {N_("/_Newsgroups/_Unsubscribe"), NULL, group_action_selected_unsubscribe, 0, NULL},
      /* p */ {N_("/_Newsgroups/Group _Properties..."), NULL, group_action_selected_properties, 0, "<StockItem>", GTK_STOCK_PROPERTIES},
      /*   */ {N_("/_Newsgroups/---"), NULL, NULL, 0, "<Separator>"},
      /* t */ {N_("/_Newsgroups/Dele_te Group"), NULL, group_action_selected_destroy, 0, "<StockItem>", GTK_STOCK_DELETE},

      /*   */ {N_("/_Articles"), NULL, NULL, 0, "<Branch>"},
      /* r */ {N_("/_Articles/Mark _Read"), "M", article_action_mark_selected_read, 0, "<ImageItem>", icon_article_read},
      /* u */ {N_("/_Articles/Mark _Unread"), NULL, article_action_mark_selected_unread, 0, "<ImageItem>", icon_article_unread},
      /*   */ {N_("/_Articles/---"), NULL, NULL, 0, "<Separator>"},
      /* o */ {N_("/_Articles/D_ownload Flagged"), "<control><shift>J", flagset_flush, 0, "<ImageItem>", icon_blue_flag},
      /* f */ {N_("/_Articles/_Flag"), "J", articlelist_selected_flag_for_dl_nolock, 0, "<ImageItem>", icon_blue_flag},
      /* n */ {N_("/_Articles/U_nflag"), "<control>J", articlelist_selected_unflag_for_dl_nolock, 0, NULL},
      /*   */ {N_("/_Articles/---"), NULL, NULL, 0, "<Separator>"},
      /* l */ {N_("/_Articles/Down_load"), NULL, article_action_download_selected, 0, NULL},
      /*   */ {N_("/_Articles/---"), NULL, NULL, 0, "<Separator>"},
      /* w */ {N_("/_Articles/_Watch Thread"), "<control>W", menu_cb, ACTION_SCORE_WATCH_THREAD, "<ImageItem>", icon_watched},
      /* i */ {N_("/_Articles/_Ignore Thread"), "<control>I", menu_cb, ACTION_SCORE_IGNORE_THREAD, "<StockItem>", GTK_STOCK_CLEAR},
      /* p    note to translators: "plonk" is a decade-old Usenet word defined as
       *      the sound an articles' author makes when he falls to the bottom of the users' killfile.
       *      so "plonk author"means "don't show this author's articles anymore." */
              {N_("/_Articles/_Plonk Author"), NULL, menu_cb, ACTION_SCORE_PLONK, NULL},
      /* v */ {N_("/_Articles/_View Article's Scores"), "<control><shift>C", menu_cb, ACTION_SCORE_VIEW, "<ImageItem>", icon_score},
      /* c */ {N_("/_Articles/_Create Score..."), "S", menu_cb, ACTION_ADD_TO_SCOREFILE, "<ImageItem>", icon_score},
      /*   */ {N_("/_Articles/---"), NULL, NULL, 0, "<Separator>"},
      /*   */ {N_("/_Articles/Cancel..."), NULL, article_action_cancel_selected, 0, "<StockItem>", GTK_STOCK_CANCEL},
      /*   */ {N_("/_Articles/Supersed_e..."), NULL, article_action_supersede_selected, 0, "<StockItem>", GTK_STOCK_CONVERT},
      /*   */ {N_("/_Articles/---"), NULL, NULL, 0, "<Separator>"},
      /* d */ {N_("/_Articles/_Delete"), "Delete", menu_cb, ACTION_ARTICLE_DELETE, "<StockItem>", GTK_STOCK_DELETE},

      /*   */ {N_("/_Post"), NULL, NULL, 0, "<Branch>"},
      /* p */ {N_("/_Post/_Post to Newsgroup..."), "P", menu_cb, ACTION_COMPOSE_NEW, "<ImageItem>", icon_compose_post},
      /* f */ {N_("/_Post/_Followup to Newsgroup..."), "F", menu_cb, ACTION_COMPOSE_FOLLOWUP, "<ImageItem>", icon_compose_followup},
      /* r */ {N_("/_Post/_Reply by Email..."), "R", message_reply_window, 0, "<ImageItem>", icon_stock_mail_reply},
      /* w */ {N_("/_Post/For_ward by Email..."), NULL, message_forward_window, 0, "<ImageItem>", icon_stock_mail_forward},
      /*   */ {N_("/_Post/---"), NULL, NULL, 0, "<Separator>"},
      /* m */ {N_("/_Post/Send Pending _Messages"), NULL, flush_sendlater_messages, 0, "<ImageItem>", icon_compose_send},

      /*   */ {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
      /* p */ {N_("/_Tools/_Posting Profiles..."), NULL, identity_cb, 0, "<ImageItem>", icon_by_me},
      /* s */ {N_("/_Tools/News _Servers..."), NULL, server_custom_cb, 0, "<ImageItem>", icon_server},
      /*   */ {N_("/_Tools/---"), NULL, NULL, 0, "<Separator>"},
      /* f */ {N_("/_Tools/Custom _Filters..."), NULL, filter_custom_cb, 0, NULL},
      /* r */ {N_("/_Tools/_Rules..."), NULL, rules_cb, 0, NULL},
      /* e */ {N_("/_Tools/_Edit Scorefile..."), NULL, menu_cb, ACTION_EDIT_SCOREFILE, "<ImageItem>", icon_score},
      /*   */ {N_("/_Tools/---"), NULL, NULL, 0, "<Separator>"},
      /* t */ {N_("/_Tools/_Task Manager..."), NULL, task_manager_spawn, 0, NULL},
      /*   */ {N_("/_Tools/---"), NULL, NULL, 0, "<Separator>"},
      /* l */ {N_("/_Tools/_Log Viewer..."), NULL, log_button_cb, 0, "<ImageItem>", icon_inform},

      /*   */ {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
      /* h */ {N_("/_Help/Pan _Home Page..."), NULL, pan_homepage_url, 0, "<StockItem>", GTK_STOCK_HOME},
      /* b */ {N_("/_Help/Report a _Bug..."), NULL, pan_bug_url, 0, "<StockItem>", GTK_STOCK_DIALOG_ERROR},
      /*   */ {N_("/_Help/---"), NULL, NULL, 0, "<Separator>"},
      /* a */ {N_("/_Help/_About..."), NULL, dialog_about_cb, 0, "<ImageItem>", icon_stock_menu_about}
};

Generated by  Doxygen 1.6.0   Back to index