gtktextlayout.c 78.5 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
96
97

static void gtk_text_layout_invalidated     (GtkTextLayout     *layout);

static void gtk_text_layout_real_invalidate     (GtkTextLayout     *layout,
98
99
                                                 const GtkTextIter *start,
                                                 const GtkTextIter *end);
100
static void gtk_text_layout_invalidate_cache    (GtkTextLayout     *layout,
101
                                                 GtkTextLine       *line);
102
static void gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
103
104
                                                 GtkTextLine       *line,
                                                 GtkTextLineData   *line_data);
105
106
107
108
109
110
111
112

static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);

static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);

enum {
  INVALIDATED,
  CHANGED,
113
  ALLOCATE_CHILD,
114
115
116
117
118
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
  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
      };

152
153
      our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
    }
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

  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),
171
                    gtk_marshal_VOID__VOID,
172
173
174
175
176
177
178
179
                    GTK_TYPE_NONE,
                    0);

  signals[CHANGED] =
    gtk_signal_new ("changed",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
180
                    gtk_marshal_VOID__INT_INT_INT,
181
182
183
184
185
186
                    GTK_TYPE_NONE,
                    3,
                    GTK_TYPE_INT,
                    GTK_TYPE_INT,
                    GTK_TYPE_INT);

187
188
189
190
191
192
193
194
195
196
197
198
  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);
  
199
  gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
200

201
202
203
204
205
206
207
208
209
210
211
  object_class->destroy = gtk_text_layout_destroy;
  gobject_class->finalize = gtk_text_layout_finalize;

  klass->wrap = gtk_text_layout_real_wrap;
  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)
{
212
  text_layout->cursor_visible = TRUE;
213
214
215
216
217
218
219
220
221
222
223
224
225
}

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)
    {
226
      gtk_text_attributes_unref (text_layout->one_style_cache);
227
228
229
230
231
232
233
234
235
236
237
      text_layout->one_style_cache = NULL;
    }
}

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

  layout = GTK_TEXT_LAYOUT (object);

238
  gtk_text_layout_set_buffer (layout, NULL);
239
240

  if (layout->default_style)
241
    gtk_text_attributes_unref (layout->default_style);
242
243
244
245
  layout->default_style = NULL;

  if (layout->ltr_context)
    {
246
      g_object_unref (G_OBJECT (layout->ltr_context));
247
248
249
250
      layout->ltr_context = NULL;
    }
  if (layout->rtl_context)
    {
251
      g_object_unref (G_OBJECT (layout->rtl_context));
252
253
      layout->rtl_context = NULL;
    }
254

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  (* 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));
274

275
276
277
278
  if (layout->buffer == buffer)
    return;

  free_style_cache (layout);
279

280
281
  if (layout->buffer)
    {
282
283
      gtk_text_btree_remove_view (_gtk_text_buffer_get_btree (layout->buffer),
                                  layout);
284

285
286
287
288
289
290
291
292
293
294
295
      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));

296
      gtk_text_btree_add_view (_gtk_text_buffer_get_btree (buffer), layout);
297
298
299
300
301
302
303
    }
}

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

305
306
307
308
309
  gtk_text_layout_invalidate_all (layout);
}

void
gtk_text_layout_set_default_style (GtkTextLayout *layout,
310
                                   GtkTextAttributes *values)
311
312
313
314
315
316
317
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (values != NULL);

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

318
  gtk_text_attributes_ref (values);
319

320
  if (layout->default_style)
321
    gtk_text_attributes_unref (layout->default_style);
322
323
324
325
326
327
328
329

  layout->default_style = values;

  gtk_text_layout_default_style_changed (layout);
}

void
gtk_text_layout_set_contexts (GtkTextLayout *layout,
330
331
                              PangoContext  *ltr_context,
                              PangoContext  *rtl_context)
332
333
334
335
{
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));

  if (layout->ltr_context)
336
    g_object_unref (G_OBJECT (ltr_context));
337
338

  layout->ltr_context = ltr_context;
339
  g_object_ref (G_OBJECT (ltr_context));
340

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

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

347
348
349
350
351
352
353
354
355
  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);
356

357
358
  if (layout->screen_width == width)
    return;
359

360
  layout->screen_width = width;
361

362
363
364
  gtk_text_layout_invalidate_all (layout);
}

