static gboolean star_key_accel_callback(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval, GdkModifierType modifier, gpointer data) { long int num = (long int)data; switch (num) { case DT_VIEW_REJECT: case DT_VIEW_DESERT: case DT_VIEW_STAR_1: case DT_VIEW_STAR_2: case DT_VIEW_STAR_3: case DT_VIEW_STAR_4: case DT_VIEW_STAR_5: case 666: { int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if(mouse_over_id <= 0) { 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); if(num == 666 || num == DT_VIEW_DESERT) image->flags &= ~0xf; else if(num == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= num; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, cimg); } sqlite3_finalize(stmt); } else { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); if(num == 666 || num == DT_VIEW_DESERT) image->flags &= ~0xf; else if(num == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= num; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, cimg); } dt_control_queue_redraw_center(); break; } default: break; } return TRUE; }
int dt_history_load_and_apply_on_selection (gchar *filename) { int res=0; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select * from selected_images", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, (int32_t)imgid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); if(img) { if (dt_exif_xmp_read(img, filename, 1)) { res=1; break; } /* if current image in develop reload history */ if (dt_dev_is_current_image(darktable.develop, imgid)) dt_dev_reload_history_items (darktable.develop); dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); dt_mipmap_cache_remove(darktable.mipmap_cache, imgid); } } sqlite3_finalize(stmt); return res; }
static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint target_type, guint time, gpointer data) { dt_view_t *self = (dt_view_t*)data; dt_map_t *lib = (dt_map_t*)self->data; gboolean success = FALSE; if((selection_data != NULL) && (selection_data->length >= 0) && target_type == DND_TARGET_IMGID) { float longitude, latitude; int *imgid = (int*)selection_data->data; if(imgid > 0) { OsmGpsMapPoint *pt = osm_gps_map_point_new_degrees(0.0, 0.0); osm_gps_map_convert_screen_to_geographic(lib->map, x, y, pt); osm_gps_map_point_get_degrees(pt, &latitude, &longitude); osm_gps_map_point_free(pt); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, *imgid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); img->longitude = longitude; img->latitude = latitude; dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, cimg); success = TRUE; } } gtk_drag_finish(context, success, FALSE, time); }
static gboolean _lib_filmstrip_ratings_key_accel_callback(GtkAccelGroup *accel_group, GObject *aceeleratable, guint keyval, GdkModifierType modifier, gpointer data) { int num = GPOINTER_TO_INT(data); switch (num) { case DT_VIEW_DESERT: case DT_VIEW_REJECT: case DT_VIEW_STAR_1: case DT_VIEW_STAR_2: case DT_VIEW_STAR_3: case DT_VIEW_STAR_4: case DT_VIEW_STAR_5: case 666: { int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if (mouse_over_id <= 0) return FALSE; /* get image from cache */ int32_t activated_image = -1; activated_image = darktable.view_manager->proxy.filmstrip.activated_image(darktable.view_manager->proxy.filmstrip.module); int offset = 0; if(mouse_over_id == activated_image) offset = dt_collection_image_offset(mouse_over_id); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); if (num == 666) image->flags &= ~0xf; else if (num == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else if(num == DT_VIEW_REJECT && ((image->flags & 0x7) == 6)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= num; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); dt_collection_hint_message(darktable.collection); // More than this, we need to redraw all if(mouse_over_id == activated_image) if(_lib_filmstrip_imgid_in_collection(darktable.collection, mouse_over_id) == 0) dt_view_filmstrip_scroll_relative(0, offset); /* redraw all */ dt_control_queue_redraw(); break; } default: break; } return TRUE; }
void dt_image_add_time_offset(const int imgid, const long int offset) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); if (!cimg) return; // get the datetime_taken and calculate the new time gint year; gint month; gint day; gint hour; gint minute; gint seconds; if (sscanf(cimg->exif_datetime_taken, "%d:%d:%d %d:%d:%d", (int*)&year, (int*)&month, (int*)&day, (int*)&hour,(int*)&minute,(int*)&seconds) != 6) { fprintf(stderr,"broken exif time in db, '%s', imgid %d\n", cimg->exif_datetime_taken, imgid); dt_image_cache_read_release(darktable.image_cache, cimg); return; } GTimeZone *tz = g_time_zone_new_utc(); GDateTime *datetime_original = g_date_time_new(tz, year, month, day, hour, minute, seconds); g_time_zone_unref(tz); if(!datetime_original) { dt_image_cache_read_release(darktable.image_cache, cimg); return; } // let's add our offset GDateTime *datetime_new = g_date_time_add_seconds(datetime_original, offset); g_date_time_unref(datetime_original); if(!datetime_new) { dt_image_cache_read_release(darktable.image_cache, cimg); return; } gchar *datetime = g_date_time_format(datetime_new, "%Y:%m:%d %H:%M:%S"); g_date_time_unref(datetime_new); // update exif_datetime_taken in img if(datetime) { dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); g_strlcpy(img->exif_datetime_taken, datetime, 20); dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_SAFE); } dt_image_cache_read_release(darktable.image_cache, cimg); g_free(datetime); }
void dt_ratings_apply_to_image (int imgid, int rating) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); // one star is a toggle, so you can easily reject images by removing the last star: if(((image->flags & 0x7) == 1) && (rating == 1)) rating = 0; 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); dt_collection_hint_message(darktable.collection); }
static void remove_preset_flag(const int imgid) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); // clear flag image->flags &= ~DT_IMAGE_AUTO_PRESETS_APPLIED; // write through to sql+xmp dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, cimg); }
static gboolean _lib_filmstrip_button_press_callback(GtkWidget *w, GdkEventButton *e, 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; int32_t mouse_over_id = strip->mouse_over_id; /* is this an activation of image */ if (e->button == 1 && e->type == GDK_2BUTTON_PRESS) if (mouse_over_id > 0) { strip->activated_image = mouse_over_id; dt_control_signal_raise(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE); return TRUE; } /* let check if any thumb controls was clicked */ switch(strip->image_over) { case DT_VIEW_DESERT: break; case DT_VIEW_REJECT: case DT_VIEW_STAR_1: case DT_VIEW_STAR_2: case DT_VIEW_STAR_3: case DT_VIEW_STAR_4: case DT_VIEW_STAR_5: { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); image->dirty = 1; if(strip->image_over == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else if(strip->image_over == DT_VIEW_REJECT && ((image->flags & 0x7) == 6)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= strip->image_over; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); break; } default: return FALSE; } return TRUE; }
void dt_image_set_location(const int32_t imgid, double lon, double lat) { /* fetch image from cache */ const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); if (!cimg) return; dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); /* set image location */ image->longitude = lon; image->latitude = lat; /* store */ dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); }
static gboolean _lib_filmstrip_ratings_key_accel_callback(GtkAccelGroup *accel_group, GObject *aceeleratable, guint keyval, GdkModifierType modifier, gpointer data) { long int num = (long int)data; switch (num) { case DT_VIEW_DESERT: case DT_VIEW_REJECT: case DT_VIEW_STAR_1: case DT_VIEW_STAR_2: case DT_VIEW_STAR_3: case DT_VIEW_STAR_4: case DT_VIEW_STAR_5: case 666: { int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if (mouse_over_id <= 0) return FALSE; /* get image from cache */ const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); if (num == 666) image->flags &= ~0xf; else if (num == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= num; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); /* redraw all */ dt_control_queue_redraw(); break; } default: break; } return TRUE; }
int dt_history_load_and_apply(int imgid, gchar *filename, int history_only) { int res = 0; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); if(img) { if(dt_exif_xmp_read(img, filename, history_only)) return 1; /* if current image in develop reload history */ if(dt_dev_is_current_image(darktable.develop, imgid)) dt_dev_reload_history_items (darktable.develop); dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, img); dt_mipmap_cache_remove(darktable.mipmap_cache, imgid); } return res; }
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 dt_image_t*checkwriteimage(lua_State*L,int index) { const dt_image_t* my_readimage=checkreadimage(L,index); return dt_image_cache_write_get(darktable.image_cache,my_readimage); }
int32_t dt_image_move(const int32_t imgid, const int32_t filmid) { //TODO: several places where string truncation could occur unnoticed int32_t result = -1; gchar oldimg[DT_MAX_PATH_LEN] = {0}; gchar newimg[DT_MAX_PATH_LEN] = {0}; dt_image_full_path(imgid, oldimg, DT_MAX_PATH_LEN); gchar *newdir = NULL; sqlite3_stmt *film_stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select folder from film_rolls where id = ?1", -1, &film_stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(film_stmt, 1, filmid); if(sqlite3_step(film_stmt) == SQLITE_ROW) newdir = g_strdup((gchar *) sqlite3_column_text(film_stmt, 0)); sqlite3_finalize(film_stmt); if(newdir) { gchar *imgbname = g_path_get_basename(oldimg); g_snprintf(newimg, DT_MAX_PATH_LEN, "%s%c%s", newdir, G_DIR_SEPARATOR, imgbname); g_free(imgbname); g_free(newdir); // statement for getting ids of the image to be moved and it's duplicates sqlite3_stmt *duplicates_stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where filename in (select filename from images " "where id = ?1) and film_id in (select film_id from images where id = ?1)", -1, &duplicates_stmt, NULL); // move image // TODO: Use gio's' g_file_move instead of g_rename? if (!g_file_test(newimg, G_FILE_TEST_EXISTS) && (g_rename(oldimg, newimg) == 0)) { // first move xmp files of image and duplicates GList *dup_list = NULL; DT_DEBUG_SQLITE3_BIND_INT(duplicates_stmt, 1, imgid); while (sqlite3_step(duplicates_stmt) == SQLITE_ROW) { int32_t id = sqlite3_column_int(duplicates_stmt, 0); dup_list = g_list_append(dup_list, GINT_TO_POINTER(id)); gchar oldxmp[512], newxmp[512]; g_strlcpy(oldxmp, oldimg, 512); g_strlcpy(newxmp, newimg, 512); dt_image_path_append_version(id, oldxmp, 512); dt_image_path_append_version(id, newxmp, 512); g_strlcat(oldxmp, ".xmp", 512); g_strlcat(newxmp, ".xmp", 512); if (g_file_test(oldxmp, G_FILE_TEST_EXISTS)) (void)g_rename(oldxmp, newxmp); } sqlite3_reset(duplicates_stmt); sqlite3_clear_bindings(duplicates_stmt); // then update database and cache // if update was performed in above loop, dt_image_path_append_version() // would return wrong version! while (dup_list) { long int id = GPOINTER_TO_INT(dup_list->data); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); img->film_id = filmid; // write through to db, but not to xmp dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); dup_list = g_list_delete_link(dup_list, dup_list); } g_list_free(dup_list); result = 0; } } return result; }
int button_pressed(dt_view_t *self, double x, double y, int which, int type, uint32_t state) { dt_library_t *lib = (dt_library_t *)self->data; lib->modifiers = state; lib->button = which; lib->select_offset_x = lib->zoom_x; lib->select_offset_y = lib->zoom_y; lib->select_offset_x += x; lib->select_offset_y += y; lib->pan = 1; if(which == 1) dt_control_change_cursor(GDK_HAND1); if(which == 1 && type == GDK_2BUTTON_PRESS) return 0; // image button pressed? if(which == 1) { switch(lib->image_over) { case DT_VIEW_DESERT: break; case DT_VIEW_REJECT: case DT_VIEW_STAR_1: case DT_VIEW_STAR_2: case DT_VIEW_STAR_3: case DT_VIEW_STAR_4: case DT_VIEW_STAR_5: { int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); if(image) { if(lib->image_over == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else if(lib->image_over == DT_VIEW_REJECT && ((image->flags & 0x7) == 6)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= lib->image_over; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); } dt_image_cache_read_release(darktable.image_cache, image); break; } case DT_VIEW_GROUP: { int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); if(!image) return 0; int group_id = image->group_id; int id = image->id; dt_image_cache_read_release(darktable.image_cache, image); if(state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) // just add the whole group to the selection. TODO: make this also work for collapsed groups. { sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert or ignore into selected_images select id from images where group_id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id); sqlite3_step(stmt); sqlite3_finalize(stmt); } else if(group_id == darktable.gui->expanded_group_id) // the group is already expanded, so ... { if(id == darktable.gui->expanded_group_id) // ... collapse it darktable.gui->expanded_group_id = -1; else // ... make the image the new representative of the group darktable.gui->expanded_group_id = dt_grouping_change_representative(id); } else // expand the group darktable.gui->expanded_group_id = group_id; dt_collection_update_query(darktable.collection); break; } default: return 0; } } return 1; }
uint32_t dt_image_import(const int32_t film_id, const char *filename, gboolean override_ignore_jpegs) { if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) return 0; const char *cc = filename + strlen(filename); for(; *cc!='.'&&cc>filename; cc--); if(!strcmp(cc, ".dt")) return 0; if(!strcmp(cc, ".dttags")) return 0; if(!strcmp(cc, ".xmp")) return 0; char *ext = g_ascii_strdown(cc+1, -1); if(override_ignore_jpegs == FALSE && (!strcmp(ext, "jpg") || !strcmp(ext, "jpeg")) && dt_conf_get_bool("ui_last/import_ignore_jpegs")) return 0; int supported = 0; char **extensions = g_strsplit(dt_supported_extensions, ",", 100); for(char **i=extensions; *i!=NULL; i++) if(!strcmp(ext, *i)) { supported = 1; break; } g_strfreev(extensions); if(!supported) { g_free(ext); return 0; } int rc; uint32_t id = 0; // select from images; if found => return gchar *imgfname; imgfname = g_path_get_basename((const gchar*)filename); sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where film_id = ?1 and filename = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC); if(sqlite3_step(stmt) == SQLITE_ROW) { id = sqlite3_column_int(stmt, 0); g_free(imgfname); sqlite3_finalize(stmt); g_free(ext); return id; } sqlite3_finalize(stmt); // also need to set the no-legacy bit, to make sure we get the right presets (new ones) uint32_t flags = dt_conf_get_int("ui_last/import_initial_rating"); if(flags > 5) { flags = 1; dt_conf_set_int("ui_last/import_initial_rating", 1); } flags |= DT_IMAGE_NO_LEGACY_PRESETS; // insert dummy image entry in database DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into images (id, film_id, filename, caption, description, " "license, sha1sum, flags) values (null, ?1, ?2, '', '', '', '', ?3)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, flags); rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where film_id = ?1 and filename = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC); if(sqlite3_step(stmt) == SQLITE_ROW) id = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); // Try to find out if this should be grouped already. gchar *basename = g_strdup(imgfname); gchar *cc2 = basename + strlen(basename); for(; *cc2!='.'&&cc2>basename; cc2--); *cc2='\0'; gchar *sql_pattern = g_strconcat(basename, ".%", NULL); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select group_id from images where film_id = ?1 and filename like ?2 and id != ?3", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, sql_pattern, -1, SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id); int group_id; if(sqlite3_step(stmt) == SQLITE_ROW) group_id = sqlite3_column_int(stmt, 0); else group_id = id; sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update images set group_id = ?1 where id = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, id); sqlite3_step(stmt); sqlite3_finalize(stmt); // printf("[image_import] importing `%s' to img id %d\n", imgfname, id); // lock as shortly as possible: const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); img->group_id = group_id; // read dttags and exif for database queries! (void) dt_exif_read(img, filename); char dtfilename[DT_MAX_PATH_LEN]; g_strlcpy(dtfilename, filename, DT_MAX_PATH_LEN); dt_image_path_append_version(id, dtfilename, DT_MAX_PATH_LEN); char *c = dtfilename + strlen(dtfilename); sprintf(c, ".xmp"); (void)dt_exif_xmp_read(img, dtfilename, 0); // write through to db, but not to xmp. dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); // add a tag with the file extension guint tagid = 0; char tagname[512]; snprintf(tagname, 512, "darktable|format|%s", ext); g_free(ext); dt_tag_new(tagname, &tagid); dt_tag_attach(tagid,id); // Search for sidecar files and import them if found. glob_t *globbuf = g_malloc(sizeof(glob_t)); // Add version wildcard gchar *fname = g_strdup(filename); gchar pattern[DT_MAX_PATH_LEN]; g_snprintf(pattern, DT_MAX_PATH_LEN, "%s", filename); char *c1 = pattern + strlen(pattern); while(*c1 != '.' && c1 > pattern) c1--; snprintf(c1, pattern + DT_MAX_PATH_LEN - c1, "_*"); char *c2 = fname + strlen(fname); while(*c2 != '.' && c2 > fname) c2--; snprintf(c1+2, pattern + DT_MAX_PATH_LEN - c1 - 2, "%s.xmp", c2); if (!glob(pattern, 0, NULL, globbuf)) { for (int i=0; i < globbuf->gl_pathc; i++) { int newid = -1; newid = dt_image_duplicate(id); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, newid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); (void)dt_exif_xmp_read(img, globbuf->gl_pathv[i], 0); dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); } globfree(globbuf); } g_free(imgfname); g_free(fname); g_free(basename); g_free(sql_pattern); g_free(globbuf); dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGE_IMPORT,id); return id; }
void dt_mipmap_cache_read_get( dt_mipmap_cache_t *cache, dt_mipmap_buffer_t *buf, const uint32_t imgid, const dt_mipmap_size_t mip, const dt_mipmap_get_flags_t flags) { const uint32_t key = get_key(imgid, mip); if(flags == DT_MIPMAP_TESTLOCK) { // simple case: only get and lock if it's there. struct dt_mipmap_buffer_dsc* dsc = (struct dt_mipmap_buffer_dsc*)dt_cache_read_testget(&cache->mip[mip].cache, key); if(dsc) { buf->width = dsc->width; buf->height = dsc->height; buf->imgid = imgid; buf->size = mip; // skip to next 8-byte alignment, for sse buffers. buf->buf = (uint8_t *)(dsc+1); } else { // set to NULL if failed. buf->width = buf->height = 0; buf->imgid = 0; buf->size = DT_MIPMAP_NONE; buf->buf = NULL; } } else if(flags == DT_MIPMAP_PREFETCH) { // and opposite: prefetch without locking if(mip > DT_MIPMAP_FULL || mip < DT_MIPMAP_0) return; dt_job_t j; dt_image_load_job_init(&j, imgid, mip); // if the job already exists, make it high-priority, if not, add it: if(dt_control_revive_job(darktable.control, &j) < 0) dt_control_add_job(darktable.control, &j); } else if(flags == DT_MIPMAP_BLOCKING) { // simple case: blocking get struct dt_mipmap_buffer_dsc* dsc = (struct dt_mipmap_buffer_dsc*)dt_cache_read_get(&cache->mip[mip].cache, key); if(!dsc) { // should never happen for anything but full images which have been moved. assert(mip == DT_MIPMAP_FULL || mip == DT_MIPMAP_F); // fprintf(stderr, "[mipmap cache get] no data in cache for imgid %u size %d!\n", imgid, mip); // sorry guys, no image for you :( buf->width = buf->height = 0; buf->imgid = 0; buf->size = DT_MIPMAP_NONE; buf->buf = NULL; } else { // fprintf(stderr, "[mipmap cache get] found data in cache for imgid %u size %d\n", imgid, mip); // uninitialized? //assert(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE || dsc->size == 0); if(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE) { __sync_fetch_and_add (&(cache->mip[mip].stats_fetches), 1); // fprintf(stderr, "[mipmap cache get] now initializing buffer for img %u mip %d!\n", imgid, mip); // we're write locked here, as requested by the alloc callback. // now fill it with data: if(mip == DT_MIPMAP_FULL) { // load the image: // make sure we access the r/w lock as shortly as possible! dt_image_t buffered_image; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); buffered_image = *cimg; // dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); // dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, cimg); char filename[DT_MAX_PATH_LEN]; gboolean from_cache = TRUE; dt_image_full_path(buffered_image.id, filename, DT_MAX_PATH_LEN, &from_cache); dt_mipmap_cache_allocator_t a = (dt_mipmap_cache_allocator_t)&dsc; struct dt_mipmap_buffer_dsc* prvdsc = dsc; dt_imageio_retval_t ret = dt_imageio_open(&buffered_image, filename, a); if(dsc != prvdsc) { // fprintf(stderr, "[mipmap cache] realloc %p\n", data); // write back to cache, too. // in case something went wrong, still keep the buffer and return it to the hashtable // so we don't produce mem leaks or unnecessary mem fragmentation. dt_cache_realloc(&cache->mip[mip].cache, key, 1, (void*)dsc); } if(ret != DT_IMAGEIO_OK) { // fprintf(stderr, "[mipmap read get] error loading image: %d\n", ret); // // we can only return a zero dimension buffer if the buffer has been allocated. // in case dsc couldn't be allocated and points to the static buffer, it contains // a dead image already. if((void *)dsc != (void *)dt_mipmap_cache_static_dead_image) dsc->width = dsc->height = 0; } else { // swap back new image data: cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); *img = buffered_image; // fprintf(stderr, "[mipmap read get] initializing full buffer img %u with %u %u -> %d %d (%p)\n", imgid, data[0], data[1], img->width, img->height, data); // don't write xmp for this (we only changed db stuff): dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); } } else if(mip == DT_MIPMAP_F) { _init_f((float *)(dsc+1), &dsc->width, &dsc->height, imgid); } else { // 8-bit thumbs, possibly need to be compressed: if(cache->compression_type) { // get per-thread temporary storage without malloc from a separate cache: const int key = dt_control_get_threadid(); // const void *cbuf = dt_cache_read_get(&cache->scratchmem.cache, key); uint8_t *scratchmem = (uint8_t *)dt_cache_write_get(&cache->scratchmem.cache, key); _init_8(scratchmem, &dsc->width, &dsc->height, imgid, mip); buf->width = dsc->width; buf->height = dsc->height; buf->imgid = imgid; buf->size = mip; buf->buf = (uint8_t *)(dsc+1); dt_mipmap_cache_compress(buf, scratchmem); dt_cache_write_release(&cache->scratchmem.cache, key); dt_cache_read_release(&cache->scratchmem.cache, key); } else { _init_8((uint8_t *)(dsc+1), &dsc->width, &dsc->height, imgid, mip); } } dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE; // drop the write lock dt_cache_write_release(&cache->mip[mip].cache, key); /* raise signal that mipmaps has been flushed to cache */ dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_MIPMAP_UPDATED); } buf->width = dsc->width; buf->height = dsc->height; buf->imgid = imgid; buf->size = mip; buf->buf = (uint8_t *)(dsc+1); if(dsc->width == 0 || dsc->height == 0) { // fprintf(stderr, "[mipmap cache get] got a zero-sized image for img %u mip %d!\n", imgid, mip); if(mip < DT_MIPMAP_F) dead_image_8(buf); else if(mip == DT_MIPMAP_F) dead_image_f(buf); else buf->buf = NULL; // full images with NULL buffer have to be handled, indicates `missing image' } } } else if(flags == DT_MIPMAP_BEST_EFFORT) { __sync_fetch_and_add (&(cache->mip[mip].stats_requests), 1); // best-effort, might also return NULL. // never decrease mip level for float buffer or full image: dt_mipmap_size_t min_mip = (mip >= DT_MIPMAP_F) ? mip : DT_MIPMAP_0; for(int k=mip; k>=min_mip && k>=0; k--) { // already loaded? dt_mipmap_cache_read_get(cache, buf, imgid, k, DT_MIPMAP_TESTLOCK); if(buf->buf && buf->width > 0 && buf->height > 0) { if(mip != k) __sync_fetch_and_add (&(cache->mip[k].stats_standin), 1); return; } // didn't succeed the first time? prefetch for later! if(mip == k) { __sync_fetch_and_add (&(cache->mip[mip].stats_near_match), 1); dt_mipmap_cache_read_get(cache, buf, imgid, mip, DT_MIPMAP_PREFETCH); } } __sync_fetch_and_add (&(cache->mip[mip].stats_misses), 1); // fprintf(stderr, "[mipmap cache get] image not found in cache: imgid %u mip %d!\n", imgid, mip); // nothing found :( buf->buf = NULL; buf->imgid = 0; buf->size = DT_MIPMAP_NONE; buf->width = buf->height = 0; } }
int main(int argc, char *arg[]) { bindtextdomain (GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); gtk_init (&argc, &arg); // parse command line arguments char *image_filename = NULL; char *xmp_filename = NULL; char *output_filename = NULL; int file_counter = 0; int width = 0, height = 0, bpp = 0; gboolean verbose = FALSE, high_quality = TRUE; for(int k=1; k<argc; k++) { if(arg[k][0] == '-') { if(!strcmp(arg[k], "--help")) { usage(arg[0]); exit(1); } else if(!strcmp(arg[k], "--version")) { printf("this is darktable-cli\ncopyright (c) 2012-2013 johannes hanika, tobias ellinghaus\n"); exit(1); } else if(!strcmp(arg[k], "--width")) { k++; width = MAX(atoi(arg[k]), 0); } else if(!strcmp(arg[k], "--height")) { k++; height = MAX(atoi(arg[k]), 0); } else if(!strcmp(arg[k], "--bpp")) { k++; bpp = MAX(atoi(arg[k]), 0); fprintf(stderr, "%s %d\n", _("TODO: sorry, due to api restrictions we currently cannot set the bpp to"), bpp); } else if(!strcmp(arg[k], "--hq")) { k++; gchar *str = g_ascii_strup(arg[k], -1); if(!g_strcmp0(str, "0") || !g_strcmp0(str, "FALSE")) high_quality = FALSE; else if(!g_strcmp0(str, "1") || !g_strcmp0(str, "TRUE")) high_quality = TRUE; else { fprintf(stderr, "%s: %s\n", _("Unknown option for --hq"), arg[k]); usage(arg[0]); exit(1); } g_free(str); } else if(!strcmp(arg[k], "-v") || !strcmp(arg[k], "--verbose")) { verbose = TRUE; } } else { if(file_counter == 0) image_filename = arg[k]; else if(file_counter == 1) xmp_filename = arg[k]; else if(file_counter == 2) output_filename = arg[k]; file_counter++; } } if(file_counter < 2 || file_counter > 3) { usage(arg[0]); exit(1); } else if(file_counter == 2) { // no xmp file given output_filename = xmp_filename; xmp_filename = NULL; } // the output file already exists, so there will be a sequence number added if(g_file_test(output_filename, G_FILE_TEST_EXISTS)) { fprintf(stderr, "%s\n", _("output file already exists, it will get renamed")); } char *m_arg[] = {"darktable-cli", "--library", ":memory:", NULL}; // init dt without gui: if(dt_init(3, m_arg, 0)) exit(1); dt_film_t film; int id = 0; int filmid = 0; gchar *directory = g_path_get_dirname(image_filename); filmid = dt_film_new(&film, directory); id = dt_image_import(filmid, image_filename, TRUE); if(!id) { fprintf(stderr, _("error: can't open file %s"), image_filename); fprintf(stderr, "\n"); exit(1); } g_free(directory); // attach xmp, if requested: if(xmp_filename) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); dt_exif_xmp_read(image, xmp_filename, 1); // don't write new xmp: dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, image); } // print the history stack if(verbose) { gchar *history = dt_history_get_items_as_string(id); if(history) printf("%s\n", history); else printf("[%s]\n", _("empty history stack")); } // try to find out the export format from the output_filename char *ext = output_filename + strlen(output_filename); while(ext > output_filename && *ext != '.') ext--; *ext = '\0'; ext++; if(!strcmp(ext, "jpg")) ext = "jpeg"; // init the export data structures int size = 0, dat_size = 0; dt_imageio_module_format_t *format; dt_imageio_module_storage_t *storage; dt_imageio_module_data_t *sdata, *fdata; storage = dt_imageio_get_storage_by_name("disk"); // only exporting to disk makes sense if(storage == NULL) { fprintf(stderr, "%s\n", _("cannot find disk storage module. please check your installation, something seems to be broken.")); exit(1); } sdata = storage->get_params(storage, &size); if(sdata == NULL) { fprintf(stderr, "%s\n", _("failed to get parameters from storage module, aborting export ...")); exit(1); } // and now for the really ugly hacks. don't tell your children about this one or they won't sleep at night any longer ... g_strlcpy((char*)sdata, output_filename, DT_MAX_PATH_LEN); // all is good now, the last line didn't happen. format = dt_imageio_get_format_by_name(ext); if(format == NULL) { fprintf(stderr, _("unknown extension '.%s'"), ext); fprintf(stderr, "\n"); exit(1); } fdata = format->get_params(format, &dat_size); if(fdata == NULL) { fprintf(stderr, "%s\n", _("failed to get parameters from format module, aborting export ...")); exit(1); } uint32_t w,h,fw,fh,sw,sh; fw=fh=sw=sh=0; storage->dimension(storage, &sw, &sh); format->dimension(format, &fw, &fh); if( sw==0 || fw==0) w=sw>fw?sw:fw; else w=sw<fw?sw:fw; if( sh==0 || fh==0) h=sh>fh?sh:fh; else h=sh<fh?sh:fh; fdata->max_width = width; fdata->max_height = height; fdata->max_width = (w!=0 && fdata->max_width >w)?w:fdata->max_width; fdata->max_height = (h!=0 && fdata->max_height >h)?h:fdata->max_height; fdata->style[0] = '\0'; //TODO: add a callback to set the bpp without going through the config storage->store(storage,sdata, id, format, fdata, 1, 1, high_quality); // cleanup time if(storage->finalize_store) storage->finalize_store(storage, sdata); storage->free_params(storage, sdata); format->free_params(format, fdata); dt_cleanup(); }
static void auto_apply_presets(dt_develop_t *dev) { const int imgid = dev->image_storage.id; if(imgid <= 0) return; int run = 0; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); if(!(cimg->flags & DT_IMAGE_AUTO_PRESETS_APPLIED)) run = 1; // flag was already set? only apply presets once in the lifetime of a history stack. // (the flag will be cleared when removing it) if(!run || cimg->id <= 0) { dt_image_cache_read_release(darktable.image_cache, cimg); return; } // keep locked, we want to be alone messing with the history of the poor fellow: dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); // be extra sure that we don't mess up history in separate threads: dt_pthread_mutex_lock(&darktable.db_insert); // cleanup DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from memory.history", NULL, NULL, NULL); const char *preset_table[2] = {"presets", "legacy_presets"}; const int legacy = (image->flags & DT_IMAGE_NO_LEGACY_PRESETS) ? 0 : 1; char query[1024]; snprintf(query, 1024, "insert into memory.history select ?1, 0, op_version, operation, op_params, enabled, blendop_params, blendop_version, multi_priority, multi_name " "from %s where autoapply=1 and " "?2 like model and ?3 like maker and ?4 like lens and " "?5 between iso_min and iso_max and " "?6 between exposure_min and exposure_max and " "?7 between aperture_min and aperture_max and " "?8 between focal_length_min and focal_length_max and " "(isldr = 0 or isldr=?9) order by writeprotect desc, " "length(model), length(maker), length(lens)", preset_table[legacy]); // query for all modules at once: sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, cimg->exif_model, strlen(cimg->exif_model), SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, cimg->exif_maker, strlen(cimg->exif_maker), SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 4, cimg->exif_lens, strlen(cimg->exif_lens), SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 5, fmaxf(0.0f, fminf(1000000, cimg->exif_iso))); DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 6, fmaxf(0.0f, fminf(1000000, cimg->exif_exposure))); DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 7, fmaxf(0.0f, fminf(1000000, cimg->exif_aperture))); DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 8, fmaxf(0.0f, fminf(1000000, cimg->exif_focal_length))); // 0: dontcare, 1: ldr, 2: raw DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 9, 2-dt_image_is_ldr(cimg)); if(sqlite3_step(stmt) == SQLITE_DONE) { sqlite3_finalize(stmt); int cnt = 0; // count what we found: DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(*) from memory.history", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) { // if there is anything.. cnt = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); // fprintf(stderr, "[auto_apply_presets] imageid %d found %d matching presets (legacy %d)\n", imgid, cnt, legacy); // advance the current history by that amount: DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update history set num=num+?1 where imgid=?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, cnt); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); if(sqlite3_step(stmt) == SQLITE_DONE) { // and finally prepend the rest with increasing numbers (starting at 0) sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into history select imgid, rowid-1, module, operation, op_params, enabled, " "blendop_params, blendop_version, multi_priority, multi_name from memory.history", -1, &stmt, NULL); sqlite3_step(stmt); } } } sqlite3_finalize(stmt); // first time we are loading the image, try to import lightroom .xmp if any if (dev->image_loading) dt_lightroom_import(dev->image_storage.id, dev, TRUE); image->flags |= DT_IMAGE_AUTO_PRESETS_APPLIED | DT_IMAGE_NO_LEGACY_PRESETS; dt_pthread_mutex_unlock(&darktable.db_insert); // make sure these end up in the image_cache + xmp (sync through here if we set the flag) dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, cimg); }
static gboolean _lib_filmstrip_button_press_callback(GtkWidget *w, GdkEventButton *e, 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; int32_t mouse_over_id = strip->mouse_over_id; strip->select = DT_LIB_FILMSTRIP_SELECT_NONE; if (e->button == 1) { if(e->type == GDK_BUTTON_PRESS) { /* let check if any thumb controls was clicked */ switch(strip->image_over) { case DT_VIEW_DESERT: /* is this an activation of image */ if ((e->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == 0) strip->select = DT_LIB_FILMSTRIP_SELECT_SINGLE; else if ((e->state & (GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) strip->select = DT_LIB_FILMSTRIP_SELECT_TOGGLE; else if ((e->state & (GDK_SHIFT_MASK)) == GDK_SHIFT_MASK) strip->select = DT_LIB_FILMSTRIP_SELECT_RANGE; if(strip->select != DT_LIB_FILMSTRIP_SELECT_NONE) { strip->select_id = mouse_over_id; return TRUE; } break; case DT_VIEW_REJECT: case DT_VIEW_STAR_1: case DT_VIEW_STAR_2: case DT_VIEW_STAR_3: case DT_VIEW_STAR_4: case DT_VIEW_STAR_5: { int offset = 0; if(mouse_over_id == strip->activated_image) offset = dt_collection_image_offset(mouse_over_id); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); if(strip->image_over == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else if(strip->image_over == DT_VIEW_REJECT && ((image->flags & 0x7) == 6)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= strip->image_over; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); dt_collection_hint_message(darktable.collection); // More than this, we need to redraw all if(mouse_over_id == strip->activated_image) if(_lib_filmstrip_imgid_in_collection(darktable.collection, mouse_over_id) == 0) dt_view_filmstrip_scroll_relative(0, offset); gtk_widget_queue_draw(darktable.view_manager->proxy.filmstrip.module->widget); return TRUE; } default: return FALSE; } } else if(e->type == GDK_2BUTTON_PRESS) { if (mouse_over_id > 0) { strip->activated_image = mouse_over_id; dt_control_signal_raise(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE); return TRUE; } } } return FALSE; }