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