 b4e110920a
			
		
	
	b4e110920a
	
	
	
		
			
			Avoid diagnostics for gcc-11 false positive out of bounds accesses See merge request GNOME/gtk!3064 (cherry picked from commit 5044031b53988627c2937c6b5f8919a37e18f21f) c514c41d Avoid diagnostics for gcc-11 false positive out of bounds accesses
		
			
				
	
	
		
			670 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			670 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | ||
|  * gtktextsegment.c --
 | ||
|  *
 | ||
|  * Code for segments in general, and toggle/char segments in particular.
 | ||
|  *
 | ||
|  * Copyright (c) 1992-1994 The Regents of the University of California.
 | ||
|  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
 | ||
|  * Copyright (c) 2000      Red Hat, Inc.
 | ||
|  * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
 | ||
|  *
 | ||
|  * This software is copyrighted by the Regents of the University of
 | ||
|  * California, Sun Microsystems, Inc., and other parties.  The
 | ||
|  * following terms apply to all files associated with the software
 | ||
|  * unless explicitly disclaimed in individual files.
 | ||
|  *
 | ||
|  * The authors hereby grant permission to use, copy, modify,
 | ||
|  * distribute, and license this software and its documentation for any
 | ||
|  * purpose, provided that existing copyright notices are retained in
 | ||
|  * all copies and that this notice is included verbatim in any
 | ||
|  * distributions. No written agreement, license, or royalty fee is
 | ||
|  * required for any of the authorized uses.  Modifications to this
 | ||
|  * software may be copyrighted by their authors and need not follow
 | ||
|  * the licensing terms described here, provided that the new terms are
 | ||
|  * clearly indicated on the first page of each file where they apply.
 | ||
|  *
 | ||
|  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
 | ||
|  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
 | ||
|  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
 | ||
|  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
 | ||
|  * OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||
|  *
 | ||
|  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
 | ||
|  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | ||
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
 | ||
|  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
 | ||
|  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
 | ||
|  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 | ||
|  *
 | ||
|  * GOVERNMENT USE: If you are acquiring this software on behalf of the
 | ||
|  * U.S. government, the Government shall have only "Restricted Rights"
 | ||
|  * in the software and related documentation as defined in the Federal
 | ||
|  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
 | ||
|  * are acquiring the software on behalf of the Department of Defense,
 | ||
|  * the software shall be classified as "Commercial Computer Software"
 | ||
|  * and the Government shall have only "Restricted Rights" as defined
 | ||
|  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
 | ||
|  * foregoing, the authors grant the U.S. Government and others acting
 | ||
|  * in its behalf permission to use and distribute the software in
 | ||
|  * accordance with the terms specified in this license.
 | ||
|  *
 | ||
|  */
 | ||
| 
 | ||
| #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
 | ||
| #include "config.h"
 | ||
| #include "gtktextbtree.h"
 | ||
| #include <string.h>
 | ||
| #include <stdlib.h>
 | ||
| #include <stdio.h>
 | ||
| #include "gtktexttag.h"
 | ||
| #include "gtktexttagtable.h"
 | ||
| #include "gtktextlayout.h"
 | ||
| #include "gtktextiterprivate.h"
 | ||
| #include "gtkdebug.h"
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * split_segment --
 | ||
|  *
 | ||
|  *      This procedure is called before adding or deleting
 | ||
|  *      segments.  It does three things: (a) it finds the segment
 | ||
|  *      containing iter;  (b) if there are several such
 | ||
|  *      segments (because some segments have zero length) then
 | ||
|  *      it picks the first segment that does not have left
 | ||
|  *      gravity;  (c) if the index refers to the middle of
 | ||
|  *      a segment then it splits the segment so that the
 | ||
|  *      index now refers to the beginning of a segment.
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      The return value is a pointer to the segment just
 | ||
|  *      before the segment corresponding to iter (as
 | ||
|  *      described above).  If the segment corresponding to
 | ||
|  *      iter is the first in its line then the return
 | ||
|  *      value is NULL.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      The segment referred to by iter is split unless
 | ||
|  *      iter refers to its first character.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
| GtkTextLineSegment*
 | ||
| gtk_text_line_segment_split (const GtkTextIter *iter)
 | ||
