gtktextdisplay.c 28.8 KB
Newer Older
1
2
3
4
5
6
7
/* gtktextdisplay.c - display layed-out text
 *
 * 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
 *
8
9
 * This file can be used under your choice of two licenses, the LGPL
 * and the original Tk license.
10
 *
11
 * LGPL:
12
 *
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 * 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:
28
29
30
31
32
 *
 * 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.
33
 *
34
35
36
37
38
39
40
41
42
 * 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.
43
 *
44
45
46
47
48
 * 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.
49
 *
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 * 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.
68
69
70
71
72
73
74
 *
 */
/*
 * 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/.
75
76
77
78
 */

#include "gtktextdisplay.h"
#include "gtktextiterprivate.h"
Tor Lillqvist's avatar
Tor Lillqvist committed
79
80

#include <pango/pango.h>
81
82
83
84
85
86

typedef struct _GtkTextRenderState GtkTextRenderState;

struct _GtkTextRenderState
{
  GtkWidget *widget;
87

88
  GtkTextAppearance *last_appearance;
89
  GtkTextAppearance *last_bg_appearance;
90
91
92
93
94
95
  GdkGC *fg_gc;
  GdkGC *bg_gc;
  GdkRectangle clip_rect;
};

static void       get_item_properties (PangoItem           *item,
96
                                       GtkTextAppearance  **appearance);
97
static GdkRegion *get_selected_clip   (GtkTextRenderState  *render_state,
98
99
100
101
102
103
104
                                       PangoLayout         *layout,
                                       PangoLayoutLine     *line,
                                       int                  x,
                                       int                  y,
                                       int                  height,
                                       int                  start_index,
                                       int                  end_index);
105
106
107

static GtkTextRenderState *
gtk_text_render_state_new (GtkWidget    *widget,
108
109
                           GdkDrawable  *drawable,
                           GdkRectangle *clip_rect)
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
{
  GtkTextRenderState *state = g_new0 (GtkTextRenderState, 1);

  state->widget = widget;
  state->fg_gc = gdk_gc_new (drawable);
  state->bg_gc = gdk_gc_new (drawable);
  state->clip_rect = *clip_rect;

  return state;
}

static void
gtk_text_render_state_destroy (GtkTextRenderState *state)
{
  gdk_gc_unref (state->fg_gc);
  gdk_gc_unref (state->bg_gc);
126

127
128
129
130
131
  g_free (state);
}

static void
gtk_text_render_state_set_color (GtkTextRenderState *state,
132
133
                                 GdkGC              *gc,
                                 GdkColor           *color)
134
135
136
137
138
139
140
{
  gdk_colormap_alloc_color (gtk_widget_get_colormap (state->widget), color, FALSE, TRUE);
  gdk_gc_set_foreground (gc, color);
}

static void
gtk_text_render_state_update (GtkTextRenderState *state,
141
                              GtkTextAppearance  *new_appearance)
142
143
144
145
{
  if (!state->last_appearance ||
      !gdk_color_equal (&new_appearance->fg_color, &state->last_appearance->fg_color))
    gtk_text_render_state_set_color (state, state->fg_gc, &new_appearance->fg_color);
146

147
148
149
150
  if (!state->last_appearance ||
      new_appearance->fg_stipple != state->last_appearance->fg_stipple)
    {
      if (new_appearance->fg_stipple)
151
152
153
154
        {
          gdk_gc_set_fill (state->fg_gc, GDK_STIPPLED);
          gdk_gc_set_stipple (state->fg_gc, new_appearance->fg_stipple);
        }
155
      else
156
157
158
        {
          gdk_gc_set_fill (state->fg_gc, GDK_SOLID);
        }
159
    }
160

161
162
  if (new_appearance->draw_bg)
    {
163
164
      if (!state->last_bg_appearance ||
          !gdk_color_equal (&new_appearance->bg_color, &state->last_bg_appearance->bg_color))
165
166
        gtk_text_render_state_set_color (state, state->bg_gc, &new_appearance->bg_color);

167
168
      if (!state->last_bg_appearance ||
          new_appearance->bg_stipple != state->last_bg_appearance->bg_stipple)
169
170
171
172
173
174
175
176
177
178
179
        {
          if (new_appearance->bg_stipple)
            {
              gdk_gc_set_fill (state->bg_gc, GDK_STIPPLED);
              gdk_gc_set_stipple (state->bg_gc, new_appearance->bg_stipple);
            }
          else
            {
              gdk_gc_set_fill (state->bg_gc, GDK_SOLID);
            }
        }
180
181

      state->last_bg_appearance = new_appearance;
182
183
184
185
186
    }

  state->last_appearance = new_appearance;
}

