gtktextlayout.c 72.6 KB
Newer Older
1
2
/* GTK - The GIMP Toolkit
 * gtktextlayout.c - calculate the layout of the text
3
4
5
6
7
8
9
 *
 * Copyright (c) 1992-1994 The Regents of the University of California.
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
 * Copyright (c) 2000 Red Hat, Inc.
 * Tk->Gtk port by Havoc Pennington
 * Pango support by Owen Taylor
 *
10
11
 * This file can be used under your choice of two licenses, the LGPL
 * and the original Tk license.
12
 *
13
 * LGPL:
14
 *
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Original Tk license:
30
31
32
33
34
 *
 * This software is copyrighted by the Regents of the University of
 * California, Sun Microsystems, Inc., and other parties.  The
 * following terms apply to all files associated with the software
 * unless explicitly disclaimed in individual files.
35
 *
36
37
38
39
40
41
42
43
44
 * The authors hereby grant permission to use, copy, modify,
 * distribute, and license this software and its documentation for any
 * purpose, provided that existing copyright notices are retained in
 * all copies and that this notice is included verbatim in any
 * distributions. No written agreement, license, or royalty fee is
 * required for any of the authorized uses.  Modifications to this
 * software may be copyrighted by their authors and need not follow
 * the licensing terms described here, provided that the new terms are
 * clearly indicated on the first page of each file where they apply.
45
 *
46
47
48
49
50
 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
51
 *
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
 * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * GOVERNMENT USE: If you are acquiring this software on behalf of the
 * U.S. government, the Government shall have only "Restricted Rights"
 * in the software and related documentation as defined in the Federal
 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
 * are acquiring the software on behalf of the Department of Defense,
 * the software shall be classified as "Commercial Computer Software"
 * and the Government shall have only "Restricted Rights" as defined
 * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
 * foregoing, the authors grant the U.S. Government and others acting
 * in its behalf permission to use and distribute the software in
 * accordance with the terms specified in this license.
70
71
72
73
74
75
76
 *
 */
/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
77
78
79
80
81
82
83
84
 */

#include "gtksignal.h"
#include "gtktextlayout.h"
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"

#include <stdlib.h>
85
#include <string.h>
86
87

static GtkTextLineData    *gtk_text_line_data_new                 (GtkTextLayout     *layout,
88
                                                                   GtkTextLine       *line);
89
90

static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
91
92
93
                                                   GtkTextLine *line,
                                                   /* may be NULL */
                                                   GtkTextLineData *line_data);
94
95

static void gtk_text_layout_real_get_log_attrs (GtkTextLayout  *layout,
96
97
98
                                                GtkTextLine    *line,
                                                PangoLogAttr  **attrs,
                                                gint           *n_attrs);
99
100
101
102

static void gtk_text_layout_invalidated     (GtkTextLayout     *layout);

static void gtk_text_layout_real_invalidate     (GtkTextLayout     *layout,
103
104
                                                 const GtkTextIter *start,
                                                 const GtkTextIter *end);
105
static void gtk_text_layout_invalidate_cache    (GtkTextLayout     *layout,
106
                                                 GtkTextLine       *line);
107
static void gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
108
109
                                                 GtkTextLine       *line,
                                                 GtkTextLineData   *line_data);
110
111
112
113
114
115
116
117

static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);

static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);

enum {
  INVALIDATED,
  CHANGED,
118
  ALLOCATE_CHILD,
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  LAST_SIGNAL
};

enum {
  ARG_0,
  LAST_ARG
};

static void gtk_text_layout_init (GtkTextLayout *text_layout);
static void gtk_text_layout_class_init (GtkTextLayoutClass *klass);
static void gtk_text_layout_destroy (GtkObject *object);
static void gtk_text_layout_finalize (GObject *object);


static GtkObjectClass *parent_class = NULL;
static guint signals[LAST_SIGNAL] = { 0 };

PangoAttrType gtk_text_attr_appearance_type = 0;

GtkType
gtk_text_layout_get_type (void)
{
  static GtkType our_type = 0;

  if (our_type == 0)
    {
      static const GtkTypeInfo our_info =
      {
        "GtkTextLayout",
        sizeof (GtkTextLayout),
        sizeof (GtkTextLayoutClass),
        (GtkClassInitFunc) gtk_text_layout_class_init,
        (GtkObjectInitFunc) gtk_text_layout_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL
      };

157
158
      our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
    }
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

  return our_type;
}

static void
gtk_text_layout_class_init (GtkTextLayoutClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);

  parent_class = gtk_type_class (GTK_TYPE_OBJECT);

  signals[INVALIDATED] =
    gtk_signal_new ("invalidated",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
176
                    gtk_marshal_VOID__VOID,
177
178
179
180
181
182
183
184
                    GTK_TYPE_NONE,
                    0);

  signals[CHANGED] =
    gtk_signal_new ("changed",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
185
                    gtk_marshal_VOID__INT_INT_INT,
186
187
188
189
190
191
                    GTK_TYPE_NONE,
                    3,
                    GTK_TYPE_INT,
                    GTK_TYPE_INT,
                    GTK_TYPE_INT);

