1124 lines
31 KiB
C
1124 lines
31 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*-
|
|
* moofiltermgr.c
|
|
*
|
|
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#include "mooutils/moofiltermgr.h"
|
|
#include "mooutils/mooprefs.h"
|
|
#include <string.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#define NUM_USER_FILTERS 5
|
|
#define ALL_FILES_GLOB "*"
|
|
|
|
typedef struct {
|
|
GtkFileFilter *filter;
|
|
GtkFileFilter *aux;
|
|
char *description;
|
|
char *glob;
|
|
gboolean user;
|
|
} Filter;
|
|
|
|
static Filter *filter_new (const char *description,
|
|
const char *glob,
|
|
gboolean user);
|
|
static void filter_free (Filter *filter);
|
|
|
|
static const char *filter_get_glob (Filter *filter);
|
|
static const char *filter_get_description (Filter *filter);
|
|
static GtkFileFilter *filter_get_gtk_filter (Filter *filter);
|
|
|
|
typedef struct {
|
|
GtkListStore *filters;
|
|
Filter *last_filter;
|
|
guint num_user;
|
|
guint num_builtin;
|
|
gboolean has_separator;
|
|
guint max_num_user;
|
|
} FilterStore;
|
|
|
|
static FilterStore *filter_store_new (MooFilterMgr *mgr);
|
|
static void filter_store_free (FilterStore *store);
|
|
|
|
static void filter_store_add_builtin (FilterStore *store,
|
|
Filter *filter,
|
|
int position);
|
|
static void filter_store_add_user (FilterStore *store,
|
|
Filter *filter);
|
|
static Filter *filter_store_get_last (FilterStore *store);
|
|
static GSList *filter_store_list_user_filters (FilterStore *store);
|
|
|
|
|
|
struct _MooFilterMgrPrivate {
|
|
FilterStore *default_store;
|
|
GHashTable *named_stores; /* user_id -> FilterStore* */
|
|
GHashTable *filters; /* glob -> Filter* */
|
|
gboolean loaded;
|
|
guint save_idle_id;
|
|
};
|
|
|
|
enum {
|
|
COLUMN_DESCRIPTION,
|
|
COLUMN_FILTER,
|
|
NUM_COLUMNS
|
|
};
|
|
|
|
|
|
static void moo_filter_mgr_class_init (MooFilterMgrClass *klass);
|
|
static void moo_filter_mgr_init (MooFilterMgr *mgr);
|
|
static void moo_filter_mgr_finalize (GObject *object);
|
|
|
|
|
|
static gboolean combo_row_separator_func (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
gpointer data);
|
|
static FilterStore *mgr_get_store (MooFilterMgr *mgr,
|
|
const char *user_id,
|
|
gboolean create);
|
|
static Filter *mgr_get_null_filter (MooFilterMgr *mgr);
|
|
static Filter *mgr_new_filter (MooFilterMgr *mgr,
|
|
const char *description,
|
|
const char *glob,
|
|
gboolean user);
|
|
static Filter *mgr_get_last_filter (MooFilterMgr *mgr,
|
|
const char *user_id);
|
|
static void mgr_set_last_filter (MooFilterMgr *mgr,
|
|
const char *user_id,
|
|
Filter *filter);
|
|
static Filter *mgr_new_user_filter (MooFilterMgr *mgr,
|
|
const char *glob,
|
|
const char *user_id);
|
|
static GSList *mgr_list_user_ids (MooFilterMgr *mgr);
|
|
|
|
static void mgr_load (MooFilterMgr *mgr);
|
|
static void mgr_save (MooFilterMgr *mgr);
|
|
|
|
|
|
/* MOO_TYPE_FILTER_MGR */
|
|
G_DEFINE_TYPE (MooFilterMgr, moo_filter_mgr, G_TYPE_OBJECT)
|
|
|
|
|
|
static void
|
|
moo_filter_mgr_class_init (MooFilterMgrClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
gobject_class->finalize = moo_filter_mgr_finalize;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_filter_mgr_init (MooFilterMgr *mgr)
|
|
{
|
|
mgr->priv = g_new0 (MooFilterMgrPrivate, 1);
|
|
|
|
mgr->priv->filters =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, (GDestroyNotify) filter_free);
|
|
g_hash_table_insert (mgr->priv->filters,
|
|
g_strdup (ALL_FILES_GLOB),
|
|
filter_new ("All Files", ALL_FILES_GLOB, FALSE));
|
|
|
|
mgr->priv->default_store = NULL;
|
|
mgr->priv->named_stores =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, (GDestroyNotify) filter_store_free);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_filter_mgr_finalize (GObject *object)
|
|
{
|
|
MooFilterMgr *mgr = MOO_FILTER_MGR (object);
|
|
|
|
if (mgr->priv->save_idle_id)
|
|
g_source_remove (mgr->priv->save_idle_id);
|
|
|
|
filter_store_free (mgr->priv->default_store);
|
|
g_hash_table_destroy (mgr->priv->named_stores);
|
|
g_hash_table_destroy (mgr->priv->filters);
|
|
|
|
g_free (mgr->priv);
|
|
|
|
G_OBJECT_CLASS (moo_filter_mgr_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
MooFilterMgr*
|
|
moo_filter_mgr_new (void)
|
|
{
|
|
return g_object_new (MOO_TYPE_FILTER_MGR, NULL);
|
|
}
|
|
|
|
|
|
void
|
|
moo_filter_mgr_init_filter_combo (MooFilterMgr *mgr,
|
|
MooCombo *combo,
|
|
const char *user_id)
|
|
{
|
|
FilterStore *store;
|
|
|
|
g_return_if_fail (MOO_IS_FILTER_MGR (mgr));
|
|
g_return_if_fail (MOO_IS_COMBO (combo));
|
|
|
|
mgr_load (mgr);
|
|
|
|
store = mgr_get_store (mgr, user_id, TRUE);
|
|
g_return_if_fail (store != NULL);
|
|
|
|
moo_combo_set_model (combo, GTK_TREE_MODEL (store->filters));
|
|
moo_combo_set_row_separator_func (combo, combo_row_separator_func, NULL);
|
|
|
|
moo_combo_set_text_column (combo, COLUMN_DESCRIPTION);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
combo_row_separator_func (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
G_GNUC_UNUSED gpointer data)
|
|
{
|
|
Filter *filter = NULL;
|
|
gtk_tree_model_get (model, iter, COLUMN_FILTER, &filter, -1);
|
|
return !filter;
|
|
}
|
|
|
|
|
|
static FilterStore*
|
|
mgr_get_store (MooFilterMgr *mgr,
|
|
const char *user_id,
|
|
gboolean create)
|
|
{
|
|
FilterStore *store;
|
|
|
|
if (!user_id)
|
|
{
|
|
if (!mgr->priv->default_store && create)
|
|
mgr->priv->default_store = filter_store_new (mgr);
|
|
return mgr->priv->default_store;
|
|
}
|
|
|
|
store = g_hash_table_lookup (mgr->priv->named_stores, user_id);
|
|
|
|
if (!store && create)
|
|
{
|
|
g_return_val_if_fail (g_utf8_validate (user_id, -1, NULL), NULL);
|
|
store = filter_store_new (mgr);
|
|
g_hash_table_insert (mgr->priv->named_stores,
|
|
g_strdup (user_id),
|
|
store);
|
|
}
|
|
|
|
return store;
|
|
}
|
|
|
|
|
|
static void
|
|
prepend_id (const char *user_id,
|
|
G_GNUC_UNUSED gpointer store,
|
|
GSList **list)
|
|
{
|
|
*list = g_slist_prepend (*list, g_strdup (user_id));
|
|
}
|
|
|
|
static GSList*
|
|
mgr_list_user_ids (MooFilterMgr *mgr)
|
|
{
|
|
GSList *list = NULL;
|
|
g_hash_table_foreach (mgr->priv->named_stores, (GHFunc) prepend_id, &list);
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
|
|
GtkFileFilter*
|
|
moo_filter_mgr_get_filter (MooFilterMgr *mgr,
|
|
GtkTreeIter *iter,
|
|
const char *user_id)
|
|
{
|
|
FilterStore *store;
|
|
Filter *filter = NULL;
|
|
|
|
g_return_val_if_fail (MOO_IS_FILTER_MGR (mgr), NULL);
|
|
g_return_val_if_fail (iter != NULL, NULL);
|
|
|
|
mgr_load (mgr);
|
|
|
|
store = mgr_get_store (mgr, user_id, FALSE);
|
|
g_return_val_if_fail (store != NULL, NULL);
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (store->filters), iter,
|
|
COLUMN_FILTER, &filter, -1);
|
|
|
|
return filter_get_gtk_filter (filter);
|
|
}
|
|
|
|
|
|
void
|
|
moo_filter_mgr_set_last_filter (MooFilterMgr *mgr,
|
|
GtkTreeIter *iter,
|
|
const char *user_id)
|
|
{
|
|
FilterStore *store;
|
|
Filter *filter = NULL;
|
|
|
|
g_return_if_fail (MOO_IS_FILTER_MGR (mgr));
|
|
g_return_if_fail (iter != NULL);
|
|
|
|
mgr_load (mgr);
|
|
|
|
store = mgr_get_store (mgr, user_id, FALSE);
|
|
g_return_if_fail (store != NULL);
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (store->filters), iter,
|
|
COLUMN_FILTER, &filter, -1);
|
|
g_return_if_fail (filter != NULL);
|
|
|
|
mgr_set_last_filter (mgr, user_id, filter);
|
|
}
|
|
|
|
|
|
GtkFileFilter*
|
|
moo_filter_mgr_get_null_filter (MooFilterMgr *mgr)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FILTER_MGR (mgr), NULL);
|
|
return mgr_get_null_filter(mgr)->filter;
|
|
}
|
|
|
|
|
|
static Filter*
|
|
mgr_get_null_filter (MooFilterMgr *mgr)
|
|
{
|
|
return g_hash_table_lookup (mgr->priv->filters, ALL_FILES_GLOB);
|
|
}
|
|
|
|
|
|
GtkFileFilter*
|
|
moo_filter_mgr_get_last_filter (MooFilterMgr *mgr,
|
|
const char *user_id)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FILTER_MGR (mgr), NULL);
|
|
|
|
mgr_load (mgr);
|
|
|
|
return filter_get_gtk_filter (mgr_get_last_filter (mgr, user_id));
|
|
}
|
|
|
|
|
|
static Filter*
|
|
mgr_get_last_filter (MooFilterMgr *mgr,
|
|
const char *user_id)
|
|
{
|
|
FilterStore *store;
|
|
|
|
g_return_val_if_fail (MOO_IS_FILTER_MGR (mgr), NULL);
|
|
|
|
store = mgr_get_store (mgr, user_id, FALSE);
|
|
|
|
if (store)
|
|
return filter_store_get_last (store);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
mgr_set_last_filter (MooFilterMgr *mgr,
|
|
const char *user_id,
|
|
Filter *filter)
|
|
{
|
|
FilterStore *store;
|
|
|
|
g_return_if_fail (MOO_IS_FILTER_MGR (mgr));
|
|
|
|
store = mgr_get_store (mgr, user_id, TRUE);
|
|
store->last_filter = filter;
|
|
|
|
mgr_save (mgr);
|
|
}
|
|
|
|
|
|
GtkFileFilter*
|
|
moo_filter_mgr_new_user_filter (MooFilterMgr *mgr,
|
|
const char *glob,
|
|
const char *user_id)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FILTER_MGR (mgr), NULL);
|
|
g_return_val_if_fail (glob != NULL, NULL);
|
|
|
|
mgr_load (mgr);
|
|
|
|
return filter_get_gtk_filter (mgr_new_user_filter (mgr, glob, user_id));
|
|
}
|
|
|
|
|
|
static Filter*
|
|
mgr_new_user_filter (MooFilterMgr *mgr,
|
|
const char *glob,
|
|
const char *user_id)
|
|
{
|
|
FilterStore *store;
|
|
Filter *filter;
|
|
|
|
filter = mgr_new_filter (mgr, glob, glob, TRUE);
|
|
g_return_val_if_fail (filter != NULL, NULL);
|
|
|
|
store = mgr_get_store (mgr, user_id, TRUE);
|
|
filter_store_add_user (store, filter);
|
|
|
|
mgr_save (mgr);
|
|
|
|
return filter;
|
|
}
|
|
|
|
|
|
GtkFileFilter*
|
|
moo_filter_mgr_new_builtin_filter (MooFilterMgr *mgr,
|
|
const char *description,
|
|
const char *glob,
|
|
const char *user_id,
|
|
int position)
|
|
{
|
|
FilterStore *store;
|
|
Filter *filter;
|
|
|
|
g_return_val_if_fail (MOO_IS_FILTER_MGR (mgr), NULL);
|
|
g_return_val_if_fail (glob != NULL, NULL);
|
|
g_return_val_if_fail (description != NULL, NULL);
|
|
|
|
mgr_load (mgr);
|
|
|
|
filter = mgr_new_filter (mgr, description, glob, FALSE);
|
|
g_return_val_if_fail (filter != NULL, NULL);
|
|
|
|
store = mgr_get_store (mgr, user_id, TRUE);
|
|
filter_store_add_builtin (store, filter, position);
|
|
|
|
return filter->filter;
|
|
}
|
|
|
|
|
|
static Filter*
|
|
mgr_new_filter (MooFilterMgr *mgr,
|
|
const char *description,
|
|
const char *glob,
|
|
gboolean user)
|
|
{
|
|
Filter *filter;
|
|
|
|
g_return_val_if_fail (description != NULL, NULL);
|
|
g_return_val_if_fail (glob != NULL, NULL);
|
|
|
|
filter = g_hash_table_lookup (mgr->priv->filters, glob);
|
|
|
|
if (!filter)
|
|
{
|
|
filter = filter_new (description, glob, user);
|
|
g_return_val_if_fail (filter != NULL, NULL);
|
|
g_hash_table_insert (mgr->priv->filters,
|
|
g_strdup (glob), filter);
|
|
}
|
|
|
|
return filter;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Filechooser
|
|
*/
|
|
|
|
static void
|
|
dialog_set_filter (MooFilterMgr *mgr,
|
|
GtkFileChooser *dialog,
|
|
Filter *filter)
|
|
{
|
|
GtkEntry *entry = g_object_get_data (G_OBJECT (dialog),
|
|
"moo-filter-entry");
|
|
gtk_entry_set_text (entry, filter_get_description (filter));
|
|
gtk_file_chooser_set_filter (dialog, filter_get_gtk_filter (filter));
|
|
mgr_set_last_filter (mgr,
|
|
g_object_get_data (G_OBJECT (dialog), "moo-filter-user-id"),
|
|
filter);
|
|
}
|
|
|
|
|
|
static void
|
|
filter_entry_activated (GtkEntry *entry,
|
|
GtkFileChooser *dialog)
|
|
{
|
|
const char *text;
|
|
Filter *filter = NULL;
|
|
MooFilterMgr *mgr;
|
|
|
|
mgr = g_object_get_data (G_OBJECT (dialog), "moo-filter-mgr");
|
|
g_return_if_fail (MOO_IS_FILTER_MGR (mgr));
|
|
|
|
text = gtk_entry_get_text (entry);
|
|
|
|
if (text && text[0])
|
|
filter = mgr_new_user_filter (mgr, text,
|
|
g_object_get_data (G_OBJECT (dialog),
|
|
"moo-filter-user-id"));
|
|
|
|
if (!filter)
|
|
filter = mgr_get_null_filter (mgr);
|
|
|
|
dialog_set_filter (mgr, dialog, filter);
|
|
}
|
|
|
|
|
|
static void
|
|
combo_changed (MooCombo *combo,
|
|
GtkFileChooser *dialog)
|
|
{
|
|
GtkTreeIter iter;
|
|
Filter *filter;
|
|
MooFilterMgr *mgr;
|
|
FilterStore *store;
|
|
|
|
if (!moo_combo_get_active_iter (combo, &iter))
|
|
return;
|
|
|
|
mgr = g_object_get_data (G_OBJECT (dialog), "moo-filter-mgr");
|
|
g_return_if_fail (MOO_IS_FILTER_MGR (mgr));
|
|
|
|
store = mgr_get_store (mgr,
|
|
g_object_get_data (G_OBJECT (dialog),
|
|
"moo-filter-user-id"),
|
|
FALSE);
|
|
g_return_if_fail (store != NULL);
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (store->filters), &iter,
|
|
COLUMN_FILTER, &filter, -1);
|
|
g_return_if_fail (filter != NULL);
|
|
|
|
dialog_set_filter (mgr, dialog, filter);
|
|
}
|
|
|
|
|
|
void
|
|
moo_filter_mgr_attach (MooFilterMgr *mgr,
|
|
GtkFileChooser *dialog,
|
|
const char *user_id)
|
|
{
|
|
GtkWidget *alignment;
|
|
GtkWidget *hbox;
|
|
GtkWidget *label;
|
|
GtkWidget *combo;
|
|
GtkWidget *entry;
|
|
Filter *filter;
|
|
|
|
g_return_if_fail (MOO_IS_FILTER_MGR (mgr));
|
|
g_return_if_fail (GTK_IS_FILE_CHOOSER (dialog));
|
|
|
|
mgr_load (mgr);
|
|
|
|
alignment = gtk_alignment_new (1, 0.5, 0, 1);
|
|
gtk_widget_show (alignment);
|
|
gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), alignment);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_widget_show (hbox);
|
|
gtk_container_add (GTK_CONTAINER (alignment), hbox);
|
|
|
|
label = gtk_label_new_with_mnemonic ("_Filter:");
|
|
gtk_widget_show (label);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
|
|
combo = moo_combo_new ();
|
|
moo_filter_mgr_init_filter_combo (mgr, MOO_COMBO (combo), user_id);
|
|
gtk_widget_show (combo);
|
|
gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
|
|
|
|
entry = MOO_COMBO(combo)->entry;
|
|
gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
|
|
|
|
g_signal_connect (entry, "activate",
|
|
G_CALLBACK (filter_entry_activated),
|
|
dialog);
|
|
g_signal_connect (combo, "changed",
|
|
G_CALLBACK (combo_changed),
|
|
dialog);
|
|
|
|
g_object_set_data (G_OBJECT (dialog), "moo-filter-combo", combo);
|
|
g_object_set_data (G_OBJECT (dialog), "moo-filter-entry", entry);
|
|
g_object_set_data_full (G_OBJECT (dialog), "moo-filter-mgr",
|
|
g_object_ref (mgr), g_object_unref);
|
|
g_object_set_data_full (G_OBJECT (dialog), "moo-filter-user-id",
|
|
g_strdup (user_id), g_free);
|
|
|
|
filter = mgr_get_last_filter (mgr, user_id);
|
|
|
|
if (filter)
|
|
dialog_set_filter (mgr, GTK_FILE_CHOOSER (dialog), filter);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* FilterStore
|
|
*/
|
|
|
|
static FilterStore*
|
|
filter_store_new (MooFilterMgr *mgr)
|
|
{
|
|
FilterStore *store = g_new0 (FilterStore, 1);
|
|
|
|
store->filters = gtk_list_store_new (NUM_COLUMNS,
|
|
G_TYPE_STRING,
|
|
G_TYPE_POINTER);
|
|
store->last_filter = NULL;
|
|
store->num_user = 0;
|
|
store->num_builtin = 0;
|
|
store->has_separator = FALSE;
|
|
store->max_num_user = NUM_USER_FILTERS;
|
|
|
|
filter_store_add_builtin (store, mgr_get_null_filter (mgr), -1);
|
|
|
|
return store;
|
|
}
|
|
|
|
|
|
static void
|
|
filter_store_free (FilterStore *store)
|
|
{
|
|
if (store)
|
|
{
|
|
g_object_unref (store->filters);
|
|
g_free (store);
|
|
}
|
|
}
|
|
|
|
|
|
static GSList*
|
|
filter_store_list_user_filters (FilterStore *store)
|
|
{
|
|
GSList *list = NULL;
|
|
GtkTreeIter iter;
|
|
guint offset;
|
|
|
|
g_return_val_if_fail (store != NULL, NULL);
|
|
|
|
if (!store->num_user)
|
|
return NULL;
|
|
|
|
offset = store->num_builtin + (store->has_separator ? 1 : 0);
|
|
|
|
if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store->filters), &iter, NULL, offset))
|
|
g_return_val_if_reached (NULL);
|
|
|
|
do
|
|
{
|
|
Filter *filter = NULL;
|
|
gtk_tree_model_get (GTK_TREE_MODEL (store->filters), &iter,
|
|
COLUMN_FILTER, &filter, -1);
|
|
list = g_slist_prepend (list, filter);
|
|
}
|
|
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store->filters), &iter));
|
|
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
filter_store_find_builtin (FilterStore *store,
|
|
Filter *filter,
|
|
GtkTreeIter *iter)
|
|
{
|
|
guint i;
|
|
Filter *filt = NULL;
|
|
|
|
for (i = 0; i < store->num_builtin; ++i)
|
|
{
|
|
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store->filters),
|
|
iter, NULL, i);
|
|
gtk_tree_model_get (GTK_TREE_MODEL (store->filters), iter,
|
|
COLUMN_FILTER, &filt, -1);
|
|
if (filter == filt)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
filter_store_add_builtin (FilterStore *store,
|
|
Filter *filter,
|
|
int position)
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
g_return_if_fail (store != NULL);
|
|
g_return_if_fail (filter != NULL);
|
|
|
|
store->last_filter = filter;
|
|
|
|
if (filter_store_find_builtin (store, filter, &iter))
|
|
return;
|
|
|
|
if (position < 0 || position > (int) store->num_builtin)
|
|
position = store->num_builtin;
|
|
|
|
gtk_list_store_insert (store->filters, &iter, position);
|
|
gtk_list_store_set (store->filters, &iter,
|
|
COLUMN_DESCRIPTION, filter_get_description (filter),
|
|
COLUMN_FILTER, filter, -1);
|
|
store->num_builtin++;
|
|
|
|
if (store->num_user && !store->has_separator)
|
|
{
|
|
gtk_list_store_insert (store->filters, &iter, store->num_builtin);
|
|
store->has_separator = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
filter_store_find_user (FilterStore *store,
|
|
Filter *filter,
|
|
GtkTreeIter *iter)
|
|
{
|
|
guint i, offset;
|
|
Filter *filt = NULL;
|
|
|
|
offset = store->num_builtin + (store->has_separator ? 1 : 0);
|
|
|
|
for (i = offset; i < offset + store->num_user; ++i)
|
|
{
|
|
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store->filters),
|
|
iter, NULL, i);
|
|
gtk_tree_model_get (GTK_TREE_MODEL (store->filters), iter,
|
|
COLUMN_FILTER, &filt, -1);
|
|
if (filter == filt)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
filter_store_add_user (FilterStore *store,
|
|
Filter *filter)
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
g_return_if_fail (store != NULL);
|
|
g_return_if_fail (filter != NULL);
|
|
|
|
store->last_filter = filter;
|
|
|
|
if (filter_store_find_builtin (store, filter, &iter))
|
|
return;
|
|
|
|
if (filter_store_find_user (store, filter, &iter))
|
|
{
|
|
gtk_list_store_remove (store->filters, &iter);
|
|
store->num_user--;
|
|
}
|
|
|
|
gtk_list_store_insert (store->filters, &iter,
|
|
store->num_builtin + (store->has_separator ? 1 : 0));
|
|
gtk_list_store_set (store->filters, &iter,
|
|
COLUMN_DESCRIPTION, filter_get_description (filter),
|
|
COLUMN_FILTER, filter, -1);
|
|
store->num_user++;
|
|
|
|
if (store->num_builtin && !store->has_separator)
|
|
{
|
|
gtk_list_store_insert (store->filters, &iter, store->num_builtin);
|
|
store->has_separator = TRUE;
|
|
}
|
|
|
|
if (store->num_user > store->max_num_user)
|
|
{
|
|
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store->filters),
|
|
&iter, NULL,
|
|
store->num_builtin +
|
|
(store->has_separator ? 1 : 0) +
|
|
store->num_user - 1);
|
|
gtk_list_store_remove (store->filters, &iter);
|
|
store->num_user--;
|
|
}
|
|
}
|
|
|
|
|
|
static Filter *filter_store_get_last (FilterStore *store)
|
|
{
|
|
if (!store->last_filter)
|
|
{
|
|
GtkTreeIter iter;
|
|
Filter *filter = NULL;
|
|
|
|
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store->filters), &iter);
|
|
gtk_tree_model_get (GTK_TREE_MODEL (store->filters), &iter,
|
|
COLUMN_FILTER, &filter, -1);
|
|
store->last_filter = filter;
|
|
}
|
|
|
|
return store->last_filter;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Filter
|
|
*/
|
|
|
|
#define NEGATE_CHAR '!'
|
|
#define GLOB_SEPARATOR ";"
|
|
|
|
static gboolean
|
|
neg_filter_func (const GtkFileFilterInfo *filter_info,
|
|
Filter *filter)
|
|
{
|
|
return !gtk_file_filter_filter (filter->aux, filter_info);
|
|
}
|
|
|
|
|
|
static Filter*
|
|
filter_new (const char *description,
|
|
const char *glob,
|
|
gboolean user)
|
|
{
|
|
Filter *filter;
|
|
char **globs, **p;
|
|
gboolean negative;
|
|
|
|
g_return_val_if_fail (description != NULL, NULL);
|
|
g_return_val_if_fail (g_utf8_validate (description, -1, NULL), NULL);
|
|
g_return_val_if_fail (g_utf8_validate (glob, -1, NULL), NULL);
|
|
g_return_val_if_fail (glob != NULL && glob[0] != 0, NULL);
|
|
g_return_val_if_fail (glob[0] != NEGATE_CHAR || glob[1] != 0, NULL);
|
|
|
|
if (glob[0] == NEGATE_CHAR)
|
|
{
|
|
negative = TRUE;
|
|
globs = g_strsplit (glob + 1, GLOB_SEPARATOR, 0);
|
|
}
|
|
else
|
|
{
|
|
negative = FALSE;
|
|
globs = g_strsplit (glob, GLOB_SEPARATOR, 0);
|
|
}
|
|
|
|
g_return_val_if_fail (globs != NULL, NULL);
|
|
|
|
filter = g_new0 (Filter, 1);
|
|
|
|
filter->description = g_strdup (description);
|
|
filter->glob = g_strdup (glob);
|
|
filter->user = user;
|
|
|
|
filter->filter = gtk_file_filter_new ();
|
|
gtk_object_sink (GTK_OBJECT (g_object_ref (filter->filter)));
|
|
gtk_file_filter_set_name (filter->filter, description);
|
|
|
|
if (negative)
|
|
{
|
|
filter->aux = gtk_file_filter_new ();
|
|
gtk_object_sink (GTK_OBJECT (g_object_ref (filter->aux)));
|
|
|
|
for (p = globs; *p != NULL; p++)
|
|
gtk_file_filter_add_pattern (filter->aux, *p);
|
|
|
|
gtk_file_filter_add_custom (filter->filter,
|
|
gtk_file_filter_get_needed (filter->aux),
|
|
(GtkFileFilterFunc) neg_filter_func,
|
|
filter, NULL);
|
|
}
|
|
else
|
|
{
|
|
for (p = globs; *p != NULL; p++)
|
|
gtk_file_filter_add_pattern (filter->filter, *p);
|
|
}
|
|
|
|
g_strfreev (globs);
|
|
return filter;
|
|
}
|
|
|
|
|
|
static void
|
|
filter_free (Filter *filter)
|
|
{
|
|
if (filter)
|
|
{
|
|
if (filter->filter)
|
|
g_object_unref (filter->filter);
|
|
filter->filter = NULL;
|
|
if (filter->aux)
|
|
g_object_unref (filter->aux);
|
|
filter->aux = NULL;
|
|
g_free (filter->description);
|
|
filter->description = NULL;
|
|
g_free (filter->glob);
|
|
filter->glob = NULL;
|
|
g_free (filter);
|
|
}
|
|
}
|
|
|
|
|
|
static GtkFileFilter *filter_get_gtk_filter (Filter *filter)
|
|
{
|
|
g_return_val_if_fail (filter != NULL, NULL);
|
|
return filter->filter;
|
|
}
|
|
|
|
|
|
static const char*
|
|
filter_get_glob (Filter *filter)
|
|
{
|
|
g_return_val_if_fail (filter != NULL, NULL);
|
|
return filter->glob;
|
|
}
|
|
|
|
|
|
static const char*
|
|
filter_get_description (Filter *filter)
|
|
{
|
|
g_return_val_if_fail (filter != NULL, NULL);
|
|
return filter->description;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Loading and saving
|
|
*/
|
|
|
|
#define FILTERS_ROOT "Editor/filters"
|
|
#define ELEMENT_DEFAULT "default"
|
|
#define ELEMENT_SET "set"
|
|
#define ELEMENT_FILTER "filter"
|
|
#define ELEMENT_LAST "last"
|
|
#define PROP_USER_ID "user-id"
|
|
#define PROP_USER "user"
|
|
#define PROP_DESCRIPTION "description"
|
|
|
|
|
|
static void
|
|
mgr_load_set (MooFilterMgr *mgr,
|
|
const char *user_id,
|
|
MooMarkupNode *root)
|
|
{
|
|
MooMarkupNode *elm;
|
|
|
|
for (elm = root->last; elm != NULL; elm = elm->prev)
|
|
{
|
|
if (!MOO_MARKUP_IS_ELEMENT (elm))
|
|
continue;
|
|
|
|
if (!strcmp (elm->name, ELEMENT_FILTER))
|
|
{
|
|
const char *glob = moo_markup_get_content (elm);
|
|
|
|
if (!glob || !glob[0])
|
|
{
|
|
g_warning ("%s: empty glob in filter entry", G_STRLOC);
|
|
continue;
|
|
}
|
|
|
|
moo_filter_mgr_new_user_filter (mgr, glob, user_id);
|
|
}
|
|
else if (!strcmp (elm->name, ELEMENT_LAST))
|
|
{
|
|
const char *glob = moo_markup_get_content (elm);
|
|
const char *user_str = moo_markup_get_prop (elm, PROP_USER);
|
|
const char *description = moo_markup_get_prop (elm, PROP_DESCRIPTION);
|
|
gboolean user;
|
|
|
|
if (!glob || !glob[0])
|
|
{
|
|
g_warning ("%s: empty glob in filter entry", G_STRLOC);
|
|
continue;
|
|
}
|
|
|
|
if (user_str)
|
|
user = strcmp (user_str, "TRUE") ? FALSE : TRUE;
|
|
else
|
|
user = FALSE;
|
|
|
|
if (!user && !description)
|
|
{
|
|
g_warning ("%s: no description in filter entry", G_STRLOC);
|
|
continue;
|
|
}
|
|
|
|
/* XXX */
|
|
if (user)
|
|
{
|
|
moo_filter_mgr_new_user_filter (mgr, glob, user_id);
|
|
}
|
|
else
|
|
{
|
|
Filter *filter;
|
|
moo_filter_mgr_new_builtin_filter (mgr, description, glob, user_id, -1);
|
|
filter = mgr_new_filter (mgr, description, glob, FALSE);
|
|
g_return_if_fail (filter != NULL);
|
|
mgr_set_last_filter (mgr, user_id, filter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_warning ("%s: invalid '%s' element", G_STRLOC, elm->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
mgr_load (MooFilterMgr *mgr)
|
|
{
|
|
MooMarkupDoc *xml;
|
|
MooMarkupNode *root, *elm;
|
|
|
|
if (mgr->priv->loaded)
|
|
return;
|
|
else
|
|
mgr->priv->loaded = TRUE;
|
|
|
|
xml = moo_prefs_get_markup ();
|
|
g_return_if_fail (xml != NULL);
|
|
|
|
root = moo_markup_get_element (MOO_MARKUP_NODE (xml), FILTERS_ROOT);
|
|
|
|
if (!root)
|
|
return;
|
|
|
|
for (elm = root->children; elm != NULL; elm = elm->next)
|
|
{
|
|
if (!MOO_MARKUP_IS_ELEMENT (elm))
|
|
continue;
|
|
|
|
if (!strcmp (elm->name, ELEMENT_DEFAULT))
|
|
{
|
|
mgr_load_set (mgr, NULL, elm);
|
|
}
|
|
else if (!strcmp (elm->name, ELEMENT_SET))
|
|
{
|
|
const char *user_id = moo_markup_get_prop (elm, PROP_USER_ID);
|
|
|
|
if (!user_id || !user_id[0])
|
|
{
|
|
g_warning ("%s: empty user id", G_STRLOC);
|
|
continue;
|
|
}
|
|
|
|
mgr_load_set (mgr, user_id, elm);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("%s: invalid '%s' element", G_STRLOC, elm->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* XXX */
|
|
static void
|
|
mgr_save_set (MooFilterMgr *mgr,
|
|
const char *user_id,
|
|
MooMarkupNode *root)
|
|
{
|
|
MooMarkupNode *elm;
|
|
GSList *list, *l;
|
|
FilterStore *store;
|
|
Filter *filter;
|
|
|
|
store = mgr_get_store (mgr, user_id, FALSE);
|
|
g_return_if_fail (store != NULL);
|
|
|
|
if (!store->num_user && !store->last_filter)
|
|
return;
|
|
|
|
if (store->last_filter)
|
|
{
|
|
filter = store->last_filter;
|
|
elm = moo_markup_create_text_element (root, ELEMENT_LAST,
|
|
filter_get_glob (filter));
|
|
moo_markup_set_prop (elm, PROP_USER, filter->user ? "TRUE" : "FALSE");
|
|
if (!filter->user)
|
|
moo_markup_set_prop (elm, PROP_DESCRIPTION, filter->description);
|
|
}
|
|
|
|
list = filter_store_list_user_filters (store);
|
|
|
|
if (!list)
|
|
return;
|
|
|
|
for (l = list; l != NULL; l = l->next)
|
|
moo_markup_create_text_element (root, ELEMENT_FILTER,
|
|
filter_get_glob (l->data));
|
|
|
|
g_slist_free (list);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
mgr_do_save (MooFilterMgr *mgr)
|
|
{
|
|
MooMarkupDoc *xml;
|
|
MooMarkupNode *root, *elm;
|
|
GSList *user_ids, *l;
|
|
|
|
mgr->priv->save_idle_id = 0;
|
|
|
|
xml = moo_prefs_get_markup ();
|
|
g_return_val_if_fail (xml != NULL, FALSE);
|
|
|
|
root = moo_markup_get_element (MOO_MARKUP_NODE (xml), FILTERS_ROOT);
|
|
|
|
if (root)
|
|
moo_markup_delete_node (root);
|
|
|
|
root = NULL;
|
|
|
|
user_ids = mgr_list_user_ids (mgr);
|
|
user_ids = g_slist_prepend (user_ids, NULL);
|
|
|
|
for (l = user_ids; l != NULL; l = l->next)
|
|
{
|
|
FilterStore *store = mgr_get_store (mgr, l->data, FALSE);
|
|
g_return_val_if_fail (!l->data || store != NULL, FALSE);
|
|
|
|
if (!store)
|
|
continue;
|
|
|
|
if (store->num_user || store->last_filter != mgr_get_null_filter (mgr))
|
|
{
|
|
if (!root)
|
|
{
|
|
root = moo_markup_create_element (MOO_MARKUP_NODE (xml), FILTERS_ROOT);
|
|
g_return_val_if_fail (root != NULL, FALSE);
|
|
}
|
|
|
|
if (l->data)
|
|
{
|
|
elm = moo_markup_create_element (root, ELEMENT_SET);
|
|
moo_markup_set_prop (elm, PROP_USER_ID, l->data);
|
|
}
|
|
else
|
|
{
|
|
elm = moo_markup_create_element (root, ELEMENT_DEFAULT);
|
|
}
|
|
|
|
mgr_save_set (mgr, l->data, elm);
|
|
}
|
|
}
|
|
|
|
g_slist_foreach (user_ids, (GFunc) g_free, NULL);
|
|
g_slist_free (user_ids);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void mgr_save (MooFilterMgr *mgr)
|
|
{
|
|
if (!mgr->priv->save_idle_id)
|
|
mgr->priv->save_idle_id = g_idle_add ((GSourceFunc) mgr_do_save, mgr);
|
|
}
|