Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
view.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2015 johannes hanika.
4 Copyright (C) 2010-2012, 2014 Henrik Andersson.
5 Copyright (C) 2010 Richard Hughes.
6 Copyright (C) 2010-2020 Tobias Ellinghaus.
7 Copyright (C) 2011 Antony Dovgal.
8 Copyright (C) 2011 Kanstantsin Shautsou.
9 Copyright (C) 2011 Moritz Lipp.
10 Copyright (C) 2011 Omari Stephens.
11 Copyright (C) 2011 Robert Bieber.
12 Copyright (C) 2011, 2013 Simon Spannagel.
13 Copyright (C) 2012-2014 José Carlos García Sogo.
14 Copyright (C) 2012, 2014-2015 Jérémy Rosen.
15 Copyright (C) 2012 marcel.
16 Copyright (C) 2012 Michal Babej.
17 Copyright (C) 2012 Mika Boström.
18 Copyright (C) 2012 Pascal de Bruijn.
19 Copyright (C) 2012-2021 Pascal Obry.
20 Copyright (C) 2012 Richard Wonka.
21 Copyright (C) 2012, 2014 Ulrich Pegelow.
22 Copyright (C) 2013, 2016, 2019-2022 Aldric Renaudin.
23 Copyright (C) 2013 Benjamin Cahill.
24 Copyright (C) 2013 Gaspard Jankowiak.
25 Copyright (C) 2013 Pierre Le Magourou.
26 Copyright (C) 2013-2016, 2020 Roman Lebedev.
27 Copyright (C) 2013-2014 Ronny Kahl.
28 Copyright (C) 2014 Mikhail Trishchenkov.
29 Copyright (C) 2014 Pedro Côrte-Real.
30 Copyright (C) 2016 Asma.
31 Copyright (C) 2017, 2019, 2021 luzpaz.
32 Copyright (C) 2017, 2019 Marcello Mamino.
33 Copyright (C) 2017-2018 Matthieu Moy.
34 Copyright (C) 2017, 2019-2020 parafin.
35 Copyright (C) 2018 grand-piano.
36 Copyright (C) 2018 rawfiner.
37 Copyright (C) 2018 Rikard Öxler.
38 Copyright (C) 2019-2020, 2022-2023, 2025-2026 Aurélien PIERRE.
39 Copyright (C) 2019 Edgardo Hoszowski.
40 Copyright (C) 2019 Felipe Contreras.
41 Copyright (C) 2019-2020 Heiko Bauke.
42 Copyright (C) 2019 Jacopo Guderzo.
43 Copyright (C) 2020 Chris Elston.
44 Copyright (C) 2020 Dan Torop.
45 Copyright (C) 2020-2022 Diederik Ter Rahe.
46 Copyright (C) 2020 Hanno Schwalm.
47 Copyright (C) 2020-2021 Hubert Kowalski.
48 Copyright (C) 2020 Marco.
49 Copyright (C) 2020 Miloš Komarčević.
50 Copyright (C) 2020-2021 Philippe Weyland.
51 Copyright (C) 2021 Bill Ferguson.
52 Copyright (C) 2021 Ralf Brown.
53 Copyright (C) 2022 Martin Bařinka.
54 Copyright (C) 2022 Nicolas Auffray.
55 Copyright (C) 2022 Sakari Kapanen.
56 Copyright (C) 2023 Maurizio Paglia.
57 Copyright (C) 2025-2026 Guillaume Stutin.
58
59 darktable is free software: you can redistribute it and/or modify
60 it under the terms of the GNU General Public License as published by
61 the Free Software Foundation, either version 3 of the License, or
62 (at your option) any later version.
63
64 darktable is distributed in the hope that it will be useful,
65 but WITHOUT ANY WARRANTY; without even the implied warranty of
66 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67 GNU General Public License for more details.
68
69 You should have received a copy of the GNU General Public License
70 along with darktable. If not, see <http://www.gnu.org/licenses/>.
71*/
72
73#include "views/view.h"
75#include "common/variables.h"
76#include "widgets/bauhaus.h"
77#include "common/sentry.h"
78#include "common/telemetry.h"
79#include "caches/image_cache.h"
80#include "caches/mipmap_cache.h"
81#include "common/module.h"
83#include "common/times.h"
84#include "common/undo.h"
86#include "common/conf.h"
87#include "common/image.h"
88#include "common/paths.h" // DT_PATH_MAX
89#include "control/control.h"
91#include "develop/develop.h"
92#include "develop/supervisor.h"
93#include "widgets/expander.h"
95
96#include "gui/application.h"
97#include "libs/lib.h"
98#ifdef GDK_WINDOWING_QUARTZ
99#endif
100
101#include <glib.h>
102#include <glib/gi18n.h>
103#include <math.h>
104#include <pango/pangocairo.h>
105#include <stdio.h>
106#include <stdlib.h>
107#include <string.h>
108#include <strings.h>
109#include "control/signal.h"
110
111#define DECORATION_SIZE_LIMIT 40
112
114static int dt_view_load_module(void *v, const char *libname, const char *module_name);
116static int32_t _view_surface_fetch_job_run(dt_job_t *job);
117
119{
121 vm->current_view = NULL;
122 vm->audio.audio_player_id = -1;
123 vm->active_images = NULL;
124}
125
127{
128 for(GList *iter = vm->views; iter; iter = g_list_next(iter))
129 {
130 dt_view_t *view = (dt_view_t *)iter->data;
131 if(view->gui_init) view->gui_init(view);
132 }
133}
134
136{
137 g_list_free(vm->active_images);
138 vm->active_images = NULL;
139 for(GList *iter = vm->views; iter; iter = g_list_next(iter)) dt_view_unload_module((dt_view_t *)iter->data);
140 g_list_free_full(vm->views, dt_free_gpointer);
141 vm->views = NULL;
142}
143
148
149// we want a stable order of views, for example for viewswitcher.
150// anything not hardcoded will be put alphabetically wrt. localised names.
151static gint sort_views(gconstpointer a, gconstpointer b)
152{
153 static const char *view_order[] = {"lighttable", "darkroom"};
154 static const int n_view_order = G_N_ELEMENTS(view_order);
155
156 dt_view_t *av = (dt_view_t *)a;
157 dt_view_t *bv = (dt_view_t *)b;
158 const char *aname = av->name(av);
159 const char *bname = bv->name(bv);
160 int apos = n_view_order;
161 int bpos = n_view_order;
162
163 for(int i = 0; i < n_view_order; i++)
164 {
165 if(!strcmp(av->module_name, view_order[i])) apos = i;
166 if(!strcmp(bv->module_name, view_order[i])) bpos = i;
167 }
168
169 // order will be zero iff apos == bpos which can only happen when both views are not in view_order
170 const int order = apos - bpos;
171 return order ? order : strcmp(aname, bname);
172}
173
178
179/* default flags for view which does not implement the flags() function */
180static uint32_t default_flags()
181{
182 return 0;
183}
184
186static int dt_view_load_module(void *v, const char *libname, const char *module_name)
187{
188 dt_view_t *module = (dt_view_t *)v;
189 g_strlcpy(module->module_name, module_name, sizeof(module->module_name));
190
191#define INCLUDE_API_FROM_MODULE_LOAD "view_load_module"
192#include "views/view_api.h"
193
194 module->data = NULL;
195 module->vscroll_size = module->vscroll_viewport_size = 1.0;
196 module->hscroll_size = module->hscroll_viewport_size = 1.0;
197 module->vscroll_pos = module->hscroll_pos = 0.0;
198 module->height = module->width = 100; // set to non-insane defaults before first expose/configure.
199
200 if(module->init) module->init(module);
201
202 return 0;
203}
204
207{
208 if(view->cleanup) view->cleanup(view);
209
210 if(view->module) g_module_close(view->module);
211}
212
213void dt_vm_remove_child(GtkWidget *widget, gpointer data)
214{
215 if(GTK_IS_CONTAINER(data))
216 gtk_container_remove(GTK_CONTAINER(data), widget);
217}
218
219/*
220 When expanders get destroyed, they destroy the child
221 so remove the child before that
222 */
223static void _remove_child(GtkWidget *child,GtkContainer *container)
224{
225 // some libs module can be used inside popups and not attached to panels, they have no container.
226 if(DTGTK_IS_EXPANDER(child))
227 {
229 if(GTK_IS_CONTAINER(evb))
230 gtk_container_remove(GTK_CONTAINER(evb), dtgtk_expander_get_body(DTGTK_EXPANDER(child)));
231 gtk_widget_destroy(child);
232 }
233 else if(GTK_IS_CONTAINER(container))
234 {
235 gtk_container_remove(container, child);
236 }
237}
238
239int dt_view_manager_switch(dt_view_manager_t *vm, const char *view_name)
240{
241 gboolean switching_to_none = *view_name == '\0';
242 dt_view_t *new_view = NULL;
243
244 if(!switching_to_none)
245 {
246 for(GList *iter = vm->views; iter; iter = g_list_next(iter))
247 {
248 dt_view_t *v = (dt_view_t *)iter->data;
249 if(!strcmp(v->module_name, view_name))
250 {
251 new_view = v;
252 break;
253 }
254 }
255 if(IS_NULL_PTR(new_view)) return 1; // the requested view doesn't exist
256 }
257
258 return dt_view_manager_switch_by_view(vm, new_view);
259}
260
262{
263 dt_view_t *old_view = vm->current_view;
264 dt_view_t *new_view = (dt_view_t *)nv; // views belong to us, we can de-const them :-)
265
266 // reset the cursor to the default one
267 dt_control_change_cursor(GDK_LEFT_PTR);
268
269 /* Reset Gtk focus */
270 gtk_window_set_focus(GTK_WINDOW(dt_gui_main_window()), NULL);
272
273 // also ignore what scrolling there was previously happening
274 memset(dt_gui_get_global()->scroll_to, 0, sizeof(dt_gui_get_global()->scroll_to));
275
276 // destroy old module list
277
278 /* clear the undo list, for now we do this unconditionally. At some point we will probably want to clear
279 only part
280 of the undo list. This should probably done with a view proxy routine returning the type of undo to
281 remove. */
283
284 /* Special case when entering nothing (just before leaving dt) */
285 if(IS_NULL_PTR(new_view))
286 {
287 if(old_view)
288 {
289 /* leave the current view*/
290 if(old_view->leave) old_view->leave(old_view);
291
292 /* iterator plugins and cleanup plugins in current view */
293 for(GList *iter = dt_lib_get_global()->plugins; iter; iter = g_list_next(iter))
294 {
295 dt_lib_module_t *plugin = (dt_lib_module_t *)(iter->data);
296
297 /* does this module belong to current view ?*/
298 if(dt_lib_is_visible_in_view(plugin, old_view))
299 {
300 if(plugin->view_leave) plugin->view_leave(plugin, old_view, NULL);
301 plugin->gui_cleanup(plugin);
302 plugin->data = NULL;
303 plugin->widget = NULL;
304 }
305 }
306 }
307
308 /* remove all widgets in all containers */
310 for(int l = 0; l < DT_UI_CONTAINER_SIZE; l++)
312 vm->current_view = NULL;
313
314 return 0;
315 }
316
317 // invariant: !IS_NULL_PTR(new_view) after this point
318 assert(!IS_NULL_PTR(new_view));
319
320 if(new_view->try_enter)
321 {
322 const int error = new_view->try_enter(new_view);
323 if(error)
324 {
326 return error;
327 }
328 }
329
330 /* cleanup current view before initialization of new */
331 if(old_view)
332 {
333 /* leave current view */
334 if(old_view->leave) old_view->leave(old_view);
335
336 /* iterator plugins and cleanup plugins in current view */
337 for(GList *iter = dt_lib_get_global()->plugins; iter; iter = g_list_next(iter))
338 {
339 dt_lib_module_t *plugin = (dt_lib_module_t *)(iter->data);
340
341 /* does this module belong to current view ?*/
342 if(dt_lib_is_visible_in_view(plugin, old_view))
343 {
344 if(plugin->view_leave) plugin->view_leave(plugin, old_view, new_view);
345 }
346 }
347
348 /* remove all widets in all containers */
349 for(int l = 0; l < DT_UI_CONTAINER_SIZE; l++)
351 }
352
353 /* change current view to the new view */
354 vm->current_view = new_view;
355
356 /* restore visible stat of panels for the new view */
358
359 /* lets add plugins related to new view into panels.
360 * this has to be done in reverse order to have the lowest position at the bottom! */
361 for(GList *iter = g_list_last(dt_lib_get_global()->plugins); iter; iter = g_list_previous(iter))
362 {
363 dt_lib_module_t *plugin = (dt_lib_module_t *)(iter->data);
364 if(dt_lib_is_visible_in_view(plugin, new_view))
365 {
366
367 /* try get the module expander */
369
370 /* if we didn't get an expander let's add the widget */
371 if(IS_NULL_PTR(w)) w = plugin->widget;
372
374 // some plugins help links depend on the view
375 if(!strcmp(plugin->plugin_name,"module_toolbox")
376 || !strcmp(plugin->plugin_name,"view_toolbox"))
377 {
378 dt_view_type_flags_t view_type = new_view->view(new_view);
379 if(view_type == DT_VIEW_LIGHTTABLE)
380 dt_gui_add_help_link(w, dt_get_help_url("lighttable_mode"));
381 if(view_type == DT_VIEW_DARKROOM)
382 dt_gui_add_help_link(w, dt_get_help_url("darkroom_bottom_panel"));
383 }
384
385
386 /* add module to its container */
387 dt_ui_container_add_widget(dt_gui_get_ui(), plugin->container(plugin), w);
388 }
389 }
390
391 /* hide/show modules as last config */
392 for(GList *iter = dt_lib_get_global()->plugins; iter; iter = g_list_next(iter))
393 {
394 dt_lib_module_t *plugin = (dt_lib_module_t *)(iter->data);
395 if(dt_lib_is_visible_in_view(plugin, new_view))
396 {
397 /* set expanded if last mode was that */
398 char var[1024];
399 gboolean expanded = FALSE;
400 gboolean visible = dt_lib_is_visible(plugin);
401 if(plugin->expandable(plugin))
402 {
403 snprintf(var, sizeof(var), "plugins/%s/%s/expanded", new_view->module_name, plugin->plugin_name);
404 expanded = dt_conf_get_bool(var);
405 dt_lib_gui_set_expanded(plugin, expanded);
406 dt_lib_set_visible(plugin, visible);
407 }
408 else
409 {
410 /* show/hide plugin widget depending on expanded flag or if plugin
411 not is expandeable() */
412 if(visible)
413 gtk_widget_show_all(plugin->widget);
414 else
415 gtk_widget_hide(plugin->widget);
416 }
417 if(plugin->view_enter) plugin->view_enter(plugin, old_view, new_view);
418 }
419 }
420
421 /* enter view. crucially, do this before initing the plugins below,
422 as e.g. modulegroups requires the dr stuff to be inited. */
423 if(new_view->enter) new_view->enter(new_view);
424
425 /* record view usage for crash reports and usage analytics */
428
429 /* raise view changed signal */
431
432 // update log visibility
434
435 // update toast visibility
437 return 0;
438}
439
441{
442 if(IS_NULL_PTR(vm->current_view)) return "";
443 if(vm->current_view->name)
444 return vm->current_view->name(vm->current_view);
445 else
446 return vm->current_view->module_name;
447}
448
450{
451 if(IS_NULL_PTR(vm) || IS_NULL_PTR(vm->current_view) || IS_NULL_PTR(vm->current_view->flags)) return 0;
452 return vm->current_view->flags();
453}
454
455void dt_view_manager_expose(dt_view_manager_t *vm, cairo_t *cr, int32_t width, int32_t height,
456 int32_t pointerx, int32_t pointery)
457{
459 {
461 cairo_paint(cr);
462 return;
463 }
464 vm->current_view->width = width;
466
467 if(vm->current_view->expose)
468 {
469 /* expose the view */
470 cairo_rectangle(cr, 0, 0, vm->current_view->width, vm->current_view->height);
471 cairo_clip(cr);
472 cairo_new_path(cr);
473 cairo_save(cr);
474 float px = pointerx, py = pointery;
475 if(pointery > vm->current_view->height)
476 {
477 px = 10000.0;
478 py = -1.0;
479 }
480 vm->current_view->expose(vm->current_view, cr, vm->current_view->width, vm->current_view->height, px, py);
481
482 cairo_restore(cr);
483 /* expose plugins */
484 for(const GList *plugins = g_list_last(dt_lib_get_global()->plugins); plugins; plugins = g_list_previous(plugins))
485 {
486 dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
487
488 /* does this module belong to current view ?*/
489 if(plugin->gui_post_expose
491 plugin->gui_post_expose(plugin, cr, vm->current_view->width, vm->current_view->height, px, py);
492 }
493 }
494}
495
497{
498 if(IS_NULL_PTR(vm->current_view)) return;
499 if(vm->current_view->reset) vm->current_view->reset(vm->current_view);
500}
501
503{
504 if(IS_NULL_PTR(vm->current_view)) return;
505 dt_view_t *v = vm->current_view;
506
507 /* lets check if any plugins want to handle mouse move */
508 gboolean handled = FALSE;
509 for(const GList *plugins = g_list_last(dt_lib_get_global()->plugins);
510 plugins;
511 plugins = g_list_previous(plugins))
512 {
513 dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
514
515 /* does this module belong to current view ?*/
516 if(plugin->mouse_leave && dt_lib_is_visible_in_view(plugin, v))
517 if(plugin->mouse_leave(plugin)) handled = TRUE;
518 }
519
520 /* if not handled by any plugin let pass to view handler*/
521 if(!handled && v->mouse_leave) v->mouse_leave(v);
522}
523
525{
526 if(IS_NULL_PTR(vm->current_view)) return;
527 if(vm->current_view->mouse_enter) vm->current_view->mouse_enter(vm->current_view);
528}
529
530void dt_view_manager_mouse_moved(dt_view_manager_t *vm, double x, double y, double pressure, int which)
531{
532 if(IS_NULL_PTR(vm->current_view)) return;
533 dt_view_t *v = vm->current_view;
534
535 /* lets check if any plugins want to handle mouse move */
536 gboolean handled = FALSE;
537 for(const GList *plugins = g_list_last(dt_lib_get_global()->plugins);
538 plugins;
539 plugins = g_list_previous(plugins))
540 {
541 dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
542
543 /* does this module belong to current view ?*/
544 if(plugin->mouse_moved && dt_lib_is_visible_in_view(plugin, v))
545 if(plugin->mouse_moved(plugin, x, y, pressure, which)) handled = TRUE;
546 }
547
548 /* if not handled by any plugin let pass to view handler*/
549 if(!handled && v->mouse_moved) v->mouse_moved(v, x, y, pressure, which);
550}
551
553{
554 if(IS_NULL_PTR(vm->current_view)) return 0;
555 dt_view_t *v = vm->current_view;
556
557 /* lets check if any plugins want to handle button press */
558 gboolean handled = FALSE;
559 for(const GList *plugins = g_list_last(dt_lib_get_global()->plugins);
560 plugins;
561 plugins = g_list_previous(plugins))
562 {
563 dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
564
565 /* does this module belong to current view ?*/
566 if(plugin->key_pressed && dt_lib_is_visible_in_view(plugin, v))
567 if(plugin->key_pressed(plugin, event)) handled = TRUE;
568 }
569
570 if(handled)
571 return 1;
572 /* if not handled by any plugin let pass to view handler*/
573 else if(v->key_pressed)
574 handled = v->key_pressed(v, event);
575
576 return handled;
577}
578
579int dt_view_manager_button_released(dt_view_manager_t *vm, double x, double y, int which, uint32_t state)
580{
581 if(IS_NULL_PTR(vm->current_view)) return 0;
582 dt_view_t *v = vm->current_view;
583
584 /* lets check if any plugins want to handle button press */
585 gboolean handled = FALSE;
586 for(const GList *plugins = g_list_last(dt_lib_get_global()->plugins);
587 plugins;
588 plugins = g_list_previous(plugins))
589 {
590 dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
591
592 /* does this module belong to current view ?*/
593 if(plugin->button_released && dt_lib_is_visible_in_view(plugin, v))
594 if(plugin->button_released(plugin, x, y, which, state)) handled = TRUE;
595 }
596
597 if(handled)
598 return 1;
599 /* if not handled by any plugin let pass to view handler*/
600 else if(v->button_released)
601 v->button_released(v, x, y, which, state);
602
603 return 0;
604}
605
606int dt_view_manager_button_pressed(dt_view_manager_t *vm, double x, double y, double pressure, int which,
607 int type, uint32_t state)
608{
609 if(IS_NULL_PTR(vm->current_view)) return 0;
610 dt_view_t *v = vm->current_view;
611
612 /* Reset Gtk focus */
613 gtk_window_set_focus(GTK_WINDOW(dt_gui_main_window()), NULL);
615
616 /* lets check if any plugins want to handle button press */
617 gboolean handled = FALSE;
618
619 for(const GList *plugins = g_list_last(dt_lib_get_global()->plugins);
620 plugins && !handled;
621 plugins = g_list_previous(plugins))
622 {
623 dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
624
625 /* does this module belong to current view ?*/
626 if(plugin->button_pressed && dt_lib_is_visible_in_view(plugin, v))
627 if(plugin->button_pressed(plugin, x, y, pressure, which, type, state)) handled = TRUE;
628 }
629
630 if(handled) return 1;
631 /* if not handled by any plugin let pass to view handler*/
632 else if(v->button_pressed)
633 return v->button_pressed(v, x, y, pressure, which, type, state);
634
635 return 0;
636}
637
639{
640 for(GList *iter = vm->views; iter; iter = g_list_next(iter))
641 {
642 // this is necessary for all
643 dt_view_t *v = (dt_view_t *)iter->data;
644 v->width = width;
645 v->height = height;
646 if(v->configure) v->configure(v, width, height);
647 }
648}
649
650int dt_view_manager_scrolled(dt_view_manager_t *vm, double x, double y, int up, int state, int delta_y)
651{
652 if(IS_NULL_PTR(vm->current_view)) return FALSE;
653 if(vm->current_view->scrolled)
654 return vm->current_view->scrolled(vm->current_view, x, y, up, state, delta_y);
655 return 0;
656}
657
667
679
681 cairo_surface_t **surface, int zoom,
682 dt_atomic_int *shutdown);
683
684static void _destroy_surface(cairo_surface_t **surface)
685{
686 if(surface && *surface && cairo_surface_get_reference_count(*surface) > 0)
687 cairo_surface_destroy(*surface);
688 if(surface) *surface = NULL;
689}
690
691static gboolean _view_surface_matches(const dt_view_image_surface_fetcher_t *fetcher, cairo_surface_t **target,
692 const int32_t imgid, const int width, const int height, const int zoom)
693{
694 return target && *target && fetcher->cached_imgid == imgid && fetcher->cached_width == width
695 && fetcher->cached_height == height && fetcher->cached_zoom == zoom;
696}
697
699{
701 params->fetcher = fetcher;
702 params->request_id = fetcher->request_id;
703 params->imgid = fetcher->imgid;
704 params->width = fetcher->width;
705 params->height = fetcher->height;
706 params->zoom = fetcher->zoom;
707
708 dt_job_t *job = dt_control_job_create(&_view_surface_fetch_job_run, "fetch image surface %i", params->imgid);
709 if(IS_NULL_PTR(job))
710 {
711 g_free(params);
712 return;
713 }
714
715 dt_atomic_set_int(&fetcher->shutdown, FALSE);
717 fetcher->job_queued = TRUE;
718 fetcher->queued_request_id = fetcher->request_id;
720}
721
722static gboolean _view_surface_commit_main(gpointer user_data)
723{
725 dt_view_image_surface_fetcher_t *fetcher = commit->fetcher;
726
727 gboolean queue_redraw = FALSE;
728 gboolean enqueue_next = FALSE;
729
730 dt_pthread_mutex_lock(&fetcher->lock);
731 fetcher->commit_pending = FALSE;
732 pthread_cond_broadcast(&fetcher->cond);
733
734 if(!fetcher->destroying && commit->request_id == fetcher->request_id && commit->result == DT_VIEW_SURFACE_OK)
735 {
736 _destroy_surface(fetcher->target);
737 if(fetcher->target) *fetcher->target = commit->surface;
738 commit->surface = NULL;
739 fetcher->cached_imgid = commit->imgid;
740 fetcher->cached_width = commit->width;
741 fetcher->cached_height = commit->height;
742 fetcher->cached_zoom = commit->zoom;
743 queue_redraw = TRUE;
744 }
745
746 if(!fetcher->destroying && !fetcher->job_queued && fetcher->request_id != commit->request_id)
747 enqueue_next = TRUE;
748 dt_pthread_mutex_unlock(&fetcher->lock);
749
750 if(commit->surface) cairo_surface_destroy(commit->surface);
751
752 if(queue_redraw)
753 {
754 GtkWidget *widget = g_weak_ref_get(&fetcher->widget_ref);
755 if(widget)
756 {
757 if(widget == dt_gui_center_widget())
758 {
760 }
761 else
762 {
763 gtk_widget_queue_draw(widget);
764 GdkWindow *window = gtk_widget_get_window(widget);
765 if(window)
766 {
767 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
768 gdk_window_process_updates(window, TRUE);
769 G_GNUC_END_IGNORE_DEPRECATIONS
770 }
771 }
772 g_object_unref(widget);
773 }
774 }
775
776 if(enqueue_next)
777 {
778 dt_pthread_mutex_lock(&fetcher->lock);
779 if(!fetcher->destroying && !fetcher->job_queued && !fetcher->commit_pending)
780 _enqueue_surface_fetch(fetcher);
781 dt_pthread_mutex_unlock(&fetcher->lock);
782 }
783
784 g_free(commit);
785 return G_SOURCE_REMOVE;
786}
787
789{
791 dt_view_image_surface_fetcher_t *fetcher = params->fetcher;
792
793 dt_pthread_mutex_lock(&fetcher->lock);
794 const gboolean stale = fetcher->destroying || params->request_id != fetcher->request_id;
795 const gboolean cancelled = dt_control_job_get_state(job) == DT_JOB_STATE_CANCELLED;
796 if(stale || cancelled)
797 {
798 if(fetcher->job_queued && fetcher->queued_request_id == params->request_id) fetcher->job_queued = FALSE;
799
800 // A stale request means the widget parameters changed while the current
801 // job was queued or running. If no newer request is already tracked
802 // locally, start the latest one immediately from the current fetcher
803 // state instead of trying to reason about queue-owned job objects.
804 if(!fetcher->destroying && params->request_id != fetcher->request_id && !fetcher->job_queued
805 && !fetcher->commit_pending)
806 _enqueue_surface_fetch(fetcher);
807
808 pthread_cond_broadcast(&fetcher->cond);
809 dt_pthread_mutex_unlock(&fetcher->lock);
810 return 0;
811 }
812 dt_pthread_mutex_unlock(&fetcher->lock);
813
814 cairo_surface_t *surface = NULL;
815 const dt_view_surface_value_t result =
816 _view_image_get_surface_internal(params->imgid, params->width, params->height, &surface, params->zoom,
817 &fetcher->shutdown);
818
820 commit->fetcher = fetcher;
821 commit->request_id = params->request_id;
822 commit->imgid = params->imgid;
823 commit->width = params->width;
824 commit->height = params->height;
825 commit->zoom = params->zoom;
826 commit->surface = surface;
827 commit->result = result;
828
829 dt_pthread_mutex_lock(&fetcher->lock);
830 if(fetcher->job_queued && fetcher->queued_request_id == params->request_id) fetcher->job_queued = FALSE;
831 fetcher->commit_pending = TRUE;
832 pthread_cond_broadcast(&fetcher->cond);
833 dt_pthread_mutex_unlock(&fetcher->lock);
834
835 g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT, _view_surface_commit_main,
836 commit, NULL);
837 g_main_context_wakeup(g_main_context_default());
838 return 0;
839}
840
842{
843 memset(fetcher, 0, sizeof(dt_view_image_surface_fetcher_t));
844 dt_pthread_mutex_init(&fetcher->lock, NULL);
845 pthread_cond_init(&fetcher->cond, NULL);
846 g_weak_ref_init(&fetcher->widget_ref, NULL);
847 fetcher->cached_imgid = UNKNOWN_IMAGE;
848 fetcher->imgid = UNKNOWN_IMAGE;
849 dt_atomic_set_int(&fetcher->shutdown, FALSE);
850}
851
853{
854 dt_pthread_mutex_lock(&fetcher->lock);
855 fetcher->destroying = TRUE;
856 dt_atomic_set_int(&fetcher->shutdown, TRUE);
857 while(fetcher->job_queued)
858 dt_pthread_cond_wait(&fetcher->cond, &fetcher->lock);
859 dt_pthread_mutex_unlock(&fetcher->lock);
860
861 for(;;)
862 {
863 dt_pthread_mutex_lock(&fetcher->lock);
864 const gboolean pending = fetcher->commit_pending;
865 dt_pthread_mutex_unlock(&fetcher->lock);
866 if(!pending) break;
867 g_main_context_iteration(g_main_context_default(), TRUE);
868 }
869
870 _destroy_surface(fetcher->target);
871 g_weak_ref_clear(&fetcher->widget_ref);
872 pthread_cond_destroy(&fetcher->cond);
874}
875
877{
878 dt_pthread_mutex_lock(&fetcher->lock);
879 fetcher->target = target;
880 fetcher->request_id++;
881 dt_atomic_set_int(&fetcher->shutdown, TRUE);
882 fetcher->cached_imgid = UNKNOWN_IMAGE;
883 fetcher->cached_width = 0;
884 fetcher->cached_height = 0;
885 fetcher->cached_zoom = 0;
886 // Also clear the *requested* imgid, not just the cached one: dt_view_image_get_surface_async()
887 // only enqueues a fetch when imgid/width/height/zoom differ from the previous call. A caller
888 // invalidating because the underlying image changed (e.g. a style applied) but requesting the
889 // exact same imgid/size/zoom afterward would otherwise see "nothing changed" and never
890 // re-enqueue a fetch, leaving the display stuck on the busy message forever.
891 fetcher->imgid = UNKNOWN_IMAGE;
892 dt_pthread_mutex_unlock(&fetcher->lock);
893
894 _destroy_surface(target);
895}
896
898 int width, int height, cairo_surface_t **target,
899 GtkWidget *widget, int zoom)
900{
901 if(IS_NULL_PTR(fetcher) || !target || !widget || width < 2 || height < 2 || imgid <= UNKNOWN_IMAGE)
902 return DT_VIEW_SURFACE_KO;
903
905
906 dt_pthread_mutex_lock(&fetcher->lock);
907 const gboolean changed = fetcher->target != target || fetcher->imgid != imgid || fetcher->width != width
908 || fetcher->height != height || fetcher->zoom != zoom;
909 const gboolean exact_match = _view_surface_matches(fetcher, target, imgid, width, height, zoom);
910 const gboolean fallback_match = target && *target && fetcher->cached_imgid == imgid
911 && fetcher->cached_zoom == zoom;
912 fetcher->target = target;
913 fetcher->imgid = imgid;
914 fetcher->width = width;
915 fetcher->height = height;
916 fetcher->zoom = zoom;
917 g_weak_ref_set(&fetcher->widget_ref, widget);
918
919 if(exact_match || fallback_match)
920 ret = DT_VIEW_SURFACE_OK;
921
922 if(changed && !exact_match && !fetcher->destroying && !fetcher->job_queued
923 && !fetcher->commit_pending)
924 {
925 fetcher->request_id++;
926 _enqueue_surface_fetch(fetcher);
927 }
928 else if(changed && !exact_match && !fetcher->destroying)
929 {
930 fetcher->request_id++;
931 dt_atomic_set_int(&fetcher->shutdown, TRUE);
932 }
933 dt_pthread_mutex_unlock(&fetcher->lock);
934
935 return ret;
936}
937
938cairo_surface_t *dt_cairo_rescale_surface(cairo_surface_t *src, int dst_w, int dst_h)
939{
940 if(!src || dst_w <= 0 || dst_h <= 0)
941 return NULL;
942
943 const int src_w = cairo_image_surface_get_width(src);
944 const int src_h = cairo_image_surface_get_height(src);
945
946 cairo_surface_t *dst =
947 cairo_image_surface_create(CAIRO_FORMAT_RGB24, dst_w, dst_h);
948
949 cairo_t *cr = cairo_create(dst);
950
951 // clear (important if aspect ratio leaves borders)
952 cairo_set_source_rgba(cr, 0, 0, 0, 0);
953 cairo_paint(cr);
954
955 double scale_x = (double)dst_w / src_w;
956 double scale_y = (double)dst_h / src_h;
957
958 double sx = scale_x;
959 double sy = scale_y;
960 double tx = 0.0;
961 double ty = 0.0;
962
963
964 double s = MIN(scale_x, scale_y);
965 sx = sy = s;
966
967 tx = (dst_w - src_w * s) * 0.5;
968 ty = (dst_h - src_h * s) * 0.5;
969
970
971 cairo_translate(cr, tx, ty);
972 cairo_scale(cr, sx, sy);
973
974 cairo_set_source_surface(cr, src, 0, 0);
975
976 cairo_pattern_t *pat = cairo_get_source(cr);
977 cairo_pattern_set_filter(pat, CAIRO_FILTER_BEST);
978 cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
979
980 cairo_paint(cr);
981
982 cairo_destroy(cr);
983
984 return dst;
985}
986
987void dt_cairo_sharpen_surface_rgb24(cairo_surface_t *surface)
988{
989 if(!surface) return;
990
991 cairo_surface_flush(surface);
992
993 unsigned char *data = cairo_image_surface_get_data(surface);
994 int width = cairo_image_surface_get_width(surface);
995 int height = cairo_image_surface_get_height(surface);
996 int stride = cairo_image_surface_get_stride(surface);
997
998 // Copy original buffer
999 unsigned char *copy = dt_alloc_align(stride * height);
1000 memcpy(copy, data, stride * height);
1001
1002 // Kernel weights
1003 const float k_center = 4.0f;
1004 const float k_edge = -0.5f;
1005 const float k_corner = -0.25f;
1006
1007 // Unsharp mask coeffs
1008 const float amount = 0.05f;
1009 const float amount_inv = 1.f - amount;
1010
1011 __OMP_PARALLEL_FOR__(collapse(2))
1012 for(int y = 1; y < height - 1; y++)
1013 for(int x = 1; x < width - 1; x++)
1014 {
1015 int idx = y * stride + x * 4;
1016
1017 for(int c = 0; c < 3; c++) // B, G, R
1018 {
1019 int i = idx + c;
1020
1021 float v =
1022 k_center * copy[i]
1023
1024 + k_edge * (
1025 copy[i - 4] + // left
1026 copy[i + 4] + // right
1027 copy[i - stride] + // top
1028 copy[i + stride] // bottom
1029 )
1030
1031 + k_corner * (
1032 copy[i - stride - 4] + // top-left
1033 copy[i - stride + 4] + // top-right
1034 copy[i + stride - 4] + // bottom-left
1035 copy[i + stride + 4] // bottom-right
1036 );
1037
1038 // Unsharp-style recombination
1039 const float out = amount_inv * copy[i] + amount * v;
1040 data[i] = (unsigned char)(CLAMP(roundf(out), 0.f, 255.f));
1041 }
1042
1043 data[idx + 3] = copy[idx + 3];
1044 }
1045
1046 dt_free_align(copy); // paired with the dt_alloc_align above
1047 cairo_surface_mark_dirty(surface);
1048}
1049
1050/* What is painted over a skull thumbnail, indexed by the dt_imageio_retval_t the mipmap cache
1051 * kept for it. The skull alone only says that the thumbnail failed; the why is what a bug report
1052 * needs (#1531). */
1053static const char *const _skull_labels[] = {
1054 [DT_IMAGEIO_OK] = "",
1055 [DT_IMAGEIO_FILE_NOT_FOUND] = N_("File not found"),
1056 [DT_IMAGEIO_FILE_CORRUPTED] = N_("Corrupted file"),
1057 [DT_IMAGEIO_CACHE_FULL] = N_("Out of RAM"),
1058 [DT_IMAGEIO_UNSUPPORTED_FORMAT] = N_("Unsupported format"),
1059 [DT_IMAGEIO_UNSUPPORTED_FEATURE] = N_("Unsupported feature"),
1060 [DT_IMAGEIO_UNSUPPORTED_CAMERA] = N_("Unsupported camera"),
1061 [DT_IMAGEIO_LOAD_FAILED] = N_("Loading error"),
1062 [DT_IMAGEIO_IOERROR] = N_("I/O error"),
1063 [DT_IMAGEIO_NO_EMBEDDED_THUMBNAIL] = N_("No embedded thumbnail"),
1064 [DT_IMAGEIO_PROCESSING_FAILED] = N_("Processing error"),
1065 [DT_IMAGEIO_ABORTED] = N_("Aborted"),
1066};
1067
1069 cairo_surface_t **surface, int zoom,
1070 dt_atomic_int *shutdown)
1071{
1072 double tt = 0;
1074 tt = dt_get_wtime();
1075
1077
1078 // if surface not null, clean it up
1079 if(*surface && cairo_surface_get_reference_count(*surface) > 0)
1080 cairo_surface_destroy(*surface);
1081 *surface = NULL;
1082
1083 // get mipmap cache image
1085
1086 if(zoom == DT_THUMBTABLE_ZOOM_FIT)
1087 {
1088 mip = dt_mipmap_cache_get_matching_size(ceilf(width * dt_gui_get_global()->ppd), ceilf(height * dt_gui_get_global()->ppd), imgid);
1089 }
1090 else
1091 {
1092 const dt_image_t *image = dt_image_cache_get(imgid, 'r');
1093 if(IS_NULL_PTR(image)) return DT_VIEW_SURFACE_KO;
1094 const int full_width = image->width;
1095 const int full_height = image->height;
1097
1098 if(zoom == DT_THUMBTABLE_ZOOM_HALF)
1099 mip = dt_mipmap_cache_get_matching_size(ceilf(full_width / 2.f ), ceilf(full_height / 2.f), imgid);
1100 else if(zoom >= DT_THUMBTABLE_ZOOM_FULL)
1101 mip = dt_mipmap_cache_get_matching_size(full_width, full_height, imgid);
1102 }
1103
1104 // Thumbnail generation: mipmap fetch (and, on a miss, the pipeline render
1105 // behind it, which surfaces through the generic thumbnail-pipe events). mip is
1106 // now resolved, so the request and its result share the real (imgid, mip) key.
1109
1110 // Can't have float32 types here
1111 if(mip >= DT_MIPMAP_F)
1112 {
1115 return DT_VIEW_SURFACE_KO;
1116 }
1117
1118 // if needed, we load the mimap buffer
1120 dt_mipmap_cache_get_with_shutdown(&buf, imgid, mip, DT_MIPMAP_BLOCKING, 'r', shutdown);
1121 const int buf_wd = buf.width;
1122 const int buf_ht = buf.height;
1123 const dt_imageio_retval_t status = buf.status;
1124
1125 /* The thumbnail's own entry could not be allocated just now: the skull below says so on the
1126 * thumbnail, the window says what to do about it. */
1128 {
1129 char path[DT_PATH_MAX] = { 0 };
1130 dt_image_full_path(imgid, path, sizeof(path), NULL, __FUNCTION__);
1131 dt_control_alert_once(_("Not enough memory"),
1132 _("Not enough memory to display the thumbnail.\n"
1133 "Close other applications or add more RAM to your system."),
1134 path[0] ? path : NULL);
1135 }
1136
1137 // if we don't get buffer, no image is available at the moment
1138 if(IS_NULL_PTR(buf.buf))
1139 {
1143 return DT_VIEW_SURFACE_KO;
1144 }
1145
1146 // so we create a new image surface to return
1147 float scale = 1.f;
1148 int img_width = buf_wd;
1149 int img_height = buf_ht;
1150
1151 if(zoom == DT_THUMBTABLE_ZOOM_FIT)
1152 {
1153 scale = fminf((float)width / (float)buf_wd, (float)height / (float)buf_ht) * dt_gui_get_global()->ppd;
1154 img_width = roundf(buf_wd * scale);
1155 img_height = roundf(buf_ht * scale);
1156
1157 // due to the forced rounding above, we need to recompute scaling
1158 scale = fmaxf((float)img_width / (float)buf_wd, (float)img_height / (float)buf_ht);
1159 }
1160 else if(zoom == DT_THUMBTABLE_ZOOM_TWICE)
1161 {
1162 // NOTE: we upscale the image surface, which means we will oversample
1163 // the full-res input buffer
1164 scale = 2.f;
1165 img_width = roundf(buf_wd * scale);
1166 img_height = roundf(buf_ht * scale);
1167 }
1168
1169 *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, img_width, img_height);
1170
1171 // we transfer cached image on a cairo_surface (with colorspace transform if needed)
1172 uint8_t *rgbbuf = (uint8_t *)calloc((size_t)buf_wd * buf_ht * 4, sizeof(uint8_t));
1173 if(IS_NULL_PTR(rgbbuf))
1174 {
1176 return ret;
1177 }
1178
1179 /* Colour-manage into display space. sRGB and AdobeRGB use the module's prepared
1180 * transforms, DT_COLORSPACE_DISPLAY passes through with an R <-> B swap, anything else
1181 * is resolved and built inside the module. A thumbnail cached with a tag that has no
1182 * DISPLAY-direction profile falls back to the plain swap rather than being dropped. */
1185
1186 const int32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, buf_wd);
1187 cairo_surface_t *tmp_surface = cairo_image_surface_create_for_data(rgbbuf, CAIRO_FORMAT_RGB24, buf_wd, buf_ht, stride);
1188 if(IS_NULL_PTR(tmp_surface))
1189 {
1190 dt_free(rgbbuf);
1191 return ret;
1192 }
1193
1194 // draw the image scaled:
1195 cairo_t *cr = cairo_create(*surface);
1196 cairo_scale(cr, scale, scale);
1197 cairo_set_source_surface(cr, tmp_surface, 0, 0);
1198
1199 // set filter no nearest:
1200 // in skull mode, we want to see big pixels.
1201 // in 1 iir mode for the right mip, we want to see exactly what the pipe gave us, 1:1 pixel for pixel.
1202 // in between, filtering just makes stuff go unsharp.
1203 if((buf_wd <= 8 && buf_ht <= 8)
1204 || fabsf(scale - 1.0f) < 0.01f
1205 || zoom == DT_THUMBTABLE_ZOOM_TWICE)
1206 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
1207 else
1208 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BEST);
1209
1210 cairo_paint(cr);
1211 cairo_surface_destroy(tmp_surface);
1212
1213 // Skull: write why across its bottom, in a band so it stays legible on small thumbnails.
1214 if(buf_wd <= 8 && buf_ht <= 8 && status != DT_IMAGEIO_OK)
1215 {
1216 cairo_identity_matrix(cr);
1217 PangoLayout *layout = pango_cairo_create_layout(cr);
1218 // Not the bauhaus font: a theme reload frees it from the GUI thread while this runs in a worker.
1219 PangoFontDescription *desc = pango_font_description_from_string("sans bold");
1220 pango_font_description_set_absolute_size(desc, DT_UI_SCALE_DEVICE(12) * PANGO_SCALE);
1221 pango_layout_set_font_description(layout, desc);
1222 pango_font_description_free(desc);
1223 pango_layout_set_text(layout, _(_skull_labels[status]), -1);
1224 pango_layout_set_width(layout, img_width * PANGO_SCALE);
1225 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
1226 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
1227
1228 /* As many lines as half the thumbnail holds, which keeps the skull's eyes clear; Pango ellipsizes
1229 * the last. WORD_CHAR: PANGO_WRAP_WORD lets a word wider than the thumbnail overflow both edges. */
1230 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
1231 pango_layout_set_height(layout, img_height / 2 * PANGO_SCALE);
1232
1233 int text_h = 0;
1234 pango_layout_get_pixel_size(layout, NULL, &text_h);
1235 // at least the skull's bottom row, which is black already
1236 const double band_h = MAX(img_height / 8., text_h + DT_UI_SCALE_DEVICE(4));
1237 cairo_rectangle(cr, 0., img_height - band_h, img_width, band_h);
1238 cairo_set_source_rgba(cr, 0., 0., 0., 0.7);
1239 cairo_fill(cr);
1240 cairo_set_source_rgb(cr, 1., 1., 1.);
1241 cairo_move_to(cr, 0., img_height - (band_h + text_h) / 2.);
1242 pango_cairo_show_layout(cr, layout);
1243 g_object_unref(layout);
1244 }
1245
1246 cairo_destroy(cr);
1247
1248 /* The async/shared surface path returns pixel-sized Cairo image surfaces.
1249 * Publish the widget PPD on the finished surface so GUI callers can place it
1250 * in logical coordinates without re-deriving HiDPI scaling on every draw. */
1251 cairo_surface_set_device_scale(*surface, dt_gui_get_global()->ppd, dt_gui_get_global()->ppd);
1252
1253 // we consider skull as ok as the image hasn't to be reloaded
1254 if(buf_wd <= 8 && buf_ht <= 8)
1255 {
1256 ret = DT_VIEW_SURFACE_OK;
1257 }
1258 else
1259 {
1260 ret = DT_VIEW_SURFACE_OK;
1261 //dt_cairo_sharpen_surface_rgb24(*surface);
1262 }
1263
1264 dt_free(rgbbuf);
1265
1266 // logs
1268 {
1270 "[dt_view_image_get_surface] id %i, mip code %i, dots %ix%i, mip %ix%i, surf %ix%i created in %0.04f sec\n",
1271 imgid, mip, width, height, buf_wd, buf_ht, img_width, img_height, dt_get_wtime() - tt);
1272 }
1274 {
1275 dt_print(DT_DEBUG_IMAGEIO, "[dt_view_image_get_surface] id %i, mip code %i, dots %ix%i, mip %ix%i, surf %ix%i\n", imgid, mip,
1276 width, height, buf_wd, buf_ht, img_width, img_height);
1277 }
1278
1279 // we consider skull as ok as the image hasn't to be reload
1282 return ret;
1283}
1284
1285dt_view_surface_value_t dt_view_image_get_surface(int32_t imgid, int width, int height, cairo_surface_t **surface,
1286 int zoom)
1287{
1288 return _view_image_get_surface_internal(imgid, width, height, surface, zoom, NULL);
1289}
1290
1291char* dt_view_extend_modes_str(const char * name, const gboolean is_hdr, const gboolean is_bw, const gboolean is_bw_flow)
1292{
1293 char* upcase = g_ascii_strup(name, -1); // extension in capital letters to avoid character descenders
1294 // convert to canonical format extension
1295 if(0 == g_ascii_strcasecmp(upcase, "JPG"))
1296 {
1297 gchar* canonical = g_strdup("JPEG");
1298 dt_free(upcase);
1299 upcase = canonical;
1300 }
1301 else if(0 == g_ascii_strcasecmp(upcase, "HDR"))
1302 {
1303 gchar* canonical = g_strdup("RGBE");
1304 dt_free(upcase);
1305 upcase = canonical;
1306 }
1307 else if(0 == g_ascii_strcasecmp(upcase, "TIF"))
1308 {
1309 gchar* canonical = g_strdup("TIFF");
1310 dt_free(upcase);
1311 upcase = canonical;
1312 }
1313
1314 if(is_hdr)
1315 {
1316 gchar* fullname = g_strdup_printf("%s HDR", upcase);
1317 dt_free(upcase);
1318 upcase = fullname;
1319 }
1320 if(is_bw)
1321 {
1322 gchar* fullname = g_strdup_printf("%s B&W", upcase);
1323 dt_free(upcase);
1324 upcase = fullname;
1325 if(!is_bw_flow)
1326 {
1327 fullname = g_strdup_printf("%s-", upcase);
1328 dt_free(upcase);
1329 upcase = fullname;
1330 }
1331 }
1332
1333 return upcase;
1334}
1335
1336
1338{
1340 if(IS_NULL_PTR(vm->active_images)) return;
1341 g_list_free(vm->active_images);
1342 vm->active_images = NULL;
1343
1345}
1346
1347void dt_view_active_images_add(int32_t imgid, gboolean raise)
1348{
1350 vm->active_images = g_list_append(vm->active_images, GINT_TO_POINTER(imgid));
1351 if(raise)
1353}
1354
1355void dt_view_active_images_remove(int32_t imgid, gboolean raise)
1356{
1358 GList *link = g_list_find(vm->active_images, GINT_TO_POINTER(imgid));
1359 if(link)
1360 {
1361 vm->active_images = g_list_delete_link(vm->active_images, link);
1362
1363 if(raise)
1365 }
1366}
1367
1368gboolean dt_view_active_images_has_imgid(int32_t imgid)
1369{
1370 return g_list_find(dt_view_active_images_get_all(), GINT_TO_POINTER(imgid)) != NULL;
1371}
1372
1377
1379{
1381 if(IS_NULL_PTR(vm->active_images)) return -1;
1382 return GPOINTER_TO_INT(vm->active_images->data);
1383}
1384
1385void dt_view_active_images_set(GList *images, gboolean raise)
1386{
1388 if(!IS_NULL_PTR(vm->active_images)
1389 && vm->active_images != images)
1390 {
1391 g_list_free(vm->active_images);
1392 }
1393
1394 vm->active_images = images;
1395
1396 if(raise)
1398}
1399
1405
1413
1414#ifdef HAVE_MAP
1415void dt_view_map_center_on_location(const dt_view_manager_t *vm, gdouble lon, gdouble lat, gdouble zoom)
1416{
1417 if(vm->proxy.map.view)
1418 vm->proxy.map.center_on_location(vm->proxy.map.view, lon, lat, zoom);
1419}
1420
1421void dt_view_map_center_on_bbox(const dt_view_manager_t *vm, gdouble lon1, gdouble lat1, gdouble lon2, gdouble lat2)
1422{
1423 if(vm->proxy.map.view)
1424 vm->proxy.map.center_on_bbox(vm->proxy.map.view, lon1, lat1, lon2, lat2);
1425}
1426
1428{
1429 if(vm->proxy.map.view)
1430 vm->proxy.map.show_osd(vm->proxy.map.view);
1431}
1432
1433void dt_view_map_set_map_source(const dt_view_manager_t *vm, OsmGpsMapSource_t map_source)
1434{
1435 if(vm->proxy.map.view)
1436 vm->proxy.map.set_map_source(vm->proxy.map.view, map_source);
1437}
1438
1440{
1441 if(vm->proxy.map.view)
1442 return vm->proxy.map.add_marker(vm->proxy.map.view, type, points);
1443 return NULL;
1444}
1445
1447{
1448 if(vm->proxy.map.view)
1449 return vm->proxy.map.remove_marker(vm->proxy.map.view, type, marker);
1450 return FALSE;
1451}
1453{
1454 if(vm->proxy.map.view)
1455 vm->proxy.map.add_location(vm->proxy.map.view, p, posid);
1456}
1457
1458void dt_view_map_location_action(const dt_view_manager_t *vm, const int action)
1459{
1460 if(vm->proxy.map.view)
1461 vm->proxy.map.location_action(vm->proxy.map.view, action);
1462}
1463
1465{
1466 if(vm->proxy.map.view)
1467 vm->proxy.map.redraw(vm->proxy.map.view);
1468}
1469
1470#endif
1471
1472#ifdef HAVE_PRINT
1474{
1475 if (vm->proxy.print.view)
1476 vm->proxy.print.print_settings(vm->proxy.print.view, pinfo, imgs);
1477}
1478#endif
1479
1480
1481static void _audio_child_watch(GPid pid, gint status, gpointer data)
1482{
1484 vm->audio.audio_player_id = -1;
1485 g_spawn_close_pid(pid);
1486}
1487
1489{
1490 char *player = dt_conf_get_string("plugins/lighttable/audio_player");
1491 if(player && *player)
1492 {
1493 char *filename = dt_image_get_audio_path(imgid);
1494 if(filename)
1495 {
1496 char *argv[] = { player, filename, NULL };
1497 gboolean ret = g_spawn_async(NULL, argv, NULL,
1498 G_SPAWN_DO_NOT_REAP_CHILD
1499 | G_SPAWN_SEARCH_PATH
1500 | G_SPAWN_STDOUT_TO_DEV_NULL
1501 | G_SPAWN_STDERR_TO_DEV_NULL,
1502 NULL, NULL,
1503 &vm->audio.audio_player_pid, NULL);
1504
1505 if(ret)
1506 {
1507 vm->audio.audio_player_id = imgid;
1509 = g_child_watch_add(vm->audio.audio_player_pid, (GChildWatchFunc)_audio_child_watch, vm);
1510 }
1511 else
1512 vm->audio.audio_player_id = -1;
1513
1514 dt_free(filename);
1515 }
1516 }
1517 dt_free(player);
1518}
1519
1521{
1522 // make sure that the process didn't finish yet and that _audio_child_watch() hasn't run
1523 if(vm->audio.audio_player_id == -1)
1524 return;
1525
1526 // we don't want to trigger the callback due to a possible race condition
1527 g_source_remove(vm->audio.audio_player_event_source);
1528#ifdef _WIN32
1529// TODO: add Windows code to actually kill the process
1530#else // _WIN32
1531 if(vm->audio.audio_player_id != -1)
1532 {
1533 if(getpgid(0) != getpgid(vm->audio.audio_player_pid))
1534 kill(-vm->audio.audio_player_pid, SIGKILL);
1535 else
1536 kill(vm->audio.audio_player_pid, SIGKILL);
1537 }
1538#endif // _WIN32
1539 g_spawn_close_pid(vm->audio.audio_player_pid);
1540 vm->audio.audio_player_id = -1;
1541}
1542
1543
1544void dt_view_image_info_update(int32_t imgid)
1545{
1547
1548 if(imgid == UNKNOWN_IMAGE)
1549 {
1551 return;
1552 }
1553
1554 char input_dir[512] = { 0 };
1555 gboolean from_cache = TRUE;
1556 dt_image_full_path(imgid, input_dir, sizeof(input_dir), &from_cache, __FUNCTION__);
1557
1560
1561 vp->filename = input_dir;
1562 vp->jobcode = "infos";
1563 vp->imgid = imgid;
1564 vp->sequence = 0;
1565 vp->escape_markup = TRUE;
1566
1567 gchar *pattern = dt_conf_get_string("plugins/darkroom/image_infos_pattern");
1568 gchar *msg = dt_variables_expand(vp, pattern, TRUE);
1569 dt_free(pattern);
1571
1573
1574 dt_free(msg);
1575}
1576
1577
1578// clang-format off
1579// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1580// vim: shiftwidth=2 expandtab tabstop=2 cindent
1581// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1582// clang-format on
GtkWidget * dt_gui_main_window(void)
void dt_ui_container_foreach(dt_ui_t *ui, const dt_ui_container_t c, GtkCallback callback)
calls a callback on all children widgets from container
void dt_ui_container_destroy_children(dt_ui_t *ui, const dt_ui_container_t c)
destroy all child widgets from container
struct dt_ui_t * dt_gui_get_ui(void)
GtkWidget * dt_gui_center_widget(void)
void dt_gui_add_help_link(GtkWidget *widget, char *link)
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_atomic_set_int(dt_atomic_int *var, int value)
atomic_int dt_atomic_int
Definition atomic.h:68
uint32_t container(dt_lib_module_t *self)
const char ** views(dt_lib_module_t *self)
static const float x
const float v
gboolean dt_colorprofiles_rgba8_to_display_bgra8(const uint8_t *const in, uint8_t *const out, const int width, const int height, const dt_colorspaces_color_profile_type_t src_space)
Convert a whole 8-bit plane from src_space to the display profile: packed RGBA8 in,...
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
int dt_conf_get_bool(const char *name)
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
char * dt_image_get_audio_path(const int32_t imgid)
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
void dt_control_alert_once(const char *title, const char *message, const char *item)
dt_control_alert() for a failure that repeats as long as its cause lasts – memory running short,...
Definition control.c:888
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:970
#define dt_control_change_cursor(cursor)
Definition control.h:121
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:654
struct dt_lib_t * dt_lib_get_global(void)
Definition darktable.c:624
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:602
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
Definition darktable.c:511
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:526
GtkWidget * window
GtkWidget * view
the tree view
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:132
static int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
Wait on cond, releasing mutex for the duration.
Definition dtpthread.h:149
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
GtkWidget * dtgtk_expander_get_body_event_box(GtkDarktableExpander *expander)
Definition expander.c:63
GtkWidget * dtgtk_expander_get_body(GtkDarktableExpander *expander)
Definition expander.c:56
#define DTGTK_EXPANDER(obj)
Definition expander.h:31
#define DTGTK_IS_EXPANDER(obj)
Definition expander.h:32
dt_geo_map_display_t
Definition geo.h:26
#define UNKNOWN_IMAGE
Definition image.h:78
dt_imageio_retval_t
Return value of image io functions: loading, exporting, and making a thumbnail.
Definition image.h:97
@ DT_IMAGEIO_LOAD_FAILED
Definition image.h:105
@ DT_IMAGEIO_ABORTED
Definition image.h:109
@ DT_IMAGEIO_OK
Definition image.h:98
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:101
@ DT_IMAGEIO_UNSUPPORTED_FEATURE
Definition image.h:103
@ DT_IMAGEIO_FILE_CORRUPTED
Definition image.h:100
@ DT_IMAGEIO_PROCESSING_FAILED
Definition image.h:108
@ DT_IMAGEIO_NO_EMBEDDED_THUMBNAIL
Definition image.h:107
@ DT_IMAGEIO_UNSUPPORTED_FORMAT
Definition image.h:102
@ DT_IMAGEIO_IOERROR
Definition image.h:106
@ DT_IMAGEIO_FILE_NOT_FOUND
Definition image.h:99
@ DT_IMAGEIO_UNSUPPORTED_CAMERA
Definition image.h:104
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
dt_job_state_t dt_control_job_get_state(_dt_job_t *job)
Definition jobs.c:106
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:138
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:419
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:132
void dt_control_job_set_params_with_size(dt_job_t *job, void *params, size_t params_size, dt_job_destroy_callback callback)
Definition jobs.c:123
@ DT_JOB_QUEUE_SYSTEM_FG
Definition jobs.h:56
@ DT_JOB_STATE_CANCELLED
Definition jobs.h:48
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
gboolean dt_lib_is_visible_in_view(dt_lib_module_t *module, const dt_view_t *view)
Definition lib.c:115
gboolean dt_lib_is_visible(dt_lib_module_t *module)
Definition lib.c:1189
void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
Definition lib.c:852
void dt_lib_set_visible(dt_lib_module_t *module, gboolean visible)
Definition lib.c:1199
GtkWidget * dt_lib_gui_get_expander(dt_lib_module_t *module)
Definition lib.c:1050
_lib_location_type_t type
Definition location.c:1
float lat
Definition location.c:3
float lon
Definition location.c:2
@ DT_DEBUG_LIGHTTABLE
Definition logging.h:60
@ DT_DEBUG_PERF
Definition logging.h:55
@ DT_DEBUG_IMAGEIO
Definition logging.h:68
int32_t dt_get_debug_flags(void)
Definition darktable.c:2094
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#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 macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
dt_imageio_retval_t status
Definition mipmap_cache.c:8
uint32_t height
Definition mipmap_cache.c:1
dt_mipmap_size_t dt_mipmap_cache_get_matching_size(const int32_t width, const int32_t height, const uint32_t imgid)
#define dt_mipmap_cache_get_with_shutdown(B, C, D, E, F, G)
@ DT_MIPMAP_BLOCKING
dt_mipmap_size_t
@ DT_MIPMAP_F
@ DT_MIPMAP_NONE
#define dt_mipmap_cache_release(B)
GList * dt_module_load_modules(const char *subdir, size_t module_size, int(*load_module_so)(void *module, const char *libname, const char *plugin_name), void(*init_module)(void *module), gint(*sort_modules)(gconstpointer a, gconstpointer b))
Load every module the given directory's manifest lists.
Definition module.c:106
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
const char * name
Definition pdf.h:90
void copy(TYPE *dest, TYPE *source, size_t num_el)
Copy a flat buffer used to initialize test matrices.
void dt_sentry_record_module_usage(const char *category, const char *name)
Definition sentry.c:701
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:390
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:619
@ DT_SIGNAL_ACTIVE_IMAGES_CHANGE
This signal is raised when image shown in the main view change no param, no returned value.
Definition signal.h:68
@ DT_SIGNAL_CONTROL_TOAST_REDRAW
This signal is raised when dt_control_toast_redraw() is called. no param, no returned value.
Definition signal.h:292
@ DT_SIGNAL_VIEWMANAGER_VIEW_CANNOT_CHANGE
This signal is raised by viewmanager when a view has changed. 1 : dt_view_t * the old view 2 : dt_vie...
Definition signal.h:92
@ DT_SIGNAL_CONTROL_LOG_REDRAW
This signal is raised when dt_control_log_redraw() is called. no param, no returned value.
Definition signal.h:287
@ DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED
This signal is raised by viewmanager when a view has changed. 1 : dt_view_t * the old view 2 : dt_vie...
Definition signal.h:85
char * dt_variables_expand(dt_variables_params_t *params, gchar *source, gboolean iterate)
void dt_variables_params_destroy(dt_variables_params_t *params)
void dt_variables_params_init(dt_variables_params_t **params)
const float uint32_t state[4]
int32_t height
Definition image.h:406
int32_t width
Definition image.h:406
char plugin_name[128]
Definition lib.h:81
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
dt_colorspaces_color_profile_type_t color_space
dt_cache_entry_t * cache_entry
NULL with a non-NULL buf when a blocking get could not allocate the thumbnail's entry: buf is then a ...
dt_imageio_retval_t status
Why the entry holds no image: a thumbnail drawn as a skull, or a full-size input with no pixels....
const gchar * filename
Definition variables.h:48
const gchar * jobcode
Definition variables.h:51
Track one asynchronous Cairo surface fetch request for a GUI widget.
Definition view.h:119
dt_pthread_mutex_t lock
Definition view.h:120
cairo_surface_t ** target
Definition view.h:123
GPid audio_player_pid
Definition view.h:207
guint audio_player_event_source
Definition view.h:209
struct dt_view_manager_t::@61 proxy
dt_view_t * current_view
Definition view.h:193
int32_t audio_player_id
Definition view.h:208
struct dt_view_manager_t::@61::@65 map
GObject *(* add_marker)(const dt_view_t *view, dt_geo_map_display_t type, GList *points)
Definition view.h:251
gboolean(* redraw)(gpointer user_data)
Definition view.h:255
void(* center_on_location)(const dt_view_t *view, gdouble lon, gdouble lat, double zoom)
Definition view.h:247
gboolean(* remove_marker)(const dt_view_t *view, dt_geo_map_display_t type, GObject *marker)
Definition view.h:252
struct dt_view_manager_t::@61::@62 module_toolbox
int32_t image_info_id
Definition view.h:203
dt_darkroom_layout_t(* get_layout)(struct dt_view_t *view)
Definition view.h:238
void(* print_settings)(const dt_view_t *view, dt_print_info_t *pinfo, dt_images_box *imgs)
Definition view.h:264
GList * views
Definition view.h:192
void(* add_location)(const dt_view_t *view, dt_map_location_data_t *p, const guint posid)
Definition view.h:253
void(* location_action)(const dt_view_t *view, const int action)
Definition view.h:254
void(* set_map_source)(const dt_view_t *view, OsmGpsMapSource_t map_source)
Definition view.h:250
struct dt_view_t * view
Definition view.h:237
struct dt_view_manager_t::@60 audio
struct dt_view_manager_t::@61::@66 print
struct dt_view_manager_t::@61::@64 darkroom
struct dt_lib_module_t *void(* add)(struct dt_lib_module_t *, GtkWidget *, dt_view_type_flags_t)
Definition view.h:224
void(* show_osd)(const dt_view_t *view)
Definition view.h:249
GList * active_images
Definition view.h:196
void(* center_on_bbox)(const dt_view_t *view, gdouble lon1, gdouble lat1, gdouble lon2, gdouble lat2)
Definition view.h:248
dt_view_image_surface_fetcher_t * fetcher
Definition view.c:670
cairo_surface_t * surface
Definition view.c:676
dt_view_surface_value_t result
Definition view.c:677
dt_view_image_surface_fetcher_t * fetcher
Definition view.c:660
uint32_t height
Definition view.h:161
GModule *void * data
Definition view.h:159
uint32_t width
Definition view.h:161
char module_name[64]
Definition view.h:155
void dt_supervisor_thumbnail(const dt_sv_op_t op, const int32_t imgid, const int width, const int height, const int mip, const gboolean success)
@ DT_SV_READ
Definition supervisor.h:80
@ DT_SV_UPDATE
Definition supervisor.h:79
static gboolean dt_supervisor_active(void)
Definition supervisor.h:95
void dt_telemetry_record_module_usage(const char *category, const char *name)
Definition telemetry.c:460
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
A widget to manage and display image thumbnails in Ansel's lighttable and filmstrip views.
@ DT_THUMBTABLE_ZOOM_TWICE
Definition thumbtable.h:81
@ DT_THUMBTABLE_ZOOM_FULL
Definition thumbtable.h:80
@ DT_THUMBTABLE_ZOOM_HALF
Definition thumbtable.h:79
@ DT_THUMBTABLE_ZOOM_FIT
Definition thumbtable.h:78
static double dt_get_wtime(void)
Definition times.h:43
void dt_undo_clear(dt_undo_t *self, uint32_t filter)
Definition undo.c:367
@ DT_UNDO_ALL
Definition undo.h:61
dt_undo_t * dt_undo_get_global(void)
Definition darktable.c:614
Telling the user something happened.
char * dt_get_help_url(char *name)
void dt_view_image_surface_fetcher_invalidate(dt_view_image_surface_fetcher_t *fetcher, cairo_surface_t **target)
Definition view.c:876
void dt_view_manager_cleanup(dt_view_manager_t *vm)
Definition view.c:135
void dt_view_image_surface_fetcher_cleanup(dt_view_image_surface_fetcher_t *fetcher)
Definition view.c:852
static int dt_view_load_module(void *v, const char *libname, const char *module_name)
Definition view.c:186
void dt_view_manager_mouse_moved(dt_view_manager_t *vm, double x, double y, double pressure, int which)
Definition view.c:530
const char * dt_view_manager_name(dt_view_manager_t *vm)
Definition view.c:440
void dt_view_map_center_on_location(const dt_view_manager_t *vm, gdouble lon, gdouble lat, gdouble zoom)
Definition view.c:1415
void dt_view_manager_mouse_enter(dt_view_manager_t *vm)
Definition view.c:524
void dt_view_manager_configure(dt_view_manager_t *vm, int width, int height)
Definition view.c:638
void dt_view_manager_reset(dt_view_manager_t *vm)
Definition view.c:496
static void _enqueue_surface_fetch(dt_view_image_surface_fetcher_t *fetcher)
Definition view.c:698
void dt_view_image_surface_fetcher_init(dt_view_image_surface_fetcher_t *fetcher)
Definition view.c:841
void dt_view_map_location_action(const dt_view_manager_t *vm, const int action)
Definition view.c:1458
cairo_surface_t * dt_cairo_rescale_surface(cairo_surface_t *src, int dst_w, int dst_h)
Definition view.c:938
void dt_view_map_redraw(const dt_view_manager_t *vm)
Definition view.c:1464
void dt_cairo_sharpen_surface_rgb24(cairo_surface_t *surface)
Definition view.c:987
static void _destroy_surface(cairo_surface_t **surface)
Definition view.c:684
static void dt_view_unload_module(dt_view_t *view)
Definition view.c:206
gboolean dt_view_active_images_has_imgid(int32_t imgid)
Definition view.c:1368
GObject * dt_view_map_add_marker(const dt_view_manager_t *vm, dt_geo_map_display_t type, GList *points)
Definition view.c:1439
int dt_view_manager_button_released(dt_view_manager_t *vm, double x, double y, int which, uint32_t state)
Definition view.c:579
void dt_view_manager_expose(dt_view_manager_t *vm, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
Definition view.c:455
void dt_view_map_show_osd(const dt_view_manager_t *vm)
Definition view.c:1427
int dt_view_manager_key_pressed(dt_view_manager_t *vm, GdkEventKey *event)
Definition view.c:552
dt_view_surface_value_t dt_view_image_get_surface_async(dt_view_image_surface_fetcher_t *fetcher, int32_t imgid, int width, int height, cairo_surface_t **target, GtkWidget *widget, int zoom)
Definition view.c:897
void dt_view_audio_start(dt_view_manager_t *vm, int32_t imgid)
Definition view.c:1488
void dt_view_manager_module_toolbox_add(dt_view_manager_t *vm, GtkWidget *tool, dt_view_type_flags_t views)
Definition view.c:1400
void dt_view_active_images_add(int32_t imgid, gboolean raise)
Definition view.c:1347
void dt_view_manager_init(dt_view_manager_t *vm)
Definition view.c:118
void dt_view_active_images_reset(gboolean raise)
Definition view.c:1337
void dt_view_active_images_remove(int32_t imgid, gboolean raise)
Definition view.c:1355
int dt_view_manager_button_pressed(dt_view_manager_t *vm, double x, double y, double pressure, int which, int type, uint32_t state)
Definition view.c:606
uint32_t dt_view_manager_current_flags(const dt_view_manager_t *vm)
Definition view.c:449
GList * dt_view_active_images_get_all()
Definition view.c:1373
static gboolean _view_surface_commit_main(gpointer user_data)
Definition view.c:722
void dt_view_manager_gui_init(dt_view_manager_t *vm)
Definition view.c:126
static const char *const _skull_labels[]
Definition view.c:1053
void dt_vm_remove_child(GtkWidget *widget, gpointer data)
Definition view.c:213
static void _remove_child(GtkWidget *child, GtkContainer *container)
Definition view.c:223
void dt_view_map_add_location(const dt_view_manager_t *vm, dt_map_location_data_t *p, const guint posid)
Definition view.c:1452
void dt_view_print_settings(const dt_view_manager_t *vm, dt_print_info_t *pinfo, dt_images_box *imgs)
Definition view.c:1473
void dt_view_audio_stop(dt_view_manager_t *vm)
Definition view.c:1520
int dt_view_manager_switch(dt_view_manager_t *vm, const char *view_name)
Definition view.c:239
static dt_view_surface_value_t _view_image_get_surface_internal(int32_t imgid, int width, int height, cairo_surface_t **surface, int zoom, dt_atomic_int *shutdown)
Definition view.c:1068
dt_view_surface_value_t dt_view_image_get_surface(int32_t imgid, int width, int height, cairo_surface_t **surface, int zoom)
Definition view.c:1285
int dt_view_manager_switch_by_view(dt_view_manager_t *vm, const dt_view_t *nv)
Definition view.c:261
static void dt_view_manager_load_modules(dt_view_manager_t *vm)
Definition view.c:174
static uint32_t default_flags()
Definition view.c:180
static gboolean _view_surface_matches(const dt_view_image_surface_fetcher_t *fetcher, cairo_surface_t **target, const int32_t imgid, const int width, const int height, const int zoom)
Definition view.c:691
void dt_view_map_center_on_bbox(const dt_view_manager_t *vm, gdouble lon1, gdouble lat1, gdouble lon2, gdouble lat2)
Definition view.c:1421
static int32_t _view_surface_fetch_job_run(dt_job_t *job)
Definition view.c:788
int dt_view_manager_scrolled(dt_view_manager_t *vm, double x, double y, int up, int state, int delta_y)
Definition view.c:650
void dt_view_map_set_map_source(const dt_view_manager_t *vm, OsmGpsMapSource_t map_source)
Definition view.c:1433
void dt_view_image_info_update(int32_t imgid)
Definition view.c:1544
void dt_view_manager_mouse_leave(dt_view_manager_t *vm)
Definition view.c:502
static gint sort_views(gconstpointer a, gconstpointer b)
Definition view.c:151
void dt_view_active_images_set(GList *images, gboolean raise)
Definition view.c:1385
int32_t dt_view_active_images_get_first()
Definition view.c:1378
gboolean dt_view_map_remove_marker(const dt_view_manager_t *vm, dt_geo_map_display_t type, GObject *marker)
Definition view.c:1446
dt_darkroom_layout_t dt_view_darkroom_get_layout(dt_view_manager_t *vm)
Definition view.c:1406
static void _audio_child_watch(GPid pid, gint status, gpointer data)
Definition view.c:1481
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:144
char * dt_view_extend_modes_str(const char *name, const gboolean is_hdr, const gboolean is_bw, const gboolean is_bw_flow)
Definition view.c:1291
dt_view_type_flags_t
Definition view.h:76
@ DT_VIEW_LIGHTTABLE
Definition view.h:77
@ DT_VIEW_DARKROOM
Definition view.h:79
dt_darkroom_layout_t
Definition view.h:94
@ DT_DARKROOM_LAYOUT_EDITING
Definition view.h:96
dt_view_surface_value_t
Definition view.h:104
@ DT_VIEW_SURFACE_OK
Definition view.h:105
@ DT_VIEW_SURFACE_KO
Definition view.h:106
void dt_widget_set_source_rgb(cairo_t *cr, dt_gui_color_t color)
void dt_widget_set_scroll_focus(GtkWidget *widget)
@ DT_GUI_COLOR_BG
#define DT_UI_SCALE_DEVICE(value)
void dt_ui_cleanup_main_table(dt_ui_t *ui)
void dt_ui_restore_panels(dt_ui_t *ui)
void dt_ui_set_image_info_label(dt_ui_t *ui, const char *label)
void dt_ui_container_add_widget(dt_ui_t *ui, const dt_ui_container_t c, GtkWidget *w)
@ DT_UI_CONTAINER_SIZE