192
193
194
195
196
197
198
199
200
201
202
203
  signals[ALLOCATE_CHILD] =
    gtk_signal_new ("allocate_child",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextLayoutClass, allocate_child),
                    gtk_marshal_VOID__OBJECT_INT_INT,
                    GTK_TYPE_NONE,
                    3,
                    GTK_TYPE_OBJECT,
                    GTK_TYPE_INT,
                    GTK_TYPE_INT);
  
204
  gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
205

206
207
208
209
210
211
212
213
214
215
216
217
  object_class->destroy = gtk_text_layout_destroy;
  gobject_class->finalize = gtk_text_layout_finalize;

  klass->wrap = gtk_text_layout_real_wrap;
  klass->get_log_attrs = gtk_text_layout_real_get_log_attrs;
  klass->invalidate = gtk_text_layout_real_invalidate;
  klass->free_line_data = gtk_text_layout_real_free_line_data;
}

void
gtk_text_layout_init (GtkTextLayout *text_layout)
{
218
  text_layout->cursor_visible = TRUE;
219
220
221
222
223
224
225
226
227
228
229
230
231
}

GtkTextLayout*
gtk_text_layout_new (void)
{
  return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
}

static void
free_style_cache (GtkTextLayout *text_layout)
{
  if (text_layout->one_style_cache)
    {
232
      gtk_text_attributes_unref (text_layout->one_style_cache);
233
234
235
236
237
238
239
240
241
242
243
      text_layout->one_style_cache = NULL;
    }
}

static void
gtk_text_layout_destroy (GtkObject *object)
{
  GtkTextLayout *layout;

  layout = GTK_TEXT_LAYOUT (object);

244
  gtk_text_layout_set_buffer (layout, NULL);
245
246

  if (layout->default_style)
247
    gtk_text_attributes_unref (layout->default_style);
248
249
250
251
  layout->default_style = NULL;

  if (layout->ltr_context)
    {
252
      g_object_unref (G_OBJECT (layout->ltr_context));
253
254
255
256
      layout->ltr_context = NULL;
    }
  if (layout->rtl_context)
    {
257
      g_object_unref (G_OBJECT (layout->rtl_context));
258
259
      layout->rtl_context = NULL;
    }
260

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  (* parent_class->destroy) (object);
}

static void
gtk_text_layout_finalize (GObject *object)
{
  GtkTextLayout *text_layout;

  text_layout = GTK_TEXT_LAYOUT (object);

  (* G_OBJECT_CLASS (parent_class)->finalize) (object);
}

void
gtk_text_layout_set_buffer (GtkTextLayout *layout,
                            GtkTextBuffer *buffer)
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
280

281
282
283
284
  if (layout->buffer == buffer)
    return;

  free_style_cache (layout);
285

286
287
  if (layout->buffer)
    {
288
289
      gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
                                  layout);
290

291
292
293
294
295
296
297
298
299
300
301
      gtk_object_unref (GTK_OBJECT (layout->buffer));
      layout->buffer = NULL;
    }

  if (buffer)
    {
      layout->buffer = buffer;

      gtk_object_sink (GTK_OBJECT (buffer));
      gtk_object_ref (GTK_OBJECT (buffer));

302
      gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
303
304
305
306
307
308
309
    }
}

void
gtk_text_layout_default_style_changed (GtkTextLayout *layout)
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
310

311
312
313
314
315
  gtk_text_layout_invalidate_all (layout);
}

void
gtk_text_layout_set_default_style (GtkTextLayout *layout,
316
                                   GtkTextAttributes *values)
317
318
319
320
321
322
323
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (values != NULL);

  if (values == layout->default_style)
    return;

324
  gtk_text_attributes_ref (values);
325

326
  if (layout->default_style)
327
    gtk_text_attributes_unref (layout->default_style);
328
329
330
331
332
333
334
335

  layout->default_style = values;

  gtk_text_layout_default_style_changed (layout);
}

void
gtk_text_layout_set_contexts (GtkTextLayout *layout,
336
337
                              PangoContext  *ltr_context,
                              PangoContext  *rtl_context)
338
339
340
341
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));

  if (layout->ltr_context)
342
    g_object_unref (G_OBJECT (ltr_context));
343
344

  layout->ltr_context = ltr_context;
345
  g_object_ref (G_OBJECT (ltr_context));
346

347
  if (layout->rtl_context)
348
    g_object_unref (G_OBJECT (rtl_context));
349
350

  layout->rtl_context = rtl_context;
351
  g_object_ref (G_OBJECT (rtl_context));
352

353
354
355
356
357
358
359
360
361
  gtk_text_layout_invalidate_all (layout);
}

void
gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (width >= 0);
  g_return_if_fail (layout->wrap_loop_count == 0);
362

363
364
  if (layout->screen_width == width)
    return;
365

366
  layout->screen_width = width;
367

368
369
370
  gtk_text_layout_invalidate_all (layout);
}

371
372
373
374
/**
 * gtk_text_layout_set_cursor_visible:
 * @layout: a #GtkTextLayout
 * @cursor_visible: If %FALSE, then the insertion cursor will not
375
376
 *   be shown, even if the text is editable.
 *
377
378
379
380
381
382
 * Sets whether the insertion cursor should be shown. Generally,
 * widgets using #GtkTextLayout will hide the cursor when the
 * widget does not have the input focus.
 **/
void
gtk_text_layout_set_cursor_visible (GtkTextLayout *layout,
383
                                    gboolean       cursor_visible)
