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