Support language change in runtime
[platform/core/uifw/inputmethod-setting.git] / im_setting_selector / input_method_setting_selector_ui.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 #include "input_method_setting_selector.h"
18 #include <string>
19 #include <app.h>
20 #include <vector>
21 #include <isf_control.h>
22 #include <algorithm>
23 #include <tzplatform_config.h>
24 #include <inputmethod_manager.h>
25
26 #define IM_SETTING_SELECTOR_PACKAGE        PACKAGE
27 #define IM_SETTING_SELECTOR_LOCALE_DIR     tzplatform_mkpath(TZ_SYS_RO_APP, PACKAGE_NAME"/res/locale")
28 #define IM_SETTING_SELECTOR_TITLE          "IDS_ST_HEADER_DEFAULT_KEYBOARD_ABB"
29 #define IM_SETTING_SELECT_KEYBOARD         "IDS_IME_BODY_SELECT_KEYBOARD"
30
31 static std::vector<ime_info_s>      g_ime_info_list;
32 static Elm_Genlist_Item_Class       *itc_im_selector = NULL;
33 static Evas_Object                  *group_radio = NULL;
34 static int                          g_active_ime_id = -1;
35
36 typedef struct {
37     void *data;
38     int index;
39 } sel_cb_data;
40
41 class ime_info_compare
42 {
43     public:
44     bool operator()(const ime_info_s &first, const ime_info_s &sec)
45     {
46         return (strcasecmp(first.label, sec.label) < 0);
47     }
48 };
49
50 static int selected_index = 0;
51
52 static void im_setting_selector_text_domain_set(void)
53 {
54     bindtextdomain(IM_SETTING_SELECTOR_PACKAGE, IM_SETTING_SELECTOR_LOCALE_DIR);
55     textdomain(IM_SETTING_SELECTOR_PACKAGE);
56 }
57
58 static Evas_Object *
59 im_setting_selector_main_window_create(const char *name)
60 {
61     Evas_Object *eo = elm_win_add(NULL, name, ELM_WIN_BASIC);
62     if (eo) {
63         Evas *e;
64         Ecore_Evas *ee;
65         Evas_Coord w = -1, h = -1;
66
67         elm_win_title_set(eo, name);
68         elm_win_borderless_set(eo, EINA_TRUE);
69         elm_win_alpha_set(eo, EINA_TRUE);
70         elm_win_conformant_set(eo, EINA_TRUE);
71         elm_win_autodel_set(eo, EINA_TRUE);
72         if (elm_win_wm_rotation_supported_get(eo)) {
73             int rots[4] = {0, 90, 180, 270};
74             elm_win_wm_rotation_available_rotations_set(eo, rots, 4);
75         }
76         e = evas_object_evas_get(eo);
77         if (e) {
78             ee = ecore_evas_ecore_evas_get(e);
79             if (ee) {
80                 ecore_evas_name_class_set(ee, "SYSTEM_POPUP", "SYSTEM_POPUP");
81             }
82         }
83
84         elm_win_screen_size_get(eo, NULL, NULL, &w, &h);
85         if (w > 0 && h > 0) {
86             evas_object_resize(eo, w, h);
87         }
88     }
89     return eo;
90 }
91
92 static void im_setting_selector_sort_ime_info(std::vector<ime_info_s> &preinstall, std::vector<ime_info_s> &user)
93 {
94     std::sort(preinstall.begin(), preinstall.end(), ime_info_compare());
95     std::sort(user.begin(), user.end(), ime_info_compare());
96     for (unsigned int i = 0; i < preinstall.size(); ++i)
97     {
98         g_ime_info_list.push_back(preinstall[i]);
99     }
100     for (unsigned int i = 0; i < user.size(); ++i)
101     {
102         g_ime_info_list.push_back(user[i]);
103     }
104 }
105
106 static void im_setting_selector_load_ime_info(void)
107 {
108     std::vector<ime_info_s>      ime_info_list_preinstall;
109     std::vector<ime_info_s>      ime_info_list_user;
110     g_ime_info_list.clear();
111     char *active_ime_appid = NULL;
112
113     int ret = ime_manager_get_active_ime(&active_ime_appid);
114     if (ret == IME_MANAGER_ERROR_NONE)
115         LOGD("get active ime : %s\n", active_ime_appid);
116     else
117         LOGW("Failed to get active ime. error : %d\n", ret);
118
119     ime_info_s *info = NULL;
120     int cnt = isf_control_get_all_ime_info(&info);
121     if (info)
122     {
123         for (int i = 0; i < cnt; ++i)
124         {
125             SECURE_LOGD("%s %s %d %d %d\n", info[i].appid, info[i].label, info[i].is_enabled, info[i].is_preinstalled, info[i].has_option);
126             if (info[i].is_enabled && info[i].is_preinstalled) {
127                 ime_info_list_preinstall.push_back(info[i]);
128             } else if (info[i].is_enabled) {
129                 ime_info_list_user.push_back(info[i]);
130             }
131         }
132         free(info);
133     }
134
135     im_setting_selector_sort_ime_info(ime_info_list_preinstall, ime_info_list_user);
136     for (unsigned int i = 0; i < g_ime_info_list.size(); ++i)
137     {
138         if (active_ime_appid && (!strcmp(active_ime_appid, g_ime_info_list[i].appid)))
139         {
140             g_active_ime_id = i;
141         }
142     }
143
144     if (active_ime_appid)
145     {
146         free(active_ime_appid);
147     }
148 }
149
150 #ifndef _WEARABLE
151 static void im_setting_selector_select_keyboard_cb(void *data, Evas_Object *obj, void *event_info)
152 {
153     /* call input method list application*/
154     int ret = ime_manager_show_ime_list();
155     if (ret != IME_MANAGER_ERROR_NONE) {
156         LOGW("Failed to show IME list. error : %d\n", ret);
157     }
158 }
159 #endif
160
161 static void im_setting_selector_update_radio_state(Elm_Object_Item *item, Evas_Object *obj, int index)
162 {
163     if (index < 0 || index >= (int)g_ime_info_list.size()) {
164         LOGW("Wrong value. index : %d, g_ime_info_list.size() : %zu\n", index, g_ime_info_list.size());
165         return;
166     }
167
168     if (item && obj) {
169         elm_genlist_item_selected_set(item, EINA_FALSE);
170         /* Update check button */
171         Evas_Object *radio = elm_object_item_part_content_get(item, "elm.swallow.end");
172         if (radio == NULL) {
173             radio = elm_object_item_part_content_get(item, "elm.icon");
174         }
175         evas_object_data_set(radio, "parent_genlist", obj);
176         elm_radio_value_set(group_radio, index);
177     }
178 }
179
180 static Eina_Bool _ime_select_idler_cb(void *data)
181 {
182     appdata *ad = (appdata *)data;
183     if (!ad) return ECORE_CALLBACK_CANCEL;
184
185     LOGD("set active IME\n");
186     isf_control_set_active_ime(g_ime_info_list[selected_index].appid);
187
188     if (ad->naviframe)
189         elm_naviframe_item_pop(ad->naviframe);
190
191     ui_app_exit();
192
193     return ECORE_CALLBACK_CANCEL;
194 }
195
196 static void im_setting_selector_ime_sel_cb(void *data, Evas_Object *obj, void *event_info)
197 {
198     sel_cb_data *cb_data = (sel_cb_data *)data;
199     if (!cb_data)
200         return;
201     int index = cb_data->index;
202     appdata *ad = (appdata *)cb_data->data;
203     if (!ad)
204         return;
205
206     Elm_Object_Item *item = (Elm_Object_Item *)event_info;
207     if (!item)
208         return;
209
210     selected_index = index;
211     im_setting_selector_update_radio_state(item, obj, index);
212     ecore_idler_add(_ime_select_idler_cb, ad);
213 }
214
215 static Evas_Object *im_setting_selector_genlist_create(appdata *ad, Evas_Object* parent, Evas_Object* conform)
216 {
217     Evas_Object *genlist = elm_genlist_add(parent);
218     elm_genlist_mode_set(genlist, ELM_LIST_COMPRESS);
219 #ifdef _CIRCLE
220     /* Circle Surface Creation */
221     if (ad->circle_surface == NULL)
222         ad->circle_surface = eext_circle_surface_conformant_add(conform);
223     Evas_Object *circle_genlist = eext_circle_object_genlist_add(genlist, ad->circle_surface);
224     eext_rotary_object_event_activated_set(circle_genlist, EINA_TRUE);
225 #endif
226     evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
227     evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
228     elm_scroller_content_min_limit(genlist, EINA_FALSE, EINA_TRUE);
229     evas_object_show(genlist);
230     return genlist;
231 }
232
233 static char *im_setting_selector_genlist_item_label_get(void *data, Evas_Object *obj, const char *part)
234 {
235     sel_cb_data *cb_data = (sel_cb_data *)data;
236     if (!cb_data)
237         return NULL;
238
239     int index = cb_data->index;
240     if (index < 0 || index >= (int)g_ime_info_list.size()) {
241         LOGW("Wrong value. index : %d, g_ime_info_list.size() : %zu\n", index, g_ime_info_list.size());
242         return NULL;
243     }
244
245     if (!strcmp(part, "elm.text") ||
246         !strcmp(part, "elm.text.main") ||
247         !strcmp(part, "elm.text.main.left")) {
248         return strdup(g_ime_info_list[index].label);
249     }
250
251     return NULL;
252 }
253
254 static Evas_Object *im_setting_selector_genlist_item_icon_get(void *data, Evas_Object *obj, const char *part)
255 {
256     sel_cb_data *cb_data = (sel_cb_data *)data;
257     if (!cb_data)
258         return NULL;
259
260     int index = cb_data->index;
261     Elm_Object_Item *it = elm_genlist_nth_item_get(obj, index);
262
263     if (!strcmp(part, "elm.swallow.end") ||
264         !strcmp(part, "elm.icon")) {
265         Evas_Object *radio = elm_radio_add(obj);
266         elm_object_style_set(radio, "list");
267         elm_radio_state_value_set(radio, index);
268         evas_object_propagate_events_set(radio, EINA_TRUE);
269         elm_radio_group_add(radio, group_radio);
270         evas_object_show(radio);
271
272         elm_atspi_accessible_relationship_append(it, ELM_ATSPI_RELATION_DESCRIBED_BY, radio);
273         elm_atspi_accessible_relationship_append(radio, ELM_ATSPI_RELATION_CONTROLLED_BY, it);
274
275         return radio;
276     }
277     return NULL;
278 }
279
280 static void
281 im_setting_selector_genlist_item_del_cb(void *data, Evas_Object *obj)
282 {
283     sel_cb_data *cb_data = (sel_cb_data *)data;
284     if (!cb_data)
285         return;
286
287     delete cb_data;
288 }
289
290 static void im_setting_selector_genlist_item_class_create(void)
291 {
292     itc_im_selector = elm_genlist_item_class_new();
293     if (itc_im_selector) {
294 #ifdef _WEARABLE
295         itc_im_selector->item_style = "1text.1icon.1";
296 #else
297         itc_im_selector->item_style = "type1";
298 #endif
299         itc_im_selector->func.text_get = im_setting_selector_genlist_item_label_get;
300         itc_im_selector->func.content_get = im_setting_selector_genlist_item_icon_get;
301         itc_im_selector->func.state_get = NULL;
302         itc_im_selector->func.del = im_setting_selector_genlist_item_del_cb;
303     }
304 }
305
306 static void im_setting_selector_add_ime(void *data) {
307     appdata *ad = (appdata *)data;
308     unsigned int i = 0;
309     if (!ad)
310         return;
311     im_setting_selector_genlist_item_class_create();
312
313     /* keyboard list */
314     for (i = 0; i < g_ime_info_list.size(); i++) {
315         sel_cb_data *cb_data = new sel_cb_data;
316         cb_data->data = data;
317         cb_data->index = i;
318
319         elm_genlist_item_append(ad->genlist,
320             itc_im_selector,
321             (void *)(cb_data),
322             NULL,
323             ELM_GENLIST_ITEM_NONE,
324             im_setting_selector_ime_sel_cb,
325             (void *)(cb_data));
326     }
327     elm_radio_value_set(group_radio, g_active_ime_id);
328 }
329
330 Evas_Object *im_setting_selector_list_create(void *data)
331 {
332     appdata *ad = (appdata *)data;
333     if (!ad)
334         return NULL;
335     ad->genlist = im_setting_selector_genlist_create(ad, ad->popup, ad->conform);
336     im_setting_selector_add_ime(ad);
337     return ad->genlist;
338 }
339
340 static Evas_Object *im_setting_selector_naviframe_create(Evas_Object* parent)
341 {
342     Evas_Object *naviframe = elm_naviframe_add(parent);
343     elm_naviframe_prev_btn_auto_pushed_set(naviframe, EINA_TRUE);
344     eext_object_event_callback_add(naviframe, EEXT_CALLBACK_BACK, eext_naviframe_back_cb, NULL);
345     evas_object_size_hint_weight_set(naviframe, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
346     evas_object_size_hint_align_set(naviframe, EVAS_HINT_FILL, EVAS_HINT_FILL);
347     elm_object_part_content_set(parent, "elm.swallow.content", naviframe);
348     evas_object_show(naviframe);
349     return naviframe;
350 }
351
352 #ifndef _WEARABLE
353 static Eina_Bool im_setting_list_navi_item_pop_cb(void *data, Elm_Object_Item *it)
354 {
355      static bool in_exit = false;
356      if (in_exit)
357              return EINA_TRUE;
358      in_exit = true;
359      if (data == NULL)
360          return EINA_TRUE;
361      ui_app_exit();
362      return EINA_TRUE;
363 }
364
365 static void
366 im_setting_selector_popup_block_clicked_cb(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
367 {
368     evas_object_del(obj);
369     ui_app_exit();
370 }
371
372 Evas_Object *im_setting_selector_popup_create(void *data)
373 {
374     appdata *ad = (appdata *)data;
375     if (!ad)
376         return NULL;
377     if (NULL == group_radio)
378     {
379         group_radio = elm_radio_add(ad->win);
380         elm_radio_state_value_set(group_radio, -1);
381     }
382     ad->popup = elm_popup_add(ad->win);
383     elm_popup_align_set(ad->popup, ELM_NOTIFY_ALIGN_FILL, 1.0);
384     evas_object_smart_callback_add(ad->popup, "block,clicked", im_setting_selector_popup_block_clicked_cb, NULL);
385     elm_object_domain_translatable_part_text_set(ad->popup, "title,text", PACKAGE, IM_SETTING_SELECTOR_TITLE);
386
387     ad->naviframe = im_setting_selector_naviframe_create(ad->win);
388     ad->genlist = im_setting_selector_list_create(data);
389
390     Evas_Object *back_btn = elm_button_add(ad->naviframe);
391     elm_object_style_set(back_btn, "naviframe/back_btn/default");
392     Elm_Object_Item *nf_main_item = elm_naviframe_item_push(ad->naviframe,
393                          NULL,
394                          back_btn,
395                          NULL,
396                          NULL,
397                          NULL);
398     elm_naviframe_item_pop_cb_set(nf_main_item, im_setting_list_navi_item_pop_cb, ad);
399
400     if (APP_TYPE_NORMAL == ad->app_type) {
401         Evas_Object *btn = elm_button_add(ad->popup);
402         elm_object_domain_translatable_text_set(btn, PACKAGE, IM_SETTING_SELECT_KEYBOARD);
403         elm_object_part_content_set(ad->popup, "button1", btn);
404         evas_object_smart_callback_add(btn, "clicked", im_setting_selector_select_keyboard_cb, ad);
405     }
406
407     elm_object_content_set(ad->popup, ad->genlist);
408     evas_object_show(ad->popup);
409     return ad->popup;
410 }
411 #endif
412
413 #ifdef _WEARABLE
414 static char *
415 im_setting_selector_title_text_get(void *data, Evas_Object *obj, const char *part)
416 {
417     return strdup(dgettext(PACKAGE, IM_SETTING_SELECTOR_TITLE));
418 }
419
420 static Evas_Object *im_setting_selector_conform_create(Evas_Object *parentWin)
421 {
422     Evas_Object *conform = elm_conformant_add(parentWin);
423     elm_win_indicator_mode_set(parentWin, ELM_WIN_INDICATOR_SHOW);
424     elm_win_indicator_opacity_set(parentWin, ELM_WIN_INDICATOR_OPAQUE);
425     evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
426     evas_object_size_hint_align_set(conform, EVAS_HINT_FILL, EVAS_HINT_FILL);
427
428     Evas_Object *bg = elm_bg_add(conform);
429     elm_object_style_set(bg, "indicator/headerbg");
430     elm_object_part_content_set(conform, "elm.swallow.indicator_bg", bg);
431     evas_object_show(bg);
432
433     Evas_Coord w = -1, h = -1;
434     elm_win_screen_size_get(parentWin, NULL, NULL, &w, &h);
435     if (w > 0 && h > 0) {
436         evas_object_resize(conform, w, h);
437     }
438     evas_object_event_callback_add(parentWin, EVAS_CALLBACK_RESIZE, NULL, conform);
439     evas_object_show(conform);
440     return conform;
441 }
442
443 static Eina_Bool _naviframe_pop_cb(void *data, Elm_Object_Item *it)
444 {
445     ui_app_exit();
446     return EINA_FALSE;
447 }
448
449 static void im_setting_selector_screen_create(void *data)
450 {
451     appdata *ad = NULL;
452     Evas_Object *genlist = NULL;
453
454     ad = (appdata *) data;
455     if (ad == NULL) return;
456
457     Elm_Genlist_Item_Class *ttc = elm_genlist_item_class_new();
458     if (!ttc) return;
459
460     ttc->item_style = "title";
461     ttc->func.text_get = im_setting_selector_title_text_get;
462
463     ad->conform = im_setting_selector_conform_create(ad->win);
464     ad->naviframe = im_setting_selector_naviframe_create(ad->conform);
465     genlist = im_setting_selector_genlist_create(ad, ad->win, ad->conform);
466     evas_object_show(ad->win);
467
468     im_setting_selector_genlist_item_class_create();
469
470     elm_genlist_mode_set(genlist, ELM_LIST_SCROLL);
471     elm_genlist_item_append(genlist, ttc, NULL, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
472
473     if (NULL == group_radio) {
474         group_radio = elm_radio_add(genlist);
475         elm_radio_state_value_set(group_radio, g_active_ime_id);
476     }
477
478     /* keyboard list */
479     for (unsigned int i = 0; i < g_ime_info_list.size(); i++) {
480         sel_cb_data *cb_data = new sel_cb_data;
481         cb_data->data = data;
482         cb_data->index = i;
483         elm_genlist_item_append(genlist,
484             itc_im_selector,
485             (void *)(cb_data),
486             NULL,
487             ELM_GENLIST_ITEM_NONE,
488             im_setting_selector_ime_sel_cb,
489             (void *)(cb_data));
490     }
491
492     elm_radio_state_value_set(group_radio, g_active_ime_id);
493     elm_radio_value_set(group_radio, g_active_ime_id);
494     elm_genlist_item_class_free(ttc);
495     Elm_Object_Item *navi_it = elm_naviframe_item_push(ad->naviframe, NULL, NULL, NULL, genlist, "empty");
496     elm_naviframe_item_pop_cb_set(navi_it, _naviframe_pop_cb, ad);
497
498 #ifdef _WEARABLE
499     elm_atspi_accessible_name_set(ad->naviframe, dgettext(PACKAGE, IM_SETTING_SELECTOR_TITLE));
500 #endif
501 }
502 #endif
503
504 void
505 im_setting_selector_app_create(void *data)
506 {
507     appdata *ad = (appdata *)data;
508     if (!ad)
509         return;
510     im_setting_selector_text_domain_set();
511     ad->win = im_setting_selector_main_window_create(PACKAGE);
512     im_setting_selector_load_ime_info();
513 #ifdef _WEARABLE
514     im_setting_selector_screen_create(ad);
515 #else
516     im_setting_selector_popup_create(ad);
517     evas_object_show(ad->win);
518 #endif
519 }
520
521 void
522 im_setting_selector_app_pause(void *data)
523 {
524     ui_app_exit();
525 }
526
527 void im_setting_selector_app_terminate(void *data)
528 {
529     g_ime_info_list.clear();
530     if (NULL != itc_im_selector)
531     {
532         elm_genlist_item_class_free(itc_im_selector);
533         itc_im_selector = NULL;
534     }
535 }