1 /*
2  * Gtktextbtree.c --
3  *
4  *      This file contains code that manages the B-tree representation
5  *      of text for the text buffer and implements character and
6  *      toggle segment types.
7  *
8  * Copyright (c) 1992-1994 The Regents of the University of California.
9  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10  * Copyright (c) 2000      Red Hat, Inc.
11  * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
12  *
13  * This software is copyrighted by the Regents of the University of
14  * California, Sun Microsystems, Inc., and other parties.  The
15  * following terms apply to all files associated with the software
16  * unless explicitly disclaimed in individual files.
17  *
18  * The authors hereby grant permission to use, copy, modify,
19  * distribute, and license this software and its documentation for any
20  * purpose, provided that existing copyright notices are retained in
21  * all copies and that this notice is included verbatim in any
22  * distributions. No written agreement, license, or royalty fee is
23  * required for any of the authorized uses.  Modifications to this
24  * software may be copyrighted by their authors and need not follow
25  * the licensing terms described here, provided that the new terms are
26  * clearly indicated on the first page of each file where they apply.
27  *
28  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32  * OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
40  *
41  * GOVERNMENT USE: If you are acquiring this software on behalf of the
42  * U.S. government, the Government shall have only "Restricted Rights"
43  * in the software and related documentation as defined in the Federal
44  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
45  * are acquiring the software on behalf of the Department of Defense,
46  * the software shall be classified as "Commercial Computer Software"
47  * and the Government shall have only "Restricted Rights" as defined
48  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
49  * foregoing, the authors grant the U.S. Government and others acting
50  * in its behalf permission to use and distribute the software in
51  * accordance with the terms specified in this license.
52  *
53  */
54 
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "config.h"
57 #include "gtktextbtree.h"
58 #include <string.h>
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include "gtktextbufferprivate.h"
62 #include "gtktexttag.h"
63 #include "gtktexttagprivate.h"
64 #include "gtktexttagtableprivate.h"
65 #include "gtktextlayout.h"
66 #include "gtktextiterprivate.h"
67 #include "gtkdebug.h"
68 #include "gtktextmarkprivate.h"
69 #include "gtktextsegment.h"
70 #include "gtkpango.h"
71 
72 /*
73  * Types
74  */
75 
76 
77 /*
78  * The structure below is used to pass information between
79  * _gtk_text_btree_get_tags and inc_count:
80  */
81 
82 typedef struct TagInfo {
83   int numTags;                  /* Number of tags for which there
84                                  * is currently information in
85                                  * tags and counts. */
86   int arraySize;                        /* Number of entries allocated for
87                                          * tags and counts. */
88   GtkTextTag **tags;           /* Array of tags seen so far.
89                                 * Malloc-ed. */
90   int *counts;                  /* Toggle count (so far) for each
91                                  * entry in tags.  Malloc-ed. */
92 } TagInfo;
93 
94 
95 /*
96  * This is used to store per-view width/height info at the tree nodes.
97  */
98 
99 typedef struct _NodeData NodeData;
100 
101 struct _NodeData {
102   gpointer view_id;
103   NodeData *next;
104 
105   /* Height and width of this node */
106   gint height;
107   signed int width : 24;
108 
109   /* boolean indicating whether the lines below this node are in need of validation.
110    * However, width/height should always represent the current total width and
111    * max height for lines below this node; the valid flag indicates whether the
112    * width/height on the lines needs recomputing, not whether the totals
113    * need recomputing.
114    */
115   guint valid : 8;		/* Actually a boolean */
116 };
117 
118 
119 /*
120  * The data structure below keeps summary information about one tag as part
121  * of the tag information in a node.
122  */
123 
124 typedef struct Summary {
125   GtkTextTagInfo *info;                     /* Handle for tag. */
126   int toggle_count;                     /* Number of transitions into or
127                                          * out of this tag that occur in
128                                          * the subtree rooted at this node. */
129   struct Summary *next;         /* Next in list of all tags for same
130                                  * node, or NULL if at end of list. */
131 } Summary;
132 
133 /*
134  * The data structure below defines a node in the B-tree.
135  */
136 
137 struct _GtkTextBTreeNode {
138   GtkTextBTreeNode *parent;         /* Pointer to parent node, or NULL if
139                                      * this is the root. */
140   GtkTextBTreeNode *next;           /* Next in list of siblings with the
141                                      * same parent node, or NULL for end
142                                      * of list. */
143   Summary *summary;             /* First in malloc-ed list of info
144                                  * about tags in this subtree (NULL if
145                                  * no tag info in the subtree). */
146   int level;                            /* Level of this node in the B-tree.
147                                          * 0 refers to the bottom of the tree
148                                          * (children are lines, not nodes). */
149   int num_lines;                        /* Total number of lines (leaves) in
150                                          * the subtree rooted here. */
151   int num_chars;                        /* Number of chars below here */
152   int num_children;                     /* Number of children of this node. */
153   union {                               /* First in linked list of children. */
154     struct _GtkTextBTreeNode *node;         /* Used if level > 0. */
155     GtkTextLine *line;         /* Used if level == 0. */
156   } children;
157 
158   NodeData *node_data;
159 };
160 
161 
162 /*
163  * Used to store the list of views in our btree
164  */
165 
166 typedef struct _BTreeView BTreeView;
167 
168 struct _BTreeView {
169   gpointer view_id;
170   GtkTextLayout *layout;
171   BTreeView *next;
172   BTreeView *prev;
173 };
174 
175 /*
176  * And the tree itself
177  */
178 
179 struct _GtkTextBTree {
180   GtkTextBTreeNode *root_node;          /* Pointer to root of B-tree. */
181   GtkTextTagTable *table;
182   GHashTable *mark_table;
183   guint refcount;
184   GtkTextMark *insert_mark;
185   GtkTextMark *selection_bound_mark;
186   GtkTextBuffer *buffer;
187   BTreeView *views;
188   GSList *tag_infos;
189   gulong tag_changed_handler;
190 
191   /* Incremented when a segment with a byte size > 0
192    * is added to or removed from the tree (i.e. the
193    * length of a line may have changed, and lines may
194    * have been added or removed). This invalidates
195    * all outstanding iterators.
196    */
197   guint chars_changed_stamp;
198   /* Incremented when any segments are added or deleted;
199    * this makes outstanding iterators recalculate their
200    * pointed-to segment and segment offset.
201    */
202   guint segments_changed_stamp;
203 
204   /* Cache the last line in the buffer */
205   GtkTextLine *last_line;
206   guint last_line_stamp;
207 
208   /* Cache the next-to-last line in the buffer,
209    * containing the end iterator
210    */
211   GtkTextLine *end_iter_line;
212   GtkTextLineSegment *end_iter_segment;
213   int end_iter_segment_byte_index;
214   int end_iter_segment_char_offset;
215   guint end_iter_line_stamp;
216   guint end_iter_segment_stamp;
217 
218   GHashTable *child_anchor_table;
219 };
220 
221 
222 /*
223  * Upper and lower bounds on how many children a node may have:
224  * rebalance when either of these limits is exceeded.  MAX_CHILDREN
225  * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
226  */
227 
228 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
229    shallow. It appears to be faster to locate a particular line number
230    if the tree is narrow and deep, since it is more finely sorted.  I
231    guess this may increase memory use though, and make it slower to
232    walk the tree in order, or locate a particular byte index (which
233    is done by walking the tree in order).
234 
235    There's basically a tradeoff here. However I'm thinking we want to
236    add pixels, byte counts, and char counts to the tree nodes,
237    at that point narrow and deep should speed up all operations,
238    not just the line number searches.
239 */
240 
241 #if 1
242 #define MAX_CHILDREN 12
243 #define MIN_CHILDREN 6
244 #else
245 #define MAX_CHILDREN 6
246 #define MIN_CHILDREN 3
247 #endif
248 
249 /*
250  * Prototypes
251  */
252 
253 static BTreeView        *gtk_text_btree_get_view                 (GtkTextBTree     *tree,
254                                                                   gpointer          view_id);
255 static void              gtk_text_btree_rebalance                (GtkTextBTree     *tree,
256                                                                   GtkTextBTreeNode *node);
257 static GtkTextLine     * get_last_line                           (GtkTextBTree     *tree);
258 static void              post_insert_fixup                       (GtkTextBTree     *tree,
259                                                                   GtkTextLine      *insert_line,
260                                                                   gint              char_count_delta,
261                                                                   gint              line_count_delta);
262 static void              gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
263                                                                   GtkTextTagInfo   *info,
264                                                                   gint              adjust);
265 static gboolean          gtk_text_btree_node_has_tag             (GtkTextBTreeNode *node,
266                                                                   GtkTextTag       *tag);
267 
268 static void             segments_changed                (GtkTextBTree     *tree);
269 static void             chars_changed                   (GtkTextBTree     *tree);
270 static void             summary_list_destroy            (Summary          *summary);
271 static GtkTextLine     *gtk_text_line_new               (void);
272 static void             gtk_text_line_destroy           (GtkTextBTree     *tree,
273                                                          GtkTextLine      *line);
274 static void             gtk_text_line_set_parent        (GtkTextLine      *line,
275                                                          GtkTextBTreeNode *node);
276 static void             gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
277                                                          gpointer          view_id);
278 
279 
280 static NodeData         *node_data_new          (gpointer  view_id);
281 static void              node_data_destroy      (NodeData *nd);
282 static void              node_data_list_destroy (NodeData *nd);
283 static NodeData         *node_data_find         (NodeData *nd,
284                                                  gpointer  view_id);
285 
286 static GtkTextBTreeNode     *gtk_text_btree_node_new                  (void);
287 #if 0
288 static void                  gtk_text_btree_node_invalidate_downward  (GtkTextBTreeNode *node);
289 #endif
290 static void                  gtk_text_btree_node_invalidate_upward    (GtkTextBTreeNode *node,
291                                                                        gpointer          view_id);
292 static NodeData *            gtk_text_btree_node_check_valid          (GtkTextBTreeNode *node,
293                                                                        gpointer          view_id);
294 static NodeData *            gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
295                                                                        gpointer          view_id);
296 static void                  gtk_text_btree_node_check_valid_upward   (GtkTextBTreeNode *node,
297                                                                        gpointer          view_id);
298 
299 static void                  gtk_text_btree_node_remove_view         (BTreeView        *view,
300                                                                       GtkTextBTreeNode *node,
301                                                                       gpointer          view_id);
302 static void                  gtk_text_btree_node_destroy             (GtkTextBTree     *tree,
303                                                                       GtkTextBTreeNode *node);
304 static void                  gtk_text_btree_node_free_empty          (GtkTextBTree *tree,
305                                                                       GtkTextBTreeNode *node);
306 static NodeData         *    gtk_text_btree_node_ensure_data         (GtkTextBTreeNode *node,
307                                                                       gpointer          view_id);
308 static void                  gtk_text_btree_node_remove_data         (GtkTextBTreeNode *node,
309                                                                       gpointer          view_id);
310 static void                  gtk_text_btree_node_get_size            (GtkTextBTreeNode *node,
311                                                                       gpointer          view_id,
312                                                                       gint             *width,
313                                                                       gint             *height);
314 static GtkTextBTreeNode *    gtk_text_btree_node_common_parent       (GtkTextBTreeNode *node1,
315                                                                       GtkTextBTreeNode *node2);
316 static void get_tree_bounds       (GtkTextBTree     *tree,
317                                    GtkTextIter      *start,
318                                    GtkTextIter      *end);
319 static void tag_changed_cb        (GtkTextTagTable  *table,
320                                    GtkTextTag       *tag,
321                                    gboolean          size_changed,
322                                    GtkTextBTree     *tree);
323 static void cleanup_line          (GtkTextLine      *line);
324 static void recompute_node_counts (GtkTextBTree     *tree,
325                                    GtkTextBTreeNode *node);
326 static void inc_count             (GtkTextTag       *tag,
327                                    int               inc,
328                                    TagInfo          *tagInfoPtr);
329 
330 static void summary_destroy       (Summary          *summary);
331 
332 static void gtk_text_btree_link_segment   (GtkTextLineSegment *seg,
333                                            const GtkTextIter  *iter);
334 static void gtk_text_btree_unlink_segment (GtkTextBTree       *tree,
335                                            GtkTextLineSegment *seg,
336                                            GtkTextLine        *line);
337 
338 
339 static GtkTextTagInfo *gtk_text_btree_get_tag_info          (GtkTextBTree   *tree,
340                                                              GtkTextTag     *tag);
341 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree   *tree,
342                                                              GtkTextTag     *tag);
343 static void            gtk_text_btree_remove_tag_info       (GtkTextBTree   *tree,
344                                                              GtkTextTag     *tag);
345 
346 static void redisplay_region (GtkTextBTree      *tree,
347                               const GtkTextIter *start,
348                               const GtkTextIter *end,
349                               gboolean           cursors_only);
350 
351 /* Inline thingies */
352 
353 static inline void
segments_changed(GtkTextBTree * tree)354 segments_changed (GtkTextBTree *tree)
355 {
356   tree->segments_changed_stamp += 1;
357 }
358 
359 static inline void
chars_changed(GtkTextBTree * tree)360 chars_changed (GtkTextBTree *tree)
361 {
362   tree->chars_changed_stamp += 1;
363 }
364 
365 /*
366  * BTree operations
367  */
368 
369 GtkTextBTree*
_gtk_text_btree_new(GtkTextTagTable * table,GtkTextBuffer * buffer)370 _gtk_text_btree_new (GtkTextTagTable *table,
371                      GtkTextBuffer *buffer)
372 {
373   GtkTextBTree *tree;
374   GtkTextBTreeNode *root_node;
375   GtkTextLine *line, *line2;
376 
377   g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
378   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
379 
380   /*
381    * The tree will initially have two empty lines.  The second line
382    * isn't actually part of the tree's contents, but its presence
383    * makes several operations easier.  The tree will have one GtkTextBTreeNode,
384    * which is also the root of the tree.
385    */
386 
387   /* Create the root node. */
388 
389   root_node = gtk_text_btree_node_new ();
390 
391   line = gtk_text_line_new ();
392   line2 = gtk_text_line_new ();
393 
394   root_node->parent = NULL;
395   root_node->next = NULL;
396   root_node->summary = NULL;
397   root_node->level = 0;
398   root_node->children.line = line;
399   root_node->num_children = 2;
400   root_node->num_lines = 2;
401   root_node->num_chars = 2;
402 
403   line->parent = root_node;
404   line->next = line2;
405 
406   line->segments = _gtk_char_segment_new ("\n", 1);
407 
408   line2->parent = root_node;
409   line2->next = NULL;
410   line2->segments = _gtk_char_segment_new ("\n", 1);
411 
412   /* Create the tree itself */
413 
414   tree = g_slice_new0 (GtkTextBTree);
415   tree->root_node = root_node;
416   tree->table = table;
417   tree->views = NULL;
418 
419   /* Set these to values that are unlikely to be found
420    * in random memory garbage, and also avoid
421    * duplicates between tree instances.
422    */
423   tree->chars_changed_stamp = g_random_int ();
424   tree->segments_changed_stamp = g_random_int ();
425 
426   tree->last_line_stamp = tree->chars_changed_stamp - 1;
427   tree->last_line = NULL;
428 
429   tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
430   tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
431   tree->end_iter_line = NULL;
432   tree->end_iter_segment_byte_index = 0;
433   tree->end_iter_segment_char_offset = 0;
434 
435   g_object_ref (tree->table);
436 
437   tree->tag_changed_handler = g_signal_connect (tree->table,
438 						"tag-changed",
439 						G_CALLBACK (tag_changed_cb),
440 						tree);
441 
442   tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
443   tree->child_anchor_table = NULL;
444 
445   /* We don't ref the buffer, since the buffer owns us;
446    * we'd have some circularity issues. The buffer always
447    * lasts longer than the BTree
448    */
449   tree->buffer = buffer;
450 
451   {
452     GtkTextIter start;
453     GtkTextLineSegment *seg;
454 
455     _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
456 
457 
458     tree->insert_mark = _gtk_text_btree_set_mark (tree,
459                                                  NULL,
460                                                  "insert",
461                                                  FALSE,
462                                                  &start,
463                                                  FALSE);
464 
465     seg = tree->insert_mark->segment;
466 
467     seg->body.mark.not_deleteable = TRUE;
468     seg->body.mark.visible = TRUE;
469 
470     tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
471                                                           NULL,
472                                                           "selection_bound",
473                                                           FALSE,
474                                                           &start,
475                                                           FALSE);
476 
477     seg = tree->selection_bound_mark->segment;
478 
479     seg->body.mark.not_deleteable = TRUE;
480 
481     g_object_ref (tree->insert_mark);
482     g_object_ref (tree->selection_bound_mark);
483   }
484 
485   tree->refcount = 1;
486 
487   return tree;
488 }
489 
490 void
_gtk_text_btree_ref(GtkTextBTree * tree)491 _gtk_text_btree_ref (GtkTextBTree *tree)
492 {
493   g_return_if_fail (tree != NULL);
494   g_return_if_fail (tree->refcount > 0);
495 
496   tree->refcount += 1;
497 }
498 
499 void
_gtk_text_btree_unref(GtkTextBTree * tree)500 _gtk_text_btree_unref (GtkTextBTree *tree)
501 {
502   g_return_if_fail (tree != NULL);
503   g_return_if_fail (tree->refcount > 0);
504 
505   tree->refcount -= 1;
506 
507   if (tree->refcount == 0)
508     {
509       g_signal_handler_disconnect (tree->table,
510                                    tree->tag_changed_handler);
511 
512       g_object_unref (tree->table);
513       tree->table = NULL;
514 
515       gtk_text_btree_node_destroy (tree, tree->root_node);
516       tree->root_node = NULL;
517 
518       g_assert (g_hash_table_size (tree->mark_table) == 0);
519       g_hash_table_destroy (tree->mark_table);
520       tree->mark_table = NULL;
521       if (tree->child_anchor_table != NULL)
522 	{
523 	  g_hash_table_destroy (tree->child_anchor_table);
524 	  tree->child_anchor_table = NULL;
525 	}
526 
527       g_object_unref (tree->insert_mark);
528       tree->insert_mark = NULL;
529       g_object_unref (tree->selection_bound_mark);
530       tree->selection_bound_mark = NULL;
531 
532       g_slice_free (GtkTextBTree, tree);
533     }
534 }
535 
536 GtkTextBuffer*
_gtk_text_btree_get_buffer(GtkTextBTree * tree)537 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
538 {
539   return tree->buffer;
540 }
541 
542 guint
_gtk_text_btree_get_chars_changed_stamp(GtkTextBTree * tree)543 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
544 {
545   return tree->chars_changed_stamp;
546 }
547 
548 guint
_gtk_text_btree_get_segments_changed_stamp(GtkTextBTree * tree)549 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
550 {
551   return tree->segments_changed_stamp;
552 }
553 
554 void
_gtk_text_btree_segments_changed(GtkTextBTree * tree)555 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
556 {
557   g_return_if_fail (tree != NULL);
558   segments_changed (tree);
559 }
560 
561 /*
562  * Indexable segment mutation
563  */
564 
565 /*
566  *  The following function is responsible for resolving the bidi direction
567  *  for the lines between start and end. But it also calculates any
568  *  dependent bidi direction for surrounding lines that change as a result
569  *  of the bidi direction decisions within the range. The function is
570  *  trying to do as little propagation as is needed.
571  */
572 static void
gtk_text_btree_resolve_bidi(GtkTextIter * start,GtkTextIter * end)573 gtk_text_btree_resolve_bidi (GtkTextIter *start,
574 			     GtkTextIter *end)
575 {
576   GtkTextBTree *tree = _gtk_text_iter_get_btree (start);
577   GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
578   PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
579 
580   /* Resolve the strong bidi direction for all lines between
581    * start and end.
582   */
583   start_line = _gtk_text_iter_get_text_line (start);
584   start_line_prev = _gtk_text_line_previous (start_line);
585   end_line = _gtk_text_iter_get_text_line (end);
586   end_line_next = _gtk_text_line_next (end_line);
587 
588   line = start_line;
589   while (line && line != end_line_next)
590     {
591       /* Loop through the segments and search for a strong character
592        */
593       GtkTextLineSegment *seg = line->segments;
594       line->dir_strong = PANGO_DIRECTION_NEUTRAL;
595 
596       while (seg)
597         {
598           if (seg->type == &gtk_text_char_type && seg->byte_count > 0)
599             {
600 	      PangoDirection pango_dir;
601 
602               pango_dir = _gtk_pango_find_base_dir (seg->body.chars, seg->byte_count);
603 
604               if (pango_dir != PANGO_DIRECTION_NEUTRAL)
605                 {
606                   line->dir_strong = pango_dir;
607                   break;
608                 }
609             }
610           seg = seg->next;
611         }
612 
613       line = _gtk_text_line_next (line);
614     }
615 
616   /* Sweep forward */
617 
618   /* The variable dir_above_propagated contains the forward propagated
619    * direction before start. It is neutral if start is in the beginning
620    * of the buffer.
621    */
622   dir_above_propagated = PANGO_DIRECTION_NEUTRAL;
623   if (start_line_prev)
624     dir_above_propagated = start_line_prev->dir_propagated_forward;
625 
626   /* Loop forward and propagate the direction of each paragraph
627    * to all neutral lines.
628    */
629   line = start_line;
630   last_strong = dir_above_propagated;
631   while (line != end_line_next)
632     {
633       if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
634         last_strong = line->dir_strong;
635 
636       line->dir_propagated_forward = last_strong;
637 
638       line = _gtk_text_line_next (line);
639     }
640 
641   /* Continue propagating as long as the previous resolved forward
642    * is different from last_strong.
643    */
644   {
645     GtkTextIter end_propagate;
646 
647     while (line &&
648 	   line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
649 	   line->dir_propagated_forward != last_strong)
650       {
651         GtkTextLine *prev = line;
652         line->dir_propagated_forward = last_strong;
653 
654         line = _gtk_text_line_next(line);
655         if (!line)
656           {
657             line = prev;
658             break;
659           }
660       }
661 
662     /* The last line to invalidate is the last line before the
663      * line with the strong character. Or in case of the end of the
664      * buffer, the last line of the buffer. (There seems to be an
665      * extra "virtual" last line in the buffer that must not be used
666      * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the
667      * _gtk_text_line_previous is ok in that case as well.)
668      */
669     line = _gtk_text_line_previous (line);
670     _gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0);
671     _gtk_text_btree_invalidate_region (tree, end, &end_propagate, FALSE);
672   }
673 
674   /* Sweep backward */
675 
676   /* The variable dir_below_propagated contains the backward propagated
677    * direction after end. It is neutral if end is at the end of
678    * the buffer.
679   */
680   dir_below_propagated = PANGO_DIRECTION_NEUTRAL;
681   if (end_line_next)
682     dir_below_propagated = end_line_next->dir_propagated_back;
683 
684   /* Loop backward and propagate the direction of each paragraph
685    * to all neutral lines.
686    */
687   line = end_line;
688   last_strong = dir_below_propagated;
689   while (line != start_line_prev)
690     {
691       if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
692         last_strong = line->dir_strong;
693 
694       line->dir_propagated_back = last_strong;
695 
696       line = _gtk_text_line_previous (line);
697     }
698 
699   /* Continue propagating as long as the resolved backward dir
700    * is different from last_strong.
701    */
702   {
703     GtkTextIter start_propagate;
704 
705     while (line &&
706 	   line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
707 	   line->dir_propagated_back != last_strong)
708       {
709         GtkTextLine *prev = line;
710         line->dir_propagated_back = last_strong;
711 
712         line = _gtk_text_line_previous (line);
713         if (!line)
714           {
715             line = prev;
716             break;
717           }
718       }
719 
720     /* We only need to invalidate for backwards propagation if the
721      * line we ended up on didn't get a direction from forwards
722      * propagation.
723      */
724     if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL)
725       {
726         _gtk_text_btree_get_iter_at_line (tree, &start_propagate, line, 0);
727         _gtk_text_btree_invalidate_region (tree, &start_propagate, start, FALSE);
728       }
729   }
730 }
731 
732 void
_gtk_text_btree_delete(GtkTextIter * start,GtkTextIter * end)733 _gtk_text_btree_delete (GtkTextIter *start,
734                         GtkTextIter *end)
735 {
736   GtkTextLineSegment *prev_seg;             /* The segment just before the start
737                                              * of the deletion range. */
738   GtkTextLineSegment *last_seg;             /* The segment just after the end
739                                              * of the deletion range. */
740   GtkTextLineSegment *seg, *next, *next2;
741   GtkTextLine *curline;
742   GtkTextBTreeNode *curnode, *node;
743   GtkTextBTree *tree;
744   GtkTextLine *start_line;
745   GtkTextLine *end_line;
746   GtkTextLine *line;
747   GtkTextLine *deleted_lines = NULL;        /* List of lines we've deleted */
748   gint start_byte_offset;
749 
750   g_return_if_fail (start != NULL);
751   g_return_if_fail (end != NULL);
752   g_return_if_fail (_gtk_text_iter_get_btree (start) ==
753                     _gtk_text_iter_get_btree (end));
754 
755   gtk_text_iter_order (start, end);
756 
757   tree = _gtk_text_iter_get_btree (start);
758 
759 #ifdef G_ENABLE_DEBUG
760   if (GTK_DEBUG_CHECK (TEXT))
761     _gtk_text_btree_check (tree);
762 #endif
763 
764   /* Broadcast the need for redisplay before we break the iterators */
765   DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
766   _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
767 
768   /* Save the byte offset so we can reset the iterators */
769   start_byte_offset = gtk_text_iter_get_line_index (start);
770 
771   start_line = _gtk_text_iter_get_text_line (start);
772   end_line = _gtk_text_iter_get_text_line (end);
773 
774   /*
775    * Split the start and end segments, so we have a place
776    * to insert our new text.
777    *
778    * Tricky point:  split at end first;  otherwise the split
779    * at end may invalidate seg and/or prev_seg. This allows
780    * us to avoid invalidating segments for start.
781    */
782 
783   last_seg = gtk_text_line_segment_split (end);
784   if (last_seg != NULL)
785     last_seg = last_seg->next;
786   else
787     last_seg = end_line->segments;
788 
789   prev_seg = gtk_text_line_segment_split (start);
790   if (prev_seg != NULL)
791     {
792       seg = prev_seg->next;
793       prev_seg->next = last_seg;
794     }
795   else
796     {
797       seg = start_line->segments;
798       start_line->segments = last_seg;
799     }
800 
801   /* notify iterators that their segments need recomputation,
802      just for robustness. */
803   segments_changed (tree);
804 
805   /*
806    * Delete all of the segments between prev_seg and last_seg.
807    */
808 
809   curline = start_line;
810   curnode = curline->parent;
811   while (seg != last_seg)
812     {
813       gint char_count = 0;
814 
815       if (seg == NULL)
816         {
817           GtkTextLine *nextline;
818 
819           /*
820            * We just ran off the end of a line.  First find the
821            * next line, then go back to the old line and delete it
822            * (unless it's the starting line for the range).
823            */
824 
825           nextline = _gtk_text_line_next (curline);
826           if (curline != start_line)
827             {
828               if (curnode == start_line->parent)
829                 start_line->next = curline->next;
830               else
831                 curnode->children.line = curline->next;
832 
833               for (node = curnode; node != NULL;
834                    node = node->parent)
835                 {
836                   /* Don't update node->num_chars, because
837                    * that was done when we deleted the segments.
838                    */
839                   node->num_lines -= 1;
840                 }
841 
842               curnode->num_children -= 1;
843               curline->next = deleted_lines;
844               deleted_lines = curline;
845             }
846 
847           curline = nextline;
848           seg = curline->segments;
849 
850           /*
851            * If the GtkTextBTreeNode is empty then delete it and its parents,
852            * recursively upwards until a non-empty GtkTextBTreeNode is found.
853            */
854 
855           while (curnode->num_children == 0)
856             {
857               GtkTextBTreeNode *parent;
858 
859               parent = curnode->parent;
860               if (parent->children.node == curnode)
861                 {
862                   parent->children.node = curnode->next;
863                 }
864               else
865                 {
866                   GtkTextBTreeNode *prevnode = parent->children.node;
867                   while (prevnode->next != curnode)
868                     {
869                       prevnode = prevnode->next;
870                     }
871                   prevnode->next = curnode->next;
872                 }
873               parent->num_children--;
874               gtk_text_btree_node_free_empty (tree, curnode);
875               curnode = parent;
876             }
877           curnode = curline->parent;
878           continue;
879         }
880 
881       next = seg->next;
882       char_count = seg->char_count;
883 
884       if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
885         {
886           /*
887            * This segment refuses to die.  Move it to prev_seg and
888            * advance prev_seg if the segment has left gravity.
889            */
890 
891           if (prev_seg == NULL)
892             {
893               seg->next = start_line->segments;
894               start_line->segments = seg;
895             }
896           else if (prev_seg->next &&
897 		   prev_seg->next != last_seg &&
898 		   seg->type == &gtk_text_toggle_off_type &&
899 		   prev_seg->next->type == &gtk_text_toggle_on_type &&
900 		   seg->body.toggle.info == prev_seg->next->body.toggle.info)
901 	    {
902 	      /* Try to match an off toggle with the matching on toggle
903 	       * if it immediately follows. This is a common case, and
904 	       * handling it here prevents quadratic blowup in
905 	       * cleanup_line() below. See bug 317125.
906 	       */
907 	      next2 = prev_seg->next->next;
908               _gtk_toggle_segment_free (prev_seg->next);
909 	      prev_seg->next = next2;
910               _gtk_toggle_segment_free (seg);
911 	      seg = NULL;
912 	    }
913 	  else
914 	    {
915               seg->next = prev_seg->next;
916               prev_seg->next = seg;
917             }
918 
919           if (seg && seg->type->leftGravity)
920             {
921               prev_seg = seg;
922             }
923         }
924       else
925         {
926           /* Segment is gone. Decrement the char count of the node and
927              all its parents. */
928           for (node = curnode; node != NULL;
929                node = node->parent)
930             {
931               node->num_chars -= char_count;
932             }
933         }
934 
935       seg = next;
936     }
937 
938   /*
939    * If the beginning and end of the deletion range are in different
940    * lines, join the two lines together and discard the ending line.
941    */
942 
943   if (start_line != end_line)
944     {
945       BTreeView *view;
946       GtkTextBTreeNode *ancestor_node;
947       GtkTextLine *prevline;
948       int chars_moved;
949 
950       /* last_seg was appended to start_line up at the top of this function */
951       chars_moved = 0;
952       for (seg = last_seg; seg != NULL;
953            seg = seg->next)
954         {
955           chars_moved += seg->char_count;
956           if (seg->type->lineChangeFunc != NULL)
957             {
958               (*seg->type->lineChangeFunc)(seg, end_line);
959             }
960         }
961 
962       for (node = start_line->parent; node != NULL;
963            node = node->parent)
964         {
965           node->num_chars += chars_moved;
966         }
967 
968       curnode = end_line->parent;
969       for (node = curnode; node != NULL;
970            node = node->parent)
971         {
972           node->num_chars -= chars_moved;
973           node->num_lines--;
974         }
975       curnode->num_children--;
976       prevline = curnode->children.line;
977       if (prevline == end_line)
978         {
979           curnode->children.line = end_line->next;
980         }
981       else
982         {
983           while (prevline->next != end_line)
984             {
985               prevline = prevline->next;
986             }
987           prevline->next = end_line->next;
988         }
989       end_line->next = deleted_lines;
990       deleted_lines = end_line;
991 
992       /* We now fix up the per-view aggregates. We add all the height and
993        * width for the deleted lines to the start line, so that when revalidation
994        * occurs, the correct change in size is seen.
995        */
996       ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
997       view = tree->views;
998       while (view)
999         {
1000           GtkTextLineData *ld;
1001 
1002           gint deleted_width = 0;
1003           gint deleted_height = 0;
1004 
1005           line = deleted_lines;
1006           while (line)
1007             {
1008               GtkTextLine *next_line = line->next;
1009               ld = _gtk_text_line_get_data (line, view->view_id);
1010 
1011               if (ld)
1012                 {
1013                   deleted_width = MAX (deleted_width, ld->width);
1014                   deleted_height += ld->height;
1015                 }
1016 
1017               line = next_line;
1018             }
1019 
1020           if (deleted_width > 0 || deleted_height > 0)
1021             {
1022               ld = _gtk_text_line_get_data (start_line, view->view_id);
1023 
1024               if (ld == NULL)
1025                 {
1026                   /* This means that start_line has never been validated.
1027                    * We don't really want to do the validation here but
1028                    * we do need to store our temporary sizes. So we
1029                    * create the line data and assume a line w/h of 0.
1030                    */
1031                   ld = _gtk_text_line_data_new (view->layout, start_line);
1032                   _gtk_text_line_add_data (start_line, ld);
1033                   ld->width = 0;
1034                   ld->height = 0;
1035                   ld->valid = FALSE;
1036                 }
1037 
1038               ld->width = MAX (deleted_width, ld->width);
1039               ld->height += deleted_height;
1040               ld->valid = FALSE;
1041             }
1042 
1043           gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
1044           if (ancestor_node->parent)
1045             gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
1046 
1047           view = view->next;
1048         }
1049 
1050       line = deleted_lines;
1051       while (line)
1052         {
1053           GtkTextLine *next_line = line->next;
1054 
1055           gtk_text_line_destroy (tree, line);
1056 
1057           line = next_line;
1058         }
1059 
1060       /* avoid dangling pointer */
1061       deleted_lines = NULL;
1062 
1063       gtk_text_btree_rebalance (tree, curnode);
1064     }
1065 
1066   /*
1067    * Cleanup the segments in the new line.
1068    */
1069 
1070   cleanup_line (start_line);
1071 
1072   /*
1073    * Lastly, rebalance the first GtkTextBTreeNode of the range.
1074    */
1075 
1076   gtk_text_btree_rebalance (tree, start_line->parent);
1077 
1078   /* Notify outstanding iterators that they
1079      are now hosed */
1080   chars_changed (tree);
1081   segments_changed (tree);
1082 
1083 #ifdef G_ENABLE_DEBUG
1084   if (GTK_DEBUG_CHECK (TEXT))
1085     _gtk_text_btree_check (tree);
1086 #endif
1087 
1088   /* Re-initialize our iterators */
1089   _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
1090   *end = *start;
1091 
1092   gtk_text_btree_resolve_bidi (start, end);
1093 }
1094 
1095 void
_gtk_text_btree_insert(GtkTextIter * iter,const gchar * text,gint len)1096 _gtk_text_btree_insert (GtkTextIter *iter,
1097                         const gchar *text,
1098                         gint         len)
1099 {
1100   GtkTextLineSegment *prev_seg;     /* The segment just before the first
1101                                      * new segment (NULL means new segment
1102                                      * is at beginning of line). */
1103   GtkTextLineSegment *cur_seg;              /* Current segment;  new characters
1104                                              * are inserted just after this one.
1105                                              * NULL means insert at beginning of
1106                                              * line. */
1107   GtkTextLine *line;           /* Current line (new segments are
1108                                 * added to this line). */
1109   GtkTextLineSegment *seg;
1110   GtkTextLine *newline;
1111   int chunk_len;                        /* # characters in current chunk. */
1112   gint sol;                           /* start of line */
1113   gint eol;                           /* Pointer to character just after last
1114                                        * one in current chunk.
1115                                        */
1116   gint delim;                          /* index of paragraph delimiter */
1117   int line_count_delta;                /* Counts change to total number of
1118                                         * lines in file.
1119                                         */
1120 
1121   int char_count_delta;                /* change to number of chars */
1122   GtkTextBTree *tree;
1123   gint start_byte_index;
1124   GtkTextLine *start_line;
1125 
1126   g_return_if_fail (text != NULL);
1127   g_return_if_fail (iter != NULL);
1128 
1129   if (len < 0)
1130     len = strlen (text);
1131 
1132   /* extract iterator info */
1133   tree = _gtk_text_iter_get_btree (iter);
1134   line = _gtk_text_iter_get_text_line (iter);
1135 
1136   start_line = line;
1137   start_byte_index = gtk_text_iter_get_line_index (iter);
1138 
1139   /* Get our insertion segment split. Note this assumes line allows
1140    * char insertions, which isn't true of the "last" line. But iter
1141    * should not be on that line, as we assert here.
1142    */
1143   g_assert (!_gtk_text_line_is_last (line, tree));
1144   prev_seg = gtk_text_line_segment_split (iter);
1145   cur_seg = prev_seg;
1146 
1147   /* Invalidate all iterators */
1148   chars_changed (tree);
1149   segments_changed (tree);
1150 
1151   /*
1152    * Chop the text up into lines and create a new segment for
1153    * each line, plus a new line for the leftovers from the
1154    * previous line.
1155    */
1156 
1157   eol = 0;
1158   sol = 0;
1159   line_count_delta = 0;
1160   char_count_delta = 0;
1161   while (eol < len)
1162     {
1163       sol = eol;
1164 
1165       pango_find_paragraph_boundary (text + sol,
1166                                      len - sol,
1167                                      &delim,
1168                                      &eol);
1169 
1170       /* make these relative to the start of the text */
1171       delim += sol;
1172       eol += sol;
1173 
1174       g_assert (eol >= sol);
1175       g_assert (delim >= sol);
1176       g_assert (eol >= delim);
1177       g_assert (sol >= 0);
1178       g_assert (eol <= len);
1179 
1180       chunk_len = eol - sol;
1181 
1182       g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1183       seg = _gtk_char_segment_new (&text[sol], chunk_len);
1184 
1185       char_count_delta += seg->char_count;
1186 
1187       if (cur_seg == NULL)
1188         {
1189           seg->next = line->segments;
1190           line->segments = seg;
1191         }
1192       else
1193         {
1194           seg->next = cur_seg->next;
1195           cur_seg->next = seg;
1196         }
1197 
1198       if (delim == eol)
1199         {
1200           /* chunk didn't end with a paragraph separator */
1201           g_assert (eol == len);
1202           break;
1203         }
1204 
1205       /*
1206        * The chunk ended with a newline, so create a new GtkTextLine
1207        * and move the remainder of the old line to it.
1208        */
1209 
1210       newline = gtk_text_line_new ();
1211       gtk_text_line_set_parent (newline, line->parent);
1212       newline->next = line->next;
1213       line->next = newline;
1214       newline->segments = seg->next;
1215       seg->next = NULL;
1216       line = newline;
1217       cur_seg = NULL;
1218       line_count_delta++;
1219     }
1220 
1221   /*
1222    * Cleanup the starting line for the insertion, plus the ending
1223    * line if it's different.
1224    */
1225 
1226   cleanup_line (start_line);
1227   if (line != start_line)
1228     {
1229       cleanup_line (line);
1230     }
1231 
1232   post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1233 
1234   /* Invalidate our region, and reset the iterator the user
1235      passed in to point to the end of the inserted text. */
1236   {
1237     GtkTextIter start;
1238     GtkTextIter end;
1239 
1240 
1241     _gtk_text_btree_get_iter_at_line (tree,
1242                                       &start,
1243                                       start_line,
1244                                       start_byte_index);
1245     end = start;
1246 
1247     /* We could almost certainly be more efficient here
1248        by saving the information from the insertion loop
1249        above. FIXME */
1250     gtk_text_iter_forward_chars (&end, char_count_delta);
1251 
1252     DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1253     _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE);
1254 
1255 
1256     /* Convenience for the user */
1257     *iter = end;
1258 
1259     gtk_text_btree_resolve_bidi (&start, &end);
1260   }
1261 }
1262 
1263 static void
insert_pixbuf_or_widget_segment(GtkTextIter * iter,GtkTextLineSegment * seg)1264 insert_pixbuf_or_widget_segment (GtkTextIter        *iter,
1265                                  GtkTextLineSegment *seg)
1266 
1267 {
1268   GtkTextIter start;
1269   GtkTextLineSegment *prevPtr;
1270   GtkTextLine *line;
1271   GtkTextBTree *tree;
1272   gint start_byte_offset;
1273 
1274   line = _gtk_text_iter_get_text_line (iter);
1275   tree = _gtk_text_iter_get_btree (iter);
1276   start_byte_offset = gtk_text_iter_get_line_index (iter);
1277 
1278   prevPtr = gtk_text_line_segment_split (iter);
1279   if (prevPtr == NULL)
1280     {
1281       seg->next = line->segments;
1282       line->segments = seg;
1283     }
1284   else
1285     {
1286       seg->next = prevPtr->next;
1287       prevPtr->next = seg;
1288     }
1289 
1290   post_insert_fixup (tree, line, 0, seg->char_count);
1291 
1292   chars_changed (tree);
1293   segments_changed (tree);
1294 
1295   /* reset *iter for the user, and invalidate tree nodes */
1296 
1297   _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1298 
1299   *iter = start;
1300   gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1301 
1302   DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1303   _gtk_text_btree_invalidate_region (tree, &start, iter, FALSE);
1304 }
1305 
1306 void
_gtk_text_btree_insert_pixbuf(GtkTextIter * iter,GdkPixbuf * pixbuf)1307 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1308                               GdkPixbuf   *pixbuf)
1309 {
1310   GtkTextLineSegment *seg;
1311 
1312   seg = _gtk_pixbuf_segment_new (pixbuf);
1313 
1314   insert_pixbuf_or_widget_segment (iter, seg);
1315 }
1316 
1317 void
_gtk_text_btree_insert_child_anchor(GtkTextIter * iter,GtkTextChildAnchor * anchor)1318 _gtk_text_btree_insert_child_anchor (GtkTextIter        *iter,
1319                                      GtkTextChildAnchor *anchor)
1320 {
1321   GtkTextLineSegment *seg;
1322   GtkTextBTree *tree;
1323 
1324   if (anchor->segment != NULL)
1325     {
1326       g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1327       return;
1328     }
1329 
1330   seg = _gtk_widget_segment_new (anchor);
1331 
1332   tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1333   seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1334 
1335   insert_pixbuf_or_widget_segment (iter, seg);
1336 
1337   if (tree->child_anchor_table == NULL)
1338     tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1339 
1340   g_hash_table_insert (tree->child_anchor_table,
1341                        seg->body.child.obj,
1342                        seg->body.child.obj);
1343 }
1344 
1345 void
_gtk_text_btree_unregister_child_anchor(GtkTextChildAnchor * anchor)1346 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1347 {
1348   GtkTextLineSegment *seg;
1349 
1350   seg = anchor->segment;
1351 
1352   g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1353                        anchor);
1354 }
1355 
1356 /*
1357  * View stuff
1358  */
1359 
1360 static GtkTextLine*
find_line_by_y(GtkTextBTree * tree,BTreeView * view,GtkTextBTreeNode * node,gint y,gint * line_top,GtkTextLine * last_line)1361 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1362                 GtkTextBTreeNode *node, gint y, gint *line_top,
1363                 GtkTextLine *last_line)
1364 {
1365   gint current_y = 0;
1366 
1367 #ifdef G_ENABLE_DEBUG
1368   if (GTK_DEBUG_CHECK (TEXT))
1369     _gtk_text_btree_check (tree);
1370 #endif
1371 
1372   if (node->level == 0)
1373     {
1374       GtkTextLine *line;
1375 
1376       line = node->children.line;
1377 
1378       while (line != NULL && line != last_line)
1379         {
1380           GtkTextLineData *ld;
1381 
1382           ld = _gtk_text_line_get_data (line, view->view_id);
1383 
1384           if (ld)
1385             {
1386               if (y < (current_y + ld->height))
1387                 return line;
1388 
1389               current_y += ld->height;
1390               *line_top += ld->height;
1391             }
1392 
1393           line = line->next;
1394         }
1395       return NULL;
1396     }
1397   else
1398     {
1399       GtkTextBTreeNode *child;
1400 
1401       child = node->children.node;
1402 
1403       while (child != NULL)
1404         {
1405           gint width;
1406           gint height;
1407 
1408           gtk_text_btree_node_get_size (child, view->view_id,
1409                                         &width, &height);
1410 
1411           if (y < (current_y + height))
1412             return find_line_by_y (tree, view, child,
1413                                    y - current_y, line_top,
1414                                    last_line);
1415 
1416           current_y += height;
1417           *line_top += height;
1418 
1419           child = child->next;
1420         }
1421 
1422       return NULL;
1423     }
1424 }
1425 
1426 GtkTextLine *
_gtk_text_btree_find_line_by_y(GtkTextBTree * tree,gpointer view_id,gint ypixel,gint * line_top_out)1427 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1428                                 gpointer      view_id,
1429                                 gint          ypixel,
1430                                 gint         *line_top_out)
1431 {
1432   GtkTextLine *line;
1433   BTreeView *view;
1434   GtkTextLine *last_line;
1435   gint line_top = 0;
1436 
1437   view = gtk_text_btree_get_view (tree, view_id);
1438   g_return_val_if_fail (view != NULL, NULL);
1439 
1440   last_line = get_last_line (tree);
1441 
1442   line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1443                          last_line);
1444 
1445   if (line_top_out)
1446     *line_top_out = line_top;
1447 
1448   return line;
1449 }
1450 
1451 static gint
find_line_top_in_line_list(GtkTextBTree * tree,BTreeView * view,GtkTextLine * line,GtkTextLine * target_line,gint y)1452 find_line_top_in_line_list (GtkTextBTree *tree,
1453                             BTreeView *view,
1454                             GtkTextLine *line,
1455                             GtkTextLine *target_line,
1456                             gint y)
1457 {
1458   while (line != NULL)
1459     {
1460       GtkTextLineData *ld;
1461 
1462       if (line == target_line)
1463         return y;
1464 
1465       ld = _gtk_text_line_get_data (line, view->view_id);
1466       if (ld)
1467         y += ld->height;
1468 
1469       line = line->next;
1470     }
1471 
1472   g_assert_not_reached (); /* If we get here, our
1473                               target line didn't exist
1474                               under its parent node */
1475   return 0;
1476 }
1477 
1478 gint
_gtk_text_btree_find_line_top(GtkTextBTree * tree,GtkTextLine * target_line,gpointer view_id)1479 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1480                               GtkTextLine *target_line,
1481                               gpointer view_id)
1482 {
1483   gint y = 0;
1484   BTreeView *view;
1485   GSList *nodes;
1486   GSList *iter;
1487   GtkTextBTreeNode *node;
1488 
1489   view = gtk_text_btree_get_view (tree, view_id);
1490 
1491   g_return_val_if_fail (view != NULL, 0);
1492 
1493   nodes = NULL;
1494   node = target_line->parent;
1495   while (node != NULL)
1496     {
1497       nodes = g_slist_prepend (nodes, node);
1498       node = node->parent;
1499     }
1500 
1501   iter = nodes;
1502   while (iter != NULL)
1503     {
1504       node = iter->data;
1505 
1506       if (node->level == 0)
1507         {
1508           g_slist_free (nodes);
1509           return find_line_top_in_line_list (tree, view,
1510                                              node->children.line,
1511                                              target_line, y);
1512         }
1513       else
1514         {
1515           GtkTextBTreeNode *child;
1516           GtkTextBTreeNode *target_node;
1517 
1518           g_assert (iter->next != NULL); /* not at level 0 */
1519           target_node = iter->next->data;
1520 
1521           child = node->children.node;
1522 
1523           while (child != NULL)
1524             {
1525               gint width;
1526               gint height;
1527 
1528               if (child == target_node)
1529                 break;
1530               else
1531                 {
1532                   gtk_text_btree_node_get_size (child, view->view_id,
1533                                                 &width, &height);
1534                   y += height;
1535                 }
1536               child = child->next;
1537             }
1538           g_assert (child != NULL); /* should have broken out before we
1539                                        ran out of nodes */
1540         }
1541 
1542       iter = iter->next;
1543     }
1544 
1545   g_assert_not_reached (); /* we return when we find the target line */
1546   return 0;
1547 }
1548 
1549 void
_gtk_text_btree_add_view(GtkTextBTree * tree,GtkTextLayout * layout)1550 _gtk_text_btree_add_view (GtkTextBTree *tree,
1551                          GtkTextLayout *layout)
1552 {
1553   BTreeView *view;
1554   GtkTextLine *last_line;
1555   GtkTextLineData *line_data;
1556 
1557   g_return_if_fail (tree != NULL);
1558 
1559   view = g_slice_new (BTreeView);
1560 
1561   view->view_id = layout;
1562   view->layout = layout;
1563 
1564   view->next = tree->views;
1565   view->prev = NULL;
1566 
1567   if (tree->views)
1568     {
1569       g_assert (tree->views->prev == NULL);
1570       tree->views->prev = view;
1571     }
1572 
1573   tree->views = view;
1574 
1575   /* The last line in the buffer has identity values for the per-view
1576    * data so that we can avoid special case checks for it in a large
1577    * number of loops
1578    */
1579   last_line = get_last_line (tree);
1580 
1581   line_data = g_slice_new (GtkTextLineData);
1582   line_data->view_id = layout;
1583   line_data->next = NULL;
1584   line_data->width = 0;
1585   line_data->height = 0;
1586   line_data->valid = TRUE;
1587 
1588   _gtk_text_line_add_data (last_line, line_data);
1589 }
1590 
1591 void
_gtk_text_btree_remove_view(GtkTextBTree * tree,gpointer view_id)1592 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1593                              gpointer      view_id)
1594 {
1595   BTreeView *view;
1596   GtkTextLine *last_line;
1597   GtkTextLineData *line_data;
1598 
1599   g_return_if_fail (tree != NULL);
1600 
1601   view = tree->views;
1602 
1603   while (view != NULL)
1604     {
1605       if (view->view_id == view_id)
1606         break;
1607 
1608       view = view->next;
1609     }
1610 
1611   g_return_if_fail (view != NULL);
1612 
1613   if (view->next)
1614     view->next->prev = view->prev;
1615 
1616   if (view->prev)
1617     view->prev->next = view->next;
1618 
1619   if (view == tree->views)
1620     tree->views = view->next;
1621 
1622   /* Remove the line data for the last line which we added ourselves.
1623    * (Do this first, so that we don't try to call the view's line data destructor on it.)
1624    */
1625   last_line = get_last_line (tree);
1626   line_data = _gtk_text_line_remove_data (last_line, view_id);
1627   g_slice_free (GtkTextLineData, line_data);
1628 
1629   gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1630 
1631   view->layout = (gpointer) 0xdeadbeef;
1632   view->view_id = (gpointer) 0xdeadbeef;
1633 
1634   g_slice_free (BTreeView, view);
1635 }
1636 
1637 void
_gtk_text_btree_invalidate_region(GtkTextBTree * tree,const GtkTextIter * start,const GtkTextIter * end,gboolean cursors_only)1638 _gtk_text_btree_invalidate_region (GtkTextBTree      *tree,
1639                                    const GtkTextIter *start,
1640                                    const GtkTextIter *end,
1641                                    gboolean           cursors_only)
1642 {
1643   BTreeView *view;
1644 
1645   view = tree->views;
1646 
1647   while (view != NULL)
1648     {
1649       if (cursors_only)
1650 	gtk_text_layout_invalidate_cursors (view->layout, start, end);
1651       else
1652 	gtk_text_layout_invalidate (view->layout, start, end);
1653 
1654       view = view->next;
1655     }
1656 }
1657 
1658 void
_gtk_text_btree_get_view_size(GtkTextBTree * tree,gpointer view_id,gint * width,gint * height)1659 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1660                               gpointer view_id,
1661                               gint *width,
1662                               gint *height)
1663 {
1664   g_return_if_fail (tree != NULL);
1665   g_return_if_fail (view_id != NULL);
1666 
1667   gtk_text_btree_node_get_size (tree->root_node, view_id,
1668                                 width, height);
1669 }
1670 
1671 /*
1672  * Tag
1673  */
1674 
1675 typedef struct {
1676   GtkTextIter *iters;
1677   guint count;
1678   guint alloced;
1679 } IterStack;
1680 
1681 static IterStack*
iter_stack_new(void)1682 iter_stack_new (void)
1683 {
1684   IterStack *stack;
1685   stack = g_slice_new (IterStack);
1686   stack->iters = NULL;
1687   stack->count = 0;
1688   stack->alloced = 0;
1689   return stack;
1690 }
1691 
1692 static void
iter_stack_push(IterStack * stack,const GtkTextIter * iter)1693 iter_stack_push (IterStack         *stack,
1694 		 const GtkTextIter *iter)
1695 {
1696   stack->count += 1;
1697   if (stack->count > stack->alloced)
1698     {
1699       stack->alloced = stack->count*2;
1700       stack->iters = g_realloc (stack->iters,
1701                                 stack->alloced * sizeof (GtkTextIter));
1702     }
1703   stack->iters[stack->count-1] = *iter;
1704 }
1705 
1706 static gboolean
iter_stack_pop(IterStack * stack,GtkTextIter * iter)1707 iter_stack_pop (IterStack   *stack,
1708 		GtkTextIter *iter)
1709 {
1710   if (stack->count == 0)
1711     return FALSE;
1712   else
1713     {
1714       stack->count -= 1;
1715       *iter = stack->iters[stack->count];
1716       return TRUE;
1717     }
1718 }
1719 
1720 static void
iter_stack_free(IterStack * stack)1721 iter_stack_free (IterStack *stack)
1722 {
1723   g_free (stack->iters);
1724   g_slice_free (IterStack, stack);
1725 }
1726 
1727 static void
iter_stack_invert(IterStack * stack)1728 iter_stack_invert (IterStack *stack)
1729 {
1730   if (stack->count > 0)
1731     {
1732       guint i = 0;
1733       guint j = stack->count - 1;
1734       while (i < j)
1735         {
1736           GtkTextIter tmp;
1737 
1738           tmp = stack->iters[i];
1739           stack->iters[i] = stack->iters[j];
1740           stack->iters[j] = tmp;
1741 
1742           ++i;
1743           --j;
1744         }
1745     }
1746 }
1747 
1748 static void
queue_tag_redisplay(GtkTextBTree * tree,GtkTextTag * tag,const GtkTextIter * start,const GtkTextIter * end)1749 queue_tag_redisplay (GtkTextBTree      *tree,
1750                      GtkTextTag        *tag,
1751                      const GtkTextIter *start,
1752                      const GtkTextIter *end)
1753 {
1754   if (_gtk_text_tag_affects_size (tag))
1755     {
1756       DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1757       _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
1758     }
1759   else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1760     {
1761       /* We only need to queue a redraw, not a relayout */
1762       redisplay_region (tree, start, end, FALSE);
1763     }
1764 
1765   /* We don't need to do anything if the tag doesn't affect display */
1766 }
1767 
1768 void
_gtk_text_btree_tag(const GtkTextIter * start_orig,const GtkTextIter * end_orig,GtkTextTag * tag,gboolean add)1769 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1770                      const GtkTextIter *end_orig,
1771                      GtkTextTag        *tag,
1772                      gboolean           add)
1773 {
1774   GtkTextLineSegment *seg, *prev;
1775   GtkTextLine *cleanupline;
1776   gboolean toggled_on;
1777   GtkTextLine *start_line;
1778   GtkTextLine *end_line;
1779   GtkTextIter iter;
1780   GtkTextIter start, end;
1781   GtkTextBTree *tree;
1782   IterStack *stack;
1783   GtkTextTagInfo *info;
1784 
1785   g_return_if_fail (start_orig != NULL);
1786   g_return_if_fail (end_orig != NULL);
1787   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1788   g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1789                     _gtk_text_iter_get_btree (end_orig));
1790   g_return_if_fail (tag->priv->table == _gtk_text_iter_get_btree (start_orig)->table);
1791 
1792 #if 0
1793   printf ("%s tag %s from %d to %d\n",
1794           add ? "Adding" : "Removing",
1795           tag->name,
1796           gtk_text_buffer_get_offset (start_orig),
1797           gtk_text_buffer_get_offset (end_orig));
1798 #endif
1799 
1800   if (gtk_text_iter_equal (start_orig, end_orig))
1801     return;
1802 
1803   start = *start_orig;
1804   end = *end_orig;
1805 
1806   gtk_text_iter_order (&start, &end);
1807 
1808   tree = _gtk_text_iter_get_btree (&start);
1809 
1810   queue_tag_redisplay (tree, tag, &start, &end);
1811 
1812   info = gtk_text_btree_get_tag_info (tree, tag);
1813 
1814   start_line = _gtk_text_iter_get_text_line (&start);
1815   end_line = _gtk_text_iter_get_text_line (&end);
1816 
1817   /* Find all tag toggles in the region; we are going to delete them.
1818      We need to find them in advance, because
1819      forward_find_tag_toggle () won't work once we start playing around
1820      with the tree. */
1821   stack = iter_stack_new ();
1822   iter = start;
1823 
1824   /* forward_to_tag_toggle() skips a toggle at the start iterator,
1825    * which is deliberate - we don't want to delete a toggle at the
1826    * start.
1827    */
1828   while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1829     {
1830       if (gtk_text_iter_compare (&iter, &end) >= 0)
1831         break;
1832       else
1833         iter_stack_push (stack, &iter);
1834     }
1835 
1836   /* We need to traverse the toggles in order. */
1837   iter_stack_invert (stack);
1838 
1839   /*
1840    * See whether the tag is present at the start of the range.  If
1841    * the state doesn't already match what we want then add a toggle
1842    * there.
1843    */
1844 
1845   toggled_on = gtk_text_iter_has_tag (&start, tag);
1846   if ( (add && !toggled_on) ||
1847        (!add && toggled_on) )
1848     {
1849       /* This could create a second toggle at the start position;
1850          cleanup_line () will remove it if so. */
1851       seg = _gtk_toggle_segment_new (info, add);
1852 
1853       prev = gtk_text_line_segment_split (&start);
1854       if (prev == NULL)
1855         {
1856           seg->next = start_line->segments;
1857           start_line->segments = seg;
1858         }
1859       else
1860         {
1861           seg->next = prev->next;
1862           prev->next = seg;
1863         }
1864 
1865       /* cleanup_line adds the new toggle to the node counts. */
1866 #if 0
1867       printf ("added toggle at start\n");
1868 #endif
1869       /* we should probably call segments_changed, but in theory
1870          any still-cached segments in the iters we are about to
1871          use are still valid, since they're in front
1872          of this spot. */
1873     }
1874 
1875   /*
1876    *
1877    * Scan the range of characters and delete any internal tag
1878    * transitions.  Keep track of what the old state was at the end
1879    * of the range, and add a toggle there if it's needed.
1880    *
1881    */
1882 
1883   cleanupline = start_line;
1884   while (iter_stack_pop (stack, &iter))
1885     {
1886       GtkTextLineSegment *indexable_seg;
1887       GtkTextLine *line;
1888 
1889       line = _gtk_text_iter_get_text_line (&iter);
1890       seg = _gtk_text_iter_get_any_segment (&iter);
1891       indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1892 
1893       g_assert (seg != NULL);
1894       g_assert (indexable_seg != NULL);
1895       g_assert (seg != indexable_seg);
1896 
1897       prev = line->segments;
1898 
1899       /* Find the segment that actually toggles this tag. */
1900       while (seg != indexable_seg)
1901         {
1902           g_assert (seg != NULL);
1903           g_assert (indexable_seg != NULL);
1904           g_assert (seg != indexable_seg);
1905 
1906           if ( (seg->type == &gtk_text_toggle_on_type ||
1907                 seg->type == &gtk_text_toggle_off_type) &&
1908                (seg->body.toggle.info == info) )
1909             break;
1910           else
1911             seg = seg->next;
1912         }
1913 
1914       g_assert (seg != NULL);
1915       g_assert (indexable_seg != NULL);
1916 
1917       g_assert (seg != indexable_seg); /* If this happens, then
1918                                           forward_to_tag_toggle was
1919                                           full of shit. */
1920       g_assert (seg->body.toggle.info->tag == tag);
1921 
1922       /* If this happens, when previously tagging we didn't merge
1923          overlapping tags. */
1924       g_assert ( (toggled_on && seg->type == &gtk_text_toggle_off_type) ||
1925                  (!toggled_on && seg->type == &gtk_text_toggle_on_type) );
1926 
1927       toggled_on = !toggled_on;
1928 
1929 #if 0
1930       printf ("deleting %s toggle\n",
1931               seg->type == &gtk_text_toggle_on_type ? "on" : "off");
1932 #endif
1933       /* Remove toggle segment from the list. */
1934       if (prev == seg)
1935         {
1936           line->segments = seg->next;
1937         }
1938       else
1939         {
1940           while (prev->next != seg)
1941             {
1942               prev = prev->next;
1943             }
1944           prev->next = seg->next;
1945         }
1946 
1947       /* Inform iterators we've hosed them. This actually reflects a
1948          bit of inefficiency; if you have the same tag toggled on and
1949          off a lot in a single line, we keep having the rescan from
1950          the front of the line. Of course we have to do that to get
1951          "prev" anyway, but here we are doing it an additional
1952          time. FIXME */
1953       segments_changed (tree);
1954 
1955       /* Update node counts */
1956       if (seg->body.toggle.inNodeCounts)
1957         {
1958           _gtk_change_node_toggle_count (line->parent,
1959                                          info, -1);
1960           seg->body.toggle.inNodeCounts = FALSE;
1961         }
1962 
1963       _gtk_toggle_segment_free (seg);
1964 
1965       /* We only clean up lines when we're done with them, saves some
1966          gratuitous line-segment-traversals */
1967 
1968       if (cleanupline != line)
1969         {
1970           cleanup_line (cleanupline);
1971           cleanupline = line;
1972         }
1973     }
1974 
1975   iter_stack_free (stack);
1976 
1977   /* toggled_on now reflects the toggle state _just before_ the
1978      end iterator. The end iterator could already have a toggle
1979      on or a toggle off. */
1980   if ( (add && !toggled_on) ||
1981        (!add && toggled_on) )
1982     {
1983       /* This could create a second toggle at the start position;
1984          cleanup_line () will remove it if so. */
1985 
1986       seg = _gtk_toggle_segment_new (info, !add);
1987 
1988       prev = gtk_text_line_segment_split (&end);
1989       if (prev == NULL)
1990         {
1991           seg->next = end_line->segments;
1992           end_line->segments = seg;
1993         }
1994       else
1995         {
1996           seg->next = prev->next;
1997           prev->next = seg;
1998         }
1999       /* cleanup_line adds the new toggle to the node counts. */
2000       g_assert (seg->body.toggle.inNodeCounts == FALSE);
2001 #if 0
2002       printf ("added toggle at end\n");
2003 #endif
2004     }
2005 
2006   /*
2007    * Cleanup cleanupline and the last line of the range, if
2008    * these are different.
2009    */
2010 
2011   cleanup_line (cleanupline);
2012   if (cleanupline != end_line)
2013     {
2014       cleanup_line (end_line);
2015     }
2016 
2017   segments_changed (tree);
2018 
2019   queue_tag_redisplay (tree, tag, &start, &end);
2020 
2021 #ifdef G_ENABLE_DEBUG
2022   if (GTK_DEBUG_CHECK (TEXT))
2023     _gtk_text_btree_check (tree);
2024 #endif
2025 }
2026 
2027 
2028 /*
2029  * "Getters"
2030  */
2031 
2032 static GtkTextLine*
get_line_internal(GtkTextBTree * tree,gint line_number,gint * real_line_number,gboolean include_last)2033 get_line_internal (GtkTextBTree *tree,
2034                    gint          line_number,
2035                    gint         *real_line_number,
2036                    gboolean      include_last)
2037 {
2038   GtkTextBTreeNode *node;
2039   GtkTextLine *line;
2040   int lines_left;
2041   int line_count;
2042 
2043   line_count = _gtk_text_btree_line_count (tree);
2044   if (!include_last)
2045     line_count -= 1;
2046 
2047   if (line_number < 0)
2048     {
2049       line_number = line_count;
2050     }
2051   else if (line_number > line_count)
2052     {
2053       line_number = line_count;
2054     }
2055 
2056   if (real_line_number)
2057     *real_line_number = line_number;
2058 
2059   node = tree->root_node;
2060   lines_left = line_number;
2061 
2062   /*
2063    * Work down through levels of the tree until a GtkTextBTreeNode is found at
2064    * level 0.
2065    */
2066 
2067   while (node->level != 0)
2068     {
2069       for (node = node->children.node;
2070            node->num_lines <= lines_left;
2071            node = node->next)
2072         {
2073 #if 0
2074           if (node == NULL)
2075             {
2076               g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2077             }
2078 #endif
2079           lines_left -= node->num_lines;
2080         }
2081     }
2082 
2083   /*
2084    * Work through the lines attached to the level-0 GtkTextBTreeNode.
2085    */
2086 
2087   for (line = node->children.line; lines_left > 0;
2088        line = line->next)
2089     {
2090 #if 0
2091       if (line == NULL)
2092         {
2093           g_error ("gtk_text_btree_find_line ran out of lines");
2094         }
2095 #endif
2096       lines_left -= 1;
2097     }
2098   return line;
2099 }
2100 
2101 GtkTextLine*
_gtk_text_btree_get_end_iter_line(GtkTextBTree * tree)2102 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2103 {
2104   return
2105     _gtk_text_btree_get_line (tree,
2106                               _gtk_text_btree_line_count (tree) - 1,
2107                               NULL);
2108 }
2109 
2110 GtkTextLine*
_gtk_text_btree_get_line(GtkTextBTree * tree,gint line_number,gint * real_line_number)2111 _gtk_text_btree_get_line (GtkTextBTree *tree,
2112                           gint          line_number,
2113                           gint         *real_line_number)
2114 {
2115   return get_line_internal (tree, line_number, real_line_number, TRUE);
2116 }
2117 
2118 GtkTextLine*
_gtk_text_btree_get_line_no_last(GtkTextBTree * tree,gint line_number,gint * real_line_number)2119 _gtk_text_btree_get_line_no_last (GtkTextBTree      *tree,
2120                                   gint               line_number,
2121                                   gint              *real_line_number)
2122 {
2123   return get_line_internal (tree, line_number, real_line_number, FALSE);
2124 }
2125 
2126 GtkTextLine*
_gtk_text_btree_get_line_at_char(GtkTextBTree * tree,gint char_index,gint * line_start_index,gint * real_char_index)2127 _gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
2128                                   gint               char_index,
2129                                   gint              *line_start_index,
2130                                   gint              *real_char_index)
2131 {
2132   GtkTextBTreeNode *node;
2133   GtkTextLine *line;
2134   GtkTextLineSegment *seg;
2135   int chars_left;
2136   int chars_in_line;
2137 
2138   node = tree->root_node;
2139 
2140   /* Clamp to valid indexes (-1 is magic for "highest index"),
2141    * node->num_chars includes the two newlines that aren't really
2142    * in the buffer.
2143    */
2144   if (char_index < 0 || char_index >= (node->num_chars - 1))
2145     {
2146       char_index = node->num_chars - 2;
2147     }
2148 
2149   *real_char_index = char_index;
2150 
2151   /*
2152    * Work down through levels of the tree until a GtkTextBTreeNode is found at
2153    * level 0.
2154    */
2155 
2156   chars_left = char_index;
2157   while (node->level != 0)
2158     {
2159       for (node = node->children.node;
2160            chars_left >= node->num_chars;
2161            node = node->next)
2162         {
2163           chars_left -= node->num_chars;
2164 
2165           g_assert (chars_left >= 0);
2166         }
2167     }
2168 
2169   if (chars_left == 0)
2170     {
2171       /* Start of a line */
2172 
2173       *line_start_index = char_index;
2174       return node->children.line;
2175     }
2176 
2177   /*
2178    * Work through the lines attached to the level-0 GtkTextBTreeNode.
2179    */
2180 
2181   chars_in_line = 0;
2182   seg = NULL;
2183   for (line = node->children.line; line != NULL; line = line->next)
2184     {
2185       seg = line->segments;
2186       while (seg != NULL)
2187         {
2188           if (chars_in_line + seg->char_count > chars_left)
2189             goto found; /* found our line/segment */
2190 
2191           chars_in_line += seg->char_count;
2192 
2193           seg = seg->next;
2194         }
2195 
2196       chars_left -= chars_in_line;
2197 
2198       chars_in_line = 0;
2199       seg = NULL;
2200     }
2201 
2202  found:
2203 
2204   g_assert (line != NULL); /* hosage, ran out of lines */
2205   g_assert (seg != NULL);
2206 
2207   *line_start_index = char_index - chars_left;
2208   return line;
2209 }
2210 
2211 /* It returns an array sorted by tags priority, ready to pass to
2212  * _gtk_text_attributes_fill_from_tags() */
2213 GtkTextTag**
_gtk_text_btree_get_tags(const GtkTextIter * iter,gint * num_tags)2214 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2215                          gint *num_tags)
2216 {
2217   GtkTextBTreeNode *node;
2218   GtkTextLine *siblingline;
2219   GtkTextLineSegment *seg;
2220   int src, dst, index;
2221   TagInfo tagInfo;
2222   GtkTextLine *line;
2223   gint byte_index;
2224 
2225 #define NUM_TAG_INFOS 10
2226 
2227   line = _gtk_text_iter_get_text_line (iter);
2228   byte_index = gtk_text_iter_get_line_index (iter);
2229 
2230   tagInfo.numTags = 0;
2231   tagInfo.arraySize = NUM_TAG_INFOS;
2232   tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2233   tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2234 
2235   /*
2236    * Record tag toggles within the line of indexPtr but preceding
2237    * indexPtr. Note that if this loop segfaults, your
2238    * byte_index probably points past the sum of all
2239    * seg->byte_count */
2240 
2241   for (index = 0, seg = line->segments;
2242        (index + seg->byte_count) <= byte_index;
2243        index += seg->byte_count, seg = seg->next)
2244     {
2245       if ((seg->type == &gtk_text_toggle_on_type)
2246           || (seg->type == &gtk_text_toggle_off_type))
2247         {
2248           inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2249         }
2250     }
2251 
2252   /*
2253    * Record toggles for tags in lines that are predecessors of
2254    * line but under the same level-0 GtkTextBTreeNode.
2255    */
2256 
2257   for (siblingline = line->parent->children.line;
2258        siblingline != line;
2259        siblingline = siblingline->next)
2260     {
2261       for (seg = siblingline->segments; seg != NULL;
2262            seg = seg->next)
2263         {
2264           if ((seg->type == &gtk_text_toggle_on_type)
2265               || (seg->type == &gtk_text_toggle_off_type))
2266             {
2267               inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2268             }
2269         }
2270     }
2271 
2272   /*
2273    * For each GtkTextBTreeNode in the ancestry of this line, record tag
2274    * toggles for all siblings that precede that GtkTextBTreeNode.
2275    */
2276 
2277   for (node = line->parent; node->parent != NULL;
2278        node = node->parent)
2279     {
2280       GtkTextBTreeNode *siblingPtr;
2281       Summary *summary;
2282 
2283       for (siblingPtr = node->parent->children.node;
2284            siblingPtr != node; siblingPtr = siblingPtr->next)
2285         {
2286           for (summary = siblingPtr->summary; summary != NULL;
2287                summary = summary->next)
2288             {
2289               if (summary->toggle_count & 1)
2290                 {
2291                   inc_count (summary->info->tag, summary->toggle_count,
2292                              &tagInfo);
2293                 }
2294             }
2295         }
2296     }
2297 
2298   /*
2299    * Go through the tag information and squash out all of the tags
2300    * that have even toggle counts (these tags exist before the point
2301    * of interest, but not at the desired character itself).
2302    */
2303 
2304   for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2305     {
2306       if (tagInfo.counts[src] & 1)
2307         {
2308           g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2309           tagInfo.tags[dst] = tagInfo.tags[src];
2310           dst++;
2311         }
2312     }
2313 
2314   *num_tags = dst;
2315   g_free (tagInfo.counts);
2316   if (dst == 0)
2317     {
2318       g_free (tagInfo.tags);
2319       return NULL;
2320     }
2321 
2322   /* Sort tags in ascending order of priority */
2323   _gtk_text_tag_array_sort (tagInfo.tags, dst);
2324 
2325   return tagInfo.tags;
2326 }
2327 
2328 static void
copy_segment(GString * string,gboolean include_hidden,gboolean include_nonchars,const GtkTextIter * start,const GtkTextIter * end)2329 copy_segment (GString *string,
2330               gboolean include_hidden,
2331               gboolean include_nonchars,
2332               const GtkTextIter *start,
2333               const GtkTextIter *end)
2334 {
2335   GtkTextLineSegment *end_seg;
2336   GtkTextLineSegment *seg;
2337 
2338   if (gtk_text_iter_equal (start, end))
2339     return;
2340 
2341   seg = _gtk_text_iter_get_indexable_segment (start);
2342   end_seg = _gtk_text_iter_get_indexable_segment (end);
2343 
2344   if (seg->type == &gtk_text_char_type)
2345     {
2346       gboolean copy = TRUE;
2347       gint copy_bytes = 0;
2348       gint copy_start = 0;
2349 
2350       /* Don't copy if we're invisible; segments are invisible/not
2351          as a whole, no need to check each char */
2352       if (!include_hidden &&
2353           _gtk_text_btree_char_is_invisible (start))
2354         {
2355           copy = FALSE;
2356           /* printf (" <invisible>\n"); */
2357         }
2358 
2359       copy_start = _gtk_text_iter_get_segment_byte (start);
2360 
2361       if (seg == end_seg)
2362         {
2363           /* End is in the same segment; need to copy fewer bytes. */
2364           gint end_byte = _gtk_text_iter_get_segment_byte (end);
2365 
2366           copy_bytes = end_byte - copy_start;
2367         }
2368       else
2369         copy_bytes = seg->byte_count - copy_start;
2370 
2371       g_assert (copy_bytes != 0); /* Due to iter equality check at
2372                                      front of this function. */
2373 
2374       if (copy)
2375         {
2376           g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2377 
2378           g_string_append_len (string,
2379                                seg->body.chars + copy_start,
2380                                copy_bytes);
2381         }
2382 
2383       /* printf ("  :%s\n", string->str); */
2384     }
2385   else if (seg->type == &gtk_text_pixbuf_type ||
2386            seg->type == &gtk_text_child_type)
2387     {
2388       gboolean copy = TRUE;
2389 
2390       if (!include_nonchars)
2391         {
2392           copy = FALSE;
2393         }
2394       else if (!include_hidden &&
2395                _gtk_text_btree_char_is_invisible (start))
2396         {
2397           copy = FALSE;
2398         }
2399 
2400       if (copy)
2401         {
2402           g_string_append_len (string,
2403                                _gtk_text_unknown_char_utf8,
2404                                GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN);
2405 
2406         }
2407     }
2408 }
2409 
2410 gchar*
_gtk_text_btree_get_text(const GtkTextIter * start_orig,const GtkTextIter * end_orig,gboolean include_hidden,gboolean include_nonchars)2411 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2412                          const GtkTextIter *end_orig,
2413                          gboolean include_hidden,
2414                          gboolean include_nonchars)
2415 {
2416   GtkTextLineSegment *seg;
2417   GtkTextLineSegment *end_seg;
2418   GString *retval;
2419   gchar *str;
2420   GtkTextIter iter;
2421   GtkTextIter start;
2422   GtkTextIter end;
2423 
2424   g_return_val_if_fail (start_orig != NULL, NULL);
2425   g_return_val_if_fail (end_orig != NULL, NULL);
2426   g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2427                         _gtk_text_iter_get_btree (end_orig), NULL);
2428 
2429   start = *start_orig;
2430   end = *end_orig;
2431 
2432   gtk_text_iter_order (&start, &end);
2433 
2434   retval = g_string_new (NULL);
2435 
2436   end_seg = _gtk_text_iter_get_indexable_segment (&end);
2437   iter = start;
2438   seg = _gtk_text_iter_get_indexable_segment (&iter);
2439   while (seg != end_seg)
2440     {
2441       copy_segment (retval, include_hidden, include_nonchars,
2442                     &iter, &end);
2443 
2444       _gtk_text_iter_forward_indexable_segment (&iter);
2445 
2446       seg = _gtk_text_iter_get_indexable_segment (&iter);
2447     }
2448 
2449   copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2450 
2451   str = retval->str;
2452   g_string_free (retval, FALSE);
2453   return str;
2454 }
2455 
2456 gint
_gtk_text_btree_line_count(GtkTextBTree * tree)2457 _gtk_text_btree_line_count (GtkTextBTree *tree)
2458 {
2459   /* Subtract bogus line at the end; we return a count
2460      of usable lines. */
2461   return tree->root_node->num_lines - 1;
2462 }
2463 
2464 gint
_gtk_text_btree_char_count(GtkTextBTree * tree)2465 _gtk_text_btree_char_count (GtkTextBTree *tree)
2466 {
2467   /* Exclude newline in bogus last line and the
2468    * one in the last line that is after the end iterator
2469    */
2470   return tree->root_node->num_chars - 2;
2471 }
2472 
2473 #define LOTSA_TAGS 1000
2474 gboolean
_gtk_text_btree_char_is_invisible(const GtkTextIter * iter)2475 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2476 {
2477   gboolean invisible = FALSE;  /* if nobody says otherwise, it's visible */
2478 
2479   int deftagCnts[LOTSA_TAGS] = { 0, };
2480   int *tagCnts = deftagCnts;
2481   GtkTextTag *deftags[LOTSA_TAGS];
2482   GtkTextTag **tags = deftags;
2483   int numTags;
2484   GtkTextBTreeNode *node;
2485   GtkTextLine *siblingline;
2486   GtkTextLineSegment *seg;
2487   GtkTextTag *tag;
2488   int i, index;
2489   GtkTextLine *line;
2490   GtkTextBTree *tree;
2491   gint byte_index;
2492 
2493   line = _gtk_text_iter_get_text_line (iter);
2494   tree = _gtk_text_iter_get_btree (iter);
2495 
2496   /* Short-circuit if we've never seen a visibility tag within the
2497    * tag table (meaning everything must be visible).
2498    */
2499   if G_LIKELY (!_gtk_text_tag_table_affects_visibility (tree->table))
2500     return FALSE;
2501 
2502   byte_index = gtk_text_iter_get_line_index (iter);
2503 
2504   numTags = gtk_text_tag_table_get_size (tree->table);
2505 
2506   /* almost always avoid malloc, so stay out of system calls */
2507   if (LOTSA_TAGS < numTags)
2508     {
2509       tagCnts = g_new0 (int, numTags);
2510       tags = g_new (GtkTextTag*, numTags);
2511     }
2512 
2513   /*
2514    * Record tag toggles within the line of indexPtr but preceding
2515    * indexPtr.
2516    */
2517 
2518   for (index = 0, seg = line->segments;
2519        (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2520        index += seg->byte_count, seg = seg->next)
2521     {
2522       if ((seg->type == &gtk_text_toggle_on_type)
2523           || (seg->type == &gtk_text_toggle_off_type))
2524         {
2525           tag = seg->body.toggle.info->tag;
2526           if (tag->priv->invisible_set)
2527             {
2528               tags[tag->priv->priority] = tag;
2529               tagCnts[tag->priv->priority]++;
2530             }
2531         }
2532     }
2533 
2534   /*
2535    * Record toggles for tags in lines that are predecessors of
2536    * line but under the same level-0 GtkTextBTreeNode.
2537    */
2538 
2539   for (siblingline = line->parent->children.line;
2540        siblingline != line;
2541        siblingline = siblingline->next)
2542     {
2543       for (seg = siblingline->segments; seg != NULL;
2544            seg = seg->next)
2545         {
2546           if ((seg->type == &gtk_text_toggle_on_type)
2547               || (seg->type == &gtk_text_toggle_off_type))
2548             {
2549               tag = seg->body.toggle.info->tag;
2550               if (tag->priv->invisible_set)
2551                 {
2552                   tags[tag->priv->priority] = tag;
2553                   tagCnts[tag->priv->priority]++;
2554                 }
2555             }
2556         }
2557     }
2558 
2559   /*
2560    * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2561    * for all siblings that precede that GtkTextBTreeNode.
2562    */
2563 
2564   for (node = line->parent; node->parent != NULL;
2565        node = node->parent)
2566     {
2567       GtkTextBTreeNode *siblingPtr;
2568       Summary *summary;
2569 
2570       for (siblingPtr = node->parent->children.node;
2571            siblingPtr != node; siblingPtr = siblingPtr->next)
2572         {
2573           for (summary = siblingPtr->summary; summary != NULL;
2574                summary = summary->next)
2575             {
2576               if (summary->toggle_count & 1)
2577                 {
2578                   tag = summary->info->tag;
2579                   if (tag->priv->invisible_set)
2580                     {
2581                       tags[tag->priv->priority] = tag;
2582                       tagCnts[tag->priv->priority] += summary->toggle_count;
2583                     }
2584                 }
2585             }
2586         }
2587     }
2588 
2589   /*
2590    * Now traverse from highest priority to lowest,
2591    * take invisible value from first odd count (= on)
2592    */
2593 
2594   for (i = numTags-1; i >=0; i--)
2595     {
2596       if (tagCnts[i] & 1)
2597         {
2598           /* FIXME not sure this should be if 0 */
2599 #if 0
2600 #ifndef ALWAYS_SHOW_SELECTION
2601           /* who would make the selection invisible? */
2602           if ((tag == tkxt->seltag)
2603               && !(tkxt->flags & GOT_FOCUS))
2604             {
2605               continue;
2606             }
2607 #endif
2608 #endif
2609           invisible = tags[i]->priv->values->invisible;
2610           break;
2611         }
2612     }
2613 
2614   if (LOTSA_TAGS < numTags)
2615     {
2616       g_free (tagCnts);
2617       g_free (tags);
2618     }
2619 
2620   return invisible;
2621 }
2622 
2623 
2624 /*
2625  * Manipulate marks
2626  */
2627 
2628 static void
redisplay_region(GtkTextBTree * tree,const GtkTextIter * start,const GtkTextIter * end,gboolean cursors_only)2629 redisplay_region (GtkTextBTree      *tree,
2630                   const GtkTextIter *start,
2631                   const GtkTextIter *end,
2632                   gboolean           cursors_only)
2633 {
2634   BTreeView *view;
2635   GtkTextLine *start_line, *end_line;
2636 
2637   if (gtk_text_iter_compare (start, end) > 0)
2638     {
2639       const GtkTextIter *tmp = start;
2640       start = end;
2641       end = tmp;
2642     }
2643 
2644   start_line = _gtk_text_iter_get_text_line (start);
2645   end_line = _gtk_text_iter_get_text_line (end);
2646 
2647   view = tree->views;
2648   while (view != NULL)
2649     {
2650       gint start_y, end_y;
2651       GtkTextLineData *ld;
2652 
2653       start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2654 
2655       if (end_line == start_line)
2656         end_y = start_y;
2657       else
2658         end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2659 
2660       ld = _gtk_text_line_get_data (start_line, view->view_id);
2661       if (ld)
2662         start_y -= ld->top_ink;
2663 
2664       ld = _gtk_text_line_get_data (end_line, view->view_id);
2665       if (ld)
2666         end_y += ld->height + ld->bottom_ink;
2667 
2668       if (cursors_only)
2669 	gtk_text_layout_cursors_changed (view->layout, start_y,
2670 					 end_y - start_y,
2671 					 end_y - start_y);
2672       else
2673 	gtk_text_layout_changed (view->layout, start_y,
2674 				 end_y - start_y,
2675 				 end_y - start_y);
2676 
2677       view = view->next;
2678     }
2679 }
2680 
2681 static void
redisplay_mark(GtkTextLineSegment * mark)2682 redisplay_mark (GtkTextLineSegment *mark)
2683 {
2684   GtkTextIter iter;
2685   GtkTextIter end;
2686   gboolean cursor_only;
2687 
2688   _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2689                                    &iter,
2690                                    mark->body.mark.obj);
2691 
2692   end = iter;
2693   gtk_text_iter_forward_char (&end);
2694 
2695   DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2696   cursor_only = mark == mark->body.mark.tree->insert_mark->segment;
2697   _gtk_text_btree_invalidate_region (mark->body.mark.tree, &iter, &end, cursor_only);
2698 }
2699 
2700 static void
redisplay_mark_if_visible(GtkTextLineSegment * mark)2701 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2702 {
2703   if (!mark->body.mark.visible)
2704     return;
2705   else
2706     redisplay_mark (mark);
2707 }
2708 
2709 static void
ensure_not_off_end(GtkTextBTree * tree,GtkTextLineSegment * mark,GtkTextIter * iter)2710 ensure_not_off_end (GtkTextBTree *tree,
2711                     GtkTextLineSegment *mark,
2712                     GtkTextIter *iter)
2713 {
2714   if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree))
2715     gtk_text_iter_backward_char (iter);
2716 }
2717 
2718 static GtkTextLineSegment*
real_set_mark(GtkTextBTree * tree,GtkTextMark * existing_mark,const gchar * name,gboolean left_gravity,const GtkTextIter * where,gboolean should_exist,gboolean redraw_selections)2719 real_set_mark (GtkTextBTree      *tree,
2720                GtkTextMark       *existing_mark,
2721                const gchar       *name,
2722                gboolean           left_gravity,
2723                const GtkTextIter *where,
2724                gboolean           should_exist,
2725                gboolean           redraw_selections)
2726 {
2727   GtkTextLineSegment *mark;
2728   GtkTextIter iter;
2729 
2730   g_return_val_if_fail (tree != NULL, NULL);
2731   g_return_val_if_fail (where != NULL, NULL);
2732   g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2733 
2734   if (existing_mark)
2735     {
2736       if (gtk_text_mark_get_buffer (existing_mark) != NULL)
2737 	mark = existing_mark->segment;
2738       else
2739 	mark = NULL;
2740     }
2741   else if (name != NULL)
2742     mark = g_hash_table_lookup (tree->mark_table,
2743                                 name);
2744   else
2745     mark = NULL;
2746 
2747   if (should_exist && mark == NULL)
2748     {
2749       g_warning ("No mark '%s' exists!", name);
2750       return NULL;
2751     }
2752 
2753   /* OK if !should_exist and it does already exist, in that case
2754    * we just move it.
2755    */
2756 
2757   iter = *where;
2758 
2759 #ifdef G_ENABLE_DEBUG
2760   if (GTK_DEBUG_CHECK (TEXT))
2761     _gtk_text_iter_check (&iter);
2762 #endif
2763 
2764   if (mark != NULL)
2765     {
2766       if (redraw_selections &&
2767           (mark == tree->insert_mark->segment ||
2768            mark == tree->selection_bound_mark->segment))
2769         {
2770           GtkTextIter old_pos;
2771 
2772           _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2773                                            mark->body.mark.obj);
2774           redisplay_region (tree, &old_pos, where, TRUE);
2775         }
2776 
2777       /*
2778        * don't let visible marks be after the final newline of the
2779        *  file.
2780        */
2781 
2782       if (mark->body.mark.visible)
2783         {
2784           ensure_not_off_end (tree, mark, &iter);
2785         }
2786 
2787       /* Redraw the mark's old location. */
2788       redisplay_mark_if_visible (mark);
2789 
2790       /* Unlink mark from its current location.
2791          This could hose our iterator... */
2792       gtk_text_btree_unlink_segment (tree, mark,
2793                                      mark->body.mark.line);
2794       mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2795       g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2796 
2797       segments_changed (tree); /* make sure the iterator recomputes its
2798                                   segment */
2799     }
2800   else
2801     {
2802       if (existing_mark)
2803 	g_object_ref (existing_mark);
2804       else
2805 	existing_mark = gtk_text_mark_new (name, left_gravity);
2806 
2807       mark = existing_mark->segment;
2808       _gtk_mark_segment_set_tree (mark, tree);
2809 
2810       mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2811 
2812       if (mark->body.mark.name)
2813         g_hash_table_insert (tree->mark_table,
2814                              mark->body.mark.name,
2815                              mark);
2816     }
2817 
2818 #ifdef G_ENABLE_DEBUG
2819   if (GTK_DEBUG_CHECK (TEXT))
2820     _gtk_text_iter_check (&iter);
2821 #endif
2822 
2823   /* Link mark into new location */
2824   gtk_text_btree_link_segment (mark, &iter);
2825 
2826   /* Invalidate some iterators. */
2827   segments_changed (tree);
2828 
2829   /*
2830    * update the screen at the mark's new location.
2831    */
2832 
2833   redisplay_mark_if_visible (mark);
2834 
2835 #ifdef G_ENABLE_DEBUG
2836   if (GTK_DEBUG_CHECK (TEXT))
2837     {
2838       _gtk_text_iter_check (&iter);
2839       _gtk_text_btree_check (tree);
2840     }
2841 #endif
2842 
2843   return mark;
2844 }
2845 
2846 
2847 GtkTextMark*
_gtk_text_btree_set_mark(GtkTextBTree * tree,GtkTextMark * existing_mark,const gchar * name,gboolean left_gravity,const GtkTextIter * iter,gboolean should_exist)2848 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2849                          GtkTextMark  *existing_mark,
2850                          const gchar *name,
2851                          gboolean left_gravity,
2852                          const GtkTextIter *iter,
2853                          gboolean should_exist)
2854 {
2855   GtkTextLineSegment *seg;
2856 
2857   seg = real_set_mark (tree, existing_mark,
2858                        name, left_gravity, iter, should_exist,
2859                        TRUE);
2860 
2861   return seg ? seg->body.mark.obj : NULL;
2862 }
2863 
2864 gboolean
_gtk_text_btree_get_selection_bounds(GtkTextBTree * tree,GtkTextIter * start,GtkTextIter * end)2865 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2866                                      GtkTextIter  *start,
2867                                      GtkTextIter  *end)
2868 {
2869   GtkTextIter tmp_start, tmp_end;
2870 
2871   _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2872                                    tree->insert_mark);
2873   _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2874                                    tree->selection_bound_mark);
2875 
2876   if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2877     {
2878       if (start)
2879         *start = tmp_start;
2880 
2881       if (end)
2882         *end = tmp_end;
2883 
2884       return FALSE;
2885     }
2886   else
2887     {
2888       gtk_text_iter_order (&tmp_start, &tmp_end);
2889 
2890       if (start)
2891         *start = tmp_start;
2892 
2893       if (end)
2894         *end = tmp_end;
2895 
2896       return TRUE;
2897     }
2898 }
2899 
2900 void
_gtk_text_btree_place_cursor(GtkTextBTree * tree,const GtkTextIter * iter)2901 _gtk_text_btree_place_cursor (GtkTextBTree      *tree,
2902                              const GtkTextIter *iter)
2903 {
2904   _gtk_text_btree_select_range (tree, iter, iter);
2905 }
2906 
2907 void
_gtk_text_btree_select_range(GtkTextBTree * tree,const GtkTextIter * ins,const GtkTextIter * bound)2908 _gtk_text_btree_select_range (GtkTextBTree      *tree,
2909 			      const GtkTextIter *ins,
2910                               const GtkTextIter *bound)
2911 {
2912   GtkTextIter old_ins, old_bound;
2913 
2914   _gtk_text_btree_get_iter_at_mark (tree, &old_ins,
2915                                     tree->insert_mark);
2916   _gtk_text_btree_get_iter_at_mark (tree, &old_bound,
2917                                     tree->selection_bound_mark);
2918 
2919   /* Check if it's no-op since gtk_text_buffer_place_cursor()
2920    * also calls this, and this will redraw the cursor line. */
2921   if (!gtk_text_iter_equal (&old_ins, ins) ||
2922       !gtk_text_iter_equal (&old_bound, bound))
2923     {
2924       redisplay_region (tree, &old_ins, &old_bound, TRUE);
2925 
2926       /* Move insert AND selection_bound before we redisplay */
2927       real_set_mark (tree, tree->insert_mark,
2928 		     "insert", FALSE, ins, TRUE, FALSE);
2929       real_set_mark (tree, tree->selection_bound_mark,
2930 		     "selection_bound", FALSE, bound, TRUE, FALSE);
2931 
2932       redisplay_region (tree, ins, bound, TRUE);
2933     }
2934 }
2935 
2936 
2937 void
_gtk_text_btree_remove_mark_by_name(GtkTextBTree * tree,const gchar * name)2938 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2939                                     const gchar *name)
2940 {
2941   GtkTextMark *mark;
2942 
2943   g_return_if_fail (tree != NULL);
2944   g_return_if_fail (name != NULL);
2945 
2946   mark = g_hash_table_lookup (tree->mark_table,
2947                               name);
2948 
2949   _gtk_text_btree_remove_mark (tree, mark);
2950 }
2951 
2952 void
_gtk_text_btree_release_mark_segment(GtkTextBTree * tree,GtkTextLineSegment * segment)2953 _gtk_text_btree_release_mark_segment (GtkTextBTree       *tree,
2954                                       GtkTextLineSegment *segment)
2955 {
2956 
2957   if (segment->body.mark.name)
2958     g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2959 
2960   segment->body.mark.tree = NULL;
2961   segment->body.mark.line = NULL;
2962 
2963   /* Remove the ref on the mark, which frees segment as a side effect
2964    * if this is the last reference.
2965    */
2966   g_object_unref (segment->body.mark.obj);
2967 }
2968 
2969 void
_gtk_text_btree_remove_mark(GtkTextBTree * tree,GtkTextMark * mark)2970 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2971                              GtkTextMark *mark)
2972 {
2973   GtkTextLineSegment *segment;
2974 
2975   g_return_if_fail (mark != NULL);
2976   g_return_if_fail (tree != NULL);
2977 
2978   segment = mark->segment;
2979 
2980   if (segment->body.mark.not_deleteable)
2981     {
2982       g_warning ("Can't delete special mark '%s'", segment->body.mark.name);
2983       return;
2984     }
2985 
2986   /* This calls cleanup_line and segments_changed */
2987   gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2988 
2989   _gtk_text_btree_release_mark_segment (tree, segment);
2990 }
2991 
2992 gboolean
_gtk_text_btree_mark_is_insert(GtkTextBTree * tree,GtkTextMark * segment)2993 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2994                                 GtkTextMark *segment)
2995 {
2996   return segment == tree->insert_mark;
2997 }
2998 
2999 gboolean
_gtk_text_btree_mark_is_selection_bound(GtkTextBTree * tree,GtkTextMark * segment)3000 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
3001                                          GtkTextMark *segment)
3002 {
3003   return segment == tree->selection_bound_mark;
3004 }
3005 
3006 GtkTextMark *
_gtk_text_btree_get_insert(GtkTextBTree * tree)3007 _gtk_text_btree_get_insert (GtkTextBTree *tree)
3008 {
3009   return tree->insert_mark;
3010 }
3011 
3012 GtkTextMark *
_gtk_text_btree_get_selection_bound(GtkTextBTree * tree)3013 _gtk_text_btree_get_selection_bound (GtkTextBTree *tree)
3014 {
3015   return tree->selection_bound_mark;
3016 }
3017 
3018 GtkTextMark*
_gtk_text_btree_get_mark_by_name(GtkTextBTree * tree,const gchar * name)3019 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
3020                                   const gchar *name)
3021 {
3022   GtkTextLineSegment *seg;
3023 
3024   g_return_val_if_fail (tree != NULL, NULL);
3025   g_return_val_if_fail (name != NULL, NULL);
3026 
3027   seg = g_hash_table_lookup (tree->mark_table, name);
3028 
3029   return seg ? seg->body.mark.obj : NULL;
3030 }
3031 
3032 /**
3033  * gtk_text_mark_set_visible:
3034  * @mark: a #GtkTextMark
3035  * @setting: visibility of mark
3036  *
3037  * Sets the visibility of @mark; the insertion point is normally
3038  * visible, i.e. you can see it as a vertical bar. Also, the text
3039  * widget uses a visible mark to indicate where a drop will occur when
3040  * dragging-and-dropping text. Most other marks are not visible.
3041  * Marks are not visible by default.
3042  *
3043  **/
3044 void
gtk_text_mark_set_visible(GtkTextMark * mark,gboolean setting)3045 gtk_text_mark_set_visible (GtkTextMark       *mark,
3046                            gboolean           setting)
3047 {
3048   GtkTextLineSegment *seg;
3049 
3050   g_return_if_fail (mark != NULL);
3051 
3052   seg = mark->segment;
3053 
3054   if (seg->body.mark.visible == setting)
3055     return;
3056   else
3057     {
3058       seg->body.mark.visible = setting;
3059 
3060       if (seg->body.mark.tree)
3061 	redisplay_mark (seg);
3062     }
3063 }
3064 
3065 GtkTextLine*
_gtk_text_btree_first_could_contain_tag(GtkTextBTree * tree,GtkTextTag * tag)3066 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
3067                                         GtkTextTag *tag)
3068 {
3069   GtkTextBTreeNode *node;
3070   GtkTextTagInfo *info;
3071 
3072   g_return_val_if_fail (tree != NULL, NULL);
3073 
3074   if (tag != NULL)
3075     {
3076       info = gtk_text_btree_get_existing_tag_info (tree, tag);
3077 
3078       if (info == NULL)
3079         return NULL;
3080 
3081       if (info->tag_root == NULL)
3082         return NULL;
3083 
3084       node = info->tag_root;
3085 
3086       /* We know the tag root has instances of the given
3087          tag below it */
3088 
3089     continue_outer_loop:
3090       g_assert (node != NULL);
3091       while (node->level > 0)
3092         {
3093           g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3094           node = node->children.node;
3095           while (node != NULL)
3096             {
3097               if (gtk_text_btree_node_has_tag (node, tag))
3098                 goto continue_outer_loop;
3099 
3100               node = node->next;
3101             }
3102           g_assert (node != NULL);
3103         }
3104 
3105       g_assert (node != NULL); /* The tag summaries said some node had
3106                                   tag toggles... */
3107 
3108       g_assert (node->level == 0);
3109 
3110       return node->children.line;
3111     }
3112   else
3113     {
3114       /* Looking for any tag at all (tag == NULL).
3115          Unfortunately this can't be done in a simple and efficient way
3116          right now; so I'm just going to return the
3117          first line in the btree. FIXME */
3118       return _gtk_text_btree_get_line (tree, 0, NULL);
3119     }
3120 }
3121 
3122 GtkTextLine*
_gtk_text_btree_last_could_contain_tag(GtkTextBTree * tree,GtkTextTag * tag)3123 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3124                                        GtkTextTag *tag)
3125 {
3126   GtkTextBTreeNode *node;
3127   GtkTextBTreeNode *last_node;
3128   GtkTextLine *line;
3129   GtkTextTagInfo *info;
3130 
3131   g_return_val_if_fail (tree != NULL, NULL);
3132 
3133   if (tag != NULL)
3134     {
3135       info = gtk_text_btree_get_existing_tag_info (tree, tag);
3136 
3137       if (info->tag_root == NULL)
3138         return NULL;
3139 
3140       node = info->tag_root;
3141       /* We know the tag root has instances of the given
3142          tag below it */
3143 
3144       while (node->level > 0)
3145         {
3146           g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3147           last_node = NULL;
3148           node = node->children.node;
3149           while (node != NULL)
3150             {
3151               if (gtk_text_btree_node_has_tag (node, tag))
3152                 last_node = node;
3153               node = node->next;
3154             }
3155 
3156           node = last_node;
3157         }
3158 
3159       g_assert (node != NULL); /* The tag summaries said some node had
3160                                   tag toggles... */
3161 
3162       g_assert (node->level == 0);
3163 
3164       /* Find the last line in this node */
3165       line = node->children.line;
3166       while (line->next != NULL)
3167         line = line->next;
3168 
3169       return line;
3170     }
3171   else
3172     {
3173       /* This search can't be done efficiently at the moment,
3174          at least not without complexity.
3175          So, we just return the last line.
3176       */
3177       return _gtk_text_btree_get_end_iter_line (tree);
3178     }
3179 }
3180 
3181 
3182 /*
3183  * Lines
3184  */
3185 
3186 gint
_gtk_text_line_get_number(GtkTextLine * line)3187 _gtk_text_line_get_number (GtkTextLine *line)
3188 {
3189   GtkTextLine *line2;
3190   GtkTextBTreeNode *node, *parent, *node2;
3191   int index;
3192 
3193   /*
3194    * First count how many lines precede this one in its level-0
3195    * GtkTextBTreeNode.
3196    */
3197 
3198   node = line->parent;
3199   index = 0;
3200   for (line2 = node->children.line; line2 != line;
3201        line2 = line2->next)
3202     {
3203       if (line2 == NULL)
3204         {
3205           g_error ("gtk_text_btree_line_number couldn't find line");
3206         }
3207       index += 1;
3208     }
3209 
3210   /*
3211    * Now work up through the levels of the tree one at a time,
3212    * counting how many lines are in GtkTextBTreeNodes preceding the current
3213    * GtkTextBTreeNode.
3214    */
3215 
3216   for (parent = node->parent ; parent != NULL;
3217        node = parent, parent = parent->parent)
3218     {
3219       for (node2 = parent->children.node; node2 != node;
3220            node2 = node2->next)
3221         {
3222           if (node2 == NULL)
3223             {
3224               g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3225             }
3226           index += node2->num_lines;
3227         }
3228     }
3229   return index;
3230 }
3231 
3232 static GtkTextLineSegment*
find_toggle_segment_before_char(GtkTextLine * line,gint char_in_line,GtkTextTag * tag)3233 find_toggle_segment_before_char (GtkTextLine *line,
3234                                  gint char_in_line,
3235                                  GtkTextTag *tag)
3236 {
3237   GtkTextLineSegment *seg;
3238   GtkTextLineSegment *toggle_seg;
3239   int index;
3240 
3241   toggle_seg = NULL;
3242   index = 0;
3243   seg = line->segments;
3244   while ( (index + seg->char_count) <= char_in_line )
3245     {
3246       if (((seg->type == &gtk_text_toggle_on_type)
3247            || (seg->type == &gtk_text_toggle_off_type))
3248           && (seg->body.toggle.info->tag == tag))
3249         toggle_seg = seg;
3250 
3251       index += seg->char_count;
3252       seg = seg->next;
3253     }
3254 
3255   return toggle_seg;
3256 }
3257 
3258 static GtkTextLineSegment*
find_toggle_segment_before_byte(GtkTextLine * line,gint byte_in_line,GtkTextTag * tag)3259 find_toggle_segment_before_byte (GtkTextLine *line,
3260                                  gint byte_in_line,
3261                                  GtkTextTag *tag)
3262 {
3263   GtkTextLineSegment *seg;
3264   GtkTextLineSegment *toggle_seg;
3265   int index;
3266 
3267   toggle_seg = NULL;
3268   index = 0;
3269   seg = line->segments;
3270   while ( (index + seg->byte_count) <= byte_in_line )
3271     {
3272       if (((seg->type == &gtk_text_toggle_on_type)
3273            || (seg->type == &gtk_text_toggle_off_type))
3274           && (seg->body.toggle.info->tag == tag))
3275         toggle_seg = seg;
3276 
3277       index += seg->byte_count;
3278       seg = seg->next;
3279     }
3280 
3281   return toggle_seg;
3282 }
3283 
3284 static gboolean
find_toggle_outside_current_line(GtkTextLine * line,GtkTextBTree * tree,GtkTextTag * tag)3285 find_toggle_outside_current_line (GtkTextLine *line,
3286                                   GtkTextBTree *tree,
3287                                   GtkTextTag *tag)
3288 {
3289   GtkTextBTreeNode *node;
3290   GtkTextLine *sibling_line;
3291   GtkTextLineSegment *seg;
3292   GtkTextLineSegment *toggle_seg;
3293   int toggles;
3294   GtkTextTagInfo *info = NULL;
3295 
3296   /*
3297    * No toggle in this line.  Look for toggles for the tag in lines
3298    * that are predecessors of line but under the same
3299    * level-0 GtkTextBTreeNode.
3300    */
3301   toggle_seg = NULL;
3302   sibling_line = line->parent->children.line;
3303   while (sibling_line != line)
3304     {
3305       seg = sibling_line->segments;
3306       while (seg != NULL)
3307         {
3308           if (((seg->type == &gtk_text_toggle_on_type)
3309                || (seg->type == &gtk_text_toggle_off_type))
3310               && (seg->body.toggle.info->tag == tag))
3311             toggle_seg = seg;
3312 
3313           seg = seg->next;
3314         }
3315 
3316       sibling_line = sibling_line->next;
3317     }
3318 
3319   if (toggle_seg != NULL)
3320     return (toggle_seg->type == &gtk_text_toggle_on_type);
3321 
3322   /*
3323    * No toggle in this GtkTextBTreeNode.  Scan upwards through the ancestors of
3324    * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3325    * siblings that precede that GtkTextBTreeNode.
3326    */
3327 
3328   info = gtk_text_btree_get_existing_tag_info (tree, tag);
3329 
3330   if (info == NULL)
3331     return FALSE;
3332 
3333   toggles = 0;
3334   node = line->parent;
3335   while (node->parent != NULL)
3336     {
3337       GtkTextBTreeNode *sibling_node;
3338 
3339       sibling_node = node->parent->children.node;
3340       while (sibling_node != node)
3341         {
3342           Summary *summary;
3343 
3344           summary = sibling_node->summary;
3345           while (summary != NULL)
3346             {
3347               if (summary->info == info)
3348                 toggles += summary->toggle_count;
3349 
3350               summary = summary->next;
3351             }
3352 
3353           sibling_node = sibling_node->next;
3354         }
3355 
3356       if (node == info->tag_root)
3357         break;
3358 
3359       node = node->parent;
3360     }
3361 
3362   /*
3363    * An odd number of toggles means that the tag is present at the
3364    * given point.
3365    */
3366 
3367   return (toggles & 1) != 0;
3368 }
3369 
3370 /* FIXME this function is far too slow, for no good reason. */
3371 gboolean
_gtk_text_line_char_has_tag(GtkTextLine * line,GtkTextBTree * tree,gint char_in_line,GtkTextTag * tag)3372 _gtk_text_line_char_has_tag (GtkTextLine *line,
3373                              GtkTextBTree *tree,
3374                              gint char_in_line,
3375                              GtkTextTag *tag)
3376 {
3377   GtkTextLineSegment *toggle_seg;
3378 
3379   g_return_val_if_fail (line != NULL, FALSE);
3380 
3381   /*
3382    * Check for toggles for the tag in the line but before
3383    * the char.  If there is one, its type indicates whether or
3384    * not the character is tagged.
3385    */
3386 
3387   toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3388 
3389   if (toggle_seg != NULL)
3390     return (toggle_seg->type == &gtk_text_toggle_on_type);
3391   else
3392     return find_toggle_outside_current_line (line, tree, tag);
3393 }
3394 
3395 gboolean
_gtk_text_line_byte_has_tag(GtkTextLine * line,GtkTextBTree * tree,gint byte_in_line,GtkTextTag * tag)3396 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3397                              GtkTextBTree *tree,
3398                              gint byte_in_line,
3399                              GtkTextTag *tag)
3400 {
3401   GtkTextLineSegment *toggle_seg;
3402 
3403   g_return_val_if_fail (line != NULL, FALSE);
3404 
3405   /*
3406    * Check for toggles for the tag in the line but before
3407    * the char.  If there is one, its type indicates whether or
3408    * not the character is tagged.
3409    */
3410 
3411   toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3412 
3413   if (toggle_seg != NULL)
3414     return (toggle_seg->type == &gtk_text_toggle_on_type);
3415   else
3416     return find_toggle_outside_current_line (line, tree, tag);
3417 }
3418 
3419 gboolean
_gtk_text_line_is_last(GtkTextLine * line,GtkTextBTree * tree)3420 _gtk_text_line_is_last (GtkTextLine *line,
3421                         GtkTextBTree *tree)
3422 {
3423   return line == get_last_line (tree);
3424 }
3425 
3426 static void
ensure_end_iter_line(GtkTextBTree * tree)3427 ensure_end_iter_line (GtkTextBTree *tree)
3428 {
3429   if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3430     {
3431       gint real_line;
3432 
3433        /* n_lines is without the magic line at the end */
3434       g_assert (_gtk_text_btree_line_count (tree) >= 1);
3435 
3436       tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3437 
3438       tree->end_iter_line_stamp = tree->chars_changed_stamp;
3439     }
3440 }
3441 
3442 static void
ensure_end_iter_segment(GtkTextBTree * tree)3443 ensure_end_iter_segment (GtkTextBTree *tree)
3444 {
3445   if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3446     {
3447       GtkTextLineSegment *seg;
3448       GtkTextLineSegment *last_with_chars;
3449 
3450       ensure_end_iter_line (tree);
3451 
3452       last_with_chars = NULL;
3453 
3454       seg = tree->end_iter_line->segments;
3455       while (seg != NULL)
3456         {
3457           if (seg->char_count > 0)
3458             last_with_chars = seg;
3459           seg = seg->next;
3460         }
3461 
3462       tree->end_iter_segment = last_with_chars;
3463 
3464       /* We know the last char in the last line is '\n' */
3465       tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3466       tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3467 
3468       tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3469 
3470       g_assert (tree->end_iter_segment->type == &gtk_text_char_type);
3471       g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3472     }
3473 }
3474 
3475 gboolean
_gtk_text_line_contains_end_iter(GtkTextLine * line,GtkTextBTree * tree)3476 _gtk_text_line_contains_end_iter (GtkTextLine  *line,
3477                                   GtkTextBTree *tree)
3478 {
3479   ensure_end_iter_line (tree);
3480 
3481   return line == tree->end_iter_line;
3482 }
3483 
3484 gboolean
_gtk_text_btree_is_end(GtkTextBTree * tree,GtkTextLine * line,GtkTextLineSegment * seg,int byte_index,int char_offset)3485 _gtk_text_btree_is_end (GtkTextBTree       *tree,
3486                         GtkTextLine        *line,
3487                         GtkTextLineSegment *seg,
3488                         int                 byte_index,
3489                         int                 char_offset)
3490 {
3491   g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3492 
3493   /* Do this first to avoid walking segments in most cases */
3494   if (!_gtk_text_line_contains_end_iter (line, tree))
3495     return FALSE;
3496 
3497   ensure_end_iter_segment (tree);
3498 
3499   if (seg != tree->end_iter_segment)
3500     return FALSE;
3501 
3502   if (byte_index >= 0)
3503     return byte_index == tree->end_iter_segment_byte_index;
3504   else
3505     return char_offset == tree->end_iter_segment_char_offset;
3506 }
3507 
3508 GtkTextLine*
_gtk_text_line_next(GtkTextLine * line)3509 _gtk_text_line_next (GtkTextLine *line)
3510 {
3511   GtkTextBTreeNode *node;
3512 
3513   if (line->next != NULL)
3514     return line->next;
3515   else
3516     {
3517       /*
3518        * This was the last line associated with the particular parent
3519        * GtkTextBTreeNode.  Search up the tree for the next GtkTextBTreeNode,
3520        * then search down from that GtkTextBTreeNode to find the first
3521        * line.
3522        */
3523 
3524       node = line->parent;
3525       while (node != NULL && node->next == NULL)
3526         node = node->parent;
3527 
3528       if (node == NULL)
3529         return NULL;
3530 
3531       node = node->next;
3532       while (node->level > 0)
3533         {
3534           node = node->children.node;
3535         }
3536 
3537       g_assert (node->children.line != line);
3538 
3539       return node->children.line;
3540     }
3541 }
3542 
3543 GtkTextLine*
_gtk_text_line_next_excluding_last(GtkTextLine * line)3544 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3545 {
3546   GtkTextLine *next;
3547 
3548   next = _gtk_text_line_next (line);
3549 
3550   /* If we were on the end iter line, we can't go to
3551    * the last line
3552    */
3553   if (next && next->next == NULL && /* these checks are optimization only */
3554       _gtk_text_line_next (next) == NULL)
3555     return NULL;
3556 
3557   return next;
3558 }
3559 
3560 GtkTextLine*
_gtk_text_line_previous(GtkTextLine * line)3561 _gtk_text_line_previous (GtkTextLine *line)
3562 {
3563   GtkTextBTreeNode *node;
3564   GtkTextBTreeNode *node2;
3565   GtkTextLine *prev;
3566 
3567   /*
3568    * Find the line under this GtkTextBTreeNode just before the starting line.
3569    */
3570   prev = line->parent->children.line;        /* First line at leaf */
3571   while (prev != line)
3572     {
3573       if (prev->next == line)
3574         return prev;
3575 
3576       prev = prev->next;
3577 
3578       if (prev == NULL)
3579         g_error ("gtk_text_btree_previous_line ran out of lines");
3580     }
3581 
3582   /*
3583    * This was the first line associated with the particular parent
3584    * GtkTextBTreeNode.  Search up the tree for the previous GtkTextBTreeNode,
3585    * then search down from that GtkTextBTreeNode to find its last line.
3586    */
3587   for (node = line->parent; ; node = node->parent)
3588     {
3589       if (node == NULL || node->parent == NULL)
3590         return NULL;
3591       else if (node != node->parent->children.node)
3592         break;
3593     }
3594 
3595   for (node2 = node->parent->children.node; ;
3596        node2 = node2->children.node)
3597     {
3598       while (node2->next != node)
3599         node2 = node2->next;
3600 
3601       if (node2->level == 0)
3602         break;
3603 
3604       node = NULL;
3605     }
3606 
3607   for (prev = node2->children.line ; ; prev = prev->next)
3608     {
3609       if (prev->next == NULL)
3610         return prev;
3611     }
3612 
3613   g_assert_not_reached ();
3614   return NULL;
3615 }
3616 
3617 
3618 GtkTextLineData*
_gtk_text_line_data_new(GtkTextLayout * layout,GtkTextLine * line)3619 _gtk_text_line_data_new (GtkTextLayout *layout,
3620                          GtkTextLine   *line)
3621 {
3622   GtkTextLineData *line_data;
3623 
3624   line_data = g_slice_new (GtkTextLineData);
3625 
3626   line_data->view_id = layout;
3627   line_data->next = NULL;
3628   line_data->width = 0;
3629   line_data->height = 0;
3630   line_data->top_ink = 0;
3631   line_data->bottom_ink = 0;
3632   line_data->valid = FALSE;
3633 
3634   return line_data;
3635 }
3636 
3637 void
_gtk_text_line_add_data(GtkTextLine * line,GtkTextLineData * data)3638 _gtk_text_line_add_data (GtkTextLine     *line,
3639                          GtkTextLineData *data)
3640 {
3641   g_return_if_fail (line != NULL);
3642   g_return_if_fail (data != NULL);
3643   g_return_if_fail (data->view_id != NULL);
3644 
3645   if (line->views)
3646     {
3647       data->next = line->views;
3648       line->views = data;
3649     }
3650   else
3651     {
3652       line->views = data;
3653     }
3654 }
3655 
3656 gpointer
_gtk_text_line_remove_data(GtkTextLine * line,gpointer view_id)3657 _gtk_text_line_remove_data (GtkTextLine *line,
3658                            gpointer view_id)
3659 {
3660   GtkTextLineData *prev;
3661   GtkTextLineData *iter;
3662 
3663   g_return_val_if_fail (line != NULL, NULL);
3664   g_return_val_if_fail (view_id != NULL, NULL);
3665 
3666   prev = NULL;
3667   iter = line->views;
3668   while (iter != NULL)
3669     {
3670       if (iter->view_id == view_id)
3671         break;
3672       prev = iter;
3673       iter = iter->next;
3674     }
3675 
3676   if (iter)
3677     {
3678       if (prev)
3679         prev->next = iter->next;
3680       else
3681         line->views = iter->next;
3682 
3683       return iter;
3684     }
3685   else
3686     return NULL;
3687 }
3688 
3689 gpointer
_gtk_text_line_get_data(GtkTextLine * line,gpointer view_id)3690 _gtk_text_line_get_data (GtkTextLine *line,
3691                          gpointer view_id)
3692 {
3693   GtkTextLineData *iter;
3694 
3695   g_return_val_if_fail (line != NULL, NULL);
3696   g_return_val_if_fail (view_id != NULL, NULL);
3697 
3698   iter = line->views;
3699   while (iter != NULL)
3700     {
3701       if (iter->view_id == view_id)
3702         break;
3703       iter = iter->next;
3704     }
3705 
3706   return iter;
3707 }
3708 
3709 void
_gtk_text_line_invalidate_wrap(GtkTextLine * line,GtkTextLineData * ld)3710 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3711                                 GtkTextLineData *ld)
3712 {
3713   /* For now this is totally unoptimized. FIXME?
3714 
3715      We could probably optimize the case where the width removed
3716      is less than the max width for the parent node,
3717      and the case where the height is unchanged when we re-wrap.
3718   */
3719 
3720   g_return_if_fail (ld != NULL);
3721 
3722   ld->valid = FALSE;
3723   gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3724 }
3725 
3726 gint
_gtk_text_line_char_count(GtkTextLine * line)3727 _gtk_text_line_char_count (GtkTextLine *line)
3728 {
3729   GtkTextLineSegment *seg;
3730   gint size;
3731 
3732   size = 0;
3733   seg = line->segments;
3734   while (seg != NULL)
3735     {
3736       size += seg->char_count;
3737       seg = seg->next;
3738     }
3739   return size;
3740 }
3741 
3742 gint
_gtk_text_line_byte_count(GtkTextLine * line)3743 _gtk_text_line_byte_count (GtkTextLine *line)
3744 {
3745   GtkTextLineSegment *seg;
3746   gint size;
3747 
3748   size = 0;
3749   seg = line->segments;
3750   while (seg != NULL)
3751     {
3752       size += seg->byte_count;
3753       seg = seg->next;
3754     }
3755 
3756   return size;
3757 }
3758 
3759 gint
_gtk_text_line_char_index(GtkTextLine * target_line)3760 _gtk_text_line_char_index (GtkTextLine *target_line)
3761 {
3762   GSList *node_stack = NULL;
3763   GtkTextBTreeNode *iter;
3764   GtkTextLine *line;
3765   gint num_chars;
3766 
3767   /* Push all our parent nodes onto a stack */
3768   iter = target_line->parent;
3769 
3770   g_assert (iter != NULL);
3771 
3772   while (iter != NULL)
3773     {
3774       node_stack = g_slist_prepend (node_stack, iter);
3775 
3776       iter = iter->parent;
3777     }
3778 
3779   /* Check that we have the root node on top of the stack. */
3780   g_assert (node_stack != NULL &&
3781             node_stack->data != NULL &&
3782             ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3783 
3784   /* Add up chars in all nodes before the nodes in our stack.
3785    */
3786 
3787   num_chars = 0;
3788   iter = node_stack->data;
3789   while (iter != NULL)
3790     {
3791       GtkTextBTreeNode *child_iter;
3792       GtkTextBTreeNode *next_node;
3793 
3794       next_node = node_stack->next ?
3795         node_stack->next->data : NULL;
3796       node_stack = g_slist_remove (node_stack, node_stack->data);
3797 
3798       if (iter->level == 0)
3799         {
3800           /* stack should be empty when we're on the last node */
3801           g_assert (node_stack == NULL);
3802           break; /* Our children are now lines */
3803         }
3804 
3805       g_assert (next_node != NULL);
3806       g_assert (iter != NULL);
3807       g_assert (next_node->parent == iter);
3808 
3809       /* Add up chars before us in the tree */
3810       child_iter = iter->children.node;
3811       while (child_iter != next_node)
3812         {
3813           g_assert (child_iter != NULL);
3814 
3815           num_chars += child_iter->num_chars;
3816 
3817           child_iter = child_iter->next;
3818         }
3819 
3820       iter = next_node;
3821     }
3822 
3823   g_assert (iter != NULL);
3824   g_assert (iter == target_line->parent);
3825 
3826   /* Since we don't store char counts in lines, only in segments, we
3827      have to iterate over the lines adding up segment char counts
3828      until we find our line.  */
3829   line = iter->children.line;
3830   while (line != target_line)
3831     {
3832       g_assert (line != NULL);
3833 
3834       num_chars += _gtk_text_line_char_count (line);
3835 
3836       line = line->next;
3837     }
3838 
3839   g_assert (line == target_line);
3840 
3841   return num_chars;
3842 }
3843 
3844 GtkTextLineSegment*
_gtk_text_line_byte_to_segment(GtkTextLine * line,gint byte_offset,gint * seg_offset)3845 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3846                                gint byte_offset,
3847                                gint *seg_offset)
3848 {
3849   GtkTextLineSegment *seg;
3850   int offset;
3851 
3852   g_return_val_if_fail (line != NULL, NULL);
3853 
3854   offset = byte_offset;
3855   seg = line->segments;
3856 
3857   while (offset >= seg->byte_count)
3858     {
3859       offset -= seg->byte_count;
3860       seg = seg->next;
3861       g_assert (seg != NULL); /* means an invalid byte index */
3862     }
3863 
3864   if (seg_offset)
3865     *seg_offset = offset;
3866 
3867   return seg;
3868 }
3869 
3870 GtkTextLineSegment*
_gtk_text_line_char_to_segment(GtkTextLine * line,gint char_offset,gint * seg_offset)3871 _gtk_text_line_char_to_segment (GtkTextLine *line,
3872                                gint char_offset,
3873                                gint *seg_offset)
3874 {
3875   GtkTextLineSegment *seg;
3876   int offset;
3877 
3878   g_return_val_if_fail (line != NULL, NULL);
3879 
3880   offset = char_offset;
3881   seg = line->segments;
3882 
3883   while (offset >= seg->char_count)
3884     {
3885       offset -= seg->char_count;
3886       seg = seg->next;
3887       g_assert (seg != NULL); /* means an invalid char index */
3888     }
3889 
3890   if (seg_offset)
3891     *seg_offset = offset;
3892 
3893   return seg;
3894 }
3895 
3896 GtkTextLineSegment*
_gtk_text_line_byte_to_any_segment(GtkTextLine * line,gint byte_offset,gint * seg_offset)3897 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3898                                    gint byte_offset,
3899                                    gint *seg_offset)
3900 {
3901   GtkTextLineSegment *seg;
3902   int offset;
3903 
3904   g_return_val_if_fail (line != NULL, NULL);
3905 
3906   offset = byte_offset;
3907   seg = line->segments;
3908 
3909   while (offset > 0 && offset >= seg->byte_count)
3910     {
3911       offset -= seg->byte_count;
3912       seg = seg->next;
3913       g_assert (seg != NULL); /* means an invalid byte index */
3914     }
3915 
3916   if (seg_offset)
3917     *seg_offset = offset;
3918 
3919   return seg;
3920 }
3921 
3922 GtkTextLineSegment*
_gtk_text_line_char_to_any_segment(GtkTextLine * line,gint char_offset,gint * seg_offset)3923 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3924                                    gint char_offset,
3925                                    gint *seg_offset)
3926 {
3927   GtkTextLineSegment *seg;
3928   int offset;
3929 
3930   g_return_val_if_fail (line != NULL, NULL);
3931 
3932   offset = char_offset;
3933   seg = line->segments;
3934 
3935   while (offset > 0 && offset >= seg->char_count)
3936     {
3937       offset -= seg->char_count;
3938       seg = seg->next;
3939       g_assert (seg != NULL); /* means an invalid byte index */
3940     }
3941 
3942   if (seg_offset)
3943     *seg_offset = offset;
3944 
3945   return seg;
3946 }
3947 
3948 gint
_gtk_text_line_byte_to_char(GtkTextLine * line,gint byte_offset)3949 _gtk_text_line_byte_to_char (GtkTextLine *line,
3950                             gint byte_offset)
3951 {
3952   gint char_offset;
3953   GtkTextLineSegment *seg;
3954 
3955   g_return_val_if_fail (line != NULL, 0);
3956   g_return_val_if_fail (byte_offset >= 0, 0);
3957 
3958   char_offset = 0;
3959   seg = line->segments;
3960   while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3961                                             the next segment) */
3962     {
3963       byte_offset -= seg->byte_count;
3964       char_offset += seg->char_count;
3965       seg = seg->next;
3966       g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3967     }
3968 
3969   g_assert (seg != NULL);
3970 
3971   /* Now byte_offset is the offset into the current segment,
3972      and char_offset is the start of the current segment.
3973      Optimize the case where no chars use > 1 byte */
3974   if (seg->byte_count == seg->char_count)
3975     return char_offset + byte_offset;
3976   else
3977     {
3978       if (seg->type == &gtk_text_char_type)
3979         return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3980       else
3981         {
3982           g_assert (seg->char_count == 1);
3983           g_assert (byte_offset == 0);
3984 
3985           return char_offset;
3986         }
3987     }
3988 }
3989 
3990 gint
_gtk_text_line_char_to_byte(GtkTextLine * line,gint char_offset)3991 _gtk_text_line_char_to_byte (GtkTextLine *line,
3992                             gint         char_offset)
3993 {
3994   g_warning ("FIXME not implemented");
3995 
3996   return 0;
3997 }
3998 
3999 /* FIXME sync with char_locate (or figure out a clean
4000    way to merge the two functions) */
4001 gboolean
_gtk_text_line_byte_locate(GtkTextLine * line,gint byte_offset,GtkTextLineSegment ** segment,GtkTextLineSegment ** any_segment,gint * seg_byte_offset,gint * line_byte_offset)4002 _gtk_text_line_byte_locate (GtkTextLine *line,
4003                             gint byte_offset,
4004                             GtkTextLineSegment **segment,
4005                             GtkTextLineSegment **any_segment,
4006                             gint *seg_byte_offset,
4007                             gint *line_byte_offset)
4008 {
4009   GtkTextLineSegment *seg;
4010   GtkTextLineSegment *after_last_indexable;
4011   GtkTextLineSegment *last_indexable;
4012   gint offset;
4013   gint bytes_in_line;
4014 
4015   g_return_val_if_fail (line != NULL, FALSE);
4016   g_return_val_if_fail (byte_offset >= 0, FALSE);
4017 
4018   *segment = NULL;
4019   *any_segment = NULL;
4020   bytes_in_line = 0;
4021 
4022   offset = byte_offset;
4023 
4024   last_indexable = NULL;
4025   after_last_indexable = line->segments;
4026   seg = line->segments;
4027 
4028   /* The loop ends when we're inside a segment;
4029      last_indexable refers to the last segment
4030      we passed entirely. */
4031   while (seg && offset >= seg->byte_count)
4032     {
4033       if (seg->char_count > 0)
4034         {
4035           offset -= seg->byte_count;
4036           bytes_in_line += seg->byte_count;
4037           last_indexable = seg;
4038           after_last_indexable = last_indexable->next;
4039         }
4040 
4041       seg = seg->next;
4042     }
4043 
4044   if (seg == NULL)
4045     {
4046       /* We went off the end of the line */
4047       if (offset != 0)
4048         g_warning ("%s: byte index off the end of the line", G_STRLOC);
4049 
4050       return FALSE;
4051     }
4052   else
4053     {
4054       *segment = seg;
4055       if (after_last_indexable != NULL)
4056         *any_segment = after_last_indexable;
4057       else
4058         *any_segment = *segment;
4059     }
4060 
4061   /* Override any_segment if we're in the middle of a segment. */
4062   if (offset > 0)
4063     *any_segment = *segment;
4064 
4065   *seg_byte_offset = offset;
4066 
4067   g_assert (*segment != NULL);
4068   g_assert (*any_segment != NULL);
4069   g_assert (*seg_byte_offset < (*segment)->byte_count);
4070 
4071   *line_byte_offset = bytes_in_line + *seg_byte_offset;
4072 
4073   return TRUE;
4074 }
4075 
4076 /* FIXME sync with byte_locate (or figure out a clean
4077    way to merge the two functions) */
4078 gboolean
_gtk_text_line_char_locate(GtkTextLine * line,gint char_offset,GtkTextLineSegment ** segment,GtkTextLineSegment ** any_segment,gint * seg_char_offset,gint * line_char_offset)4079 _gtk_text_line_char_locate     (GtkTextLine     *line,
4080                                 gint              char_offset,
4081                                 GtkTextLineSegment **segment,
4082                                 GtkTextLineSegment **any_segment,
4083                                 gint             *seg_char_offset,
4084                                 gint             *line_char_offset)
4085 {
4086   GtkTextLineSegment *seg;
4087   GtkTextLineSegment *after_last_indexable;
4088   GtkTextLineSegment *last_indexable;
4089   gint offset;
4090   gint chars_in_line;
4091 
4092   g_return_val_if_fail (line != NULL, FALSE);
4093   g_return_val_if_fail (char_offset >= 0, FALSE);
4094 
4095   *segment = NULL;
4096   *any_segment = NULL;
4097   chars_in_line = 0;
4098 
4099   offset = char_offset;
4100 
4101   last_indexable = NULL;
4102   after_last_indexable = line->segments;
4103   seg = line->segments;
4104 
4105   /* The loop ends when we're inside a segment;
4106      last_indexable refers to the last segment
4107      we passed entirely. */
4108   while (seg && offset >= seg->char_count)
4109     {
4110       if (seg->char_count > 0)
4111         {
4112           offset -= seg->char_count;
4113           chars_in_line += seg->char_count;
4114           last_indexable = seg;
4115           after_last_indexable = last_indexable->next;
4116         }
4117 
4118       seg = seg->next;
4119     }
4120 
4121   if (seg == NULL)
4122     {
4123       /* end of the line */
4124       if (offset != 0)
4125         g_warning ("%s: char offset off the end of the line", G_STRLOC);
4126 
4127       return FALSE;
4128     }
4129   else
4130     {
4131       *segment = seg;
4132       if (after_last_indexable != NULL)
4133         *any_segment = after_last_indexable;
4134       else
4135         *any_segment = *segment;
4136     }
4137 
4138   /* Override any_segment if we're in the middle of a segment. */
4139   if (offset > 0)
4140     *any_segment = *segment;
4141 
4142   *seg_char_offset = offset;
4143 
4144   g_assert (*segment != NULL);
4145   g_assert (*any_segment != NULL);
4146   g_assert (*seg_char_offset < (*segment)->char_count);
4147 
4148   *line_char_offset = chars_in_line + *seg_char_offset;
4149 
4150   return TRUE;
4151 }
4152 
4153 void
_gtk_text_line_byte_to_char_offsets(GtkTextLine * line,gint byte_offset,gint * line_char_offset,gint * seg_char_offset)4154 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4155                                     gint byte_offset,
4156                                     gint *line_char_offset,
4157                                     gint *seg_char_offset)
4158 {
4159   GtkTextLineSegment *seg;
4160   int offset;
4161 
4162   g_return_if_fail (line != NULL);
4163   g_return_if_fail (byte_offset >= 0);
4164 
4165   *line_char_offset = 0;
4166 
4167   offset = byte_offset;
4168   seg = line->segments;
4169 
4170   while (offset >= seg->byte_count)
4171     {
4172       offset -= seg->byte_count;
4173       *line_char_offset += seg->char_count;
4174       seg = seg->next;
4175       g_assert (seg != NULL); /* means an invalid char offset */
4176     }
4177 
4178   g_assert (seg->char_count > 0); /* indexable. */
4179 
4180   /* offset is now the number of bytes into the current segment we
4181    * want to go. Count chars into the current segment.
4182    */
4183 
4184   if (seg->type == &gtk_text_char_type)
4185     {
4186       *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4187 
4188       g_assert (*seg_char_offset < seg->char_count);
4189 
4190       *line_char_offset += *seg_char_offset;
4191     }
4192   else
4193     {
4194       g_assert (offset == 0);
4195       *seg_char_offset = 0;
4196     }
4197 }
4198 
4199 void
_gtk_text_line_char_to_byte_offsets(GtkTextLine * line,gint char_offset,gint * line_byte_offset,gint * seg_byte_offset)4200 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4201                                     gint char_offset,
4202                                     gint *line_byte_offset,
4203                                     gint *seg_byte_offset)
4204 {
4205   GtkTextLineSegment *seg;
4206   int offset;
4207 
4208   g_return_if_fail (line != NULL);
4209   g_return_if_fail (char_offset >= 0);
4210 
4211   *line_byte_offset = 0;
4212 
4213   offset = char_offset;
4214   seg = line->segments;
4215 
4216   while (offset >= seg->char_count)
4217     {
4218       offset -= seg->char_count;
4219       *line_byte_offset += seg->byte_count;
4220       seg = seg->next;
4221       g_assert (seg != NULL); /* means an invalid char offset */
4222     }
4223 
4224   g_assert (seg->char_count > 0); /* indexable. */
4225 
4226   /* offset is now the number of chars into the current segment we
4227      want to go. Count bytes into the current segment. */
4228 
4229   if (seg->type == &gtk_text_char_type)
4230     {
4231       const char *p;
4232 
4233       /* if in the last fourth of the segment walk backwards */
4234       if (seg->char_count - offset < seg->char_count / 4)
4235         p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
4236                                       offset - seg->char_count);
4237       else
4238         p = g_utf8_offset_to_pointer (seg->body.chars, offset);
4239 
4240       *seg_byte_offset = p - seg->body.chars;
4241 
4242       g_assert (*seg_byte_offset < seg->byte_count);
4243 
4244       *line_byte_offset += *seg_byte_offset;
4245     }
4246   else
4247     {
4248       g_assert (offset == 0);
4249       *seg_byte_offset = 0;
4250     }
4251 }
4252 
4253 static gint
node_compare(GtkTextBTreeNode * lhs,GtkTextBTreeNode * rhs)4254 node_compare (GtkTextBTreeNode *lhs,
4255               GtkTextBTreeNode *rhs)
4256 {
4257   GtkTextBTreeNode *iter;
4258   GtkTextBTreeNode *node;
4259   GtkTextBTreeNode *common_parent;
4260   GtkTextBTreeNode *parent_of_lower;
4261   GtkTextBTreeNode *parent_of_higher;
4262   gboolean lhs_is_lower;
4263   GtkTextBTreeNode *lower;
4264   GtkTextBTreeNode *higher;
4265 
4266   /* This function assumes that lhs and rhs are not underneath each
4267    * other.
4268    */
4269 
4270   if (lhs == rhs)
4271     return 0;
4272 
4273   if (lhs->level < rhs->level)
4274     {
4275       lhs_is_lower = TRUE;
4276       lower = lhs;
4277       higher = rhs;
4278     }
4279   else
4280     {
4281       lhs_is_lower = FALSE;
4282       lower = rhs;
4283       higher = lhs;
4284     }
4285 
4286   /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4287    * of the common parent we used to reach the common parent; the
4288    * ordering of these child nodes in the child list is the ordering
4289    * of lhs and rhs.
4290    */
4291 
4292   /* Get on the same level (may be on same level already) */
4293   node = lower;
4294   while (node->level < higher->level)
4295     node = node->parent;
4296 
4297   g_assert (node->level == higher->level);
4298 
4299   g_assert (node != higher); /* Happens if lower is underneath higher */
4300 
4301   /* Go up until we have two children with a common parent.
4302    */
4303   parent_of_lower = node;
4304   parent_of_higher = higher;
4305 
4306   while (parent_of_lower->parent != parent_of_higher->parent)
4307     {
4308       parent_of_lower = parent_of_lower->parent;
4309       parent_of_higher = parent_of_higher->parent;
4310     }
4311 
4312   g_assert (parent_of_lower->parent == parent_of_higher->parent);
4313 
4314   common_parent = parent_of_lower->parent;
4315 
4316   g_assert (common_parent != NULL);
4317 
4318   /* See which is first in the list of common_parent's children */
4319   iter = common_parent->children.node;
4320   while (iter != NULL)
4321     {
4322       if (iter == parent_of_higher)
4323         {
4324           /* higher is less than lower */
4325 
4326           if (lhs_is_lower)
4327             return 1; /* lhs > rhs */
4328           else
4329             return -1;
4330         }
4331       else if (iter == parent_of_lower)
4332         {
4333           /* lower is less than higher */
4334 
4335           if (lhs_is_lower)
4336             return -1; /* lhs < rhs */
4337           else
4338             return 1;
4339         }
4340 
4341       iter = iter->next;
4342     }
4343 
4344   g_assert_not_reached ();
4345   return 0;
4346 }
4347 
4348 /* remember that tag == NULL means "any tag" */
4349 GtkTextLine*
_gtk_text_line_next_could_contain_tag(GtkTextLine * line,GtkTextBTree * tree,GtkTextTag * tag)4350 _gtk_text_line_next_could_contain_tag (GtkTextLine  *line,
4351                                        GtkTextBTree *tree,
4352                                        GtkTextTag   *tag)
4353 {
4354   GtkTextBTreeNode *node;
4355   GtkTextTagInfo *info;
4356   gboolean below_tag_root;
4357 
4358   g_return_val_if_fail (line != NULL, NULL);
4359 
4360 #ifdef G_ENABLE_DEBUG
4361   if (GTK_DEBUG_CHECK (TEXT))
4362     _gtk_text_btree_check (tree);
4363 #endif
4364 
4365   if (tag == NULL)
4366     {
4367       /* Right now we can only offer linear-search if the user wants
4368        * to know about any tag toggle at all.
4369        */
4370       return _gtk_text_line_next_excluding_last (line);
4371     }
4372 
4373   /* Our tag summaries only have node precision, not line
4374    * precision. This means that if any line under a node could contain a
4375    * tag, then any of the others could also contain a tag.
4376    *
4377    * In the future we could have some mechanism to keep track of how
4378    * many toggles we've found under a node so far, since we have a
4379    * count of toggles under the node. But for now I'm going with KISS.
4380    */
4381 
4382   /* return same-node line, if any. */
4383   if (line->next)
4384     return line->next;
4385 
4386   info = gtk_text_btree_get_existing_tag_info (tree, tag);
4387   if (info == NULL)
4388     return NULL;
4389 
4390   if (info->tag_root == NULL)
4391     return NULL;
4392 
4393   if (info->tag_root == line->parent)
4394     return NULL; /* we were at the last line under the tag root */
4395 
4396   /* We need to go up out of this node, and on to the next one with
4397      toggles for the target tag. If we're below the tag root, we need to
4398      find the next node below the tag root that has tag summaries. If
4399      we're not below the tag root, we need to see if the tag root is
4400      after us in the tree, and if so, return the first line underneath
4401      the tag root. */
4402 
4403   node = line->parent;
4404   below_tag_root = FALSE;
4405   while (node != NULL)
4406     {
4407       if (node == info->tag_root)
4408         {
4409           below_tag_root = TRUE;
4410           break;
4411         }
4412 
4413       node = node->parent;
4414     }
4415 
4416   if (below_tag_root)
4417     {
4418       node = line->parent;
4419       while (node != info->tag_root)
4420         {
4421           if (node->next == NULL)
4422             node = node->parent;
4423           else
4424             {
4425               node = node->next;
4426 
4427               if (gtk_text_btree_node_has_tag (node, tag))
4428                 goto found;
4429             }
4430         }
4431       return NULL;
4432     }
4433   else
4434     {
4435       gint ordering;
4436 
4437       ordering = node_compare (line->parent, info->tag_root);
4438 
4439       if (ordering < 0)
4440         {
4441           /* Tag root is ahead of us, so search there. */
4442           node = info->tag_root;
4443           goto found;
4444         }
4445       else
4446         {
4447           /* Tag root is after us, so no more lines that
4448            * could contain the tag.
4449            */
4450           return NULL;
4451         }
4452 
4453       g_assert_not_reached ();
4454     }
4455 
4456  found:
4457 
4458   g_assert (node != NULL);
4459 
4460   /* We have to find the first sub-node of this node that contains
4461    * the target tag.
4462    */
4463 
4464   while (node->level > 0)
4465     {
4466       g_assert (node != NULL); /* If this fails, it likely means an
4467                                   incorrect tag summary led us on a
4468                                   wild goose chase down this branch of
4469                                   the tree. */
4470       node = node->children.node;
4471       while (node != NULL)
4472         {
4473           if (gtk_text_btree_node_has_tag (node, tag))
4474             break;
4475           node = node->next;
4476         }
4477     }
4478 
4479   g_assert (node != NULL);
4480   g_assert (node->level == 0);
4481 
4482   return node->children.line;
4483 }
4484 
4485 static GtkTextLine*
prev_line_under_node(GtkTextBTreeNode * node,GtkTextLine * line)4486 prev_line_under_node (GtkTextBTreeNode *node,
4487                       GtkTextLine      *line)
4488 {
4489   GtkTextLine *prev;
4490 
4491   prev = node->children.line;
4492 
4493   g_assert (prev);
4494 
4495   if (prev != line)
4496     {
4497       while (prev->next != line)
4498         prev = prev->next;
4499 
4500       return prev;
4501     }
4502 
4503   return NULL;
4504 }
4505 
4506 GtkTextLine*
_gtk_text_line_previous_could_contain_tag(GtkTextLine * line,GtkTextBTree * tree,GtkTextTag * tag)4507 _gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
4508                                           GtkTextBTree *tree,
4509                                           GtkTextTag   *tag)
4510 {
4511   GtkTextBTreeNode *node;
4512   GtkTextBTreeNode *found_node = NULL;
4513   GtkTextTagInfo *info;
4514   gboolean below_tag_root;
4515   GtkTextLine *prev;
4516   GtkTextBTreeNode *line_ancestor;
4517   GtkTextBTreeNode *line_ancestor_parent;
4518 
4519   /* See next_could_contain_tag () for more extensive comments
4520    * on what's going on here.
4521    */
4522 
4523   g_return_val_if_fail (line != NULL, NULL);
4524 
4525 #ifdef G_ENABLE_DEBUG
4526   if (GTK_DEBUG_CHECK (TEXT))
4527     _gtk_text_btree_check (tree);
4528 #endif
4529 
4530   if (tag == NULL)
4531     {
4532       /* Right now we can only offer linear-search if the user wants
4533        * to know about any tag toggle at all.
4534        */
4535       return _gtk_text_line_previous (line);
4536     }
4537 
4538   /* Return same-node line, if any. */
4539   prev = prev_line_under_node (line->parent, line);
4540   if (prev)
4541     return prev;
4542 
4543   info = gtk_text_btree_get_existing_tag_info (tree, tag);
4544   if (info == NULL)
4545     return NULL;
4546 
4547   if (info->tag_root == NULL)
4548     return NULL;
4549 
4550   if (info->tag_root == line->parent)
4551     return NULL; /* we were at the first line under the tag root */
4552 
4553   /* Are we below the tag root */
4554   node = line->parent;
4555   below_tag_root = FALSE;
4556   while (node != NULL)
4557     {
4558       if (node == info->tag_root)
4559         {
4560           below_tag_root = TRUE;
4561           break;
4562         }
4563 
4564       node = node->parent;
4565     }
4566 
4567   if (below_tag_root)
4568     {
4569       /* Look for a previous node under this tag root that has our
4570        * tag.
4571        */
4572 
4573       /* this assertion holds because line->parent is not the
4574        * tag root, we are below the tag root, and the tag
4575        * root exists.
4576        */
4577       g_assert (line->parent->parent != NULL);
4578 
4579       line_ancestor = line->parent;
4580       line_ancestor_parent = line->parent->parent;
4581 
4582       while (line_ancestor != info->tag_root)
4583         {
4584           GSList *child_nodes = NULL;
4585           GSList *tmp;
4586 
4587           /* Create reverse-order list of nodes before
4588            * line_ancestor
4589            */
4590           if (line_ancestor_parent != NULL)
4591 	    node = line_ancestor_parent->children.node;
4592 	  else
4593 	    node = line_ancestor;
4594 
4595           while (node != line_ancestor && node != NULL)
4596             {
4597               child_nodes = g_slist_prepend (child_nodes, node);
4598 
4599               node = node->next;
4600             }
4601 
4602           /* Try to find a node with our tag on it in the list */
4603           tmp = child_nodes;
4604           while (tmp != NULL)
4605             {
4606               GtkTextBTreeNode *this_node = tmp->data;
4607 
4608               g_assert (this_node != line_ancestor);
4609 
4610               if (gtk_text_btree_node_has_tag (this_node, tag))
4611                 {
4612                   found_node = this_node;
4613                   g_slist_free (child_nodes);
4614                   goto found;
4615                 }
4616 
4617               tmp = tmp->next;
4618             }
4619 
4620           g_slist_free (child_nodes);
4621 
4622           /* Didn't find anything on this level; go up one level. */
4623           line_ancestor = line_ancestor_parent;
4624           line_ancestor_parent = line_ancestor->parent;
4625         }
4626 
4627       /* No dice. */
4628       return NULL;
4629     }
4630   else
4631     {
4632       gint ordering;
4633 
4634       ordering = node_compare (line->parent, info->tag_root);
4635 
4636       if (ordering < 0)
4637         {
4638           /* Tag root is ahead of us, so no more lines
4639            * with this tag.
4640            */
4641           return NULL;
4642         }
4643       else
4644         {
4645           /* Tag root is after us, so grab last tagged
4646            * line underneath the tag root.
4647            */
4648           found_node = info->tag_root;
4649           goto found;
4650         }
4651 
4652       g_assert_not_reached ();
4653     }
4654 
4655  found:
4656 
4657   g_assert (found_node != NULL);
4658 
4659   /* We have to find the last sub-node of this node that contains
4660    * the target tag.
4661    */
4662   node = found_node;
4663 
4664   while (node->level > 0)
4665     {
4666       GSList *child_nodes = NULL;
4667       GSList *iter;
4668       g_assert (node != NULL); /* If this fails, it likely means an
4669                                   incorrect tag summary led us on a
4670                                   wild goose chase down this branch of
4671                                   the tree. */
4672 
4673       node = node->children.node;
4674       while (node != NULL)
4675         {
4676           child_nodes = g_slist_prepend (child_nodes, node);
4677           node = node->next;
4678         }
4679 
4680       node = NULL; /* detect failure to find a child node. */
4681 
4682       iter = child_nodes;
4683       while (iter != NULL)
4684         {
4685           if (gtk_text_btree_node_has_tag (iter->data, tag))
4686             {
4687               /* recurse into this node. */
4688               node = iter->data;
4689               break;
4690             }
4691 
4692           iter = iter->next;
4693         }
4694 
4695       g_slist_free (child_nodes);
4696 
4697       g_assert (node != NULL);
4698     }
4699 
4700   g_assert (node != NULL);
4701   g_assert (node->level == 0);
4702 
4703   /* this assertion is correct, but slow. */
4704   /* g_assert (node_compare (node, line->parent) < 0); */
4705 
4706   /* Return last line in this node. */
4707 
4708   prev = node->children.line;
4709   while (prev->next)
4710     prev = prev->next;
4711 
4712   return prev;
4713 }
4714 
4715 /*
4716  * Non-public function implementations
4717  */
4718 
4719 static void
summary_list_destroy(Summary * summary)4720 summary_list_destroy (Summary *summary)
4721 {
4722   g_slice_free_chain (Summary, summary, next);
4723 }
4724 
4725 static GtkTextLine*
get_last_line(GtkTextBTree * tree)4726 get_last_line (GtkTextBTree *tree)
4727 {
4728   if (tree->last_line_stamp != tree->chars_changed_stamp)
4729     {
4730       gint n_lines;
4731       GtkTextLine *line;
4732       gint real_line;
4733 
4734       n_lines = _gtk_text_btree_line_count (tree);
4735 
4736       g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4737 
4738       line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4739 
4740       tree->last_line_stamp = tree->chars_changed_stamp;
4741       tree->last_line = line;
4742     }
4743 
4744   return tree->last_line;
4745 }
4746 
4747 /*
4748  * Lines
4749  */
4750 
4751 static GtkTextLine*
gtk_text_line_new(void)4752 gtk_text_line_new (void)
4753 {
4754   GtkTextLine *line;
4755 
4756   line = g_slice_new0 (GtkTextLine);
4757   line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4758   line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4759   line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4760 
4761   return line;
4762 }
4763 
4764 static void
gtk_text_line_destroy(GtkTextBTree * tree,GtkTextLine * line)4765 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4766 {
4767   GtkTextLineData *ld;
4768   GtkTextLineData *next;
4769 
4770   g_return_if_fail (line != NULL);
4771 
4772   ld = line->views;
4773   while (ld != NULL)
4774     {
4775       BTreeView *view;
4776 
4777       view = gtk_text_btree_get_view (tree, ld->view_id);
4778 
4779       g_assert (view != NULL);
4780 
4781       next = ld->next;
4782       gtk_text_layout_free_line_data (view->layout, line, ld);
4783 
4784       ld = next;
4785     }
4786 
4787   g_slice_free (GtkTextLine, line);
4788 }
4789 
4790 static void
gtk_text_line_set_parent(GtkTextLine * line,GtkTextBTreeNode * node)4791 gtk_text_line_set_parent (GtkTextLine *line,
4792                           GtkTextBTreeNode *node)
4793 {
4794   if (line->parent == node)
4795     return;
4796   line->parent = node;
4797   gtk_text_btree_node_invalidate_upward (node, NULL);
4798 }
4799 
4800 static void
cleanup_line(GtkTextLine * line)4801 cleanup_line (GtkTextLine *line)
4802 {
4803   GtkTextLineSegment *seg, **prev_p;
4804   gboolean changed;
4805 
4806   /*
4807    * Make a pass over all of the segments in the line, giving each
4808    * a chance to clean itself up.  This could potentially change
4809    * the structure of the line, e.g. by merging two segments
4810    * together or having two segments cancel themselves;  if so,
4811    * then repeat the whole process again, since the first structure
4812    * change might make other structure changes possible.  Repeat
4813    * until eventually there are no changes.
4814    */
4815 
4816   changed = TRUE;
4817   while (changed)
4818     {
4819       changed = FALSE;
4820       prev_p = &line->segments;
4821       for (seg = *prev_p; seg != NULL; seg = *prev_p)
4822         {
4823           if (seg->type->cleanupFunc != NULL)
4824             {
4825               *prev_p = (*seg->type->cleanupFunc)(seg, line);
4826               if (seg != *prev_p)
4827 		{
4828 		  changed = TRUE;
4829 		  continue;
4830 		}
4831             }
4832 
4833 	  prev_p = &(*prev_p)->next;
4834         }
4835     }
4836 }
4837 
4838 /*
4839  * Nodes
4840  */
4841 
4842 static NodeData*
node_data_new(gpointer view_id)4843 node_data_new (gpointer view_id)
4844 {
4845   NodeData *nd;
4846 
4847   nd = g_slice_new (NodeData);
4848 
4849   nd->view_id = view_id;
4850   nd->next = NULL;
4851   nd->width = 0;
4852   nd->height = 0;
4853   nd->valid = FALSE;
4854 
4855   return nd;
4856 }
4857 
4858 static void
node_data_destroy(NodeData * nd)4859 node_data_destroy (NodeData *nd)
4860 {
4861   g_slice_free (NodeData, nd);
4862 }
4863 
4864 static void
node_data_list_destroy(NodeData * nd)4865 node_data_list_destroy (NodeData *nd)
4866 {
4867   g_slice_free_chain (NodeData, nd, next);
4868 }
4869 
4870 static NodeData*
node_data_find(NodeData * nd,gpointer view_id)4871 node_data_find (NodeData *nd,
4872 		gpointer  view_id)
4873 {
4874   while (nd != NULL)
4875     {
4876       if (nd->view_id == view_id)
4877         break;
4878       nd = nd->next;
4879     }
4880   return nd;
4881 }
4882 
4883 static void
summary_destroy(Summary * summary)4884 summary_destroy (Summary *summary)
4885 {
4886   /* Fill with error-triggering garbage */
4887   summary->info = (void*)0x1;
4888   summary->toggle_count = 567;
4889   summary->next = (void*)0x1;
4890   g_slice_free (Summary, summary);
4891 }
4892 
4893 static GtkTextBTreeNode*
gtk_text_btree_node_new(void)4894 gtk_text_btree_node_new (void)
4895 {
4896   GtkTextBTreeNode *node;
4897 
4898   node = g_slice_new (GtkTextBTreeNode);
4899 
4900   node->node_data = NULL;
4901 
4902   return node;
4903 }
4904 
4905 static void
gtk_text_btree_node_adjust_toggle_count(GtkTextBTreeNode * node,GtkTextTagInfo * info,gint adjust)4906 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode  *node,
4907                                          GtkTextTagInfo  *info,
4908                                          gint adjust)
4909 {
4910   Summary *summary;
4911 
4912   summary = node->summary;
4913   while (summary != NULL)
4914     {
4915       if (summary->info == info)
4916         {
4917           summary->toggle_count += adjust;
4918           break;
4919         }
4920 
4921       summary = summary->next;
4922     }
4923 
4924   if (summary == NULL)
4925     {
4926       /* didn't find a summary for our tag. */
4927       g_return_if_fail (adjust > 0);
4928       summary = g_slice_new (Summary);
4929       summary->info = info;
4930       summary->toggle_count = adjust;
4931       summary->next = node->summary;
4932       node->summary = summary;
4933     }
4934 }
4935 
4936 /* Note that the tag root and above do not have summaries
4937    for the tag; only nodes below the tag root have
4938    the summaries. */
4939 static gboolean
gtk_text_btree_node_has_tag(GtkTextBTreeNode * node,GtkTextTag * tag)4940 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4941 {
4942   Summary *summary;
4943 
4944   summary = node->summary;
4945   while (summary != NULL)
4946     {
4947       if (tag == NULL ||
4948           summary->info->tag == tag)
4949         return TRUE;
4950 
4951       summary = summary->next;
4952     }
4953 
4954   return FALSE;
4955 }
4956 
4957 /* Add node and all children to the damage region. */
4958 #if 0
4959 static void
4960 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4961 {
4962   NodeData *nd;
4963 
4964   nd = node->node_data;
4965   while (nd != NULL)
4966     {
4967       nd->valid = FALSE;
4968       nd = nd->next;
4969     }
4970 
4971   if (node->level == 0)
4972     {
4973       GtkTextLine *line;
4974 
4975       line = node->children.line;
4976       while (line != NULL)
4977         {
4978           GtkTextLineData *ld;
4979 
4980           ld = line->views;
4981           while (ld != NULL)
4982             {
4983               ld->valid = FALSE;
4984               ld = ld->next;
4985             }
4986 
4987           line = line->next;
4988         }
4989     }
4990   else
4991     {
4992       GtkTextBTreeNode *child;
4993 
4994       child = node->children.node;
4995 
4996       while (child != NULL)
4997         {
4998           gtk_text_btree_node_invalidate_downward (child);
4999 
5000           child = child->next;
5001         }
5002     }
5003 }
5004 #endif
5005 
5006 static void
gtk_text_btree_node_invalidate_upward(GtkTextBTreeNode * node,gpointer view_id)5007 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
5008 {
5009   GtkTextBTreeNode *iter;
5010 
5011   iter = node;
5012   while (iter != NULL)
5013     {
5014       NodeData *nd;
5015 
5016       if (view_id)
5017         {
5018           nd = node_data_find (iter->node_data, view_id);
5019 
5020           if (nd == NULL || !nd->valid)
5021             break; /* Once a node is invalid, we know its parents are as well. */
5022 
5023           nd->valid = FALSE;
5024         }
5025       else
5026         {
5027           gboolean should_continue = FALSE;
5028 
5029           nd = iter->node_data;
5030           while (nd != NULL)
5031             {
5032               if (nd->valid)
5033                 {
5034                   should_continue = TRUE;
5035                   nd->valid = FALSE;
5036                 }
5037 
5038               nd = nd->next;
5039             }
5040 
5041           if (!should_continue)
5042             break; /* This node was totally invalidated, so are its
5043                       parents */
5044         }
5045 
5046       iter = iter->parent;
5047     }
5048 }
5049 
5050 
5051 /**
5052  * _gtk_text_btree_is_valid:
5053  * @tree: a #GtkTextBTree
5054  * @view_id: ID for the view
5055  *
5056  * Check to see if the entire #GtkTextBTree is valid or not for
5057  * the given view.
5058  *
5059  * Returns: %TRUE if the entire #GtkTextBTree is valid
5060  **/
5061 gboolean
_gtk_text_btree_is_valid(GtkTextBTree * tree,gpointer view_id)5062 _gtk_text_btree_is_valid (GtkTextBTree *tree,
5063                          gpointer      view_id)
5064 {
5065   NodeData *nd;
5066   g_return_val_if_fail (tree != NULL, FALSE);
5067 
5068   nd = node_data_find (tree->root_node->node_data, view_id);
5069   return (nd && nd->valid);
5070 }
5071 
5072 typedef struct _ValidateState ValidateState;
5073 
5074 struct _ValidateState
5075 {
5076   gint remaining_pixels;
5077   gboolean in_validation;
5078   gint y;
5079   gint old_height;
5080   gint new_height;
5081 };
5082 
5083 static void
gtk_text_btree_node_validate(BTreeView * view,GtkTextBTreeNode * node,gpointer view_id,ValidateState * state)5084 gtk_text_btree_node_validate (BTreeView         *view,
5085                               GtkTextBTreeNode  *node,
5086                               gpointer           view_id,
5087                               ValidateState     *state)
5088 {
5089   gint node_valid = TRUE;
5090   gint node_width = 0;
5091   gint node_height = 0;
5092 
5093   NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5094   g_return_if_fail (!nd->valid);
5095 
5096   if (node->level == 0)
5097     {
5098       GtkTextLine *line = node->children.line;
5099       GtkTextLineData *ld;
5100 
5101       /* Iterate over leading valid lines */
5102       while (line != NULL)
5103         {
5104           ld = _gtk_text_line_get_data (line, view_id);
5105 
5106           if (!ld || !ld->valid)
5107             break;
5108           else if (state->in_validation)
5109             {
5110               state->in_validation = FALSE;
5111               return;
5112             }
5113           else
5114             {
5115               state->y += ld->height;
5116               node_width = MAX (ld->width, node_width);
5117               node_height += ld->height;
5118             }
5119 
5120           line = line->next;
5121         }
5122 
5123       state->in_validation = TRUE;
5124 
5125       /* Iterate over invalid lines */
5126       while (line != NULL)
5127         {
5128           ld = _gtk_text_line_get_data (line, view_id);
5129 
5130           if (ld && ld->valid)
5131             break;
5132           else
5133             {
5134               if (ld)
5135                 state->old_height += ld->height;
5136               ld = gtk_text_layout_wrap (view->layout, line, ld);
5137               state->new_height += ld->height;
5138 
5139               node_width = MAX (ld->width, node_width);
5140               node_height += ld->height;
5141 
5142               state->remaining_pixels -= ld->height;
5143               if (state->remaining_pixels <= 0)
5144                 {
5145                   line = line->next;
5146                   break;
5147                 }
5148             }
5149 
5150           line = line->next;
5151         }
5152 
5153       /* Iterate over the remaining lines */
5154       while (line != NULL)
5155         {
5156           ld = _gtk_text_line_get_data (line, view_id);
5157           state->in_validation = FALSE;
5158 
5159           if (!ld || !ld->valid)
5160             node_valid = FALSE;
5161 
5162           if (ld)
5163             {
5164               node_width = MAX (ld->width, node_width);
5165               node_height += ld->height;
5166             }
5167 
5168           line = line->next;
5169         }
5170     }
5171   else
5172     {
5173       GtkTextBTreeNode *child;
5174       NodeData *child_nd;
5175 
5176       child = node->children.node;
5177 
5178       /* Iterate over leading valid nodes */
5179       while (child)
5180         {
5181           child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5182 
5183           if (!child_nd->valid)
5184             break;
5185           else if (state->in_validation)
5186             {
5187               state->in_validation = FALSE;
5188               return;
5189             }
5190           else
5191             {
5192               state->y += child_nd->height;
5193               node_width = MAX (node_width, child_nd->width);
5194               node_height += child_nd->height;
5195             }
5196 
5197           child = child->next;
5198         }
5199 
5200       /* Iterate over invalid nodes */
5201       while (child)
5202         {
5203           child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5204 
5205           if (child_nd->valid)
5206             break;
5207           else
5208             {
5209               gtk_text_btree_node_validate (view, child, view_id, state);
5210 
5211               if (!child_nd->valid)
5212                 node_valid = FALSE;
5213               node_width = MAX (node_width, child_nd->width);
5214               node_height += child_nd->height;
5215 
5216               if (!state->in_validation || state->remaining_pixels <= 0)
5217                 {
5218                   child = child->next;
5219                   break;
5220                 }
5221             }
5222 
5223           child = child->next;
5224         }
5225 
5226       /* Iterate over the remaining lines */
5227       while (child)
5228         {
5229           child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5230           state->in_validation = FALSE;
5231 
5232           if (!child_nd->valid)
5233             node_valid = FALSE;
5234 
5235           node_width = MAX (child_nd->width, node_width);
5236           node_height += child_nd->height;
5237 
5238           child = child->next;
5239         }
5240     }
5241 
5242   nd->width = node_width;
5243   nd->height = node_height;
5244   nd->valid = node_valid;
5245 }
5246 
5247 /**
5248  * _gtk_text_btree_validate:
5249  * @tree: a #GtkTextBTree
5250  * @view_id: view id
5251  * @max_pixels: the maximum number of pixels to validate. (No more
5252  *              than one paragraph beyond this limit will be validated)
5253  * @y: location to store starting y coordinate of validated region
5254  * @old_height: location to store old height of validated region
5255  * @new_height: location to store new height of validated region
5256  *
5257  * Validate a single contiguous invalid region of a #GtkTextBTree for
5258  * a given view.
5259  *
5260  * Returns: %TRUE if a region has been validated, %FALSE if the
5261  * entire tree was already valid.
5262  **/
5263 gboolean
_gtk_text_btree_validate(GtkTextBTree * tree,gpointer view_id,gint max_pixels,gint * y,gint * old_height,gint * new_height)5264 _gtk_text_btree_validate (GtkTextBTree *tree,
5265                          gpointer      view_id,
5266                          gint          max_pixels,
5267                          gint         *y,
5268                          gint         *old_height,
5269                          gint         *new_height)
5270 {
5271   BTreeView *view;
5272 
5273   g_return_val_if_fail (tree != NULL, FALSE);
5274 
5275   view = gtk_text_btree_get_view (tree, view_id);
5276   g_return_val_if_fail (view != NULL, FALSE);
5277 
5278   if (!_gtk_text_btree_is_valid (tree, view_id))
5279     {
5280       ValidateState state;
5281 
5282       state.remaining_pixels = max_pixels;
5283       state.in_validation = FALSE;
5284       state.y = 0;
5285       state.old_height = 0;
5286       state.new_height = 0;
5287 
5288       gtk_text_btree_node_validate (view,
5289                                     tree->root_node,
5290                                     view_id, &state);
5291 
5292       if (y)
5293         *y = state.y;
5294       if (old_height)
5295         *old_height = state.old_height;
5296       if (new_height)
5297         *new_height = state.new_height;
5298 
5299 #ifdef G_ENABLE_DEBUG
5300       if (GTK_DEBUG_CHECK (TEXT))
5301         _gtk_text_btree_check (tree);
5302 #endif
5303 
5304       return TRUE;
5305     }
5306   else
5307     return FALSE;
5308 }
5309 
5310 static void
gtk_text_btree_node_compute_view_aggregates(GtkTextBTreeNode * node,gpointer view_id,gint * width_out,gint * height_out,gboolean * valid_out)5311 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5312                                              gpointer          view_id,
5313                                              gint             *width_out,
5314                                              gint             *height_out,
5315                                              gboolean         *valid_out)
5316 {
5317   gint width = 0;
5318   gint height = 0;
5319   gboolean valid = TRUE;
5320 
5321   if (node->level == 0)
5322     {
5323       GtkTextLine *line = node->children.line;
5324 
5325       while (line != NULL)
5326         {
5327           GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5328 
5329           if (!ld || !ld->valid)
5330             valid = FALSE;
5331 
5332           if (ld)
5333             {
5334               width = MAX (ld->width, width);
5335               height += ld->height;
5336             }
5337 
5338           line = line->next;
5339         }
5340     }
5341   else
5342     {
5343       GtkTextBTreeNode *child = node->children.node;
5344 
5345       while (child)
5346         {
5347           NodeData *child_nd = node_data_find (child->node_data, view_id);
5348 
5349           if (!child_nd || !child_nd->valid)
5350             valid = FALSE;
5351 
5352           if (child_nd)
5353             {
5354               width = MAX (child_nd->width, width);
5355               height += child_nd->height;
5356             }
5357 
5358           child = child->next;
5359         }
5360     }
5361 
5362   *width_out = width;
5363   *height_out = height;
5364   *valid_out = valid;
5365 }
5366 
5367 
5368 /* Recompute the validity and size of the view data for a given
5369  * view at this node from the immediate children of the node
5370  */
5371 static NodeData *
gtk_text_btree_node_check_valid(GtkTextBTreeNode * node,gpointer view_id)5372 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5373                                  gpointer          view_id)
5374 {
5375   NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5376   gboolean valid;
5377   gint width;
5378   gint height;
5379 
5380   gtk_text_btree_node_compute_view_aggregates (node, view_id,
5381                                                &width, &height, &valid);
5382   nd->width = width;
5383   nd->height = height;
5384   nd->valid = valid;
5385 
5386   return nd;
5387 }
5388 
5389 static void
gtk_text_btree_node_check_valid_upward(GtkTextBTreeNode * node,gpointer view_id)5390 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5391                                         gpointer          view_id)
5392 {
5393   while (node)
5394     {
5395       gtk_text_btree_node_check_valid (node, view_id);
5396       node = node->parent;
5397     }
5398 }
5399 
5400 static NodeData *
gtk_text_btree_node_check_valid_downward(GtkTextBTreeNode * node,gpointer view_id)5401 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5402                                           gpointer          view_id)
5403 {
5404   if (node->level == 0)
5405     {
5406       return gtk_text_btree_node_check_valid (node, view_id);
5407     }
5408   else
5409     {
5410       GtkTextBTreeNode *child = node->children.node;
5411 
5412       NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5413 
5414       nd->valid = TRUE;
5415       nd->width = 0;
5416       nd->height = 0;
5417 
5418       while (child)
5419         {
5420           NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5421 
5422           if (!child_nd->valid)
5423             nd->valid = FALSE;
5424           nd->width = MAX (child_nd->width, nd->width);
5425           nd->height += child_nd->height;
5426 
5427           child = child->next;
5428         }
5429       return nd;
5430     }
5431 }
5432 
5433 
5434 
5435 /**
5436  * _gtk_text_btree_validate_line:
5437  * @tree: a #GtkTextBTree
5438  * @line: line to validate
5439  * @view_id: view ID for the view to validate
5440  *
5441  * Revalidate a single line of the btree for the given view, propagate
5442  * results up through the entire tree.
5443  **/
5444 void
_gtk_text_btree_validate_line(GtkTextBTree * tree,GtkTextLine * line,gpointer view_id)5445 _gtk_text_btree_validate_line (GtkTextBTree     *tree,
5446                                GtkTextLine      *line,
5447                                gpointer          view_id)
5448 {
5449   GtkTextLineData *ld;
5450   BTreeView *view;
5451 
5452   g_return_if_fail (tree != NULL);
5453   g_return_if_fail (line != NULL);
5454 
5455   view = gtk_text_btree_get_view (tree, view_id);
5456   g_return_if_fail (view != NULL);
5457 
5458   ld = _gtk_text_line_get_data (line, view_id);
5459   if (!ld || !ld->valid)
5460     {
5461       ld = gtk_text_layout_wrap (view->layout, line, ld);
5462 
5463       gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5464     }
5465 }
5466 
5467 static void
gtk_text_btree_node_remove_view(BTreeView * view,GtkTextBTreeNode * node,gpointer view_id)5468 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5469 {
5470   if (node->level == 0)
5471     {
5472       GtkTextLine *line;
5473 
5474       line = node->children.line;
5475       while (line != NULL)
5476         {
5477           GtkTextLineData *ld;
5478 
5479           ld = _gtk_text_line_remove_data (line, view_id);
5480 
5481           if (ld)
5482             gtk_text_layout_free_line_data (view->layout, line, ld);
5483 
5484           line = line->next;
5485         }
5486     }
5487   else
5488     {
5489       GtkTextBTreeNode *child;
5490 
5491       child = node->children.node;
5492 
5493       while (child != NULL)
5494         {
5495           /* recurse */
5496           gtk_text_btree_node_remove_view (view, child, view_id);
5497 
5498           child = child->next;
5499         }
5500     }
5501 
5502   gtk_text_btree_node_remove_data (node, view_id);
5503 }
5504 
5505 static void
gtk_text_btree_node_destroy(GtkTextBTree * tree,GtkTextBTreeNode * node)5506 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5507 {
5508   if (node->level == 0)
5509     {
5510       GtkTextLine *line;
5511       GtkTextLineSegment *seg;
5512 
5513       while (node->children.line != NULL)
5514         {
5515           line = node->children.line;
5516           node->children.line = line->next;
5517           while (line->segments != NULL)
5518             {
5519               seg = line->segments;
5520               line->segments = seg->next;
5521 
5522               (*seg->type->deleteFunc) (seg, line, TRUE);
5523             }
5524           gtk_text_line_destroy (tree, line);
5525         }
5526     }
5527   else
5528     {
5529       GtkTextBTreeNode *childPtr;
5530 
5531       while (node->children.node != NULL)
5532         {
5533           childPtr = node->children.node;
5534           node->children.node = childPtr->next;
5535           gtk_text_btree_node_destroy (tree, childPtr);
5536         }
5537     }
5538 
5539   gtk_text_btree_node_free_empty (tree, node);
5540 }
5541 
5542 static void
gtk_text_btree_node_free_empty(GtkTextBTree * tree,GtkTextBTreeNode * node)5543 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5544                                 GtkTextBTreeNode *node)
5545 {
5546   g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5547                     (node->level == 0 && node->children.line == NULL));
5548 
5549   summary_list_destroy (node->summary);
5550   node_data_list_destroy (node->node_data);
5551   g_slice_free (GtkTextBTreeNode, node);
5552 }
5553 
5554 static NodeData*
gtk_text_btree_node_ensure_data(GtkTextBTreeNode * node,gpointer view_id)5555 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5556 {
5557   NodeData *nd;
5558 
5559   nd = node->node_data;
5560   while (nd != NULL)
5561     {
5562       if (nd->view_id == view_id)
5563         break;
5564 
5565       nd = nd->next;
5566     }
5567 
5568   if (nd == NULL)
5569     {
5570       nd = node_data_new (view_id);
5571 
5572       if (node->node_data)
5573         nd->next = node->node_data;
5574 
5575       node->node_data = nd;
5576     }
5577 
5578   return nd;
5579 }
5580 
5581 static void
gtk_text_btree_node_remove_data(GtkTextBTreeNode * node,gpointer view_id)5582 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5583 {
5584   NodeData *nd;
5585   NodeData *prev;
5586 
5587   prev = NULL;
5588   nd = node->node_data;
5589   while (nd != NULL)
5590     {
5591       if (nd->view_id == view_id)
5592         break;
5593 
5594       prev = nd;
5595       nd = nd->next;
5596     }
5597 
5598   if (nd == NULL)
5599     return;
5600 
5601   if (prev != NULL)
5602     prev->next = nd->next;
5603 
5604   if (node->node_data == nd)
5605     node->node_data = nd->next;
5606 
5607   nd->next = NULL;
5608 
5609   node_data_destroy (nd);
5610 }
5611 
5612 static void
gtk_text_btree_node_get_size(GtkTextBTreeNode * node,gpointer view_id,gint * width,gint * height)5613 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5614                               gint *width, gint *height)
5615 {
5616   NodeData *nd;
5617 
5618   g_return_if_fail (width != NULL);
5619   g_return_if_fail (height != NULL);
5620 
5621   nd = gtk_text_btree_node_ensure_data (node, view_id);
5622 
5623   if (width)
5624     *width = nd->width;
5625   if (height)
5626     *height = nd->height;
5627 }
5628 
5629 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5630  * here isn’t quite right, since for a lot of operations we want to
5631  * know which children of the common parent correspond to the two nodes
5632  * (e.g., when computing the order of two iters)
5633  */
5634 static GtkTextBTreeNode *
gtk_text_btree_node_common_parent(GtkTextBTreeNode * node1,GtkTextBTreeNode * node2)5635 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5636                                    GtkTextBTreeNode *node2)
5637 {
5638   while (node1->level < node2->level)
5639     node1 = node1->parent;
5640   while (node2->level < node1->level)
5641     node2 = node2->parent;
5642   while (node1 != node2)
5643     {
5644       node1 = node1->parent;
5645       node2 = node2->parent;
5646     }
5647 
5648   return node1;
5649 }
5650 
5651 /*
5652  * BTree
5653  */
5654 
5655 static BTreeView*
gtk_text_btree_get_view(GtkTextBTree * tree,gpointer view_id)5656 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5657 {
5658   BTreeView *view;
5659 
5660   view = tree->views;
5661   while (view != NULL)
5662     {
5663       if (view->view_id == view_id)
5664         break;
5665       view = view->next;
5666     }
5667 
5668   return view;
5669 }
5670 
5671 static void
get_tree_bounds(GtkTextBTree * tree,GtkTextIter * start,GtkTextIter * end)5672 get_tree_bounds (GtkTextBTree *tree,
5673                  GtkTextIter *start,
5674                  GtkTextIter *end)
5675 {
5676   _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5677   _gtk_text_btree_get_end_iter (tree, end);
5678 }
5679 
5680 static void
tag_changed_cb(GtkTextTagTable * table,GtkTextTag * tag,gboolean size_changed,GtkTextBTree * tree)5681 tag_changed_cb (GtkTextTagTable *table,
5682                 GtkTextTag      *tag,
5683                 gboolean         size_changed,
5684                 GtkTextBTree    *tree)
5685 {
5686   if (size_changed)
5687     {
5688       /* We need to queue a relayout on all regions that are tagged with
5689        * this tag.
5690        */
5691 
5692       GtkTextIter start;
5693       GtkTextIter end;
5694 
5695       if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5696         {
5697           /* Must be a last toggle if there was a first one. */
5698           _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5699           DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5700           _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE);
5701 
5702         }
5703     }
5704   else
5705     {
5706       /* We only need to queue a redraw, not a relayout */
5707       BTreeView *view;
5708 
5709       view = tree->views;
5710 
5711       while (view != NULL)
5712         {
5713           gint width, height;
5714 
5715           _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5716           gtk_text_layout_changed (view->layout, 0, height, height);
5717 
5718           view = view->next;
5719         }
5720     }
5721 }
5722 
5723 void
_gtk_text_btree_notify_will_remove_tag(GtkTextBTree * tree,GtkTextTag * tag)5724 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree    *tree,
5725                                         GtkTextTag      *tag)
5726 {
5727   /* Remove the tag from the tree */
5728 
5729   GtkTextIter start;
5730   GtkTextIter end;
5731 
5732   get_tree_bounds (tree, &start, &end);
5733 
5734   _gtk_text_btree_tag (&start, &end, tag, FALSE);
5735   gtk_text_btree_remove_tag_info (tree, tag);
5736 }
5737 
5738 
5739 /* Rebalance the out-of-whack node "node" */
5740 static void
gtk_text_btree_rebalance(GtkTextBTree * tree,GtkTextBTreeNode * node)5741 gtk_text_btree_rebalance (GtkTextBTree *tree,
5742                           GtkTextBTreeNode *node)
5743 {
5744   /*
5745    * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5746    * up through the tree one GtkTextBTreeNode at a time until the root
5747    * GtkTextBTreeNode has been processed.
5748    */
5749 
5750   while (node != NULL)
5751     {
5752       GtkTextBTreeNode *new_node, *child;
5753       GtkTextLine *line;
5754       int i;
5755 
5756       /*
5757        * Check to see if the GtkTextBTreeNode has too many children.  If it does,
5758        * then split off all but the first MIN_CHILDREN into a separate
5759        * GtkTextBTreeNode following the original one.  Then repeat until the
5760        * GtkTextBTreeNode has a decent size.
5761        */
5762 
5763       if (node->num_children > MAX_CHILDREN)
5764         {
5765           while (1)
5766             {
5767               /*
5768                * If the GtkTextBTreeNode being split is the root
5769                * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5770                * it first.
5771                */
5772 
5773               if (node->parent == NULL)
5774                 {
5775                   new_node = gtk_text_btree_node_new ();
5776                   new_node->parent = NULL;
5777                   new_node->next = NULL;
5778                   new_node->summary = NULL;
5779                   new_node->level = node->level + 1;
5780                   new_node->children.node = node;
5781                   recompute_node_counts (tree, new_node);
5782                   tree->root_node = new_node;
5783                 }
5784               new_node = gtk_text_btree_node_new ();
5785               new_node->parent = node->parent;
5786               new_node->next = node->next;
5787               node->next = new_node;
5788               new_node->summary = NULL;
5789               new_node->level = node->level;
5790               new_node->num_children = node->num_children - MIN_CHILDREN;
5791               if (node->level == 0)
5792                 {
5793                   for (i = MIN_CHILDREN-1,
5794                          line = node->children.line;
5795                        i > 0; i--, line = line->next)
5796                     {
5797                       /* Empty loop body. */
5798                     }
5799                   new_node->children.line = line->next;
5800                   line->next = NULL;
5801                 }
5802               else
5803                 {
5804                   for (i = MIN_CHILDREN-1,
5805                          child = node->children.node;
5806                        i > 0; i--, child = child->next)
5807                     {
5808                       /* Empty loop body. */
5809                     }
5810                   new_node->children.node = child->next;
5811                   child->next = NULL;
5812                 }
5813               recompute_node_counts (tree, node);
5814               node->parent->num_children++;
5815               node = new_node;
5816               if (node->num_children <= MAX_CHILDREN)
5817                 {
5818                   recompute_node_counts (tree, node);
5819                   break;
5820                 }
5821             }
5822         }
5823 
5824       while (node->num_children < MIN_CHILDREN)
5825         {
5826           GtkTextBTreeNode *other;
5827           GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5828           GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5829           int total_children, first_children;
5830 
5831           /*
5832            * Too few children for this GtkTextBTreeNode.  If this is the root then,
5833            * it's OK for it to have less than MIN_CHILDREN children
5834            * as long as it's got at least two.  If it has only one
5835            * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5836            * the tree and use its child as the new root.
5837            */
5838 
5839           if (node->parent == NULL)
5840             {
5841               if ((node->num_children == 1) && (node->level > 0))
5842                 {
5843                   tree->root_node = node->children.node;
5844                   tree->root_node->parent = NULL;
5845 
5846                   node->children.node = NULL;
5847                   gtk_text_btree_node_free_empty (tree, node);
5848                 }
5849               return;
5850             }
5851 
5852           /*
5853            * Not the root.  Make sure that there are siblings to
5854            * balance with.
5855            */
5856 
5857           if (node->parent->num_children < 2)
5858             {
5859               gtk_text_btree_rebalance (tree, node->parent);
5860               continue;
5861             }
5862 
5863           /*
5864            * Find a sibling neighbor to borrow from, and arrange for
5865            * node to be the earlier of the pair.
5866            */
5867 
5868           if (node->next == NULL)
5869             {
5870               for (other = node->parent->children.node;
5871                    other->next != node;
5872                    other = other->next)
5873                 {
5874                   /* Empty loop body. */
5875                 }
5876               node = other;
5877             }
5878           other = node->next;
5879 
5880           /*
5881            * We're going to either merge the two siblings together
5882            * into one GtkTextBTreeNode or redivide the children among them to
5883            * balance their loads.  As preparation, join their two
5884            * child lists into a single list and remember the half-way
5885            * point in the list.
5886            */
5887 
5888           total_children = node->num_children + other->num_children;
5889           first_children = total_children/2;
5890           if (node->children.node == NULL)
5891             {
5892               node->children = other->children;
5893               other->children.node = NULL;
5894               other->children.line = NULL;
5895             }
5896           if (node->level == 0)
5897             {
5898               GtkTextLine *line2;
5899 
5900               for (line2 = node->children.line, i = 1;
5901                    line2->next != NULL;
5902                    line2 = line2->next, i++)
5903                 {
5904                   if (i == first_children)
5905                     {
5906                       halfwayline = line2;
5907                     }
5908                 }
5909               line2->next = other->children.line;
5910               while (i <= first_children)
5911                 {
5912                   halfwayline = line2;
5913                   line2 = line2->next;
5914                   i++;
5915                 }
5916             }
5917           else
5918             {
5919               GtkTextBTreeNode *child2;
5920 
5921               for (child2 = node->children.node, i = 1;
5922                    child2->next != NULL;
5923                    child2 = child2->next, i++)
5924                 {
5925                   if (i <= first_children)
5926                     {
5927                       if (i == first_children)
5928                         {
5929                           halfwaynode = child2;
5930                         }
5931                     }
5932                 }
5933               child2->next = other->children.node;
5934               while (i <= first_children)
5935                 {
5936                   halfwaynode = child2;
5937                   child2 = child2->next;
5938                   i++;
5939                 }
5940             }
5941 
5942           /*
5943            * If the two siblings can simply be merged together, do it.
5944            */
5945 
5946           if (total_children <= MAX_CHILDREN)
5947             {
5948               recompute_node_counts (tree, node);
5949               node->next = other->next;
5950               node->parent->num_children--;
5951 
5952               other->children.node = NULL;
5953               other->children.line = NULL;
5954               gtk_text_btree_node_free_empty (tree, other);
5955               continue;
5956             }
5957 
5958           /*
5959            * The siblings can't be merged, so just divide their
5960            * children evenly between them.
5961            */
5962 
5963           if (node->level == 0)
5964             {
5965               other->children.line = halfwayline->next;
5966               halfwayline->next = NULL;
5967             }
5968           else
5969             {
5970               other->children.node = halfwaynode->next;
5971               halfwaynode->next = NULL;
5972             }
5973 
5974           recompute_node_counts (tree, node);
5975           recompute_node_counts (tree, other);
5976         }
5977 
5978       node = node->parent;
5979     }
5980 }
5981 
5982 static void
post_insert_fixup(GtkTextBTree * tree,GtkTextLine * line,gint line_count_delta,gint char_count_delta)5983 post_insert_fixup (GtkTextBTree *tree,
5984                    GtkTextLine *line,
5985                    gint line_count_delta,
5986                    gint char_count_delta)
5987 
5988 {
5989   GtkTextBTreeNode *node;
5990 
5991   /*
5992    * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5993    * point, then rebalance the tree if necessary.
5994    */
5995 
5996   for (node = line->parent ; node != NULL;
5997        node = node->parent)
5998     {
5999       node->num_lines += line_count_delta;
6000       node->num_chars += char_count_delta;
6001     }
6002   node = line->parent;
6003   node->num_children += line_count_delta;
6004 
6005   if (node->num_children > MAX_CHILDREN)
6006     {
6007       gtk_text_btree_rebalance (tree, node);
6008     }
6009 
6010 #ifdef G_ENABLE_DEBUG
6011   if (GTK_DEBUG_CHECK (TEXT))
6012     _gtk_text_btree_check (tree);
6013 #endif
6014 }
6015 
6016 static GtkTextTagInfo*
gtk_text_btree_get_existing_tag_info(GtkTextBTree * tree,GtkTextTag * tag)6017 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
6018                                       GtkTextTag   *tag)
6019 {
6020   GtkTextTagInfo *info;
6021   GSList *list;
6022 
6023 
6024   list = tree->tag_infos;
6025   while (list != NULL)
6026     {
6027       info = list->data;
6028       if (info->tag == tag)
6029         return info;
6030 
6031       list = list->next;
6032     }
6033 
6034   return NULL;
6035 }
6036 
6037 static GtkTextTagInfo*
gtk_text_btree_get_tag_info(GtkTextBTree * tree,GtkTextTag * tag)6038 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
6039                              GtkTextTag   *tag)
6040 {
6041   GtkTextTagInfo *info;
6042 
6043   info = gtk_text_btree_get_existing_tag_info (tree, tag);
6044 
6045   if (info == NULL)
6046     {
6047       /* didn't find it, create. */
6048 
6049       info = g_slice_new (GtkTextTagInfo);
6050 
6051       info->tag = tag;
6052       g_object_ref (tag);
6053       info->tag_root = NULL;
6054       info->toggle_count = 0;
6055 
6056       tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
6057     }
6058 
6059   return info;
6060 }
6061 
6062 static void
gtk_text_btree_remove_tag_info(GtkTextBTree * tree,GtkTextTag * tag)6063 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
6064                                 GtkTextTag   *tag)
6065 {
6066   GtkTextTagInfo *info;
6067   GSList *list;
6068   GSList *prev;
6069 
6070   prev = NULL;
6071   list = tree->tag_infos;
6072   while (list != NULL)
6073     {
6074       info = list->data;
6075       if (info->tag == tag)
6076         {
6077           if (prev != NULL)
6078             {
6079               prev->next = list->next;
6080             }
6081           else
6082             {
6083               tree->tag_infos = list->next;
6084             }
6085           list->next = NULL;
6086           g_slist_free (list);
6087 
6088           g_object_unref (info->tag);
6089 
6090           g_slice_free (GtkTextTagInfo, info);
6091           return;
6092         }
6093 
6094       prev = list;
6095       list = list->next;
6096     }
6097 }
6098 
6099 static void
recompute_level_zero_counts(GtkTextBTreeNode * node)6100 recompute_level_zero_counts (GtkTextBTreeNode *node)
6101 {
6102   GtkTextLine *line;
6103   GtkTextLineSegment *seg;
6104 
6105   g_assert (node->level == 0);
6106 
6107   line = node->children.line;
6108   while (line != NULL)
6109     {
6110       node->num_children++;
6111       node->num_lines++;
6112 
6113       if (line->parent != node)
6114         gtk_text_line_set_parent (line, node);
6115 
6116       seg = line->segments;
6117       while (seg != NULL)
6118         {
6119 
6120           node->num_chars += seg->char_count;
6121 
6122           if (((seg->type != &gtk_text_toggle_on_type)
6123                && (seg->type != &gtk_text_toggle_off_type))
6124               || !(seg->body.toggle.inNodeCounts))
6125             {
6126               ; /* nothing */
6127             }
6128           else
6129             {
6130               GtkTextTagInfo *info;
6131 
6132               info = seg->body.toggle.info;
6133 
6134               gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6135             }
6136 
6137           seg = seg->next;
6138         }
6139 
6140       line = line->next;
6141     }
6142 }
6143 
6144 static void
recompute_level_nonzero_counts(GtkTextBTreeNode * node)6145 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6146 {
6147   Summary *summary;
6148   GtkTextBTreeNode *child;
6149 
6150   g_assert (node->level > 0);
6151 
6152   child = node->children.node;
6153   while (child != NULL)
6154     {
6155       node->num_children += 1;
6156       node->num_lines += child->num_lines;
6157       node->num_chars += child->num_chars;
6158 
6159       if (child->parent != node)
6160         {
6161           child->parent = node;
6162           gtk_text_btree_node_invalidate_upward (node, NULL);
6163         }
6164 
6165       summary = child->summary;
6166       while (summary != NULL)
6167         {
6168           gtk_text_btree_node_adjust_toggle_count (node,
6169                                                    summary->info,
6170                                                    summary->toggle_count);
6171 
6172           summary = summary->next;
6173         }
6174 
6175       child = child->next;
6176     }
6177 }
6178 
6179 /*
6180  *----------------------------------------------------------------------
6181  *
6182  * recompute_node_counts --
6183  *
6184  *      This procedure is called to recompute all the counts in a GtkTextBTreeNode
6185  *      (tags, child information, etc.) by scanning the information in
6186  *      its descendants.  This procedure is called during rebalancing
6187  *      when a GtkTextBTreeNode’s child structure has changed.
6188  *
6189  * Results:
6190  *      None.
6191  *
6192  * Side effects:
6193  *      The tag counts for node are modified to reflect its current
6194  *      child structure, as are its num_children, num_lines, num_chars fields.
6195  *      Also, all of the childrens’ parent fields are made to point
6196  *      to node.
6197  *
6198  *----------------------------------------------------------------------
6199  */
6200 
6201 static void
recompute_node_counts(GtkTextBTree * tree,GtkTextBTreeNode * node)6202 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6203 {
6204   BTreeView *view;
6205   Summary *summary, *summary2;
6206 
6207   /*
6208    * Zero out all the existing counts for the GtkTextBTreeNode, but don’t delete
6209    * the existing Summary records (most of them will probably be reused).
6210    */
6211 
6212   summary = node->summary;
6213   while (summary != NULL)
6214     {
6215       summary->toggle_count = 0;
6216       summary = summary->next;
6217     }
6218 
6219   node->num_children = 0;
6220   node->num_lines = 0;
6221   node->num_chars = 0;
6222 
6223   /*
6224    * Scan through the children, adding the childrens’ tag counts into
6225    * the GtkTextBTreeNode’s tag counts and adding new Summary structures if
6226    * necessary.
6227    */
6228 
6229   if (node->level == 0)
6230     recompute_level_zero_counts (node);
6231   else
6232     recompute_level_nonzero_counts (node);
6233 
6234   view = tree->views;
6235   while (view)
6236     {
6237       gtk_text_btree_node_check_valid (node, view->view_id);
6238       view = view->next;
6239     }
6240 
6241   /*
6242    * Scan through the GtkTextBTreeNode’s tag records again and delete any Summary
6243    * records that still have a zero count, or that have all the toggles.
6244    * The GtkTextBTreeNode with the children that account for all the tags toggles
6245    * have no summary information, and they become the tag_root for the tag.
6246    */
6247 
6248   summary2 = NULL;
6249   for (summary = node->summary; summary != NULL; )
6250     {
6251       if (summary->toggle_count > 0 &&
6252           summary->toggle_count < summary->info->toggle_count)
6253         {
6254           if (node->level == summary->info->tag_root->level)
6255             {
6256               /*
6257                * The tag’s root GtkTextBTreeNode split and some toggles left.
6258                * The tag root must move up a level.
6259                */
6260               summary->info->tag_root = node->parent;
6261             }
6262           summary2 = summary;
6263           summary = summary->next;
6264           continue;
6265         }
6266       if (summary->toggle_count == summary->info->toggle_count)
6267         {
6268           /*
6269            * A GtkTextBTreeNode merge has collected all the toggles under
6270            * one GtkTextBTreeNode.  Push the root down to this level.
6271            */
6272           summary->info->tag_root = node;
6273         }
6274       if (summary2 != NULL)
6275         {
6276           summary2->next = summary->next;
6277           summary_destroy (summary);
6278           summary = summary2->next;
6279         }
6280       else
6281         {
6282           node->summary = summary->next;
6283           summary_destroy (summary);
6284           summary = node->summary;
6285         }
6286     }
6287 }
6288 
6289 void
_gtk_change_node_toggle_count(GtkTextBTreeNode * node,GtkTextTagInfo * info,gint delta)6290 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6291                                GtkTextTagInfo   *info,
6292                                gint              delta) /* may be negative */
6293 {
6294   Summary *summary, *prevPtr;
6295   GtkTextBTreeNode *node2Ptr;
6296   int rootLevel;                        /* Level of original tag root */
6297 
6298   info->toggle_count += delta;
6299 
6300   if (info->tag_root == (GtkTextBTreeNode *) NULL)
6301     {
6302       info->tag_root = node;
6303       return;
6304     }
6305 
6306   /*
6307    * Note the level of the existing root for the tag so we can detect
6308    * if it needs to be moved because of the toggle count change.
6309    */
6310 
6311   rootLevel = info->tag_root->level;
6312 
6313   /*
6314    * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6315    * summary counts at each GtkTextBTreeNode and moving the tag’s root upwards if
6316    * necessary.
6317    */
6318 
6319   for ( ; node != info->tag_root; node = node->parent)
6320     {
6321       /*
6322        * See if there’s already an entry for this tag for this GtkTextBTreeNode.  If so,
6323        * perhaps all we have to do is adjust its count.
6324        */
6325 
6326       for (prevPtr = NULL, summary = node->summary;
6327            summary != NULL;
6328            prevPtr = summary, summary = summary->next)
6329         {
6330           if (summary->info == info)
6331             {
6332               break;
6333             }
6334         }
6335       if (summary != NULL)
6336         {
6337           summary->toggle_count += delta;
6338           if (summary->toggle_count > 0 &&
6339               summary->toggle_count < info->toggle_count)
6340             {
6341               continue;
6342             }
6343           if (summary->toggle_count != 0)
6344             {
6345               /*
6346                * Should never find a GtkTextBTreeNode with max toggle count at this
6347                * point (there shouldn’t have been a summary entry in the
6348                * first place).
6349                */
6350 
6351               g_error ("%s: bad toggle count (%d) max (%d)",
6352                        G_STRLOC, summary->toggle_count, info->toggle_count);
6353             }
6354 
6355           /*
6356            * Zero toggle count;  must remove this tag from the list.
6357            */
6358 
6359           if (prevPtr == NULL)
6360             {
6361               node->summary = summary->next;
6362             }
6363           else
6364             {
6365               prevPtr->next = summary->next;
6366             }
6367           summary_destroy (summary);
6368         }
6369       else
6370         {
6371           /*
6372            * This tag isn’t currently in the summary information list.
6373            */
6374 
6375           if (rootLevel == node->level)
6376             {
6377 
6378               /*
6379                * The old tag root is at the same level in the tree as this
6380                * GtkTextBTreeNode, but it isn’t at this GtkTextBTreeNode.  Move the tag root up
6381                * a level, in the hopes that it will now cover this GtkTextBTreeNode
6382                * as well as the old root (if not, we’ll move it up again
6383                * the next time through the loop).  To push it up one level
6384                * we copy the original toggle count into the summary
6385                * information at the old root and change the root to its
6386                * parent GtkTextBTreeNode.
6387                */
6388 
6389               GtkTextBTreeNode *rootnode = info->tag_root;
6390               summary = g_slice_new (Summary);
6391               summary->info = info;
6392               summary->toggle_count = info->toggle_count - delta;
6393               summary->next = rootnode->summary;
6394               rootnode->summary = summary;
6395               rootnode = rootnode->parent;
6396               rootLevel = rootnode->level;
6397               info->tag_root = rootnode;
6398             }
6399           summary = g_slice_new (Summary);
6400           summary->info = info;
6401           summary->toggle_count = delta;
6402           summary->next = node->summary;
6403           node->summary = summary;
6404         }
6405     }
6406 
6407   /*
6408    * If we’ve decremented the toggle count, then it may be necessary
6409    * to push the tag root down one or more levels.
6410    */
6411 
6412   if (delta >= 0)
6413     {
6414       return;
6415     }
6416   if (info->toggle_count == 0)
6417     {
6418       info->tag_root = (GtkTextBTreeNode *) NULL;
6419       return;
6420     }
6421   node = info->tag_root;
6422   while (node->level > 0)
6423     {
6424       /*
6425        * See if a single child GtkTextBTreeNode accounts for all of the tag’s
6426        * toggles.  If so, push the root down one level.
6427        */
6428 
6429       for (node2Ptr = node->children.node;
6430            node2Ptr != (GtkTextBTreeNode *)NULL ;
6431            node2Ptr = node2Ptr->next)
6432         {
6433           for (prevPtr = NULL, summary = node2Ptr->summary;
6434                summary != NULL;
6435                prevPtr = summary, summary = summary->next)
6436             {
6437               if (summary->info == info)
6438                 {
6439                   break;
6440                 }
6441             }
6442           if (summary == NULL)
6443             {
6444               continue;
6445             }
6446           if (summary->toggle_count != info->toggle_count)
6447             {
6448               /*
6449                * No GtkTextBTreeNode has all toggles, so the root is still valid.
6450                */
6451 
6452               return;
6453             }
6454 
6455           /*
6456            * This GtkTextBTreeNode has all the toggles, so push down the root.
6457            */
6458 
6459           if (prevPtr == NULL)
6460             {
6461               node2Ptr->summary = summary->next;
6462             }
6463           else
6464             {
6465               prevPtr->next = summary->next;
6466             }
6467           summary_destroy (summary);
6468           info->tag_root = node2Ptr;
6469           break;
6470         }
6471       node = info->tag_root;
6472     }
6473 }
6474 
6475 /*
6476  *----------------------------------------------------------------------
6477  *
6478  * inc_count --
6479  *
6480  *      This is a utility procedure used by _gtk_text_btree_get_tags.  It
6481  *      increments the count for a particular tag, adding a new
6482  *      entry for that tag if there wasn’t one previously.
6483  *
6484  * Results:
6485  *      None.
6486  *
6487  * Side effects:
6488  *      The information at *tagInfoPtr may be modified, and the arrays
6489  *      may be reallocated to make them larger.
6490  *
6491  *----------------------------------------------------------------------
6492  */
6493 
6494 static void
inc_count(GtkTextTag * tag,int inc,TagInfo * tagInfoPtr)6495 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6496 {
6497   GtkTextTag **tag_p;
6498   int count;
6499 
6500   for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6501        count > 0; tag_p++, count--)
6502     {
6503       if (*tag_p == tag)
6504         {
6505           tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6506           return;
6507         }
6508     }
6509 
6510   /*
6511    * There isn’t currently an entry for this tag, so we have to
6512    * make a new one.  If the arrays are full, then enlarge the
6513    * arrays first.
6514    */
6515 
6516   if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6517     {
6518       GtkTextTag **newTags;
6519       int *newCounts, newSize;
6520 
6521       newSize = 2*tagInfoPtr->arraySize;
6522       newTags = (GtkTextTag **) g_malloc ((unsigned)
6523                                           (newSize*sizeof (GtkTextTag *)));
6524       memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6525               tagInfoPtr->arraySize  *sizeof (GtkTextTag *));
6526       g_free ((char *) tagInfoPtr->tags);
6527       tagInfoPtr->tags = newTags;
6528       newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6529       memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6530               tagInfoPtr->arraySize  *sizeof (int));
6531       g_free ((char *) tagInfoPtr->counts);
6532       tagInfoPtr->counts = newCounts;
6533       tagInfoPtr->arraySize = newSize;
6534     }
6535 
6536   tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6537   tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6538   tagInfoPtr->numTags++;
6539 }
6540 
6541 static void
gtk_text_btree_link_segment(GtkTextLineSegment * seg,const GtkTextIter * iter)6542 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6543                              const GtkTextIter *iter)
6544 {
6545   GtkTextLineSegment *prev;
6546   GtkTextLine *line;
6547   GtkTextBTree *tree;
6548 
6549   line = _gtk_text_iter_get_text_line (iter);
6550   tree = _gtk_text_iter_get_btree (iter);
6551 
6552   prev = gtk_text_line_segment_split (iter);
6553   if (prev == NULL)
6554     {
6555       seg->next = line->segments;
6556       line->segments = seg;
6557     }
6558   else
6559     {
6560       seg->next = prev->next;
6561       prev->next = seg;
6562     }
6563   cleanup_line (line);
6564   segments_changed (tree);
6565 
6566 #ifdef G_ENABLE_DEBUG
6567   if (GTK_DEBUG_CHECK (TEXT))
6568     _gtk_text_btree_check (tree);
6569 #endif
6570 }
6571 
6572 static void
gtk_text_btree_unlink_segment(GtkTextBTree * tree,GtkTextLineSegment * seg,GtkTextLine * line)6573 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6574                                GtkTextLineSegment *seg,
6575                                GtkTextLine *line)
6576 {
6577   GtkTextLineSegment *prev;
6578 
6579   if (line->segments == seg)
6580     {
6581       line->segments = seg->next;
6582     }
6583   else
6584     {
6585       for (prev = line->segments; prev->next != seg;
6586            prev = prev->next)
6587         {
6588           /* Empty loop body. */
6589         }
6590       prev->next = seg->next;
6591     }
6592   cleanup_line (line);
6593   segments_changed (tree);
6594 }
6595 
6596 /*
6597  * This is here because it requires BTree internals, it logically
6598  * belongs in gtktextsegment.c
6599  */
6600 
6601 
6602 /*
6603  *--------------------------------------------------------------
6604  *
6605  * _gtk_toggle_segment_check_func --
6606  *
6607  *      This procedure is invoked to perform consistency checks
6608  *      on toggle segments.
6609  *
6610  * Results:
6611  *      None.
6612  *
6613  * Side effects:
6614  *      If a consistency problem is found the procedure g_errors.
6615  *
6616  *--------------------------------------------------------------
6617  */
6618 
6619 void
_gtk_toggle_segment_check_func(GtkTextLineSegment * segPtr,GtkTextLine * line)6620 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6621                                 GtkTextLine *line)
6622 {
6623   Summary *summary;
6624   int needSummary;
6625 
6626   if (segPtr->byte_count != 0)
6627     {
6628       g_error ("toggle_segment_check_func: segment had non-zero size");
6629     }
6630   if (!segPtr->body.toggle.inNodeCounts)
6631     {
6632       g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6633     }
6634   needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6635   for (summary = line->parent->summary; ;
6636        summary = summary->next)
6637     {
6638       if (summary == NULL)
6639         {
6640           if (needSummary)
6641             {
6642               g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6643             }
6644           else
6645             {
6646               break;
6647             }
6648         }
6649       if (summary->info == segPtr->body.toggle.info)
6650         {
6651           if (!needSummary)
6652             {
6653               g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6654             }
6655           break;
6656         }
6657     }
6658 }
6659 
6660 /*
6661  * Debug
6662  */
6663 #ifdef G_ENABLE_DEBUG
6664 static void
gtk_text_btree_node_view_check_consistency(GtkTextBTree * tree,GtkTextBTreeNode * node,NodeData * nd)6665 gtk_text_btree_node_view_check_consistency (GtkTextBTree     *tree,
6666                                             GtkTextBTreeNode *node,
6667                                             NodeData         *nd)
6668 {
6669   gint width;
6670   gint height;
6671   gboolean valid;
6672   BTreeView *view;
6673 
6674   view = tree->views;
6675 
6676   while (view != NULL)
6677     {
6678       if (view->view_id == nd->view_id)
6679         break;
6680 
6681       view = view->next;
6682     }
6683 
6684   if (view == NULL)
6685     g_error ("Node has data for a view %p no longer attached to the tree",
6686              nd->view_id);
6687 
6688   gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6689                                                &width, &height, &valid);
6690 
6691   /* valid aggregate not checked the same as width/height, because on
6692    * btree rebalance we can have invalid nodes where all lines below
6693    * them are actually valid, due to moving lines around between
6694    * nodes.
6695    *
6696    * The guarantee is that if there are invalid lines the node is
6697    * invalid - we don’t guarantee that if the node is invalid there
6698    * are invalid lines.
6699    */
6700 
6701   if (nd->width != width ||
6702       nd->height != height ||
6703       (nd->valid && !valid))
6704     {
6705       g_error ("Node aggregates for view %p are invalid:\n"
6706                "Are (%d,%d,%s), should be (%d,%d,%s)",
6707                nd->view_id,
6708                nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6709                width, height, valid ? "TRUE" : "FALSE");
6710     }
6711 }
6712 
6713 static void
gtk_text_btree_node_check_consistency(GtkTextBTree * tree,GtkTextBTreeNode * node)6714 gtk_text_btree_node_check_consistency (GtkTextBTree     *tree,
6715                                        GtkTextBTreeNode *node)
6716 {
6717   GtkTextBTreeNode *childnode;
6718   Summary *summary, *summary2;
6719   GtkTextLine *line;
6720   GtkTextLineSegment *segPtr;
6721   int num_children, num_lines, num_chars, toggle_count, min_children;
6722   GtkTextLineData *ld;
6723   NodeData *nd;
6724 
6725   if (node->parent != NULL)
6726     {
6727       min_children = MIN_CHILDREN;
6728     }
6729   else if (node->level > 0)
6730     {
6731       min_children = 2;
6732     }
6733   else  {
6734     min_children = 1;
6735   }
6736   if ((node->num_children < min_children)
6737       || (node->num_children > MAX_CHILDREN))
6738     {
6739       g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6740                node->num_children);
6741     }
6742 
6743   nd = node->node_data;
6744   while (nd != NULL)
6745     {
6746       gtk_text_btree_node_view_check_consistency (tree, node, nd);
6747       nd = nd->next;
6748     }
6749 
6750   num_children = 0;
6751   num_lines = 0;
6752   num_chars = 0;
6753   if (node->level == 0)
6754     {
6755       for (line = node->children.line; line != NULL;
6756            line = line->next)
6757         {
6758           if (line->parent != node)
6759             {
6760               g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6761             }
6762           if (line->segments == NULL)
6763             {
6764               g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6765             }
6766 
6767           ld = line->views;
6768           while (ld != NULL)
6769             {
6770               /* Just ensuring we don’t segv while doing this loop */
6771 
6772               ld = ld->next;
6773             }
6774 
6775           for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6776             {
6777               if (segPtr->type->checkFunc != NULL)
6778                 {
6779                   (*segPtr->type->checkFunc)(segPtr, line);
6780                 }
6781               if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6782                   && (segPtr->next != NULL)
6783                   && (segPtr->next->byte_count == 0)
6784                   && (segPtr->next->type->leftGravity))
6785                 {
6786                   g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6787                 }
6788               if ((segPtr->next == NULL)
6789                   && (segPtr->type != &gtk_text_char_type))
6790                 {
6791                   g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6792                 }
6793 
6794               num_chars += segPtr->char_count;
6795             }
6796 
6797           num_children++;
6798           num_lines++;
6799         }
6800     }
6801   else
6802     {
6803       for (childnode = node->children.node; childnode != NULL;
6804            childnode = childnode->next)
6805         {
6806           if (childnode->parent != node)
6807             {
6808               g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6809             }
6810           if (childnode->level != (node->level-1))
6811             {
6812               g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6813                        node->level, childnode->level);
6814             }
6815           gtk_text_btree_node_check_consistency (tree, childnode);
6816           for (summary = childnode->summary; summary != NULL;
6817                summary = summary->next)
6818             {
6819               for (summary2 = node->summary; ;
6820                    summary2 = summary2->next)
6821                 {
6822                   if (summary2 == NULL)
6823                     {
6824                       if (summary->info->tag_root == node)
6825                         {
6826                           break;
6827                         }
6828                       g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6829                                summary->info->tag->priv->name,
6830                                "present in parent summaries");
6831                     }
6832                   if (summary->info == summary2->info)
6833                     {
6834                       break;
6835                     }
6836                 }
6837             }
6838           num_children++;
6839           num_lines += childnode->num_lines;
6840           num_chars += childnode->num_chars;
6841         }
6842     }
6843   if (num_children != node->num_children)
6844     {
6845       g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6846                num_children, node->num_children);
6847     }
6848   if (num_lines != node->num_lines)
6849     {
6850       g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6851                num_lines, node->num_lines);
6852     }
6853   if (num_chars != node->num_chars)
6854     {
6855       g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6856                num_chars, node->num_chars);
6857     }
6858 
6859   for (summary = node->summary; summary != NULL;
6860        summary = summary->next)
6861     {
6862       if (summary->info->toggle_count == summary->toggle_count)
6863         {
6864           g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6865                    summary->info->tag->priv->name);
6866         }
6867       toggle_count = 0;
6868       if (node->level == 0)
6869         {
6870           for (line = node->children.line; line != NULL;
6871                line = line->next)
6872             {
6873               for (segPtr = line->segments; segPtr != NULL;
6874                    segPtr = segPtr->next)
6875                 {
6876                   if ((segPtr->type != &gtk_text_toggle_on_type)
6877                       && (segPtr->type != &gtk_text_toggle_off_type))
6878                     {
6879                       continue;
6880                     }
6881                   if (segPtr->body.toggle.info == summary->info)
6882                     {
6883                       if (!segPtr->body.toggle.inNodeCounts)
6884                         g_error ("Toggle segment not in the node counts");
6885 
6886                       toggle_count ++;
6887                     }
6888                 }
6889             }
6890         }
6891       else
6892         {
6893           for (childnode = node->children.node;
6894                childnode != NULL;
6895                childnode = childnode->next)
6896             {
6897               for (summary2 = childnode->summary;
6898                    summary2 != NULL;
6899                    summary2 = summary2->next)
6900                 {
6901                   if (summary2->info == summary->info)
6902                     {
6903                       toggle_count += summary2->toggle_count;
6904                     }
6905                 }
6906             }
6907         }
6908       if (toggle_count != summary->toggle_count)
6909         {
6910           g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6911                    toggle_count, summary->toggle_count);
6912         }
6913       for (summary2 = summary->next; summary2 != NULL;
6914            summary2 = summary2->next)
6915         {
6916           if (summary2->info == summary->info)
6917             {
6918               g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6919                        summary->info->tag->priv->name);
6920             }
6921         }
6922     }
6923 }
6924 
6925 static void
listify_foreach(GtkTextTag * tag,gpointer user_data)6926 listify_foreach (GtkTextTag *tag, gpointer user_data)
6927 {
6928   GSList** listp = user_data;
6929 
6930   *listp = g_slist_prepend (*listp, tag);
6931 }
6932 
6933 static GSList*
list_of_tags(GtkTextTagTable * table)6934 list_of_tags (GtkTextTagTable *table)
6935 {
6936   GSList *list = NULL;
6937 
6938   gtk_text_tag_table_foreach (table, listify_foreach, &list);
6939 
6940   return list;
6941 }
6942 
6943 void
_gtk_text_btree_check(GtkTextBTree * tree)6944 _gtk_text_btree_check (GtkTextBTree *tree)
6945 {
6946   Summary *summary;
6947   GtkTextBTreeNode *node;
6948   GtkTextLine *line;
6949   GtkTextLineSegment *seg;
6950   GtkTextTag *tag;
6951   GSList *all_tags, *taglist = NULL;
6952   int count;
6953   GtkTextTagInfo *info;
6954 
6955   /*
6956    * Make sure that the tag toggle counts and the tag root pointers are OK.
6957    */
6958   all_tags = list_of_tags (tree->table);
6959   for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6960     {
6961       tag = taglist->data;
6962       info = gtk_text_btree_get_existing_tag_info (tree, tag);
6963       if (info != NULL)
6964         {
6965           node = info->tag_root;
6966           if (node == NULL)
6967             {
6968               if (info->toggle_count != 0)
6969                 {
6970                   g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6971                            tag->priv->name, info->toggle_count);
6972                 }
6973               continue;         /* no ranges for the tag */
6974             }
6975           else if (info->toggle_count == 0)
6976             {
6977               g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6978                        tag->priv->name);
6979             }
6980           else if (info->toggle_count & 1)
6981             {
6982               g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6983                        tag->priv->name, info->toggle_count);
6984             }
6985           for (summary = node->summary; summary != NULL;
6986                summary = summary->next)
6987             {
6988               if (summary->info->tag == tag)
6989                 {
6990                   g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6991                 }
6992             }
6993           count = 0;
6994           if (node->level > 0)
6995             {
6996               for (node = node->children.node ; node != NULL ;
6997                    node = node->next)
6998                 {
6999                   for (summary = node->summary; summary != NULL;
7000                        summary = summary->next)
7001                     {
7002                       if (summary->info->tag == tag)
7003                         {
7004                           count += summary->toggle_count;
7005                         }
7006                     }
7007                 }
7008             }
7009           else
7010             {
7011               const GtkTextLineSegmentClass *last = NULL;
7012 
7013               for (line = node->children.line ; line != NULL ;
7014                    line = line->next)
7015                 {
7016                   for (seg = line->segments; seg != NULL;
7017                        seg = seg->next)
7018                     {
7019                       if ((seg->type == &gtk_text_toggle_on_type ||
7020                            seg->type == &gtk_text_toggle_off_type) &&
7021                           seg->body.toggle.info->tag == tag)
7022                         {
7023                           if (last == seg->type)
7024                             g_error ("Two consecutive toggles on or off weren't merged");
7025                           if (!seg->body.toggle.inNodeCounts)
7026                             g_error ("Toggle segment not in the node counts");
7027 
7028                           last = seg->type;
7029 
7030                           count++;
7031                         }
7032                     }
7033                 }
7034             }
7035           if (count != info->toggle_count)
7036             {
7037               g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
7038                        info->toggle_count, tag->priv->name, count);
7039             }
7040         }
7041     }
7042 
7043   g_slist_free (all_tags);
7044 
7045   /*
7046    * Call a recursive procedure to do the main body of checks.
7047    */
7048 
7049   node = tree->root_node;
7050   gtk_text_btree_node_check_consistency (tree, tree->root_node);
7051 
7052   /*
7053    * Make sure that there are at least two lines in the text and
7054    * that the last line has no characters except a newline.
7055    */
7056 
7057   if (node->num_lines < 2)
7058     {
7059       g_error ("_gtk_text_btree_check: less than 2 lines in tree");
7060     }
7061   if (node->num_chars < 2)
7062     {
7063       g_error ("_gtk_text_btree_check: less than 2 chars in tree");
7064     }
7065   while (node->level > 0)
7066     {
7067       node = node->children.node;
7068       while (node->next != NULL)
7069         {
7070           node = node->next;
7071         }
7072     }
7073   line = node->children.line;
7074   while (line->next != NULL)
7075     {
7076       line = line->next;
7077     }
7078   seg = line->segments;
7079   while ((seg->type == &gtk_text_toggle_off_type)
7080          || (seg->type == &gtk_text_right_mark_type)
7081          || (seg->type == &gtk_text_left_mark_type))
7082     {
7083       /*
7084        * It’s OK to toggle a tag off in the last line, but
7085        * not to start a new range.  It’s also OK to have marks
7086        * in the last line.
7087        */
7088 
7089       seg = seg->next;
7090     }
7091   if (seg->type != &gtk_text_char_type)
7092     {
7093       g_error ("_gtk_text_btree_check: last line has bogus segment type");
7094     }
7095   if (seg->next != NULL)
7096     {
7097       g_error ("_gtk_text_btree_check: last line has too many segments");
7098     }
7099   if (seg->byte_count != 1)
7100     {
7101       g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7102                seg->byte_count);
7103     }
7104   if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7105     {
7106       g_error ("_gtk_text_btree_check: last line had bad value: %s",
7107                seg->body.chars);
7108     }
7109 }
7110 #endif /* G_ENABLE_DEBUG */
7111 
7112 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7113 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7114 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7115 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7116 
7117 void
_gtk_text_btree_spew(GtkTextBTree * tree)7118 _gtk_text_btree_spew (GtkTextBTree *tree)
7119 {
7120   GtkTextLine * line;
7121   int real_line;
7122 
7123   printf ("%d lines in tree %p\n",
7124           _gtk_text_btree_line_count (tree), tree);
7125 
7126   line = _gtk_text_btree_get_line (tree, 0, &real_line);
7127 
7128   while (line != NULL)
7129     {
7130       _gtk_text_btree_spew_line (tree, line);
7131       line = _gtk_text_line_next (line);
7132     }
7133 
7134   printf ("=================== Tag information\n");
7135 
7136   {
7137     GSList * list;
7138 
7139     list = tree->tag_infos;
7140 
7141     while (list != NULL)
7142       {
7143         GtkTextTagInfo *info;
7144 
7145         info = list->data;
7146 
7147         printf ("  tag '%s': root at %p, toggle count %d\n",
7148                 info->tag->priv->name, info->tag_root, info->toggle_count);
7149 
7150         list = list->next;
7151       }
7152 
7153     if (tree->tag_infos == NULL)
7154       {
7155         printf ("  (no tags in the tree)\n");
7156       }
7157   }
7158 
7159   printf ("=================== Tree nodes\n");
7160 
7161   {
7162     _gtk_text_btree_spew_node (tree->root_node, 0);
7163   }
7164 }
7165 
7166 void
_gtk_text_btree_spew_line_short(GtkTextLine * line,int indent)7167 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7168 {
7169   gchar * spaces;
7170   GtkTextLineSegment *seg;
7171 
7172   spaces = g_strnfill (indent, ' ');
7173 
7174   printf ("%sline %p chars %d bytes %d\n",
7175           spaces, line,
7176           _gtk_text_line_char_count (line),
7177           _gtk_text_line_byte_count (line));
7178 
7179   seg = line->segments;
7180   while (seg != NULL)
7181     {
7182       if (seg->type == &gtk_text_char_type)
7183         {
7184           gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7185           gchar* s;
7186           s = str;
7187           while (*s)
7188             {
7189               if (*s == '\n' || *s == '\r')
7190                 *s = '\\';
7191               ++s;
7192             }
7193           printf ("%s chars '%s'...\n", spaces, str);
7194           g_free (str);
7195         }
7196       else if (seg->type == &gtk_text_right_mark_type)
7197         {
7198           printf ("%s right mark '%s' visible: %d\n",
7199                   spaces,
7200                   seg->body.mark.name,
7201                   seg->body.mark.visible);
7202         }
7203       else if (seg->type == &gtk_text_left_mark_type)
7204         {
7205           printf ("%s left mark '%s' visible: %d\n",
7206                   spaces,
7207                   seg->body.mark.name,
7208                   seg->body.mark.visible);
7209         }
7210       else if (seg->type == &gtk_text_toggle_on_type ||
7211                seg->type == &gtk_text_toggle_off_type)
7212         {
7213           printf ("%s tag '%s' %s\n",
7214                   spaces, seg->body.toggle.info->tag->priv->name,
7215                   seg->type == &gtk_text_toggle_off_type ? "off" : "on");
7216         }
7217 
7218       seg = seg->next;
7219     }
7220 
7221   g_free (spaces);
7222 }
7223 
7224 void
_gtk_text_btree_spew_node(GtkTextBTreeNode * node,int indent)7225 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7226 {
7227   gchar * spaces;
7228   GtkTextBTreeNode *iter;
7229   Summary *s;
7230 
7231   spaces = g_strnfill (indent, ' ');
7232 
7233   printf ("%snode %p level %d children %d lines %d chars %d\n",
7234           spaces, node, node->level,
7235           node->num_children, node->num_lines, node->num_chars);
7236 
7237   s = node->summary;
7238   while (s)
7239     {
7240       printf ("%s %d toggles of '%s' below this node\n",
7241               spaces, s->toggle_count, s->info->tag->priv->name);
7242       s = s->next;
7243     }
7244 
7245   g_free (spaces);
7246 
7247   if (node->level > 0)
7248     {
7249       iter = node->children.node;
7250       while (iter != NULL)
7251         {
7252           _gtk_text_btree_spew_node (iter, indent + 2);
7253 
7254           iter = iter->next;
7255         }
7256     }
7257   else
7258     {
7259       GtkTextLine *line = node->children.line;
7260       while (line != NULL)
7261         {
7262           _gtk_text_btree_spew_line_short (line, indent + 2);
7263 
7264           line = line->next;
7265         }
7266     }
7267 }
7268 
7269 void
_gtk_text_btree_spew_line(GtkTextBTree * tree,GtkTextLine * line)7270 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7271 {
7272   GtkTextLineSegment * seg;
7273 
7274   printf ("%4d| line: %p parent: %p next: %p\n",
7275           _gtk_text_line_get_number (line), line, line->parent, line->next);
7276 
7277   seg = line->segments;
7278 
7279   while (seg != NULL)
7280     {
7281       _gtk_text_btree_spew_segment (tree, seg);
7282       seg = seg->next;
7283     }
7284 }
7285 
7286 void
_gtk_text_btree_spew_segment(GtkTextBTree * tree,GtkTextLineSegment * seg)7287 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7288 {
7289   printf ("     segment: %p type: %s bytes: %d chars: %d\n",
7290           seg, seg->type->name, seg->byte_count, seg->char_count);
7291 
7292   if (seg->type == &gtk_text_char_type)
7293     {
7294       gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7295       printf ("       '%s'\n", str);
7296       g_free (str);
7297     }
7298   else if (seg->type == &gtk_text_right_mark_type)
7299     {
7300       printf ("       right mark '%s' visible: %d not_deleteable: %d\n",
7301               seg->body.mark.name,
7302               seg->body.mark.visible,
7303               seg->body.mark.not_deleteable);
7304     }
7305   else if (seg->type == &gtk_text_left_mark_type)
7306     {
7307       printf ("       left mark '%s' visible: %d not_deleteable: %d\n",
7308               seg->body.mark.name,
7309               seg->body.mark.visible,
7310               seg->body.mark.not_deleteable);
7311     }
7312   else if (seg->type == &gtk_text_toggle_on_type ||
7313            seg->type == &gtk_text_toggle_off_type)
7314     {
7315       printf ("       tag '%s' priority %d\n",
7316               seg->body.toggle.info->tag->priv->name,
7317               seg->body.toggle.info->tag->priv->priority);
7318     }
7319 }
7320