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