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.
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
27 #include "swfdec_text_buffer.h"
28 #include "swfdec_debug.h"
29 #include "swfdec_marshal.h"
31 typedef struct _SwfdecTextBufferFormat SwfdecTextBufferFormat
;
33 struct _SwfdecTextBufferFormat
{
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
);
49 swfdec_text_buffer_format_free (SwfdecTextBufferFormat
*format
)
51 swfdec_text_attributes_reset (&format
->attr
);
52 g_slice_free (SwfdecTextBufferFormat
, format
);
63 static guint signals
[LAST_SIGNAL
] = { 0, };
65 G_DEFINE_TYPE (SwfdecTextBuffer
, swfdec_text_buffer
, G_TYPE_OBJECT
)
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
);
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
);
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
,
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
);
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
);
106 swfdec_text_buffer_new (void)
108 return g_object_new (SWFDEC_TYPE_TEXT_BUFFER
, NULL
);
112 swfdec_text_buffer_mark_one (gpointer entry
, gpointer unused
)
114 SwfdecTextBufferFormat
*format
= entry
;
116 swfdec_text_attributes_mark (&format
->attr
);
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
);
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
, };
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
);
151 #ifdef G_DISABLE_ASSERT
152 #define CHECK_ATTRIBUTES(x)
155 CHECK_ATTRIBUTES (SwfdecTextBuffer
*buffer
)
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);
166 format
= g_sequence_get (iter
);
167 g_assert (format
->start
== 0);
168 g_assert (buffer
->text
->len
> 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
;
179 /* removes duplicates in the range [iter, end) */
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
);
201 swfdec_text_buffer_insert_text (SwfdecTextBuffer
*buffer
,
202 gsize pos
, const char *text
)
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
);
215 if (pos
== buffer
->text
->len
) {
216 g_string_insert_len (buffer
->text
, pos
, text
, len
);
217 format
= swfdec_text_buffer_format_new ();
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
));
225 g_string_insert_len (buffer
->text
, pos
, text
, len
);
226 iter
= g_sequence_get_end_iter (buffer
->attributes
);
228 iter
= g_sequence_iter_prev (iter
);
229 format
= g_sequence_get (iter
);
230 if (format
->start
<= pos
)
232 format
->start
+= len
;
235 CHECK_ATTRIBUTES (buffer
);
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
));
250 swfdec_text_buffer_delete_text (SwfdecTextBuffer
*buffer
,
251 gsize pos
, gsize length
)
253 GSequenceIter
*iter
, *prev
;
254 SwfdecTextBufferFormat
*format
;
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
);
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
)
270 if (format
->start
> end
) {
271 format
->start
-= length
;
275 g_sequence_remove (prev
);
279 if (prev
&& pos
== buffer
->text
->len
)
280 g_sequence_remove (prev
);
281 CHECK_ATTRIBUTES (buffer
);
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
));
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
;
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
)
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
;
333 swfdec_text_buffer_get_unique (SwfdecTextBuffer
*buffer
,
334 gsize start
, gsize length
)
336 guint result
= SWFDEC_TEXT_ATTRIBUTES_MASK
;
337 SwfdecTextBufferFormat
*format
, *cur
;
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
)
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
)
354 result
&= ~swfdec_text_attributes_diff (&format
->attr
, &cur
->attr
);
360 static GSequenceIter
*
361 swfdec_text_buffer_copy_format (SwfdecTextBuffer
*buffer
, GSequenceIter
*iter
,
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
);
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
;
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
);
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
;
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
);
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
);
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
)
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
);
478 return &buffer
->default_attributes
;
480 format
= g_sequence_get (iter
);
481 return &format
->attr
;
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
;
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
;
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
;
515 swfdec_text_buffer_get_selection (SwfdecTextBuffer
*buffer
, gsize
*start
, gsize
*end
)
517 g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer
));
520 *start
= MIN (buffer
->cursor_start
, buffer
->cursor_end
);
522 *end
= MAX (buffer
->cursor_start
, buffer
->cursor_end
);
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
)
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
));