1 /*
2  * Copyright (C) 2002-2011 Edscott Wilson Garcia
3  * EMail: edscott@imp.mx
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program;
17  */
18 
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include "rodent.h"
25 #include "rfm_modules.h"
26 
27 /* this should be first 2 lines after headers: */
28 G_MODULE_EXPORT LIBRFM_MODULE
29 
30 #define MODULE_NAME "dotdesktop"
31 #define SUBMODULE_NAME "dotdesktop"
32 #define MODULE_LABEL _("Application Launcher...")
33 //#define MODULE_ICON_ID "xffm/places_folder-system"
34 #define MODULE_ENTRY_TIP _("Next-generation application launcher.")
35 
36 #include "module-skeleton.h"
37 // Skeleton definitions
38 
RFM_MODULE_PAUSE(NULL)39 G_MODULE_EXPORT RFM_MODULE_PAUSE(NULL)
40 G_MODULE_EXPORT RFM_MONITOR_RELOAD(NULL)
41 
42 G_MODULE_EXPORT RFM_BLOCKING_LOAD(TRUE)
43 G_MODULE_EXPORT RFM_MODULE_NAME
44 G_MODULE_EXPORT RFM_SUBMODULE_NAME
45 G_MODULE_EXPORT RFM_MODULE_LABEL
46 //G_MODULE_EXPORT RFM_MODULE_ICON_ID
47 G_MODULE_EXPORT RFM_MODULE_ENTRY_TIP
48 
49 //Not applicable:
50 //G_MODULE_EXPORT RFM_MODULE_PREFERENCES_KEY("RODENT-DOTD");
51 G_MODULE_EXPORT RFM_IS_ROOT_MODULE(TRUE)
52 //Superceded:
53 //G_MODULE_EXPORT RFM_PLUGIN_INFO(_(""))
54 G_MODULE_EXPORT RFM_MODULE_ACTIVE(TRUE)
55 G_MODULE_EXPORT RFM_MODULE_MONITOR(TRUE)
56 //
57 G_MODULE_EXPORT RFM_IS_SELECTABLE(TRUE)
58 
59 #include "dotdesktop-module.i"
60 
61 
62 G_MODULE_EXPORT
63 void *
64 module_icon_id(void){
65     // this is the module root item
66     static gchar *icon=NULL;
67     if (icon == NULL){
68 	icon = g_strdup_printf("%s/pixmaps/rodent-dotdesktop.svg", PACKAGE_DATA_DIR);
69     }
70     return icon;
71 }
72 
73 ////////////// General gmodule initialization function
74 
75 G_MODULE_EXPORT void *
g_module_check_init(GModule * module)76 g_module_check_init (GModule * module) {
77     BIND_TEXT_DOMAIN;
78     //NOOP("dotdesktop init... \n");
79     if (!glob_signal) rfm_cond_init(glob_signal);
80 
81     if(!glob_mutex) {
82 	rfm_mutex_init(glob_mutex);
83     }
84 
85     icon_hash = g_hash_table_new_full (g_str_hash, g_str_equal,g_free, g_free);
86     icon_exec_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
87     category_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
88 
89     reverse_string_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
90     string_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
91 
92     dotdesktop_t *qq=dotdesktop_v;
93     GMutex *string_hash_mutex = get_string_hash_mutex();
94     for (; qq && qq->category; qq++){
95 	if (qq->string != NULL) {
96 	    g_mutex_lock(string_hash_mutex);
97 	    g_hash_table_replace(string_hash, g_strdup(qq->category), g_strdup(qq->string));
98 	    g_mutex_unlock(string_hash_mutex);
99 	}
100     }
101 
102     rfm_view_thread_create(NULL, glob_dir_f, NULL, "glob_dir_f");
103     rfm_view_thread_create(NULL, populate_icon_hash_f, NULL, "populate_icon_hash_f");
104     rfm_view_thread_create(NULL, populate_mimetype_hash_f, NULL, "populate_mimetype_hash_f");
105     // This thread requires a termination signal from janitor.
106     rfm_view_thread_create (NULL, monitor_f, NULL, "monitor_f:dotdesktop");
107     return NULL;
108 }
109 
110 G_MODULE_EXPORT void
g_module_unload(GModule * module)111 g_module_unload(GModule * module){
112     unload_module = TRUE;
113     rfm_global_t *rfm_global_p = rfm_global();
114     g_cond_signal(rfm_global_p->status_signal);
115     g_hash_table_destroy(icon_hash);
116     g_hash_table_destroy(icon_exec_hash);
117     g_hash_table_destroy(category_hash);
118     g_hash_table_destroy(reverse_string_hash);
119     g_hash_table_destroy(string_hash);
120 }
121 //
122 //////////////  Generalized Root Module functions ///////////////////
123 //
124 
125 // Plugin functions. Plugin functions may be specified as one of the
126 // following types.
127 //
128 // void:     These functions all return a void pointer
129 //   	     and take no argument
130 // natural:  These functions all return a void pointer
131 // 	     and take a single void pointer argument.
132 // 	     The space of natural functions is isomorphic
133 // 	     to the set of natural numbers.
134 // rational: These functions all return a void pointer
135 // 	     and take a two void pointer arguments.
136 // 	     The space of rational functions is isomorphic
137 // 	     to the set of rational numbers.
138 // complex:  These functions all return a void pointer
139 // 	     and take a three void pointer arguments.
140 // 	     The space of rational functions is isomorphic
141 // 	     to the set of complex numbers with integer
142 // 	     imaginary component.
143 
144 
145 //////////////////////////////////////////////////////////////////////
146 //                          void functions                          //
147 //////////////////////////////////////////////////////////////////////
148 
149 
150 
151 //  gchar *
152 // This function returns a newly allocated string with the general information
153 // of the module. Rodent uses this to construct the popup tip. Returned value
154 // should be freed when no longer used.
155 G_MODULE_EXPORT
156 void *
plugin_info(void)157 plugin_info(void){
158     return g_strdup_printf("%s:\n%s\n",
159 	    _("Browse and run installed applications"),
160 	    _("This program is responsible for launching other applications and provides useful utilities."));
161 }
162 
163 
164 //////////////////////////////////////////////////////////////////////
165 //                        natural functions                         //
166 //////////////////////////////////////////////////////////////////////
167 
168 // gint
169 // This function returns the count of elements in the module's
170 // view. This value is compared with current value to determine
171 // whether a reload is necessary.
172 // Returns READ_ERROR	 on skip unconditional reload
173 // Parameter p is the view's widgets_p.
174 G_MODULE_EXPORT
175 void *
module_count(void * p)176 module_count (void *p) {
177     static gint count=0;
178     widgets_t *widgets_p = p;
179     gint serial=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widgets_p->paper), "dotdesktop_serial"));
180     if (serial == desktop_serial){
181 	return GINT_TO_POINTER (READ_ERROR);
182     }
183     g_object_set_data(G_OBJECT(widgets_p->paper), "dotdesktop_serial", GINT_TO_POINTER(desktop_serial));
184     return GINT_TO_POINTER (count++);
185 }
186 
187 // gboolean
188 // This function processes the double click action on the
189 // icon's label. If it returns FALSE, then the default
190 // Rodent action for label click is performed. The default
191 // Rodent action checks for file existance to do a rename
192 // entry (or symlink or duplicate).
193 // Parameter p is the item's entry pointer.
194 G_MODULE_EXPORT
label_click(void * p)195 void *label_click (void *p) {
196     // Do nothing.
197     record_entry_t *en=p;
198     if (en && en->mimetype && strcmp(en->mimetype, "application/x-desktop")==0){
199 	// do default Rodent action (rename/duplicate/link)
200 	// nah! Problem with updating, since items are not currently
201 	//      monitored
202 	// return NULL;
203     }
204     // Do nothing
205     return  GINT_TO_POINTER(1);
206 }
207 
208 
209 // const gchar *
210 // This function returns a const pointer to the entry's icon
211 // identifier.
212 // Parameter p is the item's entry pointer.
213 // Identifier may be returned as a mimetype or an absolute path.
214 G_MODULE_EXPORT
item_icon_id(void * p)215 void *item_icon_id(void *p){
216     return dotdesktop_item_icon_id(p);
217 }
218 
219 //  gchar *
220 // This function returns a newly allocated string with the general information
221 // of the entry (parameter p). Rodent uses this to construct the popup tip.
222 // Returned value should be freed when no longer used.
223 G_MODULE_EXPORT
224 void *
item_entry_tip(void * p)225 item_entry_tip(void *p){
226     return dotdesktop_entry_tip(p);
227 }
228 
229 // gboolean
230 // This function informs Rodent if the entry (parameter p) is a
231 // selectable icon or not.
232 /*G_MODULE_EXPORT
233 void *
234 is_selectable(void *p){
235     record_entry_t *en=(record_entry_t *)p;
236     if (rfm_g_file_test(en->path, G_FILE_TEST_EXISTS)){
237 	return GINT_TO_POINTER(1);
238     }
239     return NULL;
240 }*/
241 
242 // gboolean
243 // This function informs Rodent if the entry (parameter p) is a
244 // valid drop site. Drop may be processed by Rodent or by the
245 // module if process_drop() is defined.
246 G_MODULE_EXPORT
247 void *
valid_drop_site(void * p)248 valid_drop_site(void *p){
249     record_entry_t *en=p;
250     if (en && en->mimetype && strcmp(en->mimetype, "application/x-desktop")==0){
251 	// Dotdesktop files will be valid drop sites.
252 	return GINT_TO_POINTER(1);
253     }
254     // Anything else is not a valid drop site.
255     return NULL;
256 }
257 
258 
259 // gboolean
260 // This function fills in previously allocated xfdir_p
261 // with glob records and entries of the module population.
262 // Records which are filled in are:
263 // xfdir_p->pathc: Number of icons for Rodent to display
264 // xfdir_p->gl[0 ... pathc-1].pathv: Labels to display with each icon
265 // xfdir_p->gl[0 ... pathc-1].en: Record_entry_t of each icon
266 // 				  (NULL entries will point to Rodent root)
267 G_MODULE_EXPORT
268 void *
module_xfdir_get(void * p)269 module_xfdir_get (void *p) {
270     xfdir_t *xfdir_p = p;
271     return private_get_xfdir(xfdir_p);
272 
273 }
274 
275 // gboolean
276 // This is a dotdesktop specific function.
277 // The function informs Rodent if the execution of the dotdesktop
278 // file is to be done in a terminal emulator.
279 // This flag is set within the dotdesktop file.
280 G_MODULE_EXPORT
exec_in_terminal(void * p)281 void *exec_in_terminal(void *p){
282     gchar *path=p;
283     gboolean in_terminal=get_desktop_bool("Terminal", path);
284     return (GINT_TO_POINTER(in_terminal));
285 }
286 
287 // gchar *
288 // This is a dotdesktop specific function.
289 // This function returns a newly allocated string with the "Exec" command
290 // of the dotdesktop file. Rodent uses this command to spawn a
291 // background process.
292 // Returned value should be freed when no longer used.
293 // Parameter p is the path to the file.
294 // This variable is set within the dotdesktop file.
295 G_MODULE_EXPORT
item_exec(void * p)296 void *item_exec(void *p){
297     gchar *path=p;
298     if (!path) return NULL;
299     gchar *exec=get_desktop_string("Exec", path);
300     return (void *) exec;
301 }
302 
303 // gchar *
304 // This is a dotdesktop specific function.
305 // This function returns a newly allocated string with the "Name"
306 // of the dotdesktop file. Rodent uses this command to create
307 // popup menu item for files with "aaplication/x-desktop" mimetype.
308 //
309 // Returned value should be freed when no longer used.
310 // Parameter p is the path to the file.
311 // This variable is set within the dotdesktop file.
312 G_MODULE_EXPORT
item_name(void * p)313 void *item_name(void *p){
314     gchar *path=p;
315     if (!path) return NULL;
316     gchar *name=get_desktop_string("Name", path);
317     return (void *) name;
318 }
319 
320 // gchar *
321 // This is a dotdesktop specific function.
322 // This function returns a newly allocated string with the "TryExec" command
323 // of the dotdesktop file. Rodent uses this command if "Exec" is not found
324 // in the dotdesktop file to spawn a background process.
325 // Returned value should be freed when no longer used.
326 // This variable is set within the dotdesktop file.
327 G_MODULE_EXPORT
item_tryexec(void * p)328 void *item_tryexec(void *p){
329     gchar *path=p;
330     gchar *exec=get_desktop_string("TryExec", path);
331     return (void *) exec;
332 }
333 
334 // const gchar
335 // This function is used to get the icon associated to
336 // a particular exec defined in any one of the globbed
337 // dot desktop files.
338 G_MODULE_EXPORT
339 void *
get_exec_icon(void * p)340 get_exec_icon(void *p)
341 {
342     GMutex *exec_hash_mutex = get_exec_hash_mutex();
343     const gchar *exec = p;
344     gchar *hash_key = get_hash_key(exec);
345     g_mutex_lock(exec_hash_mutex);
346     const gchar *iconpath = g_hash_table_lookup (icon_exec_hash, hash_key);
347     g_mutex_unlock(exec_hash_mutex);
348     g_free(hash_key);
349     return (void *)iconpath;
350 }
351 
352 //////////////////////////////////////////////////////////////////////
353 //                        rational functions                        //
354 //////////////////////////////////////////////////////////////////////
355 // gboolean
356 // This function is used to hide popup menu elements of
357 // Rodent's standard icon area popup menu.
358 // Parameter p is the view's widgets_p pointer.
359 // Parameter q is the icon's record entry.
360 G_MODULE_EXPORT
361 void *
hide_local_menu_items(void * p,void * q)362 hide_local_menu_items (void *p, void *q) {
363     return dotdesktop_hide_local_menu_items(p, q);
364 }
365 
366 // gboolean
367 // This function informs Rodent by returning TRUE that the double click
368 // action will be processed by the module. If function returns FALSE
369 // (or is not defined in the module) then Rodent will attempt the default
370 // double click action.
371 // Parameter p is the view's widgets_p pointer.
372 // Parameter q is the icon's record entry.
373 G_MODULE_EXPORT
374 void *
double_click(void * p,void * q)375 double_click(void *p, void *q){
376     return dotdesktop_double_click(p, q);
377 }
378 
379 // gboolean
380 // This function is to generate a module specific popup menu, either on
381 // icon click or on void space click .
382 // If this function will generate a popup menu, return value should be TRUE,
383 // otherwise return FALSE so Rodent will generate the default popup menu.
384 // Popup menu for modules should be set as view data on the paper object
385 // identified by "private_popup_menu".
386 // Parameter p is the view's widgets_p pointer.
387 // Parameter q is the icon's record entry.
388 G_MODULE_EXPORT
389 void *
private_popup(void * p,void * q)390 private_popup(void *p, void *q){
391     if (!q) return NULL;
392     // no popup menu for module items...
393     record_entry_t *en = q;
394     gchar *text;
395     if (rfm_g_file_test(en->path, G_FILE_TEST_EXISTS)){
396 	gchar *name=get_desktop_string("Name",en->path);
397 	gchar *generic_name=get_desktop_string("GenericName",en->path);
398 	gchar *exec=get_desktop_string("Exec",en->path);
399 	gchar *comment=get_desktop_string("Comment",en->path);
400 	gboolean in_term=get_desktop_bool("Terminal",en->path);
401 
402 	text = g_strconcat(
403 		"<big><b>",name,"</b></big>\n",
404 		(generic_name)?"<i>(":"",
405 		(generic_name)?generic_name:"",
406 		(generic_name)?")</i>\n\n":"",
407 		(comment)?comment:"",
408 		(comment)?"\n\n":"",
409 		_("Command to run when clicked:"), " ", exec, "\n\n",
410 		_("Terminal application"), ": ", (in_term)?_("Yes"):_("No"),
411 		NULL);
412 	g_free(name);
413 	g_free(generic_name);
414 	g_free(exec);
415 	g_free(comment);
416     } else {
417         text = g_strdup_printf("<big><b>%s</b></big>\n\n%s  <b><i>%s</i></b>",
418 	    MODULE_ENTRY_TIP, _("Group"), (en->tag)?en->tag:en->path);
419     }
420     rfm_confirm(p, GTK_MESSAGE_INFO, text, NULL, NULL);
421     g_free(text);
422     return GINT_TO_POINTER(1);
423 }
424 
425 // gchar *
426 // This function returns a newly allocated string which shall
427 // be used by Rodent to complement the initial text which
428 // appears in the status area (lp command area).
429 // Returned value should be freed when no longer used.
430 // Parameter p is the view's widgets_p pointer.
431 // Parameter q is the icon's record entry.
432 G_MODULE_EXPORT
433 void *
sizetag(void * p,void * q)434 sizetag(void *p, void *q){
435     //widgets_t *widgets_p=p;
436     record_entry_t *en=q;
437     if (en && en->st == NULL){
438 	return g_strdup(_("List of categories"));
439     } else {
440 	return g_strdup(_("Browse and run installed applications"));
441     }
442 }
443 
444 //////////////////////////////////////////////////////////////////////
445 //                        complex functions                         //
446 //////////////////////////////////////////////////////////////////////
447 
448 // gboolean
449 // This function is used by Rodent to process a drop to a module defined
450 // icon. Drop is only processed if valid_drop_site() returned TRUE
451 // for the drop target entry beforehand.
452 // Parameter p is the view's widgets_p pointer.
453 // Parameter q is path of the drop target entry.
454 // Parameter r is a GList * that contains the drop element paths.
455 G_MODULE_EXPORT
456 void *
process_drop(void * p,void * q,void * r)457 process_drop(void *p, void *q, void *r){
458     widgets_t *widgets_p=p;
459     gchar *path=q;
460     GList *list=r;
461     execute_dot_desktop(widgets_p, path, list);
462     return GINT_TO_POINTER(1);
463 }
464 
465 static void *
thread_applications_menu_f(gpointer data)466 thread_applications_menu_f(gpointer data){
467     NOOP(stderr,"thread_applications_menu now\n");
468 
469     const gchar *parent_id = "applications_menu";
470     const gchar *menu_id = "applications_menu_menu";
471     GtkWidget *parent=rfm_get_widget(parent_id);
472     GtkWidget *popup_widget=rfm_get_widget(menu_id);
473     GtkWidget *old_popup= popup_widget;
474     g_object_ref(old_popup);
475 
476     popup_widget = gtk_menu_new ();
477     gtk_widget_show(popup_widget);
478     rfm_set_widget(popup_widget, menu_id);
479     gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), popup_widget);
480     g_object_unref(old_popup);
481     GtkWidget *v = gtk_menu_item_new_with_mnemonic ("rodent-dotdesktop");
482     gtk_widget_show (v);
483     gtk_container_add (GTK_CONTAINER (popup_widget), v);
484     gtk_widget_set_sensitive (v, FALSE);
485     NOOP(stderr,"populate_submenu now...\n");
486     return popup_widget;
487 }
488 
489 static void *
thread_applications_menu(gpointer data)490 thread_applications_menu(gpointer data){
491     GtkWidget *w = rfm_context_function(thread_applications_menu_f, data);
492     rodent_thread_add_menu_separator (w, NULL);
493     populate_submenu(w);
494     return w;
495 }
496 
497 
498 
499 // GtkWidget *
500 // This returns the menu for a nondetached menu
501 // Parameter p is the view's widgets_p pointer.
502 // Parameter q is the parent for the menu widget
503 //   (i.e., the spot where it will be attached)
504 //
505 
506 G_MODULE_EXPORT
507 void *
dotdesktop_nondetached_menu(void * p,void * q)508 dotdesktop_nondetached_menu(void *p, void *q){
509     NOOP(stderr, "dotdesktop_nondetached_menu now... desktop_serial=%d\n", desktop_serial);
510     if (rfm_get_gtk_thread() != g_thread_self()){
511 	DBG("dotdesktop_nondetached_menu is a main callback function only\n");
512 	return GINT_TO_POINTER(1);
513     }
514     const gchar *menu_id = "applications_menu_menu";
515     GtkWidget *popup_widget=rfm_get_widget(menu_id);
516 
517     static gint serial=-1;
518     if (popup_widget==NULL){
519 	DBG("dotdesktop_nondetached_menu: this is wrong...\n");
520 	return NULL;
521     }
522 
523     if (serial != desktop_serial) {
524 	serial = desktop_serial;
525 	rfm_view_thread_create(NULL, thread_applications_menu, NULL, "thread_applications_menu:dotdesktop");
526     }
527 
528     return GINT_TO_POINTER(1);
529     //return popup_widget;
530 }
531 
532