summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--SConstruct38
-rw-r--r--db.c312
-rw-r--r--db.h29
-rw-r--r--main.c9
-rw-r--r--wallpapers.c82
-rw-r--r--walls.ui140
-rw-r--r--walls_model.c479
-rw-r--r--walls_model.h63
-rw-r--r--window_main.c152
-rw-r--r--window_main.h8
11 files changed, 1319 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a0a897
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+.*.swp
+*.o
+/.sconf_temp
+/.sconsign.dblite
+/config.log
+/walls_ui.h
+/walls
diff --git a/SConstruct b/SConstruct
new file mode 100644
index 0000000..4aae244
--- /dev/null
+++ b/SConstruct
@@ -0,0 +1,38 @@
+env = Environment()
+
+conf = Configure(env)
+for lib, h in (('Imlib2', 'Imlib2.h'), ('glib-2.0', None), ('sqlite3', 'sqlite3.h'), ('gtk-x11-2.0', None)):
+ if h:
+ libfound = conf.CheckLibWithHeader(lib, h, 'c')
+ else:
+ libfound = conf.CheckLib(lib)
+ if not libfound:
+ print 'Can''t find %s' % lib
+ Exit(1)
+env = conf.Finish()
+
+env.Append(CCFLAGS = ['-std=gnu99', '-g'])
+env.Append(LINKFLAGS = ['-Wl,--export-dynamic'])
+env.ParseConfig('imlib2-config --cflags --libs')
+env.ParseConfig('pkg-config --cflags --libs glib-2.0')
+env.ParseConfig('pkg-config --cflags --libs sqlite3')
+env.ParseConfig('pkg-config --cflags --libs gtk+-2.0')
+
+def build_ui(target, source, env):
+ f = open(str(target[0]), 'w')
+ f.write('#ifndef _WALLS_UI_H_\n')
+ f.write('#define _WALLS_UI_H_\n')
+ f.write('#include <gtk/gtk.h>\n')
+ f.write('gchar *ui_string = \n')
+ for line in open(str(source[0]), 'r'):
+ f.write('"%s"\n' % line.replace('"', '\\"').strip())
+ f.write(';\n')
+ f.write('#endif\n')
+
+ui_builder = Builder(action = build_ui)
+
+env['BUILDERS']['walls_ui'] = ui_builder
+env.walls_ui('walls_ui.h', 'walls.ui')
+env.Program('walls', Glob('*.c'))
+
+# vim: syn=python
diff --git a/db.c b/db.c
new file mode 100644
index 0000000..2fbaed8
--- /dev/null
+++ b/db.c
@@ -0,0 +1,312 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib/gprintf.h>
+
+#include "db.h"
+
+sqlite3 *db = NULL;
+
+void db_close();
+
+static int db_create_tables() {
+ char *errmsg;
+ int rc;
+ rc = sqlite3_exec(db,
+ "create table directory ("
+ " id integer not null,"
+ " parent integer null,"
+ " name varchar(255),"
+ " primary key (id)"
+ ");"
+ "create table wallpaper ("
+ " id integer not null,"
+ " dirid integer not null,"
+ " filepath text not null,"
+ " size integer not null,"
+ " width integer not null,"
+ " height integer not null,"
+ " primary key (id)"
+ ");"
+ "create table tag ("
+ " id integer not null,"
+ " name varchar(100) not null,"
+ " primary key (id)"
+ ");"
+ "create table walltags ("
+ " wallid integer not null,"
+ " tagid integer not null,"
+ " primary key (wallid, tagid)"
+ ");",
+ NULL, NULL, &errmsg);
+ if(rc != SQLITE_OK) {
+ fprintf(stderr, "db_create_tables failed: %s\n", errmsg);
+ sqlite3_free(errmsg);
+ return 0;
+ }
+ return 1;
+}
+
+int db_open() {
+ int rc;
+ int configfound;
+ char *dbfile;
+ char *configdir;
+
+ configdir = g_strdup_printf("%s/walls", g_get_user_config_dir());
+ if(access(configdir, F_OK) == -1 && g_mkdir_with_parents(configdir, 0700) == -1) {
+ g_free(configdir);
+ return 0;
+ }
+ g_free(configdir);
+
+ dbfile = g_strdup_printf("%s/walls/db", g_get_user_config_dir());
+ printf("db file: %s\n", dbfile);
+ configfound = access(dbfile, F_OK) == 0;
+
+ rc = sqlite3_open(dbfile, &db);
+ g_free(dbfile);
+
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ if(!configfound && !db_create_tables()) {
+ db_close();
+ return 0;
+ }
+
+ return 1;
+}
+
+void db_close() {
+ if(db) {
+ sqlite3_close(db);
+ db = NULL;
+ }
+}
+
+sqlite_uint64 db_add_directory(const char *path, sqlite_uint64 parent) {
+ sqlite3_stmt *stmt;
+ int rc;
+
+ if(parent) {
+ rc = sqlite3_prepare_v2(db, "INSERT INTO directory (name, parent) VALUES (?, ?)", -1, &stmt, NULL);
+ } else {
+ rc = sqlite3_prepare_v2(db, "INSERT INTO directory (name) VALUES (?)", -1, &stmt, NULL);
+ }
+
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ rc = sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC);
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ if(parent) {
+ rc = sqlite3_bind_int(stmt, 2, parent);
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+ }
+
+ rc = sqlite3_step(stmt);
+
+ sqlite3_finalize(stmt);
+ if(rc == SQLITE_DONE) {
+ return sqlite3_last_insert_rowid(db);
+ } else {
+ return 0;
+ }
+}
+
+sqlite_uint64 db_get_directory(const char *path) {
+ sqlite3_stmt *stmt;
+ int rc;
+ sqlite_uint64 dirid;
+
+ rc = sqlite3_prepare_v2(db, "SELECT id FROM directory WHERE name = ? LIMIT 1", -1, &stmt, NULL);
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ rc = sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ rc = sqlite3_step(stmt);
+ if(rc == SQLITE_ROW) {
+ dirid = sqlite3_column_int64(stmt, 0);
+ sqlite3_finalize(stmt);
+ return dirid;
+ }
+
+ sqlite3_finalize(stmt);
+ return 0;
+}
+
+int db_get_top_level_directories(GArray **array) {
+ struct directory_t temp, *temp2;
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, "SELECT id, name FROM directory WHERE parent ISNULL ORDER BY name", -1, &stmt, NULL);
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ *array = g_array_new(TRUE, FALSE, sizeof(struct directory_t));
+ while((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ temp.name = g_strdup(sqlite3_column_text(stmt, 1));
+ temp.dirid = sqlite3_column_int64(stmt, 0);
+ g_array_append_val(*array, temp);
+ }
+
+ sqlite3_finalize(stmt);
+
+ if(rc != SQLITE_DONE) {
+ for(int i = 0; (*array)->len; i++) {
+ temp2 = &g_array_index(*array, struct directory_t, i);
+ g_free(temp2->name);
+ }
+ g_array_free(*array, TRUE);
+ return 0;
+ }
+
+ return 1;
+}
+
+int db_get_directories(sqlite_uint64 parent, GArray **array) {
+ struct directory_t temp, *temp2;
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, "SELECT id, name FROM directory 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(TRUE, FALSE, sizeof(struct directory_t));
+ while((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ temp.name = g_strdup(sqlite3_column_text(stmt, 1));
+ temp.dirid = sqlite3_column_int64(stmt, 0);
+ 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 directory_t, i);
+ g_free(temp2->name);
+ }
+ g_array_free(*array, TRUE);
+ return 0;
+ }
+
+ return 1;
+}
+
+sqlite_uint64 db_add_wallpaper(const char *filepath, sqlite_uint64 dirid, int size, int width, int height) {
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, "INSERT INTO wallpaper (dirid, filepath, size, width, height) VALUES (?, ?, ?, ?, ?)", -1, &stmt, NULL);
+
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ rc = sqlite3_bind_int64(stmt, 1, dirid);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ rc = sqlite3_bind_text(stmt, 2, filepath, -1, SQLITE_STATIC);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ rc = sqlite3_bind_int(stmt, 3, size);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ rc = sqlite3_bind_int(stmt, 4, width);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ rc = sqlite3_bind_int(stmt, 5, height);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ rc = sqlite3_step(stmt);
+
+ sqlite3_finalize(stmt);
+ if(rc == SQLITE_DONE) {
+ return sqlite3_last_insert_rowid(db);
+ } else {
+ return 0;
+ }
+}
+
+int db_get_wallpapers(sqlite_uint64 dirid, GArray **array) {
+ struct wallpaper_t temp, *temp2;
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, "SELECT id, filepath, size, width, height FROM wallpaper WHERE dirid = ? ORDER BY filepath", -1, &stmt, NULL);
+
+ if(rc != SQLITE_OK) {
+ return 0;
+ }
+
+ rc = sqlite3_bind_int64(stmt, 1, dirid);
+ if(rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ return 0;
+ }
+
+ *array = g_array_new(TRUE, FALSE, sizeof(struct wallpaper_t));
+ while((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+ temp.filepath = g_strdup(sqlite3_column_text(stmt, 1));
+ temp.id = sqlite3_column_int64(stmt, 1);
+ temp.size = sqlite3_column_int(stmt, 2);
+ temp.width = sqlite3_column_int(stmt, 3);
+ temp.height = sqlite3_column_int(stmt, 4);
+ 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 wallpaper_t, i);
+ g_free(temp2->filepath);
+ }
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/db.h b/db.h
new file mode 100644
index 0000000..3e17d9c
--- /dev/null
+++ b/db.h
@@ -0,0 +1,29 @@
+#ifndef _DB_H_
+#define _DB_H_
+
+#include <glib.h>
+#include <sqlite3.h>
+
+struct directory_t {
+ gchar *name;
+ sqlite_uint64 dirid;
+};
+
+struct wallpaper_t {
+ gchar *filepath;
+ sqlite_uint64 id;
+ int size;
+ int width;
+ int height;
+};
+
+int db_open();
+void db_close();
+sqlite_uint64 db_add_directory(const char*, sqlite_uint64);
+sqlite_uint64 db_get_directory(const char*);
+int db_get_top_level_directories(GArray**);
+int db_get_directories(sqlite_uint64, GArray**);
+sqlite_uint64 db_add_wallpaper(const char*, sqlite_uint64, int, int, int);
+int db_get_wallpapers(sqlite_uint64, GArray**);
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..7819dc6
--- /dev/null
+++ b/main.c
@@ -0,0 +1,9 @@
+#include "window_main.h"
+
+int main(int argc, char **argv) {
+ /*printf("%s\n", db_open() ? "db opened" : "failed to open db");
+ listdir("/mnt/stuff/wallpapers", 0);
+ db_close();*/
+
+ return gui_main(argc, argv);
+}
diff --git a/wallpapers.c b/wallpapers.c
new file mode 100644
index 0000000..525a6c4
--- /dev/null
+++ b/wallpapers.c
@@ -0,0 +1,82 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <Imlib2.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gprintf.h>
+
+#include "db.h"
+
+void listdir(const char *path, sqlite_uint64 parent) {
+ int pathlen = strlen(path);
+ //DIR *dir = opendir(path);
+ //struct dirent *de;
+ GDir *dir;
+ const char *filename;
+ sqlite_uint64 dir_temp;
+ sqlite_uint64 dirid;
+
+ dirid = db_get_directory(path);
+ if(dirid == 0) {
+ dirid = db_add_directory(path, parent);
+ if(dirid == 0)
+ return;
+ }
+
+ dir = g_dir_open(path, 0, NULL);
+ if(!dir)
+ return;
+
+ while((filename = g_dir_read_name(dir)) != NULL) {
+ char *filepath = g_strdup_printf("%s/%s", path, filename);
+ if(g_access(filepath, R_OK) == -1) {
+ fprintf(stderr, "Can't read %s: \n", filepath, strerror(errno));
+ g_free(filepath);
+ continue;
+ }
+ struct stat st;
+ if(g_stat(filepath, &st) == -1) {
+ fprintf(stderr, "Failed to stat file %s\n", filepath);
+ g_free(filepath);
+ continue;
+ }
+ switch(st.st_mode & S_IFMT) {
+ case S_IFDIR:
+ if(strcmp(filepath, ".") == 0 || strcmp(filepath, "..") == 0) {
+ g_free(filepath);
+ continue;
+ }
+ printf("Directory: %s\n", filepath);
+ listdir(filepath, dirid);
+ continue;
+ case S_IFLNK:
+ case S_IFREG:
+ break;
+ default:
+ printf("Skipping %s\n", filepath);
+ g_free(filepath);
+ continue;
+ }
+ Imlib_Image image;
+ image = imlib_load_image(filepath);
+ if(image) {
+ imlib_context_set_image(image);
+ printf("%s loaded: %dx%d\n", filepath, imlib_image_get_width(), imlib_image_get_height());
+ if(db_add_wallpaper(filepath, dirid, st.st_size, imlib_image_get_width(), imlib_image_get_height()))
+ printf("added\n");
+ else
+ printf("failed to add\n");
+ imlib_free_image();
+ } else {
+ fprintf(stderr, "%s failed\n", filepath);
+ }
+ g_free(filepath);
+ }
+ g_dir_close(dir);
+}
diff --git a/walls.ui b/walls.ui
new file mode 100644
index 0000000..0084e1f
--- /dev/null
+++ b/walls.ui
@@ -0,0 +1,140 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkWindow" id="window">
+ <signal name="destroy" handler="gtk_main_quit"/>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="file">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="filemenu">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="file_quit">
+ <property name="label">gtk-quit</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="gtk_main_quit"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="view">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="help">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="helpmenu">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="help_about">
+ <property name="label">gtk-about</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkNotebook" id="left_pages">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="filtree_scroller">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="filetree">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">File Hierarchy</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAspectFrame" id="aspectframe1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStatusbar" id="statusbar2">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/walls_model.c b/walls_model.c
new file mode 100644
index 0000000..0cfc4f8
--- /dev/null
+++ b/walls_model.c
@@ -0,0 +1,479 @@
+#include "walls_model.h"
+
+static void walls_model_init(WallsModel *walls_model);
+static void walls_model_class_init(WallsModelClass *klass);
+static void walls_model_tree_model_init(GtkTreeModelIface *iface);
+static void walls_model_finalize(GObject *object);
+static GtkTreeModelFlags walls_model_get_flags(GtkTreeModel *tree_model);
+static gint walls_model_get_n_columns(GtkTreeModel *tree_model);
+static GType walls_model_get_column_type(GtkTreeModel *tree_model, gint index);
+static gboolean walls_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path);
+static GtkTreePath *walls_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static void walls_model_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value);
+static gboolean walls_model_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gboolean walls_model_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent);
+static gboolean walls_model_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gint walls_model_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gboolean walls_model_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n);
+static gboolean walls_model_iter_parent(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child);
+
+static GObjectClass *parent_class = NULL;
+
+GType walls_model_get_type() {
+ static GType walls_model_type = 0;
+ if(walls_model_type == 0) {
+ static const GTypeInfo walls_model_info = {
+ sizeof(WallsModelClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) walls_model_class_init,
+ NULL,
+ NULL,
+ sizeof(WallsModel),
+ 0,
+ (GInstanceInitFunc) walls_model_init
+ };
+ static const GInterfaceInfo tree_model_info = {
+ (GInterfaceInitFunc) walls_model_tree_model_init,
+ NULL,
+ NULL
+ };
+ walls_model_type = g_type_register_static(G_TYPE_OBJECT, "WallsModel", &walls_model_info, (GTypeFlags)0);
+ g_type_add_interface_static(walls_model_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
+ }
+ return walls_model_type;
+}
+
+static void walls_model_class_init(WallsModelClass *klass) {
+ GObjectClass *object_class;
+
+ parent_class = (GObjectClass*)g_type_class_peek_parent(klass);
+ object_class = (GObjectClass*)klass;
+
+ object_class->finalize = walls_model_finalize;
+}
+
+static void walls_model_tree_model_init(GtkTreeModelIface *iface) {
+ iface->get_flags = walls_model_get_flags;
+ iface->get_n_columns = walls_model_get_n_columns;
+ iface->get_column_type = walls_model_get_column_type;
+ iface->get_iter = walls_model_get_iter;
+ iface->get_path = walls_model_get_path;
+ iface->get_value = walls_model_get_value;
+ iface->iter_next = walls_model_iter_next;
+ iface->iter_children = walls_model_iter_children;
+ iface->iter_has_child = walls_model_iter_has_child;
+ iface->iter_n_children = walls_model_iter_n_children;
+ iface->iter_nth_child = walls_model_iter_nth_child;
+ iface->iter_parent = walls_model_iter_parent;
+}
+
+static void walls_model_init(WallsModel *walls_model) {
+ WallsModelRecord temp;
+ GArray *array;
+ walls_model->n_columns = WALLS_MODEL_N_COLUMNS;
+
+ walls_model->column_types[0] = G_TYPE_STRING;
+ walls_model->column_types[1] = G_TYPE_UINT64;
+ walls_model->column_types[2] = G_TYPE_INT;
+ walls_model->column_types[3] = G_TYPE_INT;
+ walls_model->column_types[4] = G_TYPE_INT;
+
+ g_assert(WALLS_MODEL_N_COLUMNS == 5);
+
+ walls_model->num_rows = 0;
+ walls_model->rows = g_array_new(TRUE, FALSE, sizeof(WallsModelRecord));
+ walls_model->stamp = g_random_int();
+
+ if(db_get_top_level_directories(&array)) {
+ for(int i = 0; i < array->len; i++) {
+ struct directory_t *dir;
+
+ dir = &g_array_index(array, struct directory_t, i);
+
+ temp.type = WALLS_MODEL_TYPE_DIR;
+ temp.dir.name = g_strdup(dir->name);
+ temp.dir.dirid = dir->dirid;
+ temp.parent = NULL;
+ temp.children = NULL;
+ temp.pos = i;
+
+ g_free(dir->name);
+
+ g_array_append_val(walls_model->rows, temp);
+ walls_model->num_rows++;
+ }
+ g_array_free(array, TRUE);
+ }
+}
+
+static void walls_model_finalize(GObject *object) {
+ printf("walls_model_finalize\n");
+ WallsModel *walls_model;
+
+ g_assert(MODEL_IS_WALLS(object));
+
+ walls_model = WALLS_MODEL(object);
+
+ for(int i = 0; i < walls_model->rows->len; i++) {
+ WallsModelRecord *temp;
+ temp = &g_array_index(walls_model->rows, WallsModelRecord, i);
+ g_free(temp->dir.name);
+ }
+
+ g_array_free(walls_model->rows, TRUE);
+}
+
+static GtkTreeModelFlags walls_model_get_flags(GtkTreeModel *tree_model) {
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), (GtkTreeModelFlags)0);
+
+ return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static gint walls_model_get_n_columns(GtkTreeModel *tree_model) {
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), 0);
+
+ return WALLS_MODEL(tree_model)->n_columns;
+}
+
+static GType walls_model_get_column_type(GtkTreeModel *tree_model, gint index) {
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), G_TYPE_INVALID);
+ g_return_val_if_fail(index < WALLS_MODEL(tree_model)->n_columns && index >= 0, G_TYPE_INVALID);
+
+ return WALLS_MODEL(tree_model)->column_types[index];
+}
+
+inline static void fill_dir(WallsModelRecord *record) {
+ GArray *array;
+ gint dircount = 0;
+
+ record->children = g_array_new(FALSE, FALSE, sizeof(WallsModelRecord));
+ if(db_get_directories(record->dir.dirid, &array)) {
+ for(int i = 0; i < array->len; i++) {
+ WallsModelRecord temp;
+ struct directory_t *dir;
+
+ dir = &g_array_index(array, struct directory_t, i);
+ temp.type = WALLS_MODEL_TYPE_DIR;
+ temp.dir.name = g_path_get_basename(dir->name);
+ temp.dir.dirid = dir->dirid;
+ temp.parent = record;
+ temp.children = NULL;
+ temp.pos = i;
+
+ g_array_append_val(record->children, temp);
+
+ g_free(dir->name);
+ }
+ dircount = array->len;
+ g_array_free(array, TRUE);
+ }
+
+ if(db_get_wallpapers(record->dir.dirid, &array)) {
+ for(int i = 0; i < array->len; i++) {
+ WallsModelRecord temp;
+ struct wallpaper_t *wall;
+
+ wall = &g_array_index(array, struct wallpaper_t, i);
+ temp.type = WALLS_MODEL_TYPE_WALL;
+ temp.wall.filepath = g_path_get_basename(wall->filepath);
+ temp.wall.id = wall->id;
+ temp.wall.size = wall->size;
+ temp.wall.width = wall->width;
+ temp.wall.height = wall->height;
+
+ temp.parent = record;
+ temp.children = NULL;
+ temp.pos = dircount + i;
+
+ g_array_append_val(record->children, temp);
+
+ g_free(wall->filepath);
+ }
+ g_array_free(array, TRUE);
+ }
+}
+
+#define WALLS_MODEL_ENSURE_CHILDREN(obj) if(!(obj)->children) fill_dir((obj))
+
+inline static WallsModelRecord *walls_model_get_record_recursive(WallsModel *walls_model, gint *indices, gint depth) {
+ WallsModelRecord *temp;
+ if(depth == 1) {
+ return &g_array_index(walls_model->rows, WallsModelRecord, indices[depth-1]);
+ } else {
+ temp = walls_model_get_record_recursive(walls_model, indices, depth-1);
+ WALLS_MODEL_ENSURE_CHILDREN(temp);
+ return &g_array_index(temp->children, WallsModelRecord, indices[depth-1]);
+ }
+}
+
+static gboolean walls_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) {
+ WallsModel *walls_model;
+ WallsModelRecord *record;
+ gint *indices, n, depth;
+
+ g_assert(MODEL_IS_WALLS(tree_model));
+ g_assert(path != NULL);
+
+ walls_model = WALLS_MODEL(tree_model);
+
+ indices = gtk_tree_path_get_indices(path);
+ depth = gtk_tree_path_get_depth(path);
+
+ n = indices[depth-1];
+ //if(n >= walls_model->num_rows || n < 0)
+ // return FALSE;
+
+ //record = &g_array_index(walls_model->rows, WallsModelRecord, n);
+ record = walls_model_get_record_recursive(walls_model, indices, depth);
+
+ g_assert(record != NULL);
+ g_assert(record->pos == n);
+
+ iter->stamp = walls_model->stamp;
+ iter->user_data = record;
+ iter->user_data2 = NULL;
+ iter->user_data3 = NULL;
+
+ return TRUE;
+}
+
+static GtkTreePath *walls_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ GtkTreePath *path;
+ WallsModelRecord *record;
+ WallsModel *walls_model;
+
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), NULL);
+ g_return_val_if_fail(iter != NULL, NULL);
+ g_return_val_if_fail(iter->user_data != NULL, NULL);
+
+ walls_model = WALLS_MODEL(tree_model);
+
+ record = (WallsModelRecord*)iter->user_data;
+
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, record->pos);
+
+ return path;
+}
+
+static void walls_model_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value) {
+ WallsModelRecord *record;
+ WallsModel *walls_model;
+
+ g_return_if_fail(MODEL_IS_WALLS(walls_model));
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(column < WALLS_MODEL(walls_model)->n_columns);
+
+ g_value_init(value, WALLS_MODEL(walls_model)->column_types[column]);
+
+ walls_model = WALLS_MODEL(tree_model);
+
+ record = (WallsModelRecord*)iter->user_data;
+
+ g_return_if_fail(record != NULL);
+
+ //if(record->pos >= walls_model->num_rows)
+ // g_return_if_reached();
+
+ if(record->type == WALLS_MODEL_TYPE_DIR) {
+ g_value_set_string(value, record->dir.name);
+ return;
+ }
+
+ switch(column) {
+ case WALLS_MODEL_COL_NAME:
+ g_value_set_string(value, record->wall.filepath);
+ break;
+ case WALLS_MODEL_COL_ID:
+ g_value_set_uint64(value, record->wall.id);
+ break;
+ case WALLS_MODEL_COL_SIZE:
+ g_value_set_int(value, record->wall.size);
+ break;
+ case WALLS_MODEL_COL_WIDTH:
+ g_value_set_int(value, record->wall.width);
+ break;
+ case WALLS_MODEL_COL_HEIGHT:
+ g_value_set_int(value, record->wall.height);
+ break;
+ }
+}
+
+static gboolean walls_model_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ WallsModelRecord *record, *nextrecord;
+ WallsModel *walls_model;
+ GArray *array;
+
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), FALSE);
+
+ if(iter == NULL || iter->user_data == NULL)
+ return FALSE;
+
+ walls_model = WALLS_MODEL(tree_model);
+
+ record = (WallsModelRecord*)iter->user_data;
+
+ WALLS_MODEL_ENSURE_CHILDREN(record);
+
+ array = (record->parent ? record->parent->children : walls_model->rows);
+
+ if((record->pos + 1) >= array->len)
+ return FALSE;
+
+ nextrecord = &g_array_index(array, WallsModelRecord, record->pos + 1);
+
+ g_assert(nextrecord != NULL);
+ g_assert(nextrecord->pos == (record->pos + 1));
+
+ iter->stamp = walls_model->stamp;
+ iter->user_data = nextrecord;
+
+ return TRUE;
+}
+
+static gboolean walls_model_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) {
+ WallsModel *walls_model;
+ WallsModelRecord *parent_record;
+
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), FALSE);
+
+ walls_model = WALLS_MODEL(tree_model);
+
+ if(!parent) {
+ if(walls_model->num_rows == 0)
+ return FALSE;
+ iter->stamp = walls_model->stamp;
+ iter->user_data = &g_array_index(walls_model->rows, WallsModelRecord, 0);
+ return TRUE;
+ }
+
+ //g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
+
+ parent_record = (WallsModelRecord*)parent->user_data;
+
+ if(parent_record->type == WALLS_MODEL_TYPE_WALL)
+ return FALSE;
+
+ WALLS_MODEL_ENSURE_CHILDREN(parent_record);
+
+ if(parent_record->children->len == 0)
+ return FALSE;
+
+ iter->stamp = walls_model->stamp;
+ iter->user_data = &g_array_index(parent_record->children, WallsModelRecord, 0);
+
+ return TRUE;
+}
+
+static gboolean walls_model_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ WallsModelRecord *record;
+
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), FALSE);
+
+ record = (WallsModelRecord*)iter->user_data;
+
+ if(record->type == WALLS_MODEL_TYPE_DIR) {
+ //return (record->children->len > 0 ? TRUE : FALSE);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+static gint walls_model_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter) {
+ WallsModel *walls_model;
+ WallsModelRecord *record;
+ GArray *array;
+ int n;
+
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), -1);
+ g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0);
+
+ walls_model = WALLS_MODEL(tree_model);
+
+ if(!iter) {
+ return walls_model->num_rows;
+ }
+
+ record = (WallsModelRecord*)iter->user_data;
+
+ if(record->type == WALLS_MODEL_TYPE_WALL) {
+ return 0;
+ }
+
+ //array = NULL;
+ //g_return_val_if_fail(db_get_wallpapers(record->dir.dirid, &array), -1);
+ //for(n = 0; (&g_array_index(array, struct wallpaper_t, n))->filepath; n++);
+
+ WALLS_MODEL_ENSURE_CHILDREN(record);
+
+ return record->children->len;
+
+ return n;
+}
+
+static gboolean walls_model_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n) {
+ WallsModelRecord *record, *parent_record;
+ WallsModel *walls_model;
+
+ g_return_val_if_fail(MODEL_IS_WALLS(tree_model), FALSE);
+
+ walls_model = WALLS_MODEL(tree_model);
+
+ if(!parent) {
+ if(n >= walls_model->num_rows)
+ return FALSE;
+ record = &g_array_index(walls_model->rows, WallsModelRecord, n);
+
+ iter->stamp = walls_model->stamp;
+ iter->user_data = record;
+
+ return TRUE;
+ }
+
+ //if(n >= walls_model->num_rows)
+ // return FALSE;