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