Mercurial > pidgin
annotate src/gtksourceiter.c @ 12373:508512caa22e
[gaim-migrate @ 14677]
deryni pointed out that ggp_buddy_get_name leaks when the user isn't a buddy. I went to fix that and tweaked a bunch of code.
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Tue, 06 Dec 2005 19:50:47 +0000 |
parents | 0e87d5e28888 |
children | ae4ae98bca20 |
rev | line source |
---|---|
7358 | 1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- |
10297 | 2 * @file gtksourceiter.h GTK+ Source iterator |
3 * @ingroup gtkui | |
4 * | |
5 * gaim | |
7358 | 6 * |
8046 | 7 * 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 | |
9 * source distribution. | |
7358 | 10 * |
11 * 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 | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU Library General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU Library General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
24 */ | |
25 | |
26 /* | |
27 * Parts of this file are copied from the gedit and glimmer project. | |
28 */ | |
29 | |
30 #ifdef HAVE_CONFIG_H | |
31 #include <config.h> | |
32 #endif | |
33 | |
34 #include <string.h> | |
35 #include "gtksourceiter.h" | |
36 | |
37 #define GTK_TEXT_UNKNOWN_CHAR 0xFFFC | |
38 | |
39 static gchar * | |
40 g_utf8_strcasestr (const gchar *haystack, const gchar *needle) | |
41 { | |
42 gsize needle_len; | |
43 gsize haystack_len; | |
44 gchar *ret = NULL; | |
45 gchar *p; | |
46 gchar *casefold; | |
47 gchar *caseless_haystack; | |
48 gint i; | |
49 | |
50 g_return_val_if_fail (haystack != NULL, NULL); | |
51 g_return_val_if_fail (needle != NULL, NULL); | |
52 | |
53 casefold = g_utf8_casefold (haystack, -1); | |
54 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | |
55 g_free (casefold); | |
56 | |
57 needle_len = g_utf8_strlen (needle, -1); | |
58 haystack_len = g_utf8_strlen (caseless_haystack, -1); | |
59 | |
60 if (needle_len == 0) | |
61 { | |
62 ret = (gchar *)haystack; | |
63 goto finally_1; | |
64 } | |
65 | |
66 if (haystack_len < needle_len) | |
67 { | |
68 ret = NULL; | |
69 goto finally_1; | |
70 } | |
71 | |
72 p = (gchar*)caseless_haystack; | |
73 needle_len = strlen (needle); | |
74 i = 0; | |
75 | |
76 while (*p) | |
77 { | |
78 if ((strncmp (p, needle, needle_len) == 0)) | |
79 { | |
80 ret = g_utf8_offset_to_pointer (haystack, i); | |
81 goto finally_1; | |
82 } | |
83 | |
84 p = g_utf8_next_char (p); | |
85 i++; | |
86 } | |
87 | |
88 finally_1: | |
89 g_free (caseless_haystack); | |
90 | |
91 return ret; | |
92 } | |
93 | |
94 static gchar * | |
95 g_utf8_strrcasestr (const gchar *haystack, const gchar *needle) | |
96 { | |
97 gsize needle_len; | |
98 gsize haystack_len; | |
99 gchar *ret = NULL; | |
100 gchar *p; | |
101 gchar *casefold; | |
102 gchar *caseless_haystack; | |
103 gint i; | |
104 | |
105 g_return_val_if_fail (haystack != NULL, NULL); | |
106 g_return_val_if_fail (needle != NULL, NULL); | |
107 | |
108 casefold = g_utf8_casefold (haystack, -1); | |
109 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | |
110 g_free (casefold); | |
111 | |
112 needle_len = g_utf8_strlen (needle, -1); | |
113 haystack_len = g_utf8_strlen (caseless_haystack, -1); | |
114 | |
115 if (needle_len == 0) | |
116 { | |
117 ret = (gchar *)haystack; | |
118 goto finally_1; | |
119 } | |
120 | |
121 if (haystack_len < needle_len) | |
122 { | |
123 ret = NULL; | |
124 goto finally_1; | |
125 } | |
126 | |
127 haystack_len = strlen (caseless_haystack); | |
128 needle_len = strlen (needle); | |
129 p = (gchar *)caseless_haystack + haystack_len - needle_len; | |
130 i = haystack_len - needle_len; | |
131 | |
132 while (p >= caseless_haystack) | |
133 { | |
134 if (strncasecmp (p, needle, needle_len) == 0) | |
135 { | |
136 ret = g_utf8_offset_to_pointer (haystack, i); | |
137 goto finally_1; | |
138 } | |
139 | |
140 p = g_utf8_prev_char (p); | |
141 i--; | |
142 } | |
143 | |
144 finally_1: | |
145 g_free (caseless_haystack); | |
146 | |
147 return ret; | |
148 } | |
149 | |
150 static gboolean | |
151 g_utf8_caselessnmatch (const char *s1, const char *s2, | |
152 gssize n1, gssize n2) | |
153 { | |
154 gchar *casefold; | |
155 gchar *normalized_s1; | |
156 gchar *normalized_s2; | |
157 gint len_s1; | |
158 gint len_s2; | |
159 gboolean ret = FALSE; | |
160 | |
161 g_return_val_if_fail (s1 != NULL, FALSE); | |
162 g_return_val_if_fail (s2 != NULL, FALSE); | |
163 g_return_val_if_fail (n1 > 0, FALSE); | |
164 g_return_val_if_fail (n2 > 0, FALSE); | |
165 | |
166 casefold = g_utf8_casefold (s1, n1); | |
167 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | |
168 g_free (casefold); | |
169 | |
170 casefold = g_utf8_casefold (s2, n2); | |
171 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | |
172 g_free (casefold); | |
173 | |
174 len_s1 = strlen (normalized_s1); | |
175 len_s2 = strlen (normalized_s2); | |
176 | |
177 if (len_s1 < len_s2) | |
178 goto finally_2; | |
179 | |
180 ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0); | |
181 | |
182 finally_2: | |
183 g_free (normalized_s1); | |
184 g_free (normalized_s2); | |
185 | |
186 return ret; | |
187 } | |
188 | |
189 static void | |
190 forward_chars_with_skipping (GtkTextIter *iter, | |
191 gint count, | |
192 gboolean skip_invisible, | |
193 gboolean skip_nontext) | |
194 { | |
195 gint i; | |
196 | |
197 g_return_if_fail (count >= 0); | |
198 | |
199 i = count; | |
200 | |
201 while (i > 0) | |
202 { | |
203 gboolean ignored = FALSE; | |
204 | |
205 if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR) | |
206 ignored = TRUE; | |
207 | |
12205
0e87d5e28888
[gaim-migrate @ 14507]
Richard Laager <rlaager@wiktel.com>
parents:
10297
diff
changeset
|
208 #if 0 |
7358 | 209 if (!ignored && skip_invisible && |
12205
0e87d5e28888
[gaim-migrate @ 14507]
Richard Laager <rlaager@wiktel.com>
parents:
10297
diff
changeset
|
210 _gtk_text_btree_char_is_invisible (iter)) |
7358 | 211 ignored = TRUE; |
12205
0e87d5e28888
[gaim-migrate @ 14507]
Richard Laager <rlaager@wiktel.com>
parents:
10297
diff
changeset
|
212 #endif |
7358 | 213 |
214 gtk_text_iter_forward_char (iter); | |
215 | |
216 if (!ignored) | |
217 --i; | |
218 } | |
219 } | |
220 | |
221 static gboolean | |
222 lines_match (const GtkTextIter *start, | |
223 const gchar **lines, | |
224 gboolean visible_only, | |
225 gboolean slice, | |
226 GtkTextIter *match_start, | |
227 GtkTextIter *match_end) | |
228 { | |
229 GtkTextIter next; | |
230 gchar *line_text; | |
231 const gchar *found; | |
232 gint offset; | |
233 | |
234 if (*lines == NULL || **lines == '\0') | |
235 { | |
236 if (match_start) | |
237 *match_start = *start; | |
238 if (match_end) | |
239 *match_end = *start; | |
240 return TRUE; | |
241 } | |
242 | |
243 next = *start; | |
244 gtk_text_iter_forward_line (&next); | |
245 | |
246 /* No more text in buffer, but *lines is nonempty */ | |
247 if (gtk_text_iter_equal (start, &next)) | |
248 return FALSE; | |
249 | |
250 if (slice) | |
251 { | |
252 if (visible_only) | |
253 line_text = gtk_text_iter_get_visible_slice (start, &next); | |
254 else | |
255 line_text = gtk_text_iter_get_slice (start, &next); | |
256 } | |
257 else | |
258 { | |
259 if (visible_only) | |
260 line_text = gtk_text_iter_get_visible_text (start, &next); | |
261 else | |
262 line_text = gtk_text_iter_get_text (start, &next); | |
263 } | |
264 | |
265 if (match_start) /* if this is the first line we're matching */ | |
266 { | |
267 found = g_utf8_strcasestr (line_text, *lines); | |
268 } | |
269 else | |
270 { | |
271 /* If it's not the first line, we have to match from the | |
272 * start of the line. | |
273 */ | |
274 if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text), | |
275 strlen (*lines))) | |
276 found = line_text; | |
277 else | |
278 found = NULL; | |
279 } | |
280 | |
281 if (found == NULL) | |
282 { | |
283 g_free (line_text); | |
284 return FALSE; | |
285 } | |
286 | |
287 /* Get offset to start of search string */ | |
288 offset = g_utf8_strlen (line_text, found - line_text); | |
289 | |
290 next = *start; | |
291 | |
292 /* If match start needs to be returned, set it to the | |
293 * start of the search string. | |
294 */ | |
295 if (match_start) | |
296 { | |
297 *match_start = next; | |
298 | |
299 forward_chars_with_skipping (match_start, offset, | |
300 visible_only, !slice); | |
301 } | |
302 | |
303 /* Go to end of search string */ | |
304 offset += g_utf8_strlen (*lines, -1); | |
305 | |
306 forward_chars_with_skipping (&next, offset, visible_only, !slice); | |
307 | |
308 g_free (line_text); | |
309 | |
310 ++lines; | |
311 | |
312 if (match_end) | |
313 *match_end = next; | |
314 | |
315 /* pass NULL for match_start, since we don't need to find the | |
316 * start again. | |
317 */ | |
318 return lines_match (&next, lines, visible_only, slice, NULL, match_end); | |
319 } | |
320 | |
321 static gboolean | |
322 backward_lines_match (const GtkTextIter *start, | |
323 const gchar **lines, | |
324 gboolean visible_only, | |
325 gboolean slice, | |
326 GtkTextIter *match_start, | |
327 GtkTextIter *match_end) | |
328 { | |
329 GtkTextIter line, next; | |
330 gchar *line_text; | |
331 const gchar *found; | |
332 gint offset; | |
333 | |
334 if (*lines == NULL || **lines == '\0') | |
335 { | |
336 if (match_start) | |
337 *match_start = *start; | |
338 if (match_end) | |
339 *match_end = *start; | |
340 return TRUE; | |
341 } | |
342 | |
343 line = next = *start; | |
344 if (gtk_text_iter_get_line_offset (&next) == 0) | |
345 { | |
346 if (!gtk_text_iter_backward_line (&next)) | |
347 return FALSE; | |
348 } | |
349 else | |
350 gtk_text_iter_set_line_offset (&next, 0); | |
351 | |
352 if (slice) | |
353 { | |
354 if (visible_only) | |
355 line_text = gtk_text_iter_get_visible_slice (&next, &line); | |
356 else | |
357 line_text = gtk_text_iter_get_slice (&next, &line); | |
358 } | |
359 else | |
360 { | |
361 if (visible_only) | |
362 line_text = gtk_text_iter_get_visible_text (&next, &line); | |
363 else | |
364 line_text = gtk_text_iter_get_text (&next, &line); | |
365 } | |
366 | |
367 if (match_start) /* if this is the first line we're matching */ | |
368 { | |
369 found = g_utf8_strrcasestr (line_text, *lines); | |
370 } | |
371 else | |
372 { | |
373 /* If it's not the first line, we have to match from the | |
374 * start of the line. | |
375 */ | |
376 if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text), | |
377 strlen (*lines))) | |
378 found = line_text; | |
379 else | |
380 found = NULL; | |
381 } | |
382 | |
383 if (found == NULL) | |
384 { | |
385 g_free (line_text); | |
386 return FALSE; | |
387 } | |
388 | |
389 /* Get offset to start of search string */ | |
390 offset = g_utf8_strlen (line_text, found - line_text); | |
391 | |
392 /* If match start needs to be returned, set it to the | |
393 * start of the search string. | |
394 */ | |
395 if (match_start) | |
396 { | |
397 *match_start = next; | |
398 gtk_text_iter_set_visible_line_offset (match_start, offset); | |
399 } | |
400 | |
401 /* Go to end of search string */ | |
402 offset += g_utf8_strlen (*lines, -1); | |
403 | |
404 forward_chars_with_skipping (&next, offset, visible_only, !slice); | |
405 | |
406 g_free (line_text); | |
407 | |
408 ++lines; | |
409 | |
410 if (match_end) | |
411 *match_end = next; | |
412 | |
413 /* try to match the rest of the lines forward, passing NULL | |
414 * for match_start so lines_match will try to match the entire | |
415 * line */ | |
416 return lines_match (&next, lines, visible_only, | |
417 slice, NULL, match_end); | |
418 } | |
419 | |
420 /* strsplit () that retains the delimiter as part of the string. */ | |
421 static gchar ** | |
422 strbreakup (const char *string, | |
423 const char *delimiter, | |
424 gint max_tokens) | |
425 { | |
426 GSList *string_list = NULL, *slist; | |
427 gchar **str_array, *s, *casefold, *new_string; | |
428 guint i, n = 1; | |
429 | |
430 g_return_val_if_fail (string != NULL, NULL); | |
431 g_return_val_if_fail (delimiter != NULL, NULL); | |
432 | |
433 if (max_tokens < 1) | |
434 max_tokens = G_MAXINT; | |
435 | |
436 s = strstr (string, delimiter); | |
437 if (s) | |
438 { | |
439 guint delimiter_len = strlen (delimiter); | |
440 | |
441 do | |
442 { | |
443 guint len; | |
444 | |
445 len = s - string + delimiter_len; | |
446 new_string = g_new (gchar, len + 1); | |
447 strncpy (new_string, string, len); | |
448 new_string[len] = 0; | |
449 casefold = g_utf8_casefold (new_string, -1); | |
450 g_free (new_string); | |
451 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | |
452 g_free (casefold); | |
453 string_list = g_slist_prepend (string_list, new_string); | |
454 n++; | |
455 string = s + delimiter_len; | |
456 s = strstr (string, delimiter); | |
457 } while (--max_tokens && s); | |
458 } | |
459 | |
460 if (*string) | |
461 { | |
462 n++; | |
463 casefold = g_utf8_casefold (string, -1); | |
464 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL); | |
465 g_free (casefold); | |
466 string_list = g_slist_prepend (string_list, new_string); | |
467 } | |
468 | |
469 str_array = g_new (gchar*, n); | |
470 | |
471 i = n - 1; | |
472 | |
473 str_array[i--] = NULL; | |
474 for (slist = string_list; slist; slist = slist->next) | |
475 str_array[i--] = slist->data; | |
476 | |
477 g_slist_free (string_list); | |
478 | |
479 return str_array; | |
480 } | |
481 | |
482 /** | |
483 * gtk_source_iter_forward_search: | |
484 * | |
485 * 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 | |
487 * 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 | |
489 * may wish to use @limit to avoid locking up your UI on large | |
490 * buffers. | |
491 * | |
492 * If the #GTK_SOURCE_SEARCH_VISIBLE_ONLY flag is present, the match may | |
493 * have invisible text interspersed in @str. i.e. @str will be a | |
494 * possibly-noncontiguous subsequence of the matched range. similarly, | |
495 * if you specify #GTK_SOURCE_SEARCH_TEXT_ONLY, the match may have | |
496 * pixbufs or child widgets mixed inside the matched range. If these | |
497 * flags are not given, the match must be exact; the special 0xFFFC | |
498 * character in @str will match embedded pixbufs or child widgets. | |
499 * If you specify the #GTK_SOURCE_SEARCH_CASE_INSENSITIVE flag, the text will | |
500 * be matched regardless of what case it is in. | |
501 * | |
502 * Same as gtk_text_iter_forward_search(), but supports case insensitive | |
503 * searching. | |
504 * | |
8866 | 505 * @param iter start of search |
506 * @param str a search string | |
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 */ | |
7358 | 513 gboolean |
514 gtk_source_iter_forward_search (const GtkTextIter *iter, | |
515 const gchar *str, | |
516 GtkSourceSearchFlags flags, | |
517 GtkTextIter *match_start, | |
518 GtkTextIter *match_end, | |
519 const GtkTextIter *limit) | |
520 { | |
521 gchar **lines = NULL; | |
522 GtkTextIter match; | |
523 gboolean retval = FALSE; | |
524 GtkTextIter search; | |
525 gboolean visible_only; | |
526 gboolean slice; | |
527 | |
528 g_return_val_if_fail (iter != NULL, FALSE); | |
529 g_return_val_if_fail (str != NULL, FALSE); | |
530 | |
531 if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0) | |
532 return gtk_text_iter_forward_search (iter, str, flags, | |
533 match_start, match_end, | |
534 limit); | |
535 | |
536 if (limit && gtk_text_iter_compare (iter, limit) >= 0) | |
537 return FALSE; | |
538 | |
539 if (*str == '\0') | |
540 { | |
541 /* If we can move one char, return the empty string there */ | |
542 match = *iter; | |
543 | |
544 if (gtk_text_iter_forward_char (&match)) | |
545 { | |
546 if (limit && gtk_text_iter_equal (&match, limit)) | |
547 return FALSE; | |
548 | |
549 if (match_start) | |
550 *match_start = match; | |
551 if (match_end) | |
552 *match_end = match; | |
553 return TRUE; | |
554 } | |
555 else | |
556 { | |
557 return FALSE; | |
558 } | |
559 } | |
560 | |
561 visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0; | |
562 slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0; | |
563 | |
564 /* locate all lines */ | |
565 lines = strbreakup (str, "\n", -1); | |
566 | |
567 search = *iter; | |
568 | |
569 do | |
570 { | |
571 /* This loop has an inefficient worst-case, where | |
572 * gtk_text_iter_get_text () is called repeatedly on | |
573 * a single line. | |
574 */ | |
575 GtkTextIter end; | |
576 | |
577 if (limit && gtk_text_iter_compare (&search, limit) >= 0) | |
578 break; | |
579 | |
580 if (lines_match (&search, (const gchar**)lines, | |
581 visible_only, slice, &match, &end)) | |
582 { | |
583 if (limit == NULL || (limit && | |
584 gtk_text_iter_compare (&end, limit) < 0)) | |
585 { | |
586 retval = TRUE; | |
587 | |
588 if (match_start) | |
589 *match_start = match; | |
590 if (match_end) | |
591 *match_end = end; | |
592 } | |
593 break; | |
594 } | |
595 } while (gtk_text_iter_forward_line (&search)); | |
596 | |
597 g_strfreev ((gchar**)lines); | |
598 | |
599 return retval; | |
600 } | |
601 | |
602 /** | |
603 * gtk_source_iter_backward_search: | |
604 * | |
605 * Same as gtk_text_iter_backward_search(), but supports case insensitive | |
606 * searching. | |
607 * | |
8866 | 608 * @param iter a #GtkTextIter where the search begins |
609 * @param str search string | |
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 */ | |
7358 | 616 gboolean |
617 gtk_source_iter_backward_search (const GtkTextIter *iter, | |
618 const gchar *str, | |
619 GtkSourceSearchFlags flags, | |
620 GtkTextIter *match_start, | |
621 GtkTextIter *match_end, | |
622 const GtkTextIter *limit) | |
623 { | |
624 gchar **lines = NULL; | |
625 GtkTextIter match; | |
626 gboolean retval = FALSE; | |
627 GtkTextIter search; | |
628 gboolean visible_only; | |
629 gboolean slice; | |
630 | |
631 g_return_val_if_fail (iter != NULL, FALSE); | |
632 g_return_val_if_fail (str != NULL, FALSE); | |
633 | |
634 if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0) | |
635 return gtk_text_iter_backward_search (iter, str, flags, | |
636 match_start, match_end, | |
637 limit); | |
638 | |
639 if (limit && gtk_text_iter_compare (iter, limit) <= 0) | |
640 return FALSE; | |
641 | |
642 if (*str == '\0') | |
643 { | |
644 /* If we can move one char, return the empty string there */ | |
645 match = *iter; | |
646 | |
647 if (gtk_text_iter_backward_char (&match)) | |
648 { | |
649 if (limit && gtk_text_iter_equal (&match, limit)) | |
650 return FALSE; | |
651 | |
652 if (match_start) | |
653 *match_start = match; | |
654 if (match_end) | |
655 *match_end = match; | |
656 return TRUE; | |
657 } | |
658 else | |
659 { | |
660 return FALSE; | |
661 } | |
662 } | |
663 | |
664 visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0; | |
665 slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0; | |
666 | |
667 /* locate all lines */ | |
668 lines = strbreakup (str, "\n", -1); | |
669 | |
670 search = *iter; | |
671 | |
672 while (TRUE) | |
673 { | |
674 /* This loop has an inefficient worst-case, where | |
675 * gtk_text_iter_get_text () is called repeatedly on | |
676 * a single line. | |
677 */ | |
678 GtkTextIter end; | |
679 | |
680 if (limit && gtk_text_iter_compare (&search, limit) <= 0) | |
681 break; | |
682 | |
683 if (backward_lines_match (&search, (const gchar**)lines, | |
684 visible_only, slice, &match, &end)) | |
685 { | |
686 if (limit == NULL || (limit && | |
687 gtk_text_iter_compare (&end, limit) > 0)) | |
688 { | |
689 retval = TRUE; | |
690 | |
691 if (match_start) | |
692 *match_start = match; | |
693 if (match_end) | |
694 *match_end = end; | |
695 } | |
696 break; | |
697 } | |
698 | |
699 if (gtk_text_iter_get_line_offset (&search) == 0) | |
700 { | |
701 if (!gtk_text_iter_backward_line (&search)) | |
702 break; | |
703 } | |
704 else | |
705 { | |
706 gtk_text_iter_set_line_offset (&search, 0); | |
707 } | |
708 } | |
709 | |
710 g_strfreev ((gchar**)lines); | |
711 | |
712 return retval; | |
713 } | |
714 | |
715 /* | |
716 * gtk_source_iter_find_matching_bracket is implemented in gtksourcebuffer.c | |
717 */ |