Merge with e17 devel
[platform/core/uifw/e17.git] / src / bin / e_int_config_modules.c
1 #include "e.h"
2 #include <assert.h>
3
4 typedef struct _CFModule CFModule;
5 typedef struct _CFType   CFType;
6 typedef struct _CFTypes  CFTypes;
7
8 struct _CFModule
9 {
10    const char  *short_name, *name, *comment;
11    const char  *icon, *orig_path;
12    E_Module    *module;
13    Evas_Object *end;
14    int          idx;
15    Eina_Bool    enabled : 1;
16 };
17
18 struct _CFType
19 {
20    const char *key, *name, *icon;
21    Eina_Hash  *modules_hash; /* just used before constructing list */
22    Eina_List  *modules; /* sorted and ready to be used */
23 };
24
25 struct _E_Config_Dialog_Data
26 {
27    Evas        *evas;
28    Evas_Object *l_modules;
29    Evas_Object *o_toolbar;
30    Evas_Object *b_load, *b_unload;
31    Evas_Object *o_desc;
32    Eina_List   *types;
33    struct
34    {
35       Eina_List   *loaded, *unloaded;
36    } selected;
37 };
38
39 struct _CFTypes
40 {
41    size_t      key_len;
42    const char *key, *name, *icon;
43 };
44
45 /* pre defined types (used to specify icon and i18n name) */
46 static const CFTypes _types[] =
47 {
48 #define _CFT(k, n, i) \
49   {sizeof(k) - 1, k, n, i}
50    _CFT("utils", N_("Utilities"), "modules-utils"),
51    _CFT("system", N_("System"), "modules-system"),
52    _CFT("look", N_("Look"), "modules-look"),
53    _CFT("files", N_("Files"), "modules-files"),
54    _CFT("launcher", N_("Launcher"), "modules-launcher"),
55    _CFT("core", N_("Core"), "modules-core"),
56    _CFT("mobile", N_("Mobile"), "modules-mobile"),
57    _CFT("settings", N_("Settings"), "modules-settings"),
58 #undef _CFT
59    {0, NULL, NULL, NULL}
60 };
61
62 /* local function protos */
63 static void         _cftype_free(CFType *cft);
64
65 static void         _widget_list_selection_changed(void *data, Evas_Object *obj __UNUSED__);
66
67 static void        *_create_data(E_Config_Dialog *cfd);
68 static void         _free_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
69 static Evas_Object *_basic_create(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
70 static void         _fill_cat_list(E_Config_Dialog_Data *cfdata);
71 static void         _module_end_state_apply(CFModule *cfm);
72
73 static void         _toolbar_select_cb(void *data, void *data2);
74
75 static CFType      *_cftype_find(E_Config_Dialog_Data *cfdata, const char *key, const char *name, const char *icon);
76 static CFType      *_cftype_new(const char *key, const char *name, const char *icon);
77 static void         _load_modules(const char *dir, Eina_Hash *types_hash);
78 static Eina_Bool    _types_list_create_foreach_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata);
79 static int          _types_list_sort(const void *data1, const void *data2);
80
81 static void         _btn_cb_unload(void *data, void *data2);
82 static void         _btn_cb_load(void *data, void *data2);
83
84 EAPI E_Config_Dialog *
85 e_int_config_modules(E_Container *con, const char *params __UNUSED__)
86 {
87    E_Config_Dialog *cfd = NULL;
88    E_Config_Dialog_View *v = NULL;
89
90    if (e_config_dialog_find("E", "_config_modules_dialog")) return NULL;
91
92    v = E_NEW(E_Config_Dialog_View, 1);
93    v->create_cfdata = _create_data;
94    v->free_cfdata = _free_data;
95    v->basic.create_widgets = _basic_create;
96
97    cfd = e_config_dialog_new(con, _("Module Settings"),
98                              "E", "_config_modules_dialog",
99                              "preferences-plugin", 0, v, NULL);
100    return cfd;
101 }
102
103 static void *
104 _create_data(E_Config_Dialog *cfd __UNUSED__)
105 {
106    Eina_Hash *types_hash;
107    Eina_List *modules_paths, *l;
108    E_Path_Dir *epd;
109    E_Config_Dialog_Data *cfdata = E_NEW(E_Config_Dialog_Data, 1);
110
111    types_hash = eina_hash_string_superfast_new(NULL);
112    if (!types_hash) return cfdata;
113
114    modules_paths = e_path_dir_list_get(path_modules);
115    if (!modules_paths)
116      {
117         eina_hash_free(types_hash);
118         return cfdata;
119      }
120
121    EINA_LIST_FOREACH(modules_paths, l, epd)
122      {
123         if (ecore_file_is_dir(epd->dir))
124           _load_modules(epd->dir, types_hash);
125      }
126
127    e_path_dir_list_free(modules_paths);
128
129    eina_hash_foreach(types_hash, _types_list_create_foreach_cb, cfdata);
130    eina_hash_free(types_hash);
131    cfdata->types = eina_list_sort(cfdata->types, -1, _types_list_sort);
132
133    return cfdata;
134 }
135
136 static void
137 _free_data(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
138 {
139    CFType *cft;
140
141    EINA_LIST_FREE(cfdata->types, cft)
142      _cftype_free(cft);
143
144    eina_list_free(cfdata->selected.loaded);
145    eina_list_free(cfdata->selected.unloaded);
146    E_FREE(cfdata);
147 }
148
149 static Evas_Object *
150 _basic_create(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata)
151 {
152    Evas_Coord mw, mh;
153    Evas_Object *of, *ol;
154
155    cfdata->evas = e_win_evas_get(cfd->dia->win);
156
157    of = e_widget_table_add(evas, 0);
158
159    cfdata->o_toolbar = e_widget_toolbar_add(evas, 32 * e_scale, 32 * e_scale);
160    e_widget_toolbar_scrollable_set(cfdata->o_toolbar, 1);
161    _fill_cat_list(cfdata);
162    e_widget_table_object_append(of, cfdata->o_toolbar, 0, 0, 2, 1, 1, 1, 1, 0);
163
164    cfdata->l_modules = e_widget_ilist_add(evas, 32 * e_scale, 32 * e_scale, NULL);
165    e_widget_ilist_multi_select_set(cfdata->l_modules, EINA_TRUE);
166    e_widget_ilist_go(cfdata->l_modules);
167    e_widget_size_min_get(cfdata->l_modules, &mw, &mh);
168    if (mw < (200 * e_scale)) mw = 200 * e_scale;
169    if (mh < (100 * e_scale)) mh = 100 * e_scale;
170    e_widget_size_min_set(cfdata->l_modules, mw, mh);
171    e_widget_on_change_hook_set(cfdata->l_modules,
172                                _widget_list_selection_changed, cfdata);
173    e_widget_table_object_append(of, cfdata->l_modules, 0, 1, 2, 1, 1, 1, 1, 1);
174
175    ol = e_widget_button_add(evas, _("Load"), NULL, _btn_cb_load, cfdata, NULL);
176    cfdata->b_load = ol;
177    e_widget_disabled_set(ol, 1);
178    e_widget_table_object_append(of, ol, 0, 2, 1, 1, 1, 1, 1, 0);
179
180    ol = e_widget_button_add(evas, _("Unload"), NULL, _btn_cb_unload, cfdata, NULL);
181    cfdata->b_unload = ol;
182    e_widget_disabled_set(ol, 1);
183    e_widget_table_object_append(of, ol, 1, 2, 1, 1, 1, 1, 1, 0);
184
185    ol = e_widget_textblock_add(evas);
186    e_widget_size_min_set(ol, (200 * e_scale), 40 * e_scale);
187    cfdata->o_desc = ol;
188    e_widget_table_object_append(of, ol, 0, 3, 2, 1, 1, 0, 1, 0);
189
190    e_dialog_resizable_set(cfd->dia, 1);
191    e_util_win_auto_resize_fill(cfd->dia->win);
192    e_win_centered_set(cfd->dia->win, 1);
193
194    e_widget_focus_set(cfdata->o_toolbar, 1);
195    e_widget_toolbar_item_select(cfdata->o_toolbar, 0);
196
197    return of;
198 }
199
200 static void
201 _fill_cat_list(E_Config_Dialog_Data *cfdata)
202 {
203    Evas_Coord w, h;
204    Evas_Object *icon;
205    CFType *cft;
206    const CFTypes *itr;
207
208    evas_event_freeze(cfdata->evas);
209    edje_freeze();
210
211    for (itr = _types; itr->key_len > 0; itr++)
212      {
213         cft = _cftype_find(cfdata, itr->key, itr->name, itr->icon);
214         if (!cft)
215           {
216              WRN("CFT MISSING!!! key(%s) name(%s) icon(%s)", itr->key, itr->name, itr->icon);
217              continue;
218           }
219         icon = e_icon_add(cfdata->evas);
220         if (icon)
221           {
222              if (!e_util_icon_theme_set(icon, cft->icon))
223                {
224                   evas_object_del(icon);
225                   icon = NULL;
226                }
227           }
228
229         e_widget_toolbar_item_append(cfdata->o_toolbar, icon, _(cft->name),
230                                      _toolbar_select_cb, cfdata, cft);
231      }
232
233    e_widget_size_min_get(cfdata->o_toolbar, &w, &h);
234    e_widget_size_min_set(cfdata->o_toolbar, w, h);
235
236    edje_thaw();
237    evas_event_thaw(cfdata->evas);
238 }
239
240 static void
241 _list_item_append(E_Config_Dialog_Data *cfdata, CFModule *cfm)
242 {
243    Evas_Object *icon, *end;
244
245    if (!cfm->icon)
246      icon = NULL;
247    else
248      {
249         icon = e_icon_add(cfdata->evas);
250         if (icon)
251           {
252              if (!e_util_icon_theme_set(icon, cfm->icon))
253                {
254                   if (cfm->orig_path)
255                     {
256                        char *dir = ecore_file_dir_get(cfm->orig_path);
257                        char buf[PATH_MAX];
258
259                        snprintf(buf, sizeof(buf), "%s/%s.edj", dir, cfm->icon);
260                        free(dir);
261
262                        e_icon_file_edje_set(icon, buf, "icon");
263                     }
264                   else
265                     {
266                        evas_object_del(icon);
267                        icon = NULL;
268                     }
269                }
270           }
271      }
272
273    end = edje_object_add(cfdata->evas);
274    if (end)
275      {
276         if (e_theme_edje_object_set(end, "base/theme/widgets",
277                                     "e/widgets/ilist/toggle_end"))
278           {
279              cfm->end = end;
280              _module_end_state_apply(cfm);
281           }
282         else
283           {
284              EINA_LOG_ERR("your theme is missing 'e/widgets/ilist/toggle_end'!");
285              evas_object_del(end);
286              end = NULL;
287           }
288      }
289
290    e_widget_ilist_append_full(cfdata->l_modules, icon, end,
291                               cfm->name, NULL, cfm, NULL);
292 }
293
294 static void
295 _toolbar_select_cb(void *data, void *data2)
296 {
297    CFType *cft, *cft_cat;
298    E_Config_Dialog_Data *cfdata;
299    Eina_List *l_type;
300    Evas_Coord w, h;
301
302    cfdata = data;
303    cft_cat = data2;
304    if (!cfdata || !cft_cat) return;
305
306    eina_list_free(cfdata->selected.loaded);
307    eina_list_free(cfdata->selected.unloaded);
308    cfdata->selected.loaded = NULL;
309    cfdata->selected.unloaded = NULL;
310    e_widget_disabled_set(cfdata->b_load, EINA_TRUE);
311    e_widget_disabled_set(cfdata->b_unload, EINA_TRUE);
312    e_widget_textblock_markup_set(cfdata->o_desc, _("No modules selected."));
313
314    evas_event_freeze(evas_object_evas_get(cfdata->l_modules));
315    edje_freeze();
316    e_widget_ilist_freeze(cfdata->l_modules);
317    e_widget_ilist_clear(cfdata->l_modules);
318
319    EINA_LIST_FOREACH(cfdata->types, l_type, cft)
320      {
321         CFModule *cfm;
322         Eina_List *l_module;
323
324         if (strcmp(cft->key, cft_cat->key))
325           continue;
326
327         EINA_LIST_FOREACH(cft->modules, l_module, cfm)
328           _list_item_append(cfdata, cfm);
329      }
330
331    e_widget_ilist_go(cfdata->l_modules);
332    e_widget_size_min_get(cfdata->l_modules, &w, &h);
333    e_widget_size_min_set(cfdata->l_modules, w, h);
334    e_widget_ilist_thaw(cfdata->l_modules);
335    edje_thaw();
336    evas_event_thaw(evas_object_evas_get(cfdata->l_modules));
337 }
338
339 static CFModule *
340 _module_new(const char *short_name, const Efreet_Desktop *desk)
341 {
342    CFModule *cfm = E_NEW(CFModule, 1);
343
344    if (!cfm) return NULL;
345    cfm->short_name = eina_stringshare_add(short_name);
346
347    if (desk->name)
348      cfm->name = eina_stringshare_add(desk->name);
349    else
350      cfm->name = eina_stringshare_ref(cfm->short_name);
351
352    cfm->icon = eina_stringshare_add(desk->icon);
353    cfm->comment = eina_stringshare_add(desk->comment);
354    cfm->orig_path = eina_stringshare_add(desk->orig_path);
355    return cfm;
356 }
357
358 static void
359 _module_free(CFModule *cfm)
360 {
361    eina_stringshare_del(cfm->short_name);
362    eina_stringshare_del(cfm->name);
363    eina_stringshare_del(cfm->icon);
364    eina_stringshare_del(cfm->comment);
365    eina_stringshare_del(cfm->orig_path);
366    E_FREE(cfm);
367 }
368
369 static void
370 _module_end_state_apply(CFModule *cfm)
371 {
372    const char *sig;
373
374    if (!cfm->end) return;
375    sig = cfm->enabled ? "e,state,checked" : "e,state,unchecked";
376    edje_object_signal_emit(cfm->end, sig, "e");
377 }
378
379 static CFType *
380 _cftype_find(E_Config_Dialog_Data *cfdata, const char *key, const char *name, const char *icon)
381 {
382    CFType *cft;
383    Eina_List *l;
384
385    EINA_LIST_FOREACH(cfdata->types, l, cft)
386      if ((!strcmp(cft->key, key)) && (!strcmp(cft->name, name)) && (!strcmp(cft->icon, icon))) return cft;
387    return NULL;
388 }
389
390 static CFType *
391 _cftype_new(const char *key, const char *name, const char *icon)
392 {
393    CFType *cft = E_NEW(CFType, 1);
394
395    if (!cft) return NULL;
396    cft->key = eina_stringshare_add(key);
397    cft->name = eina_stringshare_add(name);
398    cft->icon = eina_stringshare_add(icon);
399    //INF("CFT NEW: key(%s) name(%s) icon(%s)", key, name, icon);
400    return cft;
401 }
402
403 static void
404 _cftype_free(CFType *cft)
405 {
406    CFModule *cfm;
407
408    assert(cft->modules_hash == NULL); // must do it before calling this function
409    EINA_LIST_FREE(cft->modules, cfm)
410      _module_free(cfm);
411
412    eina_stringshare_del(cft->key);
413    eina_stringshare_del(cft->name);
414    eina_stringshare_del(cft->icon);
415    E_FREE(cft);
416 }
417
418 static CFType *
419 _cftype_new_from_key(const char *key)
420 {
421    const CFTypes *itr;
422    char name[1024], icon[1024];
423    size_t key_len;
424
425    if (!key) return NULL;
426
427    key_len = strlen(key);
428    for (itr = _types; itr->key_len > 0; itr++)
429      {
430         if (key_len != itr->key_len) continue;
431         if (strcmp(itr->key, key) != 0) continue;
432         return _cftype_new(itr->key, itr->name, itr->icon);
433      }
434
435    if ((key_len + 1) >= sizeof(name)) return NULL;
436    if ((key_len + sizeof("enlightenment/")) >= sizeof(icon)) return NULL;
437
438    memcpy(name, key, key_len + 1);
439    name[0] = toupper(name[0]);
440
441    memcpy(icon, "enlightenment/", sizeof("enlightenment/") - 1);
442    memcpy(icon + sizeof("enlightenment/") - 1, key, key_len + 1);
443
444    return _cftype_new(key, name, icon);
445 }
446
447 static void
448 _load_modules(const char *dir, Eina_Hash *types_hash)
449 {
450    Eina_List *files;
451    char modpath[PATH_MAX];
452    char *mod;
453    int modpathlen;
454
455    modpathlen = snprintf(modpath, sizeof(modpath), "%s/", dir);
456    if (modpathlen >= (int)sizeof(modpath)) return;
457
458    files = ecore_file_ls(dir);
459    EINA_LIST_FREE(files, mod)
460      {
461         Efreet_Desktop *desk;
462         CFType *cft;
463         CFModule *cfm;
464         const char *type;
465         Eina_Bool new_type;
466
467         snprintf(modpath + modpathlen, sizeof(modpath) - modpathlen,
468                  "%s/module.desktop", mod);
469         if (!ecore_file_exists(modpath)) goto end_mod;
470         if (!(desk = efreet_desktop_new(modpath))) goto end_mod;
471
472         if (desk->x)
473           type = eina_hash_find(desk->x, "X-Enlightenment-ModuleType");
474         else
475           type = NULL;
476         if (!type) type = "utils";  // todo: warn?
477
478         cft = eina_hash_find(types_hash, type);
479         if (cft)
480           {
481              new_type = EINA_FALSE;
482              if ((cft->modules_hash) &&
483                  (eina_hash_find(cft->modules_hash, mod)))
484                goto end_desktop;
485           }
486         else
487           {
488              cft = _cftype_new_from_key(type);
489              if (cft) new_type = EINA_TRUE;
490              else goto end_desktop;
491           }
492
493         cfm = _module_new(mod, desk);
494         if (!cfm)
495           {
496              if (new_type) _cftype_free(cft);
497              goto end_desktop;
498           }
499
500         if (!cft->modules_hash)
501           cft->modules_hash = eina_hash_string_superfast_new(NULL);
502         if (!cft->modules_hash)
503           {
504              if (new_type) _cftype_free(cft);
505              _module_free(cfm);
506              goto end_desktop;
507           }
508         eina_hash_direct_add(cft->modules_hash, cfm->short_name, cfm);
509         // TODO be paranoid about hash add failure, otherwise it will leak
510
511         cft->modules = eina_list_append(cft->modules, cfm);
512         // TODO be paranoid about list append failure, otherwise it will leak
513         cfm->module = e_module_find(mod);
514         if (cfm->module)
515           cfm->enabled = e_module_enabled_get(cfm->module);
516         else
517           cfm->enabled = 0;
518
519         if (new_type)
520           eina_hash_direct_add(types_hash, cft->key, cft);
521         // TODO be paranoid about hash add failure, otherwise it will leak
522
523 end_desktop:
524         efreet_desktop_free(desk);
525 end_mod:
526         free(mod);
527      }
528 }
529
530 static int
531 _modules_list_sort(const void *data1, const void *data2)
532 {
533    const CFModule *m1 = data1, *m2 = data2;
534    return strcmp(m1->name, m2->name);
535 }
536
537 static Eina_Bool
538 _types_list_create_foreach_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata)
539 {
540    E_Config_Dialog_Data *cfdata = fdata;
541    CFType *cft = data;
542
543    // otherwise it should not be here
544    assert(cft->modules);
545    assert(cft->modules_hash);
546
547    eina_hash_free(cft->modules_hash);
548    cft->modules_hash = NULL;
549
550    cft->modules = eina_list_sort(cft->modules, -1, _modules_list_sort);
551    cfdata->types = eina_list_append(cfdata->types, cft);
552    // TODO be paranoid about list append failure, otherwise leaks memory
553    return EINA_TRUE;
554 }
555
556 static int
557 _types_list_sort(const void *data1, const void *data2)
558 {
559    const CFType *t1 = data1, *t2 = data2;
560    return strcmp(t1->name, t2->name);
561 }
562
563 static void
564 _widget_list_selection_changed(void *data, Evas_Object *obj __UNUSED__)
565 {
566    E_Config_Dialog_Data *cfdata = data;
567    const Eina_List *l;
568    const E_Ilist_Item *it;
569    CFModule *cfm = NULL;
570    const char *description;
571
572    cfdata->selected.loaded = eina_list_free(cfdata->selected.loaded);
573    cfdata->selected.unloaded = eina_list_free(cfdata->selected.unloaded);
574
575    EINA_LIST_FOREACH(e_widget_ilist_selected_items_get(cfdata->l_modules), l, it)
576      {
577         cfm = e_widget_ilist_item_data_get(it);
578
579         if (cfm->enabled)
580           {
581              cfdata->selected.loaded =
582                eina_list_append(cfdata->selected.loaded, cfm);
583              e_widget_disabled_set(cfdata->b_unload, 0);
584              e_widget_disabled_set(cfdata->b_load, 1);
585           }
586         else
587           {
588              cfdata->selected.unloaded =
589                eina_list_append(cfdata->selected.unloaded, cfm);
590              e_widget_disabled_set(cfdata->b_load, 0);
591              e_widget_disabled_set(cfdata->b_unload, 1);
592           }
593      }
594    
595
596    if ((cfm) && (eina_list_count(cfdata->selected.loaded) + eina_list_count(cfdata->selected.unloaded) == 1))
597      description = cfm->comment;
598    else if (eina_list_count(cfdata->selected.loaded) + eina_list_count(cfdata->selected.unloaded) > 1)
599      description = _("More than one module selected.");
600    else
601      description = _("No modules selected.");
602
603    e_widget_textblock_markup_set(cfdata->o_desc, description);
604 }
605
606 static void
607 _btn_cb_unload(void *data, void *data2 __UNUSED__)
608 {
609    E_Config_Dialog_Data *cfdata = data;
610    CFModule *cfm;
611
612    EINA_LIST_FREE(cfdata->selected.loaded, cfm)
613      {
614         if (!cfm->module)
615           cfm->module = e_module_find(cfm->short_name);
616
617         if (cfm->module)
618           {
619              e_module_disable(cfm->module);
620              cfm->enabled = e_module_enabled_get(cfm->module);
621           }
622
623         _module_end_state_apply(cfm);
624         cfdata->selected.unloaded = eina_list_append(cfdata->selected.unloaded, cfm);
625      }
626
627    e_widget_disabled_set(cfdata->b_unload, 1);
628    e_widget_disabled_set(cfdata->b_load, 0);
629 }
630
631 static void
632 _btn_cb_load(void *data, void *data2 __UNUSED__)
633 {
634    E_Config_Dialog_Data *cfdata = data;
635    CFModule *cfm;
636
637    EINA_LIST_FREE(cfdata->selected.unloaded, cfm)
638      {
639         if (!cfm->module)
640           cfm->module = e_module_find(cfm->short_name);
641         if (!cfm->module)
642           cfm->module = e_module_new(cfm->short_name);
643
644         if (cfm->module)
645           {
646              e_module_enable(cfm->module);
647              cfm->enabled = e_module_enabled_get(cfm->module);
648           }
649
650         _module_end_state_apply(cfm);
651         cfdata->selected.loaded = eina_list_append(cfdata->selected.loaded, cfm);
652      }
653
654    e_widget_disabled_set(cfdata->b_load, 1);
655    e_widget_disabled_set(cfdata->b_unload, 0);
656 }
657