384
385
386
387
388
389
390
{
  cursor_visible = (cursor_visible != FALSE);

  if (layout->cursor_visible != cursor_visible)
    {
      GtkTextIter iter;
      gint y, height;
391

392
393
394
395
396
      layout->cursor_visible = cursor_visible;

      /* Now queue a redraw on the paragraph containing the cursor
       */
      gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
397
                                        gtk_text_buffer_get_mark (layout->buffer, "insert"));
398
399
400
401
402
403
404
405
406
407
408

      gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
      gtk_text_layout_changed (layout, y, height, height);

      gtk_text_layout_invalidate_cache (layout, gtk_text_iter_get_text_line (&iter));
    }
}

/**
 * gtk_text_layout_get_cursor_visible:
 * @layout: a #GtkTextLayout
409
 *
410
 * Returns whether the insertion cursor will be shown.
411
 *
412
413
414
415
416
417
418
419
420
 * Return value: if %FALSE, the insertion cursor will not be
    shown, even if the text is editable.
 **/
gboolean
gtk_text_layout_get_cursor_visible (GtkTextLayout *layout)
{
  return layout->cursor_visible;
}

421
422
423
424
425
426
void
gtk_text_layout_get_size (GtkTextLayout *layout,
                          gint *width,
                          gint *height)
{
  gint w, h;
427

428
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
429

430
431
  gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
                                layout,
432
433
434
435
                                &w, &h);

  layout->width = w;
  layout->height = h;
436

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
  if (width)
    *width = layout->width;

  if (height)
    *height = layout->height;
}

static void
gtk_text_layout_invalidated (GtkTextLayout *layout)
{
  gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
}

void
gtk_text_layout_changed (GtkTextLayout *layout,
452
453
454
                         gint           y,
                         gint           old_height,
                         gint           new_height)
455
456
457
458
459
460
{
  gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
}

void
gtk_text_layout_free_line_data (GtkTextLayout     *layout,
461
462
                                GtkTextLine       *line,
                                GtkTextLineData   *line_data)
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
{
  (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
    (layout, line, line_data);
}

void
gtk_text_layout_invalidate (GtkTextLayout *layout,
                            const GtkTextIter *start_index,
                            const GtkTextIter *end_index)
{
  (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
    (layout, start_index, end_index);
}

GtkTextLineData*
gtk_text_layout_wrap (GtkTextLayout *layout,
479
480
481
                      GtkTextLine  *line,
                      /* may be NULL */
                      GtkTextLineData *line_data)
482
483
484
485
486
487
{
  return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
}

void
gtk_text_layout_get_log_attrs (GtkTextLayout  *layout,
488
489
490
                               GtkTextLine    *line,
                               PangoLogAttr  **attrs,
                               gint           *n_attrs)
491
492
493
494
495
496
497
498
{
  (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->get_log_attrs)
    (layout, line, attrs, n_attrs);
}

GSList*
gtk_text_layout_get_lines (GtkTextLayout *layout,
                           /* [top_y, bottom_y) */
499
                           gint top_y,
500
501
502
503
504
505
506
                           gint bottom_y,
                           gint *first_line_y)
{
  GtkTextLine *first_btree_line;
  GtkTextLine *last_btree_line;
  GtkTextLine *line;
  GSList *retval;
507

508
509
510
511
  g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
  g_return_val_if_fail (bottom_y > top_y, NULL);

  retval = NULL;
512

513
514
515
  first_btree_line =
    gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
                                   layout, top_y, first_line_y);
516
517
518
519
520
521
  if (first_btree_line == NULL)
    {
      g_assert (top_y > 0);
      /* off the bottom */
      return NULL;
    }
522

523
  /* -1 since bottom_y is one past */
524
525
526
  last_btree_line =
    gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
                                   layout, bottom_y - 1, NULL);
527
528

  if (!last_btree_line)
529
530
531
532
    last_btree_line =
      gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
                               gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
                               NULL);
533
534
535
536

  {
    GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
    if (ld->height == 0)
537
      G_BREAKPOINT ();
538
  }
539

540
541
542
543
544
545
546
547
548
  g_assert (last_btree_line != NULL);

  line = first_btree_line;
  while (TRUE)
    {
      retval = g_slist_prepend (retval, line);

      if (line == last_btree_line)
        break;
549

550
551
      line = gtk_text_line_next (line);
    }
552

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
  retval = g_slist_reverse (retval);

  return retval;
}

static void
invalidate_cached_style (GtkTextLayout *layout)
{
  free_style_cache (layout);
}

/* These should be called around a loop which wraps a CONTIGUOUS bunch
 * of display lines. If the lines aren't contiguous you can't call
 * these.
 */
void
gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (layout->one_style_cache == NULL);
573

574
575
576
577
578
579
580
  layout->wrap_loop_count += 1;
}

void
gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
{
  g_return_if_fail (layout->wrap_loop_count > 0);
581

582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
  layout->wrap_loop_count -= 1;

  if (layout->wrap_loop_count == 0)
    {
      /* We cache a some stuff if we're iterating over some lines wrapping
       * them. This cleans it up.
       */
      /* Nuke our cached style */
      invalidate_cached_style (layout);
      g_assert (layout->one_style_cache == NULL);
    }
}

