#include #include #include "window_main.h" #include "walls_ui.h" #include "db.h" #include "browse_model.h" #include "wallpapers.h" #include "thumbnails.h" #include "walls_conf.h" #include "window_tag.h" #include "preload.h" #include "window_config.h" enum zoom_mode_t { ZOOM_BESTFIT, ZOOM_NORMAL, ZOOM_BESTFIT_CUSTOM, ZOOM_NORMAL_CUSTOM, }; struct wallpaper_t *cur_wall = NULL; GdkPixbuf *orig_pixbuf = NULL; gint last_width = 0, last_height = 0, hpane_last_pos = 0; guint hpane_source_id = 0; gboolean layout_dragging = FALSE; gdouble layout_dragx, layout_dragy; enum zoom_mode_t zoom_mode = ZOOM_BESTFIT; gdouble zoom_factor = 1.0; GtkWindow *window; GtkImage *image = NULL; GtkWidget *layout = NULL; GtkStatusbar *statusbar; GtkIconView *thumbview; GtkWidget *window_hpane, *window_vpane, *foldtree, *tagview, *view_toolbar, *zoom_toolbar, *zoom_bestfit, *zoom_normal; GThread *wallpaper_thread = NULL; gchar *cur_wall_msg = NULL; guint image_context; void on_foldtree_selection_changed(GtkTreeSelection *treeselection, gpointer user_data); void on_main_tagview_cell_toggled(GtkCellRendererToggle *cell_renderer, gchar *path_string, gpointer user_data); inline static void foldtree_create_model(GtkTreeView *foldtree) { GtkTreeModel *tree_model; tree_model = GTK_TREE_MODEL(browse_model_new()); gtk_tree_view_set_model(foldtree, GTK_TREE_MODEL(tree_model)); } inline static void foldtree_init(GtkTreeView *foldtree) { GtkTreeViewColumn *col1; GtkCellRenderer *renderer; GtkTreeSelection *selection; col1 = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col1, "Name"); gtk_tree_view_append_column(foldtree, col1); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(col1, renderer, TRUE); gtk_tree_view_column_add_attribute(col1, renderer, "text", 0); foldtree_create_model(foldtree); selection = gtk_tree_view_get_selection(foldtree); g_signal_connect(selection, "changed", G_CALLBACK(on_foldtree_selection_changed), foldtree); } inline static void tagview_create_model(GtkTreeView *tagview) { GtkListStore *model; GArray *array; struct tag_t *tag; GtkTreeIter iter; model = gtk_list_store_new(3, 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); gtk_list_store_set(model, &iter, 0, FALSE, 1, g_strdup(tag->name), 2, tag->id, -1); g_free(tag->name); } g_array_free(array, TRUE); } gtk_tree_view_set_model(tagview, GTK_TREE_MODEL(model)); } inline static void tagview_init(GtkTreeView *tagview) { GtkTreeViewColumn *col1, *col2; GtkCellRenderer *renderer; col1 = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col1, "Show"); 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); g_signal_connect(renderer, "toggled", G_CALLBACK(on_main_tagview_cell_toggled), 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); tagview_create_model(tagview); } static void set_resize_msg() { gchar *msg; gtk_statusbar_pop(statusbar, image_context); if(zoom_factor == 0) { msg = g_strdup(cur_wall_msg); } else { msg = g_strdup_printf("%s (%d %%)", cur_wall_msg, (gint)(zoom_factor * 100.0)); } gtk_statusbar_push(statusbar, image_context, msg); g_free(msg); } static void set_image_pixbuf_resized(GdkPixbuf *pb, gint win_width, gint win_height, gint width, gint height, gdouble ratio) { set_resize_msg(ratio); gtk_widget_set_size_request(GTK_WIDGET(image), width, height); gtk_widget_set_uposition(GTK_WIDGET(image), (width < win_width ? win_width / 2 - width / 2 : 0), (height < win_height ? win_height / 2 - height / 2 : 0)); gtk_image_set_from_pixbuf(image, pb); gtk_layout_set_size(GTK_LAYOUT(layout), win_width, win_height); } static void layout_center(gint width, gint height) { GtkAdjustment *hadj, *vadj; gdouble hupper, hpagesize, vupper, vpagesize; hadj = gtk_layout_get_hadjustment(GTK_LAYOUT(layout)); vadj = gtk_layout_get_vadjustment(GTK_LAYOUT(layout)); hupper = gtk_adjustment_get_upper(GTK_ADJUSTMENT(hadj)); hpagesize = gtk_adjustment_get_page_size(GTK_ADJUSTMENT(hadj)); vupper = gtk_adjustment_get_upper(GTK_ADJUSTMENT(vadj)); vpagesize = gtk_adjustment_get_page_size(GTK_ADJUSTMENT(vadj)); gtk_adjustment_set_value(GTK_ADJUSTMENT(hadj), hupper / 2 - hpagesize / 2); gtk_adjustment_set_value(GTK_ADJUSTMENT(vadj), vupper / 2 - vpagesize / 2); } static void set_image_pixbuf_normal(GdkPixbuf *pb, gint win_width, gint win_height, gint width, gint height) { set_resize_msg(); gtk_widget_set_size_request(GTK_WIDGET(image), width, height); gtk_widget_set_uposition(GTK_WIDGET(image), (width < win_width ? win_width / 2 - width / 2 : 0), (height < win_height ? win_height / 2 - height / 2 : 0)); gtk_image_set_from_pixbuf(image, pb); gtk_layout_set_size(GTK_LAYOUT(layout), width, height); layout_center(width, height); } static void set_image_pixbuf() { GdkWindow *window; GdkPixbuf *pb; gint win_width, win_height, width, height; gdouble ratio; if(!orig_pixbuf) return; window = gtk_widget_get_window(layout); gdk_drawable_get_size(window, &win_width, &win_height); if(zoom_mode == ZOOM_BESTFIT) { pb = resize_pixbuf(orig_pixbuf, win_width, win_height, &width, &height, &ratio); if(!pb) return; zoom_factor = ratio; set_image_pixbuf_resized(pb, win_width, win_height, width, height, ratio); g_object_unref(pb); } else if(zoom_mode == ZOOM_NORMAL) { set_image_pixbuf_normal(orig_pixbuf, win_width, win_height, gdk_pixbuf_get_width(orig_pixbuf), gdk_pixbuf_get_height(orig_pixbuf)); zoom_factor = 1.; } else if(zoom_mode >= ZOOM_BESTFIT_CUSTOM) { pb = zoom_pixbuf(orig_pixbuf, zoom_factor); if(!pb) return; width = gdk_pixbuf_get_width(pb); height = gdk_pixbuf_get_height(pb); set_image_pixbuf_normal(pb, win_width, win_height, width, height); set_resize_msg(zoom_factor); g_object_unref(pb); } } void on_window_size_allocate(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { if(orig_pixbuf && (allocation->width != last_width || allocation->height != last_height)) { preload_clear_sized(); set_image_pixbuf(); last_width = allocation->width; last_height = allocation->height; } } static gboolean hpane_resize_idle_func(gpointer data) { gint pos; pos = gtk_paned_get_position(GTK_PANED(data)); if(pos != hpane_last_pos) { preload_clear_sized(); set_image_pixbuf(); hpane_last_pos = pos; return TRUE; /* Ensure that we'll keep checking until we're done resizing. */ } hpane_source_id = 0; return FALSE; } void on_window_hpane_resized(GObject *gobject, GParamSpec *pspec, gpointer user_data) { GSource *src; /* Don't let hpane_resize_idle_func run until the user has stopped moving the hpane (no movement for 100 ms). */ if(hpane_source_id > 0) { src = g_main_context_find_source_by_id(NULL, hpane_source_id); if(src) g_source_destroy(src); } hpane_source_id = g_timeout_add(100, hpane_resize_idle_func, (gpointer)gobject); } static void walls_set_window_title() { gchar *msg; msg = g_strdup_printf("walls - %s", cur_wall_msg); gtk_window_set_title(GTK_WINDOW(window), msg); g_free(msg); } static void load_pixbuf(const gchar *filepath) { GError *error = NULL; sqlite_uint64 wallid; struct wallpaper_t *wall; struct preload_hash_table_value_t *pt; gchar *base; GdkWindow *window; gint win_width, win_height; if(orig_pixbuf) g_object_unref(orig_pixbuf); pt = preload_get(filepath); if(pt) orig_pixbuf = gdk_pixbuf_copy(pt->pb_orig); else orig_pixbuf = NULL; if(!orig_pixbuf) orig_pixbuf = gdk_pixbuf_new_from_file(filepath, &error); if(!orig_pixbuf) { g_warning("%s", error->message); g_error_free(error); return; } if(cur_wall) { g_free(cur_wall->filepath); g_free(cur_wall); cur_wall = NULL; } wallid = db_get_wallpaper(filepath); if(wallid) { wall = g_malloc(sizeof(struct wallpaper_t)); if(db_get_wallpaper_data(wallid, wall)) { cur_wall = wall; } else { g_free(wall->filepath); g_free(wall); } } if(cur_wall) { base = g_path_get_basename(cur_wall->filepath); if(cur_wall_msg) g_free(cur_wall_msg); cur_wall_msg = g_strdup_printf("%s: %dx%d", base, cur_wall->width, cur_wall->height); g_free(base); walls_set_window_title(); } if(pt && pt->pb) { window = gtk_widget_get_window(layout); gdk_drawable_get_size(window, &win_width, &win_height); if(zoom_mode == ZOOM_BESTFIT) { zoom_factor = pt->ratio; set_image_pixbuf_resized(pt->pb, win_width, win_height, pt->width, pt->height, pt->ratio); } else { zoom_factor = 1.0; set_image_pixbuf_normal(pt->pb_orig, win_width, win_height, gdk_pixbuf_get_width(pt->pb_orig), gdk_pixbuf_get_height(pt->pb_orig)); } } else set_image_pixbuf(); } /** * Load wallpapers from array into liststore. */ 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); gchar *filesize = g_format_size_for_display(wall->size); gchar *tooltip = g_strdup_printf("File: %s\nResolution: %dx%d\nFilesize: %s", wall->filepath, wall->width, wall->height, filesize); g_free(filesize); gtk_list_store_append(liststore, &iter); gtk_list_store_set(liststore, &iter, 0, NULL, 1, wall->filepath, 2, wall->id, 3, tooltip, -1); g_free(wall->filepath); } g_array_free(array, TRUE); } static void start_thumb_thread(GtkListStore *liststore) { GError *error = NULL; if(thumb_thread) { thumb_thread_exit = TRUE; g_thread_join(thumb_thread); } thumb_thread = g_thread_create(add_thumbs_thread, liststore, TRUE, &error); if(!thumb_thread) { g_warning("Failed to start the thumbnail thread: %s", error->message); g_error_free(error); } } /** * Load wallpapers from the currently selected folder. */ static void display_from_foldtree(GtkTreeSelection *treeselection) { GtkTreeModel *model, *old_model; GtkTreeIter iter; struct directory_t *dir; GArray *array; GtkListStore *liststore; if(treeselection == NULL) treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(foldtree)); if(gtk_tree_selection_get_selected(treeselection, &model, &iter)) { browse_model_get_dir_record(model, &iter, &dir); if(!db_get_wallpapers(dir->dirid, &array)) return; liststore = gtk_list_store_new(4, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_STRING); fill_wall_list(liststore, array); old_model = gtk_icon_view_get_model(thumbview); gtk_icon_view_set_model(thumbview, GTK_TREE_MODEL(liststore)); start_thumb_thread(liststore); if(old_model) gtk_list_store_clear(GTK_LIST_STORE(old_model)); } } void on_foldtree_selection_changed(GtkTreeSelection *treeselection, gpointer user_data) { display_from_foldtree(treeselection); } void on_thumbview_selection_changed(GtkIconView *iconview, gpointer user_data) { GList *list; GValue value = {0}; const gchar *filename; GtkTreePath *path; GtkTreeModel *model; GtkTreeIter iter; GdkWindow *window; gint win_width, win_height; list = gtk_icon_view_get_selected_items(iconview); if(list) { if(g_list_length(list) == 1) { if(zoom_mode == ZOOM_BESTFIT_CUSTOM) zoom_mode = ZOOM_BESTFIT; else if(zoom_mode == ZOOM_NORMAL_CUSTOM) zoom_mode = ZOOM_NORMAL; path = g_list_nth_data(list, 0); model = gtk_icon_view_get_model(iconview); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get_value(model, &iter, 1, &value); filename = g_value_get_string(&value); load_pixbuf(filename); g_value_unset(&value); } g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); window = gtk_widget_get_window(layout); gdk_drawable_get_size(window, &win_width, &win_height); preload_start_thread(GTK_WIDGET(thumbview), win_width, win_height); } } gboolean on_thumbview_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data) { GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; if(gtk_icon_view_get_tooltip_context(GTK_ICON_VIEW(widget), &x, &y, keyboard_mode, &model, &path, &iter)) { GValue value = {0}; gtk_tree_model_get_value(model, &iter, 2, &value); guint64 wallid = g_value_get_uint64(&value); g_value_unset(&value); GArray *tags; gchar *tagstr = NULL; if(db_get_wall_tags(wallid, &tags)) { GString *str = g_string_new(NULL); for(int i = 0; i < tags->len; i++) { struct tag_t *tag = &g_array_index(tags, struct tag_t, i); if(i > 0) g_string_append_printf(str, ", %s", tag->name); else g_string_append(str, tag->name); g_free(tag->name); } g_array_free(tags, TRUE); if(str->len > 0) { tagstr = g_string_free(str, FALSE); } else { g_string_free(str, TRUE); } } gtk_tree_model_get_value(model, &iter, 3, &value); const gchar *pretext = g_value_get_string(&value); gchar *text; if(tagstr) { text = g_strdup_printf("%s\nTags: %s", pretext, tagstr); g_free(tagstr); } else { text = g_strdup_printf("%s", pretext); } g_value_unset(&value); gtk_tooltip_set_text(tooltip, text); g_free(text); return TRUE; } return FALSE; } /** * Load wallpapers from the currently selected tags. */ void display_from_tagview() { GtkTreeIter iter; GtkTreeModel *model, *old_model; GtkListStore *liststore; GValue value = {0}; gboolean active; GArray *tagarray, *wallarray; guint64 id; model = gtk_tree_view_get_model(GTK_TREE_VIEW(tagview)); if(!gtk_tree_model_get_iter_first(model, &iter)) return; 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) continue; gtk_tree_model_get_value(model, &iter, 2, &value); id = g_value_get_uint64(&value); g_value_unset(&value); g_array_append_val(tagarray, id); } while(gtk_tree_model_iter_next(model, &iter)); if(!db_get_walls_by_tags(tagarray, &wallarray)) { g_array_free(tagarray, TRUE); g_warning("Could not fetch walls by tags\n"); return; } liststore = gtk_list_store_new(4, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_STRING); fill_wall_list(liststore, wallarray); old_model = gtk_icon_view_get_model(thumbview); gtk_icon_view_set_model(thumbview, GTK_TREE_MODEL(liststore)); start_thumb_thread(liststore); if(old_model) gtk_list_store_clear(GTK_LIST_STORE(old_model)); } 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); display_from_tagview(); } /** * Thread function for directory adding. */ gpointer add_dir_thread(gpointer data) { gchar *directory; gboolean r = FALSE; directory = (gchar*)data; r = wallpapers_add_dir(directory, 0, statusbar, FALSE); g_free(directory); if(r) { gdk_threads_enter(); foldtree_create_model(GTK_TREE_VIEW(foldtree)); gdk_threads_leave(); } wallpaper_thread = NULL; return NULL; } /** * Thread function for recursive directory adding. */ gpointer add_dir_rec_thread(gpointer data) { gchar *directory; gboolean r = FALSE; directory = (gchar*)data; r = wallpapers_add_dir(directory, 0, statusbar, TRUE); g_free(directory); if(r) { gdk_threads_enter(); foldtree_create_model(GTK_TREE_VIEW(foldtree)); gdk_threads_leave(); } wallpaper_thread = NULL; return NULL; } /** * Thread function for removing missing files. */ gpointer remove_missing_thread(gpointer data) { struct directory_t *dir, temp; dir = data; temp.dirid = dir->dirid; temp.name = g_strdup(dir->name); wallpapers_remove_missing(&temp, statusbar, FALSE); wallpaper_thread = NULL; return NULL; } /** * Thread function for recursive removal of missing files. */ gpointer remove_missing_rec_thread(gpointer data) { struct directory_t *dir, temp; dir = data; temp.dirid = dir->dirid; temp.name = g_strdup(dir->name); wallpapers_remove_missing(&temp, statusbar, TRUE); wallpaper_thread = NULL; return NULL; } void on_add_dir_action_activate(GtkAction *action, gpointer user_data) { GtkWidget *dialog; GError *error; if(wallpaper_thread) return; dialog = gtk_file_chooser_dialog_new("Choose Directory", NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { char *directory; directory = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); wallpaper_thread = g_thread_create(add_dir_thread, directory, FALSE, &error); if(!wallpaper_thread) { g_warning("%s", error->message); g_error_free(error); } } gtk_widget_destroy(dialog); } void on_foldtree_update_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeSelection *treeselection; GtkTreeModel *model; GtkTreeIter iter; struct directory_t *dir; GError *error; if(wallpaper_thread) return; treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(foldtree)); if(gtk_tree_selection_get_selected(treeselection, &model, &iter)) { browse_model_get_dir_record(model, &iter, &dir); /* Using user_data as a gboolean for recursive calls. */ wallpaper_thread = g_thread_create(user_data == NULL ? add_dir_thread : add_dir_rec_thread, g_strdup(dir->name), FALSE, &error); if(!wallpaper_thread) { g_warning("%s", error->message); g_error_free(error); } } } void on_foldtree_remove_missing_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeSelection *treeselection; GtkTreeModel *model; GtkTreeIter iter; struct directory_t *dir; GError *error; if(wallpaper_thread) return; treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(foldtree)); if(gtk_tree_selection_get_selected(treeselection, &model, &iter)) { browse_model_get_dir_record(model, &iter, &dir); wallpaper_thread = g_thread_create(user_data == NULL ? remove_missing_thread : remove_missing_rec_thread, dir, FALSE, &error); if(!wallpaper_thread) { g_warning("%s", error->message); g_error_free(error); } } } inline static void foldtree_popup_add_items(GtkWidget *menu) { GtkWidget *update, *update_rec, *remove_missing, *remove_missing_rec; GtkTreeSelection *selection; update = gtk_menu_item_new_with_label("Update"); update_rec = gtk_menu_item_new_with_label("Update (recursive)"); remove_missing = gtk_menu_item_new_with_label("Remove missing files"); remove_missing_rec = gtk_menu_item_new_with_label("Remove missing files (recursive)"); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(foldtree)); gtk_widget_set_sensitive(update, (gtk_tree_selection_count_selected_rows(selection) == 1 ? TRUE : FALSE)); gtk_widget_set_sensitive(update_rec, (gtk_tree_selection_count_selected_rows(selection) == 1 ? TRUE : FALSE)); gtk_widget_set_sensitive(remove_missing, (gtk_tree_selection_count_selected_rows(selection) == 1 ? TRUE : FALSE)); gtk_widget_set_sensitive(remove_missing_rec, (gtk_tree_selection_count_selected_rows(selection) == 1 ? TRUE : FALSE)); gtk_menu_shell_append(GTK_MENU_SHELL(menu), update); gtk_menu_shell_append(GTK_MENU_SHELL(menu), update_rec); gtk_menu_shell_append(GTK_MENU_SHELL(menu), remove_missing); gtk_menu_shell_append(GTK_MENU_SHELL(menu), remove_missing_rec); g_signal_connect(G_OBJECT(update), "activate", G_CALLBACK(on_foldtree_update_activate), (gpointer)FALSE); g_signal_connect(G_OBJECT(update_rec), "activate", G_CALLBACK(on_foldtree_update_activate), (gpointer)TRUE); g_signal_connect(G_OBJECT(remove_missing), "activate", G_CALLBACK(on_foldtree_remove_missing_activate), (gpointer)FALSE); g_signal_connect(G_OBJECT(remove_missing_rec), "activate", G_CALLBACK(on_foldtree_remove_missing_activate), (gpointer)TRUE); gtk_widget_show(update); gtk_widget_show(update_rec); gtk_widget_show(remove_missing); gtk_widget_show(remove_missing_rec); } static void do_foldtree_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); foldtree_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_foldtree_popup_menu(GtkWidget *widget, gpointer user_data) { do_foldtree_popup(widget, NULL); return TRUE; } gboolean on_foldtree_button_press_event(GtkWidget *widget, GdkEventButton *event) { if(event->button == 3 && event->type == GDK_BUTTON_PRESS) { do_foldtree_popup(widget, event); return TRUE; } return FALSE; } /** * Callback for adding selected wallpapers to an array. */ 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); } /** * Add or remove tags for wallpapers. */ 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); } } } /** * Update tags for wallpapers. */ static void update_tags(GArray *wallarray, GtkListStore *liststore) { GtkTreeIter iter; GValue value = {0}; gboolean active, inconsistent; guint64 tagid; /* No tags available. */ if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &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) 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; GList *list; tags = gtk_menu_item_new_with_label("Tags"); list = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(thumbview)); gtk_widget_set_sensitive(tags, (g_list_length(list) > 0 ? TRUE : FALSE)); g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); 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); return TRUE; } 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; } gboolean on_left_pages_switch_page(GtkNotebook *notebook, GtkNotebookTab page, guint page_num, gpointer user_data) { switch(page_num) { case 0: display_from_foldtree(NULL); break; case 1: tagview_create_model(GTK_TREE_VIEW(tagview)); display_from_tagview(); break; } return TRUE; } void on_view_activate(GtkMenuItem *menuitem, gpointer user_data) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(view_toolbar), gtk_widget_get_visible(zoom_toolbar)); } void on_zoom_mode_toggled(GtkToggleToolButton *toolbutton, gpointer user_data) { if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(zoom_bestfit))) { zoom_mode = ZOOM_BESTFIT; } else if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(zoom_normal))) { zoom_mode = ZOOM_NORMAL; } if(zoom_mode == ZOOM_BESTFIT || zoom_mode == ZOOM_NORMAL) { set_image_pixbuf(); } } void on_zoom_in_clicked(GtkToolButton *toolbutton, gpointer user_data) { if(zoom_mode == ZOOM_BESTFIT) zoom_mode = ZOOM_BESTFIT_CUSTOM; else if(zoom_mode == ZOOM_NORMAL) zoom_mode = ZOOM_NORMAL_CUSTOM; zoom_factor *= 1.25; set_image_pixbuf(); } void on_zoom_out_clicked(GtkToolButton *toolbutton, gpointer user_data) { if(zoom_mode == ZOOM_BESTFIT) zoom_mode = ZOOM_BESTFIT_CUSTOM; else if(zoom_mode == ZOOM_NORMAL) zoom_mode = ZOOM_NORMAL_CUSTOM; zoom_factor *= .80; set_image_pixbuf(); } gboolean on_layout_button_press_event(GtkWindow *widget, GdkEventButton *event, gpointer user_data) { GtkAdjustment *hadj, *vadj; if(event->button == 1 && zoom_mode != ZOOM_BESTFIT) { hadj = gtk_layout_get_hadjustment(GTK_LAYOUT(layout)); vadj = gtk_layout_get_vadjustment(GTK_LAYOUT(layout)); layout_dragging = TRUE; layout_dragx = event->x; layout_dragy = event->y; } return FALSE; } gboolean on_layout_button_release_event(GtkWindow *widget, GdkEventButton *event, gpointer user_data) { if(event->button == 1) { layout_dragging = FALSE; } return FALSE; } gboolean on_layout_scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer user_data) { GdkModifierType mask; gint x, y; if(event->type == GDK_SCROLL) { gdk_window_get_pointer(event->window, &x, &y, &mask); if(mask & GDK_CONTROL_MASK) { gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(zoom_bestfit), FALSE); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(zoom_normal), FALSE); if(zoom_mode == ZOOM_BESTFIT) zoom_mode = ZOOM_BESTFIT_CUSTOM; else if(zoom_mode == ZOOM_NORMAL) zoom_mode = ZOOM_NORMAL_CUSTOM; zoom_factor *= (event->direction == GDK_SCROLL_UP ? 1.25 : .80); set_image_pixbuf(); } if(mask & GDK_SHIFT_MASK) { if(event->direction == GDK_SCROLL_UP) event->direction = GDK_SCROLL_LEFT; else if(event->direction == GDK_SCROLL_DOWN) event->direction = GDK_SCROLL_RIGHT; } } return FALSE; } gboolean on_layout_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) { GtkAdjustment *hadj, *vadj; gdouble adjx, adjy; gdouble hupper, hpagesize, vupper, vpagesize; if(layout_dragging && event->type == GDK_MOTION_NOTIFY) { hadj = gtk_layout_get_hadjustment(GTK_LAYOUT(layout)); vadj = gtk_layout_get_vadjustment(GTK_LAYOUT(layout)); hupper = gtk_adjustment_get_upper(GTK_ADJUSTMENT(hadj)); hpagesize = gtk_adjustment_get_page_size(GTK_ADJUSTMENT(hadj)); vupper = gtk_adjustment_get_upper(GTK_ADJUSTMENT(vadj)); vpagesize = gtk_adjustment_get_page_size(GTK_ADJUSTMENT(vadj)); adjx = gtk_adjustment_get_value(GTK_ADJUSTMENT(hadj)) + (layout_dragx - event->x); adjy = gtk_adjustment_get_value(GTK_ADJUSTMENT(vadj)) + (layout_dragy - event->y); if(adjx > hupper - hpagesize) adjx = hupper - hpagesize; if(adjy > vupper - vpagesize) adjy = vupper - vpagesize; gtk_adjustment_set_value(GTK_ADJUSTMENT(hadj), adjx); gtk_adjustment_set_value(GTK_ADJUSTMENT(vadj), adjy); } return FALSE; } static void save_window() { gint width, height; gtk_window_get_size(window, &width, &height); g_key_file_set_integer(keyfile, "window", "width", width); g_key_file_set_integer(keyfile, "window", "height", height); g_key_file_set_integer(keyfile, "window", "hpane_pos", gtk_paned_get_position(GTK_PANED(window_hpane))); g_key_file_set_integer(keyfile, "window", "vpane_pos", gtk_paned_get_position(GTK_PANED(window_vpane))); } void on_file_quit_activate(GtkMenuItem *menuitem, gpointer user_data) { save_window(); gtk_main_quit(); } void on_file_preferences_activate(GtkMenuItem *menuitem, gpointer user_data) { struct window_config_t *wct; wct = window_config_new(window); if(!wct) { return; } if(gtk_dialog_run(wct->dialog) == GTK_RESPONSE_OK) { window_config_save(wct); } gtk_widget_destroy(GTK_WIDGET(wct->dialog)); g_free(wct); } static void set_sizes() { gint width, height, hpane_pos, vpane_pos; width = conf_get_int("window", "width", 800); height = conf_get_int("window", "height", 600); gtk_window_set_default_size(window, width, height); hpane_pos = conf_get_int("window", "hpane_pos", 190); vpane_pos = conf_get_int("window", "vpane_pos", 160); gtk_paned_set_position(GTK_PANED(window_hpane), hpane_pos); gtk_paned_set_position(GTK_PANED(window_vpane), vpane_pos); } int gui_main(int argc, char **argv) { GtkBuilder *builder ; GError *error = NULL; GdkColor color; g_thread_init(NULL); gdk_threads_init(); if(!db_open()) return 1; conf_open(); gtk_init(&argc, &argv); builder = gtk_builder_new(); if(!gtk_builder_add_from_string(builder, walls_ui_string, -1, &error)) { g_warning("%s", error->message); g_error_free(error); return 1; } window = GTK_WINDOW(gtk_builder_get_object(builder, "window")); foldtree = GTK_WIDGET(gtk_builder_get_object(builder, "foldtree")); foldtree_init(GTK_TREE_VIEW(foldtree)); tagview = GTK_WIDGET(gtk_builder_get_object(builder, "tagview")); tagview_init(GTK_TREE_VIEW(tagview)); thumbview = GTK_ICON_VIEW(gtk_builder_get_object(builder, "thumbview")); gtk_icon_view_set_pixbuf_column(thumbview, 0); image = GTK_IMAGE(gtk_builder_get_object(builder, "image")); layout = GTK_WIDGET(gtk_builder_get_object(builder, "layout")); if(gdk_color_parse("#000", &color)) gtk_widget_modify_bg(GTK_WIDGET(layout), GTK_STATE_NORMAL, &color); window_hpane = GTK_WIDGET(gtk_builder_get_object(builder, "window_hpane")); g_signal_connect(window_hpane, "notify::position", G_CALLBACK(on_window_hpane_resized), window_hpane); window_vpane = GTK_WIDGET(gtk_builder_get_object(builder, "window_vpane")); statusbar = GTK_STATUSBAR(gtk_builder_get_object(builder, "statusbar")); image_context = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "Image display"); view_toolbar = GTK_WIDGET(gtk_builder_get_object(builder, "view_toolbar")); zoom_toolbar = GTK_WIDGET(gtk_builder_get_object(builder, "zoom_toolbar")); zoom_bestfit = GTK_WIDGET(gtk_builder_get_object(builder, "zoom_bestfit")); zoom_normal = GTK_WIDGET(gtk_builder_get_object(builder, "zoom_normal")); gtk_builder_connect_signals(builder, NULL); g_object_unref(G_OBJECT(builder)); set_sizes(); gtk_widget_show_all(GTK_WIDGET(window)); preload_init(); conf_load(); gdk_threads_enter(); gtk_main(); gdk_threads_leave(); preload_free(); db_close(); conf_close(); return 0; }