+static GHashTable *branch_node_table;
+
+static void grouplist_hash_init(void)
+{
+ branch_node_table = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+static void grouplist_hash_done(void)
+{
+ hash_free_strings(branch_node_table);
+ g_hash_table_destroy(branch_node_table);
+}
+
+static GtkCTreeNode *grouplist_hash_get_branch_node(const gchar *name)
+{
+ return g_hash_table_lookup(branch_node_table, name);
+}
+
+static void grouplist_hash_set_branch_node(const gchar *name,
+ GtkCTreeNode *node)
+{
+ g_hash_table_insert(branch_node_table, g_strdup(name), node);
+}
+
+static gchar *grouplist_get_parent_name(const gchar *name)
+{
+ gchar *p;
+
+ p = strrchr(name, '.');
+ if (!p)
+ return g_strdup("");
+ return g_strndup(name, p - name);
+}
+
+static GtkCTreeNode *grouplist_create_parent(const gchar *name,
+ const gchar *pattern)
+{
+ GtkCTreeNode *parent;
+ GtkCTreeNode *node;
+ gchar *cols[3];
+ gchar *parent_name;
+
+ if (*name == '\0') return NULL;
+ node = grouplist_hash_get_branch_node(name);
+ if (node != NULL) return node;
+
+ cols[0] = (gchar *)name;
+ cols[1] = cols[2] = "";
+
+ parent_name = grouplist_get_parent_name(name);
+ parent = grouplist_create_parent(parent_name, pattern);
+
+ node = parent ? GTK_CTREE_ROW(parent)->children
+ : GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
+ node = gtk_ctree_insert_node(GTK_CTREE(ctree), parent, node,
+ cols, 0, NULL, NULL, NULL, NULL,
+ FALSE, FALSE);
+ if (parent && fnmatch(pattern, parent_name, 0) != 0)
+ gtk_ctree_expand(GTK_CTREE(ctree), parent);
+ gtk_ctree_node_set_selectable(GTK_CTREE(ctree), node, FALSE);
+
+ grouplist_hash_set_branch_node(name, node);
+
+ g_free(parent_name);
+
+ return node;
+}
+
+static GtkCTreeNode *grouplist_create_branch(NewsGroupInfo *ginfo,
+ const gchar *pattern)
+{
+ GtkCTreeNode *node;
+ GtkCTreeNode *parent;
+ gchar *name = (gchar *)ginfo->name;
+ gchar *parent_name;
+ gchar *count_str;
+ gchar *cols[3];
+ gint count;
+
+ count = ginfo->last - ginfo->first;
+ if (count < 0)
+ count = 0;
+ count_str = itos(count);
+
+ cols[0] = ginfo->name;
+ cols[1] = count_str;
+ if (ginfo->type == 'y')
+ cols[2] = "";
+ else if (ginfo->type == 'm')
+ cols[2] = _("moderated");
+ else if (ginfo->type == 'n')
+ cols[2] = _("readonly");
+ else
+ cols[2] = _("unknown");
+
+ parent_name = grouplist_get_parent_name(name);
+ parent = grouplist_create_parent(parent_name, pattern);
+ node = grouplist_hash_get_branch_node(name);
+ if (node) {
+ gtk_ctree_set_node_info(GTK_CTREE(ctree), node, cols[0], 0,
+ NULL, NULL, NULL, NULL, FALSE, FALSE);
+ gtk_ctree_node_set_text(GTK_CTREE(ctree), node, 1, cols[1]);
+ gtk_ctree_node_set_text(GTK_CTREE(ctree), node, 2, cols[2]);
+ } else {
+ node = parent ? GTK_CTREE_ROW(parent)->children
+ : GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
+ node = gtk_ctree_insert_node(GTK_CTREE(ctree), parent, node,
+ cols, 0, NULL, NULL, NULL, NULL,
+ TRUE, FALSE);
+ if (parent && fnmatch(pattern, parent_name, 0) != 0)
+ gtk_ctree_expand(GTK_CTREE(ctree), parent);
+ }
+ gtk_ctree_node_set_selectable(GTK_CTREE(ctree), node, TRUE);
+ if (node)
+ gtk_ctree_node_set_row_data(GTK_CTREE(ctree), node, ginfo);
+
+ g_free(parent_name);
+
+ return node;
+}
+
+static void grouplist_expand_upwards(GtkCTree *ctree, const gchar *name) {
+ const gchar *ptr;
+ gchar *newname=g_malloc0(strlen(name));
+
+ for (ptr=name; *ptr; ptr++) {
+ if (*ptr == '.')
+ gtk_ctree_expand(ctree,
+ grouplist_hash_get_branch_node(newname));
+ newname[ptr-name] = *ptr;
+ }
+ g_free(newname);
+}
+
+static void grouplist_dialog_set_list(const gchar *pattern, gboolean refresh)