static void
gtk_text_layout_invalidate_all (GtkTextLayout *layout)
{
  GtkTextIter start;
  GtkTextIter end;
600

601
602
603
604
605
606
607
608
  if (layout->buffer == NULL)
    return;

  gtk_text_buffer_get_bounds (layout->buffer, &start, &end);

  gtk_text_layout_invalidate (layout, &start, &end);
}

609
610
static void
gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
611
                                  GtkTextLine   *line)
612
613
614
615
616
617
618
619
620
{
  if (layout->one_display_cache && line == layout->one_display_cache->line)
    {
      GtkTextLineDisplay *tmp_display = layout->one_display_cache;
      layout->one_display_cache = NULL;
      gtk_text_layout_free_line_display (layout, tmp_display);
    }
}

621
622
623
624
625
626
627
static void
gtk_text_layout_real_invalidate (GtkTextLayout *layout,
                                 const GtkTextIter *start,
                                 const GtkTextIter *end)
{
  GtkTextLine *line;
  GtkTextLine *last_line;
628

629
630
631
632
633
634
635
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (layout->wrap_loop_count == 0);

#if 0
  gtk_text_view_index_spew (start_index, "invalidate start");
  gtk_text_view_index_spew (end_index, "invalidate end");
#endif
636

637
638
  last_line = gtk_text_iter_get_text_line (end);
  line = gtk_text_iter_get_text_line (start);
639
640
641
642
643
644

  while (TRUE)
    {
      GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);

      if (line_data &&
645
646
647
648
649
650
          (line != last_line || !gtk_text_iter_starts_line (end)))
        {
          gtk_text_layout_invalidate_cache (layout, line);
          gtk_text_line_invalidate_wrap (line, line_data);
        }

651
652
      if (line == last_line)
        break;
653

654
655
      line = gtk_text_line_next (line);
    }
656

657
658
659
660
661
  gtk_text_layout_invalidated (layout);
}

static void
gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
662
663
                                     GtkTextLine       *line,
                                     GtkTextLineData   *line_data)
664
665
666
667
668
669
670
{
  if (layout->one_display_cache && line == layout->one_display_cache->line)
    {
      GtkTextLineDisplay *tmp_display = layout->one_display_cache;
      layout->one_display_cache = NULL;
      gtk_text_layout_free_line_display (layout, tmp_display);
    }
671

672
673
674
675
676
677
678
679
  g_free (line_data);
}



/**
 * gtk_text_layout_is_valid:
 * @layout: a #GtkTextLayout
680
 *
681
 * Check if there are any invalid regions in a #GtkTextLayout's buffer
682
 *
683
684
685
686
687
688
689
 * Return value: #TRUE if any invalid regions were found
 **/
gboolean
gtk_text_layout_is_valid (GtkTextLayout *layout)
{
  g_return_val_if_fail (layout != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
690

691
692
  return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
                                  layout);
693
694
695
696
697
698
699
700
701
702
703
704
705
}

/**
 * gtk_text_layout_validate_yrange:
 * @layout: a #GtkTextLayout
 * @anchor: iter pointing into a line that will be used as the
 *          coordinate origin
 * @y0: offset from the top of the line pointed to by @anchor at
 *      which to begin validation. (The offset here is in pixels
 *      after validation.)
 * @y1: offset from the top of the line pointed to by @anchor at
 *      which to end validation. (The offset here is in pixels
 *      after validation.)
706
707
 *
 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
708
709
710
711
 * signal will be emitted if any lines are validated.
 **/
void
gtk_text_layout_validate_yrange (GtkTextLayout *layout,
712
713
714
                                 GtkTextIter   *anchor,
                                 gint           y0,
                                 gint           y1)
715
716
717
718
719
720
{
  GtkTextLine *line;
  GtkTextLine *first_line = NULL;
  GtkTextLine *last_line = NULL;
  gint seen;
  gint delta_height = 0;
721
722
723
  gint first_line_y = 0;        /* Quiet GCC */
  gint last_line_y = 0;         /* Quiet GCC */

724
725
  g_return_if_fail (layout != NULL);
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
726

727
728
729
730
  if (y0 > 0)
    y0 = 0;
  if (y1 < 0)
    y1 = 0;
731

732
733
  /* Validate backwards from the anchor line to y0
   */
734
  line = gtk_text_iter_get_text_line (anchor);
735
736
737
738
739
  seen = 0;
  while (line && seen < -y0)
    {
      GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
      if (!line_data || !line_data->valid)
740
741
742
743
        {
          gint old_height = line_data ? line_data->height : 0;

          gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
744
                                        line, layout);
745
746
747
748
749
750
751
752
753
754
755
756
757
          line_data = gtk_text_line_get_data (line, layout);

          delta_height += line_data->height - old_height;

          first_line = line;
          first_line_y = -seen;
          if (!last_line)
            {
              last_line = line;
              last_line_y = -seen + line_data->height;
            }
        }

758
759
760
761
762
      seen += line_data->height;
      line = gtk_text_line_previous (line);
    }

  /* Validate forwards to y1 */
763
  line = gtk_text_iter_get_text_line (anchor);