365
366
367
368
/**
 * gtk_text_layout_set_cursor_visible:
 * @layout: a #GtkTextLayout
 * @cursor_visible: If %FALSE, then the insertion cursor will not
369
370
 *   be shown, even if the text is editable.
 *
371
372
373
374
375
376
 * 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,
377
                                    gboolean       cursor_visible)
378
379
380
381
382
383
384
{
  cursor_visible = (cursor_visible != FALSE);

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

386
387
388
389
390
      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,
391
                                        gtk_text_buffer_get_mark (layout->buffer, "insert"));
392
393
394
395
396
397
398
399
400
401
402

      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
403
 *
404
 * Returns whether the insertion cursor will be shown.
405
 *
406
407
408
409
410
411
412
413
414
 * 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;
}

Owen Taylor's avatar
Owen Taylor committed
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
/**
 * gtk_text_layout_set_preedit_string:
 * @layout: a #PangoLayout
 * @preedit_string: a string to display at the insertion point
 * @preedit_attrs: a #PangoAttrList of attributes that apply to @preedit_string
 * @cursor_pos: position of cursor within preedit string in chars
 * 
 * Set the preedit string and attributes. The preedit string is a
 * string showing text that is currently being edited and not
 * yet committed into the buffer.
 **/
void
gtk_text_layout_set_preedit_string (GtkTextLayout *layout,
				    const gchar   *preedit_string,
				    PangoAttrList *preedit_attrs,
				    gint           cursor_pos)
{
  GtkTextIter iter;
  GtkTextLine *line;
  GtkTextLineData *line_data;

  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (preedit_attrs != NULL || preedit_string == NULL);

  if (layout->preedit_string)
    g_free (layout->preedit_string);

  if (layout->preedit_attrs)
    pango_attr_list_unref (layout->preedit_attrs);

  if (preedit_string)
    {
      layout->preedit_string = g_strdup (preedit_string);
      layout->preedit_len = strlen (layout->preedit_string);
      pango_attr_list_ref (preedit_attrs);
      layout->preedit_attrs = preedit_attrs;

      cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (layout->preedit_string, -1));
      layout->preedit_cursor = g_utf8_offset_to_pointer (layout->preedit_string, cursor_pos) - layout->preedit_string;
    }
  else
    {
      layout->preedit_string = NULL;
      layout->preedit_len = 0;
      layout->preedit_attrs = NULL;
      layout->preedit_cursor = 0;
    }

  /* Now invalidate the paragraph containing the cursor
   */
  gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
				    gtk_text_buffer_get_mark (layout->buffer, "insert"));
  
  line = gtk_text_iter_get_text_line (&iter);
  line_data = gtk_text_line_get_data (line, layout);
  if (line_data)
    {
      gtk_text_layout_invalidate_cache (layout, line);
      gtk_text_line_invalidate_wrap (line, line_data);
      gtk_text_layout_invalidated (layout);
    }
}

478
479
480
481
482
483
void
gtk_text_layout_get_size (GtkTextLayout *layout,
                          gint *width,
                          gint *height)
{
  gint w, h;
484

485
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
486

487
488
  gtk_text_btree_get_view_size (_gtk_text_buffer_get_btree (layout->buffer),
                                layout,
489
490
491
492
                                &w, &h);

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

494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
  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,
509
510
511
                         gint           y,
                         gint           old_height,
                         gint           new_height)
512
513
514
515
516
517
{
  gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
}

void
gtk_text_layout_free_line_data (GtkTextLayout     *layout,
518
519
                                GtkTextLine       *line,
                                GtkTextLineData   *line_data)
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
{
  (* 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,
536
537
538
                      GtkTextLine  *line,
                      /* may be NULL */
                      GtkTextLineData *line_data)
539
540
541
542
543
544
545
{
  return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
}

GSList*
gtk_text_layout_get_lines (GtkTextLayout *layout,
                           /* [top_y, bottom_y) */
546
                           gint top_y,
547
548
549
550
551
552
553
                           gint bottom_y,
                           gint *first_line_y)
{
  GtkTextLine *first_btree_line;
  GtkTextLine *last_btree_line;
  GtkTextLine *line;
  GSList *retval;
554

555
556
557
558
  g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
  g_return_val_if_fail (bottom_y > top_y, NULL);

  retval = NULL;
559

560
561
562
  first_btree_line =
    gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
                                   layout, top_y, first_line_y);
563
564
565
566
567
568
  if (first_btree_line == NULL)
    {
      g_assert (top_y > 0);
      /* off the bottom */
      return NULL;
    }
569

570
  /* -1 since bottom_y is one past */
571
572
573
  last_btree_line =
    gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
                                   layout, bottom_y - 1, NULL);
