]> Pileus Git - ~andy/gtk/blob - gtk/gtktextsegment.c
Massive reindentation and reformatting. Arg alignment and comments still
[~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   prev = NULL;
106   seg = line->segments;
107
108   while (seg != NULL)
109     {
110       if (seg->byte_count > count)
111         {
112           if (count == 0)
113             {
114               return prev;
115             }
116           else
117             {
118               g_assert (count != seg->byte_count);
119               g_assert (seg->byte_count > 0);
120
121               gtk_text_btree_segments_changed (tree);
122
123               seg = (*seg->type->splitFunc)(seg, count);
124
125               if (prev == NULL)
126                 line->segments = seg;
127               else
128                 prev->next = seg;
129
130               return seg;
131             }
132         }
133       else if ((seg->byte_count == 0) && (count == 0)
134                && !seg->type->leftGravity)
135         {
136           return prev;
137         }
138
139       count -= seg->byte_count;
140       prev = seg;
141       seg = seg->next;
142     }
143   g_error ("split_segment reached end of line!");
144   return NULL;
145 }
146
147
148 /*
149  * Macros that determine how much space to allocate for new segments:
150  */
151
152 #define CSEG_SIZE(chars) ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
153         + 1 + (chars)))
154 #define TSEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
155         + sizeof (GtkTextToggleBody)))
156
157 /*
158  * Type functions
159  */
160
161 static void
162 char_segment_self_check (GtkTextLineSegment *seg)
163 {
164   /* This function checks the segment itself, but doesn't
165      assume the segment has been validly inserted into
166      the btree. */
167
168   g_assert (seg != NULL);
169
170   if (seg->byte_count <= 0)
171     {
172       g_error ("char_segment_check_func: segment has size <= 0");
173     }
174
175   if (strlen (seg->body.chars) != seg->byte_count)
176     {
177       g_error ("char_segment_check_func: segment has wrong size");
178     }
179
180   if (g_utf8_strlen (seg->body.chars, seg->byte_count) != seg->char_count)
181     {
182       g_error ("char segment has wrong character count");
183     }
184 }
185
186 GtkTextLineSegment*
187 _gtk_char_segment_new (const gchar *text, guint len)
188 {
189   GtkTextLineSegment *seg;
190
191   g_assert (gtk_text_byte_begins_utf8_char (text));
192
193   seg = g_malloc (CSEG_SIZE (len));
194   seg->type = &gtk_text_char_type;
195   seg->next = NULL;
196   seg->byte_count = len;
197   memcpy (seg->body.chars, text, len);
198   seg->body.chars[len] = '\0';
199
200   seg->char_count = g_utf8_strlen (seg->body.chars, seg->byte_count);
201
202   if (gtk_debug_flags & GTK_DEBUG_TEXT)
203     char_segment_self_check (seg);
204
205   return seg;
206 }
207
208 GtkTextLineSegment*
209 _gtk_char_segment_new_from_two_strings (const gchar *text1, guint len1,
210                                         const gchar *text2, guint len2)
211 {
212   GtkTextLineSegment *seg;
213
214   g_assert (gtk_text_byte_begins_utf8_char (text1));
215   g_assert (gtk_text_byte_begins_utf8_char (text2));
216
217   seg = g_malloc (CSEG_SIZE (len1+len2));
218   seg->type = &gtk_text_char_type;
219   seg->next = NULL;
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';
224
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 = g_utf8_strlen (seg->body.chars, seg->byte_count);
228
229   if (gtk_debug_flags & GTK_DEBUG_TEXT)
230     char_segment_self_check (seg);
231
232   return seg;
233 }
234
235 /*
236  *--------------------------------------------------------------
237  *
238  * char_segment_split_func --
239  *
240  *      This procedure implements splitting for character segments.
241  *
242  * Results:
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.
246  *
247  * Side effects:
248  *      Storage for segPtr is freed.
249  *
250  *--------------------------------------------------------------
251  */
252
253 static GtkTextLineSegment *
254 char_segment_split_func (GtkTextLineSegment *seg, int index)
255 {
256   GtkTextLineSegment *new1, *new2;
257
258   g_assert (index < seg->byte_count);
259
260   if (gtk_debug_flags & GTK_DEBUG_TEXT)
261     {
262       char_segment_self_check (seg);
263     }
264
265   new1 = _gtk_char_segment_new (seg->body.chars, index);
266   new2 = _gtk_char_segment_new (seg->body.chars + index, seg->byte_count - index);
267
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);
272
273   new1->next = new2;
274   new2->next = seg->next;
275
276   if (gtk_debug_flags & GTK_DEBUG_TEXT)
277     {
278       char_segment_self_check (new1);
279       char_segment_self_check (new2);
280     }
281
282   g_free (seg);
283   return new1;
284 }
285
286 /*
287  *--------------------------------------------------------------
288  *
289  * char_segment_cleanup_func --
290  *
291  *      This procedure merges adjacent character segments into
292  *      a single character segment, if possible.
293  *
294  * Results:
295  *      The return value is a pointer to the first segment in
296  *      the (new) list of segments that used to start with segPtr.
297  *
298  * Side effects:
299  *      Storage for the segments may be allocated and freed.
300  *
301  *--------------------------------------------------------------
302  */
303
304         /* ARGSUSED */
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
310                                         * used). */
311 {
312   GtkTextLineSegment *segPtr2, *newPtr;
313
314   if (gtk_debug_flags & GTK_DEBUG_TEXT)
315     char_segment_self_check (segPtr);
316
317   segPtr2 = segPtr->next;
318   if ((segPtr2 == NULL) || (segPtr2->type != &gtk_text_char_type))
319     {
320       return segPtr;
321     }
322
323   newPtr =
324     _gtk_char_segment_new_from_two_strings (segPtr->body.chars, segPtr->byte_count,
325                                             segPtr2->body.chars, segPtr2->byte_count);
326
327   newPtr->next = segPtr2->next;
328
329   if (gtk_debug_flags & GTK_DEBUG_TEXT)
330     char_segment_self_check (newPtr);
331
332   g_free (segPtr);
333   g_free (segPtr2);
334   return newPtr;
335 }
336
337 /*
338  *--------------------------------------------------------------
339  *
340  * char_segment_delete_func --
341  *
342  *      This procedure is invoked to delete a character segment.
343  *
344  * Results:
345  *      Always returns 0 to indicate that the segment was deleted.
346  *
347  * Side effects:
348  *      Storage for the segment is freed.
349  *
350  *--------------------------------------------------------------
351  */
352
353         /* ARGSUSED */
354 static int
355 char_segment_delete_func (segPtr, line, treeGone)
356      GtkTextLineSegment *segPtr;           /* Segment to delete. */
357      GtkTextLine *line;                /* Line containing segment. */
358      int treeGone;                      /* Non-zero means the entire tree is
359                                          * being deleted, so everything must
360                                          * get cleaned up. */
361 {
362   g_free ((char*) segPtr);
363   return 0;
364 }
365
366 /*
367  *--------------------------------------------------------------
368  *
369  * char_segment_check_func --
370  *
371  *      This procedure is invoked to perform consistency checks
372  *      on character segments.
373  *
374  * Results:
375  *      None.
376  *
377  * Side effects:
378  *      If the segment isn't inconsistent then the procedure
379  *      g_errors.
380  *
381  *--------------------------------------------------------------
382  */
383
384         /* ARGSUSED */
385 static void
386 char_segment_check_func (segPtr, line)
387      GtkTextLineSegment *segPtr;           /* Segment to check. */
388      GtkTextLine *line;                /* Line containing segment. */
389 {
390   char_segment_self_check (segPtr);
391
392   if (segPtr->next == NULL)
393     {
394       if (segPtr->body.chars[segPtr->byte_count-1] != '\n')
395         {
396           g_error ("char_segment_check_func: line doesn't end with newline");
397         }
398     }
399   else
400     {
401       if (segPtr->next->type == &gtk_text_char_type)
402         {
403           g_error ("char_segment_check_func: adjacent character segments weren't merged");
404         }
405     }
406 }
407
408 GtkTextLineSegment*
409 _gtk_toggle_segment_new (GtkTextTagInfo *info, gboolean on)
410 {
411   GtkTextLineSegment *seg;
412
413   seg = g_malloc (TSEG_SIZE);
414
415   seg->type = on ? &gtk_text_toggle_on_type : &gtk_text_toggle_off_type;
416
417   seg->next = NULL;
418
419   seg->byte_count = 0;
420   seg->char_count = 0;
421
422   seg->body.toggle.info = info;
423   seg->body.toggle.inNodeCounts = 0;
424
425   return seg;
426 }
427
428 /*
429  *--------------------------------------------------------------
430  *
431  * toggle_segment_delete_func --
432  *
433  *      This procedure is invoked to delete toggle segments.
434  *
435  * Results:
436  *      Returns 1 to indicate that the segment may not be deleted,
437  *      unless the entire B-tree is going away.
438  *
439  * Side effects:
440  *      If the tree is going away then the toggle's memory is
441  *      freed;  otherwise the toggle counts in GtkTextBTreeNodes above the
442  *      segment get updated.
443  *
444  *--------------------------------------------------------------
445  */
446
447 static int
448 toggle_segment_delete_func (segPtr, line, treeGone)
449      GtkTextLineSegment *segPtr;           /* Segment to check. */
450      GtkTextLine *line;                /* Line containing segment. */
451      int treeGone;                      /* Non-zero means the entire tree is
452                                          * being deleted, so everything must
453                                          * get cleaned up. */
454 {
455   if (treeGone)
456     {
457       g_free ((char *) segPtr);
458       return 0;
459     }
460
461   /*
462    * This toggle is in the middle of a range of characters that's
463    * being deleted.  Refuse to die.  We'll be moved to the end of
464    * the deleted range and our cleanup procedure will be called
465    * later.  Decrement GtkTextBTreeNode toggle counts here, and set a flag
466    * so we'll re-increment them in the cleanup procedure.
467    */
468
469   if (segPtr->body.toggle.inNodeCounts)
470     {
471       _gtk_change_node_toggle_count (line->parent,
472                                      segPtr->body.toggle.info, -1);
473       segPtr->body.toggle.inNodeCounts = 0;
474     }
475   return 1;
476 }
477
478 /*
479  *--------------------------------------------------------------
480  *
481  * toggle_segment_cleanup_func --
482  *
483  *      This procedure is called when a toggle is part of a line that's
484  *      been modified in some way.  It's invoked after the
485  *      modifications are complete.
486  *
487  * Results:
488  *      The return value is the head segment in a new list
489  *      that is to replace the tail of the line that used to
490  *      start at segPtr.  This allows the procedure to delete
491  *      or modify segPtr.
492  *
493  * Side effects:
494  *      Toggle counts in the GtkTextBTreeNodes above the new line will be
495  *      updated if they're not already.  Toggles may be collapsed
496  *      if there are duplicate toggles at the same position.
497  *
498  *--------------------------------------------------------------
499  */
500
501 static GtkTextLineSegment *
502 toggle_segment_cleanup_func (segPtr, line)
503      GtkTextLineSegment *segPtr;   /* Segment to check. */
504      GtkTextLine *line;        /* Line that now contains segment. */
505 {
506   GtkTextLineSegment *segPtr2, *prevPtr;
507   int counts;
508
509   /*
510    * If this is a toggle-off segment, look ahead through the next
511    * segments to see if there's a toggle-on segment for the same tag
512    * before any segments with non-zero size.  If so then the two
513    * toggles cancel each other;  remove them both.
514    */
515
516   if (segPtr->type == &gtk_text_toggle_off_type)
517     {
518       for (prevPtr = segPtr, segPtr2 = prevPtr->next;
519            (segPtr2 != NULL) && (segPtr2->byte_count == 0);
520            prevPtr = segPtr2, segPtr2 = prevPtr->next)
521         {
522           if (segPtr2->type != &gtk_text_toggle_on_type)
523             {
524               continue;
525             }
526           if (segPtr2->body.toggle.info != segPtr->body.toggle.info)
527             {
528               continue;
529             }
530           counts = segPtr->body.toggle.inNodeCounts
531             + segPtr2->body.toggle.inNodeCounts;
532           if (counts != 0)
533             {
534               _gtk_change_node_toggle_count (line->parent,
535                                              segPtr->body.toggle.info, -counts);
536             }
537           prevPtr->next = segPtr2->next;
538           g_free ((char *) segPtr2);
539           segPtr2 = segPtr->next;
540           g_free ((char *) segPtr);
541           return segPtr2;
542         }
543     }
544
545   if (!segPtr->body.toggle.inNodeCounts)
546     {
547       _gtk_change_node_toggle_count (line->parent,
548                                      segPtr->body.toggle.info, 1);
549       segPtr->body.toggle.inNodeCounts = 1;
550     }
551   return segPtr;
552 }
553
554 /*
555  *--------------------------------------------------------------
556  *
557  * toggle_segment_line_change_func --
558  *
559  *      This procedure is invoked when a toggle segment is about
560  *      to move from one line to another.
561  *
562  * Results:
563  *      None.
564  *
565  * Side effects:
566  *      Toggle counts are decremented in the GtkTextBTreeNodes above the line.
567  *
568  *--------------------------------------------------------------
569  */
570
571 static void
572 toggle_segment_line_change_func (segPtr, line)
573      GtkTextLineSegment *segPtr;   /* Segment to check. */
574      GtkTextLine *line;        /* Line that used to contain segment. */
575 {
576   if (segPtr->body.toggle.inNodeCounts)
577     {
578       _gtk_change_node_toggle_count (line->parent,
579                                      segPtr->body.toggle.info, -1);
580       segPtr->body.toggle.inNodeCounts = 0;
581     }
582 }
583
584 /*
585  * Virtual tables
586  */
587
588
589 GtkTextLineSegmentClass gtk_text_char_type = {
590   "character",                          /* name */
591   0,                                            /* leftGravity */
592   char_segment_split_func,                              /* splitFunc */
593   char_segment_delete_func,                             /* deleteFunc */
594   char_segment_cleanup_func,                            /* cleanupFunc */
595   NULL,         /* lineChangeFunc */
596   char_segment_check_func                               /* checkFunc */
597 };
598
599 /*
600  * Type record for segments marking the beginning of a tagged
601  * range:
602  */
603
604 GtkTextLineSegmentClass gtk_text_toggle_on_type = {
605   "toggleOn",                                   /* name */
606   0,                                            /* leftGravity */
607   NULL,                 /* splitFunc */
608   toggle_segment_delete_func,                           /* deleteFunc */
609   toggle_segment_cleanup_func,                          /* cleanupFunc */
610   toggle_segment_line_change_func,                      /* lineChangeFunc */
611   _gtk_toggle_segment_check_func                        /* checkFunc */
612 };
613
614 /*
615  * Type record for segments marking the end of a tagged
616  * range:
617  */
618
619 GtkTextLineSegmentClass gtk_text_toggle_off_type = {
620   "toggleOff",                          /* name */
621   1,                                            /* leftGravity */
622   NULL,                 /* splitFunc */
623   toggle_segment_delete_func,                           /* deleteFunc */
624   toggle_segment_cleanup_func,                          /* cleanupFunc */
625   toggle_segment_line_change_func,                      /* lineChangeFunc */
626   _gtk_toggle_segment_check_func                        /* checkFunc */
627 };