764
765
766
767
768
  seen = 0;
  while (line && seen < y1)
    {
      GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
      if (!line_data || !line_data->valid)
769
770
771
772
        {
          gint old_height = line_data ? line_data->height : 0;

          gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
773
                                        line, layout);
774
775
776
777
778
779
780
781
782
783
784
785
786
          line_data = gtk_text_line_get_data (line, layout);

          delta_height += line_data->height - old_height;

          if (!first_line)
            {
              first_line = line;
              first_line_y = seen;
            }
          last_line = line;
          last_line_y = seen + line_data->height;
        }

787
788
789
790
791
792
793
794
      seen += line_data->height;
      line = gtk_text_line_next (line);
    }

  /* If we found and validated any invalid lines, emit the changed singal
   */
  if (first_line)
    {
795
796
      gint line_top =
        gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
797
798
                                      first_line, layout);

799
      gtk_text_layout_changed (layout,
800
801
802
                               line_top,
                               last_line_y - first_line_y - delta_height,
                               last_line_y - first_line_y);
803
804
805
806
807
808
809
810
    }
}

/**
 * gtk_text_layout_validate:
 * @tree: a #GtkTextLayout
 * @max_pixels: the maximum number of pixels to validate. (No more
 *              than one paragraph beyond this limit will be validated)
811
 *
812
813
814
815
816
 * Validate regions of a #GtkTextLayout. The ::changed signal will
 * be emitted for each region validated.
 **/
void
gtk_text_layout_validate (GtkTextLayout *layout,
817
                          gint           max_pixels)
818
819
820
821
822
{
  gint y, old_height, new_height;

  g_return_if_fail (layout != NULL);
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
823

824
  while (max_pixels > 0 &&
825
         gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
826
                                  layout,  max_pixels,
827
                                  &y, &old_height, &new_height))
828
829
830
831
832
833
834
835
    {
      max_pixels -= new_height;
      gtk_text_layout_changed (layout, y, old_height, new_height);
    }
}

static GtkTextLineData*
gtk_text_layout_real_wrap (GtkTextLayout   *layout,
836
837
838
                           GtkTextLine     *line,
                           /* may be NULL */
                           GtkTextLineData *line_data)
839
840
{
  GtkTextLineDisplay *display;
841

842
  g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
843

844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
  if (line_data == NULL)
    {
      line_data = gtk_text_line_data_new (layout, line);
      gtk_text_line_add_data (line, line_data);
    }

  display = gtk_text_layout_get_line_display (layout, line, TRUE);
  line_data->width = display->width;
  line_data->height = display->height;
  line_data->valid = TRUE;
  gtk_text_layout_free_line_display (layout, display);

  return line_data;
}

static void
gtk_text_layout_real_get_log_attrs (GtkTextLayout  *layout,
861
862
863
                                    GtkTextLine    *line,
                                    PangoLogAttr  **attrs,
                                    gint           *n_attrs)
864
865
{
  GtkTextLineDisplay *display;
866

867
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
868

869
870
871
872
873
874
875
876
877
878
879
  display = gtk_text_layout_get_line_display (layout, line, TRUE);
  pango_layout_get_log_attrs (display->layout, attrs, n_attrs);
  gtk_text_layout_free_line_display (layout, display);
}

/*
 * Layout utility functions
 */

/* If you get the style with get_style () you need to call
   release_style () to free it. */
880
static GtkTextAttributes*
881
get_style (GtkTextLayout *layout,
882
           const GtkTextIter *iter)
883
884
885
{
  GtkTextTag** tags;
  gint tag_count = 0;
886
  GtkTextAttributes *style;
887

888
889
890
891
892
893
  /* If we have the one-style cache, then it means
     that we haven't seen a toggle since we filled in the
     one-style cache.
  */
  if (layout->one_style_cache != NULL)
    {
894
      gtk_text_attributes_ref (layout->one_style_cache);
895
896
897
898
      return layout->one_style_cache;
    }

  g_assert (layout->one_style_cache == NULL);
899

900
901
902
903
904
905
906
907
  /* Get the tags at this spot */
  tags = gtk_text_btree_get_tags (iter, &tag_count);

  /* No tags, use default style */
  if (tags == NULL || tag_count == 0)
    {
      /* One ref for the return value, one ref for the
         layout->one_style_cache reference */
908
909
      gtk_text_attributes_ref (layout->default_style);
      gtk_text_attributes_ref (layout->default_style);
910
911
912
913
914
915
916
      layout->one_style_cache = layout->default_style;

      if (tags)
        g_free (tags);

      return layout->default_style;
    }
917

918
919
  /* Sort tags in ascending order of priority */
  gtk_text_tag_array_sort (tags, tag_count);
920

921
  style = gtk_text_attributes_new ();
922

923
  gtk_text_attributes_copy (layout->default_style,
924
                            style);
925

926
  gtk_text_attributes_fill_from_tags (style,
927
928
                                      tags,
                                      tag_count);
929
930
931
932
933
934
935

  g_free (tags);

  g_assert (style->refcount == 1);

  /* Leave this style as the last one seen */
  g_assert (layout->one_style_cache == NULL);
936
  gtk_text_attributes_ref (style); /* ref held by layout->one_style_cache */
937
  layout->one_style_cache = style;
938

939
940
941
942
943
944
  /* Returning yet another refcount */
  return style;
}

static void
release_style (GtkTextLayout *layout,
945
               GtkTextAttributes *style)