187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
static void
get_shape_extents (PangoLayoutRun *run,
                   PangoRectangle *ink_rect,
                   PangoRectangle *logical_rect)
{
  GSList *tmp_list = run->item->extra_attrs;
    
  while (tmp_list)
    {
      PangoAttribute *attr = tmp_list->data;

      if (attr->klass->type == PANGO_ATTR_SHAPE)
	{          
	  if (logical_rect)
	    *logical_rect = ((PangoAttrShape *)attr)->logical_rect;

	  if (ink_rect)
	    *ink_rect = ((PangoAttrShape *)attr)->ink_rect;

          return;
        }

      tmp_list = tmp_list->next;
    }

  g_assert_not_reached ();
}

215
static void
216
render_layout_line (GdkDrawable        *drawable,
217
218
                    GtkTextRenderState *render_state,
                    PangoLayoutLine    *line,
219
                    GSList            **shaped_pointer,
220
221
222
                    int                 x,
                    int                 y,
                    gboolean            selected)
223
224
225
226
227
228
{
  GSList *tmp_list = line->runs;
  PangoRectangle overall_rect;
  PangoRectangle logical_rect;
  PangoRectangle ink_rect;
  gint x_off = 0;
229
230
  GdkGC *fg_gc;
  
231
232
233
234
235
236
  pango_layout_line_get_extents (line, NULL, &overall_rect);

  while (tmp_list)
    {
      PangoLayoutRun *run = tmp_list->data;
      GtkTextAppearance *appearance;
237
238
239
240
      gint risen_y;
      gint shaped_width_pixels = 0;
      gboolean need_ink = FALSE;
      
241
242
243
244
      tmp_list = tmp_list->next;

      get_item_properties (run->item, &appearance);

245
246
247
248
249
      g_assert (appearance != NULL);
      
      risen_y = y - PANGO_PIXELS (appearance->rise);
      
      if (selected)
250
        {
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
          fg_gc = render_state->widget->style->fg_gc[GTK_STATE_SELECTED];
        }
      else
        {
          gtk_text_render_state_update (render_state, appearance);
          
          fg_gc = render_state->fg_gc;
        }
      
      if (appearance->underline != PANGO_UNDERLINE_NONE ||
          appearance->strikethrough)
        need_ink = TRUE;
      
      if (appearance->is_text)
        {
          if (need_ink)
267
            pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
268
                                        &ink_rect, &logical_rect);
269
270
          else
            pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
271
                                        NULL, &logical_rect);
272
        }
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
      else
        {
          if (need_ink)
            get_shape_extents (run, &ink_rect, &logical_rect);
          else
            get_shape_extents (run, NULL, &logical_rect);
        }
      
      if (appearance->draw_bg && !selected)
        gdk_draw_rectangle (drawable, render_state->bg_gc, TRUE,
                            x + PANGO_PIXELS (x_off) + PANGO_PIXELS (logical_rect.x),
                            risen_y + PANGO_PIXELS (logical_rect.y),
                            PANGO_PIXELS (logical_rect.width),
                            PANGO_PIXELS (logical_rect.height));

      if (appearance->is_text)
        gdk_draw_glyphs (drawable, fg_gc,
                         run->item->analysis.font,
                         x + PANGO_PIXELS (x_off),
                         risen_y, run->glyphs);
      else
