4 * Code for segments in general, and toggle/char segments in particular.
6 * Copyright (c) 1992-1994 The Regents of the University of California.
7 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
8 * Copyright (c) 2000 Red Hat, Inc.
9 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
11 * This software is copyrighted by the Regents of the University of
12 * California, Sun Microsystems, Inc., and other parties. The
13 * following terms apply to all files associated with the software
14 * unless explicitly disclaimed in individual files.
16 * The authors hereby grant permission to use, copy, modify,
17 * distribute, and license this software and its documentation for any
18 * purpose, provided that existing copyright notices are retained in
19 * all copies and that this notice is included verbatim in any
20 * distributions. No written agreement, license, or royalty fee is
21 * required for any of the authorized uses. Modifications to this
22 * software may be copyrighted by their authors and need not follow
23 * the licensing terms described here, provided that the new terms are
24 * clearly indicated on the first page of each file where they apply.
26 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
27 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
28 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
29 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
33 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
34 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
35 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
36 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
37 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
39 * GOVERNMENT USE: If you are acquiring this software on behalf of the
40 * U.S. government, the Government shall have only "Restricted Rights"
41 * in the software and related documentation as defined in the Federal
42 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
43 * are acquiring the software on behalf of the Department of Defense,
44 * the software shall be classified as "Commercial Computer Software"
45 * and the Government shall have only "Restricted Rights" as defined
46 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
47 * foregoing, the authors grant the U.S. Government and others acting
48 * in its behalf permission to use and distribute the software in
49 * accordance with the terms specified in this license.
53 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
54 #include "gtktextbtree.h"
58 #include "gtktexttag.h"
59 #include "gtktexttagtable.h"
60 #include "gtktextlayout.h"
61 #include "gtktextiterprivate.h"
65 *--------------------------------------------------------------
69 * This procedure is called before adding or deleting
70 * segments. It does three things: (a) it finds the segment
71 * containing iter; (b) if there are several such
72 * segments (because some segments have zero length) then
73 * it picks the first segment that does not have left
74 * gravity; (c) if the index refers to the middle of
75 * a segment then it splits the segment so that the
76 * index now refers to the beginning of a segment.
79 * The return value is a pointer to the segment just
80 * before the segment corresponding to iter (as
81 * described above). If the segment corresponding to
82 * iter is the first in its line then the return
86 * The segment referred to by iter is split unless
87 * iter refers to its first character.
89 *--------------------------------------------------------------
93 gtk_text_line_segment_split (const GtkTextIter *iter)
95 GtkTextLineSegment *prev, *seg;
100 line = _gtk_text_iter_get_text_line (iter);
101 tree = _gtk_text_iter_get_btree (iter);
103 count = gtk_text_iter_get_line_index (iter);
105 if (gtk_debug_flags & GTK_DEBUG_TEXT)
106 _gtk_text_iter_check (iter);
109 seg = line->segments;
113 if (seg->byte_count > count)
121 g_assert (count != seg->byte_count);
122 g_assert (seg->byte_count > 0);
124 _gtk_text_btree_segments_changed (tree);
126 seg = (*seg->type->splitFunc)(seg, count);
129 line->segments = seg;
136 else if ((seg->byte_count == 0) && (count == 0)
137 && !seg->type->leftGravity)
142 count -= seg->byte_count;
146 g_error ("split_segment reached end of line!");
152 * Macros that determine how much space to allocate for new segments:
155 #define CSEG_SIZE(chars) ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
157 #define TSEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
158 + sizeof (GtkTextToggleBody)))
165 char_segment_self_check (GtkTextLineSegment *seg)
167 /* This function checks the segment itself, but doesn't
168 assume the segment has been validly inserted into
171 g_assert (seg != NULL);
173 if (seg->byte_count <= 0)
175 g_error ("char_segment_check_func: segment has size <= 0");
178 if (strlen (seg->body.chars) != seg->byte_count)
180 g_error ("char_segment_check_func: segment has wrong size");
183 if (g_utf8_strlen (seg->body.chars, seg->byte_count) != seg->char_count)
185 g_error ("char segment has wrong character count");
190 _gtk_char_segment_new (const gchar *text, guint len)
192 GtkTextLineSegment *seg;
194 g_assert (gtk_text_byte_begins_utf8_char (text));
196 seg = g_malloc (CSEG_SIZE (len));
197 seg->type = >k_text_char_type;
199 seg->byte_count = len;
200 memcpy (seg->body.chars, text, len);
201 seg->body.chars[len] = '\0';
203 seg->char_count = g_utf8_strlen (seg->body.chars, seg->byte_count);
205 if (gtk_debug_flags & GTK_DEBUG_TEXT)
206 char_segment_self_check (seg);
212 _gtk_char_segment_new_from_two_strings (const gchar *text1, guint len1,
213 const gchar *text2, guint len2)
215 GtkTextLineSegment *seg;
217 g_assert (gtk_text_byte_begins_utf8_char (text1));
218 g_assert (gtk_text_byte_begins_utf8_char (text2));
220 seg = g_malloc (CSEG_SIZE (len1+len2));
221 seg->type = >k_text_char_type;
223 seg->byte_count = len1 + len2;
224 memcpy (seg->body.chars, text1, len1);
225 memcpy (seg->body.chars + len1, text2, len2);
226 seg->body.chars[len1+len2] = '\0';
228 /* In principle this function could probably take chars1 and chars2
229 as args, since it's typically used to merge two char segments */
230 seg->char_count = g_utf8_strlen (seg->body.chars, seg->byte_count);
232 if (gtk_debug_flags & GTK_DEBUG_TEXT)
233 char_segment_self_check (seg);
239 *--------------------------------------------------------------
241 * char_segment_split_func --
243 * This procedure implements splitting for character segments.
246 * The return value is a pointer to a chain of two segments
247 * that have the same characters as segPtr except split
248 * among the two segments.
251 * Storage for segPtr is freed.
253 *--------------------------------------------------------------
256 static GtkTextLineSegment *
257 char_segment_split_func (GtkTextLineSegment *seg, int index)
259 GtkTextLineSegment *new1, *new2;
261 g_assert (index < seg->byte_count);
263 if (gtk_debug_flags & GTK_DEBUG_TEXT)
265 char_segment_self_check (seg);
268 new1 = _gtk_char_segment_new (seg->body.chars, index);
269 new2 = _gtk_char_segment_new (seg->body.chars + index, seg->byte_count - index);
271 g_assert (gtk_text_byte_begins_utf8_char (new1->body.chars));
272 g_assert (gtk_text_byte_begins_utf8_char (new2->body.chars));
273 g_assert (new1->byte_count + new2->byte_count == seg->byte_count);
274 g_assert (new1->char_count + new2->char_count == seg->char_count);
277 new2->next = seg->next;
279 if (gtk_debug_flags & GTK_DEBUG_TEXT)
281 char_segment_self_check (new1);
282 char_segment_self_check (new2);
290 *--------------------------------------------------------------
292 * char_segment_cleanup_func --
294 * This procedure merges adjacent character segments into
295 * a single character segment, if possible.
298 * The return value is a pointer to the first segment in
299 * the (new) list of segments that used to start with segPtr.
302 * Storage for the segments may be allocated and freed.
304 *--------------------------------------------------------------
308 static GtkTextLineSegment *
309 char_segment_cleanup_func (segPtr, line)
310 GtkTextLineSegment *segPtr; /* Pointer to first of two adjacent
311 * segments to join. */
312 GtkTextLine *line; /* Line containing segments (not
315 GtkTextLineSegment *segPtr2, *newPtr;
317 if (gtk_debug_flags & GTK_DEBUG_TEXT)
318 char_segment_self_check (segPtr);
320 segPtr2 = segPtr->next;
321 if ((segPtr2 == NULL) || (segPtr2->type != >k_text_char_type))
327 _gtk_char_segment_new_from_two_strings (segPtr->body.chars, segPtr->byte_count,
328 segPtr2->body.chars, segPtr2->byte_count);
330 newPtr->next = segPtr2->next;
332 if (gtk_debug_flags & GTK_DEBUG_TEXT)
333 char_segment_self_check (newPtr);
341 *--------------------------------------------------------------
343 * char_segment_delete_func --
345 * This procedure is invoked to delete a character segment.
348 * Always returns 0 to indicate that the segment was deleted.
351 * Storage for the segment is freed.
353 *--------------------------------------------------------------
358 char_segment_delete_func (segPtr, line, treeGone)
359 GtkTextLineSegment *segPtr; /* Segment to delete. */
360 GtkTextLine *line; /* Line containing segment. */
361 int treeGone; /* Non-zero means the entire tree is
362 * being deleted, so everything must
365 g_free ((char*) segPtr);
370 *--------------------------------------------------------------
372 * char_segment_check_func --
374 * This procedure is invoked to perform consistency checks
375 * on character segments.
381 * If the segment isn't inconsistent then the procedure
384 *--------------------------------------------------------------
389 char_segment_check_func (segPtr, line)
390 GtkTextLineSegment *segPtr; /* Segment to check. */
391 GtkTextLine *line; /* Line containing segment. */
393 char_segment_self_check (segPtr);
395 if (segPtr->next != NULL)
397 if (segPtr->next->type == >k_text_char_type)
399 g_error ("char_segment_check_func: adjacent character segments weren't merged");
405 _gtk_toggle_segment_new (GtkTextTagInfo *info, gboolean on)
407 GtkTextLineSegment *seg;
409 seg = g_malloc (TSEG_SIZE);
411 seg->type = on ? >k_text_toggle_on_type : >k_text_toggle_off_type;
418 seg->body.toggle.info = info;
419 seg->body.toggle.inNodeCounts = 0;
425 *--------------------------------------------------------------
427 * toggle_segment_delete_func --
429 * This procedure is invoked to delete toggle segments.
432 * Returns 1 to indicate that the segment may not be deleted,
433 * unless the entire B-tree is going away.
436 * If the tree is going away then the toggle's memory is
437 * freed; otherwise the toggle counts in GtkTextBTreeNodes above the
438 * segment get updated.
440 *--------------------------------------------------------------
444 toggle_segment_delete_func (segPtr, line, treeGone)
445 GtkTextLineSegment *segPtr; /* Segment to check. */
446 GtkTextLine *line; /* Line containing segment. */
447 int treeGone; /* Non-zero means the entire tree is
448 * being deleted, so everything must
453 g_free ((char *) segPtr);
458 * This toggle is in the middle of a range of characters that's
459 * being deleted. Refuse to die. We'll be moved to the end of
460 * the deleted range and our cleanup procedure will be called
461 * later. Decrement GtkTextBTreeNode toggle counts here, and set a flag
462 * so we'll re-increment them in the cleanup procedure.
465 if (segPtr->body.toggle.inNodeCounts)
467 _gtk_change_node_toggle_count (line->parent,
468 segPtr->body.toggle.info, -1);
469 segPtr->body.toggle.inNodeCounts = 0;
475 *--------------------------------------------------------------
477 * toggle_segment_cleanup_func --
479 * This procedure is called when a toggle is part of a line that's
480 * been modified in some way. It's invoked after the
481 * modifications are complete.
484 * The return value is the head segment in a new list
485 * that is to replace the tail of the line that used to
486 * start at segPtr. This allows the procedure to delete
490 * Toggle counts in the GtkTextBTreeNodes above the new line will be
491 * updated if they're not already. Toggles may be collapsed
492 * if there are duplicate toggles at the same position.
494 *--------------------------------------------------------------
497 static GtkTextLineSegment *
498 toggle_segment_cleanup_func (segPtr, line)
499 GtkTextLineSegment *segPtr; /* Segment to check. */
500 GtkTextLine *line; /* Line that now contains segment. */
502 GtkTextLineSegment *segPtr2, *prevPtr;
506 * If this is a toggle-off segment, look ahead through the next
507 * segments to see if there's a toggle-on segment for the same tag
508 * before any segments with non-zero size. If so then the two
509 * toggles cancel each other; remove them both.
512 if (segPtr->type == >k_text_toggle_off_type)
514 for (prevPtr = segPtr, segPtr2 = prevPtr->next;
515 (segPtr2 != NULL) && (segPtr2->byte_count == 0);
516 prevPtr = segPtr2, segPtr2 = prevPtr->next)
518 if (segPtr2->type != >k_text_toggle_on_type)
522 if (segPtr2->body.toggle.info != segPtr->body.toggle.info)
526 counts = segPtr->body.toggle.inNodeCounts
527 + segPtr2->body.toggle.inNodeCounts;
530 _gtk_change_node_toggle_count (line->parent,
531 segPtr->body.toggle.info, -counts);
533 prevPtr->next = segPtr2->next;
534 g_free ((char *) segPtr2);
535 segPtr2 = segPtr->next;
536 g_free ((char *) segPtr);
541 if (!segPtr->body.toggle.inNodeCounts)
543 _gtk_change_node_toggle_count (line->parent,
544 segPtr->body.toggle.info, 1);
545 segPtr->body.toggle.inNodeCounts = 1;
551 *--------------------------------------------------------------
553 * toggle_segment_line_change_func --
555 * This procedure is invoked when a toggle segment is about
556 * to move from one line to another.
562 * Toggle counts are decremented in the GtkTextBTreeNodes above the line.
564 *--------------------------------------------------------------
568 toggle_segment_line_change_func (segPtr, line)
569 GtkTextLineSegment *segPtr; /* Segment to check. */
570 GtkTextLine *line; /* Line that used to contain segment. */
572 if (segPtr->body.toggle.inNodeCounts)
574 _gtk_change_node_toggle_count (line->parent,
575 segPtr->body.toggle.info, -1);
576 segPtr->body.toggle.inNodeCounts = 0;
585 GtkTextLineSegmentClass gtk_text_char_type = {
586 "character", /* name */
588 char_segment_split_func, /* splitFunc */
589 char_segment_delete_func, /* deleteFunc */
590 char_segment_cleanup_func, /* cleanupFunc */
591 NULL, /* lineChangeFunc */
592 char_segment_check_func /* checkFunc */
596 * Type record for segments marking the beginning of a tagged
600 GtkTextLineSegmentClass gtk_text_toggle_on_type = {
601 "toggleOn", /* name */
603 NULL, /* splitFunc */
604 toggle_segment_delete_func, /* deleteFunc */
605 toggle_segment_cleanup_func, /* cleanupFunc */
606 toggle_segment_line_change_func, /* lineChangeFunc */
607 _gtk_toggle_segment_check_func /* checkFunc */
611 * Type record for segments marking the end of a tagged
615 GtkTextLineSegmentClass gtk_text_toggle_off_type = {
616 "toggleOff", /* name */
618 NULL, /* splitFunc */
619 toggle_segment_delete_func, /* deleteFunc */
620 toggle_segment_cleanup_func, /* cleanupFunc */
621 toggle_segment_line_change_func, /* lineChangeFunc */
622 _gtk_toggle_segment_check_func /* checkFunc */