946
947
948
949
{
  g_return_if_fail (style != NULL);
  g_return_if_fail (style->refcount > 0);

950
  gtk_text_attributes_unref (style);
951
952
953
954
955
956
957
958
959
960
}

/*
 * Lines
 */

/* This function tries to optimize the case where a line
   is completely invisible */
static gboolean
totally_invisible_line (GtkTextLayout *layout,
961
962
                        GtkTextLine   *line,
                        GtkTextIter   *iter)
963
964
965
{
  GtkTextLineSegment *seg;
  int bytes = 0;
966

967
  /* If we have a cached style, then we know it does actually apply
968
     and we can just see if it is invisible. */
969
  if (layout->one_style_cache &&
970
      !layout->one_style_cache->invisible)
971
972
973
    return FALSE;
  /* Without the cache, we check if the first char is visible, if so
     we are partially visible.  Note that we have to check this since
974
     we don't know the current invisible/noninvisible toggle state; this
975
976
977
     function can use the whole btree to get it right. */
  else
    {
978
979
980
      gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
                                       iter, line, 0);

981
      if (!gtk_text_btree_char_is_invisible (iter))
982
        return FALSE;
983
984
985
986
987
988
989
990
991
992
993
994
995
    }

  bytes = 0;
  seg = line->segments;

  while (seg != NULL)
    {
      if (seg->byte_count > 0)
        bytes += seg->byte_count;

      /* Note that these two tests can cause us to bail out
         when we shouldn't, because a higher-priority tag
         may override these settings. However the important
996
997
         thing is to only invisible really-invisible lines, rather
         than to invisible all really-invisible lines. */
998

999
1000
1001
      else if (seg->type == &gtk_text_toggle_on_type)
        {
          invalidate_cached_style (layout);
1002

1003
          /* Bail out if an elision-unsetting tag begins */
1004
1005
          if (seg->body.toggle.info->tag->invisible_set &&
              !seg->body.toggle.info->tag->values->invisible)
1006
1007
1008
1009
1010
            break;
        }
      else if (seg->type == &gtk_text_toggle_off_type)
        {
          invalidate_cached_style (layout);
1011

1012
          /* Bail out if an elision-setting tag ends */
1013
1014
          if (seg->body.toggle.info->tag->invisible_set &&
              seg->body.toggle.info->tag->values->invisible)
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
            break;
        }

      seg = seg->next;
    }

  if (seg != NULL)       /* didn't reach line end */
    return FALSE;

  return TRUE;
}

static void
set_para_values (GtkTextLayout      *layout,
1029
1030
1031
                 GtkTextAttributes *style,
                 GtkTextLineDisplay *display,
                 gdouble            *align)
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
{
  PangoAlignment pango_align = PANGO_ALIGN_LEFT;
  int layout_width;

  display->direction = style->direction;

  if (display->direction == GTK_TEXT_DIR_LTR)
    display->layout = pango_layout_new (layout->ltr_context);
  else
    display->layout = pango_layout_new (layout->rtl_context);

  switch (style->justify)
    {
    case GTK_JUSTIFY_LEFT:
      pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
      break;
    case GTK_JUSTIFY_RIGHT:
      pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
      break;
    case GTK_JUSTIFY_CENTER:
      pango_align = PANGO_ALIGN_CENTER;
      break;
    case GTK_JUSTIFY_FILL:
      g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
      break;
    default:
      g_assert_not_reached ();
      break;
    }

  switch (pango_align)
    {
1064
1065
1066
1067
1068
1069
1070
1071
1072
    case PANGO_ALIGN_LEFT:
      *align = 0.0;
      break;
    case PANGO_ALIGN_RIGHT:
      *align = 1.0;
      break;
    case PANGO_ALIGN_CENTER:
      *align = 0.5;
      break;
1073
1074
1075
1076
1077
    }

  pango_layout_set_alignment (display->layout, pango_align);
  pango_layout_set_spacing (display->layout, style->pixels_inside_wrap * PANGO_SCALE);

1078
1079
  if (style->tabs)
    pango_layout_set_tabs (display->layout, style->tabs);
1080

1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  display->top_margin = style->pixels_above_lines;
  display->height = style->pixels_above_lines + style->pixels_below_lines;
  display->bottom_margin = style->pixels_below_lines;
  display->x_offset = display->left_margin = MIN (style->left_margin, style->left_wrapped_line_margin);
  display->right_margin = style->right_margin;

  pango_layout_set_indent (display->layout, style->left_margin - style->left_wrapped_line_margin);

  switch (style->wrap_mode)
    {
    case GTK_WRAPMODE_CHAR:
      /* FIXME: Handle this; for now, fall-through */
    case GTK_WRAPMODE_WORD:
      display->total_width = -1;
      layout_width = layout->screen_width - display->x_offset - style->right_margin;
      pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
      break;
    case GTK_WRAPMODE_NONE:
      display->total_width = MAX (layout->screen_width, layout->width) - display->x_offset - style->right_margin;
      break;
    }
}

static PangoAttribute *
gtk_text_attr_appearance_copy (const PangoAttribute *attr)
{
  const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
1108

1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
  return gtk_text_attr_appearance_new (&appearance_attr->appearance);
}

