void dt_selection_select_unaltered(dt_selection_t *selection) { char *fullq = NULL; if (!selection->collection) return; /* set unaltered collection filter and update query */ uint32_t old_filter_flags = dt_collection_get_filter_flags(selection->collection); dt_collection_set_filter_flags (selection->collection, (dt_collection_get_filter_flags(selection->collection) | COLLECTION_FILTER_UNALTERED)); dt_collection_update(selection->collection); fullq = dt_util_dstrcat(fullq, "%s", "insert or ignore into selected_images "); fullq = dt_util_dstrcat(fullq, "%s", dt_collection_get_query(selection->collection)); /* clean current selection and select unaltered images */ DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from selected_images", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), fullq, NULL, NULL, NULL); /* restore collection filter and update query */ dt_collection_set_filter_flags(selection->collection, old_filter_flags); dt_collection_update(selection->collection); g_free(fullq); selection->last_single_id = -1; }
void dt_selection_select_range(dt_selection_t *selection, uint32_t imgid) { gchar *fullq = NULL; if(!selection->collection || selection->last_single_id == -1) return; /* get start and end rows for range selection */ sqlite3_stmt *stmt; int rc = 0; uint32_t sr = -1, er = -1; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), dt_collection_get_query_no_group(selection->collection), -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { const int id = sqlite3_column_int(stmt, 0); if(id == selection->last_single_id) sr = rc; if(id == imgid) er = rc; if(sr != -1 && er != -1) break; rc++; } sqlite3_finalize(stmt); /* select the images in range from start to end */ const uint32_t old_flags = dt_collection_get_query_flags(selection->collection); /* use the limit to select range of images */ dt_collection_set_query_flags(selection->collection, (old_flags | COLLECTION_QUERY_USE_LIMIT)); dt_collection_update(selection->collection); fullq = dt_util_dstrcat(fullq, "%s", "INSERT OR IGNORE INTO main.selected_images "); fullq = dt_util_dstrcat(fullq, "%s", dt_collection_get_query_no_group(selection->collection)); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), fullq, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, MIN(sr, er)); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, (MAX(sr, er) - MIN(sr, er)) + 1); sqlite3_step(stmt); sqlite3_finalize(stmt); /* reset filter */ dt_collection_set_query_flags(selection->collection, old_flags); dt_collection_update(selection->collection); // The logic above doesn't handle groups, so explicitly select the beginning and end to make sure those are selected properly dt_selection_select(selection, selection->last_single_id); dt_selection_select(selection, imgid); g_free(fullq); }
void dt_selection_select_range(dt_selection_t *selection, uint32_t imgid) { gchar *fullq = NULL; sqlite3_stmt *stmt; if (!selection->collection || selection->last_single_id == -1) return; /* get start and end rows for range selection */ int rc=0; uint32_t sr=-1,er=-1; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), dt_collection_get_query(selection->collection), -1, &stmt, NULL); while(sqlite3_step(stmt)==SQLITE_ROW) { int id = sqlite3_column_int(stmt, 0); if (id == selection->last_single_id) sr = rc; if (id == imgid) er = rc; if (sr != -1 && er != -1 ) break; rc++; } sqlite3_finalize(stmt); /* selece the images in range from start to end */ uint32_t old_flags = dt_collection_get_query_flags(selection->collection); /* use the limit to select range of images */ dt_collection_set_query_flags(selection->collection, (old_flags |COLLECTION_QUERY_USE_LIMIT)); dt_collection_update(selection->collection); fullq = dt_util_dstrcat(fullq, "%s", "insert or ignore into selected_images "); fullq = dt_util_dstrcat(fullq, "%s", dt_collection_get_query(selection->collection)); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), fullq, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, MIN(sr,er)); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, (MAX(sr,er)-MIN(sr,er))+1); sqlite3_step(stmt); /* reset filter */ dt_collection_set_query_flags(selection->collection, old_flags); dt_collection_update(selection->collection); selection->last_single_id = -1; }
const gchar *dt_collection_get_query(const dt_collection_t *collection) { /* ensure there is a query string for collection */ if(!collection->query) dt_collection_update(collection); return collection->query; }
int32_t dt_control_remove_images_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("removing %d image", "removing %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); char query[1024]; sprintf(query, "update images set flags = (flags | %d) where id in (select imgid from selected_images)",DT_IMAGE_REMOVE); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = NULL; sqlite3_stmt *stmt = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select distinct folder || '/' || filename from images, film_rolls where images.film_id = film_rolls.id and images.id in (select imgid from selected_images)", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { list = g_list_append(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0))); } sqlite3_finalize(stmt); while(t) { imgid = (long int)t->data; dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_queue_redraw_center(); return 0; }
void _selection_update_collection(gpointer instance, gpointer user_data) { dt_selection_t *selection = (dt_selection_t *)user_data; /* free previous collection copy if any */ if(selection->collection) dt_collection_free(selection->collection); /* create a new fresh copy of darktable collection */ selection->collection = dt_collection_new(darktable.collection); /* remove limit part of local collection */ dt_collection_set_query_flags(selection->collection, (dt_collection_get_query_flags(selection->collection) & (~(COLLECTION_QUERY_USE_LIMIT)))); dt_collection_update(selection->collection); }
void dt_selection_select_filmroll(dt_selection_t *selection) { // clear at start, too, just to be sure: DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM memory.tmp_selection", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "INSERT INTO memory.tmp_selection SELECT imgid FROM main.selected_images", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM main.selected_images", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "INSERT OR IGNORE INTO main.selected_images SELECT id FROM main.images WHERE film_id IN " "(SELECT film_id FROM main.images AS a JOIN memory.tmp_selection AS " "b ON a.id = b.imgid)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM memory.tmp_selection", NULL, NULL, NULL); dt_collection_update(selection->collection); selection->last_single_id = -1; }
static int32_t _generic_dt_control_fileop_images_job_run(dt_job_t *job, int32_t (*fileop_callback)(const int32_t, const int32_t), const char *desc, const char *desc_pl) { dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction = 0; gchar *newdir = (gchar *)job->user_data; /* create a cancellable bgjob ui template */ g_snprintf(message, 512, ngettext(desc, desc_pl, total), total); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); dt_control_backgroundjobs_set_cancellable(darktable.control, jid, job); // create new film roll for the destination directory dt_film_t new_film; const int32_t film_id = dt_film_new(&new_film, newdir); g_free(newdir); if (film_id <= 0) { dt_control_log(_("failed to create film roll for destination directory, aborting move..")); dt_control_backgroundjobs_destroy(darktable.control, jid); return -1; } while(t && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED) { fileop_callback(GPOINTER_TO_INT(t->data), film_id); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } dt_collection_update(darktable.collection); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); return 0; }
int32_t dt_control_delete_images_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("deleting %d image", "deleting %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt; char query[1024]; sprintf(query, "update images set flags = (flags | %d) where id in (select imgid from selected_images)",DT_IMAGE_REMOVE); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select distinct folder || '/' || filename from images, film_rolls where images.film_id = film_rolls.id and images.id in (select imgid from selected_images)", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) { list = g_list_append(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0))); } sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(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, &stmt, NULL); while(t) { imgid = (long int)t->data; char filename[DT_MAX_PATH_LEN]; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN); int duplicates = 0; DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) == SQLITE_ROW) duplicates = sqlite3_column_int(stmt, 0); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); // remove from disk: if(duplicates == 1) // don't remove the actual data if there are (other) duplicates using it (void)g_unlink(filename); dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN); char *c = filename + strlen(filename); sprintf(c, ".xmp"); (void)g_unlink(filename); dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } sqlite3_finalize(stmt); char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } g_list_free(list); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_queue_redraw_center(); return 0; }
void dt_similarity_match_image(uint32_t imgid,dt_similarity_t *data) { sqlite3_stmt *stmt; gboolean all_ok_for_match = TRUE; dt_similarity_histogram_t orginal_histogram,test_histogram; dt_similarity_lightmap_t orginal_lightmap,test_lightmap; /* create temporary mem table for matches */ DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "create temporary table if not exists similar_images (id integer,score real)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from similar_images", NULL, NULL, NULL); /* * get the histogram and lightmap data for image to match against */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select histogram,lightmap from images where id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if (sqlite3_step(stmt) == SQLITE_ROW) { /* get the histogram data */ uint32_t size = sqlite3_column_bytes(stmt,0); if (size!=sizeof(dt_similarity_histogram_t)) { all_ok_for_match = FALSE; dt_control_log(_("this image has not been indexed yet.")); } else memcpy(&orginal_histogram, sqlite3_column_blob(stmt, 0), sizeof(dt_similarity_histogram_t)); /* get the lightmap data */ size = sqlite3_column_bytes(stmt,1); if (size!=sizeof(dt_similarity_lightmap_t)) { all_ok_for_match = FALSE; dt_control_log(_("this image has not been indexed yet.")); } else memcpy(&orginal_lightmap, sqlite3_column_blob(stmt, 1), sizeof(dt_similarity_lightmap_t)); } else { all_ok_for_match = FALSE; dt_control_log(_("this image has not been indexed yet.")); } sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); /* * if all ok lets begin matching */ if (all_ok_for_match) { char query[4096]={0}; /* add target image with 100.0 in score into result to ensure it always shown in top */ sprintf(query,"insert into similar_images(id,score) values(%d,%f)",imgid,100.0); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); /* set an extended collection query for viewing the result of match */ dt_collection_set_extended_where(darktable.collection, ", similar_images where images.id = similar_images.id order by similar_images.score desc"); dt_collection_set_query_flags( darktable.collection, dt_collection_get_query_flags(darktable.collection) | COLLECTION_QUERY_USE_ONLY_WHERE_EXT); dt_collection_update(darktable.collection); dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED); /* loop thru images and generate score table */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id,histogram,lightmap from images", -1, &stmt, NULL); while (sqlite3_step(stmt) == SQLITE_ROW) { float score_histogram=0, score_lightmap=0, score_colormap=0; /* verify size of histogram and lightmap blob of test image */ if ( (sqlite3_column_bytes(stmt,1) == sizeof(dt_similarity_histogram_t)) && (sqlite3_column_bytes(stmt,2) == sizeof(dt_similarity_lightmap_t)) ) { /* * Get the histogram blob and calculate the similarity score */ memcpy(&test_histogram, sqlite3_column_blob(stmt, 1), sizeof(dt_similarity_histogram_t)); score_histogram = _similarity_match_histogram_rgb(data, &orginal_histogram, &test_histogram); /* * Get the lightmap blob and calculate the similarity score * 1.08 is a tuned constant that works well with threshold */ memcpy(&test_lightmap, sqlite3_column_blob(stmt, 2), sizeof(dt_similarity_lightmap_t)); score_lightmap = _similarity_match_lightmap(data, &orginal_lightmap, &test_lightmap); /* * then calculate the colormap similarity score */ score_colormap = _similarity_match_colormap(data, &orginal_lightmap, &test_lightmap); /* * calculate the similarity score */ float score = (pow(score_histogram, data->histogram_weight) * pow(score_lightmap, data->lightmap_weight) * pow(score_colormap, data->redmap_weight)); // fprintf(stderr,"score: %f, histo: %f, light: %f, color: %f\n",score,score_histogram,score_lightmap,score_colormap); /* * If current images scored, lets add it to similar_images table */ if(score >= 0.92) { sprintf(query,"insert into similar_images(id,score) values(%d,%f)",sqlite3_column_int(stmt, 0),score); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); /* lets redraw the view */ dt_control_queue_redraw_center(); } } else fprintf(stderr,"Image has inconsisten similarity matching data..\n"); } } sqlite3_finalize (stmt); }
void dt_collection_update_query(const dt_collection_t *collection) { char query[1024], confname[200]; gchar *complete_query = NULL; const int _n_r = dt_conf_get_int("plugins/lighttable/collect/num_rules"); const int num_rules = CLAMP(_n_r, 1, 10); char *conj[] = {"and", "or", "and not"}; complete_query = dt_util_dstrcat(complete_query, "("); for(int i=0; i<num_rules; i++) { snprintf(confname, sizeof(confname), "plugins/lighttable/collect/item%1d", i); const int property = dt_conf_get_int(confname); snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", i); gchar *text = dt_conf_get_string(confname); if(!text) break; snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", i); const int mode = dt_conf_get_int(confname); gchar *escaped_text = dt_util_str_replace(text, "'", "''"); get_query_string(property, escaped_text, query, sizeof(query)); if(i > 0) complete_query = dt_util_dstrcat(complete_query, " %s %s", conj[mode], query); else complete_query = dt_util_dstrcat(complete_query, "%s", query); g_free(escaped_text); g_free(text); } complete_query = dt_util_dstrcat(complete_query, ")"); // printf("complete query: `%s'\n", complete_query); /* set the extended where and the use of it in the query */ dt_collection_set_extended_where (collection, complete_query); dt_collection_set_query_flags (collection, (dt_collection_get_query_flags (collection) | COLLECTION_QUERY_USE_WHERE_EXT)); /* remove film id from default filter */ dt_collection_set_filter_flags (collection, (dt_collection_get_filter_flags (collection) & ~COLLECTION_FILTER_FILM_ID)); /* update query and at last the visual */ dt_collection_update (collection); /* free string */ g_free(complete_query); // remove from selected images where not in this query. sqlite3_stmt *stmt = NULL; const gchar *cquery = dt_collection_get_query(collection); complete_query = NULL; if(cquery && cquery[0] != '\0') { complete_query = dt_util_dstrcat(complete_query, "delete from selected_images where imgid not in (%s)", cquery); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), complete_query, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, 0); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, -1); sqlite3_step(stmt); sqlite3_finalize(stmt); /* free allocated strings */ g_free(complete_query); } /* raise signal of collection change, only if this is an original */ if (!collection->clone) dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED); }
int32_t dt_control_remove_images_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("removing %d image", "removing %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt = NULL; // check that we can safely remove the image char query[1024]; gboolean remove_ok = TRUE; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM images WHERE id IN (SELECT imgid FROM selected_images) AND flags&?1=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, DT_IMAGE_LOCAL_COPY); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); if (!dt_image_safe_remove(imgid)) { remove_ok = FALSE; break; } } sqlite3_finalize(stmt); if (!remove_ok) { dt_control_log(_("cannot remove local copy when the original file is not accessible.")); dt_control_backgroundjobs_destroy(darktable.control, jid); return 0; } // update remove status sprintf(query, "update images set flags = (flags | %d) where id in (select imgid from selected_images)",DT_IMAGE_REMOVE); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select distinct folder || '/' || filename from images, film_rolls where images.film_id = film_rolls.id and images.id in (select imgid from selected_images)", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { list = g_list_append(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0))); } sqlite3_finalize(stmt); while(t) { imgid = (long int)t->data; dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_queue_redraw_center(); return 0; }
int32_t dt_control_delete_images_job_run(dt_job_t *job) { int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; char *imgs = _get_image_list(t); int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("deleting %d image", "deleting %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt; _set_remove_flag(imgs); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = _get_full_pathname(imgs); free(imgs); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(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, &stmt, NULL); while(t) { imgid = GPOINTER_TO_INT(t->data); char filename[DT_MAX_PATH_LEN]; gboolean from_cache = FALSE; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache); int duplicates = 0; DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) == SQLITE_ROW) duplicates = sqlite3_column_int(stmt, 0); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); // remove from disk: if(duplicates == 1) { // there are no further duplicates so we can remove the source data file (void)g_unlink(filename); dt_image_remove(imgid); // all sidecar files - including left-overs - can be deleted; // left-overs can result when previously duplicates have been REMOVED; // no need to keep them as the source data file is gone. const int len = DT_MAX_PATH_LEN + 30; gchar pattern[len]; // NULL terminated list of glob patterns; should include "" and can be extended if needed static const gchar *glob_patterns[] = { "", "_[0-9][0-9]", "_[0-9][0-9][0-9]", "_[0-9][0-9][0-9][0-9]", NULL }; const gchar **glob_pattern = glob_patterns; GList *files = NULL; while(*glob_pattern) { snprintf(pattern, len, "%s", filename); gchar *c1 = pattern + strlen(pattern); while(*c1 != '.' && c1 > pattern) c1--; snprintf(c1, pattern + len - c1, "%s", *glob_pattern); const gchar *c2 = filename + strlen(filename); while(*c2 != '.' && c2 > filename) c2--; snprintf(c1+strlen(*glob_pattern), pattern + len - c1 - strlen(*glob_pattern), "%s.xmp", c2); #ifdef __WIN32__ WIN32_FIND_DATA data; HANDLE handle = FindFirstFile(pattern, &data); if(handle != INVALID_HANDLE_VALUE) { do files = g_list_append(files, g_strdup(data.cFileName)); while(FindNextFile(handle, &data)); } #else glob_t globbuf; if(!glob(pattern, 0, NULL, &globbuf)) { for(size_t i=0; i < globbuf.gl_pathc; i++) files = g_list_append(files, g_strdup(globbuf.gl_pathv[i])); globfree(&globbuf); } #endif glob_pattern++; } GList *file_iter = g_list_first(files); while(file_iter != NULL) { (void)g_unlink(file_iter->data); file_iter = g_list_next(file_iter); } g_list_free_full(files, g_free); } else { // don't remove the actual source data if there are further duplicates using it; // just delete the xmp file of the duplicate selected. dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN); char *c = filename + strlen(filename); sprintf(c, ".xmp"); dt_image_remove(imgid); (void)g_unlink(filename); } t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } sqlite3_finalize(stmt); char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } g_list_free(list); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); dt_control_queue_redraw_center(); return 0; }
int32_t dt_control_remove_images_job_run(dt_job_t *job) { int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; char *imgs = _get_image_list(t); int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("removing %d image", "removing %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt = NULL; // check that we can safely remove the image gboolean remove_ok = TRUE; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM images WHERE id IN (?2) AND flags&?1=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, DT_IMAGE_LOCAL_COPY); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgs, -1, SQLITE_STATIC); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); if (!dt_image_safe_remove(imgid)) { remove_ok = FALSE; break; } } sqlite3_finalize(stmt); if (!remove_ok) { dt_control_log(_("cannot remove local copy when the original file is not accessible.")); dt_control_backgroundjobs_destroy(darktable.control, jid); free(imgs); return 0; } // update remove status _set_remove_flag(imgs); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = _get_full_pathname(imgs); free(imgs); while(t) { imgid = GPOINTER_TO_INT(t->data); dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); dt_control_queue_redraw_center(); return 0; }