Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
filter.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011 Henrik Andersson.
4 Copyright (C) 2011, 2013 johannes hanika.
5 Copyright (C) 2011 Robert Bieber.
6 Copyright (C) 2011-2018 Tobias Ellinghaus.
7 Copyright (C) 2012-2013 José Carlos García Sogo.
8 Copyright (C) 2012 marcel.
9 Copyright (C) 2012 Mika Boström.
10 Copyright (C) 2012 Richard Wonka.
11 Copyright (C) 2012 Simon Spannagel.
12 Copyright (C) 2013 Benjamin Cahill.
13 Copyright (C) 2013-2014, 2016 Roman Lebedev.
14 Copyright (C) 2015 JohnnyRun.
15 Copyright (C) 2016, 2019-2022 Aldric Renaudin.
16 Copyright (C) 2017 Dan Torop.
17 Copyright (C) 2018 Mario Lueder.
18 Copyright (C) 2018-2020, 2022 Pascal Obry.
19 Copyright (C) 2018 Sam Smith.
20 Copyright (C) 2018 Simon Legner.
21 Copyright (C) 2019, 2022-2023, 2025 Aurélien PIERRE.
22 Copyright (C) 2019, 2021 Bill Ferguson.
23 Copyright (C) 2020 Chris Elston.
24 Copyright (C) 2020 Hubert Kowalski.
25 Copyright (C) 2020 jpverrue.
26 Copyright (C) 2020-2022 Philippe Weyland.
27 Copyright (C) 2021 Davide.
28 Copyright (C) 2021-2022 Diederik Ter Rahe.
29 Copyright (C) 2022 Martin Bařinka.
30 Copyright (C) 2022 Nicolas Auffray.
31 Copyright (C) 2022 Victor Forsiuk.
32 Copyright (C) 2023 Luca Zulberti.
33
34 darktable is free software: you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation, either version 3 of the License, or
37 (at your option) any later version.
38
39 darktable is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 GNU General Public License for more details.
43
44 You should have received a copy of the GNU General Public License
45 along with darktable. If not, see <http://www.gnu.org/licenses/>.
46*/
47
48#include "common/collection.h"
49#include "metadata/tags.h"
50#include "common/selection.h"
51#include "system/mem_alloc.h"
52#include "common/times.h"
54#include "control/signal.h"
55#include "control/control.h"
56#include "develop/develop.h"
57
58#include "gui/application.h"
60#include "widgets/button.h"
61#include "libs/lib.h"
62#include "libs/lib_api.h"
63#include "widgets/bauhaus.h"
64#include "widgets/popup.h"
67
68DT_MODULE(1)
69
86
87/* callback for sort combobox change */
88static void _lib_filter_sort_combobox_changed(GtkWidget *widget, gpointer user_data);
89/* callback for reverse sort check button change */
90static void _lib_filter_reverse_button_changed(GtkDarktableToggleButton *widget, gpointer user_data);
91/* updates the query and redraws the view */
93
94/* save the images order if the first collect filter is on tag*/
96/* images order change from outside */
97static void _lib_filter_images_order_change(gpointer instance, int order, dt_lib_module_t *self);
98
100static gboolean _show_popover_menu(dt_lib_module_t *self, GtkWidget *w);
101
117#define NB_ITEMS (sizeof(items) / sizeof(dt_collection_sort_t))
118
119static const char *_sort_names[]
120 = { N_("filename"),
121 N_("captured"),
122 N_("imported"),
123 N_("modified"),
124 N_("exported"),
125 N_("printed"),
126 N_("rating"),
127 N_("id"),
128 N_("color label"),
129 N_("group"),
130 N_("full path"),
131 N_("title"),
132 NULL };
133
135{
136 for(int i = 0; i < NB_ITEMS; i++)
137 {
138 if(sort == items[i])
139 return i;
140 }
141 return 0;
142}
143
144const char *name(struct dt_lib_module_t *self)
145{
146 return _("filter");
147}
148
149const char **views(dt_lib_module_t *self)
150{
151 static const char *v[] = {"lighttable", NULL};
152 return v;
153}
154
159
161{
162 return 0;
163}
164
166{
167 return 2001;
168}
169
170static void _set_widget_dimmed(GtkWidget *widget, const gboolean dimmed)
171{
172 if(dimmed) dt_gui_add_class(widget, "dt_dimmed");
173 else dt_gui_remove_class(widget, "dt_dimmed");
174 gtk_widget_queue_draw(GTK_WIDGET(widget));
175}
176
177static char *_encode_text_filter(const char *entry)
178{
179 // by default adds start and end wildcard
180 // " removes the corresponding wildcard
181 char start[2] = {0};
182 char *text = NULL;
183 char *p = (char *)entry;
184 if(strlen(entry) > 1 && !(entry[0] == '"' && entry[1] == '"'))
185 {
186 if(entry[0] == '"')
187 p++;
188 else if(entry[0])
189 start[0] = '%';
190 if(entry[strlen(entry) - 1] == '"')
191 {
192 text = g_strconcat(start, (char *)p, NULL);
193 text[strlen(text) - 1] = '\0';
194 }
195 else if(entry[0])
196 text = g_strconcat(start, (char *)p, "%", NULL);
197 }
198 return text;
199}
200
201static char *_decode_text_filter(const char *text)
202{
203 // revert the encoded filter for display
204 char start[2] = {0};
205 char *text1 = g_strdup(text);
206 char *p = text1;
207 char *text2;
208 if(text1[0])
209 {
210 if(text1[0] == '%')
211 p++;
212 else
213 start[0] = '\"';
214 if(strlen(text1) > 1 && text1[strlen(text1) - 1] == '%')
215 {
216 text1[strlen(text1) - 1] = '\0';
217 text2 = g_strconcat(start, (char *)p, NULL);
218 }
219 else
220 text2 = g_strconcat(start, (char *)p, "\"", NULL);
221 dt_free(text1);
222 return text2;
223 }
224 else return text1;
225}
226
227static gboolean _text_entry_changed_wait(gpointer user_data)
228{
229 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
231 if(d->time_out)
232 {
233 d->time_out--;
234 double clock = dt_get_wtime();
235 if(clock - d->last_key_time >= 0.4)
236 {
237 d->time_out = 1; // force the query execution
238 d->last_key_time = clock;
239 }
240
241 if(d->time_out == 1)
242 { // tell we are busy
243 _set_widget_dimmed(d->text, TRUE);
244 }
245 else if(!d->time_out)
246 {
247 char *text = _encode_text_filter(gtk_entry_get_text(GTK_ENTRY(d->text)));
248
249 // avoids activating twice the same query
251 {
254 }
255 else dt_free(text);
256 _set_widget_dimmed(d->text, FALSE);
257 return FALSE;
258 }
259 }
260 return TRUE;
261}
262
264{
265 // two timeouts 1) 0.4 sec after the last key, 2) 1.5 sec of successive keys
267 d->last_key_time = dt_get_wtime();
268 if(!d->time_out)
269 {
270 d->time_out = 15;
271 g_timeout_add(100, _text_entry_changed_wait, self);
272 }
273}
274
275static void _text_entry_changed(GtkEntry *entry, dt_lib_module_t *self)
276{
277 _launch_text_query(self);
278}
279
281{
284 gtk_entry_set_text(GTK_ENTRY(d->text), "");
285}
286
292
293gboolean _focus_search_action(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval,
294 GdkModifierType modifier, gpointer data)
295{
296 dt_lib_module_t *self = (dt_lib_module_t *)data;
298 gtk_widget_grab_focus(GTK_WIDGET(d->text));
299 return TRUE;
300}
301
311
312static void _dtgtk_button_set_active(GtkWidget *w, gboolean active)
313{
314 // shitty design: the active rejected state is signaled as a right orientation...
315 if(active)
316 DTGTK_BUTTON(w)->icon_flags |= CPF_DIRECTION_RIGHT;
317 else
318 DTGTK_BUTTON(w)->icon_flags &= ~CPF_DIRECTION_RIGHT;
319
321 gtk_widget_queue_draw(w);
322}
323
324
332
333static gboolean _colorlabel_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
334{
336
337 if(e->button == 3)
338 {
339 _show_popover_menu(self, w);
340 return TRUE;
341 }
342
344
345 // Toggle state
347
348 // Update collection flags
349 for(int i = 0; i <= DT_COLORLABELS_LAST; i++)
350 {
352 flags |= colors[i];
353 else
354 flags &= ~colors[i];
355 }
356
360 return TRUE;
361}
362
370
371
372static gboolean _altered_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
373{
374 if(e->button == 3)
375 {
376 _show_popover_menu(self, w);
377 return TRUE;
378 }
379
381
382 // Toggle state
384
385 // Update collection flags
388 else
389 flags &= ~COLLECTION_FILTER_ALTERED;
390
394 return TRUE;
395}
396
397static gboolean _unaltered_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
398{
399 if(e->button == 3)
400 {
401 _show_popover_menu(self, w);
402 return TRUE;
403 }
404
406
407 // Toggle state
409
410 // Update collection flags
413 else
414 flags &= ~COLLECTION_FILTER_UNALTERED;
415
419 return TRUE;
420}
421
422static void _culling_mode(GtkWidget *widget, gpointer data)
423{
424 dt_gui_get_global()->culling_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
425
426 // If we are exiting culling mode, we need to pop the selection before resetting the thumbtable collection
427 if(!dt_gui_get_global()->culling_mode) dt_culling_mode_to_selection();
428
429 // Anchor the re-scrollings to the right image
430 const int32_t imgid = dt_selection_get_first_id(dt_selection_get_global());
433
434 // Force-rebuild the whole thumbtable on the next collection update
435 dt_thumbtable_reset_collection(dt_gui_get_ui()->thumbtable_lighttable);
436
439}
440
445
447{
448 gtk_widget_set_halign(widget, GTK_ALIGN_FILL);
449 gtk_widget_set_hexpand(widget, TRUE);
450
451 gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
452 gtk_widget_set_vexpand(widget, FALSE);
453}
454
464
465
467{
470
471 // Update GUI button state
472 for(int i = 0; i < 7; i++)
473 {
474 gboolean active = flags & ratings[i];
475 if(i != 1)
476 {
477 // fill stars if active
478 if(active)
479 DTGTK_BUTTON(d->stars[i])->icon_data = &dt_bauhaus_get_global()->color_fg;
480 else
481 DTGTK_BUTTON(d->stars[i])->icon_data = NULL;
482 }
483
484 _dtgtk_button_set_active(d->stars[i], active);
485 }
486}
487
488
489static gboolean _rating_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
490{
492
493 if(e->button == 3)
494 {
495 _show_popover_menu(self, w);
496 return TRUE;
497 }
498
500
501 // Do range selection for >= rating if Shift + Click
502 if(dt_modifier_is(e->state, GDK_SHIFT_MASK))
503 {
504 gboolean include = TRUE;
505 for(int i = 6; i > 1; i--)
506 {
507 dtgtk_button_set_active(DTGTK_BUTTON(d->stars[i]), include);
508 if(w == d->stars[i]) include = FALSE;
509 }
510 }
511 else
512 {
513 // Toggle state for clicked button
515 }
516
517 // Update collection flags
518 for(int i = 0; i < 7; i++)
519 {
521 flags |= ratings[i];
522 else
523 flags &= ~ratings[i];
524 }
525
529 return TRUE;
530}
531
532
541
550
551
553{
555 dt_gui_menu_popup(GTK_MENU(d->menu), w, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH);
556 return TRUE;
557}
558
560{
561 GtkWidget *spacer = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
562 gtk_box_pack_start(GTK_BOX(self->widget), spacer, FALSE, FALSE, 0);
563}
564
566{
567 GtkWidget *spacer = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
568 dt_gui_add_class(spacer, "subsection");
569 gtk_box_pack_start(GTK_BOX(self->widget), spacer, FALSE, FALSE, 0);
570}
571
573{
574 /* initialize ui widgets */
576 self->data = (void *)d;
577
578 self->widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
580 dt_gui_add_class(self->widget, "quick_filter_box");
581
582 GtkWidget *label;
583
584 d->refresh = dtgtk_button_new(dtgtk_cairo_paint_refresh, 0, NULL);
585 gtk_widget_set_tooltip_text(d->refresh, _("Refresh the current collection to evict images\n"
586 "which properties have been changed\n"
587 "and don't match the current filters anymore."));
588 g_signal_connect(G_OBJECT(d->refresh), "clicked", G_CALLBACK(_refresh_collection_callback), NULL);
589 gtk_widget_set_name(d->refresh, "quick-filter-reload");
590 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->refresh), FALSE, FALSE, 0);
591
592 gchar *path = dt_accels_build_path(_("Lighttable/Actions"), _("Reload current collection"));
593 dt_accels_new_widget_shortcut(dt_gui_get_accels(), d->refresh, "activate",
594 dt_gui_get_accels()->lighttable_accels, path, GDK_KEY_r, DT_PRIMARY_MASK,
595 FALSE);
596 dt_free(path);
597
599
600 label = gtk_label_new(_("Include"));
601 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, FALSE, 0);
602
603 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
604 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), FALSE, FALSE, 0);
605 gtk_widget_set_name(hbox, "quick-filter-ratings");
606
607 // star buttons
608 for(int k = 0; k < 7; k++)
609 {
610 if(k == 0)
611 {
613 gtk_widget_set_name(d->stars[k], "no-star-filter");
614 }
615 else if(k == 1)
616 {
618 gtk_widget_set_name(d->stars[k], "rejected-filter");
619 }
620 else
621 d->stars[k] = dtgtk_button_new(dtgtk_cairo_paint_star, k, NULL);
622
623 dt_gui_add_class(d->stars[k], "star");
624 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(d->stars[k]), FALSE, FALSE, 0);
625 g_signal_connect(G_OBJECT(d->stars[k]), "button-press-event", G_CALLBACK(_rating_clicked), self);
626
627 if(k == 1)
628 {
629 GtkWidget *spacer = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
630 dt_gui_add_class(spacer, "subsection");
631 gtk_box_pack_start(GTK_BOX(hbox), spacer, FALSE, FALSE, 0);
632 }
633 }
635
636 gtk_widget_set_tooltip_text(d->stars[0], _("Toggle filtering in/out unrated images (0 star)"));
637 gtk_widget_set_tooltip_text(d->stars[1], _("Toggle filtering in/out rejected images"));
638 gtk_widget_set_tooltip_text(d->stars[2], _("Toggle filtering in/out images rated 1 star"));
639 gtk_widget_set_tooltip_text(d->stars[3], _("Toggle filtering in/out images rated 2 stars"));
640 gtk_widget_set_tooltip_text(d->stars[4], _("Toggle filtering in/out images rated 3 stars"));
641 gtk_widget_set_tooltip_text(d->stars[5], _("Toggle filtering in/out images rated 4 stars"));
642 gtk_widget_set_tooltip_text(d->stars[6], _("Toggle filtering in/out images rated 5 stars"));
643
645
646 // colorlabels filter
647 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
648 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), FALSE, FALSE, 0);
649 gtk_widget_set_name(hbox, "quickfilters-colors");
650
653
654 for(int k = 0; k < DT_COLORLABELS_LAST + 1; k++)
655 {
656 d->colors[k] = dtgtk_button_new(dtgtk_cairo_paint_label_sel, col[k], NULL);
657 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(d->colors[k]), FALSE, FALSE, 0);
658 g_signal_connect(G_OBJECT(d->colors[k]), "button-press-event", G_CALLBACK(_colorlabel_clicked), self);
659 }
661
662 gtk_widget_set_tooltip_text(d->colors[0], _("Toggle filtering in/out images without color label"));
663 gtk_widget_set_tooltip_text(d->colors[1], _("Toggle filtering in/out images with red label"));
664 gtk_widget_set_tooltip_text(d->colors[2], _("Toggle filtering in/out images with yellow label"));
665 gtk_widget_set_tooltip_text(d->colors[3], _("Toggle filtering in/out images with green label"));
666 gtk_widget_set_tooltip_text(d->colors[4], _("Toggle filtering in/out images with blue label"));
667 gtk_widget_set_tooltip_text(d->colors[5], _("Toggle filtering in/out images with purple label"));
668
670
671 // changed filter
672 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
673 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), FALSE, FALSE, 0);
674 gtk_widget_set_name(hbox, "quickfilters-altered");
675
676 d->unaltered = dtgtk_button_new(dtgtk_cairo_paint_unaltered, 0, NULL);
677 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(d->unaltered), FALSE, FALSE, 0);
678 g_signal_connect(G_OBJECT(d->unaltered), "button-press-event", G_CALLBACK(_unaltered_clicked), self);
679 gtk_widget_set_tooltip_text(d->unaltered, _("Toggle filtering in/out unedited images"));
680
681 d->altered = dtgtk_button_new(dtgtk_cairo_paint_altered, 0, NULL);
682 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(d->altered), FALSE, FALSE, 0);
683 g_signal_connect(G_OBJECT(d->altered), "button-press-event", G_CALLBACK(_altered_clicked), self);
684 gtk_widget_set_tooltip_text(d->altered, _("Toggle filtering in/out edited images"));
685
687
689
690 // Culling mode
691 d->culling = gtk_toggle_button_new_with_label(_("Restrict to selection"));
692 gtk_widget_set_tooltip_text(d->culling, _("Restrict the current view to only selected pictures"));
693 g_signal_connect(G_OBJECT(d->culling), "toggled", G_CALLBACK(_culling_mode), (gpointer)self);
694 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->culling), FALSE, FALSE, 0);
695 gtk_widget_set_name(d->culling, "quickfilter-culling");
696
697 path = dt_accels_build_path(_("Lighttable/Actions"), _("Toggle culling mode"));
698 dt_accels_new_widget_shortcut(dt_gui_get_accels(), d->culling, "activate",
699 dt_gui_get_accels()->lighttable_accels, path, GDK_KEY_s, DT_PRIMARY_MASK,
700 FALSE);
701 dt_free(path);
702
704
705 label = gtk_label_new(_("Sort by"));
706 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, FALSE, 0);
707
708 /* sort combobox */
710
711 d->sort = gtk_combo_box_text_new();
712
713 for(int i = 0; i < NB_ITEMS; i++)
714 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(d->sort), NULL, _(_sort_names[i]));
715
716 gtk_combo_box_set_active(GTK_COMBO_BOX(d->sort ), _filter_get_items(sort));
717 g_signal_connect(G_OBJECT(d->sort), "changed", G_CALLBACK(_lib_filter_sort_combobox_changed), (gpointer)self);
718 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->sort), FALSE, FALSE, 0);
719 gtk_widget_set_name(d->sort, "quick-filter-sort");
720
721 /* reverse order checkbutton */
723 if(dt_collection_get_global()->params.descending)
725 CPF_DIRECTION_DOWN, NULL);
726 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->reverse), FALSE, FALSE, 0);
727 dt_gui_add_class(d->reverse, "dt_ignore_fg_state");
728
729 /* select the last value and connect callback */
730 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->reverse),
732 g_signal_connect(G_OBJECT(d->reverse), "toggled", G_CALLBACK(_lib_filter_reverse_button_changed),
733 (gpointer)self);
734
736
737 // text filter
738 d->text = gtk_search_entry_new();
740 gtk_widget_set_hexpand(d->text, TRUE);
741 dt_gui_add_class(GTK_WIDGET(d->text), "menu-text-entry");
743 gtk_entry_set_text(GTK_ENTRY(d->text), text);
744 gtk_entry_set_placeholder_text(GTK_ENTRY(d->text), _("Search an image..."));
745 dt_free(text);
746 g_signal_connect(G_OBJECT(d->text), "search-changed", G_CALLBACK(_text_entry_changed), self);
747 g_signal_connect(G_OBJECT(d->text), "stop-search", G_CALLBACK(_reset_text_entry), self);
748 gtk_entry_set_width_chars(GTK_ENTRY(d->text), 24);
749 gtk_widget_set_tooltip_text(d->text,
750 /* xgettext:no-c-format */
751 _("filter by text from images metadata, tags, file path and name"
752 /* xgettext:no-c-format */
753 "\n`%' is the wildcard character"
754 /* xgettext:no-c-format */
755 "\nby default start and end wildcards are auto-applied"
756 /* xgettext:no-c-format */
757 "\nstarting or ending with a double quote disables the corresponding wildcard"
758 /* xgettext:no-c-format */
759 "\nis dimmed during the search execution"));
760 //dt_gui_add_class(d->text, "dt_transparent_background");
761 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->text), TRUE, TRUE, 0);
762 gtk_widget_set_name(d->text, "quickfilter-search-box");
763
764 dt_accels_new_lighttable_action(_focus_search_action, self, NULL, N_("Lighttable/Actions"), N_("Search a picture"),
765 GDK_KEY_f, DT_PRIMARY_MASK, _("Focuses the control"));
766
768
770 G_CALLBACK(_lib_filter_images_order_change), self);
771
772 // context menu
773 d->menu = gtk_menu_new();
774
775 GtkWidget *first_entry = gtk_menu_item_new_with_label(_("Select all filters"));
776 gtk_menu_shell_append(GTK_MENU_SHELL(d->menu), first_entry);
777 g_signal_connect(G_OBJECT(first_entry), "activate", G_CALLBACK(_select_all_callback), self);
778 path = dt_accels_build_path(_("Lighttable/Actions"), _("Select all filters"));
779 dt_accels_new_widget_shortcut(dt_gui_get_accels(), first_entry, "activate",
780 dt_gui_get_accels()->lighttable_accels, path, 0, 0,
781 FALSE);
782 dt_free(path);
783
784 GtkWidget *second_entry = gtk_menu_item_new_with_label(_("Deselect all filters"));
785 gtk_menu_shell_append(GTK_MENU_SHELL(d->menu), second_entry);
786 g_signal_connect(G_OBJECT(second_entry), "activate", G_CALLBACK(_select_none_callback), self);
787 path = dt_accels_build_path(_("Lighttable/Actions"), _("Deselect all filters"));
788 dt_accels_new_widget_shortcut(dt_gui_get_accels(), second_entry, "activate",
789 dt_gui_get_accels()->lighttable_accels, path, 0, 0,
790 FALSE);
791 dt_free(path);
792
793 gtk_widget_show_all(d->menu);
794}
795
797{
798 gchar *path = dt_accels_build_path(_("Lighttable/Actions"), _("Reload current collection"));
800 dt_free(path);
801
802 path = dt_accels_build_path(_("Lighttable/Actions"), _("Toggle culling mode"));
804 dt_free(path);
805
806 path = dt_accels_build_path(_("Lighttable/Actions"), _("Select all filters"));
808 dt_free(path);
809
810 path = dt_accels_build_path(_("Lighttable/Actions"), _("Deselect all filters"));
812 dt_free(path);
813
816 dt_free(self->data);
817}
818
819/* save the images order if the first collect filter is on tag*/
821{
823 if(dt_collection_get_global()->tagid)
824 {
825 const uint32_t sort = items[gtk_combo_box_get_active(GTK_COMBO_BOX(d->sort))];
826 const gboolean descending = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->reverse));
827 dt_tag_set_tag_order_by_id(dt_collection_get_global()->tagid, sort, descending);
828 }
829}
830
831static void _lib_filter_images_order_change(gpointer instance, const int order, dt_lib_module_t *self)
832{
834 gtk_combo_box_set_active(GTK_COMBO_BOX(d->sort), _filter_get_items(order & ~DT_COLLECTION_ORDER_FLAG));
835 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->reverse), order & DT_COLLECTION_ORDER_FLAG);
836}
837
838static void _lib_filter_reverse_button_changed(GtkDarktableToggleButton *widget, gpointer user_data)
839{
840 const gboolean reverse = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
841
842 if(reverse)
844 else
846 gtk_widget_queue_draw(GTK_WIDGET(widget));
847
848 /* update last settings */
850
851 /* save the images order */
852 _lib_filter_set_tag_order(user_data);
853
854 /* update query and view */
856}
857
858static void _lib_filter_sort_combobox_changed(GtkWidget *widget, gpointer user_data)
859{
860 /* update the ui last settings */
861 dt_collection_set_sort(dt_collection_get_global(), items[gtk_combo_box_get_active(GTK_COMBO_BOX(widget))], -1);
862
863 /* save the images order */
864 _lib_filter_set_tag_order(user_data);
865
866 /* update the query and view */
868}
869
871{
872 /* sometimes changes */
874
875 /* updates query */
877}
878
879// clang-format off
880// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
881// vim: shiftwidth=2 expandtab tabstop=2 cindent
882// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
883// clang-format on
void dt_accels_remove_shortcut(dt_accels_t *accels, const char *path)
Remove the shortcut object identified by path and all its accels.
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
void dt_accels_new_widget_shortcut(dt_accels_t *accels, GtkWidget *widget, const gchar *signal, GtkAccelGroup *accel_group, const gchar *accel_path, guint key_val, GdkModifierType accel_mods, const gboolean lock)
Register a new shortcut for a widget, setting up its path, default keys and accel group....
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
struct dt_accels_t * dt_gui_get_accels(void)
struct dt_ui_t * dt_gui_get_ui(void)
#define dt_accels_new_lighttable_action(a, b, w, c, d, e, f, g)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active)
Definition button.c:177
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:135
gboolean dtgtk_button_get_active(GtkDarktableButton *button)
Definition button.c:186
#define DTGTK_BUTTON(obj)
Definition button.h:40
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
Definition collection.c:837
void dt_culling_mode_to_selection()
Definition collection.c:897
void dt_collection_set_query_flags(const dt_collection_t *collection, dt_collection_query_flags_t flags)
Definition collection.c:330
void dt_collection_set_text_filter(const dt_collection_t *collection, char *text_filter)
Definition collection.c:316
dt_collection_t * dt_collection_get_global(void)
Definition collection.c:138
char * dt_collection_get_text_filter(const dt_collection_t *collection)
Definition collection.c:310
gboolean dt_collection_get_sort_descending(const dt_collection_t *collection)
Definition collection.c:384
void dt_collection_set_filter_flags(const dt_collection_t *collection, dt_collection_filter_flag_t flags)
Definition collection.c:303
dt_collection_filter_flag_t dt_collection_get_filter_flags(const dt_collection_t *collection)
Definition collection.c:297
dt_collection_sort_t dt_collection_get_sort_field(const dt_collection_t *collection)
Definition collection.c:378
void dt_collection_set_sort(const dt_collection_t *collection, dt_collection_sort_t sort, gboolean reverse)
Definition collection.c:367
dt_collection_properties_t
Definition collection.h:120
@ DT_COLLECTION_PROP_RATING
Definition collection.h:149
@ DT_COLLECTION_PROP_SORT
Definition collection.h:156
@ DT_COLLECTION_PROP_COLORLABEL
Definition collection.h:141
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:155
#define DT_COLLECTION_ORDER_FLAG
Definition collection.h:116
#define COLLECTION_QUERY_FULL
Definition collection.h:76
dt_collection_filter_flag_t
Definition collection.h:79
@ COLLECTION_FILTER_ALTERED
Definition collection.h:81
@ COLLECTION_FILTER_UNALTERED
Definition collection.h:82
@ COLLECTION_FILTER_GREEN
Definition collection.h:92
@ COLLECTION_FILTER_NONE
Definition collection.h:80
@ COLLECTION_FILTER_2_STAR
Definition collection.h:86
@ COLLECTION_FILTER_MAGENTA
Definition collection.h:94
@ COLLECTION_FILTER_0_STAR
Definition collection.h:84
@ COLLECTION_FILTER_1_STAR
Definition collection.h:85
@ COLLECTION_FILTER_BLUE
Definition collection.h:93
@ COLLECTION_FILTER_5_STAR
Definition collection.h:89
@ COLLECTION_FILTER_YELLOW
Definition collection.h:91
@ COLLECTION_FILTER_ALL
Definition collection.h:96
@ COLLECTION_FILTER_REJECTED
Definition collection.h:83
@ COLLECTION_FILTER_4_STAR
Definition collection.h:88
@ COLLECTION_FILTER_3_STAR
Definition collection.h:87
@ COLLECTION_FILTER_RED
Definition collection.h:90
@ COLLECTION_FILTER_WHITE
Definition collection.h:95
@ DT_COLLECTION_CHANGE_RELOAD
Definition collection.h:164
dt_collection_sort_t
Definition collection.h:100
@ DT_COLLECTION_SORT_EXPORT_TIMESTAMP
Definition collection.h:106
@ DT_COLLECTION_SORT_IMPORT_TIMESTAMP
Definition collection.h:104
@ DT_COLLECTION_SORT_DATETIME
Definition collection.h:103
@ DT_COLLECTION_SORT_GROUP
Definition collection.h:111
@ DT_COLLECTION_SORT_FILENAME
Definition collection.h:102
@ DT_COLLECTION_SORT_RATING
Definition collection.h:108
@ DT_COLLECTION_SORT_PATH
Definition collection.h:112
@ DT_COLLECTION_SORT_TITLE
Definition collection.h:113
@ DT_COLLECTION_SORT_CHANGE_TIMESTAMP
Definition collection.h:105
@ DT_COLLECTION_SORT_NONE
Definition collection.h:101
@ DT_COLLECTION_SORT_PRINT_TIMESTAMP
Definition collection.h:107
@ DT_COLLECTION_SORT_ID
Definition collection.h:109
@ DT_COLLECTION_SORT_COLOR
Definition collection.h:110
@ DT_COLORLABELS_PURPLE
Definition colorlabels.h:46
@ DT_COLORLABELS_GREEN
Definition colorlabels.h:44
@ DT_COLORLABELS_YELLOW
Definition colorlabels.h:43
@ DT_COLORLABELS_BLUE
Definition colorlabels.h:45
@ DT_COLORLABELS_LAST
Definition colorlabels.h:47
@ DT_COLORLABELS_RED
Definition colorlabels.h:42
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_control_set_mouse_over_id(int32_t value)
Definition control.c:994
void dt_control_set_keyboard_over_id(int32_t value)
Definition control.c:1020
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
static gboolean _text_entry_changed_wait(gpointer user_data)
Definition filter.c:227
static void _launch_text_query(dt_lib_module_t *self)
Definition filter.c:263
static char * _encode_text_filter(const char *entry)
Definition filter.c:177
static void _reset_text_filter(dt_lib_module_t *self)
Definition filter.c:280
const dt_collection_filter_flag_t colors[6]
Definition filter.c:302
static gboolean _show_popover_menu(dt_lib_module_t *self, GtkWidget *w)
Definition filter.c:552
void _widget_align_left(GtkWidget *widget)
Definition filter.c:446
static void _culling_mode(GtkWidget *widget, gpointer data)
Definition filter.c:422
static int _filter_get_items(const dt_collection_sort_t sort)
Definition filter.c:134
const dt_collection_filter_flag_t ratings[7]
Definition filter.c:455
gboolean _focus_search_action(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType modifier, gpointer data)
Definition filter.c:293
static void _update_altered_filters(dt_lib_module_t *self)
Definition filter.c:363
static gboolean _rating_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
Definition filter.c:489
const dt_collection_sort_t items[]
Definition filter.c:102
static void _lib_filter_images_order_change(gpointer instance, int order, dt_lib_module_t *self)
Definition filter.c:831
static const char * _sort_names[]
Definition filter.c:120
void gui_cleanup(dt_lib_module_t *self)
Definition filter.c:796
static void _reset_text_entry(GtkButton *button, dt_lib_module_t *self)
Definition filter.c:287
static void _refresh_collection_callback(GtkButton *button, gpointer user_data)
Definition filter.c:441
static void _update_colors_filter(dt_lib_module_t *self)
Definition filter.c:325
static char * _decode_text_filter(const char *text)
Definition filter.c:201
#define NB_ITEMS
Definition filter.c:117
static void _lib_filter_sort_combobox_changed(GtkWidget *widget, gpointer user_data)
Definition filter.c:858
static gboolean _colorlabel_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
Definition filter.c:333
static void _update_rating_filter(dt_lib_module_t *self)
Definition filter.c:466
static void _select_all_callback(GtkWidget *widget, dt_lib_module_t *self)
Definition filter.c:533
static void _select_none_callback(GtkWidget *widget, dt_lib_module_t *self)
Definition filter.c:542
uint32_t container(dt_lib_module_t *self)
Definition filter.c:155
static void _insert_subsection_sep(dt_lib_module_t *self)
Definition filter.c:565
void gui_init(dt_lib_module_t *self)
Definition filter.c:572
int position()
Definition filter.c:165
static gboolean _unaltered_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
Definition filter.c:397
const char ** views(dt_lib_module_t *self)
Definition filter.c:149
int expandable(dt_lib_module_t *self)
Definition filter.c:160
static void _set_widget_dimmed(GtkWidget *widget, const gboolean dimmed)
Definition filter.c:170
static void _dtgtk_button_set_active(GtkWidget *w, gboolean active)
Definition filter.c:312
static void _insert_section_sep(dt_lib_module_t *self)
Definition filter.c:559
static void _lib_filter_update_query(dt_lib_module_t *self, dt_collection_properties_t changed_property)
Definition filter.c:870
static gboolean _altered_clicked(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self)
Definition filter.c:372
static void _lib_filter_set_tag_order(dt_lib_module_t *self)
Definition filter.c:820
static void _text_entry_changed(GtkEntry *entry, dt_lib_module_t *self)
Definition filter.c:275
static void _lib_filter_reverse_button_changed(GtkDarktableToggleButton *widget, gpointer user_data)
Definition filter.c:838
float *const restrict const size_t k
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
const char * name
Definition pdf.h:90
void dt_gui_menu_popup(GtkMenu *menu, GtkWidget *button, GdkGravity widget_anchor, GdkGravity menu_anchor)
Definition popup.c:53
int32_t dt_selection_get_first_id(struct dt_selection_t *selection)
Definition selection.c:103
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_IMAGES_ORDER_CHANGE
This signal is raised to request image order change.
Definition signal.h:153
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
Definition signal.h:130
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
GdkRGBA color_fg
Definition bauhaus.h:277
gboolean culling_mode
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
GtkWidget * reverse
Definition filter.c:75
GtkWidget * culling
Definition filter.c:78
GtkWidget * comparator
Definition filter.c:73
GtkWidget * menu
Definition filter.c:82
GtkWidget * refresh
Definition filter.c:79
GtkWidget * unaltered
Definition filter.c:81
GtkWidget * sort
Definition filter.c:74
double last_key_time
Definition filter.c:84
GtkWidget * altered
Definition filter.c:80
GtkWidget * text
Definition filter.c:76
void dt_tag_set_tag_order_by_id(const uint32_t tagid, const uint32_t sort, const gboolean descending)
Definition tags.c:1288
void dt_thumbtable_reset_collection(dt_thumbtable_t *table)
static double dt_get_wtime(void)
Definition times.h:43
void dtgtk_togglebutton_set_paint(GtkDarktableToggleButton *button, DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
#define DTGTK_TOGGLEBUTTON(obj)
static gboolean dt_modifier_is(GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define DT_GUI_BOX_SPACING
void dt_gui_remove_class(GtkWidget *widget, const gchar *class_name)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
void dtgtk_cairo_paint_refresh(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_label_sel(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_sortby(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_reject(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_star(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_unaltered(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_altered(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_unratestar(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ CPF_DIRECTION_UP
@ CPF_DIRECTION_RIGHT
@ CPF_DIRECTION_DOWN
@ DT_UI_CONTAINER_PANEL_TOP_SECOND_ROW