1 | /* |
2 | * gtktextsegment.c -- |
3 | * |
4 | * Code for segments in general, and toggle/char segments in particular. |
5 | * |
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> |
10 | * |
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. |
15 | * |
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. |
25 | * |
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. |
31 | * |
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. |
38 | * |
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. |
50 | * |
51 | */ |
52 | |
53 | #include "config.h" |
54 | #include "gtktextbtree.h" |
55 | #include <string.h> |
56 | #include <stdlib.h> |
57 | #include <stdio.h> |
58 | #include "gtktexttag.h" |
59 | #include "gtktexttagtable.h" |
60 | #include "gtktextlayoutprivate.h" |
61 | #include "gtktextiterprivate.h" |
62 | #include "gtkdebug.h" |
63 | |
64 | /* |
65 | *-------------------------------------------------------------- |
66 | * |
67 | * split_segment -- |
68 | * |
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. |
77 | * |
78 | * Results: |
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 |
83 | * value is NULL. |
84 | * |
85 | * Side effects: |
86 | * The segment referred to by iter is split unless |
87 | * iter refers to its first character. |
88 | * |
89 | *-------------------------------------------------------------- |
90 | */ |
91 | |
92 | GtkTextLineSegment* |
93 | gtk_text_line_segment_split (const GtkTextIter *iter) |
94 | { |
95 | GtkTextLineSegment *prev, *seg; |
96 | GtkTextBTree *tree; |
97 | GtkTextLine *line; |
98 | int count; |
99 | |
100 | line = _gtk_text_iter_get_text_line (iter); |
101 | tree = _gtk_text_iter_get_btree (iter); |
102 | |
103 | count = gtk_text_iter_get_line_index (iter); |
104 | |
105 | if (GTK_DEBUG_CHECK (TEXT)) |
106 | _gtk_text_iter_check (iter); |
107 | |
108 | prev = NULL; |
109 | seg = line->segments; |
110 | |
111 | while (seg != NULL) |
112 | { |
113 | if (seg->byte_count > count) |
114 | { |
115 | if (count == 0) |
116 | { |
117 | return prev; |
118 | } |
119 | else |
120 | { |
121 | g_assert (count != seg->byte_count); |
122 | g_assert (seg->byte_count > 0); |
123 | |
124 | _gtk_text_btree_segments_changed (tree); |
125 | |
126 | seg = (*seg->type->splitFunc)(seg, count); |
127 | |
128 | if (prev == NULL) |
129 | line->segments = seg; |
130 | else |
131 | prev->next = seg; |
132 | |
133 | return seg; |
134 | } |
135 | } |
136 | else if ((seg->byte_count == 0) && (count == 0) |
137 | && !seg->type->leftGravity) |
138 | { |
139 | return prev; |
140 | } |
141 | |
142 | count -= seg->byte_count; |
143 | prev = seg; |
144 | seg = seg->next; |
145 | } |
146 | g_error ("split_segment reached end of line!" ); |
147 | return NULL; |
148 | } |
149 | |
150 | |
151 | /* |
152 | * Macros that determine how much space to allocate for new segments: |
153 | */ |
154 | |
155 | #define CSEG_SIZE(chars) ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \ |
156 | + 1 + (chars))) |
157 | #define TSEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \ |
158 | + sizeof (GtkTextToggleBody))) |
159 | |
160 | /* |
161 | * Type functions |
162 | */ |
163 | |
164 | static void |
165 | char_segment_self_check (GtkTextLineSegment *seg) |
166 | { |
167 | /* This function checks the segment itself, but doesn't |
168 | assume the segment has been validly inserted into |
169 | the btree. */ |
170 | |
171 | g_assert (seg != NULL); |
172 | |
173 | if (seg->byte_count <= 0) |
174 | { |
175 | g_error ("segment has size <= 0" ); |
176 | } |
177 | |
178 | if (strlen (s: seg->body.chars) != seg->byte_count) |
179 | { |
180 | g_error ("segment has wrong size" ); |
181 | } |
182 | |
183 | if (g_utf8_strlen (p: seg->body.chars, max: seg->byte_count) != seg->char_count) |
184 | { |
185 | g_error ("char segment has wrong character count" ); |
186 | } |
187 | } |
188 | |
189 | GtkTextLineSegment* |
190 | _gtk_char_segment_new (const char *text, guint len) |
191 | { |
192 | GtkTextLineSegment *seg; |
193 | |
194 | g_assert (gtk_text_byte_begins_utf8_char (text)); |
195 | |
196 | seg = g_slice_alloc (CSEG_SIZE (len)); |
197 | seg->type = (GtkTextLineSegmentClass *)>k_text_char_type; |
198 | seg->next = NULL; |
199 | seg->byte_count = len; |
200 | memcpy (dest: seg->body.chars, src: text, n: len); |
201 | seg->body.chars[len] = '\0'; |
202 | |
203 | seg->char_count = g_utf8_strlen (p: seg->body.chars, max: seg->byte_count); |
204 | |
205 | if (GTK_DEBUG_CHECK (TEXT)) |
206 | char_segment_self_check (seg); |
207 | |
208 | return seg; |
209 | } |
210 | |
211 | GtkTextLineSegment* |
212 | _gtk_char_segment_new_from_two_strings (const char *text1, |
213 | guint len1, |
214 | guint chars1, |
215 | const char *text2, |
216 | guint len2, |
217 | guint chars2) |
218 | { |
219 | GtkTextLineSegment *seg; |
220 | |
221 | g_assert (gtk_text_byte_begins_utf8_char (text1)); |
222 | g_assert (gtk_text_byte_begins_utf8_char (text2)); |
223 | |
224 | seg = g_slice_alloc (CSEG_SIZE (len1+len2)); |
225 | seg->type = >k_text_char_type; |
226 | seg->next = NULL; |
227 | seg->byte_count = len1 + len2; |
228 | memcpy (dest: seg->body.chars, src: text1, n: len1); |
229 | memcpy (dest: seg->body.chars + len1, src: text2, n: len2); |
230 | seg->body.chars[len1+len2] = '\0'; |
231 | |
232 | seg->char_count = chars1 + chars2; |
233 | |
234 | if (GTK_DEBUG_CHECK (TEXT)) |
235 | char_segment_self_check (seg); |
236 | |
237 | return seg; |
238 | } |
239 | |
240 | static void |
241 | _gtk_char_segment_free (GtkTextLineSegment *seg) |
242 | { |
243 | if (seg == NULL) |
244 | return; |
245 | |
246 | g_assert (seg->type == >k_text_char_type); |
247 | |
248 | g_slice_free1 (CSEG_SIZE (seg->byte_count), mem_block: seg); |
249 | } |
250 | |
251 | /* |
252 | *-------------------------------------------------------------- |
253 | * |
254 | * char_segment_split_func -- |
255 | * |
256 | * This procedure implements splitting for character segments. |
257 | * |
258 | * Results: |
259 | * The return value is a pointer to a chain of two segments |
260 | * that have the same characters as segPtr except split |
261 | * among the two segments. |
262 | * |
263 | * Side effects: |
264 | * Storage for segPtr is freed. |
265 | * |
266 | *-------------------------------------------------------------- |
267 | */ |
268 | |
269 | static GtkTextLineSegment * |
270 | char_segment_split_func (GtkTextLineSegment *seg, int index) |
271 | { |
272 | GtkTextLineSegment *new1, *new2; |
273 | |
274 | g_assert (index < seg->byte_count); |
275 | |
276 | if (GTK_DEBUG_CHECK (TEXT)) |
277 | { |
278 | char_segment_self_check (seg); |
279 | } |
280 | |
281 | new1 = _gtk_char_segment_new (text: seg->body.chars, len: index); |
282 | new2 = _gtk_char_segment_new (text: seg->body.chars + index, len: seg->byte_count - index); |
283 | |
284 | g_assert (gtk_text_byte_begins_utf8_char (new1->body.chars)); |
285 | g_assert (gtk_text_byte_begins_utf8_char (new2->body.chars)); |
286 | g_assert (new1->byte_count + new2->byte_count == seg->byte_count); |
287 | g_assert (new1->char_count + new2->char_count == seg->char_count); |
288 | |
289 | new1->next = new2; |
290 | new2->next = seg->next; |
291 | |
292 | if (GTK_DEBUG_CHECK (TEXT)) |
293 | { |
294 | char_segment_self_check (seg: new1); |
295 | char_segment_self_check (seg: new2); |
296 | } |
297 | |
298 | _gtk_char_segment_free (seg); |
299 | return new1; |
300 | } |
301 | |
302 | /* |
303 | *-------------------------------------------------------------- |
304 | * |
305 | * char_segment_cleanup_func -- |
306 | * |
307 | * This procedure merges adjacent character segments into |
308 | * a single character segment, if possible. |
309 | * |
310 | * Arguments: |
311 | * segPtr: Pointer to the first of two adjacent segments to |
312 | * join. |
313 | * line: Line containing segments (not used). |
314 | * |
315 | * Results: |
316 | * The return value is a pointer to the first segment in |
317 | * the (new) list of segments that used to start with segPtr. |
318 | * |
319 | * Side effects: |
320 | * Storage for the segments may be allocated and freed. |
321 | * |
322 | *-------------------------------------------------------------- |
323 | */ |
324 | |
325 | /* ARGSUSED */ |
326 | static GtkTextLineSegment * |
327 | char_segment_cleanup_func (GtkTextLineSegment *segPtr, GtkTextLine *line) |
328 | { |
329 | GtkTextLineSegment *segPtr2, *newPtr; |
330 | |
331 | if (GTK_DEBUG_CHECK (TEXT)) |
332 | char_segment_self_check (seg: segPtr); |
333 | |
334 | segPtr2 = segPtr->next; |
335 | if ((segPtr2 == NULL) || (segPtr2->type != >k_text_char_type)) |
336 | { |
337 | return segPtr; |
338 | } |
339 | |
340 | newPtr = |
341 | _gtk_char_segment_new_from_two_strings (text1: segPtr->body.chars, |
342 | len1: segPtr->byte_count, |
343 | chars1: segPtr->char_count, |
344 | text2: segPtr2->body.chars, |
345 | len2: segPtr2->byte_count, |
346 | chars2: segPtr2->char_count); |
347 | |
348 | newPtr->next = segPtr2->next; |
349 | |
350 | if (GTK_DEBUG_CHECK (TEXT)) |
351 | char_segment_self_check (seg: newPtr); |
352 | |
353 | _gtk_char_segment_free (seg: segPtr); |
354 | _gtk_char_segment_free (seg: segPtr2); |
355 | return newPtr; |
356 | } |
357 | |
358 | /* |
359 | *-------------------------------------------------------------- |
360 | * |
361 | * char_segment_delete_func -- |
362 | * |
363 | * This procedure is invoked to delete a character segment. |
364 | * |
365 | * Arguments: |
366 | * segPtr : Segment to delete |
367 | * line : Line containing segment |
368 | * treeGone : Non-zero means the entire tree is being |
369 | * deleted, so everything must get cleaned up. |
370 | * |
371 | * Results: |
372 | * Always returns 0 to indicate that the segment was deleted. |
373 | * |
374 | * Side effects: |
375 | * Storage for the segment is freed. |
376 | * |
377 | *-------------------------------------------------------------- |
378 | */ |
379 | |
380 | /* ARGSUSED */ |
381 | static int |
382 | char_segment_delete_func (GtkTextLineSegment *segPtr, GtkTextLine *line, int treeGone) |
383 | { |
384 | _gtk_char_segment_free (seg: segPtr); |
385 | return 0; |
386 | } |
387 | |
388 | /* |
389 | *-------------------------------------------------------------- |
390 | * |
391 | * char_segment_check_func -- |
392 | * |
393 | * This procedure is invoked to perform consistency checks |
394 | * on character segments. |
395 | * |
396 | * Arguments: |
397 | * segPtr : Segment to check |
398 | * line : Line containing segment |
399 | * |
400 | * Results: |
401 | * None. |
402 | * |
403 | * Side effects: |
404 | * If the segment isn’t inconsistent then the procedure |
405 | * g_errors. |
406 | * |
407 | *-------------------------------------------------------------- |
408 | */ |
409 | |
410 | /* ARGSUSED */ |
411 | static void |
412 | char_segment_check_func (GtkTextLineSegment *segPtr, GtkTextLine *line) |
413 | { |
414 | char_segment_self_check (seg: segPtr); |
415 | |
416 | if (segPtr->next != NULL) |
417 | { |
418 | if (segPtr->next->type == >k_text_char_type) |
419 | { |
420 | g_error ("adjacent character segments weren't merged" ); |
421 | } |
422 | } |
423 | } |
424 | |
425 | GtkTextLineSegment* |
426 | _gtk_toggle_segment_new (GtkTextTagInfo *info, gboolean on) |
427 | { |
428 | /* gcc-11 issues a diagnostic here because the size allocated |
429 | for SEG does not cover the entire size of a GtkTextLineSegment |
430 | and gcc has no way to know that the union will only be used |
431 | for limited types and the additional space is not needed. */ |
432 | #pragma GCC diagnostic push |
433 | #pragma GCC diagnostic ignored "-Warray-bounds" |
434 | GtkTextLineSegment *seg; |
435 | |
436 | seg = g_slice_alloc (TSEG_SIZE); |
437 | |
438 | seg->type = on ? >k_text_toggle_on_type : >k_text_toggle_off_type; |
439 | |
440 | seg->next = NULL; |
441 | |
442 | seg->byte_count = 0; |
443 | seg->char_count = 0; |
444 | |
445 | seg->body.toggle.info = info; |
446 | seg->body.toggle.inNodeCounts = 0; |
447 | |
448 | return seg; |
449 | #pragma GCC diagnostic pop |
450 | } |
451 | |
452 | void |
453 | _gtk_toggle_segment_free (GtkTextLineSegment *seg) |
454 | { |
455 | if (seg == NULL) |
456 | return; |
457 | |
458 | g_assert (seg->type == >k_text_toggle_on_type || |
459 | seg->type == >k_text_toggle_off_type); |
460 | |
461 | g_slice_free1 (TSEG_SIZE, mem_block: seg); |
462 | } |
463 | |
464 | /* |
465 | *-------------------------------------------------------------- |
466 | * |
467 | * toggle_segment_delete_func -- |
468 | * |
469 | * This procedure is invoked to delete toggle segments. |
470 | * |
471 | * Arguments: |
472 | * segPtr : Segment to check |
473 | * line : Line containing segment |
474 | * treeGone : Non-zero means the entire tree is being |
475 | * deleted so everything must get cleaned up |
476 | * |
477 | * Results: |
478 | * Returns 1 to indicate that the segment may not be deleted, |
479 | * unless the entire B-tree is going away. |
480 | * |
481 | * Side effects: |
482 | * If the tree is going away then the toggle’s memory is |
483 | * freed; otherwise the toggle counts in GtkTextBTreeNodes above the |
484 | * segment get updated. |
485 | * |
486 | *-------------------------------------------------------------- |
487 | */ |
488 | |
489 | static int |
490 | toggle_segment_delete_func (GtkTextLineSegment *segPtr, GtkTextLine *line, int treeGone) |
491 | { |
492 | if (treeGone) |
493 | { |
494 | _gtk_toggle_segment_free (seg: segPtr); |
495 | return 0; |
496 | } |
497 | |
498 | /* |
499 | * This toggle is in the middle of a range of characters that's |
500 | * being deleted. Refuse to die. We'll be moved to the end of |
501 | * the deleted range and our cleanup procedure will be called |
502 | * later. Decrement GtkTextBTreeNode toggle counts here, and set a flag |
503 | * so we'll re-increment them in the cleanup procedure. |
504 | */ |
505 | |
506 | if (segPtr->body.toggle.inNodeCounts) |
507 | { |
508 | _gtk_change_node_toggle_count (node: line->parent, |
509 | info: segPtr->body.toggle.info, delta: -1); |
510 | segPtr->body.toggle.inNodeCounts = 0; |
511 | } |
512 | return 1; |
513 | } |
514 | |
515 | /* |
516 | *-------------------------------------------------------------- |
517 | * |
518 | * toggle_segment_cleanup_func -- |
519 | * |
520 | * This procedure is called when a toggle is part of a line that's |
521 | * been modified in some way. It’s invoked after the |
522 | * modifications are complete. |
523 | * |
524 | * Arguments: |
525 | * segPtr : Segment to check |
526 | * line : Line that now contains segment |
527 | * |
528 | * Results: |
529 | * The return value is the head segment in a new list |
530 | * that is to replace the tail of the line that used to |
531 | * start at segPtr. This allows the procedure to delete |
532 | * or modify segPtr. |
533 | * |
534 | * Side effects: |
535 | * Toggle counts in the GtkTextBTreeNodes above the new line will be |
536 | * updated if they’re not already. Toggles may be collapsed |
537 | * if there are duplicate toggles at the same position. |
538 | * |
539 | *-------------------------------------------------------------- |
540 | */ |
541 | |
542 | static GtkTextLineSegment * |
543 | toggle_segment_cleanup_func (GtkTextLineSegment *segPtr, GtkTextLine *line) |
544 | { |
545 | GtkTextLineSegment *segPtr2, *prevPtr; |
546 | int counts; |
547 | |
548 | /* |
549 | * If this is a toggle-off segment, look ahead through the next |
550 | * segments to see if there's a toggle-on segment for the same tag |
551 | * before any segments with non-zero size. If so then the two |
552 | * toggles cancel each other; remove them both. |
553 | */ |
554 | |
555 | if (segPtr->type == >k_text_toggle_off_type) |
556 | { |
557 | for (prevPtr = segPtr, segPtr2 = prevPtr->next; |
558 | (segPtr2 != NULL) && (segPtr2->byte_count == 0); |
559 | prevPtr = segPtr2, segPtr2 = prevPtr->next) |
560 | { |
561 | if (segPtr2->type != >k_text_toggle_on_type) |
562 | { |
563 | continue; |
564 | } |
565 | if (segPtr2->body.toggle.info != segPtr->body.toggle.info) |
566 | { |
567 | continue; |
568 | } |
569 | counts = segPtr->body.toggle.inNodeCounts |
570 | + segPtr2->body.toggle.inNodeCounts; |
571 | if (counts != 0) |
572 | { |
573 | _gtk_change_node_toggle_count (node: line->parent, |
574 | info: segPtr->body.toggle.info, delta: -counts); |
575 | } |
576 | prevPtr->next = segPtr2->next; |
577 | _gtk_toggle_segment_free (seg: segPtr2); |
578 | segPtr2 = segPtr->next; |
579 | _gtk_toggle_segment_free (seg: segPtr); |
580 | return segPtr2; |
581 | } |
582 | } |
583 | |
584 | if (!segPtr->body.toggle.inNodeCounts) |
585 | { |
586 | _gtk_change_node_toggle_count (node: line->parent, |
587 | info: segPtr->body.toggle.info, delta: 1); |
588 | segPtr->body.toggle.inNodeCounts = 1; |
589 | } |
590 | return segPtr; |
591 | } |
592 | |
593 | /* |
594 | *-------------------------------------------------------------- |
595 | * |
596 | * toggle_segment_line_change_func -- |
597 | * |
598 | * This procedure is invoked when a toggle segment is about |
599 | * to move from one line to another. |
600 | * |
601 | * Arguments: |
602 | * segPtr : Segment to check |
603 | * line : Line that used to contain segment |
604 | * |
605 | * Results: |
606 | * None. |
607 | * |
608 | * Side effects: |
609 | * Toggle counts are decremented in the GtkTextBTreeNodes above the line. |
610 | * |
611 | *-------------------------------------------------------------- |
612 | */ |
613 | |
614 | static void |
615 | toggle_segment_line_change_func (GtkTextLineSegment *segPtr, GtkTextLine *line) |
616 | { |
617 | if (segPtr->body.toggle.inNodeCounts) |
618 | { |
619 | _gtk_change_node_toggle_count (node: line->parent, |
620 | info: segPtr->body.toggle.info, delta: -1); |
621 | segPtr->body.toggle.inNodeCounts = 0; |
622 | } |
623 | } |
624 | |
625 | /* |
626 | * Virtual tables |
627 | */ |
628 | |
629 | |
630 | const GtkTextLineSegmentClass gtk_text_char_type = { |
631 | "character" , /* name */ |
632 | 0, /* leftGravity */ |
633 | char_segment_split_func, /* splitFunc */ |
634 | char_segment_delete_func, /* deleteFunc */ |
635 | char_segment_cleanup_func, /* cleanupFunc */ |
636 | NULL, /* lineChangeFunc */ |
637 | char_segment_check_func /* checkFunc */ |
638 | }; |
639 | |
640 | /* |
641 | * Type record for segments marking the beginning of a tagged |
642 | * range: |
643 | */ |
644 | |
645 | const GtkTextLineSegmentClass gtk_text_toggle_on_type = { |
646 | "toggleOn" , /* name */ |
647 | 0, /* leftGravity */ |
648 | NULL, /* splitFunc */ |
649 | toggle_segment_delete_func, /* deleteFunc */ |
650 | toggle_segment_cleanup_func, /* cleanupFunc */ |
651 | toggle_segment_line_change_func, /* lineChangeFunc */ |
652 | _gtk_toggle_segment_check_func /* checkFunc */ |
653 | }; |
654 | |
655 | /* |
656 | * Type record for segments marking the end of a tagged |
657 | * range: |
658 | */ |
659 | |
660 | const GtkTextLineSegmentClass gtk_text_toggle_off_type = { |
661 | "toggleOff" , /* name */ |
662 | 1, /* leftGravity */ |
663 | NULL, /* splitFunc */ |
664 | toggle_segment_delete_func, /* deleteFunc */ |
665 | toggle_segment_cleanup_func, /* cleanupFunc */ |
666 | toggle_segment_line_change_func, /* lineChangeFunc */ |
667 | _gtk_toggle_segment_check_func /* checkFunc */ |
668 | }; |
669 | |