294
        {
295
          GObject *shaped = (*shaped_pointer)->data;
296

297
298
299
          *shaped_pointer = (*shaped_pointer)->next;
          
          if (GDK_IS_PIXBUF (shaped))
Havoc Pennington's avatar
Havoc Pennington committed
300
            {
301
302
303
304
305
306
307
308
309
310
              gint width, height;
              GdkRectangle pixbuf_rect, draw_rect;
              GdkPixbuf *pixbuf;

              pixbuf = GDK_PIXBUF (shaped);
              
              width = gdk_pixbuf_get_width (pixbuf);
              height = gdk_pixbuf_get_height (pixbuf);

              pixbuf_rect.x = x + x_off / PANGO_SCALE;
311
              pixbuf_rect.y = risen_y - height;
312
313
314
315
316
              pixbuf_rect.width = width;
              pixbuf_rect.height = height;

              if (gdk_rectangle_intersect (&pixbuf_rect, &render_state->clip_rect,
                                           &draw_rect))
Havoc Pennington's avatar
Havoc Pennington committed
317
                {
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
                  GdkBitmap *mask = NULL;
              
                  if (gdk_pixbuf_get_has_alpha (pixbuf))
                    {
                      mask = gdk_pixmap_new (drawable,
                                             gdk_pixbuf_get_width (pixbuf),
                                             gdk_pixbuf_get_height (pixbuf),
                                             1);

                      gdk_pixbuf_render_threshold_alpha (pixbuf, mask,
                                                         0, 0, 0, 0,
                                                         gdk_pixbuf_get_width (pixbuf),
                                                         gdk_pixbuf_get_height (pixbuf),
                                                         128);

                    }

                  if (mask)
                    {
                      gdk_gc_set_clip_mask (render_state->fg_gc, mask);
                      gdk_gc_set_clip_origin (render_state->fg_gc,
                                              pixbuf_rect.x, pixbuf_rect.y);
                    }

                  gdk_pixbuf_render_to_drawable (pixbuf,
                                                 drawable,
                                                 render_state->fg_gc,
                                                 draw_rect.x - pixbuf_rect.x,
                                                 draw_rect.y - pixbuf_rect.y,
                                                 draw_rect.x, draw_rect.y,
                                                 draw_rect.width,
                                                 draw_rect.height,
                                                 GDK_RGB_DITHER_NORMAL,
                                                 0, 0);

                  if (mask)
                    {
                      gdk_gc_set_clip_rectangle (render_state->fg_gc,
                                                 &render_state->clip_rect);
                      g_object_unref (G_OBJECT (mask));
                    }
Havoc Pennington's avatar
Havoc Pennington committed
359
360
                }

361
              shaped_width_pixels = width;
362
363
364
365
366
367
            }
          else if (GTK_IS_WIDGET (shaped))
            {
              gint width, height;
              GdkRectangle draw_rect;
              GtkWidget *widget;
368
              
369
370
371
372
373
              widget = GTK_WIDGET (shaped);
              
              width = widget->allocation.width;
              height = widget->allocation.height;

Tor Lillqvist's avatar
Tor Lillqvist committed
374
375
376
377
378
              g_print ("widget allocation at %d,%d %d x %d\n",
		       widget->allocation.x,
		       widget->allocation.y,
		       widget->allocation.width,
		       widget->allocation.height);
379
              
380
              if (GTK_WIDGET_DRAWABLE (widget) &&
381
382
383
384
                  gdk_rectangle_intersect (&widget->allocation,
                                           &render_state->clip_rect,
                                           &draw_rect))

Havoc Pennington's avatar
Havoc Pennington committed
385
                {
Tor Lillqvist's avatar
Tor Lillqvist committed
386
387
388
389
390
                  g_print ("drawing widget area %d,%d %d x %d\n",
			   draw_rect.x,
			   draw_rect.y,
			   draw_rect.width,
			   draw_rect.height);
391

392
                  gtk_widget_draw (widget, &draw_rect);
Havoc Pennington's avatar
Havoc Pennington committed
393
                }
394

395
              shaped_width_pixels = width;
Havoc Pennington's avatar
Havoc Pennington committed
396
            }
397
398
          else
            g_assert_not_reached (); /* not a pixbuf or widget */
399
        }
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
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

      switch (appearance->underline)
        {
        case PANGO_UNDERLINE_NONE:
          break;
        case PANGO_UNDERLINE_DOUBLE:
          g_assert (need_ink);
          gdk_draw_line (drawable, fg_gc,
                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
                         risen_y + 3,
                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
                         risen_y + 3);
          /* Fall through */
        case PANGO_UNDERLINE_SINGLE:
          g_assert (need_ink);
          gdk_draw_line (drawable, fg_gc,
                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
                         risen_y + 1,
                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
                         risen_y + 1);
          break;
        case PANGO_UNDERLINE_LOW:
          g_assert (need_ink);
          gdk_draw_line (drawable, fg_gc,
                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
                         risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1,
                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
                         risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1);
          break;
        }

      if (appearance->strikethrough)
        {          
          gint strikethrough_y = risen_y + (0.3 * logical_rect.y) / PANGO_SCALE;

          g_assert (need_ink);
          
          gdk_draw_line (drawable, fg_gc,
                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1, strikethrough_y,
                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, strikethrough_y);
        }

      if (appearance->is_text)
        x_off += logical_rect.width;
      else
        x_off += shaped_width_pixels * PANGO_SCALE;
