15aafb3318f6c3f675d220b3ee842d06eeab0af6
[framework/uifw/e17.git] / src / modules / everything / evry_plug_apps.c
1 #include "e.h"
2 #include "evry_api.h"
3
4 #define MOD_CONFIG_FILE_EPOCH 0x0001
5 #define MOD_CONFIG_FILE_GENERATION 0x009d
6 #define MOD_CONFIG_FILE_VERSION                                 \
7      ((MOD_CONFIG_FILE_EPOCH << 16) | MOD_CONFIG_FILE_GENERATION)
8
9 // FIXME clear cache on .desktop chage event
10
11 /* #undef DBG
12  * #define DBG(...) ERR(__VA_ARGS__) */
13
14 #define MAX_ITEMS 200
15
16 typedef struct _Plugin Plugin;
17 typedef struct _Module_Config Module_Config;
18 typedef struct _E_Exe E_Exe;
19 typedef struct _E_Exe_List E_Exe_List;
20
21 struct _Plugin
22 {
23   Evry_Plugin    base;
24   const char    *input;
25   Eina_List     *apps_mime;
26   Eina_List     *apps_all;
27   Eina_List     *apps_hist;
28
29   Eina_Hash     *added;
30
31   Evry_Item_App *command;
32 };
33
34 struct _E_Exe
35 {
36   const char    *path;
37 };
38
39 struct _E_Exe_List
40 {
41   Eina_List     *list;
42 };
43
44 struct _Module_Config
45 {
46   int              version;
47   const char      *cmd_terminal;
48   const char      *cmd_sudo;
49
50   E_Config_Dialog *cfd;
51   E_Module        *module;
52 };
53
54 static const Evry_API   *evry            = NULL;
55 static Evry_Module      *evry_module     = NULL;
56 static Eina_List        *handlers        = NULL;
57
58 static Module_Config    *_conf;
59 static char _module_icon[] = "system-run";
60 static Eina_List        *_plugins        = NULL;
61 static Eina_List        *_actions        = NULL;
62 static Evry_Item        *_act_open_with  = NULL;
63 static Eina_List        *exe_path        = NULL;
64 static Ecore_Idler      *exe_scan_idler  = NULL;
65 static E_Config_DD      *exelist_exe_edd = NULL;
66 static E_Config_DD      *exelist_edd     = NULL;
67 static DIR              *exe_dir         = NULL;
68 static Eina_List        *exe_list        = NULL;
69 static Eina_List        *exe_list2       = NULL;
70 static Eina_List        *apps_cache      = NULL;
71 static void _scan_executables();
72
73 /***************************************************************************/
74
75 static void _hash_free(void *data)
76 {
77    GET_APP(app, data);
78    EVRY_ITEM_FREE(app);
79 }
80
81 static Evas_Object *
82 _icon_get(Evry_Item *it, Evas *e)
83 {
84    GET_APP(app, it);
85    Evas_Object *o = NULL;
86
87    if (app->desktop && app->desktop->icon)
88      {
89         if (app->desktop->icon[0] == '/')
90           {
91              o = e_icon_add(e);
92              e_icon_preload_set(o, 1);
93
94              if (!e_icon_file_set(o, app->desktop->icon))
95                {
96                   evas_object_del(o);
97                   o = NULL;
98                }
99           }
100
101         if (!o)
102           o = evry->icon_theme_get(app->desktop->icon, e);
103
104         if (!o)
105           o = e_util_desktop_icon_add(app->desktop, 128, e);
106      }
107
108    if (!o)
109      o = evry->icon_theme_get("system-run", e);
110
111    return o;
112 }
113
114 static int
115 _exec_open_file_action(Evry_Action *act)
116 {
117    return evry->util_exec_app(EVRY_ITEM(act), act->it1.item);
118 }
119
120 static void
121 _item_free(Evry_Item *item)
122 {
123    GET_APP(app, item);
124
125    if (app->desktop)
126      efreet_desktop_free(app->desktop);
127    if (app->file)
128      eina_stringshare_del(app->file);
129
130    E_FREE(app);
131 }
132
133 static Evry_Item_App *
134 _item_new(Plugin *p, const char *label, const char *id)
135 {
136    Evry_Item_App *app;
137
138    app = EVRY_ITEM_NEW(Evry_Item_App, p, label, _icon_get, _item_free);
139    EVRY_ACTN(app)->action = &_exec_open_file_action;
140    EVRY_ACTN(app)->it1.type = EVRY_TYPE_FILE;
141    EVRY_ITEM(app)->id = eina_stringshare_add(id);
142
143    eina_hash_add(p->added, id, app);
144
145    EVRY_ACTN(app)->remember_context = EINA_TRUE;
146    EVRY_ITEM(app)->subtype = EVRY_TYPE_ACTION;
147
148    return app;
149 }
150
151 static int
152 _cb_sort(const void *data1, const void *data2)
153 {
154    const Evry_Item *it1 = data1;
155    const Evry_Item *it2 = data2;
156
157    if (it1->usage && it2->usage)
158      return (it1->usage > it2->usage ? -1 : 1);
159    if (it1->usage && !it2->usage)
160      return -1;
161    if (it2->usage && !it1->usage)
162      return 1;
163
164    if (it1->fuzzy_match || it2->fuzzy_match)
165      {
166         if (it1->fuzzy_match && !it2->fuzzy_match)
167           return -1;
168
169         if (!it1->fuzzy_match && it2->fuzzy_match)
170           return 1;
171
172         if (it1->fuzzy_match - it2->fuzzy_match)
173           return (it1->fuzzy_match - it2->fuzzy_match);
174      }
175
176    return strcasecmp(it1->label, it2->label);
177 }
178
179 /***************************************************************************/
180
181 static Evry_Item_App *
182 _item_exe_add(Plugin *p, const char *exe, int match)
183 {
184    Evry_Item_App *app = NULL;
185
186    if ((app = eina_hash_find(p->added, exe)))
187      {
188         if (eina_list_data_find_list(p->base.items, app))
189           return app;
190      }
191
192    if (!app)
193      {
194         app = _item_new(p, ecore_file_file_get(exe), exe);
195         app->file = eina_stringshare_ref(EVRY_ITEM(app)->id);
196      }
197
198    EVRY_ITEM(app)->fuzzy_match = match;
199    EVRY_PLUGIN_ITEM_APPEND(p, app);
200
201    return app;
202 }
203
204 static Eina_Bool
205 _hist_exe_get_cb(const Eina_Hash *hash __UNUSED__, const void *key, void *data, void *fdata)
206 {
207    History_Entry *he = data;
208    History_Item *hi;
209    Plugin *p = fdata;
210    Eina_List *l;
211    Evry_Item_App *app;
212    int match;
213    const char *exe = key;
214
215    EINA_LIST_FOREACH(he->items, l, hi)
216      {
217         app = NULL;
218
219         if (strcmp(hi->plugin, EVRY_PLUGIN(p)->name))
220           continue;
221
222         if (!p->input)
223           {
224              app = _item_exe_add(p, exe, 0);
225           }
226         else if ((match = evry->fuzzy_match(exe, p->input)))
227           {
228              app = _item_exe_add(p, exe, match);
229           }
230
231         if (app)
232           {
233              EVRY_ITEM(app)->hi = hi;
234              evry->history_item_usage_set(EVRY_ITEM(app), p->input, NULL);
235           }
236
237         break;
238      }
239
240    return EINA_TRUE;
241 }
242
243 static int
244 _fetch_exe(Evry_Plugin *plugin, const char *input)
245 {
246    GET_PLUGIN(p, plugin);
247    Eina_List *l;
248    Evry_Item *it;
249    History_Types *ht;
250    unsigned int len = (input ? strlen(input) : 0);
251    double max = 0.0;
252    const char *tmp, *file = NULL;
253    unsigned int min = 0, cnt = 0, end = len, tmp_len;
254    unsigned int query = (len >= (unsigned int)plugin->config->min_query);
255    EVRY_PLUGIN_ITEMS_CLEAR(p);
256
257    p->input = input;
258
259    ht = evry->history_types_get(EVRY_TYPE_APP);
260    if (ht) eina_hash_foreach(ht->types, _hist_exe_get_cb, p);
261
262    if (input)
263      {
264         if ((tmp = strchr(input, ' ')))
265           end = tmp - input;
266
267         if ((!exe_list) && (!exe_scan_idler))
268           _scan_executables();
269
270         EINA_LIST_FOREACH(exe_list, l, tmp)
271           {
272              tmp_len = strlen(tmp);
273
274              if ((end < len) &&  (tmp_len > end))
275                continue;
276
277              if (!strncmp(input, tmp, end))
278                {
279                   if (query && (cnt++ < 50) && (len != tmp_len))
280                     _item_exe_add(p, tmp, 15);
281
282                   if ((!min) || (tmp_len < min))
283                     {
284                        min = tmp_len;
285                        file = tmp;
286                     }
287                   if ((!query) && (tmp_len == len))
288                     break;
289                }
290           }
291
292         if (file)
293           {
294              GET_ITEM(it, p->command);
295
296              if (strlen(file) < len)
297                file = input;
298
299              EVRY_ITEM_LABEL_SET(it, file);
300              IF_RELEASE(p->command->file);
301              p->command->file = eina_stringshare_ref(it->label);
302              it->fuzzy_match = 11; // prefix match
303              EVRY_PLUGIN_ITEM_APPEND(p, it);
304              evry->item_changed(it, 0, 0);
305           }
306      }
307
308    EINA_LIST_FOREACH(plugin->items, l, it)
309      {
310         evry->history_item_usage_set(it, input, NULL);
311         if (input && (it->usage > max) && !strncmp(input, it->label, len))
312           max = it->usage;
313      }
314    EVRY_ITEM(p->command)->usage = (max * 2.0);
315
316    EVRY_PLUGIN_ITEMS_SORT(p, _cb_sort);
317
318    return !!(plugin->items);
319 }
320
321 static Evry_Plugin *
322 _begin_exe(Evry_Plugin *plugin, const Evry_Item *item)
323 {
324    Plugin *p;
325    Evry_Item_App *app;
326
327    if (item && (item != _act_open_with))
328      return NULL;
329
330    EVRY_PLUGIN_INSTANCE(p, plugin)
331
332    p->added = eina_hash_string_small_new(_hash_free);
333
334    app = EVRY_ITEM_NEW(Evry_Item_App, p, NULL, _icon_get, _item_free);
335    EVRY_ACTN(app)->action = &_exec_open_file_action;
336    EVRY_ACTN(app)->remember_context = EINA_TRUE;
337    EVRY_ITEM(app)->subtype = EVRY_TYPE_ACTION;
338    p->command = app;
339
340    return EVRY_PLUGIN(p);
341 }
342
343 static void
344 _finish_exe(Evry_Plugin *plugin)
345 {
346    GET_PLUGIN(p, plugin);
347    char *str;
348
349    EVRY_PLUGIN_ITEMS_CLEAR(p);
350    EVRY_ITEM_FREE(p->command);
351
352    if (p->added)
353      eina_hash_free(p->added);
354
355    if (exe_dir)
356      {
357         closedir(exe_dir);
358         exe_dir = NULL;
359      }
360    EINA_LIST_FREE(exe_path, str)
361      free(str);
362
363    if (exe_scan_idler)
364      {
365         ecore_idler_del(exe_scan_idler);
366         exe_scan_idler = NULL;
367      }
368    EINA_LIST_FREE(exe_list, str)
369      free(str);
370    EINA_LIST_FREE(exe_list2, str)
371      free(str);
372
373    E_FREE(p);
374 }
375
376 /***************************************************************************/
377
378 static void
379 _item_desktop_add(Plugin *p, Efreet_Desktop *desktop, int match)
380 {
381    Evry_Item_App *app = NULL;
382
383    if ((app = eina_hash_find(p->added, desktop->exec)))
384      {
385         if (eina_list_data_find_list(p->base.items, app))
386           return;
387      }
388
389    if (!app)
390      {
391         app = _item_new(p, desktop->name, desktop->exec);
392         efreet_desktop_ref(desktop);
393         app->desktop = desktop;
394
395         if (desktop->comment)
396           {
397              EVRY_ITEM_DETAIL_SET(app, desktop->comment);
398           }
399         else if (desktop->generic_name)
400           {
401              EVRY_ITEM_DETAIL_SET(app, desktop->generic_name);
402           }
403      }
404
405    EVRY_ITEM(app)->fuzzy_match = match;
406    EVRY_PLUGIN_ITEM_APPEND(p, app);
407 }
408
409 static void
410 _desktop_list_add(Plugin *p, Eina_List *apps, const char *input)
411 {
412    Efreet_Desktop *desktop;
413    Eina_List *l;
414    int m1, m2;
415    const char *exec, *end;
416    char buf[PATH_MAX];
417
418    EINA_LIST_FOREACH(apps, l, desktop)
419      {
420         if (eina_list_count(p->base.items) >= MAX_ITEMS) break;
421
422         m1 = m2 = 0;
423
424         if (input)
425           {
426              /* strip path and parameter */
427              exec = ecore_file_file_get(desktop->exec);
428              if ((exec) && (end = strchr(exec, '%')) &&
429                  ((end - exec) - 1 > 0))
430                {
431                   strncpy(buf, exec, (end - exec) - 1);
432                   buf[(end - exec) - 1] = '\0';
433                   m1 = evry->fuzzy_match(buf, input);
434                }
435              else
436                {
437                   m1 = evry->fuzzy_match(exec, input);
438                }
439
440              m2 = evry->fuzzy_match(desktop->name, input);
441
442              if (!m1 || (m2 && m2 < m1)) m1 = m2;
443           }
444
445         if (!input || m1) _item_desktop_add(p, desktop, m1);
446      }
447 }
448
449 static Eina_List *
450 _desktop_list_get(void)
451 {
452    Eina_List *apps = NULL;
453    Eina_List *cat_ss;
454    Eina_List *l, *ll;
455    Efreet_Desktop *d;
456
457    apps = efreet_util_desktop_name_glob_list("*");
458
459    /* remove screensaver */
460    cat_ss = efreet_util_desktop_category_list("Screensaver");
461    EINA_LIST_FOREACH(cat_ss, l, d)
462      {
463         if ((ll = eina_list_data_find_list(apps, d)))
464           {
465              efreet_desktop_free(d);
466              apps = eina_list_remove_list(apps, ll);
467           }
468         efreet_desktop_free(d);
469      }
470
471    return apps;
472 }
473
474 static Eina_Bool
475 _hist_items_get_cb(const Eina_Hash *hash __UNUSED__, const void *key, void *data, void *fdata)
476 {
477    History_Entry *he = data;
478    History_Item *hi;
479    Plugin *p = fdata;
480    Efreet_Desktop *d;
481    Eina_List *l, *ll;
482    const char *exec = key;
483
484    EINA_LIST_FOREACH(he->items, l, hi)
485      {
486         d = NULL;
487
488         if (hi->transient)
489           continue;
490
491         if (strcmp(hi->plugin, EVRY_PLUGIN(p)->name))
492           continue;
493
494         EINA_LIST_FOREACH(apps_cache, ll, d)
495           if (d->exec && !strcmp(d->exec, exec)) break;
496
497         if (!d)
498           {
499              if (!p->apps_all)
500                p->apps_all = _desktop_list_get();
501
502              EINA_LIST_FOREACH(p->apps_all, ll, d)
503                if (d->exec && !strcmp(d->exec, exec)) break;
504
505              if (d)
506                {
507                   efreet_desktop_ref(d);
508                   apps_cache = eina_list_append(apps_cache, d);
509                }
510              else
511                {
512                   /* remove from history */
513                   /* DBG("no desktop: %s", exec);
514                    * hi->transient = 1; */
515                }
516           }
517
518         if (!d)
519           {
520              DBG("app not found %s", exec);
521              break;
522           }
523
524         p->apps_hist = eina_list_append(p->apps_hist, d);
525         break;
526      }
527
528    return EINA_TRUE;
529 }
530
531 static Evry_Plugin *
532 _begin(Evry_Plugin *plugin, const Evry_Item *item)
533 {
534    Plugin *p;
535
536    if (item && (item != _act_open_with))
537      return NULL;
538
539    EVRY_PLUGIN_INSTANCE(p, plugin)
540    p->added = eina_hash_string_small_new(_hash_free);
541
542    return EVRY_PLUGIN(p);
543 }
544
545 static void
546 _finish(Evry_Plugin *plugin)
547 {
548    GET_PLUGIN(p, plugin);
549    Efreet_Desktop *desktop;
550
551    EVRY_PLUGIN_ITEMS_CLEAR(p);
552
553    if (p->added)
554      eina_hash_free(p->added);
555
556    EINA_LIST_FREE(p->apps_all, desktop)
557      efreet_desktop_free(desktop);
558
559    EINA_LIST_FREE(p->apps_hist, desktop);
560
561    EINA_LIST_FREE(p->apps_mime, desktop)
562      efreet_desktop_free(desktop);
563
564    E_FREE(p);
565 }
566
567 static int
568 _fetch(Evry_Plugin *plugin, const char *input)
569 {
570    GET_PLUGIN(p, plugin);
571    Eina_List *l;
572    Evry_Item *it;
573    History_Types *ht;
574
575    EVRY_PLUGIN_ITEMS_CLEAR(p);
576
577    if (input)
578      {
579         if (!p->apps_all)
580           p->apps_all = _desktop_list_get();
581
582         _desktop_list_add(p, p->apps_all, input);
583      }
584    else
585      {
586         _desktop_list_add(p, p->apps_mime, input);
587      }
588
589    if (!input && !(plugin->items))
590      {
591         if (!p->apps_hist)
592           {
593              ht = evry->history_types_get(EVRY_TYPE_APP);
594              if (ht) eina_hash_foreach(ht->types, _hist_items_get_cb, p);
595           }
596
597         _desktop_list_add(p, p->apps_hist, NULL);
598      }
599
600    EINA_LIST_FOREACH(plugin->items, l, it)
601      evry->history_item_usage_set(it, input, NULL);
602
603    EVRY_PLUGIN_ITEMS_SORT(p, _cb_sort);
604
605    return !!(plugin->items);
606 }
607
608 /***************************************************************************/
609
610 static Evry_Plugin *
611 _begin_mime(Evry_Plugin *plugin, const Evry_Item *item)
612 {
613    Plugin *p = NULL;
614    Efreet_Desktop *d;
615    const char *mime;
616    const char *path = NULL;
617    Eina_List *l;
618
619    if (CHECK_TYPE(item, EVRY_TYPE_ACTION))
620      {
621         GET_ACTION(act, item);
622         GET_FILE(file, act->it1.item);
623
624         if (!evry->file_path_get(file))
625           return NULL;
626
627         path = file->path;
628         mime = file->mime;
629      }
630    else if (CHECK_TYPE(item, EVRY_TYPE_FILE))
631      {
632         GET_FILE(file, item);
633
634         if (!evry->file_path_get(file))
635           return NULL;
636
637         path = file->path;
638         mime = file->mime;
639      }
640    else
641      {
642         return NULL;
643      }
644
645    if (!path || !mime || !(mime = efreet_mime_type_get(path)))
646      return NULL;
647
648    EVRY_PLUGIN_INSTANCE(p, plugin);
649
650    p->apps_mime = efreet_util_desktop_mime_list(mime);
651
652    if (strcmp(mime, "text/plain") && (!strncmp(mime, "text/", 5)))
653      {
654         l = efreet_util_desktop_mime_list("text/plain");
655
656         EINA_LIST_FREE(l, d)
657           {
658              if (!eina_list_data_find_list(p->apps_mime, d))
659                p->apps_mime = eina_list_append(p->apps_mime, d);
660              else
661                efreet_desktop_free(d);
662           }
663      }
664
665    if (item->browseable && strcmp(mime, "x-directory/normal"))
666      {
667         l = efreet_util_desktop_mime_list("x-directory/normal");
668
669         EINA_LIST_FREE(l, d)
670           {
671              if (!eina_list_data_find_list(p->apps_mime, d))
672                p->apps_mime = eina_list_append(p->apps_mime, d);
673              else
674                efreet_desktop_free(d);
675           }
676      }
677
678    if ((d = e_exehist_mime_desktop_get(mime)))
679      {
680         if ((l = eina_list_data_find_list(p->apps_mime, d)))
681           {
682              p->apps_mime = eina_list_promote_list(p->apps_mime, l);
683              efreet_desktop_free(d);
684           }
685         else
686           {
687              p->apps_mime = eina_list_prepend(p->apps_mime, d);
688           }
689      }
690
691    p->added = eina_hash_string_small_new(_hash_free);
692
693    return EVRY_PLUGIN(p);
694 }
695
696 static void
697 _finish_mime(Evry_Plugin *plugin)
698 {
699    GET_PLUGIN(p, plugin);
700    Efreet_Desktop *desktop;
701
702    EVRY_PLUGIN_ITEMS_CLEAR(p);
703
704    if (p->added)
705      eina_hash_free(p->added);
706
707    EINA_LIST_FREE(p->apps_mime, desktop)
708      efreet_desktop_free(desktop);
709
710    E_FREE(p);
711 }
712
713 static int
714 _fetch_mime(Evry_Plugin *plugin, const char *input)
715 {
716    GET_PLUGIN(p, plugin);
717    Eina_List *l;
718    Evry_Item *it;
719
720    EVRY_PLUGIN_ITEMS_CLEAR(p);
721
722    /* add apps for a given mimetype */
723    _desktop_list_add(p, p->apps_mime, input);
724
725    EINA_LIST_FOREACH(plugin->items, l, it)
726      evry->history_item_usage_set(it, input, NULL);
727
728    if (input)
729      EVRY_PLUGIN_ITEMS_SORT(p, _cb_sort);
730
731    return 1;
732 }
733
734
735 static int
736 _complete(Evry_Plugin *plugin __UNUSED__, const Evry_Item *it, char **input)
737 {
738    GET_APP(app, it);
739
740    char buf[128];
741
742    if (app->desktop)
743      {
744         char *space = strchr(app->desktop->exec, ' ');
745
746         snprintf(buf, sizeof(buf), "%s", app->desktop->exec);
747         if (space)
748           buf[1 + space - app->desktop->exec] = '\0';
749      }
750    else
751      snprintf(buf, sizeof(buf), "%s", app->file);
752
753    *input = strdup(buf);
754
755    return EVRY_COMPLETE_INPUT;
756 }
757
758 /***************************************************************************/
759
760 static int
761 _exec_app_check_item(Evry_Action *act __UNUSED__, const Evry_Item *it __UNUSED__)
762 {
763    return 1;
764 }
765
766 static int
767 _exec_app_action(Evry_Action *act)
768 {
769    return evry->util_exec_app(act->it1.item, act->it2.item);
770 }
771
772 static int
773 _exec_file_action(Evry_Action *act)
774 {
775    return evry->util_exec_app(act->it2.item, act->it1.item);
776 }
777
778 static int
779 _exec_term_action(Evry_Action *act)
780 {
781    GET_APP(app, act->it1.item);
782    Evry_Item_App *tmp;
783    char buf[1024];
784    int ret;
785    char *escaped = ecore_file_escape_name(app->file);
786
787    tmp = E_NEW(Evry_Item_App, 1);
788    snprintf(buf, sizeof(buf), "%s -hold -e %s",
789             _conf->cmd_terminal,
790             (escaped ? escaped : app->file));
791
792    tmp->file = buf;
793    ret = evry->util_exec_app(EVRY_ITEM(tmp), NULL);
794
795    E_FREE(tmp);
796    E_FREE(escaped);
797
798    return ret;
799 }
800
801 static int
802 _exec_term_check_item(Evry_Action *act __UNUSED__, const Evry_Item *it)
803 {
804    GET_APP(app, it);
805
806    if (app->file)
807      return 1;
808
809    return 0;
810 }
811
812 static int
813 _exec_sudo_action(Evry_Action *act)
814 {
815    GET_APP(app, act->it1.item);
816    Evry_Item_App *tmp;
817    char buf[1024];
818    int ret;
819
820    tmp = E_NEW(Evry_Item_App, 1);
821    snprintf(buf, sizeof(buf), "%s %s",
822             _conf->cmd_sudo,
823             (app->desktop ? app->desktop->exec : app->file));
824
825    tmp->file = buf;
826    ret = evry->util_exec_app(EVRY_ITEM(tmp), NULL);
827
828    E_FREE(tmp);
829
830    return ret;
831 }
832
833 static int
834 _edit_app_check_item(Evry_Action *act __UNUSED__, const Evry_Item *it)
835 {
836    GET_APP(app, it);
837
838    if (app->desktop)
839      return 1;
840
841    return 0;
842 }
843
844 static int
845 _edit_app_action(Evry_Action *act)
846 {
847    Efreet_Desktop *desktop;
848    GET_APP(app, act->it1.item);
849
850    if (app->desktop)
851      desktop = app->desktop;
852    else
853      {
854         char buf[128];
855         snprintf(buf, 128, "%s/.local/share/applications/%s.desktop",
856                  e_user_homedir_get(), app->file);
857         desktop = efreet_desktop_empty_new(eina_stringshare_add(buf));
858         /* XXX check if this is freed by efreet*/
859         desktop->exec = strdup(app->file);
860      }
861
862    e_desktop_edit(e_container_current_get(e_manager_current_get()), desktop);
863
864    return 1;
865 }
866
867 static int
868 _new_app_check_item(Evry_Action *act __UNUSED__, const Evry_Item *it)
869 {
870    GET_APP(app, it);
871
872    if (app->desktop)
873      return 1;
874
875    if (app->file && strlen(app->file) > 0)
876      return 1;
877
878    return 0;
879 }
880
881 static int
882 _new_app_action(Evry_Action *act)
883 {
884    char *name;
885    char buf[4096];
886    char *end;
887    Efreet_Desktop *desktop;
888    int i;
889
890    GET_APP(app, act->it1.item);
891
892    if (app->desktop)
893      name = strdup(app->desktop->name);
894    else
895      /* TODO replace '/' and remove other special characters */
896      name = strdup(app->file);
897
898    if ((end = strchr(name, ' ')))
899      name[end - name] = '\0';
900
901    for (i = 0; i < 10; i++)
902      {
903         snprintf(buf, 4096, "%s/.local/share/applications/%s-%d.desktop",
904                  e_user_homedir_get(), name, i);
905         if (ecore_file_exists(buf))
906           {
907              buf[0] = '\0';
908              continue;
909           }
910         else break;
911      }
912    free(name);
913
914    if (strlen(buf) == 0)
915      return 0;
916
917    if (!app->desktop)
918      {
919         desktop = efreet_desktop_empty_new(buf);
920         desktop->exec = (char *)eina_stringshare_add(app->file);
921      }
922    else
923      {
924         desktop = efreet_desktop_empty_new(buf);
925         if (app->desktop->name)
926           desktop->name = strdup(app->desktop->name);
927         if (app->desktop->comment)
928           desktop->comment = strdup(app->desktop->comment);
929         if (app->desktop->generic_name)
930           desktop->generic_name = strdup(app->desktop->generic_name);
931         if (app->desktop->generic_name)
932           desktop->generic_name = strdup(app->desktop->generic_name);
933         if (app->desktop->exec)
934           desktop->exec = strdup(app->desktop->exec);
935         if (app->desktop->icon)
936           desktop->icon = strdup(app->desktop->icon);
937         if (app->desktop->mime_types)
938           desktop->mime_types = eina_list_clone(app->desktop->mime_types);
939      }
940    if (desktop)
941      e_desktop_edit(e_container_current_get(e_manager_current_get()), desktop);
942
943    return 1;
944 }
945
946 static int
947 _open_term_action(Evry_Action *act)
948 {
949    GET_FILE(file, act->it1.item);
950    Evry_Item_App *tmp;
951    char cwd[4096];
952    char *dir;
953    int ret = 0;
954
955    if (!(evry->file_path_get(file)))
956      return 0;
957
958    if (IS_BROWSEABLE(file))
959      dir = strdup(file->path);
960    else
961      dir = ecore_file_dir_get(file->path);
962
963    if (dir)
964      {
965         if (!getcwd(cwd, sizeof(cwd)))
966           return 0;
967         if (chdir(dir))
968           return 0;
969
970         tmp = E_NEW(Evry_Item_App, 1);
971         tmp->file = _conf->cmd_terminal;
972
973         ret = evry->util_exec_app(EVRY_ITEM(tmp), NULL);
974         E_FREE(tmp);
975         E_FREE(dir);
976         if (chdir(cwd))
977           return 0;
978      }
979
980    return ret;
981 }
982
983 static int
984 _check_executable(Evry_Action *act __UNUSED__, const Evry_Item *it)
985 {
986    GET_FILE(file, it);
987
988    return ecore_file_can_exec(evry->file_path_get(file));
989 }
990
991 static int
992 _run_executable(Evry_Action *act)
993 {
994    GET_FILE(file, act->it1.item);
995
996    e_exec(e_util_zone_current_get(e_manager_current_get()), NULL, file->path, NULL, NULL);
997
998    return 1;
999 }
1000
1001 /***************************************************************************/
1002 static Eina_Bool
1003 _desktop_cache_update(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
1004 {
1005    Efreet_Desktop *d;
1006
1007    EINA_LIST_FREE(apps_cache, d)
1008      efreet_desktop_unref(d);
1009
1010    return EINA_TRUE;
1011 }
1012
1013 static int
1014 _plugins_init(const Evry_API *api)
1015 {
1016    Evry_Plugin *p;
1017    int prio = 0;
1018    Eina_List *l;
1019    Evry_Action *act;
1020
1021    if (evry_module->active)
1022      return EINA_TRUE;
1023
1024    evry = api;
1025
1026    if (!evry->api_version_check(EVRY_API_VERSION))
1027      return EINA_FALSE;
1028
1029    p = EVRY_PLUGIN_NEW(Plugin, N_("Applications"),
1030                        _module_icon, EVRY_TYPE_APP,
1031                        _begin, _finish, _fetch, NULL);
1032    p->complete = &_complete;
1033    p->config_path = "extensions/everything-apps";
1034    evry->plugin_register(p, EVRY_PLUGIN_SUBJECT, 1);
1035    _plugins = eina_list_append(_plugins, p);
1036
1037
1038    p = EVRY_PLUGIN_NEW(Plugin, N_("Exebuf"),
1039                        _module_icon, EVRY_TYPE_APP,
1040                        _begin_exe, _finish_exe, _fetch_exe, NULL);
1041    p->complete = &_complete;
1042    p->config_path = "extensions/everything-apps";
1043    _plugins = eina_list_append(_plugins, p);
1044    if (evry->plugin_register(p, EVRY_PLUGIN_SUBJECT, 3))
1045      p->config->min_query = 5;
1046
1047    p = EVRY_PLUGIN_NEW(Plugin, N_("Applications"),
1048                        _module_icon, EVRY_TYPE_APP,
1049                        _begin_mime, _finish, _fetch, NULL);
1050    p->complete = &_complete;
1051    p->config_path = "extensions/everything-apps";
1052    evry->plugin_register(p, EVRY_PLUGIN_OBJECT, 1);
1053    _plugins = eina_list_append(_plugins, p);
1054
1055    p = EVRY_PLUGIN_NEW(Plugin, N_("Open With..."),
1056                        _module_icon, EVRY_TYPE_APP,
1057                        _begin_mime, _finish_mime, _fetch_mime, NULL);
1058    p->config_path = "extensions/everything-apps";
1059    evry->plugin_register(p, EVRY_PLUGIN_ACTION, 1);
1060    _plugins = eina_list_append(_plugins, p);
1061
1062    act = EVRY_ACTION_NEW(N_("Launch"),
1063                          EVRY_TYPE_APP, 0,
1064                          "system-run",
1065                          _exec_app_action,
1066                          _exec_app_check_item);
1067    _actions = eina_list_append(_actions, act);
1068
1069    act = EVRY_ACTION_NEW(N_("Open File..."),
1070                          EVRY_TYPE_APP, EVRY_TYPE_FILE,
1071                          "document-open",
1072                          _exec_app_action,
1073                          _exec_app_check_item);
1074    _actions = eina_list_append(_actions, act);
1075
1076    act = EVRY_ACTION_NEW(N_("Run in Terminal"),
1077                          EVRY_TYPE_APP, 0,
1078                          "system-run",
1079                          _exec_term_action,
1080                          _exec_term_check_item);
1081    _actions = eina_list_append(_actions, act);
1082
1083    act = EVRY_ACTION_NEW(N_("Edit Application Entry"),
1084                          EVRY_TYPE_APP, 0,
1085                          "everything-launch",
1086                          _edit_app_action,
1087                          _edit_app_check_item);
1088    _actions = eina_list_append(_actions, act);
1089
1090    act = EVRY_ACTION_NEW(N_("New Application Entry"),
1091                          EVRY_TYPE_APP, 0,
1092                          "everything-launch",
1093                          _new_app_action,
1094                          _new_app_check_item);
1095    _actions = eina_list_append(_actions, act);
1096
1097    act = EVRY_ACTION_NEW(N_("Run with Sudo"),
1098                          EVRY_TYPE_APP, 0,
1099                          "system-run",
1100                          _exec_sudo_action, NULL);
1101    _actions = eina_list_append(_actions, act);
1102
1103    act = EVRY_ACTION_NEW(N_("Open with..."),
1104                          EVRY_TYPE_FILE, EVRY_TYPE_APP,
1105                          "everything-launch",
1106                          _exec_file_action, NULL);
1107    _act_open_with = EVRY_ITEM(act);
1108    _actions = eina_list_append(_actions, act);
1109
1110    act = EVRY_ACTION_NEW(N_("Open Terminal here"),
1111                          EVRY_TYPE_FILE, 0,
1112                          "system-run",
1113                          _open_term_action, NULL);
1114    _actions = eina_list_append(_actions, act);
1115
1116    act = EVRY_ACTION_NEW(N_("Run Executable"),
1117                          EVRY_TYPE_FILE, 0,
1118                          "system-run",
1119                          _run_executable,
1120                          _check_executable);
1121    _actions = eina_list_append(_actions, act);
1122
1123    EINA_LIST_FOREACH(_actions, l, act)
1124      evry->action_register(act,  prio++);
1125
1126    handlers = eina_list_append
1127      (handlers, ecore_event_handler_add
1128       (EFREET_EVENT_DESKTOP_CACHE_UPDATE, _desktop_cache_update, NULL));
1129
1130    return EINA_TRUE;
1131 }
1132
1133 static void
1134 _plugins_shutdown(void)
1135 {
1136    Evry_Action *act;
1137    Evry_Plugin *p;
1138    Efreet_Desktop *d;
1139    Ecore_Event_Handler *h;
1140
1141    if (!evry_module->active)
1142      return;
1143
1144    EINA_LIST_FREE(apps_cache, d)
1145      efreet_desktop_unref(d);
1146
1147    EINA_LIST_FREE(_plugins, p)
1148      EVRY_PLUGIN_FREE(p);
1149
1150    EINA_LIST_FREE(_actions, act)
1151      EVRY_ACTION_FREE(act);
1152
1153    EINA_LIST_FREE(handlers, h)
1154      ecore_event_handler_del(h);
1155
1156    evry_module->active = EINA_FALSE;
1157 }
1158
1159 /***************************************************************************/
1160
1161 static E_Config_DD *conf_edd = NULL;
1162
1163 struct _E_Config_Dialog_Data
1164 {
1165   char *cmd_terminal;
1166   char *cmd_sudo;
1167 };
1168
1169 static void *_create_data(E_Config_Dialog *cfd);
1170 static void _free_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
1171 static void _fill_data(E_Config_Dialog_Data *cfdata);
1172 static Evas_Object *_basic_create(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
1173 static int _basic_apply(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
1174
1175 static E_Config_Dialog *
1176 _conf_dialog(E_Container *con, const char *params __UNUSED__)
1177 {
1178    E_Config_Dialog *cfd = NULL;
1179    E_Config_Dialog_View *v = NULL;
1180
1181    if (e_config_dialog_find("everything-apps", "launcher/everything-apps")) return NULL;
1182
1183    v = E_NEW(E_Config_Dialog_View, 1);
1184    if (!v) return NULL;
1185
1186    v->create_cfdata = _create_data;
1187    v->free_cfdata = _free_data;
1188    v->basic.create_widgets = _basic_create;
1189    v->basic.apply_cfdata = _basic_apply;
1190
1191    cfd = e_config_dialog_new(con, _("Everything Applications"), "everything-apps",
1192                              "launcher/everything-apps", _module_icon, 0, v, NULL);
1193
1194    /* e_dialog_resizable_set(cfd->dia, 1); */
1195    _conf->cfd = cfd;
1196    return cfd;
1197 }
1198
1199 /* Local Functions */
1200 static void *
1201 _create_data(E_Config_Dialog *cfd __UNUSED__)
1202 {
1203    E_Config_Dialog_Data *cfdata = NULL;
1204
1205    cfdata = E_NEW(E_Config_Dialog_Data, 1);
1206    _fill_data(cfdata);
1207    return cfdata;
1208 }
1209
1210 static void
1211 _free_data(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
1212 {
1213    _conf->cfd = NULL;
1214    E_FREE(cfdata);
1215 }
1216
1217 static Evas_Object *
1218 _basic_create(E_Config_Dialog *cfd __UNUSED__, Evas *e, E_Config_Dialog_Data *cfdata)
1219 {
1220    Evas_Object *o = NULL, *of = NULL, *ow = NULL;
1221
1222    o = e_widget_list_add(e, 0, 0);
1223
1224    of = e_widget_framelist_add(e, _("Commands"), 0);
1225    ow = e_widget_label_add(e, _("Terminal Command"));
1226    e_widget_framelist_object_append(of, ow);
1227    ow = e_widget_entry_add(e, &(cfdata->cmd_terminal), NULL, NULL, NULL);
1228    e_widget_framelist_object_append(of, ow);
1229
1230    ow = e_widget_label_add(e, _("Sudo GUI"));
1231    e_widget_framelist_object_append(of, ow);
1232    ow = e_widget_entry_add(e, &(cfdata->cmd_sudo), NULL, NULL, NULL);
1233    e_widget_framelist_object_append(of, ow);
1234
1235    e_widget_list_object_append(o, of, 1, 1, 0.5);
1236
1237    return o;
1238 }
1239
1240 static void
1241 _fill_data(E_Config_Dialog_Data *cfdata)
1242 {
1243
1244 #define CP(_name) cfdata->_name = strdup(_conf->_name);
1245 #define C(_name) cfdata->_name = _conf->_name;
1246    CP(cmd_terminal);
1247    CP(cmd_sudo);
1248 #undef CP
1249 #undef C
1250 }
1251
1252 static int
1253 _basic_apply(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
1254 {
1255 #define CP(_name)                                       \
1256    if (_conf->_name)                                    \
1257      eina_stringshare_del(_conf->_name);                \
1258    _conf->_name = eina_stringshare_add(cfdata->_name);
1259 #define C(_name) _conf->_name = cfdata->_name;
1260    CP(cmd_terminal);
1261    CP(cmd_sudo);
1262 #undef CP
1263 #undef C
1264
1265    e_config_domain_save("module.everything-apps", conf_edd, _conf);
1266
1267    /* e_config_save_queue(); */
1268    return 1;
1269 }
1270
1271 static void
1272 _conf_new(void)
1273 {
1274    if (!_conf)
1275      {
1276         _conf = E_NEW(Module_Config, 1);
1277         _conf->version = (MOD_CONFIG_FILE_EPOCH << 16);
1278      }
1279
1280 #define IFMODCFG(v) if ((_conf->version & 0xffff) < v) {
1281 #define IFMODCFGEND }
1282
1283    /* setup defaults */
1284    IFMODCFG(0x009d);
1285    _conf->cmd_terminal = eina_stringshare_add("/usr/bin/xterm");
1286    _conf->cmd_sudo = eina_stringshare_add("/usr/bin/gksudo --preserve-env");
1287    IFMODCFGEND;
1288
1289    _conf->version = MOD_CONFIG_FILE_VERSION;
1290
1291    /* e_config_save_queue(); */
1292 }
1293
1294 static void
1295 _conf_free(void)
1296 {
1297    if (!_conf) return;
1298
1299    IF_RELEASE(_conf->cmd_sudo);
1300    IF_RELEASE(_conf->cmd_terminal);
1301    E_FREE(_conf);
1302 }
1303
1304 static void
1305 _conf_init(E_Module *m)
1306 {
1307    char title[4096];
1308
1309    snprintf(title, sizeof(title), "%s: %s", _("Everything Plugin"), _("Applications"));
1310    
1311    e_configure_registry_item_add("launcher/everything-apps", 110, title,
1312                                  NULL, _module_icon, _conf_dialog);
1313
1314    conf_edd = E_CONFIG_DD_NEW("Module_Config", Module_Config);
1315
1316 #undef T
1317 #undef D
1318 #define T Module_Config
1319 #define D conf_edd
1320    E_CONFIG_VAL(D, T, version, INT);
1321    E_CONFIG_VAL(D, T, cmd_terminal, STR);
1322    E_CONFIG_VAL(D, T, cmd_sudo, STR);
1323 #undef T
1324 #undef D
1325
1326    _conf = e_config_domain_load("module.everything-apps", conf_edd);
1327
1328
1329    if (_conf && !e_util_module_config_check
1330        (_("Everything Applications"), _conf->version,
1331         MOD_CONFIG_FILE_EPOCH, MOD_CONFIG_FILE_VERSION))
1332      _conf_free();
1333
1334    _conf_new();
1335    _conf->module = m;
1336 }
1337
1338 static void
1339 _conf_shutdown(void)
1340 {
1341    e_configure_registry_item_del("launcher/everything-apps");
1342    
1343    _conf_free();
1344
1345    E_CONFIG_DD_FREE(conf_edd);
1346 }
1347
1348 /***************************************************************************/
1349
1350 Eina_Bool
1351 evry_plug_apps_init(E_Module *m)
1352 {
1353    _conf_init(m);
1354
1355    EVRY_MODULE_NEW(evry_module, evry, _plugins_init, _plugins_shutdown);
1356
1357    /* taken from e_exebuf.c */
1358    exelist_exe_edd = E_CONFIG_DD_NEW("E_Exe", E_Exe);
1359 #undef T
1360 #undef D
1361 #define T E_Exe
1362 #define D exelist_exe_edd
1363    E_CONFIG_VAL(D, T, path, STR);
1364
1365    exelist_edd = E_CONFIG_DD_NEW("E_Exe_List", E_Exe_List);
1366 #undef T
1367 #undef D
1368 #define T E_Exe_List
1369 #define D exelist_edd
1370    E_CONFIG_LIST(D, T, list, exelist_exe_edd);
1371
1372    return EINA_TRUE;
1373 }
1374
1375 void
1376 evry_plug_apps_shutdown(void)
1377 {
1378    EVRY_MODULE_FREE(evry_module);
1379
1380    _conf_shutdown();
1381
1382    E_CONFIG_DD_FREE(exelist_edd);
1383    E_CONFIG_DD_FREE(exelist_exe_edd);
1384 }
1385
1386 void
1387 evry_plug_apps_save(void)
1388 {
1389    e_config_domain_save("module.everything-apps", conf_edd, _conf);
1390 }
1391
1392 /***************************************************************************/
1393
1394
1395 /* taken from e_exebuf.c */
1396 static Eina_Bool
1397 _scan_idler(void *data __UNUSED__)
1398 {
1399    struct stat st;
1400    struct dirent *dp;
1401    char *dir;
1402    char buf[4096];
1403
1404    /* no more path items left - stop scanning */
1405    if (!exe_path)
1406      {
1407         Eina_List *l, *l2;
1408         E_Exe_List *el;
1409         E_Exe *ee;
1410         int different = 0;
1411
1412         /* FIXME: check wheter they match or not */
1413         for (l = exe_list, l2 = exe_list2; l && l2; l = l->next, l2 = l2->next)
1414           {
1415              if (strcmp(l->data, l2->data))
1416                {
1417                   different = 1;
1418                   break;
1419                }
1420           }
1421         if ((l) || (l2)) different = 1;
1422         if (exe_list2)
1423           {
1424              while (exe_list)
1425                {
1426                   free(eina_list_data_get(exe_list));
1427                   exe_list = eina_list_remove_list(exe_list, exe_list);
1428                }
1429              exe_list = exe_list2;
1430              exe_list2 = NULL;
1431           }
1432         if (different)
1433           {
1434              el = calloc(1, sizeof(E_Exe_List));
1435              if (el)
1436                {
1437                   el->list = NULL;
1438                   for (l = exe_list; l; l = l->next)
1439                     {
1440                        ee = malloc(sizeof(E_Exe));
1441                        if (ee)
1442                          {
1443                             ee->path = eina_stringshare_add(l->data);
1444                             el->list = eina_list_append(el->list, ee);
1445                          }
1446                     }
1447                   e_config_domain_save("exebuf_exelist_cache", exelist_edd, el);
1448                   while (el->list)
1449                     {
1450                        ee = eina_list_data_get(el->list);
1451                        eina_stringshare_del(ee->path);
1452                        free(ee);
1453                        el->list = eina_list_remove_list(el->list, el->list);
1454                     }
1455                   free(el);
1456                }
1457           }
1458         exe_scan_idler = NULL;
1459         return ECORE_CALLBACK_CANCEL;
1460      }
1461    /* no dir is open - open the first path item */
1462    if (!exe_dir)
1463      {
1464         dir = exe_path->data;
1465         exe_dir = opendir(dir);
1466      }
1467    /* if we have an opened dir - scan the next item */
1468    if (exe_dir)
1469      {
1470         dir = exe_path->data;
1471
1472         dp = readdir(exe_dir);
1473         if (dp)
1474           {
1475              if ((strcmp(dp->d_name, ".")) && (strcmp(dp->d_name, "..")))
1476                {
1477                   snprintf(buf, sizeof(buf), "%s/%s", dir, dp->d_name);
1478                   if ((stat(buf, &st) == 0) &&
1479                       ((!S_ISDIR(st.st_mode)) &&
1480                        (!access(buf, X_OK))))
1481                     {
1482                        if (!exe_list)
1483                          exe_list = eina_list_append(exe_list, strdup(dp->d_name));
1484                        else
1485                          exe_list2 = eina_list_append(exe_list2, strdup(dp->d_name));
1486                     }
1487                }
1488           }
1489         else
1490           {
1491              /* we reached the end of a dir - remove the dir at the head
1492               * of the path list so we advance and next loop we will pick up
1493               * the next item, or if null- abort
1494               */
1495              closedir(exe_dir);
1496              exe_dir = NULL;
1497              free(eina_list_data_get(exe_path));
1498              exe_path = eina_list_remove_list(exe_path, exe_path);
1499           }
1500      }
1501    /* obviously the dir open failed - so remove the first path item */
1502    else
1503      {
1504         free(eina_list_data_get(exe_path));
1505         exe_path = eina_list_remove_list(exe_path, exe_path);
1506      }
1507    /* we have more scannign to do */
1508    return ECORE_CALLBACK_RENEW;
1509 }
1510
1511 static void
1512 _scan_executables()
1513 {
1514    /* taken from exebuf module */
1515    char *path, *pp, *last;
1516    E_Exe_List *el;
1517
1518    el = e_config_domain_load("exebuf_exelist_cache", exelist_edd);
1519    if (el)
1520      {
1521         E_Exe *ee;
1522
1523         EINA_LIST_FREE(el->list, ee)
1524           {
1525              exe_list = eina_list_append(exe_list, strdup(ee->path));
1526              eina_stringshare_del(ee->path);
1527              free(ee);
1528           }
1529         free(el);
1530      }
1531    path = getenv("PATH");
1532    if (path)
1533      {
1534         path = strdup(path);
1535         last = path;
1536         for (pp = path; pp[0]; pp++)
1537           {
1538              if (pp[0] == ':') pp[0] = '\0';
1539              if (pp[0] == 0)
1540                {
1541                   exe_path = eina_list_append(exe_path, strdup(last));
1542                   last = pp + 1;
1543                }
1544           }
1545         if (pp > last)
1546           exe_path = eina_list_append(exe_path, strdup(last));
1547         free(path);
1548      }
1549
1550    exe_scan_idler = ecore_idler_add(_scan_idler, NULL);
1551 }