void dt_ratings_apply_to_selection (int rating) { uint32_t count = dt_collection_get_selected_count(darktable.collection); if (count) { if(rating == 6) dt_control_log(ngettext("rejecting %d image", "rejecting %d images", count), count); else dt_control_log(ngettext("applying rating %d to %d image", "applying rating %d to %d images", count), rating, count); #if 0 // not updating cache gchar query[1024]= {0}; g_snprintf(query,sizeof(query), "update images set flags=(images.flags & ~7) | (7 & %d) where id in (select imgid from selected_images)", rating ); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); #endif /* for each selected image update rating */ sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from selected_images", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { dt_ratings_apply_to_image(sqlite3_column_int(stmt, 0), rating); } sqlite3_finalize(stmt); /* redraw view */ /* dt_control_queue_redraw_center() */ /* needs to be called in the caller function */ } else dt_control_log(_("no images selected to apply rating")); }
void dt_control_delete_images() { if(dt_conf_get_bool("ask_before_delete")) { GtkWidget *dialog; GtkWidget *win = dt_ui_main_window(darktable.gui->ui); int number = dt_collection_get_selected_count(darktable.collection); // Do not show the dialog if no image is selected: if(number == 0) return; dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, ngettext("do you really want to physically delete %d selected image from disk?", "do you really want to physically delete %d selected images from disk?", number), number); gtk_window_set_title(GTK_WINDOW(dialog), _("delete images?")); gint res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(res != GTK_RESPONSE_YES) return; } dt_job_t j; dt_control_delete_images_job_init(&j); dt_control_add_job(darktable.control, &j); }
static void _lib_filmstrip_dnd_get_callback(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint target_type, guint time, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_filmstrip_t *strip = (dt_lib_filmstrip_t *)self->data; g_assert(selection_data != NULL); int mouse_over_id = strip->mouse_over_id; int count = dt_collection_get_selected_count(NULL); switch(target_type) { case DND_TARGET_IMGID: { int id = ((count == 1) ? mouse_over_id : -1); gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), _DWORD, (guchar *)&id, sizeof(id)); break; } default: // return the location of the file as a last resort case DND_TARGET_URI: // TODO: add all images from the selection { if(count == 1) { gchar pathname[PATH_MAX] = { 0 }; gboolean from_cache = TRUE; dt_image_full_path(mouse_over_id, pathname, sizeof(pathname), &from_cache); gchar *uri = g_strdup_printf("file://%s", pathname); // TODO: should we add the host? gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), _BYTE, (guchar *)uri, strlen(uri)); g_free(uri); } else { sqlite3_stmt *stmt; GList *images = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT imgid FROM main.selected_images", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { int id = sqlite3_column_int(stmt, 0); gchar pathname[PATH_MAX] = { 0 }; gboolean from_cache = TRUE; dt_image_full_path(id, pathname, sizeof(pathname), &from_cache); gchar *uri = g_strdup_printf("file://%s", pathname); // TODO: should we add the host? images = g_list_append(images, uri); } sqlite3_finalize(stmt); gchar *uri_list = dt_util_glist_to_str("\r\n", images); g_list_free_full(images, g_free); gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), _BYTE, (guchar *)uri_list, strlen(uri_list)); g_free(uri_list); } break; } } }
void dt_control_move_images() { // Open file chooser dialog gchar *dir = NULL; GtkWidget *win = dt_ui_main_window(darktable.gui->ui); int number = dt_collection_get_selected_count(darktable.collection); // Do not show the dialog if no image is selected: if(number == 0) return; GtkWidget *filechooser = gtk_file_chooser_dialog_new (_("select directory"), GTK_WINDOW (win), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, (char *)NULL); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser), FALSE); if (gtk_dialog_run (GTK_DIALOG (filechooser)) == GTK_RESPONSE_ACCEPT) { dir = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser)); } gtk_widget_destroy (filechooser); if(!dir || !g_file_test(dir, G_FILE_TEST_IS_DIR)) goto abort; if(dt_conf_get_bool("ask_before_move")) { GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, ngettext("do you really want to physically move the %d selected image to %s?\n" "(all unselected duplicates will be moved along)", "do you really want to physically move %d selected images to %s?\n" "(all unselected duplicates will be moved along)", number), number, dir); gtk_window_set_title(GTK_WINDOW(dialog), ngettext("move image?", "move images?", number)); gint res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(res != GTK_RESPONSE_YES) goto abort; } dt_job_t j; dt_control_move_images_job_init(&j); j.user_data = dir; dt_control_add_job(darktable.control, &j); return; abort: g_free(dir); return; }
static void _lib_filmstrip_dnd_begin_callback(GtkWidget *widget, GdkDragContext *context, gpointer user_data) { const int ts = DT_PIXEL_APPLY_DPI(64); dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_filmstrip_t *strip = (dt_lib_filmstrip_t *)self->data; int imgid = strip->mouse_over_id; // imgid part of selection -> do nothing // otherwise -> select the current image strip->select = DT_LIB_FILMSTRIP_SELECT_NONE; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT imgid FROM main.selected_images WHERE imgid=?1 LIMIT 1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) != SQLITE_ROW) { dt_selection_select_single(darktable.selection, imgid); /* redraw filmstrip */ if(darktable.view_manager->proxy.filmstrip.module) gtk_widget_queue_draw(darktable.view_manager->proxy.filmstrip.module->widget); } sqlite3_finalize(stmt); // if we are dragging a single image -> use the thumbnail of that image // otherwise use the generic d&d icon // TODO: have something pretty in the 2nd case, too. if(dt_collection_get_selected_count(NULL) == 1) { dt_mipmap_buffer_t buf; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size(darktable.mipmap_cache, ts, ts); dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, mip, DT_MIPMAP_BLOCKING, 'r'); if(buf.buf) { for(size_t i = 3; i < (size_t)4 * buf.width * buf.height; i += 4) buf.buf[i] = UINT8_MAX; int w = ts, h = ts; if(buf.width < buf.height) w = (buf.width * ts) / buf.height; // portrait else h = (buf.height * ts) / buf.width; // landscape GdkPixbuf *source = gdk_pixbuf_new_from_data(buf.buf, GDK_COLORSPACE_RGB, TRUE, 8, buf.width, buf.height, buf.width * 4, NULL, NULL); GdkPixbuf *scaled = gdk_pixbuf_scale_simple(source, w, h, GDK_INTERP_HYPER); gtk_drag_set_icon_pixbuf(context, scaled, 0, h); if(source) g_object_unref(source); if(scaled) g_object_unref(scaled); } dt_mipmap_cache_release(darktable.mipmap_cache, &buf); } }
void dt_collection_hint_message(const dt_collection_t *collection) { /* collection hinting */ gchar message[1024]; int c = dt_collection_get_count(collection); int cs = dt_collection_get_selected_count(collection); g_snprintf(message, sizeof(message), ngettext("%d image of %d in current collection is selected", "%d images of %d in current collection are selected", cs), cs, c); dt_control_hinter_message(darktable.control, message); }
void dt_collection_hint_message(const dt_collection_t *collection) { /* collection hinting */ gchar *message; int c = dt_collection_get_count(collection); int cs = dt_collection_get_selected_count(collection); message = g_strdup_printf(ngettext("%d image of %d in current collection is selected", "%d images of %d in current collection are selected", cs), cs, c); g_idle_add(dt_collection_hint_message_internal, message); }
static void select_this_image(const int imgid) { // select this image, if no multiple selection: if(dt_collection_get_selected_count(NULL) < 2) { sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from selected_images", NULL, NULL, NULL); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert or ignore into selected_images values (?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); } }
const dt_selection_t *dt_selection_new() { dt_selection_t *s = g_malloc0(sizeof(dt_selection_t)); /* initialize the collection copy */ _selection_update_collection(NULL, (gpointer)s); /* initialize last_single_id based on current database */ s->last_single_id = -1; if(dt_collection_get_selected_count(darktable.collection) >= 1) { GList *selected_image = dt_collection_get_selected(darktable.collection, 1); s->last_single_id = GPOINTER_TO_INT(selected_image->data); g_list_free(selected_image); } /* setup signal handler for darktable collection update to update the internal collection of the selection */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED, G_CALLBACK(_selection_update_collection), (gpointer)s); return s; }
void dt_ratings_apply_to_selection (int rating) { uint32_t count = dt_collection_get_selected_count(darktable.collection); if (count) { dt_control_log(ngettext("applying rating %d to %d image", "applying rating %d to %d images", count), rating, count); #if 0 // not updating cache gchar query[1024]={0}; g_snprintf(query,1024, "update images set flags=(images.flags & ~7) | (7 & %d) where id in (select imgid from selected_images)", rating ); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); #endif /* for each selected image update rating */ sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from selected_images", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, sqlite3_column_int(stmt, 0)); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); image->flags = (image->flags & ~0x7) | (0x7 & rating); // synch through: dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); } sqlite3_finalize(stmt); /* redraw view */ dt_control_queue_redraw_center(); } else dt_control_log(_("no images selected to apply rating")); }
static gboolean _lib_tagging_tag_show(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval, GdkModifierType modifier, dt_lib_module_t* self) { int mouse_over_id = -1; int zoom = dt_conf_get_int("plugins/lighttable/images_in_row"); // the order is: // if(zoom == 1) => currently shown image // else if(selection not empty) => selected images // else if(cursor over image) => hovered image // else => return if(zoom == 1 || dt_collection_get_selected_count(darktable.collection) == 0) { DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if(mouse_over_id < 0) return TRUE; } dt_lib_tagging_t *d = (dt_lib_tagging_t*)self->data; d->floating_tag_imgid = mouse_over_id; gint x, y; gint px, py, w, h; GtkWidget *window = dt_ui_main_window(darktable.gui->ui); GtkWidget *center = dt_ui_center(darktable.gui->ui); gdk_window_get_origin(gtk_widget_get_window(center), &px, &py); w = gdk_window_get_width(gtk_widget_get_window(center)); h = gdk_window_get_height(gtk_widget_get_window(center)); x = px + 0.5*(w-FLOATING_ENTRY_WIDTH); y = py + h - 50; /* put the floating box at the mouse pointer */ // gint pointerx, pointery; // gtk_widget_get_pointer(center, &pointerx, &pointery); // x = px + pointerx + 1; // y = py + pointery + 1; d->floating_tag_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* stackoverflow.com/questions/1925568/how-to-give-keyboard-focus-to-a-pop-up-gtk-window */ gtk_widget_set_can_focus(d->floating_tag_window, TRUE); gtk_window_set_decorated(GTK_WINDOW(d->floating_tag_window), FALSE); gtk_window_set_type_hint(GTK_WINDOW(d->floating_tag_window), GDK_WINDOW_TYPE_HINT_POPUP_MENU); gtk_window_set_transient_for(GTK_WINDOW(d->floating_tag_window), GTK_WINDOW(window)); gtk_window_set_opacity(GTK_WINDOW(d->floating_tag_window), 0.8); gtk_window_move(GTK_WINDOW(d->floating_tag_window), x, y); GtkWidget *entry = gtk_entry_new(); gtk_widget_set_size_request(entry, FLOATING_ENTRY_WIDTH, -1); gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK); GtkEntryCompletion *completion = gtk_entry_completion_new(); gtk_entry_completion_set_model(completion, gtk_tree_view_get_model(GTK_TREE_VIEW(d->related))); gtk_entry_completion_set_text_column(completion, 0); gtk_entry_completion_set_inline_completion(completion, TRUE); gtk_entry_completion_set_popup_set_width(completion, FALSE); gtk_entry_set_completion(GTK_ENTRY(entry), completion); gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); gtk_container_add(GTK_CONTAINER(d->floating_tag_window), entry); g_signal_connect_swapped(entry, "focus-out-event", G_CALLBACK(gtk_widget_destroy), d->floating_tag_window); g_signal_connect(entry, "key-press-event", G_CALLBACK(_lib_tagging_tag_key_press), self); gtk_widget_show_all(d->floating_tag_window); gtk_widget_grab_focus(entry); gtk_window_present(GTK_WINDOW(d->floating_tag_window)); return TRUE; }
static void _lib_filmstrip_dnd_begin_callback(GtkWidget *widget, GdkDragContext *context, gpointer user_data) { const int ts = 64; dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_filmstrip_t *strip = (dt_lib_filmstrip_t *)self->data; int imgid = strip->mouse_over_id; // imgid part of selection -> do nothing // otherwise -> select the current image strip->select = DT_LIB_FILMSTRIP_SELECT_NONE; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from selected_images where imgid=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) != SQLITE_ROW) { dt_selection_select_single(darktable.selection, imgid); /* redraw filmstrip */ if(darktable.view_manager->proxy.filmstrip.module) gtk_widget_queue_draw(darktable.view_manager->proxy.filmstrip.module->widget); } sqlite3_finalize(stmt); // if we are dragging a single image -> use the thumbnail of that image // otherwise use the generic d&d icon // TODO: have something pretty in the 2nd case, too. if(dt_collection_get_selected_count(NULL) == 1) { dt_mipmap_buffer_t buf; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size(darktable.mipmap_cache, ts, ts); dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, mip, DT_MIPMAP_BLOCKING); if(buf.buf) { uint8_t *scratchmem = dt_mipmap_cache_alloc_scratchmem(darktable.mipmap_cache); uint8_t *buf_decompressed = dt_mipmap_cache_decompress(&buf, scratchmem); uint8_t *rgbbuf = g_malloc((buf.width+2)*(buf.height+2)*3); memset(rgbbuf, 64, (buf.width+2)*(buf.height+2)*3); for(int i=1; i<=buf.height; i++) for(int j=1; j<=buf.width; j++) for(int k=0; k<3; k++) rgbbuf[(i*(buf.width+2)+j)*3+k] = buf_decompressed[((i-1)*buf.width+j-1)*4+2-k]; int w=ts, h=ts; if(buf.width < buf.height) w = (buf.width*ts)/buf.height; // portrait else h = (buf.height*ts)/buf.width; // landscape GdkPixbuf *source = gdk_pixbuf_new_from_data(rgbbuf, GDK_COLORSPACE_RGB, FALSE, 8, (buf.width+2), (buf.height+2), (buf.width+2)*3, NULL, NULL); GdkPixbuf *scaled = gdk_pixbuf_scale_simple(source, w, h, GDK_INTERP_HYPER); gtk_drag_set_icon_pixbuf(context, scaled, 0, 0); if(source) g_object_unref(source); if(scaled) g_object_unref(scaled); free(scratchmem); g_free(rgbbuf); } dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); } }