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 #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_line(iter);
101 tree = gtk_text_iter_get_btree(iter);
103 count = gtk_text_iter_get_line_byte(iter);
106 seg = line->segments;
110 if (seg->byte_count > count)
118 g_assert(count != seg->byte_count);
119 g_assert(seg->byte_count > 0);
121 gtk_text_btree_segments_changed(tree);
123 seg = (*seg->type->splitFunc)(seg, count);
126 line->segments = seg;
133 else if ((seg->byte_count == 0) && (count == 0)
134 && !seg->type->leftGravity)
139 count -= seg->byte_count;
143 g_error("split_segment reached end of line!");
149 * Macros that determine how much space to allocate for new segments:
152 #define CSEG_SIZE(chars) ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \
154 #define TSEG_SIZE ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \
155 + sizeof(GtkTextToggleBody)))
162 char_segment_self_check(GtkTextLineSegment *seg)
164 /* This function checks the segment itself, but doesn't
165 assume the segment has been validly inserted into
168 g_assert(seg != NULL);
170 if (seg->byte_count <= 0)
172 g_error("char_segment_check_func: segment has size <= 0");
175 if (strlen(seg->body.chars) != seg->byte_count)
177 g_error("char_segment_check_func: segment has wrong size");
180 if (gtk_text_view_num_utf_chars(seg->body.chars, seg->byte_count) != seg->char_count)
182 g_error("char segment has wrong character count");
187 char_segment_new(const gchar *text, guint len)
189 GtkTextLineSegment *seg;
191 g_assert(gtk_text_byte_begins_utf8_char(text));
193 seg = g_malloc(CSEG_SIZE(len));
194 seg->type = >k_text_char_type;
196 seg->byte_count = len;
197 memcpy(seg->body.chars, text, len);
198 seg->body.chars[len] = '\0';
200 seg->char_count = gtk_text_view_num_utf_chars(seg->body.chars, seg->byte_count);
202 if (gtk_debug_flags & GTK_DEBUG_TEXT)
203 char_segment_self_check(seg);
209 char_segment_new_from_two_strings(const gchar *text1, guint len1,
210 const gchar *text2, guint len2)
212 GtkTextLineSegment *seg;
214 g_assert(gtk_text_byte_begins_utf8_char(text1));
215 g_assert(gtk_text_byte_begins_utf8_char(text2));
217 seg = g_malloc(CSEG_SIZE(len1+len2));
218 seg->type = >k_text_char_type;
220 seg->byte_count = len1 + len2;
221 memcpy(seg->body.chars, text1, len1);
222 memcpy(seg->body.chars + len1, text2, len2);
223 seg->body.chars[len1+len2] = '\0';
225 /* In principle this function could probably take chars1 and chars2
226 as args, since it's typically used to merge two char segments */
227 seg->char_count = gtk_text_view_num_utf_chars(seg->body.chars, seg->byte_count);
229 if (gtk_debug_flags & GTK_DEBUG_TEXT)
230 char_segment_self_check(seg);
236 *--------------------------------------------------------------
238 * char_segment_split_func --
240 * This procedure implements splitting for character segments.
243 * The return value is a pointer to a chain of two segments
244 * that have the same characters as segPtr except split
245 * among the two segments.
248 * Storage for segPtr is freed.
250 *--------------------------------------------------------------
253 static GtkTextLineSegment *
254 char_segment_split_func(GtkTextLineSegment *seg, int index)
256 GtkTextLineSegment *new1, *new2;
258 g_assert(index < seg->byte_count);
260 if (gtk_debug_flags & GTK_DEBUG_TEXT)
262 char_segment_self_check(seg);
265 new1 = char_segment_new(seg->body.chars, index);
266 new2 = char_segment_new(seg->body.chars + index, seg->byte_count - index);
268 g_assert(gtk_text_byte_begins_utf8_char(new1->body.chars));
269 g_assert(gtk_text_byte_begins_utf8_char(new2->body.chars));
270 g_assert(new1->byte_count + new2->byte_count == seg->byte_count);
271 g_assert(new1->char_count + new2->char_count == seg->char_count);
274 new2->next = seg->next;
276 if (gtk_debug_flags & GTK_DEBUG_TEXT)
278 char_segment_self_check(new1);
279 char_segment_self_check(new2);
287 *--------------------------------------------------------------
289 * char_segment_cleanup_func --
291 * This procedure merges adjacent character segments into
292 * a single character segment, if possible.
295 * The return value is a pointer to the first segment in
296 * the (new) list of segments that used to start with segPtr.
299 * Storage for the segments may be allocated and freed.
301 *--------------------------------------------------------------
305 static GtkTextLineSegment *
306 char_segment_cleanup_func(segPtr, line)
307 GtkTextLineSegment *segPtr; /* Pointer to first of two adjacent
308 * segments to join. */
309 GtkTextLine *line; /* Line containing segments (not
312 GtkTextLineSegment *segPtr2, *newPtr;
314 if (gtk_debug_flags & GTK_DEBUG_TEXT)
315 char_segment_self_check(segPtr);
317 segPtr2 = segPtr->next;
318 if ((segPtr2 == NULL) || (segPtr2->type != >k_text_char_type))
323 newPtr = char_segment_new_from_two_strings(segPtr->body.chars, segPtr->byte_count,
324 segPtr2->body.chars, segPtr2->byte_count);
326 newPtr->next = segPtr2->next;
328 if (gtk_debug_flags & GTK_DEBUG_TEXT)
329 char_segment_self_check(newPtr);
337 *--------------------------------------------------------------
339 * char_segment_delete_func --
341 * This procedure is invoked to delete a character segment.
344 * Always returns 0 to indicate that the segment was deleted.
347 * Storage for the segment is freed.
349 *--------------------------------------------------------------
354 char_segment_delete_func(segPtr, line, treeGone)
355 GtkTextLineSegment *segPtr; /* Segment to delete. */
356 GtkTextLine *line; /* Line containing segment. */
357 int treeGone; /* Non-zero means the entire tree is
358 * being deleted, so everything must
361 g_free((char*) segPtr);
366 *--------------------------------------------------------------
368 * char_segment_check_func --
370 * This procedure is invoked to perform consistency checks
371 * on character segments.
377 * If the segment isn't inconsistent then the procedure
380 *--------------------------------------------------------------
385 char_segment_check_func(segPtr, line)
386 GtkTextLineSegment *segPtr; /* Segment to check. */
387 GtkTextLine *line; /* Line containing segment. */
389 char_segment_self_check(segPtr);
391 if (segPtr->next == NULL)
393 if (segPtr->body.chars[segPtr->byte_count-1] != '\n')
395 g_error("char_segment_check_func: line doesn't end with newline");
400 if (segPtr->next->type == >k_text_char_type)
402 g_error("char_segment_check_func: adjacent character segments weren't merged");
408 toggle_segment_new(GtkTextTagInfo *info, gboolean on)
410 GtkTextLineSegment *seg;
412 seg = g_malloc(TSEG_SIZE);
414 seg->type = on ? >k_text_toggle_on_type : >k_text_toggle_off_type;
421 seg->body.toggle.info = info;
422 seg->body.toggle.inNodeCounts = 0;
428 *--------------------------------------------------------------
430 * toggle_segment_delete_func --
432 * This procedure is invoked to delete toggle segments.
435 * Returns 1 to indicate that the segment may not be deleted,
436 * unless the entire B-tree is going away.
439 * If the tree is going away then the toggle's memory is
440 * freed; otherwise the toggle counts in GtkTextBTreeNodes above the
441 * segment get updated.
443 *--------------------------------------------------------------
447 toggle_segment_delete_func(segPtr, line, treeGone)
448 GtkTextLineSegment *segPtr; /* Segment to check. */
449 GtkTextLine *line; /* Line containing segment. */
450 int treeGone; /* Non-zero means the entire tree is
451 * being deleted, so everything must
456 g_free((char *) segPtr);
461 * This toggle is in the middle of a range of characters that's
462 * being deleted. Refuse to die. We'll be moved to the end of
463 * the deleted range and our cleanup procedure will be called
464 * later. Decrement GtkTextBTreeNode toggle counts here, and set a flag
465 * so we'll re-increment them in the cleanup procedure.
468 if (segPtr->body.toggle.inNodeCounts)
470 change_node_toggle_count(line->parent,
471 segPtr->body.toggle.info, -1);
472 segPtr->body.toggle.inNodeCounts = 0;
478 *--------------------------------------------------------------
480 * toggle_segment_cleanup_func --
482 * This procedure is called when a toggle is part of a line that's
483 * been modified in some way. It's invoked after the
484 * modifications are complete.
487 * The return value is the head segment in a new list
488 * that is to replace the tail of the line that used to
489 * start at segPtr. This allows the procedure to delete
493 * Toggle counts in the GtkTextBTreeNodes above the new line will be
494 * updated if they're not already. Toggles may be collapsed
495 * if there are duplicate toggles at the same position.
497 *--------------------------------------------------------------
500 static GtkTextLineSegment *
501 toggle_segment_cleanup_func(segPtr, line)
502 GtkTextLineSegment *segPtr; /* Segment to check. */
503 GtkTextLine *line; /* Line that now contains segment. */
505 GtkTextLineSegment *segPtr2, *prevPtr;
509 * If this is a toggle-off segment, look ahead through the next
510 * segments to see if there's a toggle-on segment for the same tag
511 * before any segments with non-zero size. If so then the two
512 * toggles cancel each other; remove them both.
515 if (segPtr->type == >k_text_toggle_off_type)
517 for (prevPtr = segPtr, segPtr2 = prevPtr->next;
518 (segPtr2 != NULL) && (segPtr2->byte_count == 0);
519 prevPtr = segPtr2, segPtr2 = prevPtr->next)
521 if (segPtr2->type != >k_text_toggle_on_type)
525 if (segPtr2->body.toggle.info != segPtr->body.toggle.info)
529 counts = segPtr->body.toggle.inNodeCounts
530 + segPtr2->body.toggle.inNodeCounts;
533 change_node_toggle_count(line->parent,
534 segPtr->body.toggle.info, -counts);
536 prevPtr->next = segPtr2->next;
537 g_free((char *) segPtr2);
538 segPtr2 = segPtr->next;
539 g_free((char *) segPtr);
544 if (!segPtr->body.toggle.inNodeCounts)
546 change_node_toggle_count(line->parent,
547 segPtr->body.toggle.info, 1);
548 segPtr->body.toggle.inNodeCounts = 1;
554 *--------------------------------------------------------------
556 * toggle_segment_line_change_func --
558 * This procedure is invoked when a toggle segment is about
559 * to move from one line to another.
565 * Toggle counts are decremented in the GtkTextBTreeNodes above the line.
567 *--------------------------------------------------------------
571 toggle_segment_line_change_func(segPtr, line)
572 GtkTextLineSegment *segPtr; /* Segment to check. */
573 GtkTextLine *line; /* Line that used to contain segment. */
575 if (segPtr->body.toggle.inNodeCounts)
577 change_node_toggle_count(line->parent,
578 segPtr->body.toggle.info, -1);
579 segPtr->body.toggle.inNodeCounts = 0;
588 GtkTextLineSegmentClass gtk_text_char_type = {
589 "character", /* name */
591 char_segment_split_func, /* splitFunc */
592 char_segment_delete_func, /* deleteFunc */
593 char_segment_cleanup_func, /* cleanupFunc */
594 NULL, /* lineChangeFunc */
595 char_segment_check_func /* checkFunc */
599 * Type record for segments marking the beginning of a tagged
603 GtkTextLineSegmentClass gtk_text_toggle_on_type = {
604 "toggleOn", /* name */
606 NULL, /* splitFunc */
607 toggle_segment_delete_func, /* deleteFunc */
608 toggle_segment_cleanup_func, /* cleanupFunc */
609 toggle_segment_line_change_func, /* lineChangeFunc */
610 toggle_segment_check_func /* checkFunc */
614 * Type record for segments marking the end of a tagged
618 GtkTextLineSegmentClass gtk_text_toggle_off_type = {
619 "toggleOff", /* name */
621 NULL, /* splitFunc */
622 toggle_segment_delete_func, /* deleteFunc */
623 toggle_segment_cleanup_func, /* cleanupFunc */
624 toggle_segment_line_change_func, /* lineChangeFunc */
625 toggle_segment_check_func /* checkFunc */