diff options
-rw-r--r-- | .gitignore | 7 | ||||
-rw-r--r-- | SConstruct | 38 | ||||
-rw-r--r-- | db.c | 312 | ||||
-rw-r--r-- | db.h | 29 | ||||
-rw-r--r-- | main.c | 9 | ||||
-rw-r--r-- | wallpapers.c | 82 | ||||
-rw-r--r-- | walls.ui | 140 | ||||
-rw-r--r-- | walls_model.c | 479 | ||||
-rw-r--r-- | walls_model.h | 63 | ||||
-rw-r--r-- | window_main.c | 152 | ||||
-rw-r--r-- | window_main.h | 8 |
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 @@ -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; +} @@ -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 @@ -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; |