]> Pileus Git - ~andy/gtk/blob - gtk/gtktextsegment.c
Remove all references to offscreen flag which was no longer used.
[~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_line(iter);
101   tree = gtk_text_iter_get_btree(iter);
102   
103   count = gtk_text_iter_get_line_byte(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 (gtk_text_view_num_utf_chars(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 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 = gtk_text_view_num_utf_chars(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 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 = gtk_text_view_num_utf_chars(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 = char_segment_new(seg->body.chars, index);
266   new2 = 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 = char_segment_new_from_two_strings(segPtr->body.chars, segPtr->byte_count,
324                                              segPtr2->body.chars, segPtr2->byte_count);
325
326   newPtr->next = segPtr2->next;
327
328   if (gtk_debug_flags & GTK_DEBUG_TEXT)
329     char_segment_self_check(newPtr);
330   
331   g_free(segPtr);
332   g_free(segPtr2);
333   return newPtr;
334 }
335
336 /*
337  *--------------------------------------------------------------
338  *
339  * char_segment_delete_func --
340  *
341  *      This procedure is invoked to delete a character segment.
342  *
343  * Results:
344  *      Always returns 0 to indicate that the segment was deleted.
345  *
346  * Side effects:
347  *      Storage for the segment is freed.
348  *
349  *--------------------------------------------------------------
350  */
351
352         /* ARGSUSED */
353 static int
354 char_segment_delete_func(segPtr, line, treeGone)
355      GtkTextLineSegment *segPtr;           /* Segment to delete. */
356      GtkTextLine *line;                /* Line containing segment. */
357      int treeGone;                      /* Non-zero means the entire tree is
358                                          * being deleted, so everything must
359                                          * get cleaned up. */
360 {
361   g_free((char*) segPtr);
362   return 0;
363 }
364
365 /*
366  *--------------------------------------------------------------
367  *
368  * char_segment_check_func --
369  *
370  *      This procedure is invoked to perform consistency checks
371  *      on character segments.
372  *
373  * Results:
374  *      None.
375  *
376  * Side effects:
377  *      If the segment isn't inconsistent then the procedure
378  *      g_errors.
379  *
380  *--------------------------------------------------------------
381  */
382
383         /* ARGSUSED */
384 static void
385 char_segment_check_func(segPtr, line)
386      GtkTextLineSegment *segPtr;           /* Segment to check. */
387      GtkTextLine *line;                /* Line containing segment. */
388 {
389   char_segment_self_check(segPtr);
390   
391   if (segPtr->next == NULL)
392     {
393       if (segPtr->body.chars[segPtr->byte_count-1] != '\n')
394         {
395           g_error("char_segment_check_func: line doesn't end with newline");
396         }
397     }
398   else
399     {
400       if (segPtr->next->type == &gtk_text_char_type)
401         {
402           g_error("char_segment_check_func: adjacent character segments weren't merged");
403         }
404     }
405 }
406
407 GtkTextLineSegment*
408 toggle_segment_new(GtkTextTagInfo *info, gboolean on)
409 {
410   GtkTextLineSegment *seg;
411
412   seg = g_malloc(TSEG_SIZE);
413
414   seg->type = on ? &gtk_text_toggle_on_type : &gtk_text_toggle_off_type;
415
416   seg->next = NULL;
417
418   seg->byte_count = 0;
419   seg->char_count = 0;
420   
421   seg->body.toggle.info = info;
422   seg->body.toggle.inNodeCounts = 0;
423
424   return seg;
425 }
426
427 /*
428  *--------------------------------------------------------------
429  *
430  * toggle_segment_delete_func --
431  *
432  *      This procedure is invoked to delete toggle segments.
433  *
434  * Results:
435  *      Returns 1 to indicate that the segment may not be deleted,
436  *      unless the entire B-tree is going away.
437  *
438  * Side effects:
439  *      If the tree is going away then the toggle's memory is
440  *      freed;  otherwise the toggle counts in GtkTextBTreeNodes above the
441  *      segment get updated.
442  *
443  *--------------------------------------------------------------
444  */
445
446 static int
447 toggle_segment_delete_func(segPtr, line, treeGone)
448      GtkTextLineSegment *segPtr;           /* Segment to check. */
449      GtkTextLine *line;                /* Line containing segment. */
450      int treeGone;                      /* Non-zero means the entire tree is
451                                          * being deleted, so everything must
452                                          * get cleaned up. */
453 {
454   if (treeGone)
455     {
456       g_free((char *) segPtr);
457       return 0;
458     }
459
460   /*
461    * This toggle is in the middle of a range of characters that's
462    * being deleted.  Refuse to die.  We'll be moved to the end of
463    * the deleted range and our cleanup procedure will be called
464    * later.  Decrement GtkTextBTreeNode toggle counts here, and set a flag
465    * so we'll re-increment them in the cleanup procedure.
466    */
467
468   if (segPtr->body.toggle.inNodeCounts)
469     {
470       change_node_toggle_count(line->parent,
471                                segPtr->body.toggle.info, -1);
472       segPtr->body.toggle.inNodeCounts = 0;
473     }
474   return 1;
475 }
476
477 /*
478  *--------------------------------------------------------------
479  *
480  * toggle_segment_cleanup_func --
481  *
482  *      This procedure is called when a toggle is part of a line that's
483  *      been modified in some way.  It's invoked after the
484  *      modifications are complete.
485  *
486  * Results:
487  *      The return value is the head segment in a new list
488  *      that is to replace the tail of the line that used to
489  *      start at segPtr.  This allows the procedure to delete
490  *      or modify segPtr.
491  *
492  * Side effects:
493  *      Toggle counts in the GtkTextBTreeNodes above the new line will be
494  *      updated if they're not already.  Toggles may be collapsed
495  *      if there are duplicate toggles at the same position.
496  *
497  *--------------------------------------------------------------
498  */
499
500 static GtkTextLineSegment *
501 toggle_segment_cleanup_func(segPtr, line)
502      GtkTextLineSegment *segPtr;   /* Segment to check. */
503      GtkTextLine *line;        /* Line that now contains segment. */
504 {
505   GtkTextLineSegment *segPtr2, *prevPtr;
506   int counts;
507
508   /*
509    * If this is a toggle-off segment, look ahead through the next
510    * segments to see if there's a toggle-on segment for the same tag
511    * before any segments with non-zero size.  If so then the two
512    * toggles cancel each other;  remove them both.
513    */
514
515   if (segPtr->type == &gtk_text_toggle_off_type)
516     {
517       for (prevPtr = segPtr, segPtr2 = prevPtr->next;
518            (segPtr2 != NULL) && (segPtr2->byte_count == 0);
519            prevPtr = segPtr2, segPtr2 = prevPtr->next)
520         {
521           if (segPtr2->type != &gtk_text_toggle_on_type)
522             {
523               continue;
524             }
525           if (segPtr2->body.toggle.info != segPtr->body.toggle.info)
526             {
527               continue;
528             }
529           counts = segPtr->body.toggle.inNodeCounts
530             + segPtr2->body.toggle.inNodeCounts;
531           if (counts != 0)
532             {
533               change_node_toggle_count(line->parent,
534                                        segPtr->body.toggle.info, -counts);
535             }
536           prevPtr->next = segPtr2->next;
537           g_free((char *) segPtr2);
538           segPtr2 = segPtr->next;
539           g_free((char *) segPtr);
540           return segPtr2;
541         }
542     }
543
544   if (!segPtr->body.toggle.inNodeCounts)
545     {
546       change_node_toggle_count(line->parent,
547                                segPtr->body.toggle.info, 1);
548       segPtr->body.toggle.inNodeCounts = 1;
549     }
550   return segPtr;
551 }
552
553 /*
554  *--------------------------------------------------------------
555  *
556  * toggle_segment_line_change_func --
557  *
558  *      This procedure is invoked when a toggle segment is about
559  *      to move from one line to another.
560  *
561  * Results:
562  *      None.
563  *
564  * Side effects:
565  *      Toggle counts are decremented in the GtkTextBTreeNodes above the line.
566  *
567  *--------------------------------------------------------------
568  */
569
570 static void
571 toggle_segment_line_change_func(segPtr, line)
572      GtkTextLineSegment *segPtr;   /* Segment to check. */
573      GtkTextLine *line;        /* Line that used to contain segment. */
574 {
575   if (segPtr->body.toggle.inNodeCounts)
576     {
577       change_node_toggle_count(line->parent,
578                                segPtr->body.toggle.info, -1);
579       segPtr->body.toggle.inNodeCounts = 0;
580     }
581 }
582
583 /*
584  * Virtual tables
585  */
586
587
588 GtkTextLineSegmentClass gtk_text_char_type = {
589   "character",                          /* name */
590   0,                                            /* leftGravity */
591   char_segment_split_func,                              /* splitFunc */
592   char_segment_delete_func,                             /* deleteFunc */
593   char_segment_cleanup_func,                            /* cleanupFunc */
594   NULL,         /* lineChangeFunc */
595   char_segment_check_func                               /* checkFunc */
596 };
597
598 /*
599  * Type record for segments marking the beginning of a tagged
600  * range:
601  */
602
603 GtkTextLineSegmentClass gtk_text_toggle_on_type = {
604   "toggleOn",                                   /* name */
605   0,                                            /* leftGravity */
606   NULL,                 /* splitFunc */
607   toggle_segment_delete_func,                           /* deleteFunc */
608   toggle_segment_cleanup_func,                          /* cleanupFunc */
609   toggle_segment_line_change_func,                      /* lineChangeFunc */
610   toggle_segment_check_func                             /* checkFunc */
611 };
612
613 /*
614  * Type record for segments marking the end of a tagged
615  * range:
616  */
617
618 GtkTextLineSegmentClass gtk_text_toggle_off_type = {
619   "toggleOff",                          /* name */
620   1,                                            /* leftGravity */
621   NULL,                 /* splitFunc */
622   toggle_segment_delete_func,                           /* deleteFunc */
623   toggle_segment_cleanup_func,                          /* cleanupFunc */
624   toggle_segment_line_change_func,                      /* lineChangeFunc */
625   toggle_segment_check_func                             /* checkFunc */
626 };