446
447
448
    }
}

449
static void
450
render_para (GdkDrawable        *drawable,
451
452
             GtkTextRenderState *render_state,
             GtkTextLineDisplay *line_display,
453
             /* Top-left corner of paragraph including all margins */
454
455
456
457
             int                 x,
             int                 y,
             int                 selection_start_index,
             int                 selection_end_index)
458
{
459
  GSList *shaped_pointer = line_display->shaped_objects;
460
  PangoLayout *layout = line_display->layout;
461
  int byte_offset = 0;
462
463
464
465
  PangoLayoutIter *iter;
  PangoRectangle layout_logical;
  int screen_width;
  
466
  gboolean first = TRUE;
467

468
  iter = pango_layout_get_iter (layout);
469

470
  pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical);
471

472
473
474
475
476
477
478
479
480
481
  /* Adjust for margins */
  
  layout_logical.x += line_display->x_offset * PANGO_SCALE;
  layout_logical.y += line_display->top_margin * PANGO_SCALE;

  screen_width = line_display->total_width;
  
  do
    {
      PangoLayoutLine *line = pango_layout_iter_get_line (iter);
482
      int selection_y, selection_height;
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
      int first_y, last_y;
      PangoRectangle line_rect;
      int baseline;
      
      pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
      baseline = pango_layout_iter_get_baseline (iter);
      pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
      
      /* Adjust for margins */

      line_rect.x += line_display->x_offset * PANGO_SCALE;
      line_rect.y += line_display->top_margin * PANGO_SCALE;
      baseline += line_display->top_margin * PANGO_SCALE;

      /* Selection is the height of the line, plus top/bottom
       * margin if we're the first/last line
       */
      selection_y = y + PANGO_PIXELS (first_y) + line_display->top_margin;
      selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
502
503

      if (first)
504
505
506
507
        {
          selection_y -= line_display->top_margin;
          selection_height += line_display->top_margin;
        }
508
509
      
      if (pango_layout_iter_at_last_line (iter))
510
        selection_height += line_display->bottom_margin;
511
      
512
      first = FALSE;
513

514
      if (selection_start_index < byte_offset &&
515
516
517
518
519
          selection_end_index > line->length + byte_offset) /* All selected */
        {
          gdk_draw_rectangle (drawable,
                              render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
                              TRUE,
520
521
522
523
524
                              x + line_display->left_margin,
                              selection_y,
                              screen_width,
                              selection_height);

525
          render_layout_line (drawable, render_state, line, &shaped_pointer,
526
527
                              x + PANGO_PIXELS (line_rect.x),
                              y + PANGO_PIXELS (baseline),
528
529
                              TRUE);
        }
530
      else
531
        {
532
          GSList *shaped_pointer_tmp = shaped_pointer;
533

534
535
536
537
          render_layout_line (drawable, render_state,
                              line, &shaped_pointer,
                              x + PANGO_PIXELS (line_rect.x),
                              y + PANGO_PIXELS (baseline),
538
539
540
541
542
543
544
                              FALSE);

          if (selection_start_index < byte_offset + line->length &&
              selection_end_index > byte_offset) /* Some selected */
            {
              GdkRegion *clip_region = get_selected_clip (render_state, layout, line,
                                                          x + line_display->x_offset,
545
546
                                                          selection_y,
                                                          selection_height,
547
548
549
550
551
552
553
554
                                                          selection_start_index, selection_end_index);

              gdk_gc_set_clip_region (render_state->widget->style->fg_gc [GTK_STATE_SELECTED], clip_region);
              gdk_gc_set_clip_region (render_state->widget->style->bg_gc [GTK_STATE_SELECTED], clip_region);

              gdk_draw_rectangle (drawable,
                                  render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
                                  TRUE,
555
556
557
                                  x + PANGO_PIXELS (line_rect.x),
                                  selection_y,
                                  PANGO_PIXELS (line_rect.width),
558
559
                                  selection_height);

560
              render_layout_line (drawable, render_state, line, &shaped_pointer_tmp,
561
562
                                  x + PANGO_PIXELS (line_rect.x),
                                  y + PANGO_PIXELS (baseline),
563
564
565
566
567
568
569
570
                                  TRUE);

              gdk_gc_set_clip_region (render_state->widget->style->fg_gc [GTK_STATE_SELECTED], NULL);
              gdk_gc_set_clip_region (render_state->widget->style->bg_gc [GTK_STATE_SELECTED], NULL);

              gdk_region_destroy (clip_region);

              /* Paint in the ends of the line */
571
              if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
572
573
574
575
576
577
                  ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
                   (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
                {
                  gdk_draw_rectangle (drawable,
                                      render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
                                      TRUE,
578
579
580
                                      x + line_display->left_margin,
                                      selection_y,
                                      PANGO_PIXELS (line_rect.x) - line_display->left_margin,
581
582
583
                                      selection_height);
                }

584
585
              if (line_rect.x + line_rect.width <
                  (screen_width + line_display->left_margin) * PANGO_SCALE &&
586
587
588
                  ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) ||
                   (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
                {
589
                  int nonlayout_width;
590

591
592
593
                  nonlayout_width =
                    line_display->left_margin + screen_width -
                    PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
594
595
596
597

                  gdk_draw_rectangle (drawable,
                                      render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
                                      TRUE,
598
                                      x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
599
                                      selection_y,
600
                                      nonlayout_width,
601
602
603
604
                                      selection_height);
                }
            }
        }
605
606
607

      byte_offset += line->length;
    }
608
609
610
  while (pango_layout_iter_next_line (iter));

  pango_layout_iter_free (iter);
611
612
613
614
}

static GdkRegion *
get_selected_clip (GtkTextRenderState *render_state,
615
616
617
618
619
620
621
                   PangoLayout        *layout,
                   PangoLayoutLine    *line,
                   int                 x,
                   int                 y,
                   int                 height,
                   int                 start_index,
                   int                 end_index)
622
623
624
625
626
627
628
629
630
631
632
633
{
  gint *ranges;
  gint n_ranges, i;
  GdkRegion *clip_region = gdk_region_new ();
  GdkRegion *tmp_region;

  pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);

  for (i=0; i < n_ranges; i++)
    {
      GdkRectangle rect;

634
      rect.x = x + PANGO_PIXELS (ranges[2*i]);
635
      rect.y = y;
636
      rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
637
      rect.height = height;
638
      
639
640
641
642
643
644
645
646
647
648
649
650
651
      gdk_region_union_with_rect (clip_region, &rect);
    }

  tmp_region = gdk_region_rectangle (&render_state->clip_rect);
  gdk_region_intersect (clip_region, tmp_region);
  gdk_region_destroy (tmp_region);

  g_free (ranges);
  return clip_region;
}