574
575

  if (!last_btree_line)
576
577
578
579
    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);
580
581
582
583

  {
    GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
    if (ld->height == 0)
584
      G_BREAKPOINT ();
585
  }
586

587
588
589
590
591
592
593
594
595
  g_assert (last_btree_line != NULL);

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

      if (line == last_btree_line)
        break;
596

597
598
      line = gtk_text_line_next (line);
    }
599

600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
  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);
620

621
622
623
624
625
626
627
  layout->wrap_loop_count += 1;
}

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

629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
  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;
647

648
649
650
651
652
653
654
655
  if (layout->buffer == NULL)
    return;

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

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

656
657
static void
gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
658
                                  GtkTextLine   *line)
659
660
661
662
663
664
665
666
667
{
  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);
    }
}

668
669
670
671
672
673
674
static void
gtk_text_layout_real_invalidate (GtkTextLayout *layout,
                                 const GtkTextIter *start,
                                 const GtkTextIter *end)
{
  GtkTextLine *line;
  GtkTextLine *last_line;
675

676
677
678
679
680
681
682
  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
683

684
685
  last_line = gtk_text_iter_get_text_line (end);
  line = gtk_text_iter_get_text_line (start);
686
687
688
689
690
691

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

      if (line_data &&
692
693
694
695
696
697
          (line != last_line || !gtk_text_iter_starts_line (end)))
        {
          gtk_text_layout_invalidate_cache (layout, line);
          gtk_text_line_invalidate_wrap (line, line_data);
        }

698
699
      if (line == last_line)
        break;
700

701
702
      line = gtk_text_line_next (line);
    }
703

704
705
706
707
708
  gtk_text_layout_invalidated (layout);
}

static void
gtk_text_layout_real_free_line_data (GtkTextLayout     *layout,
709
710
                                     GtkTextLine       *line,
                                     GtkTextLineData   *line_data)
711
712
713
714
715
716
717
{
  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);
    }
718

719
720
721
722
723
724
725
726
  g_free (line_data);
}



/**
 * gtk_text_layout_is_valid:
 * @layout: a #GtkTextLayout
727
 *
728
 * Check if there are any invalid regions in a #GtkTextLayout's buffer
729
 *
730
731
732
733
734
735
736
 * 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);
737

738
739
  return gtk_text_btree_is_valid (_gtk_text_buffer_get_btree (layout->buffer),
                                  layout);
740
741
742
743
744
745
746
747
748
749
750
751
752
}

/**
 * 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.)
753
754
 *
 * Ensure that a region of a #GtkTextLayout is valid. The ::changed
755
756
757
758
 * signal will be emitted if any lines are validated.
 **/
void
gtk_text_layout_validate_yrange (GtkTextLayout *layout,
759
760
761
                                 GtkTextIter   *anchor,
                                 gint           y0,
                                 gint           y1)
762
763
764
765
766
767
{
  GtkTextLine *line;
  GtkTextLine *first_line = NULL;
  GtkTextLine *last_line = NULL;
  gint seen;
  gint delta_height = 0;
768
769
770
  gint first_line_y = 0;        /* Quiet GCC */
  gint last_line_y = 0;         /* Quiet GCC */

771
772
  g_return_if_fail (layout != NULL);
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
773

774
775
776
777
  if (y0 > 0)
    y0 = 0;
  if (y1 < 0)
    y1 = 0;
778
  
779
780
  /* Validate backwards from the anchor line to y0
   */
781
  line = gtk_text_iter_get_text_line (anchor);
782
783
784
785
786
  seen = 0;
  while (line && seen < -y0)
    {
      GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
      if (!line_data || !line_data->valid)
787
788
789
790
        {
          gint old_height = line_data ? line_data->height : 0;

          gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
791
                                        line, layout);
792
793
794
          line_data = gtk_text_line_get_data (line, layout);

          delta_height += line_data->height - old_height;
795
          
796
797
798
799
800
801
802
803
804
          first_line = line;
          first_line_y = -seen;
          if (!last_line)
            {
              last_line = line;
              last_line_y = -seen + line_data->height;
            }
        }

805
806
807
808
809
      seen += line_data->height;
      line = gtk_text_line_previous (line);
    }

  /* Validate forwards to y1 */
810
  line = gtk_text_iter_get_text_line (anchor);
