Mercurial > pidgin
comparison src/gtksourceiter.c @ 12465:ae4ae98bca20
[gaim-migrate @ 14775]
A patch from Bleeter, with some copyright stuff updated by me. This syncs gtksourceiter.c and gtksourceiter.h with upstream. It doesn't seem to break anything and supposedly has bug fixes. Let's see what happens....
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Mon, 12 Dec 2005 08:08:07 +0000 |
parents | 0e87d5e28888 |
children |
comparison
equal
deleted
inserted
replaced
12464:2b08a27c2342 | 12465:ae4ae98bca20 |
---|---|
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- | 1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- |
2 * @file gtksourceiter.h GTK+ Source iterator | 2 * gtksourceiter.c |
3 * @ingroup gtkui | |
4 * | |
5 * gaim | |
6 * | 3 * |
7 * Gaim is the legal property of its developers, whose names are too numerous | 4 * Gaim is the legal property of its developers, whose names are too numerous |
8 * to list here. Please refer to the COPYRIGHT file distributed with this | 5 * to list here. Please refer to the COPYRIGHT file distributed with this |
9 * source distribution. | 6 * source distribution. |
7 * | |
8 * The following copyright notice applies to this file: | |
9 * | |
10 * Copyright (C) 2000 - 2005 Paolo Maggi | |
11 * Copyright (C) 2002, 2003 Jeroen Zwartepoorte | |
10 * | 12 * |
11 * This program is free software; you can redistribute it and/or modify | 13 * This program is free software; you can redistribute it and/or modify |
12 * it under the terms of the GNU Library General Public License as published by | 14 * it under the terms of the GNU Library General Public License as published by |
13 * the Free Software Foundation; either version 2 of the License, or | 15 * the Free Software Foundation; either version 2 of the License, or |
14 * (at your option) any later version. | 16 * (at your option) any later version. |
34 #include <string.h> | 36 #include <string.h> |
35 #include "gtksourceiter.h" | 37 #include "gtksourceiter.h" |
36 | 38 |
37 #define GTK_TEXT_UNKNOWN_CHAR 0xFFFC | 39 #define GTK_TEXT_UNKNOWN_CHAR 0xFFFC |
38 | 40 |
39 static gchar * | 41 /* this function acts like g_utf8_offset_to_pointer() except that if it finds a |
42 * decomposable character it consumes the decomposition length from the given | |
43 * offset. So it's useful when the offset was calculated for the normalized | |
44 * version of str, but we need a pointer to str itself. */ | |
45 static const gchar * | |
46 pointer_from_offset_skipping_decomp (const gchar *str, gint offset) | |
47 { | |
48 gchar *casefold, *normal; | |
49 const gchar *p, *q; | |
50 | |
51 p = str; | |
52 while (offset > 0) | |
53 { | |
54 q = g_utf8_next_char (p); | |
55 casefold = g_utf8_casefold (p, q - p); | |
56 normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); | |
57 offset -= g_utf8_strlen (normal, -1); | |
58 g_free (casefold); | |
59 g_free (normal); | |
60 p = q; | |
61 } | |
62 return p; | |
63 } | |
64 | |
65 static const gchar * | |
40 g_utf8_strcasestr (const gchar *haystack, const gchar *needle) | 66 g_utf8_strcasestr (const gchar *haystack, const gchar *needle) |
41 { | 67 { |
42 gsize needle_len; | 68 gsize needle_len; |
43 gsize haystack_len; | 69 gsize haystack_len; |
44 gchar *ret = NULL; | 70 const gchar *ret = NULL; |
45 gchar *p; | 71 gchar *p; |
46 gchar *casefold; | 72 gchar *casefold; |
47 gchar *caseless_haystack; | 73 gchar *caseless_haystack; |
48 gint i; | 74 gint i; |
49 | 75 |
50 g_return_val_if_fail (haystack != NULL, NULL); | 76 g_return_val_if_fail (haystack != NULL, NULL); |
51 g_return_val_if_fail (needle != NULL, NULL); | 77 g_return_val_if_fail (needle != NULL, NULL); |
52 | 78 |
53 casefold = g_utf8_casefold (haystack, -1); | 79 casefold = g_utf8_casefold (haystack, -1); |
54 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | 80 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); |
55 g_free (casefold); | 81 g_free (casefold); |
56 | 82 |
57 needle_len = g_utf8_strlen (needle, -1); | 83 needle_len = g_utf8_strlen (needle, -1); |
58 haystack_len = g_utf8_strlen (caseless_haystack, -1); | 84 haystack_len = g_utf8_strlen (caseless_haystack, -1); |
59 | 85 |
75 | 101 |
76 while (*p) | 102 while (*p) |
77 { | 103 { |
78 if ((strncmp (p, needle, needle_len) == 0)) | 104 if ((strncmp (p, needle, needle_len) == 0)) |
79 { | 105 { |
80 ret = g_utf8_offset_to_pointer (haystack, i); | 106 ret = pointer_from_offset_skipping_decomp (haystack, i); |
81 goto finally_1; | 107 goto finally_1; |
82 } | 108 } |
83 | 109 |
84 p = g_utf8_next_char (p); | 110 p = g_utf8_next_char (p); |
85 i++; | 111 i++; |
89 g_free (caseless_haystack); | 115 g_free (caseless_haystack); |
90 | 116 |
91 return ret; | 117 return ret; |
92 } | 118 } |
93 | 119 |
94 static gchar * | 120 static const gchar * |
95 g_utf8_strrcasestr (const gchar *haystack, const gchar *needle) | 121 g_utf8_strrcasestr (const gchar *haystack, const gchar *needle) |
96 { | 122 { |
97 gsize needle_len; | 123 gsize needle_len; |
98 gsize haystack_len; | 124 gsize haystack_len; |
99 gchar *ret = NULL; | 125 const gchar *ret = NULL; |
100 gchar *p; | 126 gchar *p; |
101 gchar *casefold; | 127 gchar *casefold; |
102 gchar *caseless_haystack; | 128 gchar *caseless_haystack; |
103 gint i; | 129 gint i; |
104 | 130 |
105 g_return_val_if_fail (haystack != NULL, NULL); | 131 g_return_val_if_fail (haystack != NULL, NULL); |
106 g_return_val_if_fail (needle != NULL, NULL); | 132 g_return_val_if_fail (needle != NULL, NULL); |
107 | 133 |
108 casefold = g_utf8_casefold (haystack, -1); | 134 casefold = g_utf8_casefold (haystack, -1); |
109 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | 135 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); |
110 g_free (casefold); | 136 g_free (casefold); |
111 | 137 |
112 needle_len = g_utf8_strlen (needle, -1); | 138 needle_len = g_utf8_strlen (needle, -1); |
113 haystack_len = g_utf8_strlen (caseless_haystack, -1); | 139 haystack_len = g_utf8_strlen (caseless_haystack, -1); |
114 | 140 |
122 { | 148 { |
123 ret = NULL; | 149 ret = NULL; |
124 goto finally_1; | 150 goto finally_1; |
125 } | 151 } |
126 | 152 |
127 haystack_len = strlen (caseless_haystack); | 153 i = haystack_len - needle_len; |
154 p = g_utf8_offset_to_pointer (caseless_haystack, i); | |
128 needle_len = strlen (needle); | 155 needle_len = strlen (needle); |
129 p = (gchar *)caseless_haystack + haystack_len - needle_len; | |
130 i = haystack_len - needle_len; | |
131 | 156 |
132 while (p >= caseless_haystack) | 157 while (p >= caseless_haystack) |
133 { | 158 { |
134 if (strncasecmp (p, needle, needle_len) == 0) | 159 if (strncmp (p, needle, needle_len) == 0) |
135 { | 160 { |
136 ret = g_utf8_offset_to_pointer (haystack, i); | 161 ret = pointer_from_offset_skipping_decomp (haystack, i); |
137 goto finally_1; | 162 goto finally_1; |
138 } | 163 } |
139 | 164 |
140 p = g_utf8_prev_char (p); | 165 p = g_utf8_prev_char (p); |
141 i--; | 166 i--; |
162 g_return_val_if_fail (s2 != NULL, FALSE); | 187 g_return_val_if_fail (s2 != NULL, FALSE); |
163 g_return_val_if_fail (n1 > 0, FALSE); | 188 g_return_val_if_fail (n1 > 0, FALSE); |
164 g_return_val_if_fail (n2 > 0, FALSE); | 189 g_return_val_if_fail (n2 > 0, FALSE); |
165 | 190 |
166 casefold = g_utf8_casefold (s1, n1); | 191 casefold = g_utf8_casefold (s1, n1); |
167 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | 192 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); |
168 g_free (casefold); | 193 g_free (casefold); |
169 | 194 |
170 casefold = g_utf8_casefold (s2, n2); | 195 casefold = g_utf8_casefold (s2, n2); |
171 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | 196 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); |
172 g_free (casefold); | 197 g_free (casefold); |
173 | 198 |
174 len_s1 = strlen (normalized_s1); | 199 len_s1 = strlen (normalized_s1); |
175 len_s2 = strlen (normalized_s2); | 200 len_s2 = strlen (normalized_s2); |
176 | 201 |
188 | 213 |
189 static void | 214 static void |
190 forward_chars_with_skipping (GtkTextIter *iter, | 215 forward_chars_with_skipping (GtkTextIter *iter, |
191 gint count, | 216 gint count, |
192 gboolean skip_invisible, | 217 gboolean skip_invisible, |
193 gboolean skip_nontext) | 218 gboolean skip_nontext, |
219 gboolean skip_decomp) | |
194 { | 220 { |
195 gint i; | 221 gint i; |
196 | 222 |
197 g_return_if_fail (count >= 0); | 223 g_return_if_fail (count >= 0); |
198 | 224 |
199 i = count; | 225 i = count; |
200 | 226 |
201 while (i > 0) | 227 while (i > 0) |
202 { | 228 { |
203 gboolean ignored = FALSE; | 229 gboolean ignored = FALSE; |
230 | |
231 /* minimal workaround to avoid the infinite loop of bug #168247. | |
232 * It doesn't fix the problemjust the symptom... | |
233 */ | |
234 if (gtk_text_iter_is_end (iter)) | |
235 return; | |
204 | 236 |
205 if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR) | 237 if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR) |
206 ignored = TRUE; | 238 ignored = TRUE; |
207 | 239 |
208 #if 0 | 240 #if 0 |
209 if (!ignored && skip_invisible && | 241 if (!ignored && skip_invisible && |
210 _gtk_text_btree_char_is_invisible (iter)) | 242 /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE) |
211 ignored = TRUE; | 243 ignored = TRUE; |
212 #endif | 244 #endif |
245 | |
246 if (!ignored && skip_decomp) | |
247 { | |
248 /* being UTF8 correct sucks; this accounts for extra | |
249 offsets coming from canonical decompositions of | |
250 UTF8 characters (e.g. accented characters) which | |
251 g_utf8_normalize() performs */ | |
252 gchar *normal; | |
253 gchar buffer[6]; | |
254 gint buffer_len; | |
255 | |
256 buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer); | |
257 normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD); | |
258 i -= (g_utf8_strlen (normal, -1) - 1); | |
259 g_free (normal); | |
260 } | |
213 | 261 |
214 gtk_text_iter_forward_char (iter); | 262 gtk_text_iter_forward_char (iter); |
215 | 263 |
216 if (!ignored) | 264 if (!ignored) |
217 --i; | 265 --i; |
290 next = *start; | 338 next = *start; |
291 | 339 |
292 /* If match start needs to be returned, set it to the | 340 /* If match start needs to be returned, set it to the |
293 * start of the search string. | 341 * start of the search string. |
294 */ | 342 */ |
343 forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE); | |
295 if (match_start) | 344 if (match_start) |
296 { | 345 { |
297 *match_start = next; | 346 *match_start = next; |
298 | |
299 forward_chars_with_skipping (match_start, offset, | |
300 visible_only, !slice); | |
301 } | 347 } |
302 | 348 |
303 /* Go to end of search string */ | 349 /* Go to end of search string */ |
304 offset += g_utf8_strlen (*lines, -1); | 350 forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE); |
305 | |
306 forward_chars_with_skipping (&next, offset, visible_only, !slice); | |
307 | 351 |
308 g_free (line_text); | 352 g_free (line_text); |
309 | 353 |
310 ++lines; | 354 ++lines; |
311 | 355 |
387 } | 431 } |
388 | 432 |
389 /* Get offset to start of search string */ | 433 /* Get offset to start of search string */ |
390 offset = g_utf8_strlen (line_text, found - line_text); | 434 offset = g_utf8_strlen (line_text, found - line_text); |
391 | 435 |
436 forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE); | |
437 | |
392 /* If match start needs to be returned, set it to the | 438 /* If match start needs to be returned, set it to the |
393 * start of the search string. | 439 * start of the search string. |
394 */ | 440 */ |
395 if (match_start) | 441 if (match_start) |
396 { | 442 { |
397 *match_start = next; | 443 *match_start = next; |
398 gtk_text_iter_set_visible_line_offset (match_start, offset); | |
399 } | 444 } |
400 | 445 |
401 /* Go to end of search string */ | 446 /* Go to end of search string */ |
402 offset += g_utf8_strlen (*lines, -1); | 447 forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE); |
403 | |
404 forward_chars_with_skipping (&next, offset, visible_only, !slice); | |
405 | 448 |
406 g_free (line_text); | 449 g_free (line_text); |
407 | 450 |
408 ++lines; | 451 ++lines; |
409 | 452 |
446 new_string = g_new (gchar, len + 1); | 489 new_string = g_new (gchar, len + 1); |
447 strncpy (new_string, string, len); | 490 strncpy (new_string, string, len); |
448 new_string[len] = 0; | 491 new_string[len] = 0; |
449 casefold = g_utf8_casefold (new_string, -1); | 492 casefold = g_utf8_casefold (new_string, -1); |
450 g_free (new_string); | 493 g_free (new_string); |
451 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | 494 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); |
452 g_free (casefold); | 495 g_free (casefold); |
453 string_list = g_slist_prepend (string_list, new_string); | 496 string_list = g_slist_prepend (string_list, new_string); |
454 n++; | 497 n++; |
455 string = s + delimiter_len; | 498 string = s + delimiter_len; |
456 s = strstr (string, delimiter); | 499 s = strstr (string, delimiter); |
459 | 502 |
460 if (*string) | 503 if (*string) |
461 { | 504 { |
462 n++; | 505 n++; |
463 casefold = g_utf8_casefold (string, -1); | 506 casefold = g_utf8_casefold (string, -1); |
464 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | 507 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); |
465 g_free (casefold); | 508 g_free (casefold); |
466 string_list = g_slist_prepend (string_list, new_string); | 509 string_list = g_slist_prepend (string_list, new_string); |
467 } | 510 } |
468 | 511 |
469 str_array = g_new (gchar*, n); | 512 str_array = g_new (gchar*, n); |
479 return str_array; | 522 return str_array; |
480 } | 523 } |
481 | 524 |
482 /** | 525 /** |
483 * gtk_source_iter_forward_search: | 526 * gtk_source_iter_forward_search: |
527 * @iter: start of search. | |
528 * @str: a search string. | |
529 * @flags: flags affecting how the search is done. | |
530 * @match_start: return location for start of match, or %%NULL. | |
531 * @match_end: return location for end of match, or %%NULL. | |
532 * @limit: bound for the search, or %%NULL for the end of the buffer. | |
484 * | 533 * |
485 * Searches forward for @str. Any match is returned by setting | 534 * Searches forward for @str. Any match is returned by setting |
486 * @match_start to the first character of the match and @match_end to the | 535 * @match_start to the first character of the match and @match_end to the |
487 * first character after the match. The search will not continue past | 536 * first character after the match. The search will not continue past |
488 * @limit. Note that a search is a linear or O(n) operation, so you | 537 * @limit. Note that a search is a linear or O(n) operation, so you |
500 * be matched regardless of what case it is in. | 549 * be matched regardless of what case it is in. |
501 * | 550 * |
502 * Same as gtk_text_iter_forward_search(), but supports case insensitive | 551 * Same as gtk_text_iter_forward_search(), but supports case insensitive |
503 * searching. | 552 * searching. |
504 * | 553 * |
505 * @param iter start of search | 554 * Return value: whether a match was found. |
506 * @param str a search string | 555 **/ |
507 * @param flags flags affecting how the search is done | |
508 * @param match_start return location for start of match, or %NULL | |
509 * @param match_end return location for end of match, or %NULL | |
510 * @param limit bound for the search, or %NULL for the end of the buffer | |
511 * @return returns whether a match was found | |
512 */ | |
513 gboolean | 556 gboolean |
514 gtk_source_iter_forward_search (const GtkTextIter *iter, | 557 gtk_source_iter_forward_search (const GtkTextIter *iter, |
515 const gchar *str, | 558 const gchar *str, |
516 GtkSourceSearchFlags flags, | 559 GtkSourceSearchFlags flags, |
517 GtkTextIter *match_start, | 560 GtkTextIter *match_start, |
578 break; | 621 break; |
579 | 622 |
580 if (lines_match (&search, (const gchar**)lines, | 623 if (lines_match (&search, (const gchar**)lines, |
581 visible_only, slice, &match, &end)) | 624 visible_only, slice, &match, &end)) |
582 { | 625 { |
583 if (limit == NULL || (limit && | 626 if (limit == NULL || |
584 gtk_text_iter_compare (&end, limit) < 0)) | 627 (limit && gtk_text_iter_compare (&end, limit) <= 0)) |
585 { | 628 { |
586 retval = TRUE; | 629 retval = TRUE; |
587 | 630 |
588 if (match_start) | 631 if (match_start) |
589 *match_start = match; | 632 *match_start = match; |
599 return retval; | 642 return retval; |
600 } | 643 } |
601 | 644 |
602 /** | 645 /** |
603 * gtk_source_iter_backward_search: | 646 * gtk_source_iter_backward_search: |
647 * @iter: a #GtkTextIter where the search begins. | |
648 * @str: search string. | |
649 * @flags: bitmask of flags affecting the search. | |
650 * @match_start: return location for start of match, or %%NULL. | |
651 * @match_end: return location for end of match, or %%NULL. | |
652 * @limit: location of last possible @match_start, or %%NULL for start of buffer. | |
604 * | 653 * |
605 * Same as gtk_text_iter_backward_search(), but supports case insensitive | 654 * Same as gtk_text_iter_backward_search(), but supports case insensitive |
606 * searching. | 655 * searching. |
607 * | 656 * |
608 * @param iter a #GtkTextIter where the search begins | 657 * Return value: whether a match was found. |
609 * @param str search string | 658 **/ |
610 * @param flags bitmask of flags affecting the search | |
611 * @param match_start return location for start of match, or %NULL | |
612 * @param match_end return location for end of match, or %NULL | |
613 * @param limit location of last possible @match_start, or %NULL for start of buffer | |
614 * @return returns whether a match was found | |
615 */ | |
616 gboolean | 659 gboolean |
617 gtk_source_iter_backward_search (const GtkTextIter *iter, | 660 gtk_source_iter_backward_search (const GtkTextIter *iter, |
618 const gchar *str, | 661 const gchar *str, |
619 GtkSourceSearchFlags flags, | 662 GtkSourceSearchFlags flags, |
620 GtkTextIter *match_start, | 663 GtkTextIter *match_start, |