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,