389d424230e2ee307dc5edc5b7a9fda72343ba09
[apps/core/preloaded/calendar.git] / src / month-data.c
1 /*
2   *
3   *  Copyright 2012  Samsung Electronics Co., Ltd
4   *
5   *  Licensed under the Flora License, Version 1.0 (the "License");
6   *  you may not use this file except in compliance with the License.
7   *  You may obtain a copy of the License at
8   *
9   *       http://floralicense.org/license/
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17
18 #include "month-data.h"
19 #include "query.h"
20 #include "list-data.h"
21
22 #define CAL_MONTH_CALENDAR_LAST_LINE_POS_INDEX 41
23
24 typedef struct {
25         struct appdata *ad;
26         int rows_per_cell;
27         cal_month_calendar_event* event_table[WEEKS_OF_MONTH][DAYS_OF_WEEK][CAL_MONTH_MAX_ROWS_PER_CELL];
28         int count_table[WEEKS_OF_MONTH][DAYS_OF_WEEK];
29         struct tm grid_start_date;
30         struct tm grid_end_date;
31 } cal_month_data_s;
32
33 cal_month_data_h cal_month_data_create(struct appdata *ad, int rows_per_cell)
34 {
35         c_retv_if(ad == NULL, NULL);
36
37         cal_month_data_s* p = calloc(1, sizeof(cal_month_data_s));
38         p->ad = ad;
39         p->rows_per_cell = rows_per_cell;
40         return p;
41 }
42
43 int cal_month_data_get_day_time_by_pos(struct appdata *ad,
44                         int pos_today, int pos_selected, int pos_start, int pos_end,
45                         int *first_day, int *last_day, struct tm *start_time, struct tm *end_time, int day_pos)
46 {
47         c_retv_if(!ad, -1);
48
49         int day = 0;
50         int today = 0;
51         int start_day = 0;
52         int r = 0;
53         int day_tmp = 0;
54
55         if (-1 != day_pos) {
56                 day = CAL_MONTH_CALENDAR_LAST_LINE_POS_INDEX <= day_pos ? pos_end : day_pos;
57         } else if (-1 == pos_selected) {
58                 day = pos_today;
59         } else {
60                 day = pos_selected;
61         }
62
63         day_tmp = day + 1;
64         today = day - pos_start + 1;
65         start_day = day - (day_tmp % DAYS_OF_WEEK) + 1;
66
67         if (day < DAYS_OF_WEEK) {
68                 *first_day = 1;
69                 *last_day = DAYS_OF_WEEK - pos_start;
70         } else if (0 == (day_tmp % DAYS_OF_WEEK)) {
71                 *first_day = today - DAYS_OF_WEEK + 1;
72                 *last_day = today;
73         } else {
74                 *first_day = today - (day % DAYS_OF_WEEK);
75                 if (pos_end < (start_day + DAYS_OF_WEEK - 1))
76                         *last_day = pos_end - pos_start + 1;
77                 else
78                         *last_day = *first_day + DAYS_OF_WEEK - 1;
79         }
80
81         *start_time = ad->base_tm;
82         start_time->tm_mday = *first_day;
83         start_time->tm_hour = 0;
84         start_time->tm_min = 0;
85         start_time->tm_sec = 0;
86
87         *end_time = ad->base_tm;
88         end_time->tm_mday = *last_day;
89         end_time->tm_hour = 23;
90         end_time->tm_min = 59;
91         end_time->tm_sec = 59;
92
93         if (0 < pos_start && day < DAYS_OF_WEEK)
94                 cal_util_update_tm_day(start_time, -pos_start);
95         else if ((0 != (day_tmp % DAYS_OF_WEEK)) && (pos_end < (start_day + DAYS_OF_WEEK - 1)))
96                 cal_util_update_tm_day(end_time, start_day + DAYS_OF_WEEK - 1 - pos_end);
97
98         if (pos_end < day_pos && pos_end <= CAL_MONTH_CALENDAR_LAST_LINE_POS_INDEX  && CAL_MONTH_CALENDAR_LAST_LINE_POS_INDEX  < day_pos) {
99                 r = cal_util_update_tm_day(start_time, DAYS_OF_WEEK);
100                 c_retvm_if(-1 == r, -1, "start_time is out of range.");
101
102                 r = cal_util_update_tm_day(end_time, DAYS_OF_WEEK);
103                 c_retvm_if(-1 == r, -1, "end_time is out of range.");
104         }
105
106         return 0;
107 }
108
109 static void __cal_month_data_get_grid_range(struct appdata *ad, struct tm* grid_start_time, struct tm* grid_end_time)
110 {
111         struct tm month_start_time;
112         struct tm month_end_time;
113         cal_util_get_month_start_time(&ad->base_tm, &month_start_time);
114         cal_util_get_month_end_time(&ad->base_tm, &month_end_time);
115
116         cal_util_get_week_start_time(&month_start_time, grid_start_time, ad->wday_start);
117         cal_util_get_week_end_time(&month_end_time, grid_end_time, ad->wday_start);
118 }
119
120 static cal_list_data_h __cal_month_data_retreive_list(const struct tm* start_time, const struct tm* end_time)
121 {
122         calendar_query_h query_allday = cal_query_create_list_range_query(
123                         _calendar_instance_allday_calendar_book._uri,
124                         _calendar_instance_allday_calendar_book.start_time,
125                         _calendar_instance_allday_calendar_book.end_time,
126                         start_time, end_time, true);
127
128         calendar_query_h query_normal = cal_query_create_list_range_query(
129                         _calendar_instance_normal_calendar_book._uri,
130                         _calendar_instance_normal_calendar_book.start_time,
131                         _calendar_instance_normal_calendar_book.end_time,
132                         start_time, end_time, false);
133
134         calendar_query_h query_task = cal_query_create_list_range_query(
135                         _calendar_todo_calendar_book._uri,
136                         _calendar_todo_calendar_book.due_time,
137                         _calendar_todo_calendar_book.due_time,
138                         start_time, end_time, false);
139
140         cal_list_data_h list_data = cal_list_data_create(query_allday, query_normal, query_task,
141                                 _calendar_instance_allday_calendar_book.start_time,
142                                 _calendar_instance_normal_calendar_book.start_time,
143                                 _calendar_todo_calendar_book.due_time);
144
145         calendar_query_destroy(query_allday);
146         calendar_query_destroy(query_normal);
147         calendar_query_destroy(query_task);
148
149         return list_data;
150 }
151
152 static void __cal_month_get_mweek_and_wday(cal_month_data_s* p, const struct tm* date, int* mweek, int* wday)
153 {
154         int offset = cal_util_get_day_diff(date, &p->grid_start_date);
155 //      DBG("grid_start(%s), day(%s), offset(%d)", cal_util_print_day(&p->grid_start_date), cal_util_print_day(date), offset);
156         c_warn_if(offset < 0, "@@@@@@@@@@@@@@ Something's wrong here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
157
158         if (offset < 0)
159                 offset = 0;
160
161         *mweek = offset / 7;
162         *wday = offset % 7;
163 }
164
165 #define INVALID_ROW -1
166
167 static cal_month_calendar_event* _cal_month_data_create_node(
168                                                                                 calendar_record_h record, int wday, int* remaining_width,
169                                                                                 int prev_node_row)
170 {
171         int target_width = 7 - wday;
172         if (target_width > *remaining_width)
173                 target_width = *remaining_width;
174         *remaining_width -= target_width;
175
176         cal_month_calendar_event* event = calloc(1, sizeof(cal_month_calendar_event));
177         event->calendar_id = _calendar_get_calendar_index(record);
178         event->width = target_width;
179         event->title = strdup(_calendar_get_summary(record));
180         event->prev_row = prev_node_row;
181         event->next_row = INVALID_ROW;
182         return event;
183 }
184
185 static cal_month_calendar_event* __cal_month_data_create_plus_node(int count)
186 {
187         cal_month_calendar_event* event = calloc(1, sizeof(cal_month_calendar_event));
188         event->calendar_id = 1;
189         event->width = 1;
190         char buffer[50];
191         //TODO:i18n
192         sprintf(buffer, "+%d more", count);
193         event->title = strdup(buffer);
194         event->prev_row = INVALID_ROW;
195         event->next_row = INVALID_ROW;
196         return event;
197 }
198
199 static const cal_month_calendar_event __dummy;
200
201 static int __cal_month_data_find_first_empty_row(cal_month_data_s* p, int mweek, int wday)
202 {
203         int row;
204         for (row = 0; row < p->rows_per_cell; row++) {
205                 if (p->event_table[mweek][wday][row] == NULL)
206                         break;
207         }
208
209         if (row == p->rows_per_cell)    // No empty row!
210                 return -1;
211
212         return row;
213 }
214
215 static void __cal_month_data_occupy(cal_month_data_s* p, calendar_record_h record)
216 {
217         struct tm date = {0};
218         struct tm end_date = {0};
219
220         cal_svc_get_start_date(record, &date);
221         cal_svc_get_end_date(record, &end_date);
222
223         if (cal_util_get_day_diff(&date, &p->grid_start_date) < 0)
224                 date = p->grid_start_date;
225
226         if (cal_util_get_day_diff(&p->grid_end_date, &end_date) < 0)
227                 end_date = p->grid_end_date;
228
229         int remaining_width = cal_util_get_day_diff(&end_date, &date) + 1;
230
231         cal_month_calendar_event* prev_node = NULL;
232         int prev_node_row = INVALID_ROW;
233
234         int mweek, wday = 0;
235         __cal_month_get_mweek_and_wday(p, &date, &mweek, &wday);
236         p->count_table[mweek][wday]++;
237
238         int row = __cal_month_data_find_first_empty_row(p, mweek, wday);
239         if (row >= 0) {
240                 prev_node = p->event_table[mweek][wday][row] =
241                         _cal_month_data_create_node(record, wday, &remaining_width, INVALID_ROW);
242                 prev_node_row = row;
243         }
244         cal_util_update_tm_day(&date, 1);
245
246         // Iterate through rest of the span of the event
247         while (cal_util_compare_day(&date, &end_date) >= 0) {
248
249                 __cal_month_get_mweek_and_wday(p, &date, &mweek, &wday);
250                 p->count_table[mweek][wday]++;
251
252                 if (wday == 0) {
253                         if (prev_node != NULL) {
254                                 row = __cal_month_data_find_first_empty_row(p, mweek, wday);
255                                 if (row < 0) {
256                                         cal_util_update_tm_day(&date, 1);
257                                         continue;
258                                 }
259
260                                 prev_node->next_row = row;
261                                 prev_node = p->event_table[mweek][wday][row] =
262                                         _cal_month_data_create_node(record, wday, &remaining_width, prev_node_row);
263                                 prev_node_row = row;
264                         }
265                 } else {
266                         if (row >= 0)
267                                 p->event_table[mweek][wday][row] = &__dummy;
268                 }
269
270                 cal_util_update_tm_day(&date, 1);
271         }
272 }
273
274 static const char* tab(int n)
275 {
276         static const char tabs[] = "                                                  ";
277         return tabs + strlen(tabs) - n;
278 }
279
280 static void __fix_width(char* buffer, int len)
281 {
282         strcat(buffer, tab(15 - len));
283 }
284
285 static void __cal_month_data_dump(cal_month_data_s* p)
286 {
287         int mweek;
288         for (mweek = 0; mweek < WEEKS_OF_MONTH; mweek++) {
289                 static char buffer[1000];
290
291                 buffer[0] = 0;
292                 int wday;
293                 for (wday = 0; wday < DAYS_OF_WEEK; wday++) {
294                         char buffer2[20];
295                         sprintf(buffer2, "%d", p->count_table[mweek][wday]);
296                         strcat(buffer, buffer2);
297                         __fix_width(buffer, strlen(buffer2));
298                 }
299                 DBG("%s", buffer);
300
301                 int row;
302                 for (row = 0; row < p->rows_per_cell; row++) {
303                         buffer[0] = 0;
304                         for (wday = 0; wday < DAYS_OF_WEEK; wday++) {
305                                 cal_month_calendar_event* event =
306                                                 p->event_table[mweek][wday][row];
307 //                                              p->event_table[mweek][wday][p->rows_per_cell - row - 1];
308                                 if (event == NULL) {
309                                         strcat(buffer, "");
310                                         __fix_width(buffer, 0);
311                                 } else if (event == &__dummy) {
312                                         strcat(buffer, "(dummy)");
313                                         __fix_width(buffer, 7);
314                                 } else {
315                                         strncat(buffer, event->title, 14);
316                                         int len = strlen(event->title);
317                                         if (len > 14)
318                                                 len = 14;
319                                         __fix_width(buffer, len);
320                                 }
321                         }
322                         DBG("%s", buffer);
323                 }
324                 DBG("---------------------------------------------------------------------------------------------------");
325         }
326 }
327
328 static void __cal_month_data_unoccupy(cal_month_data_s* p, int mweek, int wday, int row, int dir)
329 {
330         if (p->event_table[mweek][wday][row] == NULL)   // This row's already been cleared
331                 return;
332
333         int wd;
334
335         DBG("===================================================================================");
336
337         DBG("%d %d %d", mweek, wday, row);
338
339         // Unoccupy tailing blocks.
340         for (wd = wday + 1; wd < DAYS_OF_WEEK; wd++) {
341                 if (p->event_table[mweek][wd][row] != &__dummy) // Head of another event or NULL
342                         break;
343                 p->event_table[mweek][wd][row] = NULL;
344                 DBG("Unoccupying dummy at %d %d %d", mweek, wd, row);
345         }
346
347         // Find head of the event block in last row.
348         for (wd = wday; wd >= 0; wd--) {
349                 if (p->event_table[mweek][wd][row] != &__dummy) // Head of this event
350                         break;
351                 p->event_table[mweek][wd][row] = NULL;  // Unoccupy blocks along the way
352                 DBG("Unoccupying dummy at %d %d %d", mweek, wd, row);
353         }
354
355         cal_month_calendar_event* head = p->event_table[mweek][wd][row];
356         int prev_row = head->prev_row;
357         int next_row = head->next_row;
358
359         // Unoccupy the head itself.
360         free(head);
361         p->event_table[mweek][wd][row] = NULL;
362         DBG("Unoccupying head at %d %d %d", mweek, wd, row);
363
364         __cal_month_data_dump(p);
365
366         // Recursively propagate to previous chained block.
367         if (dir <= 0 && prev_row != INVALID_ROW)
368                 __cal_month_data_unoccupy(p, mweek - 1, DAYS_OF_WEEK - 1, prev_row, -1);
369
370         // Recursively propagate to next chained block.
371         if (dir >= 0 && next_row != INVALID_ROW)
372                 __cal_month_data_unoccupy(p, mweek + 1, 0, next_row, 1);
373
374 }
375
376 int cal_month_data_load(cal_month_data_h month_data)
377 {
378         c_retv_if(!month_data, -1);
379         cal_month_data_s* p = (cal_month_data_s*)month_data;
380
381         __cal_month_data_get_grid_range(p->ad, &p->grid_start_date, &p->grid_end_date);
382
383         cal_list_data_h* cursor = __cal_month_data_retreive_list(&p->grid_start_date, &p->grid_end_date);
384
385         // 1. Make a pass to fill up the grid with blocks greedily.
386         bool day_has_changed;
387         calendar_record_h record = cal_list_data_get_next(cursor, &day_has_changed);
388         while (record) {
389                 __cal_month_data_occupy(p, record);
390                 record = cal_list_data_get_next(cursor, &day_has_changed);
391         }
392
393         __cal_month_data_dump(p);
394
395         int mweek;
396
397         // 2. Make second pass to make room for the +n blocks where necessary.
398         for (mweek = 0; mweek < WEEKS_OF_MONTH; mweek++) {
399                 int wday;
400                 for (wday = 0; wday < DAYS_OF_WEEK; wday++) {
401                         if (p->count_table[mweek][wday] > p->rows_per_cell &&
402                                 p->event_table[mweek][wday][p->rows_per_cell - 1] != NULL) {
403                                 __cal_month_data_unoccupy(p, mweek, wday, p->rows_per_cell - 1, 0);
404                         }
405                 }
406         }
407
408         // 3. Make third pass to place +n blocks where necessary.
409         for (mweek = 0; mweek < WEEKS_OF_MONTH; mweek++) {
410                 int wday;
411                 for (wday = 0; wday < DAYS_OF_WEEK; wday++) {
412                         int row;
413                         for (row = 0; row < p->rows_per_cell; row++) {
414                                 if (p->event_table[mweek][wday][row] == NULL) {
415                                         int num_of_showing_blocks = row;
416                                         int total_count = p->count_table[mweek][wday];
417                                         if (num_of_showing_blocks < total_count) {
418                                                 p->event_table[mweek][wday][row] =
419                                                                 __cal_month_data_create_plus_node(total_count - num_of_showing_blocks);
420                                         }
421                                 }
422                         }
423                 }
424         }
425
426
427         __cal_month_data_dump(p);
428
429         cal_list_data_destroy(cursor, true);
430
431         return 0;
432 }
433
434 int cal_month_data_get_event_count(cal_month_data_h month_data, int week, int wday)
435 {
436         c_retv_if(!month_data, -1);
437         cal_month_data_s* p = (cal_month_data_s*)month_data;
438
439         return p->count_table[week][wday];
440 }
441
442 cal_month_calendar_event* cal_month_data_get_event(cal_month_data_h month_data, int mweek, int wday, int row)
443 {
444         c_retv_if(!month_data, false);
445         cal_month_data_s* p = (cal_month_data_s*)month_data;
446         cal_month_calendar_event* event = p->event_table[mweek][wday][row];
447         if (event == &__dummy)
448                 return NULL;
449         else
450                 return event;
451 }
452
453 void cal_month_data_clear(cal_month_data_h month_data)
454 {
455         c_ret_if(!month_data);
456         cal_month_data_s* p = (cal_month_data_s*)month_data;
457
458         int mweek;
459         for (mweek = 0; mweek < WEEKS_OF_MONTH; mweek++) {
460                 int wday;
461                 for (wday = 0; wday < DAYS_OF_WEEK; wday++) {
462                         p->count_table[mweek][wday] = 0;
463                         int row;
464                         for (row = 0; row < p->rows_per_cell; row++) {
465                                 cal_month_calendar_event* event = p->event_table[mweek][wday][row];
466                                 if (event != NULL && event != &__dummy) {
467                                         if (event->title != NULL)
468                                                 free(event->title);
469                                         free(event);
470                                 }
471                                 p->event_table[mweek][wday][row] = NULL;
472                         }
473                 }
474         }
475 }
476
477 void cal_month_data_destroy(cal_month_data_h month_data)
478 {
479         c_ret_if(!month_data);
480         cal_month_data_s* p = (cal_month_data_s*)month_data;
481
482         cal_month_data_clear(month_data);
483         free(p);
484 }