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