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