static void
gtk_text_attr_appearance_destroy (PangoAttribute *attr)
{
  GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;

  if (appearance->bg_stipple)
    gdk_drawable_unref (appearance->bg_stipple);
  if (appearance->fg_stipple)
    gdk_drawable_unref (appearance->fg_stipple);
1121

1122
1123
1124
1125
1126
  g_free (attr);
}

static gboolean
gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1127
                                  const PangoAttribute *attr2)
1128
1129
1130
1131
1132
{
  const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
  const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;

  return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1133
1134
1135
1136
1137
1138
1139
          gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
          appearance1->fg_stipple ==  appearance2->fg_stipple &&
          appearance1->bg_stipple ==  appearance2->bg_stipple &&
          appearance1->underline == appearance2->underline &&
          appearance1->strikethrough == appearance2->strikethrough &&
          appearance1->draw_bg == appearance2->draw_bg);

1140
1141
1142
1143
}

/**
 * gtk_text_attr_appearance_new:
1144
1145
 * @desc:
 *
1146
1147
1148
 * Create a new font description attribute. (This attribute
 * allows setting family, style, weight, variant, stretch,
 * and size simultaneously.)
1149
1150
 *
 * Return value:
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
 **/
static PangoAttribute *
gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
{
  static PangoAttrClass klass = {
    0,
    gtk_text_attr_appearance_copy,
    gtk_text_attr_appearance_destroy,
    gtk_text_attr_appearance_compare
  };

  GtkTextAttrAppearance *result;

  if (!klass.type)
    klass.type = gtk_text_attr_appearance_type =
      pango_attr_type_register ("GtkTextAttrAppearance");

  result = g_new (GtkTextAttrAppearance, 1);
  result->attr.klass = &klass;

  result->appearance = *appearance;
1172

1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
  if (appearance->bg_stipple)
    gdk_drawable_ref (appearance->bg_stipple);
  if (appearance->fg_stipple)
    gdk_drawable_ref (appearance->fg_stipple);

  return (PangoAttribute *)result;
}

static void
add_text_attrs (GtkTextLayout      *layout,
1183
1184
1185
1186
1187
                GtkTextAttributes *style,
                gint                byte_count,
                PangoAttrList      *attrs,
                gint                start,
                gboolean            size_only)
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
{
  PangoAttribute *attr;

  attr = pango_attr_font_desc_new (style->font_desc);
  attr->start_index = start;
  attr->end_index = start + byte_count;

  pango_attr_list_insert (attrs, attr);

  if (!size_only)
    {
      attr = gtk_text_attr_appearance_new (&style->appearance);
1200

1201
1202
1203
      attr->start_index = start;
      attr->end_index = start + byte_count;

1204
      pango_attr_list_insert (attrs, attr);
1205
1206
1207
1208
    }
}

static void
Havoc Pennington's avatar
Havoc Pennington committed
1209
add_pixbuf_attrs (GtkTextLayout      *layout,
1210
                  GtkTextLineDisplay *display,
1211
                  GtkTextAttributes  *style,
1212
1213
1214
                  GtkTextLineSegment *seg,
                  PangoAttrList      *attrs,
                  gint                start)
1215
{
1216
1217
  PangoAttribute *attr;
  PangoRectangle logical_rect;
Havoc Pennington's avatar
Havoc Pennington committed
1218
  GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1219
1220
  gint width, height;

Havoc Pennington's avatar
Havoc Pennington committed
1221
1222
1223
  width = gdk_pixbuf_get_width (pixbuf->pixbuf);
  height = gdk_pixbuf_get_height (pixbuf->pixbuf);

1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
  logical_rect.x = 0;
  logical_rect.y = -height * PANGO_SCALE;
  logical_rect.width = width * PANGO_SCALE;
  logical_rect.height = height * PANGO_SCALE;

  attr = pango_attr_shape_new (&logical_rect, &logical_rect);
  attr->start_index = start;
  attr->end_index = start + seg->byte_count;
  pango_attr_list_insert (attrs, attr);

1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
  display->shaped_objects =
    g_slist_append (display->shaped_objects, pixbuf->pixbuf);
}

static void
add_child_attrs (GtkTextLayout      *layout,
                 GtkTextLineDisplay *display,
                 GtkTextAttributes  *style,
                 GtkTextLineSegment *seg,
                 PangoAttrList      *attrs,
                 gint                start)
{
  PangoAttribute *attr;
  PangoRectangle logical_rect;
  GtkTextChildAnchor *anchor;
  gint width, height;
  GSList *tmp_list;

  width = 1;
  height = 1;
  
  anchor = seg->body.child.obj;

  tmp_list = seg->body.child.widgets;
  while (tmp_list != NULL)
    {
      GtkWidget *child = tmp_list->data;

      if (_gtk_anchored_child_get_layout (child) == layout)
        {
          /* Found it */
          GtkRequisition req;

          gtk_widget_get_child_requisition (child, &req);
          
          width = req.width;
          height = req.height;

          display->shaped_objects =
            g_slist_append (display->shaped_objects, child);                           
          break;
        }
      
      tmp_list = g_slist_next (tmp_list);
    }

  if (tmp_list == NULL)
    {
      /* No widget at this anchor in this display;
       * not an error.
       */

      return;
    }
  
  logical_rect.x = 0;
  logical_rect.y = -height * PANGO_SCALE;
  logical_rect.width = width * PANGO_SCALE;
  logical_rect.height = height * PANGO_SCALE;

  attr = pango_attr_shape_new (&logical_rect, &logical_rect);
  attr->start_index = start;
  attr->end_index = start + seg->byte_count;
  pango_attr_list_insert (attrs, attr);
1298
1299
1300
1301
}

