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