src/gtksourceiter.c

changeset 12465
59859abbe998
parent 12205
1b0471533cf5
equal deleted inserted replaced
12464:bfe63ab6ebcf 12465:59859abbe998
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,

mercurial