/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * Pidgin is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * The following copyright notice applies to this file: * Copyright (C) 2000 - 2005 Paolo Maggi * Copyright (C) 2002, 2003 Jeroen Zwartepoorte * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * You should have received a copy of the GNU Library General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA. * Parts of this file are copied from the gedit and glimmer project. #include "gtksourceiter.h" #define GTK_TEXT_UNKNOWN_CHAR 0xFFFC /* this function acts like g_utf8_offset_to_pointer() except that if it finds a * decomposable character it consumes the decomposition length from the given * offset. So it's useful when the offset was calculated for the normalized * version of str, but we need a pointer to str itself. */ pointer_from_offset_skipping_decomp (const gchar *str, gint offset) gchar *casefold, *normal; q = g_utf8_next_char (p); casefold = g_utf8_casefold (p, q - p); normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); offset -= g_utf8_strlen (normal, -1); g_utf8_strcasestr (const gchar *haystack, const gchar *needle) gchar *caseless_haystack; g_return_val_if_fail (haystack != NULL, NULL); g_return_val_if_fail (needle != NULL, NULL); casefold = g_utf8_casefold (haystack, -1); caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); needle_len = g_utf8_strlen (needle, -1); haystack_len = g_utf8_strlen (caseless_haystack, -1); if (haystack_len < needle_len) p = (gchar*)caseless_haystack; needle_len = strlen (needle); if ((strncmp (p, needle, needle_len) == 0)) ret = pointer_from_offset_skipping_decomp (haystack, i); p = g_utf8_next_char (p); g_free (caseless_haystack); g_utf8_strrcasestr (const gchar *haystack, const gchar *needle) gchar *caseless_haystack; g_return_val_if_fail (haystack != NULL, NULL); g_return_val_if_fail (needle != NULL, NULL); casefold = g_utf8_casefold (haystack, -1); caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); needle_len = g_utf8_strlen (needle, -1); haystack_len = g_utf8_strlen (caseless_haystack, -1); if (haystack_len < needle_len) i = haystack_len - needle_len; p = g_utf8_offset_to_pointer (caseless_haystack, i); needle_len = strlen (needle); if (strncmp (p, needle, needle_len) == 0) ret = pointer_from_offset_skipping_decomp (haystack, i); if (p > caseless_haystack) p = g_utf8_prev_char (p); g_free (caseless_haystack); g_utf8_caselessnmatch (const char *s1, const char *s2, g_return_val_if_fail (s1 != NULL, FALSE); g_return_val_if_fail (s2 != NULL, FALSE); g_return_val_if_fail (n1 > 0, FALSE); g_return_val_if_fail (n2 > 0, FALSE); casefold = g_utf8_casefold (s1, n1); normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); casefold = g_utf8_casefold (s2, n2); normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); len_s1 = strlen (normalized_s1); len_s2 = strlen (normalized_s2); ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0); forward_chars_with_skipping (GtkTextIter *iter, g_return_if_fail (count >= 0); gboolean ignored = FALSE; /* minimal workaround to avoid the infinite loop of bug #168247. * It doesn't fix the problemjust the symptom... if (gtk_text_iter_is_end (iter)) if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR) if (!ignored && skip_invisible && /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE) if (!ignored && skip_decomp) /* being UTF8 correct sucks; this accounts for extra offsets coming from canonical decompositions of UTF8 characters (e.g. accented characters) which g_utf8_normalize() performs */ buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer); normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD); i -= (g_utf8_strlen (normal, -1) - 1); gtk_text_iter_forward_char (iter); lines_match (const GtkTextIter *start, GtkTextIter *match_start, if (*lines == NULL || **lines == '\0') gtk_text_iter_forward_line (&next); /* No more text in buffer, but *lines is nonempty */ if (gtk_text_iter_equal (start, &next)) line_text = gtk_text_iter_get_visible_slice (start, &next); line_text = gtk_text_iter_get_slice (start, &next); line_text = gtk_text_iter_get_visible_text (start, &next); line_text = gtk_text_iter_get_text (start, &next); if (match_start) /* if this is the first line we're matching */ found = g_utf8_strcasestr (line_text, *lines); /* If it's not the first line, we have to match from the if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text), /* Get offset to start of search string */ offset = g_utf8_strlen (line_text, found - line_text); /* If match start needs to be returned, set it to the * start of the search string. forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE); /* Go to end of search string */ forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE); /* pass NULL for match_start, since we don't need to find the return lines_match (&next, lines, visible_only, slice, NULL, match_end); backward_lines_match (const GtkTextIter *start, GtkTextIter *match_start, if (*lines == NULL || **lines == '\0') if (gtk_text_iter_get_line_offset (&next) == 0) if (!gtk_text_iter_backward_line (&next)) gtk_text_iter_set_line_offset (&next, 0); line_text = gtk_text_iter_get_visible_slice (&next, &line); line_text = gtk_text_iter_get_slice (&next, &line); line_text = gtk_text_iter_get_visible_text (&next, &line); line_text = gtk_text_iter_get_text (&next, &line); if (match_start) /* if this is the first line we're matching */ found = g_utf8_strrcasestr (line_text, *lines); /* If it's not the first line, we have to match from the if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text), /* Get offset to start of search string */ offset = g_utf8_strlen (line_text, found - line_text); forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE); /* If match start needs to be returned, set it to the * start of the search string. /* Go to end of search string */ forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE); /* try to match the rest of the lines forward, passing NULL * for match_start so lines_match will try to match the entire return lines_match (&next, lines, visible_only, /* strsplit () that retains the delimiter as part of the string. */ strbreakup (const char *string, GSList *string_list = NULL, *slist; gchar **str_array, *s, *casefold, *new_string; g_return_val_if_fail (string != NULL, NULL); g_return_val_if_fail (delimiter != NULL, NULL); s = strstr (string, delimiter); guint delimiter_len = strlen (delimiter); len = s - string + delimiter_len; new_string = g_new (gchar, len + 1); strncpy (new_string, string, len); casefold = g_utf8_casefold (new_string, -1); new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); string_list = g_slist_prepend (string_list, new_string); string = s + delimiter_len; s = strstr (string, delimiter); } while (--max_tokens && s); casefold = g_utf8_casefold (string, -1); new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); string_list = g_slist_prepend (string_list, new_string); str_array = g_new (gchar*, n); for (slist = string_list; slist; slist = slist->next) str_array[i--] = slist->data; g_slist_free (string_list); static GtkTextSearchFlags _source_flags_to_text_flags(GtkSourceSearchFlags flags) GtkTextSearchFlags text_flags = 0; if (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) text_flags |= GTK_TEXT_SEARCH_VISIBLE_ONLY; if (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) text_flags |= GTK_TEXT_SEARCH_TEXT_ONLY; * gtk_source_iter_forward_search: * @iter: start of search. * @flags: flags affecting how the search is done. * @match_start: return location for start of match, or %%NULL. * @match_end: return location for end of match, or %%NULL. * @limit: bound for the search, or %%NULL for the end of the buffer. * Searches forward for @str. Any match is returned by setting * @match_start to the first character of the match and @match_end to the * first character after the match. The search will not continue past * @limit. Note that a search is a linear or O(n) operation, so you * may wish to use @limit to avoid locking up your UI on large * If the #GTK_SOURCE_SEARCH_VISIBLE_ONLY flag is present, the match may * have invisible text interspersed in @str. i.e. @str will be a * possibly-noncontiguous subsequence of the matched range. similarly, * if you specify #GTK_SOURCE_SEARCH_TEXT_ONLY, the match may have * pixbufs or child widgets mixed inside the matched range. If these * flags are not given, the match must be exact; the special 0xFFFC * character in @str will match embedded pixbufs or child widgets. * If you specify the #GTK_SOURCE_SEARCH_CASE_INSENSITIVE flag, the text will * be matched regardless of what case it is in. * Same as gtk_text_iter_forward_search(), but supports case insensitive * Return value: whether a match was found. gtk_source_iter_forward_search (const GtkTextIter *iter, GtkSourceSearchFlags flags, GtkTextIter *match_start, const GtkTextIter *limit) g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (str != NULL, FALSE); if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0) return gtk_text_iter_forward_search (iter, str, _source_flags_to_text_flags(flags), if (limit && gtk_text_iter_compare (iter, limit) >= 0) /* If we can move one char, return the empty string there */ if (gtk_text_iter_forward_char (&match)) if (limit && gtk_text_iter_equal (&match, limit)) visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0; slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0; lines = strbreakup (str, "\n", -1); /* This loop has an inefficient worst-case, where * gtk_text_iter_get_text () is called repeatedly on if (limit && gtk_text_iter_compare (&search, limit) >= 0) if (lines_match (&search, (const gchar**)lines, visible_only, slice, &match, &end)) (limit && gtk_text_iter_compare (&end, limit) <= 0)) } while (gtk_text_iter_forward_line (&search)); g_strfreev ((gchar**)lines); * gtk_source_iter_backward_search: * @iter: a #GtkTextIter where the search begins. * @flags: bitmask of flags affecting the search. * @match_start: return location for start of match, or %%NULL. * @match_end: return location for end of match, or %%NULL. * @limit: location of last possible @match_start, or %%NULL for start of buffer. * Same as gtk_text_iter_backward_search(), but supports case insensitive * Return value: whether a match was found. gtk_source_iter_backward_search (const GtkTextIter *iter, GtkSourceSearchFlags flags, GtkTextIter *match_start, const GtkTextIter *limit) g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (str != NULL, FALSE); if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0) return gtk_text_iter_backward_search (iter, str, _source_flags_to_text_flags(flags), if (limit && gtk_text_iter_compare (iter, limit) <= 0) /* If we can move one char, return the empty string there */ if (gtk_text_iter_backward_char (&match)) if (limit && gtk_text_iter_equal (&match, limit)) visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0; slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0; lines = strbreakup (str, "\n", -1); /* This loop has an inefficient worst-case, where * gtk_text_iter_get_text () is called repeatedly on if (limit && gtk_text_iter_compare (&search, limit) <= 0) if (backward_lines_match (&search, (const gchar**)lines, visible_only, slice, &match, &end)) if (limit == NULL || (limit && gtk_text_iter_compare (&end, limit) > 0)) if (gtk_text_iter_get_line_offset (&search) == 0) if (!gtk_text_iter_backward_line (&search)) gtk_text_iter_set_line_offset (&search, 0); g_strfreev ((gchar**)lines); * gtk_source_iter_find_matching_bracket is implemented in gtksourcebuffer.c