diff options
-rw-r--r-- | SConstruct | 3 | ||||
-rw-r--r-- | db.c | 160 | ||||
-rw-r--r-- | db.h | 10 | ||||
-rw-r--r-- | tags.ui | 157 | ||||
-rw-r--r-- | text_input_dialog.c | 35 | ||||
-rw-r--r-- | text_input_dialog.h | 10 | ||||
-rw-r--r-- | thumbnails.c | 14 | ||||
-rw-r--r-- | walls.ui | 4 | ||||
-rw-r--r-- | window_main.c | 133 | ||||
-rw-r--r-- | window_tag.c | 204 | ||||
-rw-r--r-- | window_tag.h | 15 |
11 files changed, 725 insertions, 20 deletions
@@ -23,7 +23,7 @@ def build_ui(target, source, env): 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') + f.write('gchar *%s_string = \n' % str(target[0]).rsplit('.', 1)[0]) for line in open(str(source[0]), 'r'): f.write('"%s"\n' % line.replace('"', '\\"').strip()) f.write(';\n') @@ -33,6 +33,7 @@ ui_builder = Builder(action = build_ui) env['BUILDERS']['walls_ui'] = ui_builder env.walls_ui('walls_ui.h', 'walls.ui') +env.walls_ui('tags_ui.h', 'tags.ui') walls = env.Program('walls', Glob('*.c')) destdir = ARGUMENTS.get('DESTDIR', '') @@ -323,6 +323,44 @@ int db_get_wallpaper_data(sqlite_uint64 id, struct wallpaper_t *wall) { return 1; } +int db_get_wall_tags(sqlite_uint64 wallid, GArray **array) { + struct tag_t temp, *temp2; + 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); + + if(rc != SQLITE_OK) { + return 0; + } + + rc = sqlite3_bind_int64(stmt, 1, wallid); + 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.id = sqlite3_column_int64(stmt, 0); + temp.name = g_strdup(sqlite3_column_text(stmt, 1)); + 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_wallpapers(sqlite_uint64 dirid, GArray **array) { struct wallpaper_t temp, *temp2; sqlite3_stmt *stmt; @@ -357,8 +395,130 @@ int db_get_wallpapers(sqlite_uint64 dirid, GArray **array) { temp2 = &g_array_index(*array, struct wallpaper_t, i); g_free(temp2->filepath); } + g_array_free(*array, TRUE); + return 0; + } + + return 1; +} + +sqlite_uint64 db_add_tag(const char *name) { + sqlite3_stmt *stmt; + int rc; + + rc = sqlite3_prepare_v2(db, "INSERT INTO tag (name) VALUES (?)", -1, &stmt, NULL); + + if(rc != SQLITE_OK) { + return 0; + } + + rc = sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC); + 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_tags_all(GArray **array) { + struct tag_t temp, *temp2; + sqlite3_stmt *stmt; + int rc; + + rc = sqlite3_prepare_v2(db, "SELECT id, name FROM tag 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(sqlite3_column_text(stmt, 1)); + temp.id = 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 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; + + rc = sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO walltags (wallid, tagid) VALUES (?, ?)", -1, &stmt, NULL); + + if(rc != SQLITE_OK) { + return 0; + } + + rc = sqlite3_bind_int64(stmt, 1, wallid); + if(rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return 0; + } + + rc = sqlite3_bind_int64(stmt, 2, tagid); + if(rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return 0; + } + + rc = sqlite3_step(stmt); + + sqlite3_finalize(stmt); + if(rc == SQLITE_DONE) { + return 1; + } else { + return 0; + } +} + +int db_remove_wall_tag(sqlite_uint64 wallid, sqlite_uint64 tagid) { + sqlite3_stmt *stmt; + int rc; + + rc = sqlite3_prepare_v2(db, "DELETE FROM walltags WHERE wallid = ? AND tagid = ?", -1, &stmt, NULL); + + if(rc != SQLITE_OK) { + return 0; + } + + rc = sqlite3_bind_int64(stmt, 1, wallid); + if(rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return 0; + } + + rc = sqlite3_bind_int64(stmt, 2, tagid); + if(rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return 0; + } + + rc = sqlite3_step(stmt); + + sqlite3_finalize(stmt); + if(rc == SQLITE_DONE) { + return 1; + } else { + return 0; + } +} @@ -17,6 +17,11 @@ struct wallpaper_t { int height; }; +struct tag_t { + gchar *name; + sqlite_uint64 id; +}; + int db_open(); void db_close(); sqlite_uint64 db_add_directory(const char*, sqlite_uint64); @@ -26,6 +31,11 @@ int db_get_directories(sqlite_uint64, GArray**); sqlite_uint64 db_add_wallpaper(const char*, sqlite_uint64, int, int, int); sqlite_uint64 db_get_wallpaper(const char*); int db_get_wallpaper_data(sqlite_uint64, struct wallpaper_t*); +int db_get_wall_tags(sqlite_uint64, GArray**); int db_get_wallpapers(sqlite_uint64, GArray**); +sqlite_uint64 db_add_tag(const char*); +int db_get_tags_all(GArray**); +int db_add_wall_tag(sqlite_uint64, sqlite_uint64); +int db_remove_wall_tag(sqlite_uint64, sqlite_uint64); #endif @@ -0,0 +1,157 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="tagsdialog"> + <property name="border_width">5</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <signal name="destroy" handler="on_tagsdialog_destroy"/> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <object class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <child> + <object class="GtkTreeView" id="tagview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkVButtonBox" id="vbuttonbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="layout_style">spread</property> + <child> + <object class="GtkButton" id="addbtn"> + <property name="label">gtk-add</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_tags_addbtn_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="rembtn"> + <property name="label">gtk-remove</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_tags_rembtn_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="selallbtn"> + <property name="label">gtk-select-all</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_tags_selallbtn_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="selnonebtn"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <signal name="clicked" handler="on_tags_selnonebtn_clicked"/> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="gtk_false"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button1"> + <property name="label">gtk-apply</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="gtk_true"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button2</action-widget> + <action-widget response="-10">button1</action-widget> + </action-widgets> + </object> +</interface> diff --git a/text_input_dialog.c b/text_input_dialog.c new file mode 100644 index 0000000..da18b24 --- /dev/null +++ b/text_input_dialog.c @@ -0,0 +1,35 @@ +#include "text_input_dialog.h" + +GtkDialog *text_input_dialog_new(GtkWidget *parent, const gchar *msg) { + GtkWidget *dialog; + GtkWidget *entry; + GtkWidget *vbox; + + dialog = gtk_message_dialog_new(GTK_WINDOW(parent), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, msg); + entry = gtk_entry_new(); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_end(GTK_BOX(vbox), entry, FALSE, FALSE, 0); + + gtk_widget_show(entry); + + return GTK_DIALOG(dialog); +} + +gchar *text_input_dialog_get_text(GtkWidget *dialog) { + GtkWidget *vbox; + GtkWidget *entry; + GList *list; + GtkEntryBuffer *buf; + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + list = gtk_container_get_children(GTK_CONTAINER(vbox)); + entry = NULL; + for(int i = 0; i < g_list_length(list); i++) + if(GTK_IS_ENTRY(g_list_nth_data(list, i))) + entry = GTK_WIDGET(g_list_nth_data(list, i)); + g_assert(entry != NULL); + buf = gtk_entry_get_buffer(GTK_ENTRY(entry)); + return g_strdup(gtk_entry_buffer_get_text(buf)); +} diff --git a/text_input_dialog.h b/text_input_dialog.h new file mode 100644 index 0000000..6edf6ac --- /dev/null +++ b/text_input_dialog.h @@ -0,0 +1,10 @@ +#ifndef _TEXT_INPUT_DIALOG_H_ +#define _TEXT_INPUT_DIALOG_H_ + +#include <gtk/gtk.h> +#include <glib.h> + +GtkDialog *text_input_dialog_new(GtkWidget*, const gchar*); +gchar *text_input_dialog_get_text(GtkWidget*); + +#endif diff --git a/thumbnails.c b/thumbnails.c index e88c19d..33b0573 100644 --- a/thumbnails.c +++ b/thumbnails.c @@ -98,20 +98,6 @@ GdkPixbuf *get_thumbnail(const gchar *filepath) { return pb2; } -static void fill_wall_list(GtkListStore *liststore, GArray *array) { - GtkTreeIter iter; - - for(int i = 0; i < array->len; i++) { - struct wallpaper_t *wall; - wall = &g_array_index(array, struct wallpaper_t, i); - - gtk_list_store_append(liststore, &iter); - gtk_list_store_set(liststore, &iter, 0, NULL, 1, wall->filepath, -1); - g_free(wall->filepath); - } - g_array_free(array, TRUE); -} - gpointer add_thumbs_thread(gpointer data) { GtkListStore *liststore; GtkTreeIter iter; @@ -134,9 +134,11 @@ <object class="GtkIconView" id="thumbview"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="selection_mode">browse</property> + <property name="selection_mode">multiple</property> <property name="orientation">horizontal</property> + <signal name="button_press_event" handler="on_thumbview_button_press_event"/> <signal name="selection_changed" handler="on_thumbview_selection_changed"/> + <signal name="popup_menu" handler="on_thumbview_popup_menu"/> </object> </child> </object> diff --git a/window_main.c b/window_main.c index 38e1899..a1832e4 100644 --- a/window_main.c +++ b/window_main.c @@ -1,4 +1,3 @@ -//#include <unistd.h> #include <string.h> #include <glib/gstdio.h> @@ -12,6 +11,7 @@ #include "wallpapers.h" #include "thumbnails.h" #include "walls_conf.h" +#include "window_tag.h" struct wallpaper_t *cur_wall = NULL; GdkPixbuf *orig_pixbuf = NULL; @@ -217,7 +217,7 @@ static void fill_wall_list(GtkListStore *liststore, GArray *array) { wall = &g_array_index(array, struct wallpaper_t, i); gtk_list_store_append(liststore, &iter); - gtk_list_store_set(liststore, &iter, 0, NULL, 1, wall->filepath, -1); + gtk_list_store_set(liststore, &iter, 0, NULL, 1, wall->filepath, 2, wall->id,-1); g_free(wall->filepath); } g_array_free(array, TRUE); @@ -237,7 +237,7 @@ void on_foldtree_selection_changed(GtkTreeSelection *treeselection, gpointer use if(!db_get_wallpapers(dir->dirid, &array)) return; - liststore = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING); + liststore = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT64); fill_wall_list(liststore, array); gtk_icon_view_set_model(thumbview, GTK_TREE_MODEL(liststore)); @@ -308,6 +308,131 @@ void on_add_dir_action_activate(GtkAction *action, gpointer user_data) { gtk_widget_destroy(dialog); } +static void thumbview_selected_foreach(GtkIconView *icon_view, GtkTreePath *path, gpointer user_data) { + GtkTreeIter iter; + GtkTreeModel *model; + GArray *array; + GValue value = {0}; + guint64 id; + + model = gtk_icon_view_get_model(icon_view); + + gtk_tree_model_get_iter(model, &iter, path); + + gtk_tree_model_get_value(model, &iter, 2, &value); + + array = user_data; + id = g_value_get_uint64(&value); + g_array_append_val(array, id); +} + +inline static void update_wall_tags(GArray *wallarray, guint64 tagid, gboolean active) { + guint64 wallid; + for(int i = 0; i < wallarray->len; i++) { + wallid = g_array_index(wallarray, guint64, i); + if(active) { + db_add_wall_tag(wallid, tagid); + } else { + db_remove_wall_tag(wallid, tagid); + } + } +} + +static void update_tags(GArray *wallarray, GtkListStore *liststore) { + GtkTreeIter iter; + GValue value = {0}; + gboolean active, inconsistent; + guint64 tagid; + + if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter)) { + g_error("Failed to get iter\n"); + 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) + 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); + + update_wall_tags(wallarray, tagid, active); + } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter)); +} + +void on_tags_activate(GtkMenuItem *menuitem, gpointer user_data) { + struct tagdialog_data_t *data; + GArray *array; + + array = g_array_new(FALSE, TRUE, sizeof(guint64)); + gtk_icon_view_selected_foreach(thumbview, thumbview_selected_foreach, array); + + 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)))); + } + + gtk_widget_destroy(GTK_WIDGET(data->dialog)); + + g_array_free(array, TRUE); +} + +inline static void thumbview_popup_add_items(GtkWidget *menu) { + GtkWidget *tags; + + tags = gtk_menu_item_new_with_label("Tags"); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), tags); + + g_signal_connect(G_OBJECT(tags), "activate", G_CALLBACK(on_tags_activate), NULL); + + gtk_widget_show(tags); +} + +static void do_thumbview_popup(GtkWidget *widget, GdkEventButton *event) { + GtkWidget *menu; + int button, event_time; + + menu = gtk_menu_new(); + g_signal_connect(menu, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL); + + thumbview_popup_add_items(menu); + + if(event) { + button = event->button; + event_time = event->time; + } else { + button = 0; + event_time = gtk_get_current_event_time(); + } + + gtk_menu_attach_to_widget(GTK_MENU(menu), widget, NULL); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time); +} + +gboolean on_thumbview_popup_menu(GtkWidget *widget, gpointer user_data) { + do_thumbview_popup(widget, NULL); +} + +gboolean on_thumbview_button_press_event(GtkWidget *widget, GdkEventButton *event) { + if(event->button == 3 && event->type == GDK_BUTTON_PRESS) { + do_thumbview_popup(widget, event); + return TRUE; + } + return FALSE; +} + static void save_window() { gint width, height; @@ -356,7 +481,7 @@ int gui_main(int argc, char **argv) { gtk_init(&argc, &argv); builder = gtk_builder_new(); - if(!gtk_builder_add_from_string(builder, ui_string, -1, &error)) { + if(!gtk_builder_add_from_string(builder, walls_ui_string, -1, &error)) { g_warning("%s", error->message); g_error_free(error); return 1; diff --git a/window_tag.c b/window_tag.c new file mode 100644 index 0000000..2209572 --- /dev/null +++ b/window_tag.c @@ -0,0 +1,204 @@ +#include "window_tag.h" +#include "tags_ui.h" +#include "db.h" +#include "text_input_dialog.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; + + 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); + + if(inconsistent) { + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, FALSE, -1); + } else { + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, !active, -1); + } +} + +/* TODO: Find a better way to do this. */ +inline static gboolean is_tag_inconsistent(struct tag_t *tag, GArray *walltags, gboolean *exists) { + gboolean res; + gint n_y, n_n; + GArray *walltag_single; + gboolean found; + struct tag_t *wall_tag; + + n_y = n_n = 0; + + for(int i = 0; i < walltags->len; i++) { + walltag_single = g_array_index(walltags, GArray*, i); + found = FALSE; + for(int j = 0; j < walltag_single->len; j++) { + wall_tag = &g_array_index(walltag_single, struct tag_t, j); + if(wall_tag->id == tag->id) { + n_y++; + found = TRUE; + break; + } + } + if(!found) + n_n++; + } + if(n_y == 0) + *exists = FALSE; + else + *exists = TRUE; + 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; + struct tag_t *tag; + gboolean inconsistent, exists; + struct tagdialog_data_t *data; + + data = user_data; + wallarray = data->wallarray; + + walltags = g_array_new(FALSE, FALSE, sizeof(GArray*)); + for(int i = 0; i < wallarray->len; i++) { + if(db_get_wall_tags(g_array_index(wallarray, guint64, i), &walltag_single)) { + g_array_append_val(walltags, walltag_single); + } else { + g_error("db_get_wall_tags failed\n"); + return; + } + } + + model = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_UINT64); + + 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"); + } + + 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) { + GtkTreeViewColumn *col1, *col2; + GtkCellRenderer *renderer; + + col1 = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col1, "Select"); + gtk_tree_view_append_column(tagview, col1); + + col2 = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col2, "Name"); + gtk_tree_view_append_column(tagview, col2); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(col1, renderer, FALSE); + gtk_tree_view_column_add_attribute(col1, renderer, "active", 0); + gtk_tree_view_column_add_attribute(col1, renderer, "inconsistent", 1); + g_signal_connect(renderer, "toggled", G_CALLBACK(on_tagview_cell_toggled), user_data); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_end(col2, renderer, TRUE); + gtk_tree_view_column_add_attribute(col2, renderer, "text", 2); + + tagview_create_model(tagview, user_data); +} + +void on_tags_addbtn_clicked(GtkButton *button, gpointer user_data) { + struct tagdialog_data_t *data; + GtkDialog *dialog; + gchar *s; + + data = user_data; + dialog = text_input_dialog_new(data->dialog, "Enter the name of the new tag:"); + if(gtk_dialog_run(dialog) == GTK_RESPONSE_OK) { + s = text_input_dialog_get_text(GTK_WIDGET(dialog)); + if(db_add_tag(s)) + tagview_create_model(GTK_TREE_VIEW(data->tagview), user_data); + else + g_warning("Failed to add tag \"%s\"\n", s); + g_free(s); + } + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + +void on_tags_rembtn_clicked(GtkButton *button, gpointer user_data) { +} + +void on_tags_selallbtn_clicked(GtkButton *button, gpointer user_data) { +} + +void on_tags_selnonebtn_clicked(GtkButton *button, gpointer user_data) { +} + +void on_tagsdialog_destroy(GtkObject *object, gpointer user_data) { + g_free(user_data); +} + +struct tagdialog_data_t *window_tagview_new(GtkWidget *parent, GArray* array) { + GtkBuilder *builder; + GtkDialog *dialog; + GtkTreeView *tagview; + GtkWidget *content; + GtkButton *selnonebtn; + GtkImage *image; + GError *error = NULL; + struct tagdialog_data_t *data; + + builder = gtk_builder_new(); + if(!gtk_builder_add_from_string(builder, tags_ui_string, -1, &error)) { + g_warning("%s", error->message); + g_error_free(error); + return NULL; + } + + dialog = GTK_DIALOG(gtk_builder_get_object(builder, "tagsdialog")); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent)); + gtk_window_set_default_size(GTK_WINDOW(dialog), 300, 300); + + tagview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "tagview")); + + selnonebtn = GTK_BUTTON(gtk_builder_get_object(builder, "selnonebtn")); + gtk_button_set_label(selnonebtn, "Select None"); + + image = GTK_IMAGE(gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON)); + gtk_button_set_image(selnonebtn, GTK_WIDGET(image)); + + data = g_malloc(sizeof(struct tagdialog_data_t)); + data->dialog = GTK_WIDGET(dialog); + data->tagview = GTK_WIDGET(tagview); + data->wallarray = array; + + tagview_init(tagview, data); + + gtk_builder_connect_signals(builder, data); + + return data; +} diff --git a/window_tag.h b/window_tag.h new file mode 100644 index 0000000..96fceac --- /dev/null +++ b/window_tag.h @@ -0,0 +1,15 @@ +#ifndef _WINDOW_TAG_H_ +#define _WINDOW_TAG_H_ + +#include <gtk/gtk.h> +#include <glib.h> + +struct tagdialog_data_t { + GtkWidget *dialog; + GtkWidget *tagview; + GArray *wallarray; +}; + +struct tagdialog_data_t *window_tagview_new(GtkWidget*, GArray*); + +#endif |