9fa6182bf639526b1e1ef64696ec408ae1b8f65c
[apps/core/preloaded/calendar.git] / src / view-main-list.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
19 #include <vconf.h>
20 #include <Elementary.h>
21 #include <time.h>
22
23 #include "detail-view.h"
24 #include "noti-handler.h"
25 #include <calendar2.h>
26 #include <calendar_view.h>
27 #include "view.h"
28 #include "list-base.h"
29 #include "list-broker.h"
30
31 extern Elm_Genlist_Item_Class cal_list_itc_h;
32 extern Elm_Genlist_Item_Class cal_list_today_group_itc_h;
33 extern Elm_Genlist_Item_Class itc_1icon;
34
35 typedef struct {
36         cal_list_base_h list;
37
38         Evas_Object* layout;
39         Evas_Object* no_contents;
40
41         struct appdata *ad;
42
43         Ecore_Idler* fetch_more_top_idler;
44         Ecore_Idler* fetch_more_bottom_idler;
45
46         Ecore_Event_Handler *event_handler;
47         Evas_Coord x;
48         Evas_Coord y;
49
50         Elm_Object_Item* navi_item;
51         Elm_Object_Item *bt_new;
52         Elm_Object_Item *bt_delete;
53
54         struct tm fetch_more_top_current_day;
55         Elm_Object_Item *fetching_more_top;
56         Elm_Object_Item *fetching_more_bottom;
57
58         cal_list_broker_h append_cursor;
59         cal_list_broker_h prepend_cursor;
60
61         Eina_Bool is_append_end;
62         int prepend_count;
63 } cal_list_data;
64
65 static void __cal_list_add_date_group_title(cal_list_data *p, const struct tm* date, bool prepend)
66 {
67         struct appdata *ad = p->ad;
68         c_ret_if(!ad);
69
70         if (date->tm_year == ad->today_tm.tm_year &&
71                 date->tm_mon == ad->today_tm.tm_mon &&
72                 date->tm_mday == ad->today_tm.tm_mday) {
73                 if (prepend)
74                         ad->today_item = cal_list_base_prepend_genlist_group_item(cal_list_base_get_genlist_p(p->list), &cal_list_today_group_itc_h, date);
75                 else
76                         ad->today_item = cal_list_base_append_genlist_group_item(cal_list_base_get_genlist_p(p->list), &cal_list_today_group_itc_h, date, CAL_TASK_NONE);
77         } else {
78                 if (prepend)
79                         cal_list_base_prepend_genlist_group_item(cal_list_base_get_genlist_p(p->list), &cal_list_itc_h, date);
80                 else
81                         cal_list_base_append_genlist_group_item(cal_list_base_get_genlist_p(p->list), &cal_list_itc_h, date, CAL_TASK_NONE);
82         }
83 }
84
85 static void __cal_list_prepend_more(void* data)
86 {
87         cal_list_data *p = data;
88
89         c_ret_if(p->fetch_more_top_idler);
90
91         cal_list_broker_h cursor = p->prepend_cursor;
92
93         bool day_has_changed;
94         calendar_record_h record = cal_list_broker_get_next(cursor, &day_has_changed);
95
96         int n = 0;
97         while (record) {
98                 if (p->prepend_count != 0 && n++ == p->prepend_count)
99                         break;
100
101                 if (day_has_changed) {
102                         if (p->fetch_more_top_current_day.tm_mday != 0)
103                                 __cal_list_add_date_group_title(p, &p->fetch_more_top_current_day, true);
104                         p->fetch_more_top_current_day = *cal_list_broker_get_current_day_p(cursor);
105                 }
106
107                 cal_list_base_add_item(p->list, record, true);
108
109                 record = cal_list_broker_get_next(cursor, &day_has_changed);
110         }
111
112         if (cal_list_broker_no_more(p->prepend_cursor)) {
113                 __cal_list_add_date_group_title(p, &p->fetch_more_top_current_day, true);
114         }
115 }
116
117 static void __cal_list_append_more(void* data)
118 {
119         cal_list_data *p = data;
120
121         c_ret_if(p->fetch_more_bottom_idler);
122
123         if(p->is_append_end)
124                 return;
125
126         cal_list_broker_h cursor = p->append_cursor;
127
128         bool day_has_changed;
129         calendar_record_h record = cal_list_broker_get_next(cursor, &day_has_changed);
130
131         while (record) {
132                 if (day_has_changed) {
133                         const struct tm* tm_current_day = cal_list_broker_get_current_day_p(cursor);
134                         if(tm_current_day->tm_year == 137)      {
135                                 p->is_append_end = EINA_TRUE;
136                                 return;
137                         }
138                         __cal_list_add_date_group_title(p, tm_current_day, false);
139                 }
140
141                 cal_list_base_add_item(p->list, record, false);
142
143                 record = cal_list_broker_get_next(cursor, &day_has_changed);
144         }
145 }
146
147 static Eina_Bool __cal_list_fetch_more_top(void* data)
148 {
149         cal_list_data *p = data;
150
151         cal_list_broker_fetch(p->prepend_cursor);
152
153         ecore_idler_del(p->fetch_more_top_idler);
154         p->fetch_more_top_idler = NULL;
155
156         if (p->fetching_more_top) {     // UI is waiting for fetch to finish so that items can be prepended.
157                 elm_object_item_del(p->fetching_more_top);
158                 p->fetching_more_top = NULL;
159                 __cal_list_prepend_more(p);
160                 cal_list_broker_fetch(p->prepend_cursor);       // Do next prefetch right away.
161         }
162
163         return ECORE_CALLBACK_CANCEL;
164 }
165
166 static Eina_Bool __cal_list_fetch_more_bottom(void* data)
167 {
168         cal_list_data *p = data;
169
170         cal_list_broker_fetch(p->append_cursor);
171
172         ecore_idler_del(p->fetch_more_bottom_idler);
173         p->fetch_more_bottom_idler = NULL;
174
175         if (p->fetching_more_bottom) {  // UI is waiting for fetch to finish so that items can be appended.
176                 elm_object_item_del(p->fetching_more_bottom);
177                 p->fetching_more_bottom = NULL;
178                 __cal_list_append_more(p);
179                 cal_list_broker_fetch(p->append_cursor);        // Do next prefetch right away.
180         }
181
182         return ECORE_CALLBACK_CANCEL;
183 }
184
185 void __cal_list_show_fetching_more_top(cal_list_data* p)
186 {
187         if (p->fetching_more_top)
188                 return;
189         Elm_Object_Item* it = elm_genlist_first_item_get(cal_list_base_get_genlist_p(p->list));
190         p->fetching_more_top = elm_genlist_item_prepend(cal_list_base_get_genlist_p(p->list), &itc_1icon, p, it, ELM_GENLIST_ITEM_NONE, NULL, NULL);
191 }
192
193 void __cal_list_show_fetching_more_bottom(cal_list_data* p)
194 {
195         if (p->fetching_more_bottom)
196                 return;
197         p->fetching_more_bottom = elm_genlist_item_prepend(cal_list_base_get_genlist_p(p->list), &itc_1icon, p, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
198 }
199
200 static void __cal_list_gl_mode_scroll_to_top(void *data, Evas_Object *obj, void *event_info)
201 {
202         c_ret_if(!obj || !data);
203         cal_list_data *p = data;
204
205         if(cal_list_broker_no_more(p->prepend_cursor))
206                 return;
207
208         if (p->fetch_more_top_idler) {  // fetching in progress
209                 __cal_list_show_fetching_more_top(p);
210                 return;
211         }
212
213         __cal_list_prepend_more(p);
214         p->fetch_more_top_idler = ecore_idler_add(__cal_list_fetch_more_top, p);        // Schedule next prefetch right away.
215 }
216
217 static void __cal_list_gl_mode_scroll_to_bottom(void *data, Evas_Object *obj, void *event_info)
218 {
219         c_ret_if(!obj || !data);
220         cal_list_data *p = data;
221
222         if(cal_list_broker_no_more(p->append_cursor))
223                 return;
224
225         if (p->fetch_more_bottom_idler) {       // fetching in progress
226                 __cal_list_show_fetching_more_bottom(p);
227                 return;
228         }
229
230         __cal_list_append_more(p);
231         p->fetch_more_bottom_idler = ecore_idler_add(__cal_list_fetch_more_bottom, p);  // Schedule next prefetch right away.
232 }
233
234 static void __cal_list_create_no_contents(cal_list_data *p)
235 {
236         CAL_FN_START;
237
238         c_ret_if(!p);
239         c_ret_if(!p->layout);
240
241         if (p->no_contents)
242                 return;
243
244         char* title = NULL;
245
246         title = C_("IDS_CLD_BODY_NO_EVENTS");
247
248         Evas_Object *no_contents = cal_util_add_search_nocontents(p->layout, title);
249         c_ret_if(!no_contents);
250
251         elm_object_part_content_set(p->layout, "sw", no_contents);
252
253         p->no_contents = no_contents;
254 }
255
256 static void __cal_list_delete_no_contents(cal_list_data *p)
257 {
258         CAL_FN_START;
259
260         c_ret_if(!p);
261
262         if (!p->no_contents)
263                 return;
264
265         elm_object_part_content_unset(p->layout, "sw");
266
267         evas_object_del(p->no_contents);
268
269         p->no_contents = NULL;
270 }
271
272 #define MIN_ITEM_COUNT_TO_FILL_PAGE 8
273
274 static void __cal_list_update_list(cal_list_data *p)
275 {
276         CAL_FN_START;
277
278         c_ret_if(!p);
279
280         struct appdata *ad = p->ad;
281         c_ret_if(!ad);
282         ad->today_item = NULL;
283
284         p->fetch_more_top_current_day.tm_mday = 0;
285
286         if (p->list != NULL) {
287                 cal_list_base_delete(p->list);
288                 p->list = NULL;
289         }
290
291         if (p->prepend_cursor != NULL)
292                 cal_list_broker_destroy(p->prepend_cursor);
293         if (p->append_cursor != NULL)
294                 cal_list_broker_destroy(p->append_cursor);
295
296         p->prepend_cursor = cal_list_broker_create(&ad->base_tm, -1, ad->is_display_complete_todo);
297         p->append_cursor = cal_list_broker_create(&ad->base_tm, 1, ad->is_display_complete_todo);
298
299         cal_list_broker_h cursor = p->append_cursor;
300
301         cal_list_broker_fetch(cursor);
302
303         bool day_has_changed;
304         calendar_record_h record = cal_list_broker_get_next(cursor, &day_has_changed);
305
306         if (record) {
307
308                 if (p->list == NULL) {
309                         __cal_list_delete_no_contents(p);
310                         p->list = cal_list_base_create(ad, p->layout, "sw", true, NULL, NULL);
311                 }
312
313 //              Eina_Bool today_has_instance = EINA_FALSE;
314
315                 while (record) {
316                         if (day_has_changed) {
317                                 const struct tm* tm_current_day = cal_list_broker_get_current_day_p(cursor);
318                                 __cal_list_add_date_group_title(p, tm_current_day, false);
319                         }
320
321                         cal_list_base_add_item(p->list, record, false);
322
323                         record = cal_list_broker_get_next(cursor, &day_has_changed);
324                 }
325         }
326
327         if (p->list == NULL || cal_list_base_get_item_count(p->list) < MIN_ITEM_COUNT_TO_FILL_PAGE) {
328
329                 cal_list_broker_fetch(p->prepend_cursor);
330
331                 if (!cal_list_broker_no_more(p->prepend_cursor)) {
332
333                         if (p->list == NULL) {
334                                 __cal_list_delete_no_contents(p);
335                                 p->list = cal_list_base_create(ad, p->layout, "sw", true, NULL, NULL);
336                         }
337
338                         p->prepend_count = MIN_ITEM_COUNT_TO_FILL_PAGE - cal_list_base_get_item_count(p->list);
339                         __cal_list_prepend_more(p);
340
341                         elm_genlist_item_bring_in(
342                                         elm_genlist_last_item_get(cal_list_base_get_genlist_p(p->list)),
343                                         ELM_GENLIST_ITEM_SCROLLTO_IN);
344                 }
345         }
346
347         p->prepend_count = 10;
348
349         if (p->list == NULL) {
350                 cal_list_base_delete(p->list);
351                 __cal_list_create_no_contents(p);
352         } else {
353                 evas_object_smart_callback_add(cal_list_base_get_genlist_p(p->list), "edge,bottom", __cal_list_gl_mode_scroll_to_bottom, p);
354                 evas_object_smart_callback_add(cal_list_base_get_genlist_p(p->list), "edge,top", __cal_list_gl_mode_scroll_to_top, p);
355         }
356
357 //      if (!today_has_instance) {
358 //              cal_list_genlist_item_data *item_data = calloc(1, sizeof(cal_list_genlist_item_data));
359 //              c_retm_if(!item_data, "calloc is failed");
360 //
361 //              item_data->tm = calloc(1, sizeof(struct tm));
362 //              c_retm_if(!item_data->tm, "malloc is failed");
363 //
364 //              *(item_data->tm) = ad->today_tm;
365 //              item_data->todo_group_type = CAL_TASK_NONE;
366 //
367 //              Elm_Object_Item *first_item = elm_genlist_first_item_get(cal_list_base_get_genlist_p(p->list));
368 //              item_data->it = elm_genlist_item_insert_before(cal_list_base_get_genlist_p(p->list), &cal_list_today_group_itc_h, item_data, NULL, first_item, ELM_GENLIST_ITEM_NONE, NULL, NULL);
369 //              if (item_data->it == NULL) {
370 //                      ERR("insert failed");
371 //                      free(item_data->tm);
372 //                      return;
373 //              }
374 //
375 //              elm_genlist_item_select_mode_set(item_data->it, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
376 //      }
377
378         p->fetch_more_top_idler = ecore_idler_add(__cal_list_fetch_more_top, p);
379         p->fetch_more_bottom_idler = ecore_idler_add(__cal_list_fetch_more_bottom, p);
380
381         CAL_FN_END;
382 }
383
384 static void __cal_list_day_event_changed_callback(const char* view_uri, void *user_data)
385 {
386         c_retm_if(!user_data, "user_data is null");
387
388         cal_list_data *p = user_data;
389
390         cal_list_base_delete_popup(p->list);
391
392         cal_list_update_view(p->layout);
393 }
394
395 static void __cal_list_delete_layout(void *data, Evas *e, Evas_Object *obj, void *ei)
396 {
397         CAL_FN_START;
398
399         cal_list_data *p = data;
400         c_retm_if(!p, "priv is null.");
401
402         if (p->fetch_more_bottom_idler) {
403                 ecore_idler_del(p->fetch_more_bottom_idler);
404                 p->fetch_more_bottom_idler = NULL;
405         }
406
407         if (p->fetch_more_top_idler) {
408                 ecore_idler_del(p->fetch_more_top_idler);
409                 p->fetch_more_top_idler = NULL;
410         }
411
412         cal_list_base_delete_popup_idler(p->list);
413
414         calendar_db_remove_changed_cb(_calendar_event._uri, __cal_list_day_event_changed_callback, p);
415
416         if (p->event_handler)
417                 ecore_event_handler_del(p->event_handler);
418
419         cal_list_base_delete_popup(p->list);
420
421         free(p);
422
423         CAL_FN_END;
424 }
425
426 static Eina_Bool __cal_list_mouse_button_down_callback(void *data, int type, void *event)
427 {
428         c_retvm_if(!event, EINA_FALSE, "event is null");
429         c_retvm_if(!data, EINA_FALSE, "data is null");
430
431         Ecore_Event_Mouse_Button *ev = event;
432         cal_list_data *p = data;
433
434         p->x = ev->x;
435         p->y = ev->y;
436
437         return ECORE_CALLBACK_RENEW;
438 }
439
440 void cal_list_scroll_today(struct appdata* ad)
441 {
442         CAL_FN_START;
443
444         c_retm_if(!ad, "ad is null.");
445
446         Elm_Object_Item* it = ad->today_item;
447         if (it) {
448                 elm_genlist_item_bring_in(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
449                 DBG("scroll to : %p", it);
450         }
451
452         CAL_FN_END;
453 }
454
455 static void __cal_list_update_delete_button(cal_list_data *p)
456 {
457         c_ret_if(!p);
458
459         Elm_Object_Item *delete_button = NULL;
460
461         delete_button = p->ad->bt_delete;
462
463         if (cal_list_base_get_genlist_p(p->list) && elm_genlist_first_item_get(cal_list_base_get_genlist_p(p->list)))
464                 elm_object_item_disabled_set(delete_button, EINA_FALSE);
465         else
466                 elm_object_item_disabled_set(delete_button, EINA_TRUE);
467 }
468
469 Evas_Object* cal_list_create_view(struct appdata *ad, Evas_Object *main)
470 {
471         CAL_FN_START;
472
473         CAL_ASSERT(ad);
474         c_retv_if(!ad, NULL);
475         c_retv_if(!main, NULL);
476
477         cal_list_data *p = calloc(1, sizeof(cal_list_data));
478         c_retv_if(!p, NULL);
479
480         p->ad = ad;
481
482         Evas_Object *layout = cal_util_add_layout(main, "list/base/without_searchbar");
483         ad->tapbar_focus_view = CV_LIST;
484
485         if (!layout) {
486                 free(p);
487
488                 ERR("layout is null.");
489
490                 return NULL;
491         }
492
493         evas_object_data_set(layout, "priv", p);
494
495         p->layout = layout;
496
497         elm_object_disabled_set(p->ad->new_event_button, EINA_FALSE);
498
499         Evas_Object *bg = cal_util_add_bg(layout, EINA_FALSE);
500         if (!bg) {
501
502                 free(p);
503                 free(layout);
504
505                 ERR("bg is null.");
506
507                 return NULL;
508         }
509
510         elm_object_part_content_set(layout, "base", bg);
511
512         evas_object_event_callback_add(layout, EVAS_CALLBACK_DEL, __cal_list_delete_layout, p);
513
514         p->event_handler = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, __cal_list_mouse_button_down_callback, p);
515
516         CAL_FN_END;
517
518         return layout;
519 }
520
521 void cal_list_update_view(Evas_Object *ly)
522 {
523         CAL_FN_START;
524
525         CAL_ASSERT(ly);
526
527         c_ret_if(!ly);
528
529         cal_list_data *p = CAL_UTIL_GET_PRIV_DATA(ly);
530         c_ret_if(!p);
531
532         struct appdata *ad = p->ad;
533         c_ret_if(!ad);
534
535         __cal_list_update_list(p);
536
537         __cal_list_update_delete_button(p);
538
539
540         CAL_FN_END;
541 }