1 #ifdef COPYRIGHT_INFORMATION
2 #include "gplv3.h"
3 #endif
4 /* this file is included by rodent_popup.c */
5 /*
6 * Copyright (C) 2002-2012 Edscott Wilson Garcia
7 * EMail: edscott@users.sf.net
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program;
22 */
23 typedef struct keybind_t {
24 guint key;
25 guint mask;
26 gpointer callback;
27 } keybind_t;
28
29 static gchar *auto_C_name[] = {AUTO_C_NAME};
30
31 static int
32 set_auto_command (int j,
33 const gchar * name,
34 const gchar * alt_label,
35 const gchar * app,
36 const gchar * path
37 );
38
39
40 //static void gui_add_to_keylist (guint in_key, guint mask, gpointer callback);
41
42
43 ////////////////////////////////////////////////////////////////////////////////////////
44 // rodent_do_popup guts (callback function)
45 ////////////////////////////////////////////////////////////////////////////////////////
46
47 static void
popup_autostuff(record_entry_t * en)48 popup_autostuff (record_entry_t * en) {
49 if (!en) return;
50 gint j = 0;
51
52 NOOP(stderr, "...popup_autostuff\n");
53
54
55 // Special "open in new window" item for partition types
56 // FIXME
57 // XXX: This should probably be done by the fstab module, not here.
58 if(FSTAB_is_partition_type (en)) {
59 NOOP(stderr, "test FSTAB_is_partition_type (en)...\n");
60 // only first item (open in new window...)
61 gchar *mount_point = FSTAB_get_mnt_dir (en->path);
62 if(mount_point) {
63 j = set_auto_command ( j, auto_C_name[j],
64 _("Open in New Window"), "rodent-fm", mount_point);
65 g_free (mount_point);
66 }
67 }
68 // Special run command for dot desktop files.
69 // XXX: as above, this should probably be done by the module...
70 else if(IS_LOCAL_TYPE(en->type) &&
71 en->mimetype && strcmp(en->mimetype, "application/x-desktop")==0 ) {
72 if (rfm_void(PLUGIN_DIR, "dotdesktop", "module_active")){
73 // get the Icon
74 const gchar *Icon=rfm_natural(PLUGIN_DIR, "dotdesktop", (void *)en, "item_icon_id");
75 // get the Name
76 gchar *Name=rfm_natural(PLUGIN_DIR, "dotdesktop", en->path, "item_name");
77 GtkWidget *a =
78 rfm_get_widget ("autotype_Prun");
79 if (Name) {
80
81 GtkWidget *a;
82 a = rfm_get_widget ("autotype_Prun");
83 gchar *q = rfm_utf_string (Name);
84 rfm_replace_menu_label(a, q);
85 /*label = gtk_bin_get_child (GTK_BIN (a));
86 gtk_label_set_text ((GtkLabel *) label, q);*/
87 g_free (q);
88 g_object_set_data(G_OBJECT(a), "record_entry", (void *)en);
89 }
90 if (Icon) {
91 rfm_replace_menu_image(a, Icon);
92 }
93 if (Icon && Name){
94 SHOW_IT ( "autotype_Prun");
95 }
96 g_free (Name);
97 }
98 }
99 // Executable files get the special "run in terminal" menu item:
100 else if(IS_EXE_TYPE(en->type)) {
101 if (IS_SDIR(en->type)) {
102 // ignore
103 } else {
104 gchar *cmd;
105
106 GtkWidget *a = rfm_get_widget ("autotype_Prun");
107 g_object_set_data(G_OBJECT(a), "record_entry", (void *)en);
108 cmd = g_strdup_printf ("%s (%s)", _("Run in terminal window"), _("Is executable"));
109 gchar *q = rfm_utf_string (cmd);
110 rfm_replace_menu_label(a, q);
111 g_free (cmd);
112 g_free (q);
113
114 rfm_replace_menu_image(a, "xffm/emblem_terminal");
115 SHOW_IT ( "autotype_Prun");
116 }
117 }
118
119
120 // Mimetype associations.
121 // These mimetype associations are defined in the file
122 // "applications-module.xml" and harvested from
123 // installed dot desktop files by the dotdesktop module.
124 //
125 // Associations should be done to the basic mimetype
126 // (not any alias) for sanity sake.
127 //
128 // This is the nitty gritty.
129 // Suggest an application based on the file's mimetype.
130 //
131 NOOP(stderr, "AUTO: menu nitty-gritty now\n");
132 gchar *magic_type = NULL;
133 gchar *mime_type = NULL;
134 if (IS_LOCAL_TYPE(en->type) && 1) {
135 magic_type = rfm_rational(RFM_MODULE_DIR, "mime", (void *)en, "mime_magic", "mime_function");
136 if (!en->mimemagic) magic_type = g_strdup(_("unknown"));
137 }
138 else magic_type = g_strdup(_("unknown"));
139 // G_FILE_TEST_EXISTS is a downer on remote connections
140 if (g_path_is_absolute(en->path)) {
141 mime_type = MIME_type (en->path, en->st);
142 }
143
144 // These are the menu items set from dotdesktop files
145 // and "applications-module.xml"
146
147 if(g_path_is_absolute(en->path)) {
148 int k;
149 NOOP(stderr, "AUTO: menu nitty-gritty mime-type=%s magic-type=%s\n", mime_type, magic_type);
150
151 // here we use the freedesktop and magic
152 // mimetype to get the run program.
153 // We loop to get commands based on simple mimetype and magic mimetype,
154 // in that order.
155 for(k = 0; k < 2; k++) {
156 gchar **apps = NULL;
157 int i = 0;
158 if(k == 0) {
159 apps = MIME_apps (mime_type);
160 NOOP(stderr, "AUTO: menu nitty-gritty (%s) apps=0x%lx\n", mime_type, (long)apps);
161 } else {
162 apps = MIME_apps (magic_type);
163 NOOP(stderr, "AUTO: menu nitty-gritty (%s) apps=0x%lx\n", magic_type, (long)apps);
164 }
165
166 if(apps && apps[i])
167 for(; apps && apps[i]; i++) {
168 NOOP ("AUTO: j=%d, i=%d %s -> %s\n", j, i, auto_C_name[j], apps[i]);fflush(NULL);
169
170 if(!auto_C_name[j]) {
171 // no menuitem spaces left.
172 continue;
173 }
174 if (!strlen(apps[i])) {
175 // bug trap, just in case an empty string slips past.
176 continue;
177 }
178 j = set_auto_command ( j, auto_C_name[j], NULL,
179 // the command:
180 apps[i], en->path);
181 }
182 g_strfreev (apps);
183 }
184
185 }
186
187 NOOP(stderr, "AUTO: editor test...\n");
188 gboolean has_editor=FALSE;
189 gint k;
190 gchar **apps = MIME_apps (mime_type);
191 for (k=0; k<j && apps && apps[k]; k++){
192 const gchar **editors = rfm_get_editors();
193 for (; editors && *editors; editors++){
194 if (strstr(apps[k], *editors)) {
195 has_editor=TRUE;
196 break;
197 }
198 if (has_editor) break;
199 }
200 }
201 g_strfreev (apps);
202 if (!has_editor){
203 // Default editor command.
204 gchar *text_editor = rodent_get_text_editor(en);
205 TRACE( "Got text editor: %s\n", text_editor);
206 if (text_editor){
207 j = set_auto_command ( j,
208 auto_C_name[j], NULL,
209 // the command:
210 text_editor, en->path);
211 g_free(text_editor);
212 }
213 }
214
215
216 if(g_path_is_absolute(en->path)) {
217 SHOW_IT ( "open_with_menuitem");
218 SHOW_IT ( "open_with_separator");
219 }
220 NOOP(stderr, "AUTO: done!\n");
221
222 g_free (magic_type);
223 g_free (mime_type);
224 return;
225 }
226
227
228 ////////////////////////////////////////////////////////////////////////////////////////
229
230 /**************************************************************************/
231 /************************** pasteboard stuff ****************************/
232 /**************************************************************************/
233
234 /* */
235
236 /**************************************************************************/
237
238 ///// popup callbacks....
239
240 /* this file is included by rodent_popup.c */
241
242
243 static void
setup_bookmark_menuitem(record_entry_t * en,const gchar * menuitem,gboolean on)244 setup_bookmark_menuitem(record_entry_t *en,
245 const gchar *menuitem,
246 gboolean on){
247 GtkWidget *w=rfm_get_widget( menuitem);
248 if (!w) {
249 DBG("no %s widget!\n", menuitem);
250 return;
251 }
252
253 gchar *basename=g_path_get_basename(en->path);
254 gchar *baseutf=rfm_utf_string (basename);
255 g_free(basename);
256 gchar *text=g_strdup_printf("%s: <b><i>%s</i></b>",
257 (on)?_("Add bookmark"):_("Remove bookmark"),
258 baseutf);
259 g_free(baseutf);
260 NOOP(stderr, "...setup_bookmark_menuitem\n");
261 gchar *q = rfm_utf_string (text);
262 g_free (text);
263 rfm_replace_menu_label(w, q);
264
265 /*GtkWidget *label = gtk_bin_get_child (GTK_BIN (w));
266 gtk_label_set_markup ((GtkLabel *) label, q);*/
267 g_free (q);
268
269 SHOW_IT (menuitem);
270 gchar *path = g_object_get_data(G_OBJECT(w), "path");
271 g_free(path);
272 g_object_set_data(G_OBJECT(w), "path", g_strdup(en->path));
273 NOOP("setting path to %s\n", en->path);
274 //FIXME:
275 #if 0
276 // I don't like this way of determine expose region.
277 // Should do this in the callback, finding the appropriate
278 // population_p from path information.
279 if (population_p) {
280 GdkRectangle *rect=g_object_get_data(G_OBJECT(w), "rect");
281 if (!rect) rect=(GdkRectangle *)malloc(sizeof(GdkRectangle));
282 if (rfm_get_population_icon_rect(view_p, population_p, rect)) {
283 g_object_set_data(G_OBJECT(w), "rect", rect);
284 } else {
285 g_object_set_data(G_OBJECT(w), "rect", NULL);
286 }
287 } else {
288 g_object_set_data(G_OBJECT(w), "rect", NULL);
289 }
290 #endif
291
292 }
293
294
295
296 /////////////////////////////////////////////77
297 ////// rodent_menu
298
299 /*************************** specific commands to menu ***************/
300
301 /****************** gtk functions for callbacks *********************/
302 // show_mount: shows the menu mount item if fstab module available
303 static void
show_mount_item(view_t * view_p)304 show_mount_item (view_t *view_p) {
305 if(rfm_void (PLUGIN_DIR, "fstab", "is_root_module") == NULL) return;
306 if(g_slist_length (view_p->selection_list) != 1) return;
307 record_entry_t *en = view_p->selection_list->data;
308 if (!en || !en->path) return;
309
310 gint mounted = FALSE;
311 gboolean in_fstab = FALSE;
312 gboolean partition = FSTAB_is_partition_type (en);
313 if(partition || IS_SDIR(en->type)) {
314 mounted = FSTAB_entry_is_mounted (en);
315 in_fstab = FSTAB_is_in_fstab (en->path);
316 }
317 gboolean isofs =
318 (en->mimetype &&
319 strstr(en->mimetype, "application/x-cd-image")) ||
320 (en->mimemagic &&
321 strstr(en->mimemagic, "application/x-cd-image"));
322 // This does not good. Mount volume will be /dev/loopX
323 if (isofs) {
324 mounted = FSTAB_entry_is_mounted (en);
325 }
326
327
328 NOOP ("POPUPx %s is mounted=%d\n", en->path, mounted);
329 gboolean do_mount_item = mounted || in_fstab || partition || isofs;
330 if(do_mount_item) {
331 if(mounted>0){ SHOW_IT ( "unmountP");}
332 else if (mounted < 0){ SHOW_IT ( "mount_broken");}
333 else { SHOW_IT ( "mountP");}
334 }
335 }
336
337 static void
show_remove_item(view_t * view_p)338 show_remove_item (view_t *view_p) {
339 record_entry_t *en = view_p->selection_list->data;
340 if(!en || !en->path) return;
341 if(IS_UP_TYPE(en->type))return;
342 /* for local remove: */
343 if(g_path_is_absolute(en->path)) SHOW_IT ( "remove_menuitem");
344 }
345
346 static void
show_properties_item(view_t * view_p)347 show_properties_item (view_t *view_p) {
348 if (!rfm_void(RFM_MODULE_DIR, "properties", "module_active")) return;
349 record_entry_t *en = view_p->selection_list->data;
350 if(!en || !en->path) return;
351 if (!g_path_is_absolute (en->path)) return;
352 SHOW_IT ( "properties_menuitem");
353 return;
354 }
355
356
357 static void
recursive_dirname(widgets_t * widgets_p,char * path,int level)358 recursive_dirname (widgets_t * widgets_p, char *path, int level) {
359 if(!path || strcmp (path, "/") == 0)
360 return;
361 if(level >= DEEPEST_DIR_MENU_LEVELS)
362 return;
363 gchar *b = g_path_get_dirname (path);
364 gchar *name = g_strdup_printf ("level-%d", level);
365
366 GtkWidget *a = rfm_get_widget (name);
367 NOOP(stderr, "...recursive_dirname\n");
368 gchar *q = rfm_utf_string (b);
369 rfm_replace_menu_label(a, q);
370 /*GtkWidget *label = gtk_bin_get_child (GTK_BIN (a));
371 gtk_label_set_text ((GtkLabel *) label, q);*/
372 g_free (q);
373 gchar *old_b;
374 if((old_b = g_object_get_data (G_OBJECT (a), "path")) != NULL) {
375 g_object_set_data (G_OBJECT (a), "path", NULL);
376 g_free (old_b);
377 }
378 g_object_set_data (G_OBJECT (a), "path", (gpointer) b);
379 SHOW_IT ( name);
380
381 recursive_dirname (widgets_p, b, level + 1);
382 g_free (name);
383 return;
384 }
385
386 #define MENU_IN_MODULE(x) (view_p->module && rfm_void(PLUGIN_DIR,view_p->module,(x)))
387
388 static void
show_open_items(view_t * view_p)389 show_open_items (view_t *view_p) {
390 record_entry_t *en = view_p->selection_list->data;
391 if(IS_UP_TYPE(en->type))return;
392
393 // file_menu is already shown by here...
394 SHOW_IT("select_menu");
395 SHOW_IT("open_with_menuitem");
396 SHOW_IT("copy_menuitem");
397 SHOW_IT("cut_menuitem");
398 HIDE_IT ("newfile_menuitem");
399 HIDE_IT ("newdirectory_menuitem");
400
401 if (en->mimetype &&
402 rfm_void(PLUGIN_DIR, "dotdesktop", "module_active") &&
403 strcmp(en->mimetype, "application/x-desktop")==0 )
404 {
405 NOOP ("showing autotype_Prun\n");
406 SHOW_IT ( "autotype_Prun");
407 }
408
409
410 return;
411 }
412
413
414 ////////////////////////////////////////////////
415 //// menu.i
416
417
418 static void
clean_object_data(GtkWidget * a,const gchar * data_id)419 clean_object_data(GtkWidget *a, const gchar *data_id)
420 {
421 gchar *old_text = g_object_get_data (G_OBJECT (a), data_id);
422 if((old_text = g_object_get_data (G_OBJECT (a), data_id)) != NULL) {
423 g_object_set_data (G_OBJECT (a), data_id, NULL);
424 g_free (old_text);
425 }
426 }
427
428 static int
set_auto_command(int j,const gchar * name,const gchar * alt_label,const gchar * app,const gchar * path)429 set_auto_command (int j,
430 const gchar * name,
431 const gchar * alt_label,
432 const gchar * app,
433 const gchar * path
434 ) {
435 widgets_t *widgets_p = rfm_get_widget ("widgets_p");
436 int jj;
437
438 gchar *dirname = g_path_get_dirname (path);
439 gchar *basename = g_path_get_basename (path);
440
441 GtkWidget *a = rfm_get_widget (name);
442
443 const gchar * output_ext=MIME_command_output_ext(app);
444 view_t *view_p=widgets_p->view_p;
445 if(!MIME_is_valid_command (app)) {
446 if (strcmp(app, "rodent-newtab")==0 && view_p->tab_constructor) {
447 NOOP (">> set_auto_command: %s name=%s\n", app, name);
448 }
449 else if (strcmp(app, "rodent-newwin")==0) {
450 NOOP (">> set_auto_command: %s name=%s\n", app, name);
451 }
452 else if (strcmp(app, "rodent-bcrypt")==0) {
453 NOOP (">> set_auto_command: %s name=%s\n", app, name);
454 }
455 else {
456 return j;
457 }
458 }
459
460 gchar *command;
461 if(strncmp (app, "sudo -A ", strlen ("sudo -A ")) == 0) {
462 if(getuid () == 0)
463 return j; /* don't sudoize root */
464 if(!strlen (app + strlen ("sudo -A "))
465 || !MIME_is_valid_command (app + strlen ("sudo -A ")))
466 return j;
467 }
468
469 TRACE( "looking for command icon \"%s\"\n",app);
470 gchar *app_no_args = g_strdup(app);
471 if (strchr(app_no_args, ' ')) *strchr(app_no_args, ' ')=0;
472 const gchar *icon_id=(const gchar *)
473 rfm_natural(PLUGIN_DIR, "dotdesktop", (void *)app_no_args, "get_exec_icon");
474
475 // try custom mime icon
476 if (!icon_id){
477 icon_id=MIME_command_icon (app_no_args);
478 NOOP("icon id= %s, %s\n", icon_id, app_no_args);
479 }
480
481 // try named icon
482 gchar *icon=NULL;
483 if (!icon_id) {
484 NOOP(stderr, "try named icon for %s\n", app_no_args);
485 GdkPixbuf *pix = rfm_get_pixbuf (app_no_args, SMALL_ICON_SIZE); //refs
486
487 // This should always be local and absolute.
488 if (pix){
489 icon_id = icon;
490 g_object_unref(pix);
491 }
492 }
493 g_free(app_no_args);
494
495 if (!icon_id) {
496 NOOP("2. command icon \"%s\" not found!\n",app);
497 icon_id="xffm/stock_execute";
498 }
499
500 if (icon_id) {
501 rfm_replace_menu_image(a, icon_id);
502 }
503 g_free(icon);
504
505
506 gchar *effective_app = NULL;
507
508 NOOP(stderr, "...set_auto_command\n");
509 //label = gtk_bin_get_child (GTK_BIN (a));
510 if(alt_label) {
511 gchar *new_label = rfm_utf_string (alt_label);
512 rfm_replace_menu_label(a, new_label);
513 g_free(new_label);
514 } else {
515 /* allow mixed utf8-eucjp paths */
516 gchar *new_label;
517 {
518 const gchar *text = MIME_command_text (app);
519 if (!text && !strstr(app, " %s")){
520 // If app does not end with a %s, try with %s
521 effective_app = g_strdup_printf("%s %%s", app);
522 text = MIME_command_text (effective_app);
523 if (text) app = effective_app;
524 } else if (!text && *(strstr(app, " %s")+strlen(" %s"))==0){
525 // If app does end with a %s, try without it.
526 effective_app = g_strdup(app);
527 if (strstr(effective_app, " %s")) *strstr(effective_app, " %s") = 0;
528 text = MIME_command_text (effective_app);
529 if (text) app = effective_app;
530 }
531 const gchar *text2 = MIME_command_text2 (app);
532 TRACE( "set_auto_command(): looking for %s in hash: %s %s\n",app, text, text2);
533 gchar *q;
534 if (text) {
535 gchar *texto;
536 if (text2){
537 texto=g_strconcat(_(text), _(text2), NULL);
538 } else {
539 texto=g_strdup(_(text));
540 }
541 NOOP("MIME: found\n");
542 if (strstr(texto,"%s")!=NULL) {
543 q = MIME_mk_command_line (texto, basename);
544 } else {
545 if (output_ext)
546 q = g_strdup_printf("%s (%s)", texto, output_ext);
547 else
548 q = g_strdup(texto);
549 }
550 g_free(texto);
551 }
552 else {
553 NOOP("MIME: not found\n");
554 q = MIME_mk_command_line (app, basename);
555 }
556 new_label = rfm_utf_string (q);
557 g_free (q);
558 }
559 rfm_replace_menu_label(a, new_label);
560 //gtk_label_set_text ((GtkLabel *) label, new_label);
561 g_free (new_label);
562 }
563
564 if(output_ext) {
565 NOOP ("command is: %s output_ext is; %s\n", app, output_ext);
566 command = g_strdup (app);
567 } else {
568 /* this is to use relative paths for arguments for all
569 * commands except those where the output directory (or
570 * widgets_p->workdir) is requested from the user.
571 * */
572 NOOP ("making command from (%s, %s)\n", app, path);
573 command = MIME_mk_command_line (app, path);
574 }
575
576 /* check for duplicates */
577 {
578 gint argcp1;
579 gchar **argvp1;
580 GError *error = NULL;
581 if(!g_shell_parse_argv ((const gchar *)command, &argcp1, &argvp1, &error)) {
582 g_free (command);
583 g_error_free (error);
584 g_strfreev (argvp1);
585 NOOP ("!g_shell_parse_argv at popup_autostuff\n");
586 return j;
587 }
588 for(jj = 0; jj < j; jj++) {
589 gboolean is_duplicate = TRUE;
590 gint ac,
591 argcp2;
592 gchar **argvp2;
593 gchar *set_command = g_object_get_data (G_OBJECT (rfm_get_widget (auto_C_name[jj])),
594 "command");
595 NOOP ("AUTO: j=%d: %s == %s\n", jj, set_command, command);
596 if(!g_shell_parse_argv ((const gchar *)set_command, &argcp2, &argvp2, &error)) {
597 g_free (basename);
598 g_free (command);
599 g_error_free (error);
600 g_strfreev (argvp2);
601 NOOP ("!g_shell_parse_argv at popup_autostuff\n");
602 return j;
603 }
604 if(argcp2 != argcp1) {
605 is_duplicate = FALSE;
606 } else
607 for(ac = 0; ac < argcp2; ac++) {
608 if(strcmp (argvp2[ac], argvp1[ac])) {
609 is_duplicate = FALSE;
610 break;
611 }
612 }
613 NOOP ("0x%lx, 0x%lx\n", (unsigned long)argvp1, (unsigned long)argvp2);
614
615 g_strfreev (argvp2);
616 argvp2 = NULL;
617 /*if (strcmp(set_command,command)==0) */
618 if(is_duplicate) {
619 g_free (basename);
620 g_free (command);
621 NOOP ("AUTO: is_duplicate at popup_autostuff\n");
622 return j;
623 }
624 } /* end for */
625 g_strfreev (argvp1);
626 argvp1 = NULL;
627 }
628 /* OK, by now we know the command is not duplicated... */
629 // But is it a valid command??
630
631
632 /* now let's free up any left over data from previous
633 * generated menus... */
634 clean_object_data(a, "command");
635 clean_object_data(a, "workdir");
636 g_object_set_data (G_OBJECT (a), "command", (gpointer) command);
637 g_object_set_data (G_OBJECT (a), "workdir", (gpointer) dirname);
638 if(MIME_command_output (app)) {
639 g_object_set_data (G_OBJECT (a), "querypath", (gpointer)_("Specify Output Directory..."));
640 } else {
641 g_object_set_data (G_OBJECT (a), "querypath", NULL);
642 }
643
644 if(output_ext) {
645 g_object_set_data (G_OBJECT (a), "output_arg", g_strdup (path));
646 } else {
647 clean_object_data(a, "output_arg");
648 g_object_set_data (G_OBJECT (a), "output_arg", NULL);
649 }
650 NOOP(stderr, "AUTO:command=%s, output_arg=%s\n", command, path);
651 g_object_set_data (G_OBJECT (a), "output_ext", (gpointer) output_ext);
652 NOOP ("AUTO:now showing %s, active widget=0x%lx\n", name, (unsigned long)widgets_p->paper);
653 SHOW_IT ( name);
654 j++;
655 g_free (basename);
656 g_free (effective_app);
657 return j;
658 }
659
660
661
662
663