1 /*
2 * This file is part of YAD.
3 *
4 * YAD is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * YAD is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with YAD. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Copyright (C) 2008-2019, Victor Ananjevsky <ananasik@gmail.com>
18 */
19
20 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE 1
22 #endif
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30
31 #include <sys/ipc.h>
32 #include <sys/shm.h>
33
34 #include "yad.h"
35
36 const YadStock yad_stock_items[] = {
37 { "yad-about", N_("About"), "help-about" },
38 { "yad-add", N_("Add"), "list-add" },
39 { "yad-apply", N_("Apply"), "gtk-apply" },
40 { "yad-cancel", N_("Cancel"), "gtk-cancel" },
41 { "yad-clear", N_("Clear"), "document-clear" },
42 { "yad-close", N_("Close"), "window-close" },
43 { "yad-edit", N_("Edit"), "gtk-edit" },
44 { "yad-execute", N_("Execute"), "system-run" },
45 { "yad-no", N_("No"), "gtk-no" },
46 { "yad-ok", N_("OK"), "gtk-ok" },
47 { "yad-open", N_("Open"), "document-open" },
48 { "yad-print", N_("Print"), "document-print" },
49 { "yad-quit", N_("Quit"), "application-exit" },
50 { "yad-refresh", N_("Refresh"), "view-refresh" },
51 { "yad-remove", N_("Remove"), "list-remove" },
52 { "yad-save", N_("Save"), "document-save" },
53 { "yad-search", N_("Search"), "system-search" },
54 { "yad-settings", N_("Settings"), "gtk-preferences" },
55 { "yad-yes", N_("Yes"), "gtk-yes" }
56 };
57
58 gboolean
stock_lookup(gchar * key,YadStock * it)59 stock_lookup (gchar *key, YadStock *it)
60 {
61 gint i;
62 gboolean found = FALSE;
63
64 if (key == NULL || strncmp (key, "yad-", 4) != 0)
65 return FALSE;
66
67 for (i = 0; i < YAD_STOCK_COUNT; i++)
68 {
69 if (strcmp (key, yad_stock_items[i].key) == 0)
70 {
71 it->key = yad_stock_items[i].key;
72 it->label = _(yad_stock_items[i].label);
73 it->icon = yad_stock_items[i].icon;
74 found = TRUE;
75 break;
76 }
77 }
78
79 return found;
80 }
81
82 GdkPixbuf *
get_pixbuf(gchar * name,YadIconSize size,gboolean force)83 get_pixbuf (gchar *name, YadIconSize size, gboolean force)
84 {
85 gint w, h;
86 GdkPixbuf *pb = NULL;
87 GError *err = NULL;
88
89 if (size == YAD_BIG_ICON)
90 gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &w, &h);
91 else
92 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
93
94 if (g_file_test (name, G_FILE_TEST_EXISTS))
95 {
96 pb = gdk_pixbuf_new_from_file (name, &err);
97 if (!pb)
98 {
99 g_printerr ("yad: get_pixbuf(): %s\n", err->message);
100 g_error_free (err);
101 }
102 }
103 else
104 pb = gtk_icon_theme_load_icon (yad_icon_theme, name, MIN (w, h), GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL);
105
106 if (!pb)
107 {
108 if (size == YAD_BIG_ICON)
109 pb = g_object_ref (big_fallback_image);
110 else
111 pb = g_object_ref (small_fallback_image);
112 }
113
114 /* force scaling image to specific size */
115 if (!options.data.keep_icon_size && force && pb)
116 {
117 gint iw = gdk_pixbuf_get_width (pb);
118 gint ih = gdk_pixbuf_get_height (pb);
119
120 if (w != iw || h != ih)
121 {
122 GdkPixbuf *spb;
123 spb = gdk_pixbuf_scale_simple (pb, w, h, GDK_INTERP_BILINEAR);
124 g_object_unref (pb);
125 pb = spb;
126 }
127 }
128
129 return pb;
130 }
131
132 gchar *
get_color(GdkRGBA * c)133 get_color (GdkRGBA *c)
134 {
135 gchar *res = NULL;
136
137 switch (options.color_data.mode)
138 {
139 case YAD_COLOR_HEX:
140 if (options.color_data.alpha)
141 res = g_strdup_printf ("#%02X%02X%02X%02X", (int) (c->red * 255), (int) (c->green * 255), (int) (c->blue * 255), (int) (c->alpha * 255));
142 else
143 res = g_strdup_printf ("#%02X%02X%02X", (int) (c->red * 255), (int) (c->green * 255), (int) (c->blue * 255));
144 break;
145 case YAD_COLOR_RGB:
146 res = gdk_rgba_to_string (c);
147 break;
148 }
149 return res;
150 }
151
152 void
update_preview(GtkFileChooser * chooser,GtkWidget * p)153 update_preview (GtkFileChooser * chooser, GtkWidget *p)
154 {
155 gchar *uri;
156 static gchar *normal_path = NULL;
157 static gchar *large_path = NULL;
158
159 /* init thumbnails path */
160 if (!normal_path)
161 normal_path = g_build_filename (g_get_user_cache_dir (), "thumbnails", "normal", NULL);
162 if (!large_path)
163 large_path = g_build_filename (g_get_user_cache_dir (), "thumbnails", "large", NULL);
164
165 /* load preview */
166 uri = gtk_file_chooser_get_preview_uri (chooser);
167 if (uri)
168 {
169 gchar *file;
170 GChecksum *chs;
171 GdkPixbuf *pb = NULL;
172
173 chs = g_checksum_new (G_CHECKSUM_MD5);
174 g_checksum_update (chs, (const guchar *) uri, -1);
175 /* first try to get preview from large thumbnail */
176 file = g_strdup_printf ("%s/%s.png", large_path, g_checksum_get_string (chs));
177 if (options.common_data.large_preview && g_file_test (file, G_FILE_TEST_EXISTS))
178 pb = gdk_pixbuf_new_from_file (file, NULL);
179 else
180 {
181 /* try to get preview from normal thumbnail */
182 g_free (file);
183 file = g_strdup_printf ("%s/%s.png", normal_path, g_checksum_get_string (chs));
184 if (!options.common_data.large_preview && g_file_test (file, G_FILE_TEST_EXISTS))
185 pb = gdk_pixbuf_new_from_file (file, NULL);
186 else
187 {
188 /* try to create it */
189 g_free (file);
190 file = g_filename_from_uri (uri, NULL, NULL);
191 if (options.common_data.large_preview)
192 pb = gdk_pixbuf_new_from_file_at_scale (file, 256, 256, TRUE, NULL);
193 else
194 pb = gdk_pixbuf_new_from_file_at_scale (file, 128, 128, TRUE, NULL);
195 if (pb)
196 {
197 struct stat st;
198 gchar *smtime;
199
200 stat (file, &st);
201 smtime = g_strdup_printf ("%d", st.st_mtime);
202 g_free (file);
203
204 /* save thumbnail */
205 if (options.common_data.large_preview)
206 {
207 g_mkdir_with_parents (large_path, 0755);
208 file = g_strdup_printf ("%s/%s.png", large_path, g_checksum_get_string (chs));
209 }
210 else
211 {
212 g_mkdir_with_parents (normal_path, 0755);
213 file = g_strdup_printf ("%s/%s.png", normal_path, g_checksum_get_string (chs));
214 }
215 gdk_pixbuf_save (pb, file, "png", NULL,
216 "tEXt::Thumb::URI", uri,
217 "tEXt::Thumb::MTime", smtime,
218 NULL);
219 g_free (smtime);
220 }
221 }
222 }
223 g_checksum_free (chs);
224 g_free (file);
225
226 if (pb)
227 {
228 gtk_image_set_from_pixbuf (GTK_IMAGE (p), pb);
229 g_object_unref (pb);
230 gtk_file_chooser_set_preview_widget_active (chooser, TRUE);
231 }
232 else
233 gtk_file_chooser_set_preview_widget_active (chooser, FALSE);
234
235 g_free (uri);
236 }
237 else
238 gtk_file_chooser_set_preview_widget_active (chooser, FALSE);
239 }
240
241 gchar **
split_arg(const gchar * str)242 split_arg (const gchar * str)
243 {
244 gchar **res;
245 gchar *p_col;
246
247 res = g_new0 (gchar *, 3);
248
249 p_col = g_strrstr (str, ":");
250 if (p_col && p_col[1])
251 {
252 res[0] = g_strndup (str, p_col - str);
253 res[1] = g_strdup (p_col + 1);
254 }
255 else
256 res[0] = g_strdup (str);
257
258 return res;
259 }
260
261 YadNTabs *
get_tabs(key_t key,gboolean create)262 get_tabs (key_t key, gboolean create)
263 {
264 YadNTabs *t = NULL;
265 int shmid, i, max_tab;
266
267 /* get shared memory */
268 #ifndef STANDALONE
269 max_tab = g_settings_get_int (settings, "max-tab") + 1;
270 #else
271 max_tab = MAX_TABS + 1;
272 #endif
273 if (create)
274 {
275 if ((shmid = shmget (key, max_tab * sizeof (YadNTabs), IPC_CREAT | IPC_EXCL | 0644)) == -1)
276 {
277 g_printerr ("yad: cannot create shared memory for key %d: %s\n", key, strerror (errno));
278 return NULL;
279 }
280 }
281 else
282 {
283 if ((shmid = shmget (key, max_tab * sizeof (YadNTabs), 0)) == -1)
284 {
285 if (errno != ENOENT)
286 g_printerr ("yad: cannot get shared memory for key %d: %s\n", key, strerror (errno));
287 return NULL;
288 }
289 }
290
291 /* attach shared memory */
292 if ((t = shmat (shmid, NULL, 0)) == (YadNTabs *) -1)
293 {
294 g_printerr ("yad: cannot attach shared memory for key %d: %s\n", key, strerror (errno));
295 return NULL;
296 }
297
298 /* initialize memory */
299 if (create)
300 {
301 for (i = 1; i < max_tab; i++)
302 {
303 t[i].pid = -1;
304 t[i].xid = 0;
305 }
306 t[0].pid = shmid;
307 /* lastly, allow plugs to write shmem */
308 t[0].xid = 1;
309 }
310
311 return t;
312 }
313
314 GtkWidget *
get_label(gchar * str,guint border,GtkWidget * w)315 get_label (gchar *str, guint border, GtkWidget *w)
316 {
317 GtkWidget *t, *i, *l;
318 YadStock it;
319 gchar **vals;
320
321 if (!str || !*str)
322 return gtk_label_new (NULL);
323
324 l = i = NULL;
325
326 t = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
327 gtk_container_set_border_width (GTK_CONTAINER (t), border);
328
329 gtk_widget_set_halign (t, GTK_ALIGN_CENTER);
330 gtk_widget_set_valign (t, GTK_ALIGN_CENTER);
331
332 vals = g_strsplit_set (str, options.common_data.item_separator, 3);
333 if (stock_lookup (vals[0], &it))
334 {
335 l = gtk_label_new_with_mnemonic (it.label);
336 i = gtk_image_new_from_pixbuf (get_pixbuf (it.icon, YAD_SMALL_ICON, TRUE));
337 }
338 else
339 {
340 if (vals[0] && *vals[0])
341 {
342 l = gtk_label_new (NULL);
343 if (!options.data.no_markup)
344 gtk_label_set_markup_with_mnemonic (GTK_LABEL (l), vals[0]);
345 else
346 gtk_label_set_text_with_mnemonic (GTK_LABEL (l), vals[0]);
347 }
348
349 if (vals[1] && *vals[1])
350 i = gtk_image_new_from_pixbuf (get_pixbuf (vals[1], YAD_SMALL_ICON, TRUE));
351 }
352
353 if (i)
354 gtk_container_add (GTK_CONTAINER (t), i);
355
356 if (l)
357 {
358 if (w)
359 gtk_label_set_mnemonic_widget (GTK_LABEL (l), w);
360 gtk_label_set_xalign (GTK_LABEL (l), 0.0);
361 gtk_box_pack_start (GTK_BOX (t), l, FALSE, FALSE, 1);
362 }
363
364 /* !!! must check both 1 and 2 values for !NULL */
365 if (vals[1] && vals[2] && *vals[2])
366 {
367 if (!options.data.no_markup)
368 gtk_widget_set_tooltip_markup (t, vals[2]);
369 else
370 gtk_widget_set_tooltip_text (t, vals[2]);
371 }
372
373 g_strfreev (vals);
374
375 gtk_widget_show_all (t);
376
377 return t;
378 }
379
380 gchar *
escape_str(gchar * str)381 escape_str (gchar *str)
382 {
383 gchar *res, *buf = str;
384 guint i = 0, len;
385
386 if (!str)
387 return NULL;
388
389 len = strlen (str);
390 res = (gchar *) calloc (len * 2 + 1, sizeof (gchar));
391
392 while (*buf)
393 {
394 switch (*buf)
395 {
396 case '\n':
397 strcpy (res + i, "\\n");
398 i += 2;
399 break;
400 case '\t':
401 strcpy (res + i, "\\t");
402 i += 2;
403 break;
404 case '\\':
405 strcpy (res + i, "\\\\");
406 i += 2;
407 break;
408 default:
409 *(res + i) = *buf;
410 i++;
411 break;
412 }
413 buf++;
414 }
415 res[i] = '\0';
416
417 return res;
418 }
419
420 gchar *
escape_char(gchar * str,gchar ch)421 escape_char (gchar *str, gchar ch)
422 {
423 gchar *res, *buf = str;
424 guint i = 0, len;
425
426 if (!str)
427 return NULL;
428
429 len = strlen (str);
430 res = (gchar *) calloc (len * 2 + 1, sizeof (gchar));
431
432 while (*buf)
433 {
434 if (*buf == ch)
435 {
436 strcpy (res + i, "\\\"");
437 i += 2;
438 }
439 else
440 {
441 *(res + i) = *buf;
442 i++;
443 }
444 buf++;
445 }
446 res[i] = '\0';
447
448 return res;
449 }
450
451 gboolean
check_complete(GtkEntryCompletion * c,const gchar * key,GtkTreeIter * iter,gpointer data)452 check_complete (GtkEntryCompletion *c, const gchar *key, GtkTreeIter *iter, gpointer data)
453 {
454 gchar *value = NULL;
455 GtkTreeModel *model = gtk_entry_completion_get_model (c);
456 gboolean found = FALSE;
457
458 if (!model || !key || !key[0])
459 return FALSE;
460
461 gtk_tree_model_get (model, iter, 0, &value, -1);
462
463 if (value)
464 {
465 gchar **words = NULL;
466 guint i = 0;
467
468 switch (options.common_data.complete)
469 {
470 case YAD_COMPLETE_ANY:
471 words = g_strsplit_set (key, " \t", -1);
472 while (words[i])
473 {
474 if (strcasestr (value, words[i]) != NULL)
475 {
476 /* found one of the words */
477 found = TRUE;
478 break;
479 }
480 i++;
481 }
482 break;
483 case YAD_COMPLETE_ALL:
484 words = g_strsplit_set (key, " \t", -1);
485 found = TRUE;
486 while (words[i])
487 {
488 if (strcasestr (value, words[i]) == NULL)
489 {
490 /* not found one of the words */
491 found = FALSE;
492 break;
493 }
494 i++;
495 }
496 break;
497 case YAD_COMPLETE_REGEX:
498 found = g_regex_match_simple (key, value, G_REGEX_CASELESS | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY);
499 break;
500 default: ;
501 }
502
503 if (words)
504 g_strfreev (words);
505 }
506
507 return found;
508 }
509
510 void
parse_geometry()511 parse_geometry ()
512 {
513 gchar *geom, *ptr;
514 gint w = -1, h = -1, x = 0, y = 0;
515 gboolean usexy = FALSE;
516 guint i = 0;
517
518 if (!options.data.geometry)
519 return;
520
521 geom = options.data.geometry;
522
523 if (geom[i] != '+' && geom[i] != '-')
524 {
525 ptr = geom + i;
526 w = atoi (ptr);
527
528 while (geom[i] && geom[i] != 'x') i++;
529
530 if (!geom[i])
531 return;
532
533 ptr = geom + i + 1;
534 h = atoi (ptr);
535
536 while (geom[i] && geom[i] != '-' && geom[i] != '+') i++;
537 }
538
539 if (geom[i])
540 {
541 usexy = TRUE;
542
543 ptr = geom + i;
544 x = atoi (ptr);
545
546 i++;
547 while (geom[i] && geom[i] != '-' && geom[i] != '+') i++;
548
549 if (!geom[i])
550 return;
551
552 ptr = geom + i;
553 y = atoi (ptr);
554 }
555
556 if (w != -1)
557 options.data.width = w;
558 if (h != -1)
559 options.data.height = h;
560 options.data.posx = x;
561 options.data.posy = y;
562 options.data.use_posx = options.data.use_posy = usexy;
563 }
564
565 gboolean
get_bool_val(gchar * str)566 get_bool_val (gchar *str)
567 {
568 if (!str || !str[0])
569 return FALSE;
570
571 switch (str[0])
572 {
573 case '1':
574 case 't':
575 case 'T':
576 case 'y':
577 case 'Y':
578 if (strcasecmp (str, "t") == 0 || strcasecmp (str, "true") == 0 ||
579 strcasecmp (str, "y") == 0 || strcasecmp (str, "yes") == 0 ||
580 strcmp (str, "1") == 0)
581 return TRUE;
582 break;
583 case '0':
584 case 'f':
585 case 'F':
586 case 'n':
587 case 'N':
588 if (strcasecmp (str, "f") == 0 || strcasecmp (str, "false") == 0 ||
589 strcasecmp (str, "n") == 0 || strcasecmp (str, "no") == 0 ||
590 strcmp (str, "0") == 0)
591 return FALSE;
592 break;
593 case 'o':
594 case 'O':
595 if (strcasecmp (str, "on") == 0)
596 return TRUE;
597 else if (strcasecmp (str, "off") == 0)
598 return FALSE;
599 break;
600 default: ;
601 }
602
603 g_printerr ("yad: wrong boolean value '%s'\n", str);
604 return FALSE;
605 }
606
607 gchar *
print_bool_val(gboolean val)608 print_bool_val (gboolean val)
609 {
610 gchar *ret = "";
611
612 switch (options.common_data.bool_fmt)
613 {
614 case YAD_BOOL_FMT_UT:
615 ret = val ? "TRUE" : "FALSE";
616 break;
617 case YAD_BOOL_FMT_UY:
618 ret = val ? "YES" : "NO";
619 break;
620 case YAD_BOOL_FMT_UO:
621 ret = val ? "ON" : "OFF";
622 break;
623 case YAD_BOOL_FMT_LT:
624 ret = val ? "true" : "false";
625 break;
626 case YAD_BOOL_FMT_LY:
627 ret = val ? "yes" : "no";
628 break;
629 case YAD_BOOL_FMT_LO:
630 ret = val ? "on" : "off";
631 break;
632 case YAD_BOOL_FMT_1:
633 ret = val ? "1" : "0";
634 break;
635 }
636
637 return ret;
638 }
639
640 gint
run_command_sync(gchar * cmd,gchar ** out)641 run_command_sync (gchar *cmd, gchar **out)
642 {
643 gint ret = 0;
644 gchar *full_cmd = NULL;
645 GError *err = NULL;
646
647 if (options.data.use_interp)
648 {
649 if (g_strstr_len (options.data.interp, -1, "%s") != NULL)
650 full_cmd = g_strdup_printf (options.data.interp, cmd);
651 else
652 full_cmd = g_strdup_printf ("%s %s", options.data.interp, cmd);
653 }
654 else
655 full_cmd = g_strdup (cmd);
656
657 if (!g_spawn_command_line_sync (full_cmd, out, NULL, &ret, &err))
658 {
659 if (options.debug)
660 g_printerr (_("WARNING: Run command failed: %s\n"), err->message);
661 g_error_free (err);
662 }
663
664 g_free (full_cmd);
665 return ret;
666 }
667
668 void
run_command_async(gchar * cmd)669 run_command_async (gchar *cmd)
670 {
671 gchar *full_cmd = NULL;
672 GError *err = NULL;
673
674 if (options.data.use_interp)
675 {
676 if (g_strstr_len (options.data.interp, -1, "%s") != NULL)
677 full_cmd = g_strdup_printf (options.data.interp, cmd);
678 else
679 full_cmd = g_strdup_printf ("%s %s", options.data.interp, cmd);
680 }
681 else
682 full_cmd = g_strdup (cmd);
683
684 if (!g_spawn_command_line_async (full_cmd, &err))
685 {
686 if (options.debug)
687 g_printerr (_("WARNING: Run command failed: %s\n"), err->message);
688 g_error_free (err);
689 }
690
691 g_free (full_cmd);
692 }
693
694 gchar *
pango_to_css(gchar * font)695 pango_to_css (gchar *font)
696 {
697 PangoFontDescription *desc;
698 PangoFontMask mask;
699 GString *str;
700 gchar *res;
701
702 str = g_string_new (NULL);
703
704 desc = pango_font_description_from_string (font);
705 mask = pango_font_description_get_set_fields (desc);
706
707 if (mask & PANGO_FONT_MASK_STYLE)
708 {
709 switch (pango_font_description_get_style (desc))
710 {
711 case PANGO_STYLE_OBLIQUE:
712 g_string_append (str, "oblique ");
713 break;
714 case PANGO_STYLE_ITALIC:
715 g_string_append (str, "italic ");
716 break;
717 }
718 }
719 if (mask & PANGO_FONT_MASK_VARIANT)
720 {
721 if (pango_font_description_get_variant (desc) == PANGO_VARIANT_SMALL_CAPS)
722 g_string_append (str, "small-caps ");
723 }
724
725 if (mask & PANGO_FONT_MASK_WEIGHT)
726 {
727 switch (pango_font_description_get_weight (desc))
728 {
729 case PANGO_WEIGHT_THIN:
730 g_string_append (str, "Thin ");
731 break;
732 case PANGO_WEIGHT_ULTRALIGHT:
733 g_string_append (str, "Ultralight ");
734 break;
735 case PANGO_WEIGHT_LIGHT:
736 g_string_append (str, "Light ");
737 break;
738 case PANGO_WEIGHT_SEMILIGHT:
739 g_string_append (str, "Semilight ");
740 break;
741 case PANGO_WEIGHT_BOOK:
742 g_string_append (str, "Book ");
743 break;
744 case PANGO_WEIGHT_MEDIUM:
745 g_string_append (str, "Medium ");
746 break;
747 case PANGO_WEIGHT_SEMIBOLD:
748 g_string_append (str, "Semibold ");
749 break;
750 case PANGO_WEIGHT_BOLD:
751 g_string_append (str, "Bold ");
752 break;
753 case PANGO_WEIGHT_ULTRABOLD:
754 g_string_append (str, "Ultrabold ");
755 break;
756 case PANGO_WEIGHT_HEAVY:
757 g_string_append (str, "Heavy ");
758 break;
759 case PANGO_WEIGHT_ULTRAHEAVY:
760 g_string_append (str, "Ultraheavy ");
761 break;
762 }
763 }
764
765 if (mask & PANGO_FONT_MASK_SIZE)
766 {
767 if (pango_font_description_get_size_is_absolute (desc))
768 g_string_append_printf (str, "%dpx ", pango_font_description_get_size (desc) / PANGO_SCALE);
769 else
770 g_string_append_printf (str, "%dpt ", pango_font_description_get_size (desc) / PANGO_SCALE);
771 }
772
773 if (mask & PANGO_FONT_MASK_FAMILY)
774 g_string_append (str, pango_font_description_get_family (desc));
775
776 if (str->str)
777 res = str->str;
778 else
779 res = g_strdup (font);
780
781 return res;
782 }
783
784 void
open_uri(const gchar * uri)785 open_uri (const gchar *uri)
786 {
787 gchar *cmdline;
788
789 if (!uri || !uri[0])
790 return;
791
792 if (g_strstr_len (options.data.uri_handler, -1, "%s") != NULL)
793 cmdline = g_strdup_printf (options.data.uri_handler, uri);
794 else
795 cmdline = g_strdup_printf ("%s '%s'", options.data.uri_handler, uri);
796 run_command_async (cmdline);
797 g_free (cmdline);
798 }
799
800 #ifdef HAVE_SPELL
801 void
show_langs()802 show_langs ()
803 {
804 const GList *lng;
805
806 for (lng = gspell_language_get_available (); lng; lng = lng->next)
807 {
808 const GspellLanguage *l = lng->data;
809 g_print ("%s\n", gspell_language_get_code (l));
810 }
811 }
812 #endif
813
814 #ifdef HAVE_SOURCEVIEW
815 void
show_themes()816 show_themes ()
817 {
818 GtkSourceStyleSchemeManager *sm;
819 const gchar **si;
820 guint i = 0;
821
822 sm = gtk_source_style_scheme_manager_get_default ();
823 if ((si = (const gchar **) gtk_source_style_scheme_manager_get_scheme_ids (sm)) == NULL)
824 return;
825
826 while (si[i])
827 {
828 GtkSourceStyleScheme *s = gtk_source_style_scheme_manager_get_scheme (sm, si[i]);
829 g_print ("%s\n", gtk_source_style_scheme_get_name (s));
830 i++;
831 }
832 }
833 #endif
834