actually set the variable here
[swfdec.git] / swfdec / swfdec_text_buffer.c
blob867725e2be2cfc33077b904cec5df97c9aea0472
1 /* Swfdec
2 * Copyright (C) 2006-2008 Benjamin Otte <otte@gnome.org>
3 * 2007 Pekka Lampila <pekka.lampila@iki.fi>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <string.h>
27 #include "swfdec_text_buffer.h"
28 #include "swfdec_debug.h"
29 #include "swfdec_marshal.h"
31 typedef struct _SwfdecTextBufferFormat SwfdecTextBufferFormat;
33 struct _SwfdecTextBufferFormat {
34 guint start;
35 SwfdecTextAttributes attr;
38 static SwfdecTextBufferFormat *
39 swfdec_text_buffer_format_new (void)
41 SwfdecTextBufferFormat *format;
43 format = g_slice_new0 (SwfdecTextBufferFormat);
44 swfdec_text_attributes_reset (&format->attr);
45 return format;
48 static void
49 swfdec_text_buffer_format_free (SwfdecTextBufferFormat *format)
51 swfdec_text_attributes_reset (&format->attr);
52 g_slice_free (SwfdecTextBufferFormat, format);
55 /*** OBJECT ***/
57 enum {
58 TEXT_CHANGED,
59 CURSOR_CHANGED,
60 LAST_SIGNAL
63 static guint signals[LAST_SIGNAL] = { 0, };
65 G_DEFINE_TYPE (SwfdecTextBuffer, swfdec_text_buffer, G_TYPE_OBJECT)
67 static void
68 swfdec_text_buffer_dispose (GObject *object)
70 SwfdecTextBuffer *buffer = SWFDEC_TEXT_BUFFER (object);
72 if (buffer->text != NULL) {
73 g_string_free (buffer->text, TRUE);
74 buffer->text = NULL;
76 g_sequence_free (buffer->attributes);
77 swfdec_text_attributes_reset (&buffer->default_attributes);
79 G_OBJECT_CLASS (swfdec_text_buffer_parent_class)->dispose (object);
82 static void
83 swfdec_text_buffer_class_init (SwfdecTextBufferClass *klass)
85 GObjectClass *object_class = G_OBJECT_CLASS (klass);
87 object_class->dispose = swfdec_text_buffer_dispose;
89 signals[TEXT_CHANGED] = g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass),
90 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
91 G_TYPE_NONE, 0);
92 signals[CURSOR_CHANGED] = g_signal_new ("cursor-changed", G_TYPE_FROM_CLASS (klass),
93 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__ULONG_ULONG,
94 G_TYPE_NONE, 2, G_TYPE_ULONG, G_TYPE_ULONG);
97 static void
98 swfdec_text_buffer_init (SwfdecTextBuffer *buffer)
100 buffer->text = g_string_new ("");
101 buffer->attributes = g_sequence_new ((GDestroyNotify) swfdec_text_buffer_format_free);
102 swfdec_text_attributes_reset (&buffer->default_attributes);
105 SwfdecTextBuffer *
106 swfdec_text_buffer_new (void)
108 return g_object_new (SWFDEC_TYPE_TEXT_BUFFER, NULL);
111 static void
112 swfdec_text_buffer_mark_one (gpointer entry, gpointer unused)
114 SwfdecTextBufferFormat *format = entry;
116 swfdec_text_attributes_mark (&format->attr);
119 void
120 swfdec_text_buffer_mark (SwfdecTextBuffer *buffer)
122 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
124 g_sequence_foreach (buffer->attributes, swfdec_text_buffer_mark_one, NULL);
127 static int
128 swfdec_text_buffer_format_compare (gconstpointer a, gconstpointer b, gpointer unused)
130 return ((const SwfdecTextBufferFormat *) a)->start -
131 ((const SwfdecTextBufferFormat *) b)->start;
134 /* return iter to format relevant for position, that is the format pointed to
135 * by iter will have a lower or equal start index than pos */
136 static GSequenceIter *
137 swfdec_text_buffer_get_iter_for_pos (SwfdecTextBuffer *buffer, guint pos)
139 SwfdecTextBufferFormat format = { pos, };
140 GSequenceIter *iter;
142 iter = g_sequence_search (buffer->attributes, &format,
143 swfdec_text_buffer_format_compare, NULL);
144 if (g_sequence_iter_is_end (iter) ||
145 ((SwfdecTextBufferFormat *) g_sequence_get (iter))->start > pos)
146 iter = g_sequence_iter_prev (iter);
148 return iter;
151 #ifdef G_DISABLE_ASSERT
152 #define CHECK_ATTRIBUTES(x)
153 #else
154 static void
155 CHECK_ATTRIBUTES (SwfdecTextBuffer *buffer)
157 guint last;
158 GSequenceIter *iter;
159 SwfdecTextBufferFormat *format;
161 iter = g_sequence_get_begin_iter (buffer->attributes);
162 if (g_sequence_iter_is_end (iter)) {
163 g_assert (buffer->text->len == 0);
164 return;
166 format = g_sequence_get (iter);
167 g_assert (format->start == 0);
168 g_assert (buffer->text->len > 0);
169 last = 0;
170 for (iter = g_sequence_iter_next (iter); !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
171 format = g_sequence_get (iter);
172 g_assert (format->start > last);
173 g_assert (format->start < buffer->text->len);
174 last = format->start;
177 #endif
179 /* removes duplicates in the range [iter, end) */
180 static void
181 swfdec_text_buffer_remove_duplicates (GSequenceIter *iter, GSequenceIter *end)
183 SwfdecTextBufferFormat *format, *next;
185 g_assert (iter != end);
187 format = g_sequence_get (iter);
188 for (iter = g_sequence_iter_next (iter); iter != end; iter = g_sequence_iter_next (iter)) {
189 next = g_sequence_get (iter);
190 if (swfdec_text_attributes_diff (&format->attr, &next->attr) == 0) {
191 GSequenceIter *prev = g_sequence_iter_prev (iter);
192 g_sequence_remove (iter);
193 iter = prev;
194 } else {
195 format = next;
200 void
201 swfdec_text_buffer_insert_text (SwfdecTextBuffer *buffer,
202 gsize pos, const char *text)
204 gsize len;
205 GSequenceIter *iter;
206 SwfdecTextBufferFormat *format;
208 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
209 g_return_if_fail (pos <= buffer->text->len);
210 g_return_if_fail (text != NULL);
212 len = strlen (text);
213 if (len == 0)
214 return;
215 if (pos == buffer->text->len) {
216 g_string_insert_len (buffer->text, pos, text, len);
217 format = swfdec_text_buffer_format_new ();
218 format->start = pos;
219 swfdec_text_attributes_copy (&format->attr,
220 &buffer->default_attributes, SWFDEC_TEXT_ATTRIBUTES_MASK);
221 iter = g_sequence_append (buffer->attributes, format);
222 swfdec_text_buffer_remove_duplicates (g_sequence_iter_prev (iter),
223 g_sequence_iter_next (iter));
224 } else {
225 g_string_insert_len (buffer->text, pos, text, len);
226 iter = g_sequence_get_end_iter (buffer->attributes);
227 for (;;) {
228 iter = g_sequence_iter_prev (iter);
229 format = g_sequence_get (iter);
230 if (format->start <= pos)
231 break;
232 format->start += len;
235 CHECK_ATTRIBUTES (buffer);
237 /* adapt cursor */
238 if (buffer->cursor_start >= pos)
239 buffer->cursor_start += len;
240 if (buffer->cursor_end >= pos)
241 buffer->cursor_end += len;
243 g_signal_emit (buffer, signals[TEXT_CHANGED], 0);
244 g_signal_emit (buffer, signals[CURSOR_CHANGED], 0,
245 (gulong) MIN (buffer->cursor_start, buffer->cursor_end),
246 (gulong) MAX (buffer->cursor_start, buffer->cursor_end));
249 void
250 swfdec_text_buffer_delete_text (SwfdecTextBuffer *buffer,
251 gsize pos, gsize length)
253 GSequenceIter *iter, *prev;
254 SwfdecTextBufferFormat *format;
255 gsize end;
257 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
258 g_return_if_fail (pos + length <= buffer->text->len);
259 g_return_if_fail (length > 0);
261 g_string_erase (buffer->text, pos, length);
262 end = pos + length;
263 prev = NULL;
264 for (iter = swfdec_text_buffer_get_iter_for_pos (buffer, pos);
265 !g_sequence_iter_is_end (iter);
266 iter = g_sequence_iter_next (iter)) {
267 format = g_sequence_get (iter);
268 if (format->start < pos)
269 continue;
270 if (format->start > end) {
271 format->start -= length;
272 continue;
274 if (prev)
275 g_sequence_remove (prev);
276 format->start = pos;
277 prev = iter;
279 if (prev && pos == buffer->text->len)
280 g_sequence_remove (prev);
281 CHECK_ATTRIBUTES (buffer);
283 /* adapt cursor */
284 if (buffer->cursor_start > end)
285 buffer->cursor_start -= length;
286 else if (buffer->cursor_start > pos)
287 buffer->cursor_start = pos;
288 if (buffer->cursor_end > end)
289 buffer->cursor_end -= length;
290 else if (buffer->cursor_end > pos)
291 buffer->cursor_end = pos;
293 g_signal_emit (buffer, signals[TEXT_CHANGED], 0);
294 g_signal_emit (buffer, signals[CURSOR_CHANGED], 0,
295 (gulong) MIN (buffer->cursor_start, buffer->cursor_end),
296 (gulong) MAX (buffer->cursor_start, buffer->cursor_end));
299 const char *
300 swfdec_text_buffer_get_text (SwfdecTextBuffer *buffer)
302 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
304 return buffer->text->str;
307 gsize
308 swfdec_text_buffer_get_length (SwfdecTextBuffer *buffer)
310 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
312 return buffer->text->len;
315 const SwfdecTextAttributes *
316 swfdec_text_buffer_get_attributes (SwfdecTextBuffer *buffer, gsize pos)
318 GSequenceIter *iter;
319 SwfdecTextBufferFormat *format;
321 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
322 g_return_val_if_fail (pos <= buffer->text->len, NULL);
324 if (pos == buffer->text->len)
325 return &buffer->default_attributes;
327 iter = swfdec_text_buffer_get_iter_for_pos (buffer, pos);
328 format = g_sequence_get (iter);
329 return &format->attr;
332 guint
333 swfdec_text_buffer_get_unique (SwfdecTextBuffer *buffer,
334 gsize start, gsize length)
336 guint result = SWFDEC_TEXT_ATTRIBUTES_MASK;
337 SwfdecTextBufferFormat *format, *cur;
338 GSequenceIter *iter;
339 gsize end;
341 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
342 g_return_val_if_fail (start + length <= buffer->text->len, 0);
344 if (start == buffer->text->len)
345 return result;
347 end = start + length;
348 iter = swfdec_text_buffer_get_iter_for_pos (buffer, start);
349 format = g_sequence_get (iter);
350 for (iter = g_sequence_iter_next (iter); !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
351 cur = g_sequence_get (iter);
352 if (cur->start >= end)
353 break;
354 result &= ~swfdec_text_attributes_diff (&format->attr, &cur->attr);
357 return result;
360 static GSequenceIter *
361 swfdec_text_buffer_copy_format (SwfdecTextBuffer *buffer, GSequenceIter *iter,
362 gsize pos)
364 SwfdecTextBufferFormat *format, *new_format;
366 format = g_sequence_get (iter);
367 new_format = swfdec_text_buffer_format_new ();
368 new_format->start = pos;
369 swfdec_text_attributes_copy (&new_format->attr, &format->attr,
370 SWFDEC_TEXT_ATTRIBUTES_MASK);
371 return g_sequence_insert_before (g_sequence_iter_next (iter), new_format);
374 void
375 swfdec_text_buffer_set_attributes (SwfdecTextBuffer *buffer, gsize start,
376 gsize length, const SwfdecTextAttributes *attr, guint mask)
378 SwfdecTextBufferFormat *format;
379 GSequenceIter *start_iter, *iter, *end_iter;
380 gsize end;
382 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
383 g_return_if_fail (start + length <= buffer->text->len);
384 g_return_if_fail (length > 0);
385 g_return_if_fail (attr != NULL);
386 g_return_if_fail (mask != 0);
388 /* ensure start and end position have an attribtues entry. If not, create one */
389 end = start + length;
390 start_iter = swfdec_text_buffer_get_iter_for_pos (buffer, start);
391 format = g_sequence_get (start_iter);
392 if (format->start < start) {
393 start_iter = swfdec_text_buffer_copy_format (buffer, start_iter, start);
395 if (end == buffer->text->len) {
396 end_iter = g_sequence_get_end_iter (buffer->attributes);
397 } else {
398 end_iter = swfdec_text_buffer_get_iter_for_pos (buffer, end);
399 format = g_sequence_get (end_iter);
400 if (format->start < end) {
401 end_iter = swfdec_text_buffer_copy_format (buffer, end_iter, end);
404 /* start_iter points to first item to modify, end_iter points after last item to modify */
406 /* modify all formats in range [start_iter, end_iter) */
407 for (iter = start_iter; iter != end_iter; iter = g_sequence_iter_next (iter)) {
408 format = g_sequence_get (iter);
409 swfdec_text_attributes_copy (&format->attr, attr, mask);
412 /* remove entries that are identical now */
413 swfdec_text_buffer_remove_duplicates (g_sequence_iter_prev (start_iter),
414 g_sequence_iter_next (end_iter));
415 CHECK_ATTRIBUTES (buffer);
417 g_signal_emit (buffer, signals[TEXT_CHANGED], 0);
420 const SwfdecTextAttributes *
421 swfdec_text_buffer_get_default_attributes (SwfdecTextBuffer *buffer)
423 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
425 return &buffer->default_attributes;
428 void
429 swfdec_text_buffer_set_default_attributes (SwfdecTextBuffer *buffer,
430 const SwfdecTextAttributes *attr, guint mask)
432 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
433 g_return_if_fail (attr != NULL);
435 swfdec_text_attributes_copy (&buffer->default_attributes, attr, mask);
438 void
439 swfdec_text_buffer_reset_default_attributes (SwfdecTextBuffer *buffer)
441 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
443 swfdec_text_attributes_reset (&buffer->default_attributes);
446 /*** ITERATOR ***/
448 SwfdecTextBufferIter *
449 swfdec_text_buffer_get_iter (SwfdecTextBuffer *buffer, gsize pos)
451 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
452 g_return_val_if_fail (pos <= buffer->text->len, NULL);
454 if (pos == buffer->text->len)
455 return NULL;
457 return swfdec_text_buffer_get_iter_for_pos (buffer, pos);
460 SwfdecTextBufferIter *
461 swfdec_text_buffer_iter_next (SwfdecTextBuffer *buffer, SwfdecTextBufferIter *iter)
463 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
464 g_return_val_if_fail (iter != NULL, NULL);
466 iter = g_sequence_iter_next (iter);
467 return g_sequence_iter_is_end (iter) ? NULL : iter;
470 const SwfdecTextAttributes *
471 swfdec_text_buffer_iter_get_attributes (SwfdecTextBuffer *buffer, SwfdecTextBufferIter *iter)
473 SwfdecTextBufferFormat *format;
475 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
477 if (iter == NULL)
478 return &buffer->default_attributes;
480 format = g_sequence_get (iter);
481 return &format->attr;
484 gsize
485 swfdec_text_buffer_iter_get_start (SwfdecTextBuffer *buffer, SwfdecTextBufferIter *iter)
487 SwfdecTextBufferFormat *format;
489 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
490 g_return_val_if_fail (iter != NULL, 0);
492 format = g_sequence_get (iter);
493 return format->start;
496 /*** CURSOR API ***/
498 gsize
499 swfdec_text_buffer_get_cursor (SwfdecTextBuffer *buffer)
501 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
503 return buffer->cursor_end;
506 gboolean
507 swfdec_text_buffer_has_selection (SwfdecTextBuffer *buffer)
509 g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), FALSE);
511 return buffer->cursor_start != buffer->cursor_end;
514 void
515 swfdec_text_buffer_get_selection (SwfdecTextBuffer *buffer, gsize *start, gsize *end)
517 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
519 if (start)
520 *start = MIN (buffer->cursor_start, buffer->cursor_end);
521 if (end)
522 *end = MAX (buffer->cursor_start, buffer->cursor_end);
525 void
526 swfdec_text_buffer_set_cursor (SwfdecTextBuffer *buffer, gsize start, gsize end)
528 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
529 g_return_if_fail (start <= swfdec_text_buffer_get_length (buffer));
530 g_return_if_fail (end <= swfdec_text_buffer_get_length (buffer));
532 if (buffer->cursor_start == start &&
533 buffer->cursor_end == end)
534 return;
536 buffer->cursor_start = start;
537 buffer->cursor_end = end;
538 g_signal_emit (buffer, signals[CURSOR_CHANGED], 0,
539 (gulong) MIN (buffer->cursor_start, buffer->cursor_end),
540 (gulong) MAX (buffer->cursor_start, buffer->cursor_end));