]> Pileus Git - ~andy/gtk/blob - gtk/gtktextsegment.c
Adapt to uscore-ification of gtktextiterprivate
[~andy/gtk] / gtk / gtktextsegment.c
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 "gtktextbtree.h"
54 #include <string.h>
55 #include <ctype.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include "gtktexttag.h"
59 #include "gtktexttagtable.h"
60 #include "gtktextlayout.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_flags & GTK_DEBUG_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 ("char_segment_check_func: segment has size <= 0");
176     }
177
178   if (strlen (seg->body.chars) != seg->byte_count)
179     {
180       g_error ("char_segment_check_func: segment has wrong size");
181     }
182
183   if (g_utf8_strlen (seg->body.chars, 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 gchar *text, guint len)
191 {
192   GtkTextLineSegment *seg;
193
194   g_assert (gtk_text_byte_begins_utf8_char (text));
195
196   seg = g_malloc (CSEG_SIZE (len));
197   seg->type = &gtk_text_char_type;
198   seg->next = NULL;
199   seg->byte_count = len;
200   memcpy (seg->body.chars, text, len);
201   seg->body.chars[len] = '\0';
202
203   seg->char_count = g_utf8_strlen (seg->body.chars, seg->byte_count);
204
205   if (gtk_debug_flags & GTK_DEBUG_TEXT)
206     char_segment_self_check (seg);
207
208   return seg;
209 }
210
211 GtkTextLineSegment*
212 _gtk_char_segment_new_from_two_strings (const gchar *text1, guint len1,
213                                         const gchar *text2, guint len2)
214 {
215   GtkTextLineSegment *seg;
216
217   g_assert (gtk_text_byte_begins_utf8_char (text1));
218   g_assert (gtk_text_byte_begins_utf8_char (text2));
219
220   seg = g_malloc (CSEG_SIZE (len1+len2));
221   seg->type = &gtk_text_char_type;
222   seg->next = NULL;
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';
227
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);
231
232   if (gtk_debug_flags & GTK_DEBUG_TEXT)
233     char_segment_self_check (seg);
234
235   return seg;
236 }
237
238 /*
239  *--------------------------------------------------------------
240  *
241  * char_segment_split_func --
242  *
243  *      This procedure implements splitting for character segments.
244  *
245  * Results:
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.
249  *
250  * Side effects:
251  *      Storage for segPtr is freed.
252  *
253  *--------------------------------------------------------------
254  */
255
256 static GtkTextLineSegment *
257 char_segment_split_func (GtkTextLineSegment *seg, int index)
258 {
259   GtkTextLineSegment *new1, *new2;
260
261   g_assert (index < seg->byte_count);
262
263   if (gtk_debug_flags & GTK_DEBUG_TEXT)
264     {
265       char_segment_self_check (seg);
266     }
267
268   new1 = _gtk_char_segment_new (seg->body.chars, index);
269   new2 = _gtk_char_segment_new (seg->body.chars + index, seg->byte_count - index);
270
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);
275
276   new1->next = new2;
277   new2->next = seg->next;
278
279   if (gtk_debug_flags & GTK_DEBUG_TEXT)
280     {
281       char_segment_self_check (new1);
282       char_segment_self_check (new2);
283     }
284
285   g_free (seg);
286   return new1;
287 }
288
289 /*
290  *--------------------------------------------------------------
291  *
292  * char_segment_cleanup_func --
293  *
294  *      This procedure merges adjacent character segments into
295  *      a single character segment, if possible.
296  *
297  * Results:
298  *      The return value is a pointer to the first segment in
299  *      the (new) list of segments that used to start with segPtr.
300  *
301  * Side effects:
302  *      Storage for the segments may be allocated and freed.
303  *
304  *--------------------------------------------------------------
305  */
306
307         /* ARGSUSED */
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
313                                         * used). */
314 {
315   GtkTextLineSegment *segPtr2, *newPtr;
316
317   if (gtk_debug_flags & GTK_DEBUG_TEXT)
318     char_segment_self_check (segPtr);
319
320   segPtr2 = segPtr->next;
321   if ((segPtr2 == NULL) || (segPtr2->type != &gtk_text_char_type))
322     {
323       return segPtr;
324     }
325
326   newPtr =
327     _gtk_char_segment_new_from_two_strings (segPtr->body.chars, segPtr->byte_count,
328                                             segPtr2->body.chars, segPtr2->byte_count);
329
330   newPtr->next = segPtr2->next;
331
332   if (gtk_debug_flags & GTK_DEBUG_TEXT)
333     char_segment_self_check (newPtr);
334
335   g_free (segPtr);
336   g_free (segPtr2);
337   return newPtr;
338 }
339
340 /*
341  *--------------------------------------------------------------
342  *
343  * char_segment_delete_func --
344  *
345  *      This procedure is invoked to delete a character segment.
346  *
347  * Results:
348  *      Always returns 0 to indicate that the segment was deleted.
349  *
350  * Side effects:
351  *      Storage for the segment is freed.
352  *
353  *--------------------------------------------------------------
354  */
355
356         /* ARGSUSED */
357 static int
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
363                                          * get cleaned up. */
364 {
365   g_free ((char*) segPtr);
366   return 0;
367 }
368
369 /*
370  *--------------------------------------------------------------
371  *
372  * char_segment_check_func --
373  *
374  *      This procedure is invoked to perform consistency checks
375  *      on character segments.
376  *
377  * Results:
378  *      None.
379  *
380  * Side effects:
381  *      If the segment isn't inconsistent then the procedure
382  *      g_errors.
383  *
384  *--------------------------------------------------------------
385  */
386
387         /* ARGSUSED */
388 static void
389 char_segment_check_func (segPtr, line)
390      GtkTextLineSegment *segPtr;           /* Segment to check. */
391      GtkTextLine *line;                /* Line containing segment. */
392 {
393   char_segment_self_check (segPtr);
394
395   if (segPtr->next != NULL)
396     {
397       if (segPtr->next->type == &gtk_text_char_type)
398         {
399           g_error ("char_segment_check_func: adjacent character segments weren't merged");
400         }
401     }
402 }
403
404 GtkTextLineSegment*
405 _gtk_toggle_segment_new (GtkTextTagInfo *info, gboolean on)
406 {
407   GtkTextLineSegment *seg;
408
409   seg = g_malloc (TSEG_SIZE);
410
411   seg->type = on ? &gtk_text_toggle_on_type : &gtk_text_toggle_off_type;
412
413   seg->next = NULL;
414
415   seg->byte_count = 0;
416   seg->char_count = 0;
417
418   seg->body.toggle.info = info;
419   seg->body.toggle.inNodeCounts = 0;
420
421   return seg;
422 }
423
424 /*
425  *--------------------------------------------------------------
426  *
427  * toggle_segment_delete_func --
428  *
429  *      This procedure is invoked to delete toggle segments.
430  *
431  * Results:
432  *      Returns 1 to indicate that the segment may not be deleted,
433  *      unless the entire B-tree is going away.
434  *
435  * Side effects:
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.
439  *
440  *--------------------------------------------------------------
441  */
442
443 static int
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
449                                          * get cleaned up. */
450 {
451   if (treeGone)
452     {
453       g_free ((char *) segPtr);
454       return 0;
455     }
456
457   /*
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.
463    */
464
465   if (segPtr->body.toggle.inNodeCounts)
466     {
467       _gtk_change_node_toggle_count (line->parent,
468                                      segPtr->body.toggle.info, -1);
469       segPtr->body.toggle.inNodeCounts = 0;
470     }
471   return 1;
472 }
473
474 /*
475  *--------------------------------------------------------------
476  *
477  * toggle_segment_cleanup_func --
478  *
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.
482  *
483  * Results:
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
487  *      or modify segPtr.
488  *
489  * Side effects:
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.
493  *
494  *--------------------------------------------------------------
495  */
496
497 static GtkTextLineSegment *
498 toggle_segment_cleanup_func (segPtr, line)
499      GtkTextLineSegment *segPtr;   /* Segment to check. */
500      GtkTextLine *line;        /* Line that now contains segment. */
501 {
502   GtkTextLineSegment *segPtr2, *prevPtr;
503   int counts;
504
505   /*
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.
510    */
511
512   if (segPtr->type == &gtk_text_toggle_off_type)
513     {
514       for (prevPtr = segPtr, segPtr2 = prevPtr->next;
515            (segPtr2 != NULL) && (segPtr2->byte_count == 0);
516            prevPtr = segPtr2, segPtr2 = prevPtr->next)
517         {
518           if (segPtr2->type != &gtk_text_toggle_on_type)
519             {
520               continue;
521             }
522           if (segPtr2->body.toggle.info != segPtr->body.toggle.info)
523             {
524               continue;
525             }
526           counts = segPtr->body.toggle.inNodeCounts
527             + segPtr2->body.toggle.inNodeCounts;
528           if (counts != 0)
529             {
530               _gtk_change_node_toggle_count (line->parent,
531                                              segPtr->body.toggle.info, -counts);
532             }
533           prevPtr->next = segPtr2->next;
534           g_free ((char *) segPtr2);
535           segPtr2 = segPtr->next;
536           g_free ((char *) segPtr);
537           return segPtr2;
538         }
539     }
540
541   if (!segPtr->body.toggle.inNodeCounts)
542     {
543       _gtk_change_node_toggle_count (line->parent,
544                                      segPtr->body.toggle.info, 1);
545       segPtr->body.toggle.inNodeCounts = 1;
546     }
547   return segPtr;
548 }
549
550 /*
551  *--------------------------------------------------------------
552  *
553  * toggle_segment_line_change_func --
554  *
555  *      This procedure is invoked when a toggle segment is about
556  *      to move from one line to another.
557  *
558  * Results:
559  *      None.
560  *
561  * Side effects:
562  *      Toggle counts are decremented in the GtkTextBTreeNodes above the line.
563  *
564  *--------------------------------------------------------------
565  */
566
567 static void
568 toggle_segment_line_change_func (segPtr, line)
569      GtkTextLineSegment *segPtr;   /* Segment to check. */
570      GtkTextLine *line;        /* Line that used to contain segment. */
571 {
572   if (segPtr->body.toggle.inNodeCounts)
573     {
574       _gtk_change_node_toggle_count (line->parent,
575                                      segPtr->body.toggle.info, -1);
576       segPtr->body.toggle.inNodeCounts = 0;
577     }
578 }
579
580 /*
581  * Virtual tables
582  */
583
584
585 GtkTextLineSegmentClass gtk_text_char_type = {
586   "character",                          /* name */
587   0,                                            /* leftGravity */
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 */
593 };
594
595 /*
596  * Type record for segments marking the beginning of a tagged
597  * range:
598  */
599
600 GtkTextLineSegmentClass gtk_text_toggle_on_type = {
601   "toggleOn",                                   /* name */
602   0,                                            /* leftGravity */
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 */
608 };
609
610 /*
611  * Type record for segments marking the end of a tagged
612  * range:
613  */
614
615 GtkTextLineSegmentClass gtk_text_toggle_off_type = {
616   "toggleOff",                          /* name */
617   1,                                            /* leftGravity */
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 */
623 };