static void
add_cursor (GtkTextLayout      *layout,
1302
1303
1304
            GtkTextLineDisplay *display,
            GtkTextLineSegment *seg,
            gint                start)
1305
1306
1307
1308
{
  PangoRectangle strong_pos, weak_pos;
  GtkTextCursorDisplay *cursor;

1309
1310
  /* Hide insertion cursor when we have a selection or the layout
   * user has hidden the cursor.
1311
   */
1312
  if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
Havoc Pennington's avatar
Havoc Pennington committed
1313
                                     seg->body.mark.obj) &&
1314
1315
      (!layout->cursor_visible ||
       gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1316
    return;
1317

1318
  pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1319

1320
  cursor = g_new (GtkTextCursorDisplay, 1);
1321

1322
1323
1324
1325
1326
  cursor->x = strong_pos.x / PANGO_SCALE;
  cursor->y = strong_pos.y / PANGO_SCALE;
  cursor->height = strong_pos.height / PANGO_SCALE;
  cursor->is_strong = TRUE;
  display->cursors = g_slist_prepend (display->cursors, cursor);
1327

1328
1329
1330
1331
1332
  if (weak_pos.x == strong_pos.x)
    cursor->is_weak = TRUE;
  else
    {
      cursor->is_weak = FALSE;
1333

1334
      cursor = g_new (GtkTextCursorDisplay, 1);
1335

1336
1337
1338
1339
1340
1341
1342
1343
1344
      cursor->x = weak_pos.x / PANGO_SCALE;
      cursor->y = weak_pos.y / PANGO_SCALE;
      cursor->height = weak_pos.height / PANGO_SCALE;
      cursor->is_strong = FALSE;
      cursor->is_weak = TRUE;
      display->cursors = g_slist_prepend (display->cursors, cursor);
    }
}

1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
static void
allocate_child_widgets (GtkTextLayout      *layout,
                        GtkTextLineDisplay *display)
{
  
#if 0
  gtk_signal_emit (GTK_OBJECT (layout),
                   signals[ALLOCATE_CHILD],
                   child,
                   x, y);
#endif
}

1358
1359
GtkTextLineDisplay *
gtk_text_layout_get_line_display (GtkTextLayout *layout,
1360
1361
                                  GtkTextLine   *line,
                                  gboolean       size_only)
1362
1363
1364
1365
{
  GtkTextLineDisplay *display;
  GtkTextLineSegment *seg;
  GtkTextIter iter;
1366
  GtkTextAttributes *style;
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
  gchar *text;
  PangoAttrList *attrs;
  gint byte_count, byte_offset;
  gdouble align;
  PangoRectangle extents;
  gboolean para_values_set = FALSE;
  GSList *cursor_byte_offsets = NULL;
  GSList *cursor_segs = NULL;
  GSList *tmp_list1, *tmp_list2;

  g_return_val_if_fail (line != NULL, NULL);

  if (layout->one_display_cache)
    {
      if (line == layout->one_display_cache->line &&
1382
1383
          (size_only || !layout->one_display_cache->size_only))
        return layout->one_display_cache;
1384
      else
1385
1386
1387
1388
1389
        {
          GtkTextLineDisplay *tmp_display = layout->one_display_cache;
          layout->one_display_cache = NULL;
          gtk_text_layout_free_line_display (layout, tmp_display);
        }
1390
    }
1391

1392
1393
1394
1395
1396
  display = g_new0 (GtkTextLineDisplay, 1);

  display->size_only = size_only;
  display->line = line;

1397
1398
  gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
                                   &iter, line, 0);
1399

1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
  /* Special-case optimization for completely
   * invisible lines; makes it faster to deal
   * with sequences of invisible lines.
   */
  if (totally_invisible_line (layout, line, &iter))
    return display;

  /* Allocate space for flat text for buffer
   */
  byte_count = gtk_text_line_byte_count (line);
  text = g_malloc (byte_count);

  attrs = pango_attr_list_new ();

  /* Iterate over segments, creating display chunks for them. */
  byte_offset = 0;
  seg = gtk_text_iter_get_any_segment (&iter);
  while (seg != NULL)
    {
      /* Displayable segments */
      if (seg->type == &gtk_text_char_type ||
1421
1422
          seg->type == &gtk_text_pixbuf_type ||
          seg->type == &gtk_text_child_type)
1423
        {
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
          gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
                                           &iter, line,
                                           byte_offset);
          style = get_style (layout, &iter);

          /* We have to delay setting the paragraph values until we
           * hit the first pixbuf or text segment because toggles at
           * the beginning of the paragraph should affect the
           * paragraph-global values
           */
          if (!para_values_set)
            {
              set_para_values (layout, style, display, &align);
              para_values_set = TRUE;
            }
1439

1440
          /* First see if the chunk is invisible, and ignore it if so. Tk
1441
1442
1443
1444
           * looked at tabs, wrap mode, etc. before doing this, but
           * that made no sense to me, so I am just skipping the
           * invisible chunks
           */
1445
          if (!style->invisible)
1446
            {