gtktextdisplay.c 28.5 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
368
            }
          else if (GTK_IS_WIDGET (shaped))
            {
              gint width, height;
              GdkRectangle draw_rect;
              GtkWidget *widget;

369
370
371
372
              /* FIXME this doesn't work at all, and remember to use
               * risen_y
               */
              
373
374
375
376
377
378
379
380
381
              widget = GTK_WIDGET (shaped);
              
              width = widget->allocation.width;
              height = widget->allocation.height;

              if (GTK_WIDGET_DRAWABLE (widget) &&
                  gtk_widget_intersect (widget,
                                        &render_state->clip_rect,
                                        &draw_rect))
Havoc Pennington's avatar
Havoc Pennington committed
382
                {
383
                  gtk_widget_draw (widget, &draw_rect);
Havoc Pennington's avatar
Havoc Pennington committed
384
                }
385

386
              shaped_width_pixels = width;
Havoc Pennington's avatar
Havoc Pennington committed
387
            }
388
389
          else
            g_assert_not_reached (); /* not a pixbuf or widget */
390
        }
391
392
393
394
395
396
397
398
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

      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;
437
438
439
    }
}

440
static void
441
render_para (GdkDrawable        *drawable,
442
443
             GtkTextRenderState *render_state,
             GtkTextLineDisplay *line_display,
444
             /* Top-left corner of paragraph including all margins */
445
446
447
448
             int                 x,
             int                 y,
             int                 selection_start_index,
             int                 selection_end_index)
449
{
450
  GSList *shaped_pointer = line_display->shaped_objects;
451
  PangoLayout *layout = line_display->layout;
452
  int byte_offset = 0;
453
454
455
456
  PangoLayoutIter *iter;
  PangoRectangle layout_logical;
  int screen_width;
  
457
  gboolean first = TRUE;
458

459
  iter = pango_layout_get_iter (layout);
460

461
  pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical);
462

463
464
465
466
467
468
469
  /* 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;
  if (screen_width < 0)
470
    {
471
472
473
474
475
476
477
      screen_width = pango_layout_get_width (layout);
      screen_width = PANGO_PIXELS (screen_width);
    }
  
  do
    {
      PangoLayoutLine *line = pango_layout_iter_get_line (iter);
478
      int selection_y, selection_height;
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
      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);
498
499

      if (first)
500
501
502
503
        {
          selection_y -= line_display->top_margin;
          selection_height += line_display->top_margin;
        }
504
505
      
      if (pango_layout_iter_at_last_line (iter))
506
        selection_height += line_display->bottom_margin;
507
      
508
      first = FALSE;
509

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

521
          render_layout_line (drawable, render_state, line, &shaped_pointer,
522
523
                              x + PANGO_PIXELS (line_rect.x),
                              y + PANGO_PIXELS (baseline),
524
525
                              TRUE);
        }
526
      else
527
        {
528
          GSList *shaped_pointer_tmp = shaped_pointer;
529

530
531
532
533
          render_layout_line (drawable, render_state,
                              line, &shaped_pointer,
                              x + PANGO_PIXELS (line_rect.x),
                              y + PANGO_PIXELS (baseline),
534
535
536
537
538
539
540
                              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,
541
542
                                                          selection_y,
                                                          selection_height,
543
544
545
546
547
548
549
550
                                                          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,
551
552
553
                                  x + PANGO_PIXELS (line_rect.x),
                                  selection_y,
                                  PANGO_PIXELS (line_rect.width),
554
555
                                  selection_height);

556
              render_layout_line (drawable, render_state, line, &shaped_pointer_tmp,
557
558
                                  x + PANGO_PIXELS (line_rect.x),
                                  y + PANGO_PIXELS (baseline),
559
560
561
562
563
564
565
566
                                  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 */
567
              if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
568
569
570
571
572
573
                  ((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,
574
575
576
                                      x + line_display->left_margin,
                                      selection_y,
                                      PANGO_PIXELS (line_rect.x) - line_display->left_margin,
577
578
579
                                      selection_height);
                }

580
581
              if (line_rect.x + line_rect.width <
                  (screen_width + line_display->left_margin) * PANGO_SCALE &&
582
583
584
                  ((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)))
                {
585
                  int nonlayout_width;
586

587
588
589
                  nonlayout_width =
                    line_display->left_margin + screen_width -
                    PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
590
591
592
593

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

      byte_offset += line->length;
    }
604
605
606
  while (pango_layout_iter_next_line (iter));

  pango_layout_iter_free (iter);
607
608
609
610
}

static GdkRegion *
get_selected_clip (GtkTextRenderState *render_state,
611
612
613
614
615
616
617
                   PangoLayout        *layout,
                   PangoLayoutLine    *line,
                   int                 x,
                   int                 y,
                   int                 height,
                   int                 start_index,
                   int                 end_index)
618
619
620
621
622
623
624
625
626
627
628
629
{
  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;

630
      rect.x = x + PANGO_PIXELS (ranges[2*i]);
631
      rect.y = y;
632
      rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
633
      rect.height = height;
634
      
635
636
637
638
639
640
641
642
643
644
645
646
647
      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,
648
                     GtkTextAppearance **appearance)
649
650
{
  GSList *tmp_list = item->extra_attrs;
651

652
  *appearance = NULL;
653

654
655
656
657
658
  while (tmp_list)
    {
      PangoAttribute *attr = tmp_list->data;

      if (attr->klass->type == gtk_text_attr_appearance_type)
659
660
661
        {
          *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
        }
662
663

      tmp_list = tmp_list->next;
664
665
666
667
668
    }
}

void
gtk_text_layout_draw (GtkTextLayout *layout,
669
670
671
672
673
674
675
676
677
678
679
680
                      GtkWidget *widget,
                      GdkDrawable *drawable,
                      /* Location of the layout
                         in buffer coordinates */
                      gint x_offset,
                      gint y_offset,
                      /* Region of the layout to
                         render */
                      gint x,
                      gint y,
                      gint width,
                      gint height)
681
682
683
684
685
686
687
{
  GdkRectangle clip;
  gint current_y;
  GSList *cursor_list;
  GtkTextRenderState *render_state;
  GtkTextIter selection_start, selection_end;
  gboolean have_selection = FALSE;
688
689
690
  GSList *line_list;
  GSList *tmp_list;
  
691
692
693
694
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
  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);
720

721
  if (gtk_text_buffer_get_selection_bounds (layout->buffer,
722
723
                                            &selection_start,
                                            &selection_end))
724
    have_selection = TRUE;
725

726
727
728
729
730
731
732
733
  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;
734

735
      line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
736
      
737
      if (have_selection)
738
739
740
741
742
        {
          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),
743
                                           &line_start,
744
745
                                           line, 0);
          gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
746
                                           &line_end,
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
                                           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;
            }
        }

764
      render_para (drawable, render_state, line_display,
765
                   - x_offset,
766
                   current_y,
767
768
                   selection_start_index, selection_end_index);

769
770
771
772
773
774

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

      cursor_list = line_display->cursors;
      while (cursor_list)
775
776
777
778
779
780
781
782
783
784
785
786
787
        {
          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,
                         line_display->x_offset + cursor->x,
                         current_y + line_display->top_margin + cursor->y,
                         line_display->x_offset + cursor->x,
788
                         current_y + line_display->top_margin + cursor->y + cursor->height - 1);
789
790
791

          cursor_list = cursor_list->next;
        }
792
793
794

      current_y += line_display->height;
      gtk_text_layout_free_line_display (layout, line_display);
795

796
797
798
799
800
801
802
803
      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);
}