811
812
813
814
815
  seen = 0;
  while (line && seen < y1)
    {
      GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
      if (!line_data || !line_data->valid)
816
817
818
819
        {
          gint old_height = line_data ? line_data->height : 0;

          gtk_text_btree_validate_line (_gtk_text_buffer_get_btree (layout->buffer),
820
                                        line, layout);
821
822
823
          line_data = gtk_text_line_get_data (line, layout);

          delta_height += line_data->height - old_height;
824
          
825
826
827
828
829
830
831
832
833
          if (!first_line)
            {
              first_line = line;
              first_line_y = seen;
            }
          last_line = line;
          last_line_y = seen + line_data->height;
        }

834
835
836
837
838
839
840
841
      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)
    {
842
843
      gint line_top =
        gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
844
845
                                      first_line, layout);

846
      gtk_text_layout_changed (layout,
847
848
849
                               line_top,
                               last_line_y - first_line_y - delta_height,
                               last_line_y - first_line_y);
850
851
852
853
854
855
856
857
    }
}

/**
 * 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)
858
 *
859
860
861
862
863
 * Validate regions of a #GtkTextLayout. The ::changed signal will
 * be emitted for each region validated.
 **/
void
gtk_text_layout_validate (GtkTextLayout *layout,
864
                          gint           max_pixels)
865
866
867
868
869
{
  gint y, old_height, new_height;

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

871
  while (max_pixels > 0 &&
872
         gtk_text_btree_validate (_gtk_text_buffer_get_btree (layout->buffer),
873
                                  layout,  max_pixels,
874
                                  &y, &old_height, &new_height))
875
876
877
878
879
880
881
882
    {
      max_pixels -= new_height;
      gtk_text_layout_changed (layout, y, old_height, new_height);
    }
}

static GtkTextLineData*
gtk_text_layout_real_wrap (GtkTextLayout   *layout,
883
884
885
                           GtkTextLine     *line,
                           /* may be NULL */
                           GtkTextLineData *line_data)
886
887
{
  GtkTextLineDisplay *display;
888

889
  g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
890

891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
  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;
}

/*
 * Layout utility functions
 */

/* If you get the style with get_style () you need to call
   release_style () to free it. */
912
static GtkTextAttributes*
913
get_style (GtkTextLayout *layout,
914
           const GtkTextIter *iter)
915
916
917
{
  GtkTextTag** tags;
  gint tag_count = 0;
918
  GtkTextAttributes *style;
919

920
921
922
923
924
925
  /* 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)
    {
926
      gtk_text_attributes_ref (layout->one_style_cache);
927
928
929
930
      return layout->one_style_cache;
    }

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

932
933
934
935
936
937
938
939
  /* 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 */
940
941
      gtk_text_attributes_ref (layout->default_style);
      gtk_text_attributes_ref (layout->default_style);
942
943
944
945
946
947
948
      layout->one_style_cache = layout->default_style;

      if (tags)
        g_free (tags);

      return layout->default_style;
    }
949

950
951
  /* Sort tags in ascending order of priority */
  gtk_text_tag_array_sort (tags, tag_count);
952

953
  style = gtk_text_attributes_new ();
954

955
  gtk_text_attributes_copy (layout->default_style,
956
                            style);
957

958
  gtk_text_attributes_fill_from_tags (style,
959
960
                                      tags,
                                      tag_count);
961
962
963
964
965
966
967

  g_free (tags);

  g_assert (style->refcount == 1);

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

971
972
973
974
975
976
  /* Returning yet another refcount */
  return style;
}

static void
release_style (GtkTextLayout *layout,
977
               GtkTextAttributes *style)
978
979
980
981
{
  g_return_if_fail (style != NULL);
  g_return_if_fail (style->refcount > 0);

982
  gtk_text_attributes_unref (style);
983
984
985
986
987
988
989
990
991
992
}

/*
 * Lines
 */

/* This function tries to optimize the case where a line
   is completely invisible */
static gboolean
totally_invisible_line (GtkTextLayout *layout,
993
994
                        GtkTextLine   *line,
                        GtkTextIter   *iter)
995
996
997
{
  GtkTextLineSegment *seg;
  int bytes = 0;
998

999
  /* If we have a cached style, then we know it does actually apply
1000
     and we can just see if it is invisible. */
1001
  if (layout->one_style_cache &&
1002
      !layout->one_style_cache->invisible)
1003
1004
1005
    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
1006
     we don't know the current invisible/noninvisible toggle state; this
1007
1008
1009
     function can use the whole btree to get it right. */
  else
    {
1010
1011
1012
      gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
                                       iter, line, 0);

1013
      if (!gtk_text_btree_char_is_invisible (iter))
1014
        return FALSE;
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
    }

  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
