Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
film.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2013 johannes hanika.
4 Copyright (C) 2010-2012 Henrik Andersson.
5 Copyright (C) 2010-2018, 2020 Tobias Ellinghaus.
6 Copyright (C) 2012 James C. McPherson.
7 Copyright (C) 2012 Jesper Pedersen.
8 Copyright (C) 2012 José Carlos García Sogo.
9 Copyright (C) 2012 Richard Wonka.
10 Copyright (C) 2013 Dennis Gnad.
11 Copyright (C) 2013-2015 Jérémy Rosen.
12 Copyright (C) 2013-2014, 2020-2021 Pascal Obry.
13 Copyright (C) 2013 Simon Spannagel.
14 Copyright (C) 2014-2016 Roman Lebedev.
15 Copyright (C) 2017 luzpaz.
16 Copyright (C) 2018 Edgardo Hoszowski.
17 Copyright (C) 2018 parafin.
18 Copyright (C) 2019, 2022, 2025 Aurélien PIERRE.
19 Copyright (C) 2019-2020 Hanno Schwalm.
20 Copyright (C) 2019, 2021-2022 Philippe Weyland.
21 Copyright (C) 2020-2021 Aldric Renaudin.
22 Copyright (C) 2020 Heiko Bauke.
23 Copyright (C) 2020 Hubert Kowalski.
24 Copyright (C) 2020 JP Verrue.
25 Copyright (C) 2020 Nicolas Auffray.
26 Copyright (C) 2021-2022 HansBull.
27 Copyright (C) 2021 Ralf Brown.
28 Copyright (C) 2022 Martin Bařinka.
29
30 darktable is free software: you can redistribute it and/or modify
31 it under the terms of the GNU General Public License as published by
32 the Free Software Foundation, either version 3 of the License, or
33 (at your option) any later version.
34
35 darktable is distributed in the hope that it will be useful,
36 but WITHOUT ANY WARRANTY; without even the implied warranty of
37 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 GNU General Public License for more details.
39
40 You should have received a copy of the GNU General Public License
41 along with darktable. If not, see <http://www.gnu.org/licenses/>.
42*/
43#include "common/film.h"
44#include "common/collection.h"
45#include "common/darktable.h"
46#include "common/debug.h"
47#include "common/dtpthread.h"
48#include "common/image_cache.h"
49#include "common/tags.h"
50#include "control/conf.h"
51#include "control/control.h"
52#include "control/jobs.h"
53#include "views/view.h"
54
55#include <assert.h>
56#include <errno.h>
57#include <limits.h>
58#include <math.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <strings.h>
63#include <sys/stat.h>
64#include <sys/types.h>
65#include <unistd.h>
66#ifdef GDK_WINDOWING_QUARTZ
67#include "osx/osx.h"
68#endif
69
71{
73 film->last_loaded = film->num_images = 0;
74 film->dirname[0] = '\0';
75 film->dir = NULL;
76 film->id = -1;
77 film->ref = 0;
78}
79
81{
83 if(film->dir)
84 {
85 g_dir_close(film->dir);
86 film->dir = NULL;
87 }
88}
89
90void dt_film_set_query(const int32_t id)
91{
92 /* enable film id filter and set film id */
93 dt_conf_set_int("plugins/lighttable/collect/num_rules", 1);
94 dt_conf_set_int("plugins/lighttable/collect/item0", 0);
95 sqlite3_stmt *stmt;
96 // clang-format off
98 "SELECT id, folder"
99 " FROM main.film_rolls"
100 " WHERE id = ?1", -1, &stmt, NULL);
101 // clang-format on
102 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
103 if(sqlite3_step(stmt) == SQLITE_ROW)
104 {
105 dt_conf_set_string("plugins/lighttable/collect/string0", (gchar *)sqlite3_column_text(stmt, 1));
106 }
107 sqlite3_finalize(stmt);
109}
110
111int32_t dt_film_get_id(const char *folder)
112{
113 int32_t filmroll_id = -1;
114 sqlite3_stmt *stmt;
116#ifdef _WIN32
117 "SELECT id FROM main.film_rolls WHERE folder LIKE ?1",
118#else
119 "SELECT id FROM main.film_rolls WHERE folder = ?1",
120#endif
121 -1, &stmt, NULL);
122 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, folder, -1, SQLITE_STATIC);
123 if(sqlite3_step(stmt) == SQLITE_ROW) filmroll_id = sqlite3_column_int(stmt, 0);
124 sqlite3_finalize(stmt);
125 return filmroll_id;
126}
127
128int dt_film_open(const int32_t id)
129{
130 sqlite3_stmt *stmt;
131 // clang-format off
133 "SELECT id, folder"
134 " FROM main.film_rolls"
135 " WHERE id = ?1", -1, &stmt, NULL);
136 // clang-format on
137 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
138 if(sqlite3_step(stmt) == SQLITE_ROW)
139 {
140 sqlite3_finalize(stmt);
141
142 // clang-format off
144 "UPDATE main.film_rolls"
145 " SET access_timestamp = strftime('%s', 'now')"
146 " WHERE id = ?1", -1, &stmt,
147 NULL);
148 // clang-format on
149 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
150 sqlite3_step(stmt);
151 }
152 sqlite3_finalize(stmt);
153 // TODO: prefetch to cache using image_open
157 return 0;
158}
159
160
161int dt_film_new(dt_film_t *film, const char *directory)
162{
163 sqlite3_stmt *stmt;
164
165 // Try open filmroll for folder if exists
166 film->id = -1;
167 g_strlcpy(film->dirname, directory, sizeof(film->dirname));
168
169 // remove a closing '/', unless it's also the start
170 char *last = &film->dirname[strlen(film->dirname) - 1];
171 if(*last == '/' && last != film->dirname) *last = '\0';
172
173 /* if we didn't find an id, lets instantiate a new filmroll */
174 film->id = dt_film_get_id(film->dirname);
175
176 /* if we didn't find an id, lets instantiate a new filmroll */
177 if(film->id <= 0)
178 {
179 // create a new filmroll
180 /* insert a new film roll into database */
181 // clang-format off
183 "INSERT INTO main.film_rolls (id, access_timestamp, folder)"
184 " VALUES (NULL, strftime('%s', 'now'), ?1)",
185 -1, &stmt, NULL);
186 // clang-format on
187 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, film->dirname, -1, SQLITE_STATIC);
188 const int rc = sqlite3_step(stmt);
189 if(rc != SQLITE_DONE)
190 fprintf(stderr, "[film_new] failed to insert film roll! %s\n",
191 sqlite3_errmsg(dt_database_get(darktable.db)));
192 sqlite3_finalize(stmt);
193 /* requery for filmroll and fetch new id */
194 film->id = dt_film_get_id(film->dirname);
195 if(film->id)
196 {
197 // add it to the table memory.film_folder
198 sqlite3_stmt *stmt2;
199 // clang-format off
201 "INSERT INTO memory.film_folder (id, status) "
202 "VALUES (?1, 1)",
203 -1, &stmt2, NULL);
204 // clang-format on
205 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 1, film->id);
206 sqlite3_step(stmt2);
207 sqlite3_finalize(stmt2);
208 }
209 }
210#ifdef _WIN32
211 else
212 {
213 // make sure we reuse the same path case
214 // clang-format off
216 "SELECT folder FROM main.film_rolls WHERE id = ?1",
217 -1, &stmt, NULL);
218 // clang-format on
219 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film->id);
220 if(sqlite3_step(stmt) != SQLITE_ROW)
221 g_strlcpy(film->dirname, (const char *)sqlite3_column_text(stmt, 0), sizeof(film->dirname));
222 sqlite3_finalize(stmt);
223 }
224#endif
225
226 if(film->id <= 0)
227 dt_print(DT_DEBUG_IMPORT, "[Import] Could not create a new filmid for %s\n", directory);
228 else
229 dt_print(DT_DEBUG_IMPORT, "[Import] Reusing or creating filmid %i for %s\n", film->id, directory);
230
231 if(film->id <= 0) return 0;
232 film->last_loaded = 0;
233 return film->id;
234}
235
236int dt_film_import(const char *dirname)
237{
238 GError *error = NULL;
239
240 /* initialize a film object*/
241 dt_film_t *film = (dt_film_t *)malloc(sizeof(dt_film_t));
242 dt_film_init(film);
243
244 dt_film_new(film, dirname);
245
246 /* bail out if we got troubles */
247 if(film->id <= 0)
248 {
249 // if the film is empty => remove it again.
250 if(dt_film_is_empty(film->id))
251 {
252 dt_film_remove(film->id);
253 }
254 dt_film_cleanup(film);
255 dt_free(film);
256 return 0;
257 }
258
259 // when called without job system running the import will be done synchronously and destroy the film object
260 const int filmid = film->id;
261
262 /* at last put import film job on queue */
263 film->last_loaded = 0;
264 film->dir = g_dir_open(film->dirname, 0, &error);
265 if(error)
266 {
267 fprintf(stderr, "[film_import] failed to open directory %s: %s\n", film->dirname, error->message);
268 g_error_free(error);
269 dt_film_cleanup(film);
270 dt_free(film);
271 return 0;
272 }
273
274 // launch import job
276
277 return filmid;
278}
279
280static gboolean ask_and_delete(gpointer user_data)
281{
282 GList *empty_dirs = (GList *)user_data;
283 const int n_empty_dirs = g_list_length(empty_dirs);
284
285 GtkWidget *dialog;
287
288 dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
289 GTK_BUTTONS_YES_NO,
290 ngettext("do you want to remove this empty directory?",
291 "do you want to remove these empty directories?", n_empty_dirs));
292#ifdef GDK_WINDOWING_QUARTZ
294#endif
295
296 gtk_window_set_title(GTK_WINDOW(dialog),
297 ngettext("remove empty directory?", "remove empty directories?", n_empty_dirs));
298
299 GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
300
301 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
302 gtk_widget_set_vexpand(scroll, TRUE);
303 dt_gui_add_class(scroll, "dt_recessed_scroll");
304
305 GtkListStore *store = gtk_list_store_new(1, G_TYPE_STRING);
306
307 for(GList *list_iter = empty_dirs; list_iter; list_iter = g_list_next(list_iter))
308 {
309 GtkTreeIter iter;
310 gtk_list_store_append(store, &iter);
311 gtk_list_store_set(store, &iter, 0, list_iter->data, -1);
312 }
313
314 GtkWidget *tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
315 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
316 gtk_widget_set_name(GTK_WIDGET(tree), "delete-dialog");
317 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_("name"), gtk_cell_renderer_text_new(),
318 "text", 0, NULL);
319 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
320
321 gtk_container_add(GTK_CONTAINER(scroll), tree);
322 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
323 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), DT_PIXEL_APPLY_DPI(25));
324
325 gtk_container_add(GTK_CONTAINER(content_area), scroll);
326
327 gtk_widget_show_all(dialog); // needed for the content area!
328
329 const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
330 gtk_widget_destroy(dialog);
331 if(res == GTK_RESPONSE_YES)
332 for(GList *iter = empty_dirs; iter; iter = g_list_next(iter))
333 rmdir((char *)iter->data);
334
335 g_list_free_full(empty_dirs, dt_free_gpointer);
336 empty_dirs = NULL;
337 g_object_unref(store);
338
339 return FALSE;
340}
341
343{
344 // remove all empty film rolls from db:
345 GList *empty_dirs = NULL;
346 gboolean ask_before_rmdir = dt_conf_get_bool("ask_before_rmdir");
347 gboolean raise_signal = FALSE;
348 sqlite3_stmt *stmt;
350 "SELECT id,folder"
351 " FROM main.film_rolls AS B"
352 " WHERE (SELECT COUNT(*)"
353 " FROM main.images AS A"
354 " WHERE A.film_id=B.id) = 0",
355 -1, &stmt, NULL);
356 while(sqlite3_step(stmt) == SQLITE_ROW)
357 {
358 sqlite3_stmt *inner_stmt;
359 raise_signal = TRUE;
360 const gint id = sqlite3_column_int(stmt, 0);
361 const gchar *folder = (const gchar *)sqlite3_column_text(stmt, 1);
363 "DELETE FROM main.film_rolls WHERE id=?1", -1,
364 &inner_stmt, NULL);
365 DT_DEBUG_SQLITE3_BIND_INT(inner_stmt, 1, id);
366 sqlite3_step(inner_stmt);
367 sqlite3_finalize(inner_stmt);
368
369 if(dt_util_is_dir_empty(folder))
370 {
371 if(ask_before_rmdir) empty_dirs = g_list_prepend(empty_dirs, g_strdup(folder));
372 else rmdir(folder);
373 }
374 }
375 sqlite3_finalize(stmt);
377
378 // dispatch asking for deletion (and subsequent deletion) to the gui thread
379 if(empty_dirs)
380 g_idle_add(ask_and_delete, g_list_reverse(empty_dirs));
381}
382
383gboolean dt_film_is_empty(const int id)
384{
385 gboolean empty = FALSE;
386 sqlite3_stmt *stmt;
388 "SELECT id FROM main.images WHERE film_id = ?1", -1,
389 &stmt, NULL);
390 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
391 if(sqlite3_step(stmt) != SQLITE_ROW) empty = TRUE;
392 sqlite3_finalize(stmt);
393 return empty;
394}
395
396// This is basically the same as dt_image_remove() from common/image.c.
397// It just does the iteration over all images in the SQL statement
398void dt_film_remove(const int id)
399{
400 // only allowed if local copies have their original accessible
401
402 sqlite3_stmt *stmt;
403
404 gboolean remove_ok = TRUE;
405
407 "SELECT id FROM main.images WHERE film_id = ?1", -1,
408 &stmt, NULL);
409 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
410
411 while(sqlite3_step(stmt) == SQLITE_ROW)
412 {
413 const int32_t imgid = sqlite3_column_int(stmt, 0);
414 if(!dt_image_safe_remove(imgid))
415 {
416 remove_ok = FALSE;
417 break;
418 }
419 }
420 sqlite3_finalize(stmt);
421
422 if(!remove_ok)
423 {
424 dt_control_log(_("cannot remove film roll having local copies with non accessible originals"));
425 return;
426 }
427
428 // query is needed a second time for mipmap and image cache
430 "SELECT id FROM main.images WHERE film_id = ?1", -1,
431 &stmt, NULL);
432 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
433 while(sqlite3_step(stmt) == SQLITE_ROW)
434 {
435 const int32_t imgid = sqlite3_column_int(stmt, 0);
439 }
440 sqlite3_finalize(stmt);
441
442 // due to foreign keys, all images with references to the film roll are deleted,
443 // and likewise all entries with references to those images
445 "DELETE FROM main.film_rolls WHERE id = ?1", -1,
446 &stmt, NULL);
447 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
448 sqlite3_step(stmt);
449 sqlite3_finalize(stmt);
450 // dt_control_update_recent_films();
451
453}
454
455void dt_film_relocate(const char *old_path, const char *new_path)
456{
457 if(IS_NULL_PTR(old_path) || IS_NULL_PTR(new_path)) return;
458
459 // Gather every film roll under old_path together with its remapped folder first, so we do
460 // not mutate the table while still iterating the SELECT.
461 sqlite3_stmt *stmt;
462 gchar *like = g_strdup_printf("%s%%", old_path);
464 "SELECT id, folder FROM main.film_rolls WHERE folder LIKE ?1", -1, &stmt, NULL);
465 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, like, -1, SQLITE_TRANSIENT);
466 g_free(like);
467
468 GList *ids = NULL;
469 GList *folders = NULL;
470 while(sqlite3_step(stmt) == SQLITE_ROW)
471 {
472 const int id = sqlite3_column_int(stmt, 0);
473 const gchar *old = (const gchar *)sqlite3_column_text(stmt, 1);
474 gchar *final = g_strcmp0(old, old_path) ? g_strdup_printf("%s/%s", new_path, old + strlen(old_path) + 1)
475 : g_strdup(new_path);
476 ids = g_list_prepend(ids, GINT_TO_POINTER(id));
477 folders = g_list_prepend(folders, final);
478 }
479 sqlite3_finalize(stmt);
480
481 sqlite3_stmt *up;
483 "UPDATE main.film_rolls SET folder=?1 WHERE id=?2", -1, &up, NULL);
484 for(GList *i = ids, *f = folders; i && f; i = g_list_next(i), f = g_list_next(f))
485 {
486 sqlite3_reset(up);
487 sqlite3_clear_bindings(up);
488 DT_DEBUG_SQLITE3_BIND_TEXT(up, 1, (const char *)f->data, -1, SQLITE_TRANSIENT);
489 DT_DEBUG_SQLITE3_BIND_INT(up, 2, GPOINTER_TO_INT(i->data));
490 sqlite3_step(up);
491 }
492 sqlite3_finalize(up);
493 g_list_free(ids);
494 g_list_free_full(folders, g_free);
495}
496
497GList *dt_film_get_image_ids(const int filmid)
498{
499 GList *result = NULL;
500 sqlite3_stmt *stmt;
502 "SELECT id FROM main.images WHERE film_id = ?1",
503 -1, &stmt, NULL);
504 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid);
505 while(sqlite3_step(stmt) == SQLITE_ROW)
506 {
507 const int id = sqlite3_column_int(stmt, 0);
508 result = g_list_prepend(result, GINT_TO_POINTER(id));
509 }
510 sqlite3_finalize(stmt);
511 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
512}
513
515{
516 sqlite3_stmt *stmt, *stmt2;
518 "DELETE FROM memory.film_folder",
519 -1, &stmt, NULL);
520 sqlite3_step(stmt);
521 sqlite3_finalize(stmt);
522
524 "SELECT id, folder FROM main.film_rolls",
525 -1, &stmt, NULL);
526
527 // clang-format off
529 "INSERT INTO memory.film_folder (id, status) "
530 "VALUES (?1, ?2)",
531 -1, &stmt2, NULL);
532 // clang-format on
533
534 while(sqlite3_step(stmt) == SQLITE_ROW)
535 {
536 const int filmid = sqlite3_column_int(stmt, 0);
537 const char *folder = (char *)sqlite3_column_text(stmt, 1);
538 const int status = g_file_test(folder, G_FILE_TEST_IS_DIR);
539 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 1, filmid);
540 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 2, status);
541 sqlite3_step(stmt2);
542 sqlite3_reset(stmt2);
543 }
544 sqlite3_finalize(stmt);
545 sqlite3_finalize(stmt2);
546}
547
548// clang-format off
549// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
550// vim: shiftwidth=2 expandtab tabstop=2 cindent
551// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
552// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:142
@ DT_COLLECTION_CHANGE_NEW_QUERY
Definition collection.h:149
const dt_aligned_pixel_t f
int dt_image_local_copy_reset(const int32_t imgid)
gboolean dt_image_safe_remove(const int32_t imgid)
int dt_conf_get_bool(const char *name)
void dt_conf_set_int(const char *name, int val)
void dt_conf_set_string(const char *name, const char *val)
void dt_control_log(const char *msg,...)
Definition control.c:761
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_IMPORT
Definition darktable.h:742
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3646
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition debug.h:107
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition debug.h:118
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition debug.h:115
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int32_t imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total, const gboolean high_quality, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_export_metadata_t *metadata)
Definition disk.c:252
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:359
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:379
void dt_film_init(dt_film_t *film)
Definition film.c:70
void dt_film_relocate(const char *old_path, const char *new_path)
Definition film.c:455
gboolean dt_film_is_empty(const int id)
Definition film.c:383
int dt_film_new(dt_film_t *film, const char *directory)
Definition film.c:161
void dt_film_remove_empty()
Definition film.c:342
void dt_film_remove(const int id)
Definition film.c:398
int32_t dt_film_get_id(const char *folder)
Definition film.c:111
void dt_film_set_query(const int32_t id)
Definition film.c:90
GList * dt_film_get_image_ids(const int filmid)
Definition film.c:497
int dt_film_open(const int32_t id)
Definition film.c:128
static gboolean ask_and_delete(gpointer user_data)
Definition film.c:280
void dt_film_set_folder_status()
Definition film.c:514
int dt_film_import(const char *dirname)
Definition film.c:236
void dt_film_cleanup(dt_film_t *film)
Definition film.c:80
dt_job_t * dt_film_import1_create(dt_film_t *film)
Definition film_jobs.c:79
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
void dt_image_cache_remove(dt_image_cache_t *cache, const int32_t imgid)
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:405
@ DT_JOB_QUEUE_USER_BG
Definition jobs.h:55
void dt_mipmap_cache_remove(dt_mipmap_cache_t *cache, const int32_t imgid, const gboolean flush_disk)
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_FILMROLLS_CHANGED
This signal is raised when a filmroll is deleted/changed but not imported.
Definition signal.h:156
@ DT_SIGNAL_FILMROLLS_REMOVED
This signal is raised only when a filmroll is removed.
Definition signal.h:159
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_collection_t * collection
Definition darktable.h:781
struct dt_mipmap_cache_t * mipmap_cache
Definition darktable.h:776
const struct dt_database_t * db
Definition darktable.h:779
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_image_cache_t * image_cache
Definition darktable.h:777
struct dt_view_manager_t * view_manager
Definition darktable.h:772
struct dt_control_t * control
Definition darktable.h:773
int32_t num_images
Definition film.h:49
dt_pthread_mutex_t images_mutex
Definition film.h:47
int32_t id
Definition film.h:45
GDir * dir
Definition film.h:48
int32_t ref
Definition film.h:50
int32_t last_loaded
Definition film.h:49
char dirname[512]
Definition film.h:46
dt_ui_t * ui
Definition gtk.h:164
gboolean dt_util_is_dir_empty(const char *dirname)
Definition utility.c:375
void dt_view_manager_reset(dt_view_manager_t *vm)
Definition view.c:486