1 /*
2 * pluma-utils.c
3 * This file is part of pluma
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 * Copyright (C) 2012-2021 MATE Developers
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 2 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; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26 /*
27 * Modified by the pluma Team, 1998-2005. See the AUTHORS file for a
28 * list of people on the pluma Team.
29 * See the ChangeLog files for a list of changes.
30 *
31 * $Id$
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <fcntl.h>
43 #include <string.h>
44
45 #include <glib.h>
46 #include <glib/gi18n.h>
47 #include <gio/gio.h>
48
49 #include "pluma-utils.h"
50
51 #include "pluma-settings.h"
52 #include "pluma-document.h"
53 #include "pluma-debug.h"
54
55 /* For the workspace/viewport stuff */
56 #include <gdk/gdkx.h>
57 #include <X11/Xlib.h>
58 #include <X11/Xutil.h>
59 #include <X11/Xatom.h>
60
61 /**
62 * pluma_utils_uris_has_file_scheme
63 *
64 * Returns: %TRUE if @uri is a file: uri and is not a chained uri
65 */
66 gboolean
pluma_utils_uri_has_file_scheme(const gchar * uri)67 pluma_utils_uri_has_file_scheme (const gchar *uri)
68 {
69 GFile *gfile;
70 gboolean res;
71
72 gfile = g_file_new_for_uri (uri);
73 res = g_file_has_uri_scheme (gfile, "file");
74
75 g_object_unref (gfile);
76 return res;
77 }
78
79 /* FIXME: we should check for chained URIs */
80 gboolean
pluma_utils_uri_has_writable_scheme(const gchar * uri)81 pluma_utils_uri_has_writable_scheme (const gchar *uri)
82 {
83 /* FIXME: Avoid making a new settings variable here. */
84 GSettings *settings;
85 GFile *gfile;
86 gchar *scheme;
87 GSList *writable_schemes;
88 gboolean res;
89
90 settings = g_settings_new (PLUMA_SCHEMA_ID);
91 gfile = g_file_new_for_uri (uri);
92 scheme = g_file_get_uri_scheme (gfile);
93
94 g_return_val_if_fail (scheme != NULL, FALSE);
95
96 g_object_unref (gfile);
97
98 writable_schemes = pluma_settings_get_writable_vfs_schemes (settings);
99
100 /* CHECK: should we use g_ascii_strcasecmp? - Paolo (Nov 6, 2005) */
101 res = (g_slist_find_custom (writable_schemes,
102 scheme,
103 (GCompareFunc)strcmp) != NULL);
104
105 g_slist_free_full (writable_schemes, g_free);
106
107 g_free (scheme);
108 g_object_unref (settings);
109
110 return res;
111 }
112
113 static void
widget_get_origin(GtkWidget * widget,gint * x,gint * y)114 widget_get_origin (GtkWidget *widget, gint *x, gint *y)
115
116 {
117 GdkWindow *window;
118
119 window = gtk_widget_get_window (widget);
120 gdk_window_get_origin (window, x, y);
121 }
122
123 void
pluma_utils_menu_position_under_widget(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer user_data)124 pluma_utils_menu_position_under_widget (GtkMenu *menu,
125 gint *x,
126 gint *y,
127 gboolean *push_in,
128 gpointer user_data)
129 {
130 GtkWidget *widget;
131 GtkRequisition requisition;
132 GtkAllocation allocation;
133
134 widget = GTK_WIDGET (user_data);
135 widget_get_origin (widget, x, y);
136
137 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &requisition);
138
139 gtk_widget_get_allocation (widget, &allocation);
140
141 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
142 {
143 *x += allocation.x + allocation.width - requisition.width;
144 }
145 else
146 {
147 *x += allocation.x;
148 }
149
150 *y += allocation.y + allocation.height;
151
152 *push_in = TRUE;
153 }
154
155 void
menu_popup_at_treeview_selection(GtkWidget * menu,GtkWidget * treeview)156 menu_popup_at_treeview_selection (GtkWidget *menu,
157 GtkWidget *treeview)
158 {
159 GtkTreePath *path;
160 GtkTreeViewColumn *column;
161 GdkWindow *bin_window;
162 GdkRectangle rect;
163
164 gtk_tree_view_get_cursor (GTK_TREE_VIEW (treeview), &path, &column);
165 g_return_if_fail (path != NULL);
166
167 if (column == NULL)
168 column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 0);
169
170 bin_window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (treeview));
171 gtk_tree_view_get_cell_area (GTK_TREE_VIEW (treeview), path, column, &rect);
172
173 gtk_menu_popup_at_rect (GTK_MENU (menu), bin_window, &rect,
174 GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST,
175 NULL);
176
177 gtk_tree_path_free(path);
178 }
179
180 /**
181 * pluma_gtk_button_new_with_icon:
182 * @label:
183 * @icon_name:
184 *
185 * Returns: (transfer full):
186 */
187
188 GtkWidget *
pluma_gtk_button_new_with_icon(const gchar * label,const gchar * icon_name)189 pluma_gtk_button_new_with_icon (const gchar *label,
190 const gchar *icon_name)
191 {
192 GtkWidget *button;
193
194 button = gtk_button_new_with_mnemonic (label);
195 gtk_button_set_image (GTK_BUTTON (button),
196 gtk_image_new_from_icon_name (icon_name,
197 GTK_ICON_SIZE_BUTTON));
198
199 return button;
200 }
201
202 /**
203 * pluma_dialog_add_button:
204 * @dialog:
205 * @text:
206 * @icon_name:
207 * @response_id:
208 *
209 * Returns: (transfer none):
210 */
211 GtkWidget *
pluma_dialog_add_button(GtkDialog * dialog,const gchar * text,const gchar * icon_name,gint response_id)212 pluma_dialog_add_button (GtkDialog *dialog,
213 const gchar *text,
214 const gchar *icon_name,
215 gint response_id)
216 {
217 GtkWidget *button;
218
219 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
220 g_return_val_if_fail (text != NULL, NULL);
221 g_return_val_if_fail (icon_name != NULL, NULL);
222
223 button = pluma_gtk_button_new_with_icon (text, icon_name);
224 g_return_val_if_fail (button != NULL, NULL);
225
226 gtk_widget_set_can_default (button, TRUE);
227
228 gtk_widget_show (button);
229
230 gtk_dialog_add_action_widget (dialog, button, response_id);
231
232 return button;
233 }
234
235 /*
236 * n: len of the string in bytes
237 */
238 gboolean
g_utf8_caselessnmatch(const char * s1,const char * s2,gssize n1,gssize n2)239 g_utf8_caselessnmatch (const char *s1, const char *s2, gssize n1, gssize n2)
240 {
241 gchar *casefold;
242 gchar *normalized_s1;
243 gchar *normalized_s2;
244 gint len_s1;
245 gint len_s2;
246 gboolean ret = FALSE;
247
248 g_return_val_if_fail (s1 != NULL, FALSE);
249 g_return_val_if_fail (s2 != NULL, FALSE);
250 g_return_val_if_fail (n1 > 0, FALSE);
251 g_return_val_if_fail (n2 > 0, FALSE);
252
253 casefold = g_utf8_casefold (s1, n1);
254 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
255 g_free (casefold);
256
257 casefold = g_utf8_casefold (s2, n2);
258 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
259 g_free (casefold);
260
261 len_s1 = strlen (normalized_s1);
262 len_s2 = strlen (normalized_s2);
263
264 if (len_s1 < len_s2)
265 goto finally_2;
266
267 ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
268
269 finally_2:
270 g_free (normalized_s1);
271 g_free (normalized_s2);
272
273 return ret;
274 }
275
276 /**
277 * pluma_utils_set_atk_name_description:
278 * @widget: The Gtk widget for which name/description to be set
279 * @name: Atk name string
280 * @description: Atk description string
281 *
282 * This function sets up name and description
283 * for a specified gtk widget.
284 */
285 void
pluma_utils_set_atk_name_description(GtkWidget * widget,const gchar * name,const gchar * description)286 pluma_utils_set_atk_name_description (GtkWidget *widget,
287 const gchar *name,
288 const gchar *description)
289 {
290 AtkObject *aobj;
291
292 aobj = gtk_widget_get_accessible (widget);
293
294 if (!(GTK_IS_ACCESSIBLE (aobj)))
295 return;
296
297 if(name)
298 atk_object_set_name (aobj, name);
299
300 if(description)
301 atk_object_set_description (aobj, description);
302 }
303
304 /**
305 * pluma_set_atk_relation:
306 * @obj1: specified widget.
307 * @obj2: specified widget.
308 * @rel_type: the type of relation to set up.
309 *
310 * This function establishes atk relation
311 * between 2 specified widgets.
312 */
313 void
pluma_utils_set_atk_relation(GtkWidget * obj1,GtkWidget * obj2,AtkRelationType rel_type)314 pluma_utils_set_atk_relation (GtkWidget *obj1,
315 GtkWidget *obj2,
316 AtkRelationType rel_type )
317 {
318 AtkObject *atk_obj1, *atk_obj2;
319 AtkRelationSet *relation_set;
320 AtkObject *targets[1];
321 AtkRelation *relation;
322
323 atk_obj1 = gtk_widget_get_accessible (obj1);
324 atk_obj2 = gtk_widget_get_accessible (obj2);
325
326 if (!(GTK_IS_ACCESSIBLE (atk_obj1)) || !(GTK_IS_ACCESSIBLE (atk_obj2)))
327 return;
328
329 relation_set = atk_object_ref_relation_set (atk_obj1);
330 targets[0] = atk_obj2;
331
332 relation = atk_relation_new (targets, 1, rel_type);
333 atk_relation_set_add (relation_set, relation);
334
335 g_object_unref (G_OBJECT (relation));
336 }
337
338 gboolean
pluma_utils_uri_exists(const gchar * text_uri)339 pluma_utils_uri_exists (const gchar* text_uri)
340 {
341 GFile *gfile;
342 gboolean res;
343
344 g_return_val_if_fail (text_uri != NULL, FALSE);
345
346 pluma_debug_message (DEBUG_UTILS, "text_uri: %s", text_uri);
347
348 gfile = g_file_new_for_uri (text_uri);
349 res = g_file_query_exists (gfile, NULL);
350
351 g_object_unref (gfile);
352
353 pluma_debug_message (DEBUG_UTILS, res ? "TRUE" : "FALSE");
354
355 return res;
356 }
357
358 gchar *
pluma_utils_escape_search_text(const gchar * text)359 pluma_utils_escape_search_text (const gchar* text)
360 {
361 GString *str;
362 gint length;
363 const gchar *p;
364 const gchar *end;
365
366 if (text == NULL)
367 return NULL;
368
369 pluma_debug_message (DEBUG_SEARCH, "Text: %s", text);
370
371 length = strlen (text);
372
373 /* no escape when typing.
374 * The short circuit works only for ascii, but we only
375 * care about not escaping a single '\' */
376 if (length == 1)
377 return g_strdup (text);
378
379 str = g_string_new ("");
380
381 p = text;
382 end = text + length;
383
384 while (p != end)
385 {
386 const gchar *next;
387 next = g_utf8_next_char (p);
388
389 switch (*p)
390 {
391 case '\n':
392 g_string_append (str, "\\n");
393 break;
394 case '\r':
395 g_string_append (str, "\\r");
396 break;
397 case '\t':
398 g_string_append (str, "\\t");
399 break;
400 case '\\':
401 g_string_append (str, "\\\\");
402 break;
403 default:
404 g_string_append_len (str, p, next - p);
405 break;
406 }
407
408 p = next;
409 }
410
411 return g_string_free (str, FALSE);
412 }
413
414 gchar *
pluma_utils_unescape_search_text(const gchar * text)415 pluma_utils_unescape_search_text (const gchar *text)
416 {
417 GString *str;
418 gint length;
419 gboolean drop_prev = FALSE;
420 const gchar *cur;
421 const gchar *end;
422 const gchar *prev;
423
424 if (text == NULL)
425 return NULL;
426
427 length = strlen (text);
428
429 str = g_string_new ("");
430
431 cur = text;
432 end = text + length;
433 prev = NULL;
434
435 while (cur != end)
436 {
437 const gchar *next;
438 next = g_utf8_next_char (cur);
439
440 if (prev && (*prev == '\\'))
441 {
442 switch (*cur)
443 {
444 case 'n':
445 str = g_string_append (str, "\n");
446 break;
447 case 'r':
448 str = g_string_append (str, "\r");
449 break;
450 case 't':
451 str = g_string_append (str, "\t");
452 break;
453 case '\\':
454 str = g_string_append (str, "\\");
455 drop_prev = TRUE;
456 break;
457 default:
458 str = g_string_append (str, "\\");
459 str = g_string_append_len (str, cur, next - cur);
460 break;
461 }
462 }
463 else if (*cur != '\\')
464 {
465 str = g_string_append_len (str, cur, next - cur);
466 }
467 else if ((next == end) && (*cur == '\\'))
468 {
469 str = g_string_append (str, "\\");
470 }
471
472 if (!drop_prev)
473 {
474 prev = cur;
475 }
476 else
477 {
478 prev = NULL;
479 drop_prev = FALSE;
480 }
481
482 cur = next;
483 }
484
485 return g_string_free (str, FALSE);
486 }
487
488 void
pluma_warning(GtkWindow * parent,const gchar * format,...)489 pluma_warning (GtkWindow *parent, const gchar *format, ...)
490 {
491 va_list args;
492 gchar *str;
493 GtkWidget *dialog;
494 GtkWindowGroup *wg = NULL;
495
496 g_return_if_fail (format != NULL);
497
498 if (parent != NULL)
499 wg = gtk_window_get_group (parent);
500
501 va_start (args, format);
502 str = g_strdup_vprintf (format, args);
503 va_end (args);
504
505 dialog = gtk_message_dialog_new_with_markup (
506 parent,
507 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
508 GTK_MESSAGE_ERROR,
509 GTK_BUTTONS_OK,
510 "%s", str);
511
512 g_free (str);
513
514 if (wg != NULL)
515 gtk_window_group_add_window (wg, GTK_WINDOW (dialog));
516
517 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
518
519 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
520
521 g_signal_connect (G_OBJECT (dialog),
522 "response",
523 G_CALLBACK (gtk_widget_destroy),
524 NULL);
525
526 gtk_widget_show (dialog);
527 }
528
529 /*
530 * Doubles underscore to avoid spurious menu accels.
531 */
532 gchar *
pluma_utils_escape_underscores(const gchar * text,gssize length)533 pluma_utils_escape_underscores (const gchar* text,
534 gssize length)
535 {
536 GString *str;
537 const gchar *p;
538 const gchar *end;
539
540 g_return_val_if_fail (text != NULL, NULL);
541
542 if (length < 0)
543 length = strlen (text);
544
545 str = g_string_sized_new (length);
546
547 p = text;
548 end = text + length;
549
550 while (p != end)
551 {
552 const gchar *next;
553 next = g_utf8_next_char (p);
554
555 switch (*p)
556 {
557 case '_':
558 g_string_append (str, "__");
559 break;
560 default:
561 g_string_append_len (str, p, next - p);
562 break;
563 }
564
565 p = next;
566 }
567
568 return g_string_free (str, FALSE);
569 }
570
571 /* the following functions are taken from eel */
572
573 static gchar *
pluma_utils_str_truncate(const gchar * string,guint truncate_length,gboolean middle)574 pluma_utils_str_truncate (const gchar *string,
575 guint truncate_length,
576 gboolean middle)
577 {
578 GString *truncated;
579 guint length;
580 guint n_chars;
581 guint num_left_chars;
582 guint right_offset;
583 guint delimiter_length;
584 const gchar *delimiter = "\342\200\246";
585
586 g_return_val_if_fail (string != NULL, NULL);
587
588 length = strlen (string);
589
590 g_return_val_if_fail (g_utf8_validate (string, length, NULL), NULL);
591
592 /* It doesnt make sense to truncate strings to less than
593 * the size of the delimiter plus 2 characters (one on each
594 * side)
595 */
596 delimiter_length = g_utf8_strlen (delimiter, -1);
597 if (truncate_length < (delimiter_length + 2)) {
598 return g_strdup (string);
599 }
600
601 n_chars = g_utf8_strlen (string, length);
602
603 /* Make sure the string is not already small enough. */
604 if (n_chars <= truncate_length) {
605 return g_strdup (string);
606 }
607
608 /* Find the 'middle' where the truncation will occur. */
609 if (middle)
610 {
611 num_left_chars = (truncate_length - delimiter_length) / 2;
612 right_offset = n_chars - truncate_length + num_left_chars + delimiter_length;
613
614 truncated = g_string_new_len (string,
615 g_utf8_offset_to_pointer (string, num_left_chars) - string);
616 g_string_append (truncated, delimiter);
617 g_string_append (truncated, g_utf8_offset_to_pointer (string, right_offset));
618 }
619 else
620 {
621 num_left_chars = truncate_length - delimiter_length;
622 truncated = g_string_new_len (string,
623 g_utf8_offset_to_pointer (string, num_left_chars) - string);
624 g_string_append (truncated, delimiter);
625 }
626
627 return g_string_free (truncated, FALSE);
628 }
629
630 gchar *
pluma_utils_str_middle_truncate(const gchar * string,guint truncate_length)631 pluma_utils_str_middle_truncate (const gchar *string,
632 guint truncate_length)
633 {
634 return pluma_utils_str_truncate (string, truncate_length, TRUE);
635 }
636
637 gchar *
pluma_utils_str_end_truncate(const gchar * string,guint truncate_length)638 pluma_utils_str_end_truncate (const gchar *string,
639 guint truncate_length)
640 {
641 return pluma_utils_str_truncate (string, truncate_length, FALSE);
642 }
643
644 gchar *
pluma_utils_make_valid_utf8(const char * name)645 pluma_utils_make_valid_utf8 (const char *name)
646 {
647 GString *string;
648 const char *remainder, *invalid;
649 int remaining_bytes, valid_bytes;
650
651 g_return_val_if_fail (name != NULL, NULL);
652
653 string = NULL;
654 remainder = name;
655 remaining_bytes = strlen (name);
656
657 while (remaining_bytes != 0) {
658 if (g_utf8_validate (remainder, remaining_bytes, &invalid)) {
659 break;
660 }
661 valid_bytes = invalid - remainder;
662
663 if (string == NULL) {
664 string = g_string_sized_new (remaining_bytes);
665 }
666 g_string_append_len (string, remainder, valid_bytes);
667 /* append U+FFFD REPLACEMENT CHARACTER */
668 g_string_append (string, "\357\277\275");
669
670 remaining_bytes -= valid_bytes + 1;
671 remainder = invalid + 1;
672 }
673
674 if (string == NULL) {
675 return g_strdup (name);
676 }
677
678 g_string_append (string, remainder);
679
680 g_assert (g_utf8_validate (string->str, -1, NULL));
681
682 return g_string_free (string, FALSE);
683 }
684
685 /**
686 * pluma_utils_uri_get_dirname:
687 *
688 * Note: this function replace home dir with ~
689 */
690 gchar *
pluma_utils_uri_get_dirname(const gchar * uri)691 pluma_utils_uri_get_dirname (const gchar *uri)
692 {
693 gchar *res;
694 gchar *str;
695
696 g_return_val_if_fail (uri != NULL, NULL);
697
698 /* CHECK: does it work with uri chaining? - Paolo */
699 str = g_path_get_dirname (uri);
700 g_return_val_if_fail (str != NULL, g_strdup ("."));
701
702 if ((strlen (str) == 1) && (*str == '.'))
703 {
704 g_free (str);
705
706 return NULL;
707 }
708
709 res = pluma_utils_replace_home_dir_with_tilde (str);
710
711 g_free (str);
712
713 return res;
714 }
715
716 /**
717 * pluma_utils_location_get_dirname_for_display:
718 * @location: the location
719 *
720 * Returns a string suitable to be displayed in the UI indicating
721 * the name of the directory where the file is located.
722 * For remote files it may also contain the hostname etc.
723 * For local files it tries to replace the home dir with ~.
724 *
725 * Returns: a string to display the dirname
726 */
727 gchar *
pluma_utils_location_get_dirname_for_display(GFile * location)728 pluma_utils_location_get_dirname_for_display (GFile *location)
729 {
730 gchar *uri;
731 gchar *res;
732 GMount *mount;
733
734 g_return_val_if_fail (location != NULL, NULL);
735
736 /* we use the parse name, that is either the local path
737 * or an uri but which is utf8 safe */
738 uri = g_file_get_parse_name (location);
739
740 /* FIXME: this is sync... is it a problem? */
741 mount = g_file_find_enclosing_mount (location, NULL, NULL);
742 if (mount != NULL)
743 {
744 gchar *mount_name;
745 gchar *path = NULL;
746 gchar *dirname;
747
748 mount_name = g_mount_get_name (mount);
749 g_object_unref (mount);
750
751 /* obtain the "path" part of the uri */
752 pluma_utils_decode_uri (uri,
753 NULL, NULL,
754 NULL, NULL,
755 &path);
756
757 if (path == NULL)
758 {
759 dirname = pluma_utils_uri_get_dirname (uri);
760 }
761 else
762 {
763 dirname = pluma_utils_uri_get_dirname (path);
764 }
765
766 if (dirname == NULL || strcmp (dirname, ".") == 0)
767 {
768 res = mount_name;
769 }
770 else
771 {
772 res = g_strdup_printf ("%s %s", mount_name, dirname);
773 g_free (mount_name);
774 }
775
776 g_free (path);
777 g_free (dirname);
778 }
779 else
780 {
781 /* fallback for local files or uris without mounts */
782 res = pluma_utils_uri_get_dirname (uri);
783 }
784
785 g_free (uri);
786
787 return res;
788 }
789
790 gchar *
pluma_utils_replace_home_dir_with_tilde(const gchar * uri)791 pluma_utils_replace_home_dir_with_tilde (const gchar *uri)
792 {
793 gchar *tmp;
794 gchar *home;
795
796 g_return_val_if_fail (uri != NULL, NULL);
797
798 /* Note that g_get_home_dir returns a const string */
799 tmp = (gchar *)g_get_home_dir ();
800
801 if (tmp == NULL)
802 return g_strdup (uri);
803
804 home = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
805 if (home == NULL)
806 return g_strdup (uri);
807
808 if (strcmp (uri, home) == 0)
809 {
810 g_free (home);
811
812 return g_strdup ("~");
813 }
814
815 tmp = home;
816 home = g_strdup_printf ("%s/", tmp);
817 g_free (tmp);
818
819 if (g_str_has_prefix (uri, home))
820 {
821 gchar *res;
822
823 res = g_strdup_printf ("~/%s", uri + strlen (home));
824
825 g_free (home);
826
827 return res;
828 }
829
830 g_free (home);
831
832 return g_strdup (uri);
833 }
834
835 /* the following two functions are courtesy of galeon */
836
837 /**
838 * pluma_utils_get_current_workspace:
839 *
840 * Get the current workspace
841 *
842 * Get the currently visible workspace for the #GdkScreen.
843 *
844 * If the X11 window property isn't found, 0 (the first workspace)
845 * is returned.
846 */
847 guint
pluma_utils_get_current_workspace(GdkScreen * screen)848 pluma_utils_get_current_workspace (GdkScreen *screen)
849 {
850 if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
851 {
852 GdkWindow *root_win;
853 GdkDisplay *display;
854 Atom type;
855 gint format;
856 gulong nitems;
857 gulong bytes_after;
858 guint *current_desktop;
859 gint err, result;
860 guint ret = 0;
861
862 g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
863
864 root_win = gdk_screen_get_root_window (screen);
865 display = gdk_screen_get_display (screen);
866
867 gdk_x11_display_error_trap_push (display);
868 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID(root_win),
869 gdk_x11_get_xatom_by_name_for_display (display, "_NET_CURRENT_DESKTOP"),
870 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
871 &bytes_after, (gpointer) ¤t_desktop);
872 err = gdk_x11_display_error_trap_pop (display);
873
874 if (err != Success || result != Success)
875 return ret;
876
877 if (type == XA_CARDINAL && format == 32 && nitems > 0)
878 ret = current_desktop[0];
879
880 XFree (current_desktop);
881 return ret;
882 }
883 else
884 /* FIXME: on mac etc proably there are native APIs
885 * to get the current workspace etc */
886 return 0;
887 }
888
889 /**
890 * pluma_utils_get_window_workspace:
891 *
892 * Get the workspace the window is on
893 *
894 * This function gets the workspace that the #GtkWindow is visible on,
895 * it returns PLUMA_ALL_WORKSPACES if the window is sticky, or if
896 * the window manager doesn support this function
897 */
898 guint
pluma_utils_get_window_workspace(GtkWindow * gtkwindow)899 pluma_utils_get_window_workspace (GtkWindow *gtkwindow)
900 {
901
902 if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
903 {
904 GdkWindow *window;
905 GdkDisplay *display;
906 Atom type;
907 gint format;
908 gulong nitems;
909 gulong bytes_after;
910 guint *workspace;
911 gint err, result;
912 guint ret = PLUMA_ALL_WORKSPACES;
913
914 g_return_val_if_fail (GTK_IS_WINDOW (gtkwindow), 0);
915 g_return_val_if_fail (gtk_widget_get_realized (GTK_WIDGET (gtkwindow)), 0);
916
917 window = gtk_widget_get_window (GTK_WIDGET (gtkwindow));
918 display = gdk_window_get_display (window);
919
920 gdk_x11_display_error_trap_push (display);
921 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
922 gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
923 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
924 &bytes_after, (gpointer) &workspace);
925 err = gdk_x11_display_error_trap_pop (display);
926
927 if (err != Success || result != Success)
928 return ret;
929
930 if (type == XA_CARDINAL && format == 32 && nitems > 0)
931 ret = workspace[0];
932
933 XFree (workspace);
934 return ret;
935 }
936 else
937 /* FIXME: on mac etc proably there are native APIs
938 * to get the current workspace etc */
939 return 0;
940 }
941
942 /**
943 * pluma_utils_get_current_viewport:
944 *
945 * Get the current viewport origin
946 *
947 * Get the currently visible viewport origin for the #GdkScreen.
948 *
949 * If the X11 window property isn't found, (0, 0) is returned.
950 */
951 void
pluma_utils_get_current_viewport(GdkScreen * screen,gint * x,gint * y)952 pluma_utils_get_current_viewport (GdkScreen *screen,
953 gint *x,
954 gint *y)
955 {
956 if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
957 {
958 GdkWindow *root_win;
959 GdkDisplay *display;
960 Atom type;
961 gint format;
962 gulong nitems;
963 gulong bytes_after;
964 gulong *coordinates;
965 gint err, result;
966
967 g_return_if_fail (GDK_IS_SCREEN (screen));
968 g_return_if_fail (x != NULL && y != NULL);
969
970 /* Default values for the viewport origin */
971 *x = 0;
972 *y = 0;
973
974 root_win = gdk_screen_get_root_window (screen);
975 display = gdk_screen_get_display (screen);
976
977 gdk_x11_display_error_trap_push (display);
978 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (root_win),
979 gdk_x11_get_xatom_by_name_for_display (display, "_NET_DESKTOP_VIEWPORT"),
980 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
981 &bytes_after, (void*) &coordinates);
982 err = gdk_x11_display_error_trap_pop (display);
983
984 if (err != Success || result != Success)
985 return;
986
987 if (type != XA_CARDINAL || format != 32 || nitems < 2)
988 {
989 XFree (coordinates);
990 return;
991 }
992
993 *x = coordinates[0];
994 *y = coordinates[1];
995 XFree (coordinates);
996 }
997 else
998 /* FIXME: on mac etc proably there are native APIs
999 * to get the current workspace etc */
1000 {
1001 *x = 0;
1002 *y = 0;
1003 }
1004 }
1005
1006 static gboolean
is_valid_scheme_character(gchar c)1007 is_valid_scheme_character (gchar c)
1008 {
1009 return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
1010 }
1011
1012 static gboolean
has_valid_scheme(const gchar * uri)1013 has_valid_scheme (const gchar *uri)
1014 {
1015 const gchar *p;
1016
1017 p = uri;
1018
1019 if (!is_valid_scheme_character (*p)) {
1020 return FALSE;
1021 }
1022
1023 do {
1024 p++;
1025 } while (is_valid_scheme_character (*p));
1026
1027 return *p == ':';
1028 }
1029
1030 gboolean
pluma_utils_is_valid_uri(const gchar * uri)1031 pluma_utils_is_valid_uri (const gchar *uri)
1032 {
1033 const guchar *p;
1034
1035 if (uri == NULL)
1036 return FALSE;
1037
1038 if (!has_valid_scheme (uri))
1039 return FALSE;
1040
1041 /* We expect to have a fully valid set of characters */
1042 for (p = (const guchar *)uri; *p; p++) {
1043 if (*p == '%')
1044 {
1045 ++p;
1046 if (!g_ascii_isxdigit (*p))
1047 return FALSE;
1048
1049 ++p;
1050 if (!g_ascii_isxdigit (*p))
1051 return FALSE;
1052 }
1053 else
1054 {
1055 if (*p <= 32 || *p >= 128)
1056 return FALSE;
1057 }
1058 }
1059
1060 return TRUE;
1061 }
1062
1063 static GtkWidget *
handle_builder_error(const gchar * message,...)1064 handle_builder_error (const gchar *message, ...)
1065 {
1066 GtkWidget *label;
1067 gchar *msg;
1068 gchar *msg_plain;
1069 va_list args;
1070
1071 va_start (args, message);
1072 msg_plain = g_strdup_vprintf (message, args);
1073 va_end (args);
1074
1075 label = gtk_label_new (NULL);
1076 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1077
1078 msg = g_strconcat ("<span size=\"large\" weight=\"bold\">",
1079 msg_plain, "</span>\n\n",
1080 _("Please check your installation."),
1081 NULL);
1082
1083 gtk_label_set_markup (GTK_LABEL (label), msg);
1084
1085 g_free (msg_plain);
1086 g_free (msg);
1087
1088 gtk_widget_set_margin_start (label, 5);
1089 gtk_widget_set_margin_end (label, 5);
1090 gtk_widget_set_margin_top (label, 5);
1091 gtk_widget_set_margin_bottom (label, 5);
1092
1093 return label;
1094 }
1095
1096 /* FIXME this is an issue for introspection */
1097 /**
1098 * pluma_utils_get_ui_objects:
1099 * @filename: the path to the gtk builder file
1100 * @root_objects: a %NULL terminated list of root objects to load or NULL to
1101 * load all objects
1102 * @error_widget: a pointer were a #GtkLabel
1103 * @object_name: the name of the first object
1104 * @...: a pointer were the first object is returned, followed by more
1105 * name / object pairs and terminated by %NULL.
1106 *
1107 * This function gets the requested objects from a GtkBuilder ui file. In case
1108 * of error it returns %FALSE and sets error_widget to a GtkLabel containing
1109 * the error message to display.
1110 *
1111 * Returns: %FALSE if an error occurs, %TRUE on success.
1112 */
1113 gboolean
pluma_utils_get_ui_objects(const gchar * filename,gchar ** root_objects,GtkWidget ** error_widget,const gchar * object_name,...)1114 pluma_utils_get_ui_objects (const gchar *filename,
1115 gchar **root_objects,
1116 GtkWidget **error_widget,
1117 const gchar *object_name,
1118 ...)
1119 {
1120
1121 GtkBuilder *builder;
1122 va_list args;
1123 const gchar *name;
1124 GError *error = NULL;
1125 gchar *filename_markup;
1126 gboolean ret = TRUE;
1127
1128 g_return_val_if_fail (filename != NULL, FALSE);
1129 g_return_val_if_fail (error_widget != NULL, FALSE);
1130 g_return_val_if_fail (object_name != NULL, FALSE);
1131
1132 filename_markup = g_markup_printf_escaped ("<i>%s</i>", filename);
1133 *error_widget = NULL;
1134
1135 builder = gtk_builder_new ();
1136
1137 if (root_objects != NULL)
1138 {
1139 gtk_builder_add_objects_from_file (builder,
1140 filename,
1141 root_objects,
1142 &error);
1143 }
1144 else
1145 {
1146 gtk_builder_add_from_file (builder,
1147 filename,
1148 &error);
1149 }
1150
1151 if (error != NULL)
1152 {
1153 *error_widget = handle_builder_error (_("Unable to open UI file %s. Error: %s"),
1154 filename_markup,
1155 error->message);
1156 g_error_free (error);
1157 g_free (filename_markup);
1158 g_object_unref (builder);
1159
1160 return FALSE;
1161 }
1162
1163 va_start (args, object_name);
1164 for (name = object_name; name; name = va_arg (args, const gchar *) )
1165 {
1166 GObject **gobj;
1167
1168 gobj = va_arg (args, GObject **);
1169 *gobj = gtk_builder_get_object (builder, name);
1170
1171 if (!*gobj)
1172 {
1173 *error_widget = handle_builder_error (_("Unable to find the object '%s' inside file %s."),
1174 name,
1175 filename_markup),
1176 ret = FALSE;
1177 break;
1178 }
1179
1180 /* we return a new ref for the root objects,
1181 * the others are already reffed by their parent root object */
1182 if (root_objects != NULL)
1183 {
1184 gint i;
1185
1186 for (i = 0; root_objects[i] != NULL; ++i)
1187 {
1188 if ((strcmp (name, root_objects[i]) == 0))
1189 {
1190 g_object_ref (*gobj);
1191 }
1192 }
1193 }
1194 }
1195 va_end (args);
1196
1197 g_free (filename_markup);
1198 g_object_unref (builder);
1199
1200 return ret;
1201 }
1202
1203 gchar *
pluma_utils_make_canonical_uri_from_shell_arg(const gchar * str)1204 pluma_utils_make_canonical_uri_from_shell_arg (const gchar *str)
1205 {
1206 GFile *gfile;
1207 gchar *uri;
1208
1209 g_return_val_if_fail (str != NULL, NULL);
1210 g_return_val_if_fail (*str != '\0', NULL);
1211
1212 /* Note for the future:
1213 * FIXME: is still still relevant?
1214 *
1215 * <federico> paolo: and flame whoever tells
1216 * you that file:///mate/test_files/hëllò
1217 * doesn't work --- that's not a valid URI
1218 *
1219 * <paolo> federico: well, another solution that
1220 * does not requires patch to _from_shell_args
1221 * is to check that the string returned by it
1222 * contains only ASCII chars
1223 * <federico> paolo: hmmmm, isn't there
1224 * mate_vfs_is_uri_valid() or something?
1225 * <paolo>: I will use pluma_utils_is_valid_uri ()
1226 *
1227 */
1228
1229 gfile = g_file_new_for_commandline_arg (str);
1230 uri = g_file_get_uri (gfile);
1231 g_object_unref (gfile);
1232
1233 if (pluma_utils_is_valid_uri (uri))
1234 return uri;
1235
1236 g_free (uri);
1237 return NULL;
1238 }
1239
1240 /**
1241 * pluma_utils_file_has_parent:
1242 * @gfile: the GFile to check the parent for
1243 *
1244 * Return %TRUE if the specified gfile has a parent (is not the root), %FALSE
1245 * otherwise
1246 */
1247 gboolean
pluma_utils_file_has_parent(GFile * gfile)1248 pluma_utils_file_has_parent (GFile *gfile)
1249 {
1250 GFile *parent;
1251 gboolean ret;
1252
1253 parent = g_file_get_parent (gfile);
1254 ret = parent != NULL;
1255
1256 if (parent)
1257 g_object_unref (parent);
1258
1259 return ret;
1260 }
1261
1262 /**
1263 * pluma_utils_basename_for_display:
1264 * @uri: uri for which the basename should be displayed
1265 *
1266 * Return the basename of a file suitable for display to users.
1267 */
1268 gchar *
pluma_utils_basename_for_display(gchar const * uri)1269 pluma_utils_basename_for_display (gchar const *uri)
1270 {
1271 gchar *name;
1272 GFile *gfile;
1273 gchar *hn;
1274
1275 g_return_val_if_fail (uri != NULL, NULL);
1276
1277 gfile = g_file_new_for_uri (uri);
1278
1279 /* First, try to query the display name, but only on local files */
1280 if (g_file_has_uri_scheme (gfile, "file"))
1281 {
1282 GFileInfo *info;
1283 info = g_file_query_info (gfile,
1284 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
1285 G_FILE_QUERY_INFO_NONE,
1286 NULL,
1287 NULL);
1288
1289 if (info)
1290 {
1291 /* Simply get the display name to use as the basename */
1292 name = g_strdup (g_file_info_get_display_name (info));
1293 g_object_unref (info);
1294 }
1295 else
1296 {
1297 /* This is a local file, and therefore we will use
1298 * g_filename_display_basename on the local path */
1299 gchar *local_path;
1300
1301 local_path = g_file_get_path (gfile);
1302 name = g_filename_display_basename (local_path);
1303 g_free (local_path);
1304 }
1305 }
1306 else if (pluma_utils_file_has_parent (gfile) || !pluma_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL))
1307 {
1308 /* For remote files with a parent (so not just http://foo.com)
1309 or remote file for which the decoding of the host name fails,
1310 use the _parse_name and take basename of that */
1311 gchar *parse_name;
1312 gchar *base;
1313
1314 parse_name = g_file_get_parse_name (gfile);
1315 base = g_filename_display_basename (parse_name);
1316 name = g_uri_unescape_string (base, NULL);
1317
1318 g_free (base);
1319 g_free (parse_name);
1320 }
1321 else
1322 {
1323 /* display '/ on <host>' using the decoded host */
1324 gchar *hn_utf8;
1325
1326 if (hn != NULL)
1327 hn_utf8 = pluma_utils_make_valid_utf8 (hn);
1328 else
1329 /* we should never get here */
1330 hn_utf8 = g_strdup ("?");
1331
1332 /* Translators: '/ on <remote-share>' */
1333 name = g_strdup_printf (_("/ on %s"), hn_utf8);
1334
1335 g_free (hn_utf8);
1336 g_free (hn);
1337 }
1338
1339 g_object_unref (gfile);
1340
1341 return name;
1342 }
1343
1344 /**
1345 * pluma_utils_uri_for_display:
1346 * @uri: uri to be displayed.
1347 *
1348 * Filter, modify, unescape and change @uri to make it appropriate
1349 * for display to users.
1350 *
1351 * This function is a convenient wrapper for g_file_get_parse_name
1352 *
1353 * Return value: a string which represents @uri and can be displayed.
1354 */
1355 gchar *
pluma_utils_uri_for_display(const gchar * uri)1356 pluma_utils_uri_for_display (const gchar *uri)
1357 {
1358 GFile *gfile;
1359 gchar *parse_name;
1360
1361 gfile = g_file_new_for_uri (uri);
1362 parse_name = g_file_get_parse_name (gfile);
1363 g_object_unref (gfile);
1364
1365 return parse_name;
1366 }
1367
1368 /**
1369 * pluma_utils_drop_get_uris:
1370 * @selection_data: the #GtkSelectionData from drag_data_received
1371 *
1372 * Create a list of valid uri's from a uri-list drop.
1373 *
1374 * Return value: (transfer full): a string array which will hold the uris or %NULL if there
1375 * were no valid uris. g_strfreev should be used when the
1376 * string array is no longer used
1377 */
1378 gchar **
pluma_utils_drop_get_uris(GtkSelectionData * selection_data)1379 pluma_utils_drop_get_uris (GtkSelectionData *selection_data)
1380 {
1381 gchar **uris;
1382 gint i;
1383 gint p = 0;
1384 gchar **uri_list;
1385
1386 uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data));
1387 uri_list = g_new0(gchar *, g_strv_length (uris) + 1);
1388
1389 for (i = 0; uris[i] != NULL; i++)
1390 {
1391 gchar *uri;
1392
1393 uri = pluma_utils_make_canonical_uri_from_shell_arg (uris[i]);
1394
1395 /* Silently ignore malformed URI/filename */
1396 if (uri != NULL)
1397 uri_list[p++] = uri;
1398 }
1399
1400 g_strfreev (uris);
1401
1402 if (*uri_list == NULL)
1403 {
1404 g_free(uri_list);
1405 return NULL;
1406 }
1407
1408 return uri_list;
1409 }
1410
1411 static void
null_ptr(gchar ** ptr)1412 null_ptr (gchar **ptr)
1413 {
1414 if (ptr)
1415 *ptr = NULL;
1416 }
1417
1418 /**
1419 * pluma_utils_decode_uri:
1420 * @uri: the uri to decode
1421 * @scheme: (allow-none): return value pointer for the uri's
1422 * scheme (e.g. http, sftp, ...), or %NULL
1423 * @user: (allow-none): return value pointer for the uri user info, or %NULL
1424 * @host: (allow-none): return value pointer for the uri host, or %NULL
1425 * @port: (allow-none): return value pointer for the uri port, or %NULL
1426 * @path: (allow-none): return value pointer for the uri path, or %NULL
1427 *
1428 * Parse and break an uri apart in its individual components like the uri
1429 * scheme, user info, port, host and path. The return value pointer can be
1430 * %NULL to ignore certain parts of the uri. If the function returns %TRUE, then
1431 * all return value pointers should be freed using g_free
1432 *
1433 * Return value: %TRUE if the uri could be properly decoded, %FALSE otherwise.
1434 */
1435 gboolean
pluma_utils_decode_uri(const gchar * uri,gchar ** scheme,gchar ** user,gchar ** host,gchar ** port,gchar ** path)1436 pluma_utils_decode_uri (const gchar *uri,
1437 gchar **scheme,
1438 gchar **user,
1439 gchar **host,
1440 gchar **port,
1441 gchar **path
1442 )
1443 {
1444 /* Largely copied from glib/gio/gdummyfile.c:_g_decode_uri. This
1445 * functionality should be in glib/gio, but for now we implement it
1446 * ourselves (see bug #546182) */
1447
1448 const char *p, *in, *hier_part_start, *hier_part_end;
1449 char *out;
1450 char c;
1451
1452 /* From RFC 3986 Decodes:
1453 * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
1454 */
1455
1456 p = uri;
1457
1458 null_ptr (scheme);
1459 null_ptr (user);
1460 null_ptr (port);
1461 null_ptr (host);
1462 null_ptr (path);
1463
1464 /* Decode scheme:
1465 * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
1466 */
1467
1468 if (!g_ascii_isalpha (*p))
1469 return FALSE;
1470
1471 while (1)
1472 {
1473 c = *p++;
1474
1475 if (c == ':')
1476 break;
1477
1478 if (!(g_ascii_isalnum(c) ||
1479 c == '+' ||
1480 c == '-' ||
1481 c == '.'))
1482 return FALSE;
1483 }
1484
1485 if (scheme)
1486 {
1487 *scheme = g_malloc (p - uri);
1488 out = *scheme;
1489
1490 for (in = uri; in < p - 1; in++)
1491 *out++ = g_ascii_tolower (*in);
1492
1493 *out = '\0';
1494 }
1495
1496 hier_part_start = p;
1497 hier_part_end = p + strlen (p);
1498
1499 if (hier_part_start[0] == '/' && hier_part_start[1] == '/')
1500 {
1501 const char *authority_start, *authority_end;
1502 const char *userinfo_start, *userinfo_end;
1503 const char *host_start, *host_end;
1504 const char *port_start;
1505
1506 authority_start = hier_part_start + 2;
1507 /* authority is always followed by / or nothing */
1508 authority_end = memchr (authority_start, '/', hier_part_end - authority_start);
1509
1510 if (authority_end == NULL)
1511 authority_end = hier_part_end;
1512
1513 /* 3.2:
1514 * authority = [ userinfo "@" ] host [ ":" port ]
1515 */
1516
1517 userinfo_end = memchr (authority_start, '@', authority_end - authority_start);
1518
1519 if (userinfo_end)
1520 {
1521 userinfo_start = authority_start;
1522
1523 if (user)
1524 *user = g_uri_unescape_segment (userinfo_start, userinfo_end, NULL);
1525
1526 if (user && *user == NULL)
1527 {
1528 if (scheme)
1529 g_free (*scheme);
1530
1531 return FALSE;
1532 }
1533
1534 host_start = userinfo_end + 1;
1535 }
1536 else
1537 host_start = authority_start;
1538
1539 port_start = memchr (host_start, ':', authority_end - host_start);
1540
1541 if (port_start)
1542 {
1543 host_end = port_start++;
1544
1545 if (port)
1546 *port = g_strndup (port_start, authority_end - port_start);
1547 }
1548 else
1549 host_end = authority_end;
1550
1551 if (host)
1552 *host = g_strndup (host_start, host_end - host_start);
1553
1554 hier_part_start = authority_end;
1555 }
1556
1557 if (path)
1558 *path = g_uri_unescape_segment (hier_part_start, hier_part_end, "/");
1559
1560 return TRUE;
1561 }
1562
1563 gboolean
pluma_gtk_text_iter_regex_search(const GtkTextIter * iter,const gchar * str,GtkTextSearchFlags flags,GtkTextIter * match_start,GtkTextIter * match_end,const GtkTextIter * limit,gboolean forward_search,gchar ** replace_text)1564 pluma_gtk_text_iter_regex_search (const GtkTextIter *iter,
1565 const gchar *str,
1566 GtkTextSearchFlags flags,
1567 GtkTextIter *match_start,
1568 GtkTextIter *match_end,
1569 const GtkTextIter *limit,
1570 gboolean forward_search,
1571 gchar **replace_text)
1572 {
1573 GRegex *regex;
1574 GRegexCompileFlags compile_flags;
1575 GMatchInfo *match_info;
1576 gchar *text;
1577 GtkTextIter *begin_iter;
1578 GtkTextIter *end_iter;
1579 gchar *match_string;
1580 gboolean found;
1581
1582 match_string = "";
1583 compile_flags = G_REGEX_OPTIMIZE | G_REGEX_MULTILINE;
1584
1585 if ((flags & GTK_TEXT_SEARCH_CASE_INSENSITIVE) != 0)
1586 compile_flags |= G_REGEX_CASELESS;
1587
1588 regex = g_regex_new (str,compile_flags,0,NULL);
1589
1590 if (regex == NULL)
1591 return FALSE;
1592
1593 begin_iter = gtk_text_iter_copy (iter);
1594
1595 if (limit == NULL)
1596 {
1597 end_iter = gtk_text_iter_copy (begin_iter);
1598 if (forward_search)
1599 {
1600 gtk_text_buffer_get_end_iter (gtk_text_iter_get_buffer(begin_iter),
1601 end_iter);
1602 }
1603 else
1604 {
1605 gtk_text_buffer_get_start_iter (gtk_text_iter_get_buffer (begin_iter),
1606 end_iter);
1607 }
1608 }
1609 else
1610 {
1611 end_iter = gtk_text_iter_copy (limit);
1612 }
1613
1614 if ((flags & GTK_TEXT_SEARCH_TEXT_ONLY) != 0)
1615 {
1616 if ((flags & GTK_TEXT_SEARCH_VISIBLE_ONLY) != 0)
1617 text = gtk_text_iter_get_visible_text (begin_iter, end_iter);
1618 else
1619 text = gtk_text_iter_get_text (begin_iter, end_iter);
1620 }
1621 else if ((flags & GTK_TEXT_SEARCH_VISIBLE_ONLY) != 0)
1622 text = gtk_text_iter_get_visible_slice (begin_iter, end_iter);
1623 else
1624 text = gtk_text_iter_get_slice (begin_iter, end_iter);
1625
1626 found = g_regex_match (regex, text, 0, &match_info);
1627
1628 if (!found)
1629 goto free_resources;
1630
1631 if((replace_text != NULL) && (*replace_text != NULL))
1632 {
1633 *replace_text = g_match_info_expand_references (match_info,
1634 *replace_text,
1635 NULL);
1636 }
1637
1638 while (g_match_info_matches (match_info))
1639 {
1640 match_string = g_match_info_fetch (match_info, 0);
1641
1642 if (forward_search)
1643 break;
1644
1645 g_match_info_next (match_info, NULL);
1646 }
1647
1648
1649 if (forward_search)
1650 {
1651 gtk_text_iter_forward_search (begin_iter,
1652 match_string,
1653 flags,
1654 match_start,
1655 match_end,
1656 limit);
1657 } else {
1658 gtk_text_iter_backward_search (begin_iter,
1659 match_string,
1660 flags,
1661 match_start,
1662 match_end,
1663 limit);
1664 }
1665
1666 free_resources:
1667 gtk_text_iter_free (begin_iter);
1668 gtk_text_iter_free (end_iter);
1669 g_match_info_free (match_info);
1670 g_regex_unref (regex);
1671 return found;
1672 }
1673