1 /*
2 * xed-utils.c
3 * This file is part of xed
4 *
5 * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
6 * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi
7 * Copyright (C) 2003-2005 Paolo Maggi
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 /*
26 * Modified by the xed Team, 1998-2005. See the AUTHORS file for a
27 * list of people on the xed Team.
28 * See the ChangeLog files for a list of changes.
29 *
30 * $Id$
31 */
32
33 #include <config.h>
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <glib.h>
41 #include <glib/gi18n.h>
42 #include <gio/gio.h>
43 #include <gtksourceview/gtksource.h>
44
45 #include "xed-utils.h"
46 #include "xed-document.h"
47 #include "xed-debug.h"
48
49 /* For the workspace/viewport stuff */
50 #ifdef GDK_WINDOWING_X11
51 #include <gdk/gdkx.h>
52 #include <X11/Xatom.h>
53 #endif
54
55 #define STDIN_DELAY_MICROSECONDS 100000
56
57 /* FIXME: remove this with gtk 2.12, it has gdk_color_to_string */
58 gchar *
xed_gdk_color_to_string(GdkColor color)59 xed_gdk_color_to_string (GdkColor color)
60 {
61 return g_strdup_printf ("#%04x%04x%04x", color.red, color.green, color.blue);
62 }
63
64 gint
xed_string_to_clamped_gint(const gchar * text)65 xed_string_to_clamped_gint (const gchar *text)
66 {
67 long int long_line = strtol (text, NULL, 10);
68 return CLAMP (long_line, INT_MIN, INT_MAX);
69 }
70
71 /*
72 * n: len of the string in bytes
73 */
74 gboolean
g_utf8_caselessnmatch(const char * s1,const char * s2,gssize n1,gssize n2)75 g_utf8_caselessnmatch (const char *s1,
76 const char *s2,
77 gssize n1,
78 gssize n2)
79 {
80 gchar *casefold;
81 gchar *normalized_s1;
82 gchar *normalized_s2;
83 gint len_s1;
84 gint len_s2;
85 gboolean ret = FALSE;
86
87 g_return_val_if_fail (s1 != NULL, FALSE);
88 g_return_val_if_fail (s2 != NULL, FALSE);
89 g_return_val_if_fail (n1 > 0, FALSE);
90 g_return_val_if_fail (n2 > 0, FALSE);
91
92 casefold = g_utf8_casefold (s1, n1);
93 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
94 g_free (casefold);
95
96 casefold = g_utf8_casefold (s2, n2);
97 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
98 g_free (casefold);
99
100 len_s1 = strlen (normalized_s1);
101 len_s2 = strlen (normalized_s2);
102
103 if (len_s1 < len_s2)
104 {
105 goto finally_2;
106 }
107
108 ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
109
110 finally_2:
111 g_free (normalized_s1);
112 g_free (normalized_s2);
113
114 return ret;
115 }
116
117 /**
118 * xed_utils_set_atk_name_description:
119 * @widget: The Gtk widget for which name/description to be set
120 * @name: Atk name string
121 * @description: Atk description string
122 *
123 * This function sets up name and description
124 * for a specified gtk widget.
125 */
126 void
xed_utils_set_atk_name_description(GtkWidget * widget,const gchar * name,const gchar * description)127 xed_utils_set_atk_name_description (GtkWidget *widget,
128 const gchar *name,
129 const gchar *description)
130 {
131 AtkObject *aobj;
132
133 aobj = gtk_widget_get_accessible (widget);
134
135 if (!(GTK_IS_ACCESSIBLE (aobj)))
136 {
137 return;
138 }
139
140 if (name)
141 {
142 atk_object_set_name (aobj, name);
143 }
144
145 if (description)
146 {
147 atk_object_set_description (aobj, description);
148 }
149 }
150
151 /**
152 * xed_set_atk_relation:
153 * @obj1: specified widget.
154 * @obj2: specified widget.
155 * @rel_type: the type of relation to set up.
156 *
157 * This function establishes atk relation
158 * between 2 specified widgets.
159 */
160 void
xed_utils_set_atk_relation(GtkWidget * obj1,GtkWidget * obj2,AtkRelationType rel_type)161 xed_utils_set_atk_relation (GtkWidget *obj1,
162 GtkWidget *obj2,
163 AtkRelationType rel_type )
164 {
165 AtkObject *atk_obj1, *atk_obj2;
166 AtkRelationSet *relation_set;
167 AtkObject *targets[1];
168 AtkRelation *relation;
169
170 atk_obj1 = gtk_widget_get_accessible (obj1);
171 atk_obj2 = gtk_widget_get_accessible (obj2);
172
173 if (!(GTK_IS_ACCESSIBLE (atk_obj1)) || !(GTK_IS_ACCESSIBLE (atk_obj2)))
174 {
175 return;
176 }
177
178 relation_set = atk_object_ref_relation_set (atk_obj1);
179 targets[0] = atk_obj2;
180
181 relation = atk_relation_new (targets, 1, rel_type);
182 atk_relation_set_add (relation_set, relation);
183
184 g_object_unref (G_OBJECT (relation));
185 }
186
187 void
xed_warning(GtkWindow * parent,const gchar * format,...)188 xed_warning (GtkWindow *parent,
189 const gchar *format,
190 ...)
191 {
192 va_list args;
193 gchar *str;
194 GtkWidget *dialog;
195 GtkWindowGroup *wg = NULL;
196
197 g_return_if_fail (format != NULL);
198
199 if (parent != NULL)
200 {
201 wg = gtk_window_get_group (parent);
202 }
203
204 va_start (args, format);
205 str = g_strdup_vprintf (format, args);
206 va_end (args);
207
208 dialog = gtk_message_dialog_new_with_markup (parent,
209 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
210 GTK_MESSAGE_ERROR,
211 GTK_BUTTONS_OK,
212 "%s", str);
213
214 g_free (str);
215
216 if (wg != NULL)
217 {
218 gtk_window_group_add_window (wg, GTK_WINDOW (dialog));
219 }
220
221 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
222
223 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
224
225 g_signal_connect (G_OBJECT (dialog), "response",
226 G_CALLBACK (gtk_widget_destroy), NULL);
227
228 gtk_widget_show (dialog);
229 }
230
231 /*
232 * Doubles underscore to avoid spurious menu accels.
233 */
234 gchar *
xed_utils_escape_underscores(const gchar * text,gssize length)235 xed_utils_escape_underscores (const gchar* text,
236 gssize length)
237 {
238 GString *str;
239 const gchar *p;
240 const gchar *end;
241
242 g_return_val_if_fail (text != NULL, NULL);
243
244 if (length < 0)
245 {
246 length = strlen (text);
247 }
248
249 str = g_string_sized_new (length);
250
251 p = text;
252 end = text + length;
253
254 while (p != end)
255 {
256 const gchar *next;
257 next = g_utf8_next_char (p);
258
259 switch (*p)
260 {
261 case '_':
262 g_string_append (str, "__");
263 break;
264 default:
265 g_string_append_len (str, p, next - p);
266 break;
267 }
268
269 p = next;
270 }
271
272 return g_string_free (str, FALSE);
273 }
274
275 /* the following functions are taken from eel */
276
277 static gchar *
xed_utils_str_truncate(const gchar * string,guint truncate_length,gboolean middle)278 xed_utils_str_truncate (const gchar *string,
279 guint truncate_length,
280 gboolean middle)
281 {
282 GString *truncated;
283 guint length;
284 guint n_chars;
285 guint num_left_chars;
286 guint right_offset;
287 guint delimiter_length;
288 const gchar *delimiter = "\342\200\246";
289
290 g_return_val_if_fail (string != NULL, NULL);
291
292 length = strlen (string);
293
294 g_return_val_if_fail (g_utf8_validate (string, length, NULL), NULL);
295
296 /* It doesnt make sense to truncate strings to less than
297 * the size of the delimiter plus 2 characters (one on each
298 * side)
299 */
300 delimiter_length = g_utf8_strlen (delimiter, -1);
301 if (truncate_length < (delimiter_length + 2))
302 {
303 return g_strdup (string);
304 }
305
306 n_chars = g_utf8_strlen (string, length);
307
308 /* Make sure the string is not already small enough. */
309 if (n_chars <= truncate_length)
310 {
311 return g_strdup (string);
312 }
313
314 /* Find the 'middle' where the truncation will occur. */
315 if (middle)
316 {
317 num_left_chars = (truncate_length - delimiter_length) / 2;
318 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
319
320 truncated = g_string_new_len (string, g_utf8_offset_to_pointer (string, num_left_chars) - string);
321 g_string_append (truncated, delimiter);
322 g_string_append (truncated, g_utf8_offset_to_pointer (string, right_offset));
323 }
324 else
325 {
326 num_left_chars = truncate_length - delimiter_length;
327 truncated = g_string_new_len (string, g_utf8_offset_to_pointer (string, num_left_chars) - string);
328 g_string_append (truncated, delimiter);
329 }
330
331 return g_string_free (truncated, FALSE);
332 }
333
334 gchar *
xed_utils_str_middle_truncate(const gchar * string,guint truncate_length)335 xed_utils_str_middle_truncate (const gchar *string,
336 guint truncate_length)
337 {
338 return xed_utils_str_truncate (string, truncate_length, TRUE);
339 }
340
341 gchar *
xed_utils_str_end_truncate(const gchar * string,guint truncate_length)342 xed_utils_str_end_truncate (const gchar *string,
343 guint truncate_length)
344 {
345 return xed_utils_str_truncate (string, truncate_length, FALSE);
346 }
347
348 gchar *
xed_utils_make_valid_utf8(const char * name)349 xed_utils_make_valid_utf8 (const char *name)
350 {
351 GString *string;
352 const char *remainder, *invalid;
353 int remaining_bytes, valid_bytes;
354
355 g_return_val_if_fail (name != NULL, NULL);
356
357 string = NULL;
358 remainder = name;
359 remaining_bytes = strlen (name);
360
361 while (remaining_bytes != 0)
362 {
363 if (g_utf8_validate (remainder, remaining_bytes, &invalid))
364 {
365 break;
366 }
367 valid_bytes = invalid - remainder;
368
369 if (string == NULL)
370 {
371 string = g_string_sized_new (remaining_bytes);
372 }
373 g_string_append_len (string, remainder, valid_bytes);
374 /* append U+FFFD REPLACEMENT CHARACTER */
375 g_string_append (string, "\357\277\275");
376
377 remaining_bytes -= valid_bytes + 1;
378 remainder = invalid + 1;
379 }
380
381 if (string == NULL)
382 {
383 return g_strdup (name);
384 }
385
386 g_string_append (string, remainder);
387
388 g_assert (g_utf8_validate (string->str, -1, NULL));
389
390 return g_string_free (string, FALSE);
391 }
392
393 /**
394 * xed_utils_uri_get_dirname:
395 * @uri: the URI to extract the dirname from
396 *
397 * Note: this function replace home dir with ~
398 */
399 gchar *
xed_utils_uri_get_dirname(const gchar * uri)400 xed_utils_uri_get_dirname (const gchar *uri)
401 {
402 gchar *res;
403 gchar *str;
404
405 g_return_val_if_fail (uri != NULL, NULL);
406
407 /* CHECK: does it work with uri chaining? - Paolo */
408 str = g_path_get_dirname (uri);
409 g_return_val_if_fail (str != NULL, g_strdup ("."));
410
411 if ((strlen (str) == 1) && (*str == '.'))
412 {
413 g_free (str);
414
415 return NULL;
416 }
417
418 res = xed_utils_replace_home_dir_with_tilde (str);
419
420 g_free (str);
421
422 return res;
423 }
424
425 /* the following two functions are courtesy of galeon */
426
427 /**
428 * xed_utils_get_current_workspace:
429 * @screen: a #GdkScreen
430 *
431 * Get the currently visible workspace for the #GdkScreen.
432 *
433 * If the X11 window property isn't found, 0 (the first workspace)
434 * is returned.
435 */
436 guint
xed_utils_get_current_workspace(GdkScreen * screen)437 xed_utils_get_current_workspace (GdkScreen *screen)
438 {
439 #ifdef GDK_WINDOWING_X11
440 GdkWindow *root_win;
441 GdkDisplay *display;
442 guint ret = 0;
443
444 g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
445
446 root_win = gdk_screen_get_root_window (screen);
447 display = gdk_screen_get_display (screen);
448
449 if (GDK_IS_X11_DISPLAY (display))
450 {
451 Atom type;
452 gint format;
453 gulong nitems;
454 gulong bytes_after;
455 guint *current_desktop;
456 gint err, result;
457
458 gdk_x11_display_error_trap_push (display);
459 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (root_win),
460 gdk_x11_get_xatom_by_name_for_display (display, "_NET_CURRENT_DESKTOP"),
461 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
462 &bytes_after, (gpointer) ¤t_desktop);
463 err = gdk_x11_display_error_trap_pop (display);
464
465 if (err != Success || result != Success)
466 return ret;
467
468 if (type == XA_CARDINAL && format == 32 && nitems > 0)
469 ret = current_desktop[0];
470
471 XFree (current_desktop);
472 }
473
474 return ret;
475 #else
476 /* FIXME: on mac etc proably there are native APIs
477 * to get the current workspace etc */
478 return 0;
479 #endif
480 }
481
482 /**
483 * xed_utils_get_window_workspace:
484 * @gtkwindow: a #GtkWindow.
485 *
486 * Get the workspace the window is on.
487 *
488 * This function gets the workspace that the #GtkWindow is visible on,
489 * it returns XED_ALL_WORKSPACES if the window is sticky, or if
490 * the window manager doesn't support this function.
491 *
492 * Returns: the workspace the window is on.
493 */
494 guint
xed_utils_get_window_workspace(GtkWindow * gtkwindow)495 xed_utils_get_window_workspace (GtkWindow *gtkwindow)
496 {
497 #ifdef GDK_WINDOWING_X11
498 GdkWindow *window;
499 GdkDisplay *display;
500 Atom type;
501 gint format;
502 gulong nitems;
503 gulong bytes_after;
504 guint *workspace;
505 gint err, result;
506 guint ret = XED_ALL_WORKSPACES;
507
508 g_return_val_if_fail (GTK_IS_WINDOW (gtkwindow), 0);
509 g_return_val_if_fail (gtk_widget_get_realized (GTK_WIDGET (gtkwindow)), 0);
510
511 window = gtk_widget_get_window (GTK_WIDGET (gtkwindow));
512 display = gdk_window_get_display (window);
513
514 if (GDK_IS_X11_DISPLAY (display))
515 {
516 gdk_x11_display_error_trap_push (display);
517 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
518 gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
519 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
520 &bytes_after, (gpointer) &workspace);
521 err = gdk_x11_display_error_trap_pop (display);
522
523 if (err != Success || result != Success)
524 return ret;
525
526 if (type == XA_CARDINAL && format == 32 && nitems > 0)
527 ret = workspace[0];
528
529 XFree (workspace);
530 }
531
532 return ret;
533 #else
534 /* FIXME: on mac etc proably there are native APIs
535 * to get the current workspace etc */
536 return 0;
537 #endif
538 }
539
540 /**
541 * xed_utils_get_current_viewport:
542 * @screen: a #GdkScreen
543 * @x: (out): x-axis point.
544 * @y: (out): y-axis point.
545 *
546 * Get the currently visible viewport origin for the #GdkScreen.
547 *
548 * If the X11 window property isn't found, (0, 0) is returned.
549 */
550 void
xed_utils_get_current_viewport(GdkScreen * screen,gint * x,gint * y)551 xed_utils_get_current_viewport (GdkScreen *screen,
552 gint *x,
553 gint *y)
554 {
555 #ifdef GDK_WINDOWING_X11
556 GdkWindow *root_win;
557 GdkDisplay *display;
558 Atom type;
559 gint format;
560 gulong nitems;
561 gulong bytes_after;
562 gulong *coordinates;
563 gint err, result;
564
565 g_return_if_fail (GDK_IS_SCREEN (screen));
566 g_return_if_fail (x != NULL && y != NULL);
567
568 /* Default values for the viewport origin */
569 *x = 0;
570 *y = 0;
571
572 root_win = gdk_screen_get_root_window (screen);
573 display = gdk_screen_get_display (screen);
574
575 if (GDK_IS_X11_DISPLAY (display))
576 {
577 gdk_x11_display_error_trap_push (display);
578 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (root_win),
579 gdk_x11_get_xatom_by_name_for_display (display, "_NET_DESKTOP_VIEWPORT"),
580 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
581 &bytes_after, (void*) &coordinates);
582 err = gdk_x11_display_error_trap_pop (display);
583
584 if (err != Success || result != Success)
585 return;
586
587 if (type != XA_CARDINAL || format != 32 || nitems < 2)
588 {
589 XFree (coordinates);
590 return;
591 }
592
593 *x = coordinates[0];
594 *y = coordinates[1];
595 XFree (coordinates);
596 }
597 #else
598 /* FIXME: on mac etc proably there are native APIs
599 * to get the current workspace etc */
600 *x = 0;
601 *y = 0;
602 #endif
603 }
604
605 /**
606 * xed_utils_location_get_dirname_for_display
607 * @location: the location
608 *
609 * Returns a string suitable to be displayed in the UI indicating
610 * the name of the directory where the file is located.
611 * For remote files it may also contain the hostname etc.
612 * For local files it tries to replace the home dir with ~.
613 *
614 * Returns: a string to display the dirname
615 */
616 gchar *
xed_utils_location_get_dirname_for_display(GFile * location)617 xed_utils_location_get_dirname_for_display (GFile *location)
618 {
619 gchar *uri;
620 gchar *res;
621 GMount *mount;
622
623 g_return_val_if_fail (location != NULL, NULL);
624
625 /* we use the parse name, that is either the local path
626 * or an uri but which is utf8 safe */
627 uri = g_file_get_parse_name (location);
628
629 /* FIXME: this is sync... is it a problem? */
630 mount = g_file_find_enclosing_mount (location, NULL, NULL);
631 if (mount != NULL)
632 {
633 gchar *mount_name;
634 gchar *path = NULL;
635 gchar *dirname;
636
637 mount_name = g_mount_get_name (mount);
638 g_object_unref (mount);
639
640 /* obtain the "path" part of the uri */
641 xed_utils_decode_uri (uri,
642 NULL, NULL,
643 NULL, NULL,
644 &path);
645
646 if (path == NULL)
647 {
648 dirname = xed_utils_uri_get_dirname (uri);
649 }
650 else
651 {
652 dirname = xed_utils_uri_get_dirname (path);
653 }
654
655 if (dirname == NULL || strcmp (dirname, ".") == 0)
656 {
657 res = mount_name;
658 }
659 else
660 {
661 res = g_strdup_printf ("%s %s", mount_name, dirname);
662 g_free (mount_name);
663 }
664
665 g_free (path);
666 g_free (dirname);
667 }
668 else
669 {
670 /* fallback for local files or uris without mounts */
671 res = xed_utils_uri_get_dirname (uri);
672 }
673
674 g_free (uri);
675
676 return res;
677 }
678
679 gchar *
xed_utils_replace_home_dir_with_tilde(const gchar * uri)680 xed_utils_replace_home_dir_with_tilde (const gchar *uri)
681 {
682 gchar *tmp;
683 gchar *home;
684
685 g_return_val_if_fail (uri != NULL, NULL);
686
687 /* Note that g_get_home_dir returns a const string */
688 tmp = (gchar *)g_get_home_dir ();
689
690 if (tmp == NULL)
691 {
692 return g_strdup (uri);
693 }
694
695 home = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
696 if (home == NULL)
697 {
698 return g_strdup (uri);
699 }
700
701 if (strcmp (uri, home) == 0)
702 {
703 g_free (home);
704
705 return g_strdup ("~");
706 }
707
708 tmp = home;
709 home = g_strdup_printf ("%s/", tmp);
710 g_free (tmp);
711
712 if (g_str_has_prefix (uri, home))
713 {
714 gchar *res;
715
716 res = g_strdup_printf ("~/%s", uri + strlen (home));
717
718 g_free (home);
719
720 return res;
721 }
722
723 g_free (home);
724
725 return g_strdup (uri);
726 }
727
728 static gboolean
is_valid_scheme_character(gchar c)729 is_valid_scheme_character (gchar c)
730 {
731 return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
732 }
733
734 static gboolean
has_valid_scheme(const gchar * uri)735 has_valid_scheme (const gchar *uri)
736 {
737 const gchar *p;
738
739 p = uri;
740
741 if (!is_valid_scheme_character (*p))
742 {
743 return FALSE;
744 }
745
746 do
747 {
748 p++;
749 } while (is_valid_scheme_character (*p));
750
751 return *p == ':';
752 }
753
754 gboolean
xed_utils_is_valid_location(GFile * location)755 xed_utils_is_valid_location (GFile *location)
756 {
757 const guchar *p;
758 gchar *uri;
759 gboolean is_valid;
760
761 if (location == NULL)
762 {
763 return FALSE;
764 }
765
766 uri = g_file_get_uri (location);
767
768 if (!has_valid_scheme (uri))
769 {
770 g_free (uri);
771 return FALSE;
772 }
773
774 is_valid = TRUE;
775
776 /* We expect to have a fully valid set of characters */
777 for (p = (const guchar *)uri; *p; p++)
778 {
779 if (*p == '%')
780 {
781 ++p;
782 if (!g_ascii_isxdigit (*p))
783 {
784 is_valid = FALSE;
785 break;
786 }
787
788 ++p;
789 if (!g_ascii_isxdigit (*p))
790 {
791 is_valid = FALSE;
792 break;
793 }
794 }
795 else
796 {
797 if (*p <= 32 || *p >= 128)
798 {
799 is_valid = FALSE;
800 break;
801 }
802 }
803 }
804
805 g_free (uri);
806
807 return is_valid;
808 }
809
810 static GtkWidget *handle_builder_error (const gchar *message, ...) G_GNUC_PRINTF (1, 2);
811
812 static GtkWidget *
handle_builder_error(const gchar * message,...)813 handle_builder_error (const gchar *message,
814 ...)
815 {
816 GtkWidget *label;
817 gchar *msg;
818 gchar *msg_plain;
819 va_list args;
820
821 va_start (args, message);
822 msg_plain = g_strdup_vprintf (message, args);
823 va_end (args);
824
825 label = gtk_label_new (NULL);
826 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
827
828 msg = g_strconcat ("<span size=\"large\" weight=\"bold\">",
829 msg_plain, "</span>\n\n",
830 _("Please check your installation."),
831 NULL);
832
833 gtk_label_set_markup (GTK_LABEL (label), msg);
834
835 g_free (msg_plain);
836 g_free (msg);
837
838 gtk_widget_set_margin_start (label, 5);
839 gtk_widget_set_margin_end (label, 5);
840 gtk_widget_set_margin_top (label, 5);
841 gtk_widget_set_margin_bottom (label, 5);
842
843 return label;
844 }
845
846 /* FIXME this is an issue for introspection */
847 /**
848 * xed_utils_get_ui_objects:
849 * @filename: the path to the gtk builder file
850 * @root_objects: a %NULL terminated list of root objects to load or NULL to
851 * load all objects
852 * @error_widget: a pointer were a #GtkLabel
853 * @object_name: the name of the first object
854 * @...: a pointer were the first object is returned, followed by more
855 * name / object pairs and terminated by %NULL.
856 *
857 * This function gets the requested objects from a GtkBuilder ui file. In case
858 * of error it returns %FALSE and sets error_widget to a GtkLabel containing
859 * the error message to display.
860 *
861 * Returns: %FALSE if an error occurs, %TRUE on success.
862 */
863 gboolean
xed_utils_get_ui_objects(const gchar * filename,gchar ** root_objects,GtkWidget ** error_widget,const gchar * object_name,...)864 xed_utils_get_ui_objects (const gchar *filename,
865 gchar **root_objects,
866 GtkWidget **error_widget,
867 const gchar *object_name,
868 ...)
869 {
870
871 GtkBuilder *builder;
872 va_list args;
873 const gchar *name;
874 GError *error = NULL;
875 gchar *filename_markup;
876 gboolean ret = TRUE;
877
878 g_return_val_if_fail (filename != NULL, FALSE);
879 g_return_val_if_fail (error_widget != NULL, FALSE);
880 g_return_val_if_fail (object_name != NULL, FALSE);
881
882 filename_markup = g_markup_printf_escaped ("<i>%s</i>", filename);
883 *error_widget = NULL;
884
885 builder = gtk_builder_new ();
886
887 if (root_objects != NULL)
888 {
889 gtk_builder_add_objects_from_file (builder, filename, root_objects, &error);
890 }
891 else
892 {
893 gtk_builder_add_from_file (builder, filename, &error);
894 }
895
896 if (error != NULL)
897 {
898 *error_widget = handle_builder_error (_("Unable to open UI file %s. Error: %s"),
899 filename_markup,
900 error->message);
901 g_error_free (error);
902 g_free (filename_markup);
903 g_object_unref (builder);
904
905 return FALSE;
906 }
907
908 va_start (args, object_name);
909 for (name = object_name; name; name = va_arg (args, const gchar *) )
910 {
911 GObject **gobj;
912
913 gobj = va_arg (args, GObject **);
914 *gobj = gtk_builder_get_object (builder, name);
915
916 if (!*gobj)
917 {
918 *error_widget = handle_builder_error (_("Unable to find the object '%s' inside file %s."),
919 name,
920 filename_markup),
921 ret = FALSE;
922 break;
923 }
924
925 /* we return a new ref for the root objects,
926 * the others are already reffed by their parent root object */
927 if (root_objects != NULL)
928 {
929 gint i;
930
931 for (i = 0; root_objects[i] != NULL; ++i)
932 {
933 if ((strcmp (name, root_objects[i]) == 0))
934 {
935 g_object_ref (*gobj);
936 }
937 }
938 }
939 }
940 va_end (args);
941
942 g_free (filename_markup);
943 g_object_unref (builder);
944
945 return ret;
946 }
947
948 gchar *
xed_utils_make_canonical_uri_from_shell_arg(const gchar * str)949 xed_utils_make_canonical_uri_from_shell_arg (const gchar *str)
950 {
951 GFile *gfile;
952 gchar *uri;
953
954 g_return_val_if_fail (str != NULL, NULL);
955 g_return_val_if_fail (*str != '\0', NULL);
956
957 /* Note for the future:
958 * FIXME: is still still relevant?
959 *
960 * <federico> paolo: and flame whoever tells
961 * you that file:///mate/test_files/hëllò
962 * doesn't work --- that's not a valid URI
963 *
964 * <paolo> federico: well, another solution that
965 * does not requires patch to _from_shell_args
966 * is to check that the string returned by it
967 * contains only ASCII chars
968 * <federico> paolo: hmmmm, isn't there
969 * mate_vfs_is_uri_valid() or something?
970 * <paolo>: I will use xed_utils_is_valid_uri ()
971 *
972 */
973
974 gfile = g_file_new_for_commandline_arg (str);
975
976 if (xed_utils_is_valid_location (gfile))
977 {
978 uri = g_file_get_uri (gfile);
979 g_object_unref (gfile);
980 return uri;
981 }
982
983 g_object_unref (gfile);
984 return NULL;
985 }
986
987 /**
988 * xed_utils_file_has_parent:
989 * @gfile: the GFile to check the parent for
990 *
991 * Return %TRUE if the specified gfile has a parent (is not the root), %FALSE
992 * otherwise
993 */
994 gboolean
xed_utils_file_has_parent(GFile * gfile)995 xed_utils_file_has_parent (GFile *gfile)
996 {
997 GFile *parent;
998 gboolean ret;
999
1000 parent = g_file_get_parent (gfile);
1001 ret = parent != NULL;
1002
1003 if (parent)
1004 {
1005 g_object_unref (parent);
1006 }
1007
1008 return ret;
1009 }
1010
1011 /**
1012 * xed_utils_basename_for_display:
1013 * @location: location for which the basename should be displayed
1014 *
1015 * Return the basename of a file suitable for display to users.
1016 */
1017 gchar *
xed_utils_basename_for_display(GFile * location)1018 xed_utils_basename_for_display (GFile *location)
1019 {
1020 gchar *name;
1021 gchar *hn;
1022 gchar *uri;
1023
1024 g_return_val_if_fail (G_IS_FILE (location), NULL);
1025
1026 uri = g_file_get_uri (location);
1027
1028 /* First, try to query the display name, but only on local files */
1029 if (g_file_has_uri_scheme (location, "file"))
1030 {
1031 GFileInfo *info;
1032 info = g_file_query_info (location,
1033 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
1034 G_FILE_QUERY_INFO_NONE,
1035 NULL,
1036 NULL);
1037
1038 if (info)
1039 {
1040 /* Simply get the display name to use as the basename */
1041 name = g_strdup (g_file_info_get_display_name (info));
1042 g_object_unref (info);
1043 }
1044 else
1045 {
1046 /* This is a local file, and therefore we will use
1047 * g_filename_display_basename on the local path */
1048 gchar *local_path;
1049
1050 local_path = g_file_get_path (location);
1051 name = g_filename_display_basename (local_path);
1052 g_free (local_path);
1053 }
1054 }
1055 else if (xed_utils_file_has_parent (location) || !xed_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL))
1056 {
1057 /* For remote files with a parent (so not just http://foo.com)
1058 or remote file for which the decoding of the host name fails,
1059 use the _parse_name and take basename of that */
1060 gchar *parse_name;
1061 gchar *base;
1062
1063 parse_name = g_file_get_parse_name (location);
1064 base = g_filename_display_basename (parse_name);
1065 name = g_uri_unescape_string (base, NULL);
1066
1067 g_free (base);
1068 g_free (parse_name);
1069 }
1070 else
1071 {
1072 /* display '/ on <host>' using the decoded host */
1073 gchar *hn_utf8;
1074
1075 if (hn != NULL)
1076 {
1077 hn_utf8 = xed_utils_make_valid_utf8 (hn);
1078 }
1079 else
1080 {
1081 /* we should never get here */
1082 hn_utf8 = g_strdup ("?");
1083 }
1084
1085 /* Translators: '/ on <remote-share>' */
1086 name = g_strdup_printf (_("/ on %s"), hn_utf8);
1087
1088 g_free (hn_utf8);
1089 g_free (hn);
1090 }
1091
1092 g_free (uri);
1093
1094 return name;
1095 }
1096
1097 /**
1098 * xed_utils_drop_get_uris:
1099 * @selection_data: the #GtkSelectionData from drag_data_received
1100 *
1101 * Create a list of valid uri's from a uri-list drop.
1102 *
1103 * Return value: (transfer full): a string array which will hold the uris or %NULL if there
1104 * were no valid uris. g_strfreev should be used when the
1105 * string array is no longer used
1106 */
1107 gchar **
xed_utils_drop_get_uris(GtkSelectionData * selection_data)1108 xed_utils_drop_get_uris (GtkSelectionData *selection_data)
1109 {
1110 gchar **uris;
1111 gint i;
1112 gint p = 0;
1113 gchar **uri_list;
1114
1115 uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data));
1116 uri_list = g_new0(gchar *, g_strv_length (uris) + 1);
1117
1118 for (i = 0; uris[i] != NULL; i++)
1119 {
1120 gchar *uri;
1121
1122 uri = xed_utils_make_canonical_uri_from_shell_arg (uris[i]);
1123
1124 /* Silently ignore malformed URI/filename */
1125 if (uri != NULL)
1126 {
1127 uri_list[p++] = uri;
1128 }
1129 }
1130
1131 g_strfreev (uris);
1132
1133 if (*uri_list == NULL)
1134 {
1135 g_free(uri_list);
1136 return NULL;
1137 }
1138
1139 return uri_list;
1140 }
1141
1142 static void
null_ptr(gchar ** ptr)1143 null_ptr (gchar **ptr)
1144 {
1145 if (ptr)
1146 {
1147 *ptr = NULL;
1148 }
1149 }
1150
1151 /**
1152 * xed_utils_decode_uri:
1153 * @uri: the uri to decode
1154 * @scheme: (allow-none): return value pointer for the uri's
1155 * scheme (e.g. http, sftp, ...), or %NULL
1156 * @user: (allow-none): return value pointer for the uri user info, or %NULL
1157 * @port: (allow-none): return value pointer for the uri port, or %NULL
1158 * @host: (allow-none): return value pointer for the uri host, or %NULL
1159 * @path: (allow-none): return value pointer for the uri path, or %NULL
1160 *
1161 * Parse and break an uri apart in its individual components like the uri
1162 * scheme, user info, port, host and path. The return value pointer can be
1163 * %NULL to ignore certain parts of the uri. If the function returns %TRUE, then
1164 * all return value pointers should be freed using g_free
1165 *
1166 * Return value: %TRUE if the uri could be properly decoded, %FALSE otherwise.
1167 */
1168 gboolean
xed_utils_decode_uri(const gchar * uri,gchar ** scheme,gchar ** user,gchar ** host,gchar ** port,gchar ** path)1169 xed_utils_decode_uri (const gchar *uri,
1170 gchar **scheme,
1171 gchar **user,
1172 gchar **host,
1173 gchar **port,
1174 gchar **path
1175 )
1176 {
1177 /* Largely copied from glib/gio/gdummyfile.c:_g_decode_uri. This
1178 * functionality should be in glib/gio, but for now we implement it
1179 * ourselves (see bug #546182) */
1180
1181 const char *p, *in, *hier_part_start, *hier_part_end;
1182 char *out;
1183 char c;
1184
1185 /* From RFC 3986 Decodes:
1186 * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
1187 */
1188
1189 p = uri;
1190
1191 null_ptr (scheme);
1192 null_ptr (user);
1193 null_ptr (port);
1194 null_ptr (host);
1195 null_ptr (path);
1196
1197 /* Decode scheme:
1198 * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
1199 */
1200
1201 if (!g_ascii_isalpha (*p))
1202 {
1203 return FALSE;
1204 }
1205
1206 while (1)
1207 {
1208 c = *p++;
1209
1210 if (c == ':')
1211 {
1212 break;
1213 }
1214
1215 if (!(g_ascii_isalnum(c) || c == '+' || c == '-' || c == '.'))
1216 {
1217 return FALSE;
1218 }
1219 }
1220
1221 if (scheme)
1222 {
1223 *scheme = g_malloc (p - uri);
1224 out = *scheme;
1225
1226 for (in = uri; in < p - 1; in++)
1227 {
1228 *out++ = g_ascii_tolower (*in);
1229 }
1230
1231 *out = '\0';
1232 }
1233
1234 hier_part_start = p;
1235 hier_part_end = p + strlen (p);
1236
1237 if (hier_part_start[0] == '/' && hier_part_start[1] == '/')
1238 {
1239 const char *authority_start, *authority_end;
1240 const char *userinfo_start, *userinfo_end;
1241 const char *host_start, *host_end;
1242 const char *port_start;
1243
1244 authority_start = hier_part_start + 2;
1245 /* authority is always followed by / or nothing */
1246 authority_end = memchr (authority_start, '/', hier_part_end - authority_start);
1247
1248 if (authority_end == NULL)
1249 {
1250 authority_end = hier_part_end;
1251 }
1252
1253 /* 3.2:
1254 * authority = [ userinfo "@" ] host [ ":" port ]
1255 */
1256
1257 userinfo_end = memchr (authority_start, '@', authority_end - authority_start);
1258
1259 if (userinfo_end)
1260 {
1261 userinfo_start = authority_start;
1262
1263 if (user)
1264 {
1265 *user = g_uri_unescape_segment (userinfo_start, userinfo_end, NULL);
1266 }
1267
1268 if (user && *user == NULL)
1269 {
1270 if (scheme)
1271 {
1272 g_free (*scheme);
1273 }
1274
1275 return FALSE;
1276 }
1277
1278 host_start = userinfo_end + 1;
1279 }
1280 else
1281 {
1282 host_start = authority_start;
1283 }
1284
1285 port_start = memchr (host_start, ':', authority_end - host_start);
1286
1287 if (port_start)
1288 {
1289 host_end = port_start++;
1290
1291 if (port)
1292 {
1293 *port = g_strndup (port_start, authority_end - port_start);
1294 }
1295 }
1296 else
1297 {
1298 host_end = authority_end;
1299 }
1300
1301 if (host)
1302 {
1303 *host = g_strndup (host_start, host_end - host_start);
1304 }
1305
1306 hier_part_start = authority_end;
1307 }
1308
1309 if (path)
1310 {
1311 *path = g_uri_unescape_segment (hier_part_start, hier_part_end, "/");
1312 }
1313
1314 return TRUE;
1315 }
1316
1317 static gboolean
data_exists(GSList * list,const gpointer data)1318 data_exists (GSList *list,
1319 const gpointer data)
1320 {
1321 for (; list != NULL; list = g_slist_next (list))
1322 {
1323 if (list->data == data)
1324 {
1325 return TRUE;
1326 }
1327 }
1328
1329 return FALSE;
1330 }
1331
1332 GSList *
_xed_utils_encoding_strv_to_list(const gchar * const * enc_str)1333 _xed_utils_encoding_strv_to_list (const gchar * const *enc_str)
1334 {
1335 GSList *res = NULL;
1336 gchar **p;
1337
1338 for (p = (gchar **)enc_str; p != NULL && *p != NULL; p++)
1339 {
1340 const gchar *charset = *p;
1341 const GtkSourceEncoding *enc;
1342
1343 if (g_str_equal (charset, "CURRENT"))
1344 {
1345 g_get_charset (&charset);
1346 }
1347
1348 g_return_val_if_fail (charset != NULL, NULL);
1349 enc = gtk_source_encoding_get_from_charset (charset);
1350
1351 if (enc != NULL &&
1352 !data_exists (res, (gpointer)enc))
1353 {
1354 res = g_slist_prepend (res, (gpointer)enc);
1355 }
1356 }
1357
1358 return g_slist_reverse (res);
1359 }
1360
1361 gchar **
_xed_utils_encoding_list_to_strv(const GSList * enc_list)1362 _xed_utils_encoding_list_to_strv (const GSList *enc_list)
1363 {
1364 GSList *l;
1365 GPtrArray *array;
1366
1367 array = g_ptr_array_sized_new (g_slist_length ((GSList *)enc_list) + 1);
1368
1369 for (l = (GSList *)enc_list; l != NULL; l = g_slist_next (l))
1370 {
1371 const GtkSourceEncoding *enc = l->data;
1372 const gchar *charset = gtk_source_encoding_get_charset (enc);
1373
1374 g_return_val_if_fail (charset != NULL, NULL);
1375
1376 g_ptr_array_add (array, g_strdup (charset));
1377 }
1378
1379 g_ptr_array_add (array, NULL);
1380
1381 return (gchar **)g_ptr_array_free (array, FALSE);
1382 }
1383