Merge branch 'tizen_6.0' into tizen_6.0_hotfix
[platform/core/uifw/inputmethod-setting.git] / im_setting_list / input_method_setting_list_popup_view.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_list.h"
18 #include "input_method_setting_list_ui.h"
19 #include "input_method_setting_list_popup_view.h"
20 #include <string>
21 #include <efl_extension.h>
22 #include <vector>
23 #include <isf_control.h>
24 #include <algorithm>
25 #include <inputmethod_manager.h>
26 #include <vconf.h>
27
28 #define IM_SETTING_LIST_POPUP_VIEW_TITLE          "IDS_ST_HEADER_DEFAULT_KEYBOARD_ABB"
29
30 static std::vector<ime_info_s>      g_active_ime_info_list;
31 static Elm_Genlist_Item_Class       *itc_im_list = NULL;
32 static Evas_Object                  *group_radio = NULL;
33 static int                          g_active_ime_id = -1;
34
35 typedef struct {
36     void        *data;
37     int         index;
38 } sel_cb_data;
39
40 class ime_info_compare
41 {
42     public:
43     bool operator()(const ime_info_s &first, const ime_info_s &sec)
44     {
45         return (strcasecmp(first.label, sec.label) < 0);
46     }
47 };
48
49 static int selected_index = 0;
50
51 static void im_setting_list_sort_ime_info(std::vector<ime_info_s> &preinstall, std::vector<ime_info_s> &user)
52 {
53     std::sort(preinstall.begin(), preinstall.end(), ime_info_compare());
54     std::sort(user.begin(), user.end(), ime_info_compare());
55     for (unsigned int i = 0; i < preinstall.size(); ++i)
56     {
57         g_active_ime_info_list.push_back(preinstall[i]);
58     }
59     for (unsigned int i = 0; i < user.size(); ++i)
60     {
61         g_active_ime_info_list.push_back(user[i]);
62     }
63 }
64
65 static void im_setting_list_load_active_ime_info(void)
66 {
67     std::vector<ime_info_s>      active_ime_info_list_preinstall;
68     std::vector<ime_info_s>      active_ime_info_list_user;
69     g_active_ime_info_list.clear();
70     char *active_ime_appid = NULL;
71
72     int ret = ime_manager_get_active_ime(&active_ime_appid);
73     if (ret == IME_MANAGER_ERROR_NONE)
74         LOGD("get active ime : %s\n", active_ime_appid);
75     else
76         LOGW("Failed to get active ime. error : %d\n", ret);
77
78     ime_info_s *info = NULL;
79     int cnt = isf_control_get_all_ime_info(&info);
80     if (info)
81     {
82         for (int i = 0; i < cnt; ++i)
83         {
84             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);
85             if (info[i].is_enabled && info[i].is_preinstalled) {
86                 active_ime_info_list_preinstall.push_back(info[i]);
87             } else if (info[i].is_enabled) {
88                 active_ime_info_list_user.push_back(info[i]);
89             }
90         }
91         free(info);
92     }
93
94     im_setting_list_sort_ime_info(active_ime_info_list_preinstall, active_ime_info_list_user);
95     for (unsigned int i = 0; i < g_active_ime_info_list.size(); ++i)
96     {
97         if (active_ime_appid && (!strcmp(active_ime_appid, g_active_ime_info_list[i].appid)))
98         {
99             g_active_ime_id = i;
100         }
101     }
102
103     if (active_ime_appid)
104     {
105         free(active_ime_appid);
106     }
107 }
108
109 static void im_setting_list_update_radio_state(Elm_Object_Item *item, Evas_Object *obj, int index)
110 {
111     if (index < 0 || index >= (int)g_active_ime_info_list.size()) {
112         LOGW("Wrong value. index : %d, g_active_ime_info_list.size() : %zu\n", index, g_active_ime_info_list.size());
113         return;
114     }
115
116     if (item && obj) {
117         elm_genlist_item_selected_set(item, EINA_FALSE);
118         /* Update radio button */
119         Evas_Object *radio = elm_object_item_part_content_get(item, "elm.swallow.end");
120         if (radio == NULL) {
121             radio = elm_object_item_part_content_get(item, "elm.icon");
122         }
123         evas_object_data_set(radio, "parent_genlist", obj);
124         elm_radio_value_set(group_radio, index);
125     }
126 }
127
128 void im_setting_list_update_window_selector(void *data)
129 {
130     appdata *ad = (appdata *)data;
131     if (!ad)
132         return;
133     im_setting_list_load_active_ime_info();
134     im_setting_list_update_window(ad);
135 }
136
137 static Eina_Bool _ime_select_idler_cb(void *data)
138 {
139     appdata *ad = (appdata *)data;
140     if (!ad) return ECORE_CALLBACK_CANCEL;
141
142     LOGD("set active IME\n");
143     if (isf_control_set_active_ime(g_active_ime_info_list[selected_index].appid) != 0)
144         LOGW("Failed to set active IME : %s\n", g_active_ime_info_list[selected_index].appid);
145
146     LOGD("update window selector\n");
147     im_setting_list_update_window_selector(ad);
148
149     LOGD("delete popup\n");
150     if (ad->popup) {
151         evas_object_del(ad->popup);
152     }
153     ad->popup = NULL;
154
155     if (ad->app_type == APP_TYPE_NORMAL) {
156         if (ad->naviframe)
157             elm_naviframe_item_pop(ad->naviframe);
158     } else {
159 #ifdef _WEARABLE
160         if (ad->naviframe)
161             elm_naviframe_item_pop(ad->naviframe);
162 #endif
163     }
164
165     return ECORE_CALLBACK_CANCEL;
166 }
167
168 static void im_setting_list_ime_sel_cb(void *data, Evas_Object *obj, void *event_info)
169 {
170     sel_cb_data *cb_data = (sel_cb_data *)data;
171     if (!cb_data)
172         return;
173
174     appdata *ad = (appdata *)cb_data->data;
175     if (!ad)
176         return;
177
178     int index = cb_data->index;
179     selected_index = index;
180
181     Elm_Object_Item *item = (Elm_Object_Item *)event_info;
182     if (!item) {
183         return;
184     }
185
186     im_setting_list_update_radio_state(item, obj, index);
187
188     ecore_idler_add(_ime_select_idler_cb, ad);
189 }
190
191 static void gl_lang_changed(void *data, Evas_Object *obj, void *event_info)
192 {
193     im_setting_list_load_active_ime_info();
194
195     elm_genlist_realized_items_update(obj);
196 }
197
198 #if !(defined(_WEARABLE) || defined(_MOBILE))
199 static void gl_realized_cb(void *data, Evas_Object *obj, void *event_info)
200 {
201     Elm_Object_Item *it = (Elm_Object_Item *)event_info;
202     Elm_Object_Item *first_item = elm_genlist_first_item_get(obj);
203     Elm_Object_Item *last_item = elm_genlist_last_item_get(obj);
204
205     if (it == first_item && it == last_item) {
206         elm_object_item_signal_emit(it, "elm,state,group,single", "elm");
207     }
208     else if (it == first_item) {
209         elm_object_item_signal_emit(it, "elm,state,group,top", "elm");
210     }
211     else if (it == last_item) {
212         elm_object_item_signal_emit(it, "elm,state,group,bottom", "elm");
213     }
214     else {
215         elm_object_item_signal_emit(it, "elm,state,group,middle", "elm");
216     }
217 }
218 #endif
219
220 static Evas_Object *im_setting_list_genlist_create(appdata *ad, Evas_Object* parent, Evas_Object* conform)
221 {
222     if (!parent)
223         return NULL;
224     Evas_Object *genlist = elm_genlist_add(parent);
225     elm_genlist_mode_set(genlist, ELM_LIST_COMPRESS);
226 #ifdef _CIRCLE
227     /* Circle Surface Creation */
228     if (ad->circle_surface == NULL)
229         ad->circle_surface = eext_circle_surface_conformant_add(conform);
230     Evas_Object *circle_genlist = eext_circle_object_genlist_add(genlist, ad->circle_surface);
231     eext_rotary_object_event_activated_set(circle_genlist, EINA_TRUE);
232 #endif
233     evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
234     evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
235     elm_scroller_content_min_limit(genlist, EINA_FALSE, EINA_TRUE);
236     evas_object_smart_callback_add(genlist, "language,changed", gl_lang_changed, NULL);
237 #if !(defined(_WEARABLE) || defined(_MOBILE))
238     evas_object_smart_callback_add(genlist, "realized", gl_realized_cb, NULL);
239 #endif
240     evas_object_show(genlist);
241     return genlist;
242 }
243
244 static char *im_setting_list_genlist_item_label_get(void *data, Evas_Object *obj, const char *part)
245 {
246     sel_cb_data *cb_data = (sel_cb_data *)data;
247     if (!cb_data)
248         return NULL;
249
250     int index = cb_data->index;
251     if (index < 0 || index >= (int)g_active_ime_info_list.size()) {
252         LOGW("Wrong value. index : %d, g_active_ime_info_list.size() : %zu\n", index, g_active_ime_info_list.size());
253         return NULL;
254     }
255
256     if (!strcmp(part, "elm.text") ||
257         !strcmp(part, "elm.text.main") ||
258         !strcmp(part, "elm.text.main.left")) {
259         return strdup(g_active_ime_info_list[index].label);
260     }
261
262     return NULL;
263 }
264
265 static Evas_Object *im_setting_list_genlist_item_icon_get(void *data, Evas_Object *obj, const char *part)
266 {
267     sel_cb_data *cb_data = (sel_cb_data *)data;
268     if (!cb_data)
269         return NULL;
270
271     int index = cb_data->index;
272     Elm_Object_Item *it = elm_genlist_nth_item_get(obj, index);
273
274     if (!strcmp(part, "elm.swallow.end") ||
275         !strcmp(part, "elm.icon")) {
276         Evas_Object *radio = elm_radio_add(obj);
277         elm_object_style_set(radio, "list");
278         elm_radio_state_value_set(radio, index);
279         evas_object_propagate_events_set(radio, EINA_TRUE);
280         elm_radio_group_add(radio, group_radio);
281         evas_object_show(radio);
282
283         elm_atspi_accessible_relationship_append(it, ELM_ATSPI_RELATION_DESCRIBED_BY, radio);
284         elm_atspi_accessible_relationship_append(radio, ELM_ATSPI_RELATION_CONTROLLED_BY, it);
285
286         return radio;
287     }
288     return NULL;
289 }
290
291 static void im_setting_list_genlist_item_del_cb(void *data, Evas_Object *obj)
292 {
293     sel_cb_data *cb_data = (sel_cb_data *)data;
294     if (!cb_data)
295         return;
296
297     delete cb_data;
298 }
299
300 static void im_setting_list_genlist_item_class_create(void)
301 {
302     itc_im_list = elm_genlist_item_class_new();
303     if (itc_im_list) {
304 #ifdef _WEARABLE
305         itc_im_list->item_style = "1text.1icon.1";
306 #elif _MOBILE
307         itc_im_list->item_style = "type1";
308 #else
309         itc_im_list->item_style = "1line";
310 #endif
311         itc_im_list->func.text_get = im_setting_list_genlist_item_label_get;
312         itc_im_list->func.content_get = im_setting_list_genlist_item_icon_get;
313         itc_im_list->func.state_get = NULL;
314         itc_im_list->func.del = im_setting_list_genlist_item_del_cb;
315     }
316 }
317
318 #ifndef _WEARABLE
319 static Evas_Object *im_setting_list_list_create(void *data)
320 {
321     appdata *ad = (appdata *)data;
322     if (!ad)
323         return NULL;
324     im_setting_list_genlist_item_class_create();
325     Evas_Object *genlist = NULL;
326     genlist = im_setting_list_genlist_create(ad, ad->popup, ad->conform);
327     unsigned int i = 0;
328
329     /* keyboard list */
330     for (i = 0; i < g_active_ime_info_list.size(); i++) {
331         sel_cb_data *cb_data = new sel_cb_data;
332         cb_data->data = data;
333         cb_data->index = i;
334         elm_genlist_item_append(genlist,
335             itc_im_list,
336             (void *)(cb_data),
337             NULL,
338             ELM_GENLIST_ITEM_NONE,
339             im_setting_list_ime_sel_cb,
340             (void *)(cb_data));
341     }
342     elm_radio_value_set(group_radio, g_active_ime_id);
343
344     return genlist;
345 }
346
347 static void
348 im_setting_list_popup_block_clicked_cb(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
349 {
350     appdata *ad = (appdata *)data;
351     if (!ad)
352         return;
353     if (ad->popup) {
354         evas_object_del(ad->popup);
355     }
356     ad->popup = NULL;
357 }
358
359 static void im_setting_list_popup_view_back_cb(void *data, Evas_Object *obj, void *event_info)
360 {
361     appdata *ad = (appdata *)data;
362     if (!ad)
363         return;
364     eext_object_event_callback_del(obj, EEXT_CALLBACK_BACK, im_setting_list_popup_view_back_cb);
365     if (ad->popup) {
366         evas_object_del(ad->popup);
367     }
368     ad->popup = NULL;
369 }
370
371 static Evas_Object *im_setting_list_popup_create(void *data)
372 {
373     appdata *ad = (appdata *)data;
374     if (!ad || !ad->win)
375         return NULL;
376     Evas_Object *parentWin = ad->win;
377     if (NULL == group_radio)
378     {
379         group_radio = elm_radio_add(parentWin);
380         elm_radio_state_value_set(group_radio, -1);
381     }
382
383     Evas_Object *popup = elm_popup_add(parentWin);
384     elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0);
385     evas_object_smart_callback_add(popup, "block,clicked", im_setting_list_popup_block_clicked_cb, data);
386     elm_object_domain_translatable_part_text_set(popup, "title,text", PACKAGE, IM_SETTING_LIST_POPUP_VIEW_TITLE);
387 #if defined(_MOBILE) || defined(_WEARABLE)
388     elm_object_style_set(popup, "theme_bg");
389 #endif
390     eext_object_event_callback_add(popup, EEXT_CALLBACK_BACK, im_setting_list_popup_view_back_cb, data);
391     ad->popup = popup;
392
393     Evas_Object *genlist = im_setting_list_list_create(data);
394     elm_object_content_set(popup, genlist);
395     evas_object_show(popup);
396     return popup;
397 }
398 #endif
399
400 #ifdef _WEARABLE
401 static void _active_keyboard_changed_cb(keynode_t* node, void* data)
402 {
403     im_setting_list_load_active_ime_info();
404     if (group_radio != NULL) {
405         elm_radio_value_set(group_radio, g_active_ime_id);
406     }
407 }
408
409 static char *
410 im_setting_list_default_keyboard_title_text_get(void *data, Evas_Object *obj, const char *part)
411 {
412     return strdup(dgettext(PACKAGE, IM_SETTING_LIST_POPUP_VIEW_TITLE));
413 }
414
415 static Eina_Bool _pop_cb(void *data, Elm_Object_Item *it)
416 {
417 #ifdef _CIRCLE
418     appdata *ad = (appdata *)data;
419     if (ad && ad->main_circle_genlist)
420         eext_rotary_object_event_activated_set(ad->main_circle_genlist, EINA_TRUE);
421 #endif
422
423     vconf_ignore_key_changed(VCONFKEY_ISF_ACTIVE_KEYBOARD_UUID, _active_keyboard_changed_cb);
424
425     return EINA_TRUE;
426 }
427
428 static void im_setting_list_screen_create(void *data)
429 {
430     appdata *ad = NULL;
431     Evas_Object *genlist = NULL;
432
433     ad = (appdata *) data;
434     if (ad == NULL) return;
435
436     Elm_Genlist_Item_Class *ttc = elm_genlist_item_class_new();
437     if (!ttc) return;
438
439     ttc->item_style = "title";
440     ttc->func.text_get = im_setting_list_default_keyboard_title_text_get;
441
442     im_setting_list_genlist_item_class_create();
443     genlist = im_setting_list_genlist_create(ad, ad->win, ad->conform);
444
445     elm_genlist_mode_set(genlist, ELM_LIST_SCROLL);
446     elm_genlist_item_append(genlist, ttc, NULL, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
447
448     if (NULL == group_radio) {
449         group_radio = elm_radio_add(ad->win);
450         elm_radio_state_value_set(group_radio, g_active_ime_id);
451     }
452
453     /* keyboard list */
454     for (unsigned int i = 0; i < g_active_ime_info_list.size(); i++) {
455         sel_cb_data *cb_data = new sel_cb_data;
456         cb_data->data = data;
457         cb_data->index = i;
458         elm_genlist_item_append(genlist,
459             itc_im_list,
460             (void *)(cb_data),
461             NULL,
462             ELM_GENLIST_ITEM_NONE,
463             im_setting_list_ime_sel_cb,
464             (void *)(cb_data));
465     }
466
467     elm_radio_state_value_set(group_radio, g_active_ime_id);
468     elm_radio_value_set(group_radio, g_active_ime_id);
469     elm_genlist_item_class_free(ttc);
470
471 #ifdef _CIRCLE
472     im_setting_list_add_padding(genlist);
473 #endif
474
475     Elm_Object_Item *navi_it = elm_naviframe_item_push(ad->naviframe, NULL, NULL, NULL, genlist, "empty");
476 #ifdef _WEARABLE
477     elm_atspi_accessible_name_set(navi_it, dgettext(PACKAGE, IM_SETTING_LIST_POPUP_VIEW_TITLE));
478 #endif
479     elm_naviframe_item_pop_cb_set(navi_it, _pop_cb, ad);
480
481     vconf_notify_key_changed(VCONFKEY_ISF_ACTIVE_KEYBOARD_UUID, _active_keyboard_changed_cb, data);
482 }
483 #endif
484
485 void im_setting_list_popup_view_del(void *data)
486 {
487     appdata *ad = (appdata *)data;
488     if (!ad || !ad->win)
489         return;
490
491     if (ad->popup) {
492         evas_object_del(ad->popup);
493     }
494     ad->popup = NULL;
495
496     if (ad->naviframe) {
497         elm_naviframe_item_pop(ad->naviframe);
498     }
499 }
500
501 void
502 im_setting_list_popup_view_create(void *data)
503 {
504     appdata *ad = (appdata *)data;
505     if (!ad || !ad->win)
506         return;
507
508     im_setting_list_load_active_ime_info();
509 #ifdef _WEARABLE
510     im_setting_list_screen_create(data);
511 #else
512     im_setting_list_popup_create(data);
513 #endif
514 }