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