1028
1029
         thing is to only invisible really-invisible lines, rather
         than to invisible all really-invisible lines. */
1030

1031
1032
1033
      else if (seg->type == &gtk_text_toggle_on_type)
        {
          invalidate_cached_style (layout);
1034

1035
          /* Bail out if an elision-unsetting tag begins */
1036
1037
          if (seg->body.toggle.info->tag->invisible_set &&
              !seg->body.toggle.info->tag->values->invisible)
1038
1039
1040
1041
1042
            break;
        }
      else if (seg->type == &gtk_text_toggle_off_type)
        {
          invalidate_cached_style (layout);
1043

1044
          /* Bail out if an elision-setting tag ends */
1045
1046
          if (seg->body.toggle.info->tag->invisible_set &&
              seg->body.toggle.info->tag->values->invisible)
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
            break;
        }

      seg = seg->next;
    }

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

  return TRUE;
}

static void
set_para_values (GtkTextLayout      *layout,
1061
1062
1063
                 GtkTextAttributes *style,
                 GtkTextLineDisplay *display,
                 gdouble            *align)
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
{
  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)
    {
1096
1097
1098
1099
1100
1101
1102
1103
1104
    case PANGO_ALIGN_LEFT:
      *align = 0.0;
      break;
    case PANGO_ALIGN_RIGHT:
      *align = 1.0;
      break;
    case PANGO_ALIGN_CENTER:
      *align = 0.5;
      break;
1105
1106
1107
    }

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

1111
1112
  if (style->tabs)
    pango_layout_set_tabs (display->layout, style->tabs);
1113

1114
1115
1116
  display->top_margin = style->pixels_above_lines;
  display->height = style->pixels_above_lines + style->pixels_below_lines;
  display->bottom_margin = style->pixels_below_lines;
1117
  display->left_margin = style->left_margin;
1118
  display->right_margin = style->right_margin;
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  
  if (style->indent < 0)
    {
      /* This means the margins can be negative. FIXME
       * test that things work if they are.
       */
      
      if (pango_align == PANGO_ALIGN_LEFT)
        display->left_margin += style->indent;
      else if (pango_align == PANGO_ALIGN_RIGHT)
        display->right_margin += style->indent;
    }
  
  display->x_offset = display->left_margin;

1134

1135
1136
  pango_layout_set_indent (display->layout,
                           style->indent * PANGO_SCALE);
1137
1138
1139
1140
1141
1142
1143

  switch (style->wrap_mode)
    {
    case GTK_WRAPMODE_CHAR:
      /* FIXME: Handle this; for now, fall-through */
    case GTK_WRAPMODE_WORD:
      display->total_width = -1;
1144
      layout_width = layout->screen_width - display->left_margin - display->right_margin;
1145
1146
1147
      pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
      break;
    case GTK_WRAPMODE_NONE:
1148
      display->total_width = MAX (layout->screen_width, layout->width) - display->left_margin - display->right_margin;
1149
1150
1151
1152
1153
1154
1155
1156
      break;
    }
}

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

1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
  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);
1170

1171
1172
1173
1174
1175
  g_free (attr);
}

static gboolean
gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
1176
                                  const PangoAttribute *attr2)
1177
1178
1179
1180
1181
{
  const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
  const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;

  return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
1182
1183
1184
1185
1186
1187
1188
          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);

1189
1190
1191
1192
}

/**
 * gtk_text_attr_appearance_new:
1193
1194
 * @desc:
 *
1195
1196
1197
 * Create a new font description attribute. (This attribute
 * allows setting family, style, weight, variant, stretch,
 * and size simultaneously.)
1198
1199
 *
 * Return value:
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
 **/
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;
1221

1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
  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,
1232
                GtkTextAttributes  *style,
1233
1234
1235
1236
                gint                byte_count,
                PangoAttrList      *attrs,
                gint                start,
                gboolean            size_only)
1237
1238
1239
{
  PangoAttribute *attr;

1240
  attr = pango_attr_font_desc_new (&style->font);
1241
1242
1243
1244
1245
1246
1247
1248
  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);
1249

1250
1251
1252
      attr->start_index = start;
      attr->end_index = start + byte_count;