| {
 | ||
|   GtkTextLineSegment *prev, *seg;
 | ||
|   GtkTextBTree *tree;
 | ||
|   GtkTextLine *line;
 | ||
|   int count;
 | ||
| 
 | ||
|   line = _gtk_text_iter_get_text_line (iter);
 | ||
|   tree = _gtk_text_iter_get_btree (iter);
 | ||
| 
 | ||
|   count = gtk_text_iter_get_line_index (iter);
 | ||
| 
 | ||
|   if (GTK_DEBUG_CHECK (TEXT))
 | ||
|     _gtk_text_iter_check (iter);
 | ||
|   
 | ||
|   prev = NULL;
 | ||
|   seg = line->segments;
 | ||
| 
 | ||
|   while (seg != NULL)
 | ||
|     {
 | ||
|       if (seg->byte_count > count)
 | ||
|         {
 | ||
|           if (count == 0)
 | ||
|             {
 | ||
|               return prev;
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               g_assert (count != seg->byte_count);
 | ||
|               g_assert (seg->byte_count > 0);
 | ||
| 
 | ||
|               _gtk_text_btree_segments_changed (tree);
 | ||
| 
 | ||
|               seg = (*seg->type->splitFunc)(seg, count);
 | ||
| 
 | ||
|               if (prev == NULL)
 | ||
|                 line->segments = seg;
 | ||
|               else
 | ||
|                 prev->next = seg;
 | ||
| 
 | ||
|               return seg;
 | ||
|             }
 | ||
|         }
 | ||
|       else if ((seg->byte_count == 0) && (count == 0)
 | ||
|                && !seg->type->leftGravity)
 | ||
|         {
 | ||
|           return prev;
 | ||
|         }
 | ||
| 
 | ||
|       count -= seg->byte_count;
 | ||
|       prev = seg;
 | ||
|       seg = seg->next;
 | ||
|     }
 | ||
|   g_error ("split_segment reached end of line!");
 | ||
|   return NULL;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
|  * Macros that determine how much space to allocate for new segments:
 | ||
|  */
 | ||
| 
 | ||
| #define CSEG_SIZE(chars) ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
 | ||
|         + 1 + (chars)))
 | ||
| #define TSEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
 | ||
|         + sizeof (GtkTextToggleBody)))
 | ||
| 
 | ||
| /*
 | ||
|  * Type functions
 | ||
|  */
 | ||
| 
 | ||
| static void
 | ||
| char_segment_self_check (GtkTextLineSegment *seg)
 | ||
