/*
* 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 .
*
*/
#include "config.h"
#include "gtktextbtreeprivate.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "gtktexttag.h"
#include "gtktexttagtable.h"
#include "gtktextlayoutprivate.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 char *text, guint len)
{
GtkTextLineSegment *seg;
g_assert (gtk_text_byte_begins_utf8_char (text));
seg = g_malloc (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 char *text1,
guint len1,
guint chars1,
const char *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_malloc (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_free (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"
#if defined (__clang_major__) && __clang_major__ >= 22
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Walloc-size"
#endif
GtkTextLineSegment *seg;
seg = g_malloc (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;
#if defined (__clang_major__) && __clang_major__ >= 22
#pragma clang diagnostic pop
#endif
#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_free (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 */
};
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland