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