]> Pileus Git - ~andy/gtk/blob - gtk/gtktextdisplay.c
47be831749f7ec075e66cdc9f5dc1d57f2cbca2f
[~andy/gtk] / gtk / gtktextdisplay.c
1 /* gtktextdisplay.c - display layed-out text
2  *
3  * Copyright (c) 1992-1994 The Regents of the University of California.
4  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
5  * Copyright (c) 2000 Red Hat, Inc.
6  * Tk->Gtk port by Havoc Pennington
7  *
8  * This file can be used under your choice of two licenses, the LGPL
9  * and the original Tk license.
10  *
11  * LGPL:
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free
25  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  *
27  * Original Tk license:
28  *
29  * This software is copyrighted by the Regents of the University of
30  * California, Sun Microsystems, Inc., and other parties.  The
31  * following terms apply to all files associated with the software
32  * unless explicitly disclaimed in individual files.
33  *
34  * The authors hereby grant permission to use, copy, modify,
35  * distribute, and license this software and its documentation for any
36  * purpose, provided that existing copyright notices are retained in
37  * all copies and that this notice is included verbatim in any
38  * distributions. No written agreement, license, or royalty fee is
39  * required for any of the authorized uses.  Modifications to this
40  * software may be copyrighted by their authors and need not follow
41  * the licensing terms described here, provided that the new terms are
42  * clearly indicated on the first page of each file where they apply.
43  *
44  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
45  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
46  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
47  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
48  * OF THE POSSIBILITY OF SUCH DAMAGE.
49  *
50  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
51  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
53  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
54  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
55  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
56  *
57  * GOVERNMENT USE: If you are acquiring this software on behalf of the
58  * U.S. government, the Government shall have only "Restricted Rights"
59  * in the software and related documentation as defined in the Federal
60  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
61  * are acquiring the software on behalf of the Department of Defense,
62  * the software shall be classified as "Commercial Computer Software"
63  * and the Government shall have only "Restricted Rights" as defined
64  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
65  * foregoing, the authors grant the U.S. Government and others acting
66  * in its behalf permission to use and distribute the software in
67  * accordance with the terms specified in this license.
68  *
69  */
70 /*
71  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
72  * file for a list of people on the GTK+ Team.  See the ChangeLog
73  * files for a list of changes.  These files are distributed with
74  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
75  */
76
77 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
78 #include <config.h>
79 #include "gtktextdisplay.h"
80 #include "gtkalias.h"
81 /* DO NOT go putting private headers in here. This file should only
82  * use the semi-public headers, as with gtktextview.c.
83  */
84
85 #define GTK_TYPE_TEXT_RENDERER            (_gtk_text_renderer_get_type())
86 #define GTK_TEXT_RENDERER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_TEXT_RENDERER, GtkTextRenderer))
87 #define GTK_IS_TEXT_RENDERER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_TEXT_RENDERER))
88 #define GTK_TEXT_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
89 #define GTK_IS_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_RENDERER))
90 #define GTK_TEXT_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
91
92 typedef struct _GtkTextRenderer      GtkTextRenderer;
93 typedef struct _GtkTextRendererClass GtkTextRendererClass;
94
95 struct _GtkTextRenderer
96 {
97   GdkPangoRenderer parent_instance;
98
99   GdkScreen *screen;
100
101   GtkWidget *widget;
102   GdkDrawable *drawable;
103   GdkRectangle clip_rect;
104   
105   GdkColor *error_color;        /* Error underline color for this widget */
106   GList *widgets;               /* widgets encountered when drawing */
107   
108   gboolean selected;
109 };
110
111 struct _GtkTextRendererClass
112 {
113   GdkPangoRendererClass parent_class;
114 };
115
116 G_DEFINE_TYPE (GtkTextRenderer, _gtk_text_renderer, GDK_TYPE_PANGO_RENDERER)
117
118 static GdkColor *
119 text_renderer_get_error_color (GtkTextRenderer *text_renderer)
120 {
121   static const GdkColor red = { 0, 0xffff, 0, 0 };
122
123   if (!text_renderer->error_color)
124     gtk_widget_style_get (text_renderer->widget,
125                           "error-underline_color", &text_renderer->error_color,
126                           NULL);
127   
128   if (!text_renderer->error_color)
129     text_renderer->error_color = gdk_color_copy (&red);
130
131   return text_renderer->error_color;
132 }
133
134 static void
135 text_renderer_set_gdk_color (GtkTextRenderer *text_renderer,
136                              PangoRenderPart  part,
137                              GdkColor        *gdk_color)
138 {
139   PangoRenderer *renderer = PANGO_RENDERER (text_renderer);
140
141   if (gdk_color)
142     {
143       PangoColor color;
144
145       color.red = gdk_color->red;
146       color.green = gdk_color->green;
147       color.blue = gdk_color->blue;
148       
149       pango_renderer_set_color (renderer, part, &color);
150     }
151   else
152     pango_renderer_set_color (renderer, part, NULL);
153            
154 }
155
156 static GtkTextAppearance *
157 get_item_appearance (PangoItem *item)
158 {
159   GSList *tmp_list = item->analysis.extra_attrs;
160
161   while (tmp_list)
162     {
163       PangoAttribute *attr = tmp_list->data;
164
165       if (attr->klass->type == gtk_text_attr_appearance_type)
166         return &((GtkTextAttrAppearance *)attr)->appearance;
167
168       tmp_list = tmp_list->next;
169     }
170
171   return NULL;
172 }
173
174 static void
175 gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
176                                PangoLayoutRun *run)
177 {
178   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
179   GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
180   GdkColor *bg_color, *fg_color, *underline_color;
181   GdkPixmap *fg_stipple, *bg_stipple;
182   GtkTextAppearance *appearance;
183
184   PANGO_RENDERER_CLASS (_gtk_text_renderer_parent_class)->prepare_run (renderer, run);
185
186   appearance = get_item_appearance (run->item);
187   g_assert (appearance != NULL);
188   
189   if (appearance->draw_bg && !text_renderer->selected)
190     bg_color = &appearance->bg_color;
191   else
192     bg_color = NULL;
193   
194   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
195
196   if (text_renderer->selected)
197     {
198       if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
199         fg_color = &text_renderer->widget->style->text[GTK_STATE_SELECTED];
200       else
201         fg_color = &text_renderer->widget->style->text[GTK_STATE_ACTIVE];
202     }
203   else
204     fg_color = &appearance->fg_color;
205
206   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_FOREGROUND, fg_color);
207   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_color);
208
209   if (appearance->underline == PANGO_UNDERLINE_ERROR)
210     underline_color = text_renderer_get_error_color (text_renderer);
211   else
212     underline_color = fg_color;
213
214   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_UNDERLINE, underline_color);
215
216   fg_stipple = appearance->fg_stipple;
217   if (fg_stipple && text_renderer->screen != gdk_drawable_get_screen (fg_stipple))
218     {
219       g_warning ("gtk_text_renderer_prepare_run:\n"
220                  "The foreground stipple bitmap has been created on the wrong screen.\n"
221                  "Ignoring the stipple bitmap information.");
222       fg_stipple = NULL;
223     }
224       
225   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_FOREGROUND, fg_stipple);
226   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_stipple);
227   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_UNDERLINE, fg_stipple);
228
229   bg_stipple = appearance->draw_bg ? appearance->bg_stipple : NULL;
230   
231   if (bg_stipple && text_renderer->screen != gdk_drawable_get_screen (bg_stipple))
232     {
233       g_warning ("gtk_text_renderer_prepare_run:\n"
234                  "The background stipple bitmap has been created on the wrong screen.\n"
235                  "Ignoring the stipple bitmap information.");
236       bg_stipple = NULL;
237     }
238   
239   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_BACKGROUND, bg_stipple);
240 }
241
242 static void
243 gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
244                               PangoAttrShape  *attr,
245                               int              x,
246                               int              y)
247 {
248   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
249   GdkGC *fg_gc;
250
251   if (text_renderer->selected)
252     {
253       if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
254         fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
255       else
256         fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
257     }
258   else
259     fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_NORMAL];
260   
261   if (attr->data == NULL)
262     {
263       /* This happens if we have an empty widget anchor. Draw
264        * something empty-looking.
265        */
266       GdkRectangle shape_rect, draw_rect;
267       
268       shape_rect.x = PANGO_PIXELS (x);
269       shape_rect.y = PANGO_PIXELS (y + attr->logical_rect.y);
270       shape_rect.width = PANGO_PIXELS (x + attr->logical_rect.width) - shape_rect.x;
271       shape_rect.height = PANGO_PIXELS (y + attr->logical_rect.y + attr->logical_rect.height) - shape_rect.y;
272       
273       if (gdk_rectangle_intersect (&shape_rect, &text_renderer->clip_rect,
274                                    &draw_rect))
275         {
276           gdk_draw_rectangle (text_renderer->drawable, fg_gc,
277                               FALSE, shape_rect.x, shape_rect.y,
278                               shape_rect.width, shape_rect.height);
279           
280           gdk_draw_line (text_renderer->drawable, fg_gc,
281                          shape_rect.x, shape_rect.y,
282                          shape_rect.x + shape_rect.width,
283                          shape_rect.y + shape_rect.height);
284           
285           gdk_draw_line (text_renderer->drawable, fg_gc,
286                          shape_rect.x + shape_rect.width, shape_rect.y,
287                          shape_rect.x,
288                          shape_rect.y + shape_rect.height);
289         }
290     }
291   else if (GDK_IS_PIXBUF (attr->data))
292     {
293       gint width, height;
294       GdkRectangle pixbuf_rect, draw_rect;
295       GdkPixbuf *pixbuf;
296       
297       pixbuf = GDK_PIXBUF (attr->data);
298       
299       width = gdk_pixbuf_get_width (pixbuf);
300       height = gdk_pixbuf_get_height (pixbuf);
301       
302       pixbuf_rect.x = PANGO_PIXELS (x);
303       pixbuf_rect.y = PANGO_PIXELS (y) - height;
304       pixbuf_rect.width = width;
305       pixbuf_rect.height = height;
306       
307       if (gdk_rectangle_intersect (&pixbuf_rect, &text_renderer->clip_rect,
308                                    &draw_rect))
309         {
310           gdk_draw_pixbuf (text_renderer->drawable,
311                            fg_gc,
312                            pixbuf,
313                            draw_rect.x - pixbuf_rect.x,
314                            draw_rect.y - pixbuf_rect.y,
315                            draw_rect.x, draw_rect.y,
316                            draw_rect.width,
317                            draw_rect.height,
318                            GDK_RGB_DITHER_NORMAL,
319                            0, 0);
320         }
321     }
322   else if (GTK_IS_WIDGET (attr->data))
323     {
324       GtkWidget *widget;
325       
326       widget = GTK_WIDGET (attr->data);
327
328       text_renderer->widgets = g_list_prepend (text_renderer->widgets,
329                                                g_object_ref (widget));
330     }
331   else
332     g_assert_not_reached (); /* not a pixbuf or widget */
333 }
334
335 static void
336 gtk_text_renderer_finalize (GObject *object)
337 {
338   G_OBJECT_CLASS (_gtk_text_renderer_parent_class)->finalize (object);
339 }
340
341 static void
342 _gtk_text_renderer_init (GtkTextRenderer *renderer)
343 {
344 }
345
346 static void
347 _gtk_text_renderer_class_init (GtkTextRendererClass *klass)
348 {
349   GObjectClass *object_class = G_OBJECT_CLASS (klass);
350   
351   PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
352   
353   renderer_class->prepare_run = gtk_text_renderer_prepare_run;
354   renderer_class->draw_shape = gtk_text_renderer_draw_shape;
355
356   object_class->finalize = gtk_text_renderer_finalize;
357 }
358
359 static void
360 text_renderer_set_selected (GtkTextRenderer *text_renderer,
361                             gboolean         selected)
362 {
363   text_renderer->selected = selected;
364 }
365
366 static void
367 text_renderer_begin (GtkTextRenderer *text_renderer,
368                      GtkWidget       *widget,
369                      GdkDrawable     *drawable,
370                      GdkRectangle    *clip_rect)
371 {
372   text_renderer->widget = widget;
373   text_renderer->drawable = drawable;
374   text_renderer->clip_rect = *clip_rect;
375
376   gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), drawable);
377   gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer),
378                              widget->style->text_gc[widget->state]);
379 }
380
381 /* Returns a GSList of (referenced) widgets encountered while drawing.
382  */
383 static GList *
384 text_renderer_end (GtkTextRenderer *text_renderer)
385 {
386   GList *widgets = text_renderer->widgets;
387
388   text_renderer->widget = NULL;
389   text_renderer->drawable = NULL;
390
391   text_renderer->widgets = NULL;
392
393   if (text_renderer->error_color)
394     {
395       gdk_color_free (text_renderer->error_color);
396       text_renderer->error_color = NULL;
397     }
398
399   gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), NULL);
400   gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
401
402   return widgets;
403 }
404
405
406 static GdkRegion *
407 get_selected_clip (GtkTextRenderer    *text_renderer,
408                    PangoLayout        *layout,
409                    PangoLayoutLine    *line,
410                    int                 x,
411                    int                 y,
412                    int                 height,
413                    int                 start_index,
414                    int                 end_index)
415 {
416   gint *ranges;
417   gint n_ranges, i;
418   GdkRegion *clip_region = gdk_region_new ();
419   GdkRegion *tmp_region;
420
421   pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
422
423   for (i=0; i < n_ranges; i++)
424     {
425       GdkRectangle rect;
426
427       rect.x = x + PANGO_PIXELS (ranges[2*i]);
428       rect.y = y;
429       rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
430       rect.height = height;
431       
432       gdk_region_union_with_rect (clip_region, &rect);
433     }
434
435   tmp_region = gdk_region_rectangle (&text_renderer->clip_rect);
436   gdk_region_intersect (clip_region, tmp_region);
437   gdk_region_destroy (tmp_region);
438
439   g_free (ranges);
440   return clip_region;
441 }
442
443 static void
444 render_para (GtkTextRenderer    *text_renderer,
445              GtkTextLineDisplay *line_display,
446              /* Top-left corner of paragraph including all margins */
447              int                 x,
448              int                 y,
449              int                 selection_start_index,
450              int                 selection_end_index)
451 {
452   PangoLayout *layout = line_display->layout;
453   int byte_offset = 0;
454   PangoLayoutIter *iter;
455   PangoRectangle layout_logical;
456   int screen_width;
457   GdkGC *selection_gc, *fg_gc;
458   gint state;
459   
460   gboolean first = TRUE;
461
462   iter = pango_layout_get_iter (layout);
463
464   pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical);
465
466   /* Adjust for margins */
467   
468   layout_logical.x += line_display->x_offset * PANGO_SCALE;
469   layout_logical.y += line_display->top_margin * PANGO_SCALE;
470
471   screen_width = line_display->total_width;
472   
473   if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
474     state = GTK_STATE_SELECTED;
475   else
476     state = GTK_STATE_ACTIVE;
477
478   selection_gc = text_renderer->widget->style->base_gc [state];
479   fg_gc = text_renderer->widget->style->text_gc[text_renderer->widget->state];
480
481   do
482     {
483       PangoLayoutLine *line = pango_layout_iter_get_line (iter);
484       int selection_y, selection_height;
485       int first_y, last_y;
486       PangoRectangle line_rect;
487       int baseline;
488       
489       pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
490       baseline = pango_layout_iter_get_baseline (iter);
491       pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
492       
493       /* Adjust for margins */
494
495       line_rect.x += line_display->x_offset * PANGO_SCALE;
496       line_rect.y += line_display->top_margin * PANGO_SCALE;
497       baseline += line_display->top_margin * PANGO_SCALE;
498
499       /* Selection is the height of the line, plus top/bottom
500        * margin if we're the first/last line
501        */
502       selection_y = y + PANGO_PIXELS (first_y) + line_display->top_margin;
503       selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
504
505       if (first)
506         {
507           selection_y -= line_display->top_margin;
508           selection_height += line_display->top_margin;
509         }
510       
511       if (pango_layout_iter_at_last_line (iter))
512         selection_height += line_display->bottom_margin;
513       
514       first = FALSE;
515
516       if (selection_start_index < byte_offset &&
517           selection_end_index > line->length + byte_offset) /* All selected */
518         {
519           gdk_draw_rectangle (text_renderer->drawable,
520                               selection_gc,
521                               TRUE,
522                               x + line_display->left_margin,
523                               selection_y,
524                               screen_width,
525                               selection_height);
526
527           text_renderer_set_selected (text_renderer, TRUE);
528           pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
529                                            line, 
530                                            PANGO_SCALE * x + line_rect.x,
531                                            PANGO_SCALE * y + baseline);
532         }
533       else
534         {
535           if (line_display->pg_bg_color)
536             {
537               GdkGC *bg_gc;
538               
539               bg_gc = gdk_gc_new (text_renderer->drawable);
540               gdk_gc_set_fill (bg_gc, GDK_SOLID);
541               gdk_gc_set_rgb_fg_color (bg_gc, line_display->pg_bg_color);
542             
543               gdk_draw_rectangle (text_renderer->drawable,
544                                   bg_gc,
545                                   TRUE,
546                                   x + line_display->left_margin,
547                                   selection_y,
548                                   screen_width,
549                                   selection_height);
550               
551               g_object_unref (bg_gc);
552             }
553         
554           text_renderer_set_selected (text_renderer, FALSE);
555           pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
556                                            line, 
557                                            PANGO_SCALE * x + line_rect.x,
558                                            PANGO_SCALE * y + baseline);
559
560           /* Check if some part of the line is selected; the newline
561            * that is after line->length for the last line of the
562            * paragraph counts as part of the line for this
563            */
564           if ((selection_start_index < byte_offset + line->length ||
565                (selection_start_index == byte_offset + line->length && pango_layout_iter_at_last_line (iter))) &&
566               selection_end_index > byte_offset)
567             {
568               GdkRegion *clip_region = get_selected_clip (text_renderer, layout, line,
569                                                           x + line_display->x_offset,
570                                                           selection_y,
571                                                           selection_height,
572                                                           selection_start_index, selection_end_index);
573
574               /* When we change the clip on the foreground GC, we have to set
575                * it on the rendererer again, since the rendererer might have
576                * copied the GC to change attributes.
577                */
578               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
579               gdk_gc_set_clip_region (selection_gc, clip_region);
580               gdk_gc_set_clip_region (fg_gc, clip_region);
581               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
582
583               gdk_draw_rectangle (text_renderer->drawable,
584                                   selection_gc,
585                                   TRUE,
586                                   x + PANGO_PIXELS (line_rect.x),
587                                   selection_y,
588                                   PANGO_PIXELS (line_rect.width),
589                                   selection_height);
590
591               text_renderer_set_selected (text_renderer, TRUE);
592               pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
593                                                line, 
594                                                PANGO_SCALE * x + line_rect.x,
595                                                PANGO_SCALE * y + baseline);
596
597               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
598               gdk_gc_set_clip_region (selection_gc, NULL);
599               gdk_gc_set_clip_region (fg_gc, NULL);
600               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
601               
602               gdk_region_destroy (clip_region);
603
604               /* Paint in the ends of the line */
605               if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
606                   ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
607                    (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
608                 {
609                   gdk_draw_rectangle (text_renderer->drawable,
610                                       selection_gc,
611                                       TRUE,
612                                       x + line_display->left_margin,
613                                       selection_y,
614                                       PANGO_PIXELS (line_rect.x) - line_display->left_margin,
615                                       selection_height);
616                 }
617
618               if (line_rect.x + line_rect.width <
619                   (screen_width + line_display->left_margin) * PANGO_SCALE &&
620                   ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) ||
621                    (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
622                 {
623                   int nonlayout_width;
624
625                   nonlayout_width =
626                     line_display->left_margin + screen_width -
627                     PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
628
629                   gdk_draw_rectangle (text_renderer->drawable,
630                                       selection_gc,
631                                       TRUE,
632                                       x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
633                                       selection_y,
634                                       nonlayout_width,
635                                       selection_height);
636                 }
637             }
638         }
639
640       byte_offset += line->length;
641     }
642   while (pango_layout_iter_next_line (iter));
643
644   pango_layout_iter_free (iter);
645 }
646
647 static void
648 on_renderer_display_closed (GdkDisplay       *display,
649                             GtkTextRenderer  *text_renderer)
650 {
651   g_signal_handlers_disconnect_by_func (text_renderer->screen,
652                                         (gpointer)on_renderer_display_closed,
653                                         text_renderer);
654   g_object_set_data (G_OBJECT (text_renderer->screen), g_intern_static_string ("gtk-text-renderer"), NULL);
655 }
656
657 static GtkTextRenderer *
658 get_text_renderer (GdkScreen *screen)
659 {
660   GtkTextRenderer *text_renderer;
661
662   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
663   
664   text_renderer = g_object_get_data (G_OBJECT (screen), "gtk-text-renderer");
665   if (!text_renderer)
666     {
667       text_renderer = g_object_new (GTK_TYPE_TEXT_RENDERER, "screen", screen, NULL);
668       text_renderer->screen = screen;
669       
670       g_object_set_data_full (G_OBJECT (screen), g_intern_static_string ("gtk-text-renderer"), text_renderer,
671                               (GDestroyNotify)g_object_unref);
672
673       g_signal_connect (gdk_screen_get_display (screen), "closed",
674                         G_CALLBACK (on_renderer_display_closed), text_renderer);
675     }
676
677   return text_renderer;
678 }
679
680 void
681 gtk_text_layout_draw (GtkTextLayout *layout,
682                       GtkWidget *widget,
683                       GdkDrawable *drawable,
684                       GdkGC       *cursor_gc,
685                       /* Location of the drawable
686                          in layout coordinates */
687                       gint x_offset,
688                       gint y_offset,
689                       /* Region of the layout to
690                          render */
691                       gint x,
692                       gint y,
693                       gint width,
694                       gint height,
695                       /* widgets to expose */
696                       GList **widgets)
697 {
698   GdkRectangle clip;
699   gint current_y;
700   GSList *cursor_list;
701   GtkTextRenderer *text_renderer;
702   GtkTextIter selection_start, selection_end;
703   gboolean have_selection = FALSE;
704   GSList *line_list;
705   GSList *tmp_list;
706   GList *tmp_widgets;
707   
708   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
709   g_return_if_fail (layout->default_style != NULL);
710   g_return_if_fail (layout->buffer != NULL);
711   g_return_if_fail (drawable != NULL);
712   g_return_if_fail (width >= 0);
713   g_return_if_fail (height >= 0);
714
715   if (width == 0 || height == 0)
716     return;
717
718   line_list =  gtk_text_layout_get_lines (layout, y + y_offset, y + y_offset + height, &current_y);
719   current_y -= y_offset;
720
721   if (line_list == NULL)
722     return; /* nothing on the screen */
723
724   clip.x = x;
725   clip.y = y;
726   clip.width = width;
727   clip.height = height;
728
729   text_renderer = get_text_renderer (gdk_drawable_get_screen (drawable));
730
731   text_renderer_begin (text_renderer, widget, drawable, &clip);
732
733   gtk_text_layout_wrap_loop_start (layout);
734
735   if (gtk_text_buffer_get_selection_bounds (layout->buffer,
736                                             &selection_start,
737                                             &selection_end))
738     have_selection = TRUE;
739
740   tmp_list = line_list;
741   while (tmp_list != NULL)
742     {
743       GtkTextLineDisplay *line_display;
744       gint selection_start_index = -1;
745       gint selection_end_index = -1;
746       gboolean have_strong;
747       gboolean have_weak;
748
749       GtkTextLine *line = tmp_list->data;
750
751       line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
752
753       if (line_display->height > 0)
754         {
755           g_assert (line_display->layout != NULL);
756           
757           if (have_selection)
758             {
759               GtkTextIter line_start, line_end;
760               gint byte_count;
761               
762               gtk_text_layout_get_iter_at_line (layout,
763                                                 &line_start,
764                                                 line, 0);
765               line_end = line_start;
766               if (!gtk_text_iter_ends_line (&line_end))
767                 gtk_text_iter_forward_to_line_end (&line_end);
768               byte_count = gtk_text_iter_get_visible_line_index (&line_end);     
769
770               if (gtk_text_iter_compare (&selection_start, &line_end) <= 0 &&
771                   gtk_text_iter_compare (&selection_end, &line_start) >= 0)
772                 {
773                   if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
774                     selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
775                   else
776                     selection_start_index = -1;
777
778                   if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
779                     selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
780                   else
781                     selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
782                 }
783             }
784
785           render_para (text_renderer, line_display,
786                        - x_offset,
787                        current_y,
788                        selection_start_index, selection_end_index);
789
790           /* We paint the cursors last, because they overlap another chunk
791          and need to appear on top. */
792
793           have_strong = FALSE;
794           have_weak = FALSE;
795           
796           cursor_list = line_display->cursors;
797           while (cursor_list)
798             {
799               GtkTextCursorDisplay *cursor = cursor_list->data;
800               if (cursor->is_strong)
801                 have_strong = TRUE;
802               else
803                 have_weak = TRUE;
804               
805               cursor_list = cursor_list->next;
806             }
807           
808           cursor_list = line_display->cursors;
809           while (cursor_list)
810             {
811               GtkTextCursorDisplay *cursor = cursor_list->data;
812               GtkTextDirection dir;
813               GdkRectangle cursor_location;
814
815               dir = line_display->direction;
816               if (have_strong && have_weak)
817                 {
818                   if (!cursor->is_strong)
819                     dir = (dir == GTK_TEXT_DIR_RTL) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
820                 }
821  
822               cursor_location.x = line_display->x_offset + cursor->x - x_offset;
823               cursor_location.y = current_y + line_display->top_margin + cursor->y;
824               cursor_location.width = 0;
825               cursor_location.height = cursor->height;
826
827               gtk_draw_insertion_cursor (widget, drawable, &clip, &cursor_location,
828                                          cursor->is_strong,
829                                          dir, have_strong && have_weak);
830
831               cursor_list = cursor_list->next;
832             }
833         } /* line_display->height > 0 */
834           
835       current_y += line_display->height;
836       gtk_text_layout_free_line_display (layout, line_display);
837       
838       tmp_list = g_slist_next (tmp_list);
839     }
840
841   gtk_text_layout_wrap_loop_end (layout);
842
843   tmp_widgets = text_renderer_end (text_renderer);
844   if (widgets)
845     *widgets = tmp_widgets;
846   else
847     {
848       g_list_foreach (tmp_widgets, (GFunc)g_object_unref, NULL);
849       g_list_free (tmp_widgets);
850     }
851
852   g_slist_free (line_list);
853 }
854
855 #define __GTK_TEXT_DISPLAY_C__
856 #include "gtkaliasdef.c"