Adds a keyword filtering feature to Timeline PanView.
[geeqie.git] / src / pan-view / pan-timeline.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "pan-timeline.h"
23
24 #include "metadata.h"
25 #include "pan-item.h"
26 #include "pan-util.h"
27 #include "pan-view.h"
28 #include "ui_fileops.h"
29
30 void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
31 {
32         GList *list;
33         GList *work;
34         GHashTable *filter_kw_table;
35         GHashTableIter filter_kw_iter;
36         gchar *filter_kw;
37         gint x, y;
38         time_t group_start_date;
39         gint total;
40         gint count;
41         PanItem *pi_month = NULL;
42         PanItem *pi_day = NULL;
43         gint month_start;
44         gint day_start;
45         gint x_width;
46         gint y_height;
47
48         list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
49         filter_kw_table = pw->filter_ui->filter_kw_table;  // Shorthand.
50
51         if (pw->cache_list && pw->exif_date_enable)
52                 {
53                 pw->cache_list = pan_cache_sort(pw->cache_list, SORT_NAME, TRUE);
54                 list = filelist_sort(list, SORT_NAME, TRUE);
55                 pan_cache_sync_date(pw, list);
56                 }
57
58         pw->cache_list = pan_cache_sort(pw->cache_list, SORT_TIME, TRUE);
59         list = filelist_sort(list, SORT_TIME, TRUE);
60
61         *width = PAN_BOX_BORDER * 2;
62         *height = PAN_BOX_BORDER * 2;
63
64         x = 0;
65         y = 0;
66         month_start = y;
67         day_start = month_start;
68         x_width = 0;
69         y_height = 0;
70         group_start_date = 0;
71         // total and count are used to enforce a stride of PAN_GROUP_MAX thumbs.
72         total = 0;
73         count = 0;
74         work = list;
75         while (work)
76                 {
77                 FileData *fd;
78                 PanItem *pi;
79
80                 fd = work->data;
81                 work = work->next;
82
83                 // Don't show images that fail the keyword test.
84                 if (g_hash_table_size(filter_kw_table) > 0)
85                         {
86                         gint match_count = 0;
87                         gint miss_count = 0;
88                         // TODO(xsdg): OPTIMIZATION Do the search inside of metadata.c to avoid a
89                         // bunch of string list copies.
90                         // TODO(xsdg): Allow user to switch between union and intersection.
91                         GList *img_keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
92                         if (!img_keywords) continue;
93
94                         g_hash_table_iter_init(&filter_kw_iter, filter_kw_table);
95                         while (g_hash_table_iter_next(&filter_kw_iter, (void**)&filter_kw, NULL))
96                                 {
97                                 if (g_list_find_custom(img_keywords, filter_kw, (GCompareFunc)g_strcmp0))
98                                         {
99                                         ++match_count;
100                                         }
101                                 else
102                                         {
103                                         ++miss_count;
104                                         }
105                                 if (miss_count > 0) break;
106                                 }
107
108                         string_list_free(img_keywords);
109                         if (miss_count > 0 || match_count == 0) continue;
110                         }
111
112                 if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_DAY))
113                         {
114                         // FD starts a new day group.
115                         GList *needle;
116                         gchar *buf;
117
118                         if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_MONTH))
119                                 {
120                                 // FD starts a new month group.
121                                 pi_day = NULL;
122
123                                 if (pi_month)
124                                         {
125                                         x = pi_month->x + pi_month->width + PAN_BOX_BORDER;
126                                         }
127                                 else
128                                         {
129                                         x = PAN_BOX_BORDER;
130                                         }
131
132                                 y = PAN_BOX_BORDER;
133
134                                 buf = pan_date_value_string(fd->date, PAN_DATE_LENGTH_MONTH);
135                                 pi = pan_item_text_new(pw, x, y, buf,
136                                                        PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
137                                                        PAN_TEXT_BORDER_SIZE,
138                                                        PAN_TEXT_COLOR, 255);
139                                 g_free(buf);
140                                 y += pi->height;
141
142                                 pi_month = pan_item_box_new(pw, file_data_ref(fd),
143                                                             x, y, 0, 0,
144                                                             PAN_BOX_OUTLINE_THICKNESS,
145                                                             PAN_BOX_COLOR, PAN_BOX_ALPHA,
146                                                             PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
147
148                                 x += PAN_BOX_BORDER;
149                                 y += PAN_BOX_BORDER;
150                                 month_start = y;
151                                 }
152
153                         if (pi_day) x = pi_day->x + pi_day->width + PAN_BOX_BORDER;
154
155                         group_start_date = fd->date;
156                         total = 1;
157                         count = 0;
158
159                         needle = work;
160                         while (needle)
161                                 {
162                                 FileData *nfd;
163
164                                 nfd = needle->data;
165                                 if (pan_date_compare(nfd->date, group_start_date, PAN_DATE_LENGTH_DAY))
166                                         {
167                                         needle = needle->next;
168                                         total++;
169                                         }
170                                 else
171                                         {
172                                         needle = NULL;
173                                         }
174                                 }
175
176                         buf = pan_date_value_string(fd->date, PAN_DATE_LENGTH_WEEK);
177                         pi = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_NONE,
178                                                PAN_TEXT_BORDER_SIZE,
179                                                PAN_TEXT_COLOR, 255);
180                         g_free(buf);
181
182                         y += pi->height;
183
184                         pi_day = pan_item_box_new(pw, file_data_ref(fd), x, y, 0, 0,
185                                                   PAN_BOX_OUTLINE_THICKNESS,
186                                                   PAN_BOX_COLOR, PAN_BOX_ALPHA,
187                                                   PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
188
189                         x += PAN_BOX_BORDER;
190                         y += PAN_BOX_BORDER;
191                         day_start = y;
192                         }
193
194                 if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
195                         {
196                         pi = pan_item_image_new(pw, fd, x, y, 10, 10);
197                         if (pi->width > x_width) x_width = pi->width;
198                         y_height = pi->height;
199                         }
200                 else
201                         {
202                         pi = pan_item_thumb_new(pw, fd, x, y);
203                         x_width = PAN_THUMB_SIZE;
204                         y_height = PAN_THUMB_SIZE;
205                         }
206
207                 pan_item_size_by_item(pi_day, pi, PAN_BOX_BORDER);
208                 pan_item_size_by_item(pi_month, pi_day, PAN_BOX_BORDER);
209
210                 total--;
211                 count++;
212
213                 if (total > 0 && count < PAN_GROUP_MAX)
214                         {
215                         y += y_height + PAN_THUMB_GAP;
216                         }
217                 else
218                         {
219                         x += x_width + PAN_THUMB_GAP;
220                         x_width = 0;
221                         count = 0;
222
223                         if (total > 0)
224                                 y = day_start;
225                         else
226                                 y = month_start;
227                         }
228
229                 pan_item_size_coordinates(pi_month, PAN_BOX_BORDER, width, height);
230                 }
231
232         g_list_free(list);
233 }
234 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */