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