static void
get_item_properties (PangoItem          *item,
652
                     GtkTextAppearance **appearance)
653
654
{
  GSList *tmp_list = item->extra_attrs;
655

656
  *appearance = NULL;
657

658
659
660
661
662
  while (tmp_list)
    {
      PangoAttribute *attr = tmp_list->data;

      if (attr->klass->type == gtk_text_attr_appearance_type)
663
664
665
        {
          *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
        }
666
667

      tmp_list = tmp_list->next;
668
669
670
671
672
    }
}

void
gtk_text_layout_draw (GtkTextLayout *layout,
673
674
                      GtkWidget *widget,
                      GdkDrawable *drawable,
675
676
                      /* Location of the drawable
                         in layout coordinates */
677
678
679
680
681
682
683
684
                      gint x_offset,
                      gint y_offset,
                      /* Region of the layout to
                         render */
                      gint x,
                      gint y,
                      gint width,
                      gint height)
685
686
687
688
689
690
691
{
  GdkRectangle clip;
  gint current_y;
  GSList *cursor_list;
  GtkTextRenderState *render_state;
  GtkTextIter selection_start, selection_end;
  gboolean have_selection = FALSE;
692
693
694
  GSList *line_list;
  GSList *tmp_list;
  
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (layout->default_style != NULL);
  g_return_if_fail (layout->buffer != NULL);
  g_return_if_fail (drawable != NULL);
  g_return_if_fail (x_offset >= 0);
  g_return_if_fail (y_offset >= 0);
  g_return_if_fail (width >= 0);
  g_return_if_fail (height >= 0);

  if (width == 0 || height == 0)
    return;

  line_list =  gtk_text_layout_get_lines (layout, y + y_offset, y + y_offset + height, &current_y);
  current_y -= y_offset;

  if (line_list == NULL)
    return; /* nothing on the screen */

  clip.x = x;
  clip.y = y;
  clip.width = width;
  clip.height = height;

  render_state = gtk_text_render_state_new (widget, drawable, &clip);

  gdk_gc_set_clip_rectangle (render_state->fg_gc, &clip);
  gdk_gc_set_clip_rectangle (render_state->bg_gc, &clip);

  gtk_text_layout_wrap_loop_start (layout);
724

725
  if (gtk_text_buffer_get_selection_bounds (layout->buffer,
726
727
                                            &selection_start,
                                            &selection_end))
728
    have_selection = TRUE;
729

730
731
732
733
734
735
736
737
  tmp_list = line_list;
  while (tmp_list != NULL)
    {
      GtkTextLineDisplay *line_display;
      gint selection_start_index = -1;
      gint selection_end_index = -1;

      GtkTextLine *line = tmp_list->data;
738

739
      line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
740
      
741
      if (have_selection)
742
743
744
745
746
        {
          GtkTextIter line_start, line_end;
          gint byte_count = gtk_text_line_byte_count (line);

          gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
747
                                           &line_start,
748
749
                                           line, 0);
          gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
750
                                           &line_end,
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
                                           line, byte_count - 1);

          if (gtk_text_iter_compare (&selection_start, &line_end) < 0 &&
              gtk_text_iter_compare (&selection_end, &line_start) > 0)
            {
              if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
                selection_start_index = gtk_text_iter_get_line_index (&selection_start);
              else
                selection_start_index = -1;

              if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
                selection_end_index = gtk_text_iter_get_line_index (&selection_end);
              else
                selection_end_index = byte_count;
            }
        }

