summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--db.c73
-rw-r--r--db.h3
-rw-r--r--tag_model.c489
-rw-r--r--tag_model.h58
-rw-r--r--window_main.c59
-rw-r--r--window_tag.c72
6 files changed, 672 insertions, 82 deletions
diff --git a/db.c b/db.c
index e6f0368..935e9c1 100644
--- a/db.c
+++ b/db.c
@@ -354,7 +354,7 @@ int db_get_wall_tags(sqlite_uint64 wallid, GArray **array) {
sqlite3_stmt *stmt;
int rc;
- rc = sqlite3_prepare_v2(db, "SELECT t.id, t.name FROM tag t JOIN walltags w ON (w.tagid = t.id AND w.wallid = ?)", -1, &stmt, NULL);
+ rc = sqlite3_prepare_v2(db, "SELECT t.id, t.name, t.parent FROM tag t JOIN walltags w ON (w.tagid = t.id AND w.wallid = ?)", -1, &stmt, NULL);
if(rc != SQLITE_OK) {
return 0;
@@ -369,6 +369,7 @@ int db_get_wall_tags(sqlite_uint64 wallid, GArray **array) {
*array = g_array_new(FALSE, FALSE, sizeof(struct tag_t));
while((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
temp.id = sqlite3_column_int64(stmt, 0);
+ temp.parent = sqlite3_column_int64(stmt, 2);
temp.name = g_strdup((const gchar*)sqlite3_column_text(stmt, 1));
g_array_append_val(*array, temp);
}
@@ -630,6 +631,76 @@ int db_get_tags_all(GArray **array) {
return 1;
}
+int db_get_top_level_tags(GArray **array) {
+ struct tag_t temp, *temp2;
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, "SELECT id, name, parent FROM tag WHERE parent IS NULL ORDER BY name", -1, &stmt, NULL);
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ *array = g_array_new(FALSE, FALSE, sizeof(struct tag_t));
+ while((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ temp.name = g_strdup((const gchar*)sqlite3_column_text(stmt, 1));
+ temp.id = sqlite3_column_int64(stmt, 0);
+ temp.parent = sqlite3_column_int64(stmt, 2);
+ g_array_append_val(*array, temp);
+ }
+
+ sqlite3_finalize(stmt);
+
+ if(rc != SQLITE_DONE) {
+ for(int i = 0; i < (*array)->len; i++) {
+ temp2 = &g_array_index(*array, struct tag_t, i);
+ g_free(temp2->name);
+ }
+ g_array_free(*array, TRUE);
+ return 0;
+ }
+
+ return 1;
+}
+
+int db_get_tags(GArray **array, sqlite3_uint64 parent) {
+ struct tag_t temp, *temp2;
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, "SELECT id, name, parent FROM tag WHERE parent = ? ORDER BY name", -1, &stmt, NULL);
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ rc = sqlite3_bind_int64(stmt, 1, parent);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ *array = g_array_new(FALSE, FALSE, sizeof(struct tag_t));
+ while((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ temp.name = g_strdup((const gchar*)sqlite3_column_text(stmt, 1));
+ temp.id = sqlite3_column_int64(stmt, 0);
+ temp.parent = sqlite3_column_int64(stmt, 2);
+ g_array_append_val(*array, temp);
+ }
+
+ sqlite3_finalize(stmt);
+
+ if(rc != SQLITE_DONE) {
+ for(int i = 0; i < (*array)->len; i++) {
+ temp2 = &g_array_index(*array, struct tag_t, i);
+ g_free(temp2->name);
+ }
+ g_array_free(*array, TRUE);
+ return 0;
+ }
+
+ return 1;
+}
+
int db_add_wall_tag(sqlite_uint64 wallid, sqlite_uint64 tagid) {
sqlite3_stmt *stmt;
int rc;
diff --git a/db.h b/db.h
index a2c19b1..969bace 100644
--- a/db.h
+++ b/db.h
@@ -22,6 +22,7 @@ struct wallpaper_t {
struct tag_t {
gchar *name;
sqlite_uint64 id;
+ sqlite_uint64 parent;
};
int db_open();
@@ -39,6 +40,8 @@ int db_get_wallpapers(sqlite_uint64, GArray**);
int db_get_walls_by_tags(GArray*, GArray**);
sqlite_uint64 db_add_tag(const char*, sqlite_uint64);
int db_get_tags_all(GArray**);
+int db_get_top_level_tags(GArray**);
+int db_get_tags(GArray**, sqlite_uint64);
int db_add_wall_tag(sqlite_uint64, sqlite_uint64);
void db_remove_tag(sqlite_uint64);
int db_remove_wall_tag(sqlite_uint64, sqlite_uint64);
diff --git a/tag_model.c b/tag_model.c
new file mode 100644
index 0000000..8144e4c
--- /dev/null
+++ b/tag_model.c
@@ -0,0 +1,489 @@
+#include "tag_model.h"
+
+static void tag_model_init(TagModel *tag_model);
+static void tag_model_class_init(TagModelClass *klass);
+static void tag_model_tree_model_init(GtkTreeModelIface *iface);
+static void tag_model_finalize(GObject *object);
+static GtkTreeModelFlags tag_model_get_flags(GtkTreeModel *tree_model);
+static gint tag_model_get_n_columns(GtkTreeModel *tree_model);
+static GType tag_model_get_column_type(GtkTreeModel *tree_model, gint index);
+static gboolean tag_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path);
+static GtkTreePath *tag_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static void tag_model_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value);
+static gboolean tag_model_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gboolean tag_model_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent);
+static gboolean tag_model_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gint tag_model_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gboolean tag_model_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n);
+static gboolean tag_model_iter_parent(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child);
+
+static GObjectClass *parent_class = NULL;
+
+GType tag_model_get_type() {
+ static GType tag_model_type = 0;
+ if(tag_model_type == 0) {
+ static const GTypeInfo tag_model_info = {
+ sizeof(TagModelClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) tag_model_class_init,
+ NULL,
+ NULL,
+ sizeof(TagModel),
+ 0,
+ (GInstanceInitFunc) tag_model_init
+ };
+ static const GInterfaceInfo tree_model_info = {
+ (GInterfaceInitFunc) tag_model_tree_model_init,
+ NULL,
+ NULL
+ };
+ tag_model_type = g_type_register_static(G_TYPE_OBJECT, "TagModel", &tag_model_info, (GTypeFlags)0);
+ g_type_add_interface_static(tag_model_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
+ }
+ return tag_model_type;
+}
+
+static void tag_model_class_init(TagModelClass *klass) {
+ GObjectClass *object_class;
+
+ parent_class = (GObjectClass*)g_type_class_peek_parent(klass);
+ object_class = (GObjectClass*)klass;
+
+ object_class->finalize = tag_model_finalize;
+}
+
+static void tag_model_tree_model_init(GtkTreeModelIface *iface) {
+ iface->get_flags = tag_model_get_flags;
+ iface->get_n_columns = tag_model_get_n_columns;
+ iface->get_column_type = tag_model_get_column_type;
+ iface->get_iter = tag_model_get_iter;
+ iface->get_path = tag_model_get_path;
+ iface->get_value = tag_model_get_value;
+ iface->iter_next = tag_model_iter_next;
+ iface->iter_children = tag_model_iter_children;
+ iface->iter_has_child = tag_model_iter_has_child;
+ iface->iter_n_children = tag_model_iter_n_children;
+ iface->iter_nth_child = tag_model_iter_nth_child;
+ iface->iter_parent = tag_model_iter_parent;
+}
+
+static void tag_model_init_children(TagModelRecord *record) {
+ GArray *array;
+
+ record->children = g_array_new(FALSE, FALSE, sizeof(TagModelRecord));
+ if(db_get_tags(&array, record->tag.id)) {
+ for(int i = 0; i < array->len; i++) {
+ TagModelRecord temp;
+ struct tag_t *tag;
+
+ tag = &g_array_index(array, struct tag_t, i);
+ temp.tag.name = g_strdup(tag->name);
+ temp.tag.id = tag->id;
+ temp.tag.parent = tag->parent;
+ temp.checked = temp.inconsistent = FALSE;
+ temp.parent = record;
+ temp.children = NULL;
+ temp.pos = i;
+
+ g_array_append_val(record->children, temp);
+ tag_model_init_children(&g_array_index(record->children, TagModelRecord, temp.pos));
+
+ g_free(tag->name);
+ }
+ g_array_free(array, TRUE);
+ }
+}
+
+static void tag_model_init(TagModel *tag_model) {
+ TagModelRecord temp;
+ GArray *array;
+ tag_model->n_columns = TAG_MODEL_N_COLUMNS;
+
+ tag_model->column_types[0] = G_TYPE_BOOLEAN;
+ tag_model->column_types[1] = G_TYPE_BOOLEAN;
+ tag_model->column_types[2] = G_TYPE_STRING;
+
+ g_assert(TAG_MODEL_N_COLUMNS == 3);
+
+ tag_model->num_rows = 0;
+ tag_model->rows = g_array_new(FALSE, FALSE, sizeof(TagModelRecord));
+ tag_model->stamp = g_random_int();
+
+ if(db_get_top_level_tags(&array)) {
+ for(int i = 0; i < array->len; i++) {
+ struct tag_t *tag;
+
+ tag = &g_array_index(array, struct tag_t, i);
+
+ temp.tag.name = g_strdup(tag->name);
+ temp.tag.id = tag->id;
+ temp.tag.parent = tag->parent;
+ temp.checked = temp.inconsistent = FALSE;
+ temp.parent = NULL;
+ temp.children = NULL;
+ temp.pos = i;
+
+ g_free(tag->name);
+
+ g_array_append_val(tag_model->rows, temp);
+ tag_model_init_children(&g_array_index(tag_model->rows, TagModelRecord, tag_model->num_rows));
+ tag_model->num_rows++;
+ }
+ g_array_free(array, TRUE);
+ }
+}
+
+static void finalize_recursive(TagModelRecord *record) {
+ if(!record->children)
+ return;
+
+ for(int i = 0; i < record->children->len; i++) {
+ TagModelRecord *temp;
+ temp = &g_array_index(record->children, TagModelRecord, i);
+ g_free(temp->tag.name);
+ }
+}
+
+static void tag_model_finalize(GObject *object) {
+ TagModel *tag_model;
+
+ g_assert(MODEL_IS_TAG(object));
+
+ tag_model = TAG_MODEL(object);
+
+ for(int i = 0; i < tag_model->rows->len; i++) {
+ TagModelRecord *temp;
+ temp = &g_array_index(tag_model->rows, TagModelRecord, i);
+ g_free(temp->tag.name);
+ finalize_recursive(temp);
+ }
+
+ g_array_free(tag_model->rows, TRUE);
+}
+
+static GtkTreeModelFlags tag_model_get_flags(GtkTreeModel *tree_model) {
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), (GtkTreeModelFlags)0);
+
+ return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static gint tag_model_get_n_columns(GtkTreeModel *tree_model) {
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), 0);
+
+ return TAG_MODEL(tree_model)->n_columns;
+}
+
+static GType tag_model_get_column_type(GtkTreeModel *tree_model, gint index) {
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), G_TYPE_INVALID);
+ g_return_val_if_fail(index < TAG_MODEL(tree_model)->n_columns && index >= 0, G_TYPE_INVALID);
+
+ return TAG_MODEL(tree_model)->column_types[index];
+}
+
+inline static TagModelRecord *tag_model_get_record_recursive(TagModel *tag_model, gint *indices, gint depth) {
+ TagModelRecord *temp;
+ if(depth == 1) {
+ return &g_array_index(tag_model->rows, TagModelRecord, indices[depth-1]);
+ } else {
+ temp = tag_model_get_record_recursive(tag_model, indices, depth-1);
+ return &g_array_index(temp->children, TagModelRecord, indices[depth-1]);
+ }
+}
+
+static gboolean tag_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) {
+ TagModel *tag_model;
+ TagModelRecord *record;
+ gint *indices, n, depth;
+
+ g_assert(MODEL_IS_TAG(tree_model));
+ g_assert(path != NULL);
+
+ tag_model = TAG_MODEL(tree_model);
+
+ indices = gtk_tree_path_get_indices(path);
+ depth = gtk_tree_path_get_depth(path);
+
+ n = indices[depth-1];
+ //if(n >= tag_model->num_rows || n < 0)
+ // return FALSE;
+
+ //record = &g_array_index(tag_model->rows, TagModelRecord, n);
+ record = tag_model_get_record_recursive(tag_model, indices, depth);
+
+ g_assert(record != NULL);
+ g_assert(record->pos == n);
+
+ iter->stamp = tag_model->stamp;
+ iter->user_data = record;
+ iter->user_data2 = NULL;
+ iter->user_data3 = NULL;
+
+ return TRUE;
+}
+
+static GtkTreePath *tag_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ GtkTreePath *path;
+ TagModelRecord *record;
+ TagModel *tag_model;
+
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), NULL);
+ g_return_val_if_fail(iter != NULL, NULL);
+ g_return_val_if_fail(iter->user_data != NULL, NULL);
+
+ tag_model = TAG_MODEL(tree_model);
+
+ record = (TagModelRecord*)iter->user_data;
+
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, record->pos);
+
+ return path;
+}
+
+static void tag_model_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value) {
+ TagModelRecord *record;
+ TagModel *tag_model;
+
+ g_return_if_fail(MODEL_IS_TAG(tree_model));
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(column < TAG_MODEL(tree_model)->n_columns);
+
+ g_value_init(value, TAG_MODEL(tree_model)->column_types[column]);
+
+ tag_model = TAG_MODEL(tree_model);
+
+ record = (TagModelRecord*)iter->user_data;
+
+ g_return_if_fail(record != NULL);
+
+ //if(record->pos >= tag_model->num_rows)
+ // g_return_if_reached();
+
+ switch(column) {
+ case TAG_MODEL_COL_CHECKED:
+ g_value_set_boolean(value, record->checked);
+ break;
+ case TAG_MODEL_COL_INCONSISTENT:
+ g_value_set_boolean(value, record->inconsistent);
+ break;
+ case TAG_MODEL_COL_NAME:
+ g_value_set_string_take_ownership(value, g_strdup(record->tag.name));
+ break;
+ }
+}
+
+static gboolean tag_model_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ TagModelRecord *record, *nextrecord;
+ TagModel *tag_model;
+ GArray *array;
+
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), FALSE);
+
+ if(iter == NULL || iter->user_data == NULL)
+ return FALSE;
+
+ tag_model = TAG_MODEL(tree_model);
+
+ record = (TagModelRecord*)iter->user_data;
+
+ array = (record->parent ? record->parent->children : tag_model->rows);
+
+ if((record->pos + 1) >= array->len)
+ return FALSE;
+
+ nextrecord = &g_array_index(array, TagModelRecord, record->pos + 1);
+
+ g_assert(nextrecord != NULL);
+ g_assert(nextrecord->pos == (record->pos + 1));
+
+ iter->stamp = tag_model->stamp;
+ iter->user_data = nextrecord;
+
+ return TRUE;
+}
+
+static gboolean tag_model_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) {
+ TagModel *tag_model;
+ TagModelRecord *parent_record;
+
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), FALSE);
+
+ tag_model = TAG_MODEL(tree_model);
+
+ if(!parent) {
+ if(tag_model->num_rows == 0)
+ return FALSE;
+ iter->stamp = tag_model->stamp;
+ iter->user_data = &g_array_index(tag_model->rows, TagModelRecord, 0);
+ return TRUE;
+ }
+
+ //g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
+
+ parent_record = (TagModelRecord*)parent->user_data;
+
+ if(parent_record->children->len == 0)
+ return FALSE;
+
+ iter->stamp = tag_model->stamp;
+ iter->user_data = &g_array_index(parent_record->children, TagModelRecord, 0);
+
+ return TRUE;
+}
+
+static gboolean tag_model_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ TagModelRecord *record;
+
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), FALSE);
+
+ record = (TagModelRecord*)iter->user_data;
+
+ return (record->parent == NULL && TAG_MODEL(tree_model)->num_rows == 0 ? FALSE : (record->children->len > 0 ? TRUE : FALSE));
+}
+
+static gint tag_model_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ TagModel *tag_model;
+ TagModelRecord *record;
+
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), -1);
+ g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0);
+
+ tag_model = TAG_MODEL(tree_model);
+
+ if(!iter) {
+ return tag_model->num_rows;
+ }
+
+ record = (TagModelRecord*)iter->user_data;
+
+ return record->children->len;
+}
+
+static gboolean tag_model_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n) {
+ TagModelRecord *record, *parent_record;
+ TagModel *tag_model;
+
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), FALSE);
+
+ tag_model = TAG_MODEL(tree_model);
+
+ if(!parent) {
+ if(n >= tag_model->num_rows)
+ return FALSE;
+ record = &g_array_index(tag_model->rows, TagModelRecord, n);
+
+ iter->stamp = tag_model->stamp;
+ iter->user_data = record;
+
+ return TRUE;
+ }
+
+ //if(n >= tag_model->num_rows)
+ // return FALSE;
+
+ parent_record = parent->user_data;
+
+ if(n >= parent_record->children->len)
+ return FALSE;
+
+ record = &g_array_index(parent_record->children, TagModelRecord, n);
+
+ g_assert(record != NULL);
+ g_assert(record->pos == n);
+
+ iter->stamp = tag_model->stamp;
+ iter->user_data = record;
+
+ return TRUE;
+}
+
+static gboolean tag_model_iter_parent(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) {
+ TagModelRecord *childrecord;
+ TagModel *tag_model;
+
+ g_return_val_if_fail(MODEL_IS_TAG(tree_model), FALSE);
+
+ tag_model = TAG_MODEL(tree_model);
+
+ childrecord = (TagModelRecord*)child->user_data;
+
+ iter->stamp = tag_model->stamp;
+ iter->user_data = childrecord->parent;
+
+ return TRUE;
+}
+
+TagModel *tag_model_new() {
+ TagModel *new_tag_model;
+
+ new_tag_model = (TagModel*)g_object_new(MODEL_TYPE_TAG, NULL);
+
+ g_assert(new_tag_model != NULL);
+
+ return new_tag_model;
+}
+
+void tag_model_get_tag_record(GtkTreeModel *tree_model, GtkTreeIter *iter, struct tag_t **tag) {
+ TagModel *tag_model;
+ TagModelRecord *record;
+
+ g_assert(MODEL_IS_TAG(tree_model));
+ g_assert(iter != NULL);
+
+ tag_model = TAG_MODEL(tree_model);
+ record = (TagModelRecord*)iter->user_data;
+ *tag = &record->tag;
+}
+
+gboolean tag_model_get_checked(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ TagModel *tag_model;
+ TagModelRecord *record;
+
+ g_assert(MODEL_IS_TAG(tree_model));
+ g_assert(iter != NULL);
+
+ tag_model = TAG_MODEL(tree_model);
+ record = (TagModelRecord*)iter->user_data;
+
+ return record->checked;
+}
+
+void tag_model_set_checked(GtkTreeModel *tree_model, GtkTreeIter *iter, gboolean checked) {
+ TagModel *tag_model;
+ TagModelRecord *record;
+
+ g_assert(MODEL_IS_TAG(tree_model));
+ g_assert(iter != NULL);
+
+ tag_model = TAG_MODEL(tree_model);
+ record = (TagModelRecord*)iter->user_data;
+
+ record->checked = checked;
+}
+
+gboolean tag_model_get_inconsistent(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ TagModel *tag_model;
+ TagModelRecord *record;
+
+ g_assert(MODEL_IS_TAG(tree_model));
+ g_assert(iter != NULL);
+
+ tag_model = TAG_MODEL(tree_model);
+ record = (TagModelRecord*)iter->user_data;
+
+ return record->inconsistent;
+}
+
+void tag_model_set_inconsistent(GtkTreeModel *tree_model, GtkTreeIter *iter, gboolean inconsistent) {
+ TagModel *tag_model;
+ TagModelRecord *record;
+
+ g_assert(MODEL_IS_TAG(tree_model));
+ g_assert(iter != NULL);
+
+ tag_model = TAG_MODEL(tree_model);
+ record = (TagModelRecord*)iter->user_data;
+
+ record->inconsistent = inconsistent;
+}
diff --git a/tag_model.h b/tag_model.h
new file mode 100644
index 0000000..43e59a1
--- /dev/null
+++ b/tag_model.h
@@ -0,0 +1,58 @@
+#ifndef _TAG_MODEL_H_
+#define _TAG_MODEL_H_
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include "db.h"
+
+#define MODEL_TYPE_TAG (tag_model_get_type())
+#define TAG_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MODEL_TYPE_TAG, TagModel))
+#define TAG_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MODEL_TYPE_TAG, TagModelClass))
+#define MODEL_IS_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MODEL_TYPE_TAG))
+#define MODEL_IS_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MODEL_TYPE_TAG))
+#define TAG_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MODEL_TYPE_TAG, TagModelClass))
+
+enum {
+ TAG_MODEL_COL_CHECKED = 0,
+ TAG_MODEL_COL_INCONSISTENT,
+ TAG_MODEL_COL_NAME,
+ TAG_MODEL_N_COLUMNS,
+};
+
+typedef struct _TagModelRecord TagModelRecord;
+typedef struct _TagModel TagModel;
+typedef struct _TagModelClass TagModelClass;
+
+struct _TagModelRecord {
+ struct tag_t tag;
+ gboolean checked;
+ gboolean inconsistent;
+ TagModelRecord *parent;
+ GArray *children;
+ guint pos;
+};
+
+struct _TagModel {
+ GObject parent;
+
+ guint num_rows;
+ GArray *rows;
+ gint n_columns;
+ GType column_types[TAG_MODEL_N_COLUMNS];
+ gint stamp;
+};
+
+struct _TagModelClass {
+ GObjectClass parent_class;
+};
+
+GType tag_model_get_type();
+TagModel *tag_model_new();
+void tag_model_get_tag_record(GtkTreeModel*, GtkTreeIter*, struct tag_t**);
+gboolean tag_model_get_checked(GtkTreeModel*, GtkTreeIter*);
+void tag_model_set_checked(GtkTreeModel*, GtkTreeIter*, gboolean);
+gboolean tag_model_get_inconsistent(GtkTreeModel*, GtkTreeIter*);
+void tag_model_set_inconsistent(GtkTreeModel*, GtkTreeIter*, gboolean);
+
+#endif
diff --git a/window_main.c b/window_main.c
index e5cd3b7..d327177 100644
--- a/window_main.c
+++ b/window_main.c
@@ -11,6 +11,7 @@
#include "window_tag.h"
#include "preload.h"
#include "window_config.h"
+#include "tag_model.h"
enum zoom_mode_t {
ZOOM_BESTFIT,
@@ -80,6 +81,9 @@ inline static void tagview_create_model(GtkTreeView *tagview) {
struct tag_t *tag;
GtkTreeIter iter;
+ gtk_tree_view_set_model(tagview, GTK_TREE_MODEL(tag_model_new()));
+ return;
+
model = gtk_list_store_new(3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_UINT64);
if(db_get_tags_all(&array)) {
@@ -114,7 +118,7 @@ inline static void tagview_init(GtkTreeView *tagview) {
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_end(col2, renderer, TRUE);
- gtk_tree_view_column_add_attribute(col2, renderer, "text", 1);
+ gtk_tree_view_column_add_attribute(col2, renderer, "text", 2);
tagview_create_model(tagview);
}
@@ -493,10 +497,8 @@ void display_from_tagview() {
GtkTreeIter iter;
GtkTreeModel *model, *old_model;
GtkListStore *liststore;
- GValue value = {0};
- gboolean active;
GArray *tagarray, *wallarray;
- guint64 id;
+ struct tag_t *tag;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(tagview));
@@ -506,18 +508,12 @@ void display_from_tagview() {
tagarray = g_array_new(FALSE, FALSE, sizeof(guint64));
do {
- gtk_tree_model_get_value(model, &iter, 0, &value);
- active = g_value_get_boolean(&value);
- g_value_unset(&value);
-
- if(!active)
+ if(!tag_model_get_checked(model, &iter))
continue;
- gtk_tree_model_get_value(model, &iter, 2, &value);
- id = g_value_get_uint64(&value);
- g_value_unset(&value);
+ tag_model_get_tag_record(GTK_TREE_MODEL(model), &iter, &tag);
- g_array_append_val(tagarray, id);
+ g_array_append_val(tagarray, tag->id);
} while(gtk_tree_model_iter_next(model, &iter));
if(!db_get_walls_by_tags(tagarray, &wallarray)) {
@@ -542,17 +538,14 @@ void display_from_tagview() {
void on_main_tagview_cell_toggled(GtkCellRendererToggle *cell_renderer, gchar *path_string, gpointer user_data) {
GtkTreeIter iter;
GtkTreeModel *model;
- GValue value = {0};
gboolean active;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(tagview));
gtk_tree_model_get_iter_from_string(model, &iter, path_string);
- gtk_tree_model_get_value(model, &iter, 0, &value);
- active = g_value_get_boolean(&value);
- g_value_unset(&value);
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, !active, -1);
+ active = tag_model_get_checked(GTK_TREE_MODEL(model), &iter);
+ tag_model_set_checked(GTK_TREE_MODEL(model), &iter, !active);
display_from_tagview();
}
@@ -813,36 +806,26 @@ inline static void update_wall_tags(GArray *wallarray, guint64 tagid, gboolean a
/**
* Update tags for wallpapers.
*/
-static void update_tags(GArray *wallarray, GtkListStore *liststore) {
+static void update_tags(GArray *wallarray, GtkTreeModel *model) {
GtkTreeIter iter;
- GValue value = {0};
- gboolean active, inconsistent;
- guint64 tagid;
+ gboolean active;
+ struct tag_t *tag;
/* No tags available. */
- if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter)) {
+ if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) {
return;
}
do {
- gtk_tree_model_get_value(GTK_TREE_MODEL(liststore), &iter, 1, &value);
- inconsistent = g_value_get_boolean(&value);
- g_value_unset(&value);
-
/* Don't update inconsistent tags. */
- if(inconsistent)
+ if(tag_model_get_inconsistent(GTK_TREE_MODEL(model), &iter))
continue;
- gtk_tree_model_get_value(GTK_TREE_MODEL(liststore), &iter, 0, &value);
- active = g_value_get_boolean(&value);
- g_value_unset(&value);
-
- gtk_tree_model_get_value(GTK_TREE_MODEL(liststore), &iter, 3, &value);
- tagid = g_value_get_uint64(&value);
- g_value_unset(&value);
+ active = tag_model_get_checked(GTK_TREE_MODEL(model), &iter);
+ tag_model_get_tag_record(GTK_TREE_MODEL(model), &iter, &tag);
- update_wall_tags(wallarray, tagid, active);
- } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter));
+ update_wall_tags(wallarray, tag->id, active);
+ } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
}
void on_tags_activate(GtkMenuItem *menuitem, gpointer user_data) {
@@ -855,7 +838,7 @@ void on_tags_activate(GtkMenuItem *menuitem, gpointer user_data) {
data = window_tagview_new(GTK_WIDGET(window), array);
if(gtk_dialog_run(GTK_DIALOG(data->dialog)) == GTK_RESPONSE_APPLY) {
- update_tags(array, GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->tagview))));
+ update_tags(array, GTK_TREE_MODEL(gtk_tree_view_get_model(GTK_TREE_VIEW(data->tagview))));
}
gtk_widget_destroy(GTK_WIDGET(data->dialog));
diff --git a/window_tag.c b/window_tag.c
index ca6d6f4..da77cdc 100644
--- a/window_tag.c
+++ b/window_tag.c
@@ -2,12 +2,12 @@
#include "tags_ui.h"
#include "db.h"
#include "text_input_dialog.h"
+#include "tag_model.h"
void on_tagview_cell_toggled(GtkCellRendererToggle *cell_renderer, gchar *path_string, gpointer user_data) {
GtkTreeIter iter;
GtkTreeModel *model;
struct tagdialog_data_t *data;
- GValue value = {0};
gboolean active, inconsistent;
data = user_data;
@@ -15,18 +15,14 @@ void on_tagview_cell_toggled(GtkCellRendererToggle *cell_renderer, gchar *path_s
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->tagview));
gtk_tree_model_get_iter_from_string(model, &iter, path_string);
- gtk_tree_model_get_value(model, &iter, 0, &value);
- active = g_value_get_boolean(&value);
- g_value_unset(&value);
- gtk_tree_model_get_value(model, &iter, 1, &value);
- inconsistent = g_value_get_boolean(&value);
- g_value_unset(&value);
+ active = tag_model_get_checked(model, &iter);
+ inconsistent = tag_model_get_inconsistent(model, &iter);
if(inconsistent) {
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, FALSE, -1);
+ tag_model_set_inconsistent(GTK_TREE_MODEL(model), &iter, FALSE);
} else {
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, !active, -1);
+ tag_model_set_checked(GTK_TREE_MODEL(model), &iter, !active);
}
}
@@ -60,12 +56,24 @@ inline static gboolean is_tag_inconsistent(struct tag_t *tag, GArray *walltags,
return (n_n == 0 || n_y == 0 ? FALSE : TRUE);
}
-static void tagview_create_model(GtkTreeView *tagview, gpointer user_data) {
- GtkListStore *model;
- GArray *array, *walltags, *walltag_single, *wallarray;
- GtkTreeIter iter;
+/**
+ * foreach-callback for setting checked and inconsistent in the tag model.
+ */
+static gboolean tagview_model_foreach(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) {
struct tag_t *tag;
gboolean inconsistent, exists;
+
+ tag_model_get_tag_record(GTK_TREE_MODEL(model), iter, &tag);
+ inconsistent = is_tag_inconsistent(tag, (GArray*)user_data, &exists);
+ tag_model_set_checked(GTK_TREE_MODEL(model), iter, exists);
+ tag_model_set_inconsistent(GTK_TREE_MODEL(model), iter, inconsistent);
+
+ return FALSE;
+}
+
+static void tagview_create_model(GtkTreeView *tagview, gpointer user_data) {
+ GtkTreeModel *model;
+ GArray *walltags, *walltag_single, *wallarray;
struct tagdialog_data_t *data;
data = user_data;
@@ -81,28 +89,16 @@ static void tagview_create_model(GtkTreeView *tagview, gpointer user_data) {
}
}
- model = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_UINT64);
+ model = GTK_TREE_MODEL(tag_model_new());
+ gtk_tree_view_set_model(tagview, GTK_TREE_MODEL(model));
- if(db_get_tags_all(&array)) {
- for(int i = 0; i < array->len; i++) {
- tag = &g_array_index(array, struct tag_t, i);
- gtk_list_store_append(model, &iter);
- inconsistent = is_tag_inconsistent(tag, walltags, &exists);
- gtk_list_store_set(model, &iter, 0, exists, 1, inconsistent, 2, g_strdup(tag->name), 3, tag->id, -1);
- g_free(tag->name);
- }
- g_array_free(array, TRUE);
- } else {
- g_warning("Could not fetch tags\n");
- }
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model), tagview_model_foreach, walltags);
for(int i = 0; i < walltags->len; i++) {
walltag_single = g_array_index(walltags, GArray*, i);
g_array_free(walltag_single, TRUE);
}
g_array_free(walltags, TRUE);
-
- gtk_tree_view_set_model(tagview, GTK_TREE_MODEL(model));
}
static void tagview_init(GtkTreeView *tagview, gpointer user_data) {
@@ -154,9 +150,7 @@ void on_tags_rembtn_clicked(GtkButton *button, gpointer user_data) {
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
- gchar *tagname;
- guint64 tagid;
- GValue value = {0};
+ struct tag_t *tag;
data = user_data;
@@ -164,21 +158,13 @@ void on_tags_rembtn_clicked(GtkButton *button, gpointer user_data) {
if(!gtk_tree_selection_get_selected(selection, &model, &iter))
return;
- gtk_tree_model_get_value(model, &iter, 3, &value);
- tagid = g_value_get_uint64(&value);
- g_value_unset(&value);
-
- gtk_tree_model_get_value(model, &iter, 2, &value);
- tagname = g_value_dup_string(&value);
- g_value_unset(&value);
+ tag_model_get_tag_record(GTK_TREE_MODEL(model), &iter, &tag);
dialog = gtk_message_dialog_new(GTK_WINDOW(data->dialog), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Are you sure you want to delete the tag \"%s\"?", tagname);
-
- g_free(tagname);
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Are you sure you want to delete the tag \"%s\" and all its children?", tag->name);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) {
- db_remove_tag(tagid);
+ db_remove_tag(tag->id);
tagview_create_model(GTK_TREE_VIEW(data->tagview), user_data);
}
@@ -198,7 +184,7 @@ static void tags_select(gpointer user_data, gboolean select) {
return;
do {
- gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, select, -1);
+ tag_model_set_checked(GTK_TREE_MODEL(model), &iter, select);
} while(gtk_tree_model_iter_next(model, &iter));
}