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