Mercurial > pidgin
annotate src/gtksourceiter.c @ 13623:51d436a267ac
[gaim-migrate @ 16009]
Use the GSlice allocator (for logs and log sets) when compiling with glib 2.10 or greater and eliminate some unnecessary memory zeroing. This cuts the log loading time in half for one of Luke's contacts.
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Tue, 11 Apr 2006 16:06:24 +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 */ |