1253
      pango_attr_list_insert (attrs, attr);
1254
1255
1256
1257
    }
}

static void
Havoc Pennington's avatar
Havoc Pennington committed
1258
add_pixbuf_attrs (GtkTextLayout      *layout,
1259
                  GtkTextLineDisplay *display,
1260
                  GtkTextAttributes  *style,
1261
1262
1263
                  GtkTextLineSegment *seg,
                  PangoAttrList      *attrs,
                  gint                start)
1264
{
1265
1266
  PangoAttribute *attr;
  PangoRectangle logical_rect;
Havoc Pennington's avatar
Havoc Pennington committed
1267
  GtkTextPixbuf *pixbuf = &seg->body.pixbuf;
1268
1269
  gint width, height;

Havoc Pennington's avatar
Havoc Pennington committed
1270
1271
1272
  width = gdk_pixbuf_get_width (pixbuf->pixbuf);
  height = gdk_pixbuf_get_height (pixbuf->pixbuf);

1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
  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);

1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
  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;
    }
Owen Taylor's avatar
Owen Taylor committed
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348

  if (layout->preedit_string)
    {
      g_free (layout->preedit_string);
      layout->preedit_string = NULL;
    }

  if (layout->preedit_attrs)
    {
      pango_attr_list_unref (layout->preedit_attrs);
      layout->preedit_attrs = NULL;
    }
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
  
  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);
1359
1360
1361
1362
}

static void
add_cursor (GtkTextLayout      *layout,
1363
1364
1365
            GtkTextLineDisplay *display,
            GtkTextLineSegment *seg,
            gint                start)
1366
1367
1368
1369
{
  PangoRectangle strong_pos, weak_pos;
  GtkTextCursorDisplay *cursor;

1370
1371
  /* Hide insertion cursor when we have a selection or the layout
   * user has hidden the cursor.
1372
   */
1373
  if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
Havoc Pennington's avatar
Havoc Pennington committed
1374
                                     seg->body.mark.obj) &&
1375
1376
      (!layout->cursor_visible ||
       gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
1377
    return;
1378

1379
  pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
1380

1381
  cursor = g_new (GtkTextCursorDisplay, 1);
1382

1383
1384
1385
1386
1387
  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);
1388

1389
1390
1391
1392
1393
  if (weak_pos.x == strong_pos.x)
    cursor->is_weak = TRUE;
  else
    {
      cursor->is_weak = FALSE;
1394

1395
      cursor = g_new (GtkTextCursorDisplay, 1);
1396

1397
1398
1399
1400
1401
1402
1403
1404
1405
      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);
    }
}

1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
static void
allocate_child_widgets (GtkTextLayout      *layout,
                        GtkTextLineDisplay *display)
{
  
#if 0
  gtk_signal_emit (GTK_OBJECT (layout),
                   signals[ALLOCATE_CHILD],
                   child,
                   x, y);
#endif
}

Owen Taylor's avatar
Owen Taylor committed
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
static void
convert_color (GdkColor       *result,
	       PangoAttrColor *attr)
{
  result->red = attr->red;
  result->blue = attr->blue;
  result->green = attr->green;
}

/* This function is used to convert the preedit string attributes, which are
 * standard PangoAttributes, into the custom attributes used by the text
 * widget and insert them into a attr list with a given offset.
 */
static void
add_preedit_attrs (GtkTextLayout     *layout,
		   GtkTextAttributes *style,
		   PangoAttrList     *attrs,
		   gint               offset,
		   gboolean           size_only)
{
  PangoAttrIterator *iter = pango_attr_list_get_iterator (layout->preedit_attrs);

  do
    {
      GtkTextAppearance appearance = style->appearance;
      PangoFontDescription font_desc;
      PangoAttribute *insert_attr;
      GSList *extra_attrs = NULL;
      GSList *tmp_list;
      gint start, end;

      pango_attr_iterator_range (iter, &start, &end);

      if (end == G_MAXINT)
	end = layout->preedit_len;
      
1455
      pango_attr_iterator_get_font (iter, &style->font,
Owen Taylor's avatar
Owen Taylor committed
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
				    &font_desc, size_only ? NULL : &extra_attrs);
      
      tmp_list = extra_attrs;
      while (tmp_list)
	{
	  PangoAttribute *attr = tmp_list->data;
	  
	  switch (attr->klass->type)
	    {
	    case PANGO_ATTR_FOREGROUND