| {
 | ||
|   /* This function checks the segment itself, but doesn't
 | ||
|      assume the segment has been validly inserted into
 | ||
|      the btree. */
 | ||
| 
 | ||
|   g_assert (seg != NULL);
 | ||
| 
 | ||
|   if (seg->byte_count <= 0)
 | ||
|     {
 | ||
|       g_error ("segment has size <= 0");
 | ||
|     }
 | ||
| 
 | ||
|   if (strlen (seg->body.chars) != seg->byte_count)
 | ||
|     {
 | ||
|       g_error ("segment has wrong size");
 | ||
|     }
 | ||
| 
 | ||
|   if (g_utf8_strlen (seg->body.chars, seg->byte_count) != seg->char_count)
 | ||
|     {
 | ||
|       g_error ("char segment has wrong character count");
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| GtkTextLineSegment*
 | ||
| _gtk_char_segment_new (const gchar *text, guint len)
 | ||
| {
 | ||
|   GtkTextLineSegment *seg;
 | ||
| 
 | ||
|   g_assert (gtk_text_byte_begins_utf8_char (text));
 | ||
| 
 | ||
|   seg = g_slice_alloc (CSEG_SIZE (len));
 | ||
|   seg->type = (GtkTextLineSegmentClass *)>k_text_char_type;
 | ||
|   seg->next = NULL;
 | ||
|   seg->byte_count = len;
 | ||
|   memcpy (seg->body.chars, text, len);
 | ||
|   seg->body.chars[len] = '\0';
 | ||
| 
 | ||
|   seg->char_count = g_utf8_strlen (seg->body.chars, seg->byte_count);
 | ||
| 
 | ||
|   if (GTK_DEBUG_CHECK (TEXT))
 | ||
|     char_segment_self_check (seg);
 | ||
| 
 | ||
|   return seg;
 | ||
| }
 | ||
| 
 | ||
| GtkTextLineSegment*
 | ||
| _gtk_char_segment_new_from_two_strings (const gchar *text1, 
 | ||
| 					guint        len1, 
 | ||
| 					guint        chars1,
 | ||
|                                         const gchar *text2, 
 | ||
| 					guint        len2, 
 | ||
| 					guint        chars2)
 | ||
| {
 | ||
|   GtkTextLineSegment *seg;
 | ||
| 
 | ||
|   g_assert (gtk_text_byte_begins_utf8_char (text1));
 | ||
|   g_assert (gtk_text_byte_begins_utf8_char (text2));
 | ||
| 
 | ||
|   seg = g_slice_alloc (CSEG_SIZE (len1+len2));
 | ||
|   seg->type = >k_text_char_type;
 | ||
|   seg->next = NULL;
 | ||
|   seg->byte_count = len1 + len2;
 | ||
|   memcpy (seg->body.chars, text1, len1);
 | ||
|   memcpy (seg->body.chars + len1, text2, len2);
 | ||
|   seg->body.chars[len1+len2] = '\0';
 | ||
| 
 | ||
|   seg->char_count = chars1 + chars2;
 | ||
| 
 | ||
|   if (GTK_DEBUG_CHECK (TEXT))
 | ||
|     char_segment_self_check (seg);
 | ||
| 
 | ||
|   return seg;
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| _gtk_char_segment_free (GtkTextLineSegment *seg)
 | ||
| {
 | ||
|   if (seg == NULL)
 | ||
|     return;
 | ||
| 
 | ||
|   g_assert (seg->type == >k_text_char_type);
 | ||
| 
 | ||
|   g_slice_free1 (CSEG_SIZE (seg->byte_count), seg);
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * char_segment_split_func --
 | ||
|  *
 | ||
|  *      This procedure implements splitting for character segments.
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      The return value is a pointer to a chain of two segments
 | ||
|  *      that have the same characters as segPtr except split
 | ||
|  *      among the two segments.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      Storage for segPtr is freed.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
| static GtkTextLineSegment *
 | ||
| char_segment_split_func (GtkTextLineSegment *seg, int index)
 | ||
| {
 | ||
|   GtkTextLineSegment *new1, *new2;
 | ||
| 
 | ||
|   g_assert (index < seg->byte_count);
 | ||
| 
 | ||
|   if (GTK_DEBUG_CHECK (TEXT))
 | ||
|     {
 | ||
|       char_segment_self_check (seg);
 | ||
|     }
 | ||
| 
 | ||
|   new1 = _gtk_char_segment_new (seg->body.chars, index);
 | ||
|   new2 = _gtk_char_segment_new (seg->body.chars + index, seg->byte_count - index);
 | ||
| 
 | ||
|   g_assert (gtk_text_byte_begins_utf8_char (new1->body.chars));
 | ||
|   g_assert (gtk_text_byte_begins_utf8_char (new2->body.chars));
 | ||
|   g_assert (new1->byte_count + new2->byte_count == seg->byte_count);
 | ||
|   g_assert (new1->char_count + new2->char_count == seg->char_count);
 | ||
| 
 | ||
|   new1->next = new2;
 | ||
|   new2->next = seg->next;
 | ||
| 
 | ||
|   if (GTK_DEBUG_CHECK (TEXT))
 | ||
|     {
 | ||
|       char_segment_self_check (new1);
 | ||
|       char_segment_self_check (new2);
 | ||
|     }
 | ||
| 
 | ||
|   _gtk_char_segment_free (seg);
 | ||
|   return new1;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * char_segment_cleanup_func --
 | ||
|  *
 | ||
|  *      This procedure merges adjacent character segments into
 | ||
|  *      a single character segment, if possible.
 | ||
|  *
 | ||
|  * Arguments:
 | ||
|  *      segPtr: Pointer to the first of two adjacent segments to
 | ||
|  *              join.
 | ||
|  *      line:   Line containing segments (not used).
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      The return value is a pointer to the first segment in
 | ||
|  *      the (new) list of segments that used to start with segPtr.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      Storage for the segments may be allocated and freed.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
|         /* ARGSUSED */
 | ||
| static GtkTextLineSegment *
 | ||
| char_segment_cleanup_func (GtkTextLineSegment *segPtr, GtkTextLine *line)
 | ||
| {
 | ||
|   GtkTextLineSegment *segPtr2, *newPtr;
 | ||
| 
 | ||
|   if (GTK_DEBUG_CHECK (TEXT))
 | ||
|     char_segment_self_check (segPtr);
 | ||
| 
 | ||
|   segPtr2 = segPtr->next;
 | ||
|   if ((segPtr2 == NULL) || (segPtr2->type != >k_text_char_type))
 | ||
|     {
 | ||
|       return segPtr;
 | ||
|     }
 | ||
| 
 | ||
|   newPtr =
 | ||
|     _gtk_char_segment_new_from_two_strings (segPtr->body.chars, 
 | ||
| 					    segPtr->byte_count,
 | ||
| 					    segPtr->char_count,
 | ||
|                                             segPtr2->body.chars, 
 | ||
| 					    segPtr2->byte_count,
 | ||
| 					    segPtr2->char_count);
 | ||
| 
 | ||
|   newPtr->next = segPtr2->next;
 | ||
| 
 | ||
|   if (GTK_DEBUG_CHECK (TEXT))
 | ||
|     char_segment_self_check (newPtr);
 | ||
| 
 | ||
|   _gtk_char_segment_free (segPtr);
 | ||
|   _gtk_char_segment_free (segPtr2);
 | ||
|   return newPtr;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * char_segment_delete_func --
 | ||
|  *
 | ||
|  *      This procedure is invoked to delete a character segment.
 | ||
|  *
 | ||
|  * Arguments:
 | ||
|  *      segPtr   : Segment to delete
 | ||
|  *      line     : Line containing segment
 | ||
|  *      treeGone : Non-zero means the entire tree is being
 | ||
|  *                 deleted, so everything must get cleaned up.
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      Always returns 0 to indicate that the segment was deleted.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      Storage for the segment is freed.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
|         /* ARGSUSED */
 | ||
| static int
 | ||
| char_segment_delete_func (GtkTextLineSegment *segPtr, GtkTextLine *line, int treeGone)
 | ||
| {
 | ||
|   _gtk_char_segment_free (segPtr);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * char_segment_check_func --
 | ||
|  *
 | ||
|  *      This procedure is invoked to perform consistency checks
 | ||
|  *      on character segments.
 | ||
|  *
 | ||
|  * Arguments:
 | ||
|  *      segPtr : Segment to check
 | ||
|  *      line   : Line containing segment
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      None.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      If the segment isn’t inconsistent then the procedure
 | ||
|  *      g_errors.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
|         /* ARGSUSED */
 | ||
| static void
 | ||
| char_segment_check_func (GtkTextLineSegment *segPtr, GtkTextLine *line)
 | ||
| {
 | ||
|   char_segment_self_check (segPtr);
 | ||
| 
 | ||
|   if (segPtr->next != NULL)
 | ||
|     {
 | ||
|       if (segPtr->next->type == >k_text_char_type)
 | ||
|         {
 | ||
|           g_error ("adjacent character segments weren't merged");
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| GtkTextLineSegment*
 | ||
| _gtk_toggle_segment_new (GtkTextTagInfo *info, gboolean on)
 | ||
| {
 | ||
|   /* gcc-11 issues a diagnostic here because the size allocated
 | ||
|      for SEG does not cover the entire size of a GtkTextLineSegment
 | ||
|      and gcc has no way to know that the union will only be used
 | ||
|      for limited types and the additional space is not needed.  */
 | ||
| #pragma GCC diagnostic push
 | ||
| #pragma GCC diagnostic ignored "-Warray-bounds"
 | ||
|   GtkTextLineSegment *seg;
 | ||
| 
 | ||
|   seg = g_slice_alloc (TSEG_SIZE);
 | ||
| 
 | ||
|   seg->type = on ? >k_text_toggle_on_type : >k_text_toggle_off_type;
 | ||
| 
 | ||
|   seg->next = NULL;
 | ||
| 
 | ||
|   seg->byte_count = 0;
 | ||
|   seg->char_count = 0;
 | ||
| 
 | ||
|   seg->body.toggle.info = info;
 | ||
|   seg->body.toggle.inNodeCounts = 0;
 | ||
| 
 | ||
|   return seg;
 | ||
| #pragma GCC diagnostic pop
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| _gtk_toggle_segment_free (GtkTextLineSegment *seg)
 | ||
| {
 | ||
|   if (seg == NULL)
 | ||
|     return;
 | ||
| 
 | ||
|   g_assert (seg->type == >k_text_toggle_on_type ||
 | ||
|             seg->type == >k_text_toggle_off_type);
 | ||
| 
 | ||
|   g_slice_free1 (TSEG_SIZE, seg);
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * toggle_segment_delete_func --
 | ||
|  *
 | ||
|  *      This procedure is invoked to delete toggle segments.
 | ||
|  *
 | ||
|  * Arguments:
 | ||
|  *      segPtr   : Segment to check
 | ||
|  *      line     : Line containing segment
 | ||
|  *      treeGone : Non-zero means the entire tree is being
 | ||
|  *                 deleted so everything must get cleaned up
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      Returns 1 to indicate that the segment may not be deleted,
 | ||
|  *      unless the entire B-tree is going away.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      If the tree is going away then the toggle’s memory is
 | ||
|  *      freed;  otherwise the toggle counts in GtkTextBTreeNodes above the
 | ||
|  *      segment get updated.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
| static int
 | ||
| toggle_segment_delete_func (GtkTextLineSegment *segPtr, GtkTextLine *line, int treeGone)
 | ||
| {
 | ||
|   if (treeGone)
 | ||
|     {
 | ||
|       _gtk_toggle_segment_free (segPtr);
 | ||
|       return 0;
 | ||
|     }
 | ||
| 
 | ||
|   /*
 | ||
|    * This toggle is in the middle of a range of characters that's
 | ||
|    * being deleted.  Refuse to die.  We'll be moved to the end of
 | ||
|    * the deleted range and our cleanup procedure will be called
 | ||
|    * later.  Decrement GtkTextBTreeNode toggle counts here, and set a flag
 | ||
|    * so we'll re-increment them in the cleanup procedure.
 | ||
|    */
 | ||
| 
 | ||
|   if (segPtr->body.toggle.inNodeCounts)
 | ||
|     {
 | ||
|       _gtk_change_node_toggle_count (line->parent,
 | ||
|                                      segPtr->body.toggle.info, -1);
 | ||
|       segPtr->body.toggle.inNodeCounts = 0;
 | ||
|     }
 | ||
|   return 1;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * toggle_segment_cleanup_func --
 | ||
|  *
 | ||
|  *      This procedure is called when a toggle is part of a line that's
 | ||
|  *      been modified in some way.  It’s invoked after the
 | ||
|  *      modifications are complete.
 | ||
|  *
 | ||
|  * Arguments:
 | ||
|  *      segPtr : Segment to check
 | ||
|  *      line   : Line that now contains segment
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      The return value is the head segment in a new list
 | ||
|  *      that is to replace the tail of the line that used to
 | ||
|  *      start at segPtr.  This allows the procedure to delete
 | ||
|  *      or modify segPtr.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      Toggle counts in the GtkTextBTreeNodes above the new line will be
 | ||
|  *      updated if they’re not already.  Toggles may be collapsed
 | ||
|  *      if there are duplicate toggles at the same position.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
| static GtkTextLineSegment *
 | ||
| toggle_segment_cleanup_func (GtkTextLineSegment *segPtr, GtkTextLine *line)
 | ||
| {
 | ||
|   GtkTextLineSegment *segPtr2, *prevPtr;
 | ||
|   int counts;
 | ||
| 
 | ||
|   /*
 | ||
|    * If this is a toggle-off segment, look ahead through the next
 | ||
|    * segments to see if there's a toggle-on segment for the same tag
 | ||
|    * before any segments with non-zero size.  If so then the two
 | ||
|    * toggles cancel each other;  remove them both.
 | ||
|    */
 | ||
| 
 | ||
|   if (segPtr->type == >k_text_toggle_off_type)
 | ||
|     {
 | ||
|       for (prevPtr = segPtr, segPtr2 = prevPtr->next;
 | ||
|            (segPtr2 != NULL) && (segPtr2->byte_count == 0);
 | ||
|            prevPtr = segPtr2, segPtr2 = prevPtr->next)
 | ||
|         {
 | ||
|           if (segPtr2->type != >k_text_toggle_on_type)
 | ||
|             {
 | ||
|               continue;
 | ||
|             }
 | ||
|           if (segPtr2->body.toggle.info != segPtr->body.toggle.info)
 | ||
|             {
 | ||
|               continue;
 | ||
|             }
 | ||
|           counts = segPtr->body.toggle.inNodeCounts
 | ||
|             + segPtr2->body.toggle.inNodeCounts;
 | ||
|           if (counts != 0)
 | ||
|             {
 | ||
|               _gtk_change_node_toggle_count (line->parent,
 | ||
|                                              segPtr->body.toggle.info, -counts);
 | ||
|             }
 | ||
|           prevPtr->next = segPtr2->next;
 | ||
|           _gtk_toggle_segment_free (segPtr2);
 | ||
|           segPtr2 = segPtr->next;
 | ||
|           _gtk_toggle_segment_free (segPtr);
 | ||
|           return segPtr2;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   if (!segPtr->body.toggle.inNodeCounts)
 | ||
|     {
 | ||
|       _gtk_change_node_toggle_count (line->parent,
 | ||
|                                      segPtr->body.toggle.info, 1);
 | ||
|       segPtr->body.toggle.inNodeCounts = 1;
 | ||
|     }
 | ||
|   return segPtr;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *--------------------------------------------------------------
 | ||
|  *
 | ||
|  * toggle_segment_line_change_func --
 | ||
|  *
 | ||
|  *      This procedure is invoked when a toggle segment is about
 | ||
|  *      to move from one line to another.
 | ||
|  *
 | ||
|  * Arguments:
 | ||
|  *      segPtr : Segment to check
 | ||
|  *      line   : Line that used to contain segment
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *      None.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *      Toggle counts are decremented in the GtkTextBTreeNodes above the line.
 | ||
|  *
 | ||
|  *--------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
| static void
 | ||
| toggle_segment_line_change_func (GtkTextLineSegment *segPtr, GtkTextLine *line)
 | ||
| {
 | ||
|   if (segPtr->body.toggle.inNodeCounts)
 | ||
|     {
 | ||
|       _gtk_change_node_toggle_count (line->parent,
 | ||
|                                      segPtr->body.toggle.info, -1);
 | ||
|       segPtr->body.toggle.inNodeCounts = 0;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  * Virtual tables
 | ||
|  */
 | ||
| 
 | ||
| 
 | ||
| const GtkTextLineSegmentClass gtk_text_char_type = {
 | ||
|   "character",                          /* name */
 | ||
|   0,                                            /* leftGravity */
 | ||
|   char_segment_split_func,                              /* splitFunc */
 | ||
|   char_segment_delete_func,                             /* deleteFunc */
 | ||
|   char_segment_cleanup_func,                            /* cleanupFunc */
 | ||
|   NULL,         /* lineChangeFunc */
 | ||
|   char_segment_check_func                               /* checkFunc */
 | ||
| };
 | ||
| 
 | ||
| /*
 | ||
|  * Type record for segments marking the beginning of a tagged
 | ||
|  * range:
 | ||
|  */
 | ||
| 
 | ||
| const GtkTextLineSegmentClass gtk_text_toggle_on_type = {
 | ||
|   "toggleOn",                                   /* name */
 | ||
|   0,                                            /* leftGravity */
 | ||
|   NULL,                 /* splitFunc */
 | ||
|   toggle_segment_delete_func,                           /* deleteFunc */
 | ||
|   toggle_segment_cleanup_func,                          /* cleanupFunc */
 | ||
|   toggle_segment_line_change_func,                      /* lineChangeFunc */
 | ||
|   _gtk_toggle_segment_check_func                        /* checkFunc */
 | ||
| };
 | ||
| 
 | ||
| /*
 | ||
|  * Type record for segments marking the end of a tagged
 | ||
|  * range:
 | ||
|  */
 | ||
| 
 | ||
| const GtkTextLineSegmentClass gtk_text_toggle_off_type = {
 | ||
|   "toggleOff",                          /* name */
 | ||
|   1,                                            /* leftGravity */
 | ||
|   NULL,                 /* splitFunc */
 | ||
|   toggle_segment_delete_func,                           /* deleteFunc */
 | ||
|   toggle_segment_cleanup_func,                          /* cleanupFunc */
 | ||
|   toggle_segment_line_change_func,                      /* lineChangeFunc */
 | ||
|   _gtk_toggle_segment_check_func                        /* checkFunc */
 | ||
| };
 |