Update codes for TIZEN 2.0
[apps/home/memo.git] / src / memo_text_editor.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://www.tizenopensource.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 <stdlib.h>
20 #include <stdio.h>
21 #include <appcore-efl.h>
22 #include <Elementary.h>
23 #include <Ecore_X.h>
24 #include <dlog.h>
25 #include <gravel.h>
26 #include <vconf-keys.h>
27 #include <extended-elm.h>
28 #include <supplement.h>
29 #include <memo-assist.h>
30 #include <memo_string.h>
31 #include "memo_log.h"
32
33 typedef struct __text_editor_t {
34     Evas_Object *win;
35     Evas_Object *parent;
36     Memo_Component_Callback cb;
37     void *data;
38     memo_data_t *record;
39     char buf[MEMO_BUFFER_SIZE]; /* for temporarily usage only */
40     Eina_Bool savable;
41
42     /* content */
43     Evas_Object *body_main;
44     Evas_Object *toolbar;
45     Evas_Object *btn_size;
46     void *h_fss;
47     Evas_Object *btn_color;
48     void *h_fcs;
49
50     /* entry */
51     Evas_Object *entry;
52     Ecore_IMF_Context *imf_context;
53     Elm_Entry_Filter_Limit_Size limit_filter_data;
54
55     /* original font information */
56     int init_fsize;
57     int init_fcolor;
58 } text_editor_t;
59
60 static void _font_size_selector_del(text_editor_t *te);
61 static void _font_color_selector_del(text_editor_t *te);
62
63 static void _save_record(text_editor_t *te)
64 {
65     MEMO_FUN_BEG();
66     if (te->record->content != NULL) {
67         SFREE(te->record->content);
68     }
69
70     /* content will be freed automatically in memo_free_data when editor destoried */
71     te->record->content = elm_entry_markup_to_utf8(elm_entry_entry_get(te->entry));
72     /* records synced from KIES has no font info, apply font enforce policy here to overwrite default font info */
73     te->record->font_respect = 1;
74     if (te->record->id != -1) {
75         memo_mod_data(te->record);
76     } else {
77         memo_add_data(te->record);
78     }
79     MEMO_FUN_END();
80 }
81
82 static void _on_save_yes(void *data, Evas_Object *obj, void *event_info)
83 {
84     MEMO_FUN_BEG();
85     text_editor_t *te = (text_editor_t *)data;
86     _save_record(te);
87     te->cb(te->data, "save", NULL);
88     MEMO_FUN_END();
89 }
90
91 static void _on_save_no(void *data, Evas_Object *obj, void *event_info)
92 {
93     MEMO_FUN_BEG();
94     text_editor_t *te = (text_editor_t *)data;
95     te->cb(te->data, "cancel", NULL);
96     MEMO_FUN_END();
97 }
98
99 static Eina_Bool _launch_yes_no_popup(void *data)
100 {
101     MEMO_FUN_BEG();
102     text_editor_t *te = (text_editor_t *)data;
103     memo_create_yes_no_popup(te->win, MEMO_I18N_SAVE_MEMO, _on_save_yes, _on_save_no, te);
104     MEMO_FUN_END();
105     return EINA_FALSE;
106 }
107
108 static void _entry_content_update(text_editor_t *te)
109 {
110     MEMO_FUN_BEG();
111     unsigned char *color = (unsigned char *)&te->record->font_color;
112     char *content = elm_entry_markup_to_utf8(elm_entry_entry_get(te->entry));
113     char *markup = elm_entry_utf8_to_markup(content);
114     snprintf(te->buf, MEMO_BUFFER_SIZE,
115          "<font_size=%d><color=#%02x%02x%02x>", te->record->font_size, color[2], color[1],
116          color[0]);
117     elm_entry_entry_set(te->entry, te->buf);
118     elm_entry_entry_insert(te->entry, markup);
119     SFREE(content);
120     SFREE(markup);
121     MEMO_FUN_END();
122 }
123
124 static void _font_size_selector_callback(void *data, const char *msg, void *event)
125 {
126     MEMO_FUN_BEG();
127     text_editor_t *te = (text_editor_t *)data;
128     if (strcmp(msg, "layout") == 0) {
129         elm_object_part_content_set(te->body_main, "elm.swallow.tool", (Evas_Object *)event);
130     } else if (strcmp(msg, "change") == 0) {
131         te->record->font_size = (int)event;
132         _entry_content_update(te);
133     } else if (strcmp(msg, "flick,down") == 0) {
134         _font_size_selector_del(te);
135         edje_object_signal_emit(elm_layout_edje_get(te->body_main), "expand", "elm");
136     }
137     MEMO_FUN_END();
138 }
139
140 static void _font_size_selector_del(text_editor_t *te)
141 {
142     MEMO_FUN_BEG();
143     if (te->h_fss != NULL) {
144         elm_object_part_content_unset(te->body_main, "elm.swallow.tool");
145         memo_del_font_size_selector(te->h_fss);
146         te->h_fss = NULL;
147         memo_tool_btn_focus_set(te->btn_size, EINA_FALSE);
148     }
149     MEMO_FUN_END();
150 }
151
152 static void _on_font_size_btn_up(void *data, Evas *e, Evas_Object *evas_obj, void *event_info)
153 {
154         MEMO_FUN_BEG();
155         text_editor_t *te = (text_editor_t *)data;
156         if (te->h_fss != NULL) { /* font size selector already open */
157                 MEMO_FUN_END();
158                 return;
159         }
160         if (te->h_fcs != NULL) { /* destroy font color selector */
161                 _font_color_selector_del(te);
162         }
163         /* load font size selector */
164         service_h service = NULL;
165         service_create(&service);
166         snprintf(te->buf, MEMO_BUFFER_SIZE, "%d", te->record->font_size);
167         service_add_extra_data(service, "current", te->buf);
168         snprintf(te->buf, MEMO_BUFFER_SIZE, "%d", 0xFFFFFFFF);
169         service_add_extra_data(service, "bg_color", te->buf);
170         te->h_fss = memo_load_font_size_selector(te->body_main, service, _font_size_selector_callback, te);
171         service_destroy(service);
172         memo_tool_btn_focus_set(te->btn_size, EINA_TRUE);
173         edje_object_signal_emit(elm_layout_edje_get(te->body_main), "abbrev", "elm");
174         /* hide input panel */
175         ecore_imf_context_input_panel_hide(te->imf_context);
176         MEMO_FUN_END();
177 }
178
179 void _font_color_selector_callback(void *data, const char *msg, void *event)
180 {
181     MEMO_FUN_BEG();
182     text_editor_t *te = (text_editor_t *)data;
183     if (strcmp(msg, "layout") == 0) {
184         elm_object_part_content_set(te->body_main, "elm.swallow.tool", (Evas_Object *)event);
185     } else if (strcmp(msg, "change") == 0) {
186         LOGD("%s : %x\n", msg, (int)event);
187         te->record->font_color = (int)event;
188         _entry_content_update(te);
189     } else if (strcmp(msg, "flick,down") == 0) {
190         _font_color_selector_del(te);
191         edje_object_signal_emit(elm_layout_edje_get(te->body_main), "expand", "elm");
192     }
193     MEMO_FUN_END();
194 }
195
196 static void _font_color_selector_del(text_editor_t *te)
197 {
198     MEMO_FUN_BEG();
199     if (te->h_fcs != NULL) {
200         elm_object_part_content_unset(te->body_main, "elm.swallow.tool");
201         memo_del_color_selector(te->h_fcs);
202         memo_tool_btn_focus_set(te->btn_color, EINA_FALSE);
203         te->h_fcs = NULL;
204     }
205     MEMO_FUN_END();
206 }
207
208 static void _on_font_color_btn_up(void *data, Evas *e, Evas_Object *evas_obj, void *event_info)
209 {
210         MEMO_FUN_BEG();
211         text_editor_t *te = (text_editor_t *)data;
212         if (te->h_fcs!= NULL) { /* font color selector already open */
213         MEMO_FUN_END();
214         return;
215         }
216         if (te->h_fss!= NULL) { /* destroy font  size selector */
217         _font_size_selector_del(te);
218         }
219         /* load font color selector */
220         service_h service = NULL;
221         service_create(&service);
222         snprintf(te->buf, MEMO_BUFFER_SIZE, "%d", te->record->font_color);
223         service_add_extra_data(service, "color", te->buf);
224         snprintf(te->buf, MEMO_BUFFER_SIZE, "%d", 0xFFFFFFFF);
225         service_add_extra_data(service, "bg_color", te->buf);
226         te->h_fcs = memo_load_color_selector(te->body_main, service, _font_color_selector_callback, te);
227         service_destroy(service);
228         memo_tool_btn_focus_set(te->btn_color, EINA_TRUE);
229         edje_object_signal_emit(elm_layout_edje_get(te->body_main), "abbrev", "elm");
230         /* hide input panel */
231         ecore_imf_context_input_panel_hide(te->imf_context);
232         MEMO_FUN_END();
233 }
234
235 static void _input_panel_event_callback(void *data, Ecore_IMF_Context *ctx, int value)
236 {
237     MEMO_FUN_BEG();
238     text_editor_t *te = (text_editor_t *)data;
239     if (value == ECORE_IMF_INPUT_PANEL_STATE_HIDE) {
240         if ((te->h_fss==NULL) && (te->h_fcs==NULL)) { /* hide by sweep donw */
241             edje_object_signal_emit(elm_layout_edje_get(te->body_main), "expand", "elm");
242         }
243     } else if (value == ECORE_IMF_INPUT_PANEL_STATE_SHOW) {
244         edje_object_signal_emit(elm_layout_edje_get(te->body_main), "abbrev", "elm");
245     }
246     MEMO_FUN_END();
247 }
248
249 static void _on_entry_clicked(void *data, Evas_Object *obj, void *event_info)
250 {
251         MEMO_FUN_BEG();
252         text_editor_t *te = (text_editor_t *)data;
253         if (te->h_fss!= NULL) { /* destroy font  size selector */
254                 _font_size_selector_del(te);
255         }
256         if (te->h_fcs != NULL) { /* destroy font color selector */
257                 _font_color_selector_del(te);
258         }
259 #ifdef __i386__
260         Ecore_IMF_Context *imf_context = elm_entry_imf_context_get(te->entry);
261         if (ecore_imf_context_input_panel_state_get(imf_context) ==
262                 ECORE_IMF_INPUT_PANEL_STATE_HIDE) {
263                 //USB keyboard is active
264                 edje_object_signal_emit(elm_layout_edje_get(te->body_main), "expand", "elm");
265         }
266 #endif
267         MEMO_FUN_END();
268 }
269
270 static void _on_entry_content_change(void *data, Evas_Object *obj, void *event_info)
271 {
272     MEMO_FUN_BEG();
273     text_editor_t *te = (text_editor_t *)data;
274     unsigned char *color = (unsigned char *)&te->record->font_color;
275
276     const char *text = elm_entry_entry_get(te->entry);
277     char *content = elm_entry_markup_to_utf8(text);
278     te->savable = EINA_TRUE;
279     if (strcmp(content, "") == 0) {
280         te->savable = EINA_FALSE;
281     } else if (te->record->content != NULL) { /* check if same as init string */
282         if ((te->init_fsize==te->record->font_size)
283             && (te->init_fcolor==te->record->font_color)
284             && (strcmp(te->record->content, content)==0)) {
285             te->savable = EINA_FALSE;
286         }
287     }
288     te->cb(te->data, "savable", (void *)(te->savable?1:0));
289
290     /* after paste/cut, the font size/color information may be cleared */
291     snprintf(te->buf, MEMO_BUFFER_SIZE, "<font_size=%d><color=#%02x%02x%02x>",
292         te->record->font_size, color[2], color[1], color[0]);
293     if (text[0] == '\0') { /* no content */
294         elm_entry_entry_insert(te->entry, te->buf);
295     } else if (text[0] != '<') { /* insert font information */
296         elm_entry_cursor_begin_set(te->entry);
297         elm_entry_entry_insert(te->entry, te->buf);
298         elm_entry_cursor_end_set(te->entry);
299     }
300     SFREE(content);
301     MEMO_FUN_END();
302 }
303
304 static void _on_yes(void *data, Evas_Object *obj, void *event_info)
305 {
306     MEMO_FUN_BEG();
307     text_editor_t *te = (text_editor_t *)data;
308     _save_record(te);
309     te->cb(te->data, "drawing", NULL);
310     MEMO_FUN_END();
311 }
312
313 static void _on_no(void *data, Evas_Object *obj, void *event_info)
314 {
315     MEMO_FUN_BEG();
316     text_editor_t *te = (text_editor_t *)data;
317     te->cb(te->data, "drawing", NULL);
318     MEMO_FUN_END();
319 }
320
321 static void _on_drawing_mode(void *data, Evas_Object *obj, void *event_info)
322 {
323     MEMO_FUN_BEG();
324     text_editor_t *te = (text_editor_t *)data;
325     if (te->savable) { /* modified, need user confirmation */
326         memo_create_yes_no_popup(te->parent, MEMO_I18N_SAVE_MEMO, _on_yes, _on_no, te);
327     } else {
328         te->cb(te->data, "drawing", NULL);
329     }
330     MEMO_FUN_END();
331 }
332
333 static void _create_text_editor_layout(text_editor_t *te, service_h service)
334 {
335     MEMO_FUN_BEG();
336     int w = 0;
337     int h = 0;
338     ecore_x_window_size_get(ecore_x_window_root_first_get(), &w, &h);
339     snprintf(te->buf, MEMO_BUFFER_SIZE, "text_editor_%d_%d", w, h);
340     te->body_main = elm_layout_create(te->parent, EDJ_FILE, te->buf);
341     te->cb(te->data, "layout", te->body_main);
342     /* title */
343     te->cb(te->data, "title", te->record->id==-1 ? MEMO_I18N_CREATE_MEMO : MEMO_I18N_EDIT_MEMO);
344     /* date */
345     memo_time_format(te->buf, MEMO_BUFFER_SIZE, te->record->modi_time);
346     edje_object_part_text_set(elm_layout_edje_get(te->body_main), "elm.text.date", te->buf);
347     /* toolbar */
348     te->toolbar = elm_swallowed_layout(te->body_main, "elm.swallow.toolbar", EDJ_FILE, "edit_toolbar");
349     te->btn_size = elm_swallowed_layout(te->toolbar, "elm.swallow.btn1", EDJ_DIR"/white/memo.edj", "tl_font_size");
350     evas_object_event_callback_add(te->btn_size, EVAS_CALLBACK_MOUSE_UP, _on_font_size_btn_up, te);
351     te->btn_color = elm_swallowed_layout(te->toolbar, "elm.swallow.btn2", EDJ_DIR"/white/memo.edj", "tl_font_color");
352     evas_object_event_callback_add(te->btn_color, EVAS_CALLBACK_MOUSE_UP, _on_font_color_btn_up, te);
353     if (!service_key_check(service, "toggle", "disable")) {
354         elm_swallowed_button(te->toolbar, "elm.swallow.btn4", MEMO_I18N_DRAWING, _on_drawing_mode, te);
355     }
356     /* entry */
357     Evas_Object *sc = elm_swallowed_scroller(te->body_main, "elm.swallow.text");
358     unsigned char *color = (unsigned char *)&te->record->font_color;
359     snprintf(te->buf, MEMO_BUFFER_SIZE, "<font_size=%d><color=#%02x%02x%02x>",
360         te->record->font_size, color[2], color[1], color[0]);
361     te->entry = elm_entry_create(te->body_main, te->buf);
362     elm_entry_input_panel_enabled_set(te->entry, EINA_TRUE);
363     evas_object_size_hint_align_set(te->entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
364     evas_object_size_hint_weight_set(te->entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
365     elm_entry_autocapital_type_set(te->entry, ELM_AUTOCAPITAL_TYPE_SENTENCE);
366     elm_object_content_set(sc, te->entry);
367     evas_object_smart_callback_add(te->entry, "changed", _on_entry_content_change, te);
368     evas_object_smart_callback_add(te->entry, "preedit,changed", _on_entry_content_change, te);
369     evas_object_smart_callback_add(te->entry, "clicked", _on_entry_clicked, te);
370     /* limit size */
371     elm_entry_markup_filter_append(te->entry, elm_entry_filter_limit_size, &te->limit_filter_data);
372     /* entry content */
373     if (te->record->content != NULL) {
374         char *content = elm_entry_utf8_to_markup(te->record->content);
375         elm_entry_entry_insert(te->entry, content);
376         SFREE(content);
377     } else { /* set init string */
378         char *str = NULL;
379         service_get_extra_data(service, "init_str", &str);
380         if (str != NULL) {
381             elm_entry_entry_insert(te->entry, str);
382             SFREE(str);
383         }
384     }
385     elm_object_focus_set(te->entry, EINA_TRUE);
386     /* imf context */
387     te->imf_context = elm_entry_imf_context_get(te->entry);
388     ecore_imf_context_input_panel_event_callback_add(te->imf_context,
389         ECORE_IMF_INPUT_PANEL_STATE_EVENT, _input_panel_event_callback, te);
390     /* savable */
391     te->cb(te->data, "savable", (void *)(te->savable?1:0));
392     MEMO_FUN_END();
393 }
394
395 void memo_text_editor_time_format_update(void *h_te)
396 {
397     MEMO_FUN_BEG();
398     text_editor_t *te = (text_editor_t *)h_te;
399     memo_time_format(te->buf, MEMO_BUFFER_SIZE, te->record->modi_time);
400     edje_object_part_text_set(elm_layout_edje_get(te->body_main), "elm.text.date", te->buf);
401     MEMO_FUN_END();
402 }
403
404 void memo_text_editor_cancel(void *h_te)
405 {
406     MEMO_FUN_BEG();
407     text_editor_t *te = (text_editor_t *)h_te;
408     if (!te->savable) { /* not modified, return directly */
409         te->cb(te->data, "cancel", NULL);
410     } else {
411         /* hide imf, otherwise popup will be covered by imf */
412         if (te->imf_context != NULL) {
413             ecore_imf_context_input_panel_hide(te->imf_context);
414         }
415         _launch_yes_no_popup(te);
416     }
417     MEMO_FUN_END();
418 }
419
420 void memo_text_editor_save(void *h_te)
421 {
422     MEMO_FUN_BEG();
423     text_editor_t *te = (text_editor_t *)h_te;
424     _save_record(te);
425     te->cb(te->data, "save", NULL);
426     MEMO_FUN_END();
427 }
428
429 void *memo_load_text_editor(Evas_Object *win, Evas_Object *parent, service_h service, Memo_Component_Callback cb, void *data)
430 {
431     MEMO_FUN_BEG();
432     text_editor_t *te = SMALLOC(text_editor_t);
433     RETVIF(te==NULL, NULL);
434     service_dump(service);
435     te->win = win;
436     te->parent = parent;
437     te->cb = (cb==NULL ? memo_com_dummy_cb : cb); /* make sure cb is not null, no need to check legitimacy of cb when call */
438     te->data = data;
439
440     /* init */
441     char *s = NULL;
442     service_get_extra_data(service, "index", &s);
443     if (s == NULL) {
444         te->record = memo_create_data();
445         te->record->id = -1;
446         te->record->modi_time = time((time_t *) 0);
447         te->record->font_size = 44;
448         te->record->font_color = 0xff000000;
449     } else {        /* load item */
450         te->record = memo_get_data(atoi(s));
451         SFREE(s);
452     }
453     te->init_fsize = te->record->font_size;
454     te->init_fcolor = te->record->font_color;
455     /* limitation */
456     te->limit_filter_data.max_char_count = 0;
457     te->limit_filter_data.max_byte_count = 1000;
458
459     _create_text_editor_layout(te, service);
460     MEMO_FUN_END();
461     return (void *)te;
462 }
463
464 void memo_destroy_text_editor(void *h_te)
465 {
466     MEMO_FUN_BEG();
467     text_editor_t *te = (text_editor_t *)h_te;
468     ecore_imf_context_input_panel_event_callback_del(te->imf_context,
469         ECORE_IMF_INPUT_PANEL_STATE_EVENT, _input_panel_event_callback);
470     ecore_imf_context_input_panel_hide(te->imf_context);
471     if (te->h_fss!= NULL) { /* destroy font  size selector */
472         _font_size_selector_del(te);
473     }
474     if (te->h_fcs != NULL) { /* destroy font color selector */
475         _font_color_selector_del(te);
476     }
477     memo_free_data(te->record);
478     evas_object_del(te->body_main);
479     SFREE(te);
480     MEMO_FUN_END();
481 }