768
      render_para (drawable, render_state, line_display,
769
                   - x_offset,
770
                   current_y,
771
772
                   selection_start_index, selection_end_index);

773
774
775
776
777
778

      /* We paint the cursors last, because they overlap another chunk
         and need to appear on top. */

      cursor_list = line_display->cursors;
      while (cursor_list)
779
780
781
782
783
784
785
786
787
788
        {
          GtkTextCursorDisplay *cursor = cursor_list->data;
          GdkGC *gc;

          if (cursor->is_strong)
            gc = widget->style->bg_gc[GTK_STATE_SELECTED];
          else
            gc = widget->style->fg_gc[GTK_STATE_NORMAL];

          gdk_draw_line (drawable, gc,
789
                         line_display->x_offset + cursor->x - x_offset,
790
                         current_y + line_display->top_margin + cursor->y,
791
                         line_display->x_offset + cursor->x - x_offset,
792
                         current_y + line_display->top_margin + cursor->y + cursor->height - 1);
793
794
795

          cursor_list = cursor_list->next;
        }
796
797
798

      current_y += line_display->height;
      gtk_text_layout_free_line_display (layout, line_display);
799
800
801
      render_state->last_appearance = NULL;
      render_state->last_bg_appearance = NULL;
      
802
803
804
805
806
807
808
809
      tmp_list = g_slist_next (tmp_list);
    }

  gtk_text_layout_wrap_loop_end (layout);
  gtk_text_render_state_destroy (render_state);

  g_slist_free (line_list);
}