]> Pileus Git - ~andy/gtk/blob - gtk/gtktextdisplay.c
Updated Norwegian bokmål translation
[~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 "gtkintl.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 enum {
96   NORMAL,
97   SELECTED,
98   CURSOR
99 };
100
101 struct _GtkTextRenderer
102 {
103   GdkPangoRenderer parent_instance;
104
105   GdkScreen *screen;
106
107   GtkWidget *widget;
108   GdkDrawable *drawable;
109   GdkRectangle clip_rect;
110   
111   GdkColor *error_color;        /* Error underline color for this widget */
112   GList *widgets;               /* widgets encountered when drawing */
113   
114   int state;
115 };
116
117 struct _GtkTextRendererClass
118 {
119   GdkPangoRendererClass parent_class;
120 };
121
122 G_DEFINE_TYPE (GtkTextRenderer, _gtk_text_renderer, GDK_TYPE_PANGO_RENDERER)
123
124 static GdkColor *
125 text_renderer_get_error_color (GtkTextRenderer *text_renderer)
126 {
127   static const GdkColor red = { 0, 0xffff, 0, 0 };
128
129   if (!text_renderer->error_color)
130     gtk_widget_style_get (text_renderer->widget,
131                           "error-underline-color", &text_renderer->error_color,
132                           NULL);
133   
134   if (!text_renderer->error_color)
135     text_renderer->error_color = gdk_color_copy (&red);
136
137   return text_renderer->error_color;
138 }
139
140 static void
141 text_renderer_set_gdk_color (GtkTextRenderer *text_renderer,
142                              PangoRenderPart  part,
143                              GdkColor        *gdk_color)
144 {
145   PangoRenderer *renderer = PANGO_RENDERER (text_renderer);
146
147   if (gdk_color)
148     {
149       PangoColor color;
150
151       color.red = gdk_color->red;
152       color.green = gdk_color->green;
153       color.blue = gdk_color->blue;
154       
155       pango_renderer_set_color (renderer, part, &color);
156     }
157   else
158     pango_renderer_set_color (renderer, part, NULL);
159            
160 }
161
162 static GtkTextAppearance *
163 get_item_appearance (PangoItem *item)
164 {
165   GSList *tmp_list = item->analysis.extra_attrs;
166
167   while (tmp_list)
168     {
169       PangoAttribute *attr = tmp_list->data;
170
171       if (attr->klass->type == gtk_text_attr_appearance_type)
172         return &((GtkTextAttrAppearance *)attr)->appearance;
173
174       tmp_list = tmp_list->next;
175     }
176
177   return NULL;
178 }
179
180 static void
181 gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
182                                PangoLayoutRun *run)
183 {
184   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
185   GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
186   GdkColor *bg_color, *fg_color, *underline_color;
187   GdkPixmap *fg_stipple, *bg_stipple;
188   GtkTextAppearance *appearance;
189
190   PANGO_RENDERER_CLASS (_gtk_text_renderer_parent_class)->prepare_run (renderer, run);
191
192   appearance = get_item_appearance (run->item);
193   g_assert (appearance != NULL);
194
195   if (appearance->draw_bg && text_renderer->state == NORMAL)
196     bg_color = &appearance->bg_color;
197   else
198     bg_color = NULL;
199   
200   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
201
202   if (text_renderer->state == SELECTED)
203     {
204       if (gtk_widget_has_focus (text_renderer->widget))
205         fg_color = &text_renderer->widget->style->text[GTK_STATE_SELECTED];
206       else
207         fg_color = &text_renderer->widget->style->text[GTK_STATE_ACTIVE];
208     }
209   else if (text_renderer->state == CURSOR && gtk_widget_has_focus (text_renderer->widget))
210     fg_color = &text_renderer->widget->style->base[GTK_STATE_NORMAL];
211   else
212     fg_color = &appearance->fg_color;
213
214   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_FOREGROUND, fg_color);
215   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_color);
216
217   if (appearance->underline == PANGO_UNDERLINE_ERROR)
218     underline_color = text_renderer_get_error_color (text_renderer);
219   else
220     underline_color = fg_color;
221
222   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_UNDERLINE, underline_color);
223
224   fg_stipple = appearance->fg_stipple;
225   if (fg_stipple && text_renderer->screen != gdk_drawable_get_screen (fg_stipple))
226     {
227       g_warning ("gtk_text_renderer_prepare_run:\n"
228                  "The foreground stipple bitmap has been created on the wrong screen.\n"
229                  "Ignoring the stipple bitmap information.");
230       fg_stipple = NULL;
231     }
232       
233   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_FOREGROUND, fg_stipple);
234   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_stipple);
235   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_UNDERLINE, fg_stipple);
236
237   bg_stipple = appearance->draw_bg ? appearance->bg_stipple : NULL;
238   
239   if (bg_stipple && text_renderer->screen != gdk_drawable_get_screen (bg_stipple))
240     {
241       g_warning ("gtk_text_renderer_prepare_run:\n"
242                  "The background stipple bitmap has been created on the wrong screen.\n"
243                  "Ignoring the stipple bitmap information.");
244       bg_stipple = NULL;
245     }
246   
247   gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_BACKGROUND, bg_stipple);
248 }
249
250 static void
251 gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
252                               PangoAttrShape  *attr,
253                               int              x,
254                               int              y)
255 {
256   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
257   GdkGC *fg_gc;
258
259   if (text_renderer->state == SELECTED)
260     {
261       if (gtk_widget_has_focus (text_renderer->widget))
262         fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
263       else
264         fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
265     }
266   else if (text_renderer->state == CURSOR && gtk_widget_has_focus (text_renderer->widget))
267     fg_gc = text_renderer->widget->style->base_gc[GTK_STATE_NORMAL];
268   else
269     fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_NORMAL];
270   
271   if (attr->data == NULL)
272     {
273       /* This happens if we have an empty widget anchor. Draw
274        * something empty-looking.
275        */
276       GdkRectangle shape_rect, draw_rect;
277       
278       shape_rect.x = PANGO_PIXELS (x);
279       shape_rect.y = PANGO_PIXELS (y + attr->logical_rect.y);
280       shape_rect.width = PANGO_PIXELS (x + attr->logical_rect.width) - shape_rect.x;
281       shape_rect.height = PANGO_PIXELS (y + attr->logical_rect.y + attr->logical_rect.height) - shape_rect.y;
282       
283       if (gdk_rectangle_intersect (&shape_rect, &text_renderer->clip_rect,
284                                    &draw_rect))
285         {
286           gdk_draw_rectangle (text_renderer->drawable, fg_gc,
287                               FALSE, shape_rect.x, shape_rect.y,
288                               shape_rect.width, shape_rect.height);
289           
290           gdk_draw_line (text_renderer->drawable, fg_gc,
291                          shape_rect.x, shape_rect.y,
292                          shape_rect.x + shape_rect.width,
293                          shape_rect.y + shape_rect.height);
294           
295           gdk_draw_line (text_renderer->drawable, fg_gc,
296                          shape_rect.x + shape_rect.width, shape_rect.y,
297                          shape_rect.x,
298                          shape_rect.y + shape_rect.height);
299         }
300     }
301   else if (GDK_IS_PIXBUF (attr->data))
302     {
303       gint width, height;
304       GdkRectangle pixbuf_rect, draw_rect;
305       GdkPixbuf *pixbuf;
306       
307       pixbuf = GDK_PIXBUF (attr->data);
308       
309       width = gdk_pixbuf_get_width (pixbuf);
310       height = gdk_pixbuf_get_height (pixbuf);
311       
312       pixbuf_rect.x = PANGO_PIXELS (x);
313       pixbuf_rect.y = PANGO_PIXELS (y) - height;
314       pixbuf_rect.width = width;
315       pixbuf_rect.height = height;
316       
317       if (gdk_rectangle_intersect (&pixbuf_rect, &text_renderer->clip_rect,
318                                    &draw_rect))
319         {
320           gdk_draw_pixbuf (text_renderer->drawable,
321                            fg_gc,
322                            pixbuf,
323                            draw_rect.x - pixbuf_rect.x,
324                            draw_rect.y - pixbuf_rect.y,
325                            draw_rect.x, draw_rect.y,
326                            draw_rect.width,
327                            draw_rect.height,
328                            GDK_RGB_DITHER_NORMAL,
329                            0, 0);
330         }
331     }
332   else if (GTK_IS_WIDGET (attr->data))
333     {
334       GtkWidget *widget;
335       
336       widget = GTK_WIDGET (attr->data);
337
338       text_renderer->widgets = g_list_prepend (text_renderer->widgets,
339                                                g_object_ref (widget));
340     }
341   else
342     g_assert_not_reached (); /* not a pixbuf or widget */
343 }
344
345 static void
346 gtk_text_renderer_finalize (GObject *object)
347 {
348   G_OBJECT_CLASS (_gtk_text_renderer_parent_class)->finalize (object);
349 }
350
351 static void
352 _gtk_text_renderer_init (GtkTextRenderer *renderer)
353 {
354 }
355
356 static void
357 _gtk_text_renderer_class_init (GtkTextRendererClass *klass)
358 {
359   GObjectClass *object_class = G_OBJECT_CLASS (klass);
360   
361   PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
362   
363   renderer_class->prepare_run = gtk_text_renderer_prepare_run;
364   renderer_class->draw_shape = gtk_text_renderer_draw_shape;
365
366   object_class->finalize = gtk_text_renderer_finalize;
367 }
368
369 static void
370 text_renderer_set_state (GtkTextRenderer *text_renderer,
371                          int              state)
372 {
373   text_renderer->state = state;
374 }
375
376 static void
377 text_renderer_begin (GtkTextRenderer *text_renderer,
378                      GtkWidget       *widget,
379                      GdkDrawable     *drawable,
380                      GdkRectangle    *clip_rect)
381 {
382   text_renderer->widget = widget;
383   text_renderer->drawable = drawable;
384   text_renderer->clip_rect = *clip_rect;
385
386   gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), drawable);
387   gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer),
388                              widget->style->text_gc[widget->state]);
389 }
390
391 /* Returns a GSList of (referenced) widgets encountered while drawing.
392  */
393 static GList *
394 text_renderer_end (GtkTextRenderer *text_renderer)
395 {
396   GList *widgets = text_renderer->widgets;
397
398   text_renderer->widget = NULL;
399   text_renderer->drawable = NULL;
400
401   text_renderer->widgets = NULL;
402
403   if (text_renderer->error_color)
404     {
405       gdk_color_free (text_renderer->error_color);
406       text_renderer->error_color = NULL;
407     }
408
409   gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), NULL);
410   gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
411
412   return widgets;
413 }
414
415
416 static cairo_region_t *
417 get_selected_clip (GtkTextRenderer    *text_renderer,
418                    PangoLayout        *layout,
419                    PangoLayoutLine    *line,
420                    int                 x,
421                    int                 y,
422                    int                 height,
423                    int                 start_index,
424                    int                 end_index)
425 {
426   gint *ranges;
427   gint n_ranges, i;
428   cairo_region_t *clip_region = cairo_region_create ();
429   cairo_region_t *tmp_region;
430
431   pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
432
433   for (i=0; i < n_ranges; i++)
434     {
435       GdkRectangle rect;
436
437       rect.x = x + PANGO_PIXELS (ranges[2*i]);
438       rect.y = y;
439       rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
440       rect.height = height;
441       
442       cairo_region_union_rectangle (clip_region, &rect);
443     }
444
445   tmp_region = cairo_region_create_rectangle (&text_renderer->clip_rect);
446   cairo_region_intersect (clip_region, tmp_region);
447   cairo_region_destroy (tmp_region);
448
449   g_free (ranges);
450   return clip_region;
451 }
452
453 static void
454 render_para (GtkTextRenderer    *text_renderer,
455              GtkTextLineDisplay *line_display,
456              /* Top-left corner of paragraph including all margins */
457              int                 x,
458              int                 y,
459              int                 selection_start_index,
460              int                 selection_end_index)
461 {
462   PangoLayout *layout = line_display->layout;
463   int byte_offset = 0;
464   PangoLayoutIter *iter;
465   PangoRectangle layout_logical;
466   int screen_width;
467   GdkGC *selection_gc, *fg_gc;
468   gint state;
469   
470   gboolean first = TRUE;
471
472   iter = pango_layout_get_iter (layout);
473
474   pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical);
475
476   /* Adjust for margins */
477   
478   layout_logical.x += line_display->x_offset * PANGO_SCALE;
479   layout_logical.y += line_display->top_margin * PANGO_SCALE;
480
481   screen_width = line_display->total_width;
482   
483   if (gtk_widget_has_focus (text_renderer->widget))
484     state = GTK_STATE_SELECTED;
485   else
486     state = GTK_STATE_ACTIVE;
487
488   selection_gc = text_renderer->widget->style->base_gc [state];
489   fg_gc = text_renderer->widget->style->text_gc[text_renderer->widget->state];
490
491   do
492     {
493       PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
494       int selection_y, selection_height;
495       int first_y, last_y;
496       PangoRectangle line_rect;
497       int baseline;
498       gboolean at_last_line;
499       
500       pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
501       baseline = pango_layout_iter_get_baseline (iter);
502       pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
503       
504       /* Adjust for margins */
505
506       line_rect.x += line_display->x_offset * PANGO_SCALE;
507       line_rect.y += line_display->top_margin * PANGO_SCALE;
508       baseline += line_display->top_margin * PANGO_SCALE;
509
510       /* Selection is the height of the line, plus top/bottom
511        * margin if we're the first/last line
512        */
513       selection_y = y + PANGO_PIXELS (first_y) + line_display->top_margin;
514       selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
515
516       if (first)
517         {
518           selection_y -= line_display->top_margin;
519           selection_height += line_display->top_margin;
520         }
521
522       at_last_line = pango_layout_iter_at_last_line (iter);
523       if (at_last_line)
524         selection_height += line_display->bottom_margin;
525       
526       first = FALSE;
527
528       if (selection_start_index < byte_offset &&
529           selection_end_index > line->length + byte_offset) /* All selected */
530         {
531           gdk_draw_rectangle (text_renderer->drawable,
532                               selection_gc,
533                               TRUE,
534                               x + line_display->left_margin,
535                               selection_y,
536                               screen_width,
537                               selection_height);
538
539           text_renderer_set_state (text_renderer, SELECTED);
540           pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
541                                            line, 
542                                            PANGO_SCALE * x + line_rect.x,
543                                            PANGO_SCALE * y + baseline);
544         }
545       else
546         {
547           if (line_display->pg_bg_color)
548             {
549               GdkGC *bg_gc;
550               
551               bg_gc = gdk_gc_new (text_renderer->drawable);
552               gdk_gc_set_fill (bg_gc, GDK_SOLID);
553               gdk_gc_set_rgb_fg_color (bg_gc, line_display->pg_bg_color);
554             
555               gdk_draw_rectangle (text_renderer->drawable,
556                                   bg_gc,
557                                   TRUE,
558                                   x + line_display->left_margin,
559                                   selection_y,
560                                   screen_width,
561                                   selection_height);
562               
563               g_object_unref (bg_gc);
564             }
565         
566           text_renderer_set_state (text_renderer, NORMAL);
567           pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
568                                            line, 
569                                            PANGO_SCALE * x + line_rect.x,
570                                            PANGO_SCALE * y + baseline);
571
572           /* Check if some part of the line is selected; the newline
573            * that is after line->length for the last line of the
574            * paragraph counts as part of the line for this
575            */
576           if ((selection_start_index < byte_offset + line->length ||
577                (selection_start_index == byte_offset + line->length && pango_layout_iter_at_last_line (iter))) &&
578               selection_end_index > byte_offset)
579             {
580               cairo_region_t *clip_region = get_selected_clip (text_renderer, layout, line,
581                                                           x + line_display->x_offset,
582                                                           selection_y,
583                                                           selection_height,
584                                                           selection_start_index, selection_end_index);
585
586               /* When we change the clip on the foreground GC, we have to set
587                * it on the rendererer again, since the rendererer might have
588                * copied the GC to change attributes.
589                */
590               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
591               gdk_gc_set_clip_region (selection_gc, clip_region);
592               gdk_gc_set_clip_region (fg_gc, clip_region);
593               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
594
595               gdk_draw_rectangle (text_renderer->drawable,
596                                   selection_gc,
597                                   TRUE,
598                                   x + PANGO_PIXELS (line_rect.x),
599                                   selection_y,
600                                   PANGO_PIXELS (line_rect.width),
601                                   selection_height);
602
603               text_renderer_set_state (text_renderer, SELECTED);
604               pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
605                                                line, 
606                                                PANGO_SCALE * x + line_rect.x,
607                                                PANGO_SCALE * y + baseline);
608
609               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
610               gdk_gc_set_clip_region (selection_gc, NULL);
611               gdk_gc_set_clip_region (fg_gc, NULL);
612               gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
613               
614               cairo_region_destroy (clip_region);
615
616               /* Paint in the ends of the line */
617               if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
618                   ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
619                    (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
620                 {
621                   gdk_draw_rectangle (text_renderer->drawable,
622                                       selection_gc,
623                                       TRUE,
624                                       x + line_display->left_margin,
625                                       selection_y,
626                                       PANGO_PIXELS (line_rect.x) - line_display->left_margin,
627                                       selection_height);
628                 }
629
630               if (line_rect.x + line_rect.width <
631                   (screen_width + line_display->left_margin) * PANGO_SCALE &&
632                   ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) ||
633                    (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
634                 {
635                   int nonlayout_width;
636
637                   nonlayout_width =
638                     line_display->left_margin + screen_width -
639                     PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
640
641                   gdk_draw_rectangle (text_renderer->drawable,
642                                       selection_gc,
643                                       TRUE,
644                                       x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
645                                       selection_y,
646                                       nonlayout_width,
647                                       selection_height);
648                 }
649             }
650           else if (line_display->has_block_cursor &&
651                    gtk_widget_has_focus (text_renderer->widget) &&
652                    byte_offset <= line_display->insert_index &&
653                    (line_display->insert_index < byte_offset + line->length ||
654                     (at_last_line && line_display->insert_index == byte_offset + line->length)))
655             {
656               GdkRectangle cursor_rect;
657               GdkGC *cursor_gc;
658
659               /* we draw text using base color on filled cursor rectangle of cursor color
660                * (normally white on black) */
661               cursor_gc = _gtk_widget_get_cursor_gc (text_renderer->widget);
662
663               cursor_rect.x = x + line_display->x_offset + line_display->block_cursor.x;
664               cursor_rect.y = y + line_display->block_cursor.y + line_display->top_margin;
665               cursor_rect.width = line_display->block_cursor.width;
666               cursor_rect.height = line_display->block_cursor.height;
667
668               gdk_gc_set_clip_rectangle (cursor_gc, &cursor_rect);
669
670               gdk_draw_rectangle (text_renderer->drawable,
671                                   cursor_gc,
672                                   TRUE,
673                                   cursor_rect.x,
674                                   cursor_rect.y,
675                                   cursor_rect.width,
676                                   cursor_rect.height);
677
678               gdk_gc_set_clip_region (cursor_gc, NULL);
679
680               /* draw text under the cursor if any */
681               if (!line_display->cursor_at_line_end)
682                 {
683                   GdkGC *cursor_text_gc;
684
685                   cursor_text_gc = text_renderer->widget->style->base_gc[text_renderer->widget->state];
686                   gdk_gc_set_clip_rectangle (cursor_text_gc, &cursor_rect);
687
688                   gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), cursor_text_gc);
689                   text_renderer_set_state (text_renderer, CURSOR);
690
691                   pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
692                                                    line,
693                                                    PANGO_SCALE * x + line_rect.x,
694                                                    PANGO_SCALE * y + baseline);
695
696                   gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
697                   gdk_gc_set_clip_region (cursor_text_gc, NULL);
698                 }
699             }
700         }
701
702       byte_offset += line->length;
703     }
704   while (pango_layout_iter_next_line (iter));
705
706   pango_layout_iter_free (iter);
707 }
708
709 static void
710 on_renderer_display_closed (GdkDisplay       *display,
711                             gboolean          is_error,
712                             GtkTextRenderer  *text_renderer)
713 {
714   g_signal_handlers_disconnect_by_func (text_renderer->screen,
715                                         (gpointer)on_renderer_display_closed,
716                                         text_renderer);
717   g_object_set_data (G_OBJECT (text_renderer->screen), I_("gtk-text-renderer"), NULL);
718 }
719
720 static GtkTextRenderer *
721 get_text_renderer (GdkScreen *screen)
722 {
723   GtkTextRenderer *text_renderer;
724
725   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
726   
727   text_renderer = g_object_get_data (G_OBJECT (screen), "gtk-text-renderer");
728   if (!text_renderer)
729     {
730       text_renderer = g_object_new (GTK_TYPE_TEXT_RENDERER, "screen", screen, NULL);
731       text_renderer->screen = screen;
732       
733       g_object_set_data_full (G_OBJECT (screen), I_("gtk-text-renderer"), text_renderer,
734                               (GDestroyNotify)g_object_unref);
735
736       g_signal_connect_object (gdk_screen_get_display (screen), "closed",
737                                G_CALLBACK (on_renderer_display_closed),
738                                text_renderer, 0);
739     }
740
741   return text_renderer;
742 }
743
744 void
745 gtk_text_layout_draw (GtkTextLayout *layout,
746                       GtkWidget *widget,
747                       GdkDrawable *drawable,
748                       GdkGC       *cursor_gc,
749                       /* Location of the drawable
750                          in layout coordinates */
751                       gint x_offset,
752                       gint y_offset,
753                       /* Region of the layout to
754                          render */
755                       gint x,
756                       gint y,
757                       gint width,
758                       gint height,
759                       /* widgets to expose */
760                       GList **widgets)
761 {
762   GdkRectangle clip;
763   gint current_y;
764   GSList *cursor_list;
765   GtkTextRenderer *text_renderer;
766   GtkTextIter selection_start, selection_end;
767   gboolean have_selection = FALSE;
768   GSList *line_list;
769   GSList *tmp_list;
770   GList *tmp_widgets;
771   
772   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
773   g_return_if_fail (layout->default_style != NULL);
774   g_return_if_fail (layout->buffer != NULL);
775   g_return_if_fail (drawable != NULL);
776   g_return_if_fail (width >= 0);
777   g_return_if_fail (height >= 0);
778
779   if (width == 0 || height == 0)
780     return;
781
782   line_list =  gtk_text_layout_get_lines (layout, y + y_offset, y + y_offset + height, &current_y);
783   current_y -= y_offset;
784
785   if (line_list == NULL)
786     return; /* nothing on the screen */
787
788   clip.x = x;
789   clip.y = y;
790   clip.width = width;
791   clip.height = height;
792
793   text_renderer = get_text_renderer (gdk_drawable_get_screen (drawable));
794
795   text_renderer_begin (text_renderer, widget, drawable, &clip);
796
797   gtk_text_layout_wrap_loop_start (layout);
798
799   if (gtk_text_buffer_get_selection_bounds (layout->buffer,
800                                             &selection_start,
801                                             &selection_end))
802     have_selection = TRUE;
803
804   tmp_list = line_list;
805   while (tmp_list != NULL)
806     {
807       GtkTextLineDisplay *line_display;
808       gint selection_start_index = -1;
809       gint selection_end_index = -1;
810       gboolean have_strong;
811       gboolean have_weak;
812
813       GtkTextLine *line = tmp_list->data;
814
815       line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
816
817       if (line_display->height > 0)
818         {
819           g_assert (line_display->layout != NULL);
820           
821           if (have_selection)
822             {
823               GtkTextIter line_start, line_end;
824               gint byte_count;
825               
826               gtk_text_layout_get_iter_at_line (layout,
827                                                 &line_start,
828                                                 line, 0);
829               line_end = line_start;
830               if (!gtk_text_iter_ends_line (&line_end))
831                 gtk_text_iter_forward_to_line_end (&line_end);
832               byte_count = gtk_text_iter_get_visible_line_index (&line_end);     
833
834               if (gtk_text_iter_compare (&selection_start, &line_end) <= 0 &&
835                   gtk_text_iter_compare (&selection_end, &line_start) >= 0)
836                 {
837                   if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
838                     selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
839                   else
840                     selection_start_index = -1;
841
842                   if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
843                     selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
844                   else
845                     selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
846                 }
847             }
848
849           render_para (text_renderer, line_display,
850                        - x_offset,
851                        current_y,
852                        selection_start_index, selection_end_index);
853
854           /* We paint the cursors last, because they overlap another chunk
855          and need to appear on top. */
856
857           have_strong = FALSE;
858           have_weak = FALSE;
859           
860           cursor_list = line_display->cursors;
861           while (cursor_list)
862             {
863               GtkTextCursorDisplay *cursor = cursor_list->data;
864               if (cursor->is_strong)
865                 have_strong = TRUE;
866               else
867                 have_weak = TRUE;
868               
869               cursor_list = cursor_list->next;
870             }
871           
872           cursor_list = line_display->cursors;
873           while (cursor_list)
874             {
875               GtkTextCursorDisplay *cursor = cursor_list->data;
876               GtkTextDirection dir;
877               GdkRectangle cursor_location;
878
879               dir = line_display->direction;
880               if (have_strong && have_weak)
881                 {
882                   if (!cursor->is_strong)
883                     dir = (dir == GTK_TEXT_DIR_RTL) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
884                 }
885  
886               cursor_location.x = line_display->x_offset + cursor->x - x_offset;
887               cursor_location.y = current_y + line_display->top_margin + cursor->y;
888               cursor_location.width = 0;
889               cursor_location.height = cursor->height;
890
891               gtk_draw_insertion_cursor (widget, drawable, &clip, &cursor_location,
892                                          cursor->is_strong,
893                                          dir, have_strong && have_weak);
894
895               cursor_list = cursor_list->next;
896             }
897         } /* line_display->height > 0 */
898           
899       current_y += line_display->height;
900       gtk_text_layout_free_line_display (layout, line_display);
901       
902       tmp_list = g_slist_next (tmp_list);
903     }
904
905   gtk_text_layout_wrap_loop_end (layout);
906
907   tmp_widgets = text_renderer_end (text_renderer);
908   if (widgets)
909     *widgets = tmp_widgets;
910   else
911     {
912       g_list_foreach (tmp_widgets, (GFunc)g_object_unref, NULL);
913       g_list_free (tmp_widgets);
914     }
915
916   g_slist_free (line_list);
917 }