Add pan filtering to all of the pan view modes
[geeqie.git] / src / pan-view / pan-calendar.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-calendar.h"
23
24 #include <glib/gprintf.h>
25 #include <math.h>
26
27 #include "pan-util.h"
28 #include "pan-view.h"
29 #include "pan-view-filter.h"
30 #include "pixbuf_util.h"
31
32 #define PAN_CAL_POPUP_COLOR 220, 220, 220
33 #define PAN_CAL_POPUP_ALPHA 255
34 #define PAN_CAL_POPUP_BORDER 1
35 #define PAN_CAL_POPUP_BORDER_COLOR 0, 0, 0
36 #define PAN_CAL_POPUP_TEXT_COLOR 0, 0, 0
37
38 #define PAN_CAL_DAY_WIDTH 100
39 #define PAN_CAL_DAY_HEIGHT 80
40
41 #define PAN_CAL_DAY_COLOR 255, 255, 255
42 #define PAN_CAL_DAY_ALPHA 220
43 #define PAN_CAL_DAY_BORDER 2
44 #define PAN_CAL_DAY_BORDER_COLOR 0, 0, 0
45 #define PAN_CAL_DAY_TEXT_COLOR 0, 0, 0
46
47 #define PAN_CAL_MONTH_COLOR 255, 255, 255
48 #define PAN_CAL_MONTH_ALPHA 200
49 #define PAN_CAL_MONTH_BORDER 4
50 #define PAN_CAL_MONTH_BORDER_COLOR 0, 0, 0
51 #define PAN_CAL_MONTH_TEXT_COLOR 0, 0, 0
52
53 #define PAN_CAL_DOT_SIZE 3
54 #define PAN_CAL_DOT_GAP 2
55 #define PAN_CAL_DOT_COLOR 128, 128, 128
56 #define PAN_CAL_DOT_ALPHA 128
57
58
59 /*
60  *-----------------------------------------------------------------------------
61  * calendar
62  *-----------------------------------------------------------------------------
63  */
64
65 void pan_calendar_update(PanWindow *pw, PanItem *pi_day)
66 {
67         PanItem *pbox;
68         PanItem *pi;
69         GList *list;
70         GList *work;
71         gint x1, y1, x2, y2, x3, y3;
72         gint x, y, w, h;
73         gint grid;
74         gint column;
75
76         while ((pi = pan_item_find_by_key(pw, PAN_ITEM_NONE, "day_bubble"))) pan_item_remove(pw, pi);
77
78         if (!pi_day || pi_day->type != PAN_ITEM_BOX ||
79             !pi_day->key || strcmp(pi_day->key, "day") != 0) return;
80
81         list = pan_layout_intersect(pw, pi_day->x, pi_day->y, pi_day->width, pi_day->height);
82
83         work = list;
84         while (work)
85                 {
86                 PanItem *dot;
87                 GList *node;
88
89                 dot = work->data;
90                 node = work;
91                 work = work->next;
92
93                 if (dot->type != PAN_ITEM_BOX || !dot->fd ||
94                     !dot->key || strcmp(dot->key, "dot") != 0)
95                         {
96                         list = g_list_delete_link(list, node);
97                         }
98                 }
99
100         grid = (gint)(sqrt(g_list_length(list)) + 0.5);
101
102         x = pi_day->x + pi_day->width + 4;
103         y = pi_day->y;
104
105         pbox = pan_item_box_new(pw, NULL, x, y, PAN_BOX_BORDER, PAN_BOX_BORDER,
106                                 PAN_CAL_POPUP_BORDER,
107                                 PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA,
108                                 PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
109         pan_item_set_key(pbox, "day_bubble");
110
111         if (pi_day->fd)
112                 {
113                 PanItem *plabel;
114                 gchar *buf;
115
116                 buf = pan_date_value_string(pi_day->fd->date, PAN_DATE_LENGTH_WEEK);
117                 plabel = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
118                                            PAN_TEXT_BORDER_SIZE,
119                                            PAN_CAL_POPUP_TEXT_COLOR, 255);
120                 pan_item_set_key(plabel, "day_bubble");
121                 g_free(buf);
122
123                 pan_item_size_by_item(pbox, plabel, 0);
124
125                 y += plabel->height;
126                 }
127
128         if (list)
129                 {
130                 column = 0;
131
132                 x += PAN_BOX_BORDER;
133                 y += PAN_BOX_BORDER;
134
135                 work = list;
136                 while (work)
137                         {
138                         PanItem *dot;
139
140                         dot = work->data;
141                         work = work->next;
142
143                         if (dot->fd)
144                                 {
145                                 PanItem *pimg;
146
147                                 pimg = pan_item_thumb_new(pw, file_data_ref(dot->fd), x, y);
148                                 pan_item_set_key(pimg, "day_bubble");
149
150                                 pan_item_size_by_item(pbox, pimg, PAN_BOX_BORDER);
151
152                                 column++;
153                                 if (column < grid)
154                                         {
155                                         x += PAN_THUMB_SIZE + PAN_THUMB_GAP;
156                                         }
157                                 else
158                                         {
159                                         column = 0;
160                                         x = pbox->x + PAN_BOX_BORDER;
161                                         y += PAN_THUMB_SIZE + PAN_THUMB_GAP;
162                                         }
163                                 }
164                         }
165                 }
166
167         x1 = pi_day->x + pi_day->width - 8;
168         y1 = pi_day->y + 8;
169         x2 = pbox->x + 1;
170         y2 = pbox->y + MIN(42, pbox->height);
171         x3 = pbox->x + 1;
172         y3 = MAX(pbox->y, y2 - 30);
173         util_clip_triangle(x1, y1, x2, y2, x3, y3,
174                            &x, &y, &w, &h);
175
176         pi = pan_item_tri_new(pw, NULL, x, y, w, h,
177                               x1, y1, x2, y2, x3, y3,
178                               PAN_CAL_POPUP_COLOR, PAN_CAL_POPUP_ALPHA);
179         pan_item_tri_border(pi, PAN_BORDER_1 | PAN_BORDER_3, PAN_CAL_POPUP_BORDER_COLOR, PAN_CAL_POPUP_ALPHA);
180         pan_item_set_key(pi, "day_bubble");
181         pan_item_added(pw, pi);
182
183         pan_item_box_shadow(pbox, PAN_SHADOW_OFFSET * 2, PAN_SHADOW_FADE * 2);
184         pan_item_added(pw, pbox);
185
186         pan_layout_resize(pw);
187 }
188
189 void pan_calendar_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
190 {
191         GList *list;
192         GList *work;
193         gint x, y;
194         time_t tc;
195         gint count;
196         gint day_max;
197         gint grid;
198         gint year = 0;
199         gint month = 0;
200         gint end_year = 0;
201         gint end_month = 0;
202
203         list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
204         pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
205
206         if (pw->cache_list && pw->exif_date_enable)
207                 {
208                 pw->cache_list = pan_cache_sort(pw->cache_list, SORT_NAME, TRUE);
209                 list = filelist_sort(list, SORT_NAME, TRUE);
210                 pan_cache_sync_date(pw, list);
211                 }
212
213         pw->cache_list = pan_cache_sort(pw->cache_list, SORT_TIME, TRUE);
214         list = filelist_sort(list, SORT_TIME, TRUE);
215
216         day_max = 0;
217         count = 0;
218         tc = 0;
219         work = list;
220         while (work)
221                 {
222                 FileData *fd;
223
224                 fd = work->data;
225                 work = work->next;
226
227                 if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
228                         {
229                         count = 0;
230                         tc = fd->date;
231                         }
232                 else
233                         {
234                         count++;
235                         if (day_max < count) day_max = count;
236                         }
237                 }
238
239         DEBUG_1("biggest day contains %d images", day_max);
240
241         grid = (gint)(sqrt((gdouble)day_max) + 0.5) * (PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2 + PAN_THUMB_GAP);
242
243         if (list)
244                 {
245                 FileData *fd = list->data;
246
247                 year = pan_date_value(fd->date, PAN_DATE_LENGTH_YEAR);
248                 month = pan_date_value(fd->date, PAN_DATE_LENGTH_MONTH);
249                 }
250
251         work = g_list_last(list);
252         if (work)
253                 {
254                 FileData *fd = work->data;
255                 end_year = pan_date_value(fd->date, PAN_DATE_LENGTH_YEAR);
256                 end_month = pan_date_value(fd->date, PAN_DATE_LENGTH_MONTH);
257                 }
258
259         *width = PAN_BOX_BORDER * 2;
260         *height = PAN_BOX_BORDER * 2;
261
262         x = PAN_BOX_BORDER;
263         y = PAN_BOX_BORDER;
264
265         work = list;
266         while (work && (year < end_year || (year == end_year && month <= end_month)))
267                 {
268                 PanItem *pi_month;
269                 PanItem *pi_text;
270                 gint day;
271                 gint days;
272                 gint col;
273                 gint row;
274                 time_t dt;
275                 gchar *buf;
276
277                 /* figure last second of this month */
278                 dt = pan_date_to_time((month == 12) ? year + 1 : year, (month == 12) ? 1 : month + 1, 1);
279                 dt -= 60 * 60 * 24;
280
281                 /* anything to show this month? */
282                 if (!pan_date_compare(((FileData *)(work->data))->date, dt, PAN_DATE_LENGTH_MONTH))
283                         {
284                         month ++;
285                         if (month > 12)
286                                 {
287                                 year++;
288                                 month = 1;
289                                 }
290                         continue;
291                         }
292
293                 days = pan_date_value(dt, PAN_DATE_LENGTH_DAY);
294                 dt = pan_date_to_time(year, month, 1);
295                 col = pan_date_value(dt, PAN_DATE_LENGTH_WEEK);
296                 row = 1;
297
298                 x = PAN_BOX_BORDER;
299
300                 pi_month = pan_item_box_new(pw, NULL, x, y, PAN_CAL_DAY_WIDTH * 7, PAN_CAL_DAY_HEIGHT / 4,
301                                             PAN_CAL_MONTH_BORDER,
302                                             PAN_CAL_MONTH_COLOR, PAN_CAL_MONTH_ALPHA,
303                                             PAN_CAL_MONTH_BORDER_COLOR, PAN_CAL_MONTH_ALPHA);
304                 buf = pan_date_value_string(dt, PAN_DATE_LENGTH_MONTH);
305                 pi_text = pan_item_text_new(pw, x, y, buf,
306                                             PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
307                                             PAN_TEXT_BORDER_SIZE,
308                                             PAN_CAL_MONTH_TEXT_COLOR, 255);
309                 g_free(buf);
310                 pi_text->x = pi_month->x + (pi_month->width - pi_text->width) / 2;
311
312                 pi_month->height = pi_text->y + pi_text->height - pi_month->y;
313
314                 x = PAN_BOX_BORDER + col * PAN_CAL_DAY_WIDTH;
315                 y = pi_month->y + pi_month->height + PAN_BOX_BORDER;
316
317                 for (day = 1; day <= days; day++)
318                         {
319                         FileData *fd;
320                         PanItem *pi_day;
321                         gint dx, dy;
322                         gint n = 0;
323                         gchar fake_path[20];
324
325                         dt = pan_date_to_time(year, month, day);
326
327                         /*
328                          * Create a FileData entry that represents the given day.
329                          * It does not correspond to any real file
330                          */
331
332                         g_snprintf(fake_path, sizeof(fake_path), "//%04d-%02d-%02d", year, month, day);
333                         fd = file_data_new_no_grouping(fake_path);
334                         fd->date = dt;
335                         pi_day = pan_item_box_new(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT,
336                                                   PAN_CAL_DAY_BORDER,
337                                                   PAN_CAL_DAY_COLOR, PAN_CAL_DAY_ALPHA,
338                                                   PAN_CAL_DAY_BORDER_COLOR, PAN_CAL_DAY_ALPHA);
339                         pan_item_set_key(pi_day, "day");
340
341                         dx = x + PAN_CAL_DOT_GAP * 2;
342                         dy = y + PAN_CAL_DOT_GAP * 2;
343
344                         fd = (work) ? work->data : NULL;
345                         while (fd && pan_date_compare(fd->date, dt, PAN_DATE_LENGTH_DAY))
346                                 {
347                                 PanItem *pi;
348
349                                 pi = pan_item_box_new(pw, fd, dx, dy, PAN_CAL_DOT_SIZE, PAN_CAL_DOT_SIZE,
350                                                       0,
351                                                       PAN_CAL_DOT_COLOR, PAN_CAL_DOT_ALPHA,
352                                                       0, 0, 0, 0);
353                                 pan_item_set_key(pi, "dot");
354
355                                 dx += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
356                                 if (dx + PAN_CAL_DOT_SIZE > pi_day->x + pi_day->width - PAN_CAL_DOT_GAP * 2)
357                                         {
358                                         dx = x + PAN_CAL_DOT_GAP * 2;
359                                         dy += PAN_CAL_DOT_SIZE + PAN_CAL_DOT_GAP;
360                                         }
361                                 if (dy + PAN_CAL_DOT_SIZE > pi_day->y + pi_day->height - PAN_CAL_DOT_GAP * 2)
362                                         {
363                                         /* must keep all dots within respective day even if it gets ugly */
364                                         dy = y + PAN_CAL_DOT_GAP * 2;
365                                         }
366
367                                 n++;
368
369                                 work = work->next;
370                                 fd = (work) ? work->data : NULL;
371                                 }
372
373                         if (n > 0)
374                                 {
375                                 PanItem *pi;
376
377                                 pi_day->color_r = MAX(pi_day->color_r - 61 - n * 3, 80);
378                                 pi_day->color_g = pi_day->color_r;
379
380                                 buf = g_strdup_printf("( %d )", n);
381                                 pi = pan_item_text_new(pw, x, y, buf, PAN_TEXT_ATTR_NONE,
382                                                        PAN_TEXT_BORDER_SIZE,
383                                                        PAN_CAL_DAY_TEXT_COLOR, 255);
384                                 g_free(buf);
385
386                                 pi->x = pi_day->x + (pi_day->width - pi->width) / 2;
387                                 pi->y = pi_day->y + (pi_day->height - pi->height) / 2;
388                                 }
389
390                         buf = g_strdup_printf("%d", day);
391                         pan_item_text_new(pw, x + 4, y + 4, buf, PAN_TEXT_ATTR_BOLD | PAN_TEXT_ATTR_HEADING,
392                                           PAN_TEXT_BORDER_SIZE,
393                                           PAN_CAL_DAY_TEXT_COLOR, 255);
394                         g_free(buf);
395
396
397                         pan_item_size_coordinates(pi_day, PAN_BOX_BORDER, width, height);
398
399                         col++;
400                         if (col > 6)
401                                 {
402                                 col = 0;
403                                 row++;
404                                 x = PAN_BOX_BORDER;
405                                 y += PAN_CAL_DAY_HEIGHT;
406                                 }
407                         else
408                                 {
409                                 x += PAN_CAL_DAY_WIDTH;
410                                 }
411                         }
412
413                 if (col > 0) y += PAN_CAL_DAY_HEIGHT;
414                 y += PAN_BOX_BORDER * 2;
415
416                 month ++;
417                 if (month > 12)
418                         {
419                         year++;
420                         month = 1;
421                         }
422                 }
423
424         *width += grid;
425         *height = MAX(*height, grid + PAN_BOX_BORDER * 2 * 2);
426
427         g_list_free(list);
428 }
429 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */