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, see <http://www.gnu.org/licenses/>.Free
25  *
26  * Original Tk license:
27  *
28  * This software is copyrighted by the Regents of the University of
29  * California, Sun Microsystems, Inc., and other parties.  The
30  * following terms apply to all files associated with the software
31  * unless explicitly disclaimed in individual files.
32  *
33  * The authors hereby grant permission to use, copy, modify,
34  * distribute, and license this software and its documentation for any
35  * purpose, provided that existing copyright notices are retained in
36  * all copies and that this notice is included verbatim in any
37  * distributions. No written agreement, license, or royalty fee is
38  * required for any of the authorized uses.  Modifications to this
39  * software may be copyrighted by their authors and need not follow
40  * the licensing terms described here, provided that the new terms are
41  * clearly indicated on the first page of each file where they apply.
42  *
43  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
44  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
45  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
46  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
47  * OF THE POSSIBILITY OF SUCH DAMAGE.
48  *
49  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
50  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
51  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
52  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
53  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
54  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
55  *
56  * GOVERNMENT USE: If you are acquiring this software on behalf of the
57  * U.S. government, the Government shall have only "Restricted Rights"
58  * in the software and related documentation as defined in the Federal
59  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
60  * are acquiring the software on behalf of the Department of Defense,
61  * the software shall be classified as "Commercial Computer Software"
62  * and the Government shall have only "Restricted Rights" as defined
63  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
64  * foregoing, the authors grant the U.S. Government and others acting
65  * in its behalf permission to use and distribute the software in
66  * accordance with the terms specified in this license.
67  *
68  */
69 /*
70  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
71  * file for a list of people on the GTK+ Team.  See the ChangeLog
72  * files for a list of changes.  These files are distributed with
73  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
74  */
75 
76 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
77 #include "config.h"
78 #include "gtktextattributesprivate.h"
79 #include "gtktextdisplay.h"
80 #include "gtkwidgetprivate.h"
81 #include "gtkstylecontextprivate.h"
82 #include "gtkintl.h"
83 
84 /* DO NOT go putting private headers in here. This file should only
85  * use the semi-public headers, as with gtktextview.c.
86  */
87 
88 #define GTK_TYPE_TEXT_RENDERER            (_gtk_text_renderer_get_type())
89 #define GTK_TEXT_RENDERER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_TEXT_RENDERER, GtkTextRenderer))
90 #define GTK_IS_TEXT_RENDERER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_TEXT_RENDERER))
91 #define GTK_TEXT_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
92 #define GTK_IS_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_RENDERER))
93 #define GTK_TEXT_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
94 
95 typedef struct _GtkTextRenderer      GtkTextRenderer;
96 typedef struct _GtkTextRendererClass GtkTextRendererClass;
97 
98 enum {
99   NORMAL,
100   SELECTED,
101   CURSOR
102 };
103 
104 struct _GtkTextRenderer
105 {
106   PangoRenderer parent_instance;
107 
108   GtkWidget *widget;
109   cairo_t *cr;
110 
111   GdkRGBA *error_color;	/* Error underline color for this widget */
112   GList *widgets;      	/* widgets encountered when drawing */
113 
114   guint state : 2;
115 };
116 
117 struct _GtkTextRendererClass
118 {
119   PangoRendererClass parent_class;
120 };
121 
122 GType _gtk_text_renderer_get_type (void);
123 
G_DEFINE_TYPE(GtkTextRenderer,_gtk_text_renderer,PANGO_TYPE_RENDERER)124 G_DEFINE_TYPE (GtkTextRenderer, _gtk_text_renderer, PANGO_TYPE_RENDERER)
125 
126 static void
127 text_renderer_set_rgba (GtkTextRenderer *text_renderer,
128 			PangoRenderPart  part,
129 			const GdkRGBA   *rgba)
130 {
131   PangoRenderer *renderer = PANGO_RENDERER (text_renderer);
132   PangoColor color = { 0, };
133   guint16 alpha;
134 
135   if (rgba)
136     {
137       color.red = (guint16)(rgba->red * 65535);
138       color.green = (guint16)(rgba->green * 65535);
139       color.blue = (guint16)(rgba->blue * 65535);
140       alpha = (guint16)(rgba->alpha * 65535);
141       pango_renderer_set_color (renderer, part, &color);
142       pango_renderer_set_alpha (renderer, part, alpha);
143     }
144   else
145     {
146       pango_renderer_set_color (renderer, part, NULL);
147       pango_renderer_set_alpha (renderer, part, 0);
148     }
149 }
150 
151 static GtkTextAppearance *
get_item_appearance(PangoItem * item)152 get_item_appearance (PangoItem *item)
153 {
154   GSList *tmp_list = item->analysis.extra_attrs;
155 
156   while (tmp_list)
157     {
158       PangoAttribute *attr = tmp_list->data;
159 
160       if (attr->klass->type == gtk_text_attr_appearance_type)
161 	return &((GtkTextAttrAppearance *)attr)->appearance;
162 
163       tmp_list = tmp_list->next;
164     }
165 
166   return NULL;
167 }
168 
169 extern GtkCssNode *gtk_text_view_get_text_node      (GtkTextView *text_view);
170 extern GtkCssNode *gtk_text_view_get_selection_node (GtkTextView *text_view);
171 
172 static void
gtk_text_renderer_prepare_run(PangoRenderer * renderer,PangoLayoutRun * run)173 gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
174 			       PangoLayoutRun *run)
175 {
176   GtkStyleContext *context;
177   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
178   GdkRGBA *bg_rgba = NULL;
179   GdkRGBA *fg_rgba = NULL;
180   GtkTextAppearance *appearance;
181 
182   PANGO_RENDERER_CLASS (_gtk_text_renderer_parent_class)->prepare_run (renderer, run);
183 
184   appearance = get_item_appearance (run->item);
185   g_assert (appearance != NULL);
186 
187   context = gtk_widget_get_style_context (text_renderer->widget);
188 
189   if (appearance->draw_bg && text_renderer->state == NORMAL)
190     bg_rgba = appearance->rgba[0];
191   else
192     bg_rgba = NULL;
193 
194   text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_rgba);
195 
196   if (text_renderer->state == SELECTED)
197     {
198       GtkCssNode *selection_node;
199 
200       selection_node = gtk_text_view_get_selection_node ((GtkTextView *)text_renderer->widget);
201       gtk_style_context_save_to_node (context, selection_node);
202 
203       gtk_style_context_get (context, gtk_style_context_get_state (context),
204                              "color", &fg_rgba,
205                              NULL);
206 
207       gtk_style_context_restore (context);
208     }
209   else if (text_renderer->state == CURSOR && gtk_widget_has_focus (text_renderer->widget))
210     {
211       gtk_style_context_get (context, gtk_style_context_get_state (context),
212                              "background-color", &fg_rgba,
213                               NULL);
214     }
215   else
216     fg_rgba = appearance->rgba[1];
217 
218   text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_FOREGROUND, fg_rgba);
219 
220   if (GTK_TEXT_APPEARANCE_GET_STRIKETHROUGH_RGBA_SET (appearance))
221     {
222       GdkRGBA rgba;
223 
224       GTK_TEXT_APPEARANCE_GET_STRIKETHROUGH_RGBA (appearance, &rgba);
225       text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, &rgba);
226     }
227   else
228     text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_rgba);
229 
230   if (GTK_TEXT_APPEARANCE_GET_UNDERLINE_RGBA_SET (appearance))
231     {
232       GdkRGBA rgba;
233 
234       GTK_TEXT_APPEARANCE_GET_UNDERLINE_RGBA (appearance, &rgba);
235       text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_UNDERLINE, &rgba);
236     }
237   else if (appearance->underline == PANGO_UNDERLINE_ERROR)
238     {
239       if (!text_renderer->error_color)
240         {
241 	  GdkColor *color = NULL;
242 
243           gtk_style_context_get_style (context,
244                                        "error-underline-color", &color,
245                                        NULL);
246 
247 	  if (color)
248 	    {
249 	      GdkRGBA rgba;
250 
251 	      rgba.red = color->red / 65535.;
252 	      rgba.green = color->green / 65535.;
253 	      rgba.blue = color->blue / 65535.;
254 	      rgba.alpha = 1;
255 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
256 	      gdk_color_free (color);
257 G_GNUC_END_IGNORE_DEPRECATIONS
258 
259 	      text_renderer->error_color = gdk_rgba_copy (&rgba);
260 	    }
261 	  else
262 	    {
263 	      static const GdkRGBA red = { 1, 0, 0, 1 };
264 	      text_renderer->error_color = gdk_rgba_copy (&red);
265 	    }
266         }
267 
268       text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_UNDERLINE, text_renderer->error_color);
269     }
270   else
271     text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_UNDERLINE, fg_rgba);
272 
273   if (fg_rgba != appearance->rgba[1])
274     gdk_rgba_free (fg_rgba);
275 }
276 
277 static void
set_color(GtkTextRenderer * text_renderer,PangoRenderPart part)278 set_color (GtkTextRenderer *text_renderer,
279            PangoRenderPart  part)
280 {
281   PangoColor *color;
282   GdkRGBA rgba;
283   guint16 alpha;
284 
285   cairo_save (text_renderer->cr);
286 
287   color = pango_renderer_get_color (PANGO_RENDERER (text_renderer), part);
288   alpha = pango_renderer_get_alpha (PANGO_RENDERER (text_renderer), part);
289   if (color)
290     {
291       rgba.red = color->red / 65535.;
292       rgba.green = color->green / 65535.;
293       rgba.blue = color->blue / 65535.;
294       rgba.alpha = alpha / 65535.;
295       gdk_cairo_set_source_rgba (text_renderer->cr, &rgba);
296     }
297 }
298 
299 static void
unset_color(GtkTextRenderer * text_renderer)300 unset_color (GtkTextRenderer *text_renderer)
301 {
302   cairo_restore (text_renderer->cr);
303 }
304 
305 static void
gtk_text_renderer_draw_glyphs(PangoRenderer * renderer,PangoFont * font,PangoGlyphString * glyphs,int x,int y)306 gtk_text_renderer_draw_glyphs (PangoRenderer     *renderer,
307                                PangoFont         *font,
308                                PangoGlyphString  *glyphs,
309                                int                x,
310                                int                y)
311 {
312   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
313 
314   set_color (text_renderer, PANGO_RENDER_PART_FOREGROUND);
315 
316   cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
317   pango_cairo_show_glyph_string (text_renderer->cr, font, glyphs);
318 
319   unset_color (text_renderer);
320 }
321 
322 static void
gtk_text_renderer_draw_glyph_item(PangoRenderer * renderer,const char * text,PangoGlyphItem * glyph_item,int x,int y)323 gtk_text_renderer_draw_glyph_item (PangoRenderer     *renderer,
324                                    const char        *text,
325                                    PangoGlyphItem    *glyph_item,
326                                    int                x,
327                                    int                y)
328 {
329   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
330 
331   set_color (text_renderer, PANGO_RENDER_PART_FOREGROUND);
332 
333   cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
334   pango_cairo_show_glyph_item (text_renderer->cr, text, glyph_item);
335 
336   unset_color (text_renderer);
337 }
338 
339 static void
gtk_text_renderer_draw_rectangle(PangoRenderer * renderer,PangoRenderPart part,int x,int y,int width,int height)340 gtk_text_renderer_draw_rectangle (PangoRenderer     *renderer,
341 				  PangoRenderPart    part,
342 				  int                x,
343 				  int                y,
344 				  int                width,
345 				  int                height)
346 {
347   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
348 
349   set_color (text_renderer, part);
350 
351   cairo_rectangle (text_renderer->cr,
352                    (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
353 		   (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
354   cairo_fill (text_renderer->cr);
355 
356   unset_color (text_renderer);
357 }
358 
359 static void
gtk_text_renderer_draw_trapezoid(PangoRenderer * renderer,PangoRenderPart part,double y1_,double x11,double x21,double y2,double x12,double x22)360 gtk_text_renderer_draw_trapezoid (PangoRenderer     *renderer,
361 				  PangoRenderPart    part,
362 				  double             y1_,
363 				  double             x11,
364 				  double             x21,
365 				  double             y2,
366 				  double             x12,
367 				  double             x22)
368 {
369   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
370   cairo_t *cr;
371   cairo_matrix_t matrix;
372 
373   set_color (text_renderer, part);
374 
375   cr = text_renderer->cr;
376 
377   cairo_get_matrix (cr, &matrix);
378   matrix.xx = matrix.yy = 1.0;
379   matrix.xy = matrix.yx = 0.0;
380   cairo_set_matrix (cr, &matrix);
381 
382   cairo_move_to (cr, x11, y1_);
383   cairo_line_to (cr, x21, y1_);
384   cairo_line_to (cr, x22, y2);
385   cairo_line_to (cr, x12, y2);
386   cairo_close_path (cr);
387 
388   cairo_fill (cr);
389 
390   unset_color (text_renderer);
391 }
392 
393 static void
gtk_text_renderer_draw_error_underline(PangoRenderer * renderer,int x,int y,int width,int height)394 gtk_text_renderer_draw_error_underline (PangoRenderer *renderer,
395 					int            x,
396 					int            y,
397 					int            width,
398 					int            height)
399 {
400   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
401 
402   set_color (text_renderer, PANGO_RENDER_PART_UNDERLINE);
403 
404   pango_cairo_show_error_underline (text_renderer->cr,
405                                     (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
406                                     (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
407 
408   unset_color (text_renderer);
409 }
410 
411 static void
gtk_text_renderer_draw_shape(PangoRenderer * renderer,PangoAttrShape * attr,int x,int y)412 gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
413 			      PangoAttrShape  *attr,
414 			      int              x,
415 			      int              y)
416 {
417   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
418 
419   if (attr->data == NULL)
420     {
421       /* This happens if we have an empty widget anchor. Draw
422        * something empty-looking.
423        */
424       GdkRectangle shape_rect;
425       cairo_t *cr;
426 
427       shape_rect.x = PANGO_PIXELS (x);
428       shape_rect.y = PANGO_PIXELS (y + attr->logical_rect.y);
429       shape_rect.width = PANGO_PIXELS (x + attr->logical_rect.width) - shape_rect.x;
430       shape_rect.height = PANGO_PIXELS (y + attr->logical_rect.y + attr->logical_rect.height) - shape_rect.y;
431 
432       set_color (text_renderer, PANGO_RENDER_PART_FOREGROUND);
433 
434       cr = text_renderer->cr;
435 
436       cairo_set_line_width (cr, 1.0);
437 
438       cairo_rectangle (cr,
439                        shape_rect.x + 0.5, shape_rect.y + 0.5,
440                        shape_rect.width - 1, shape_rect.height - 1);
441       cairo_move_to (cr, shape_rect.x + 0.5, shape_rect.y + 0.5);
442       cairo_line_to (cr,
443                      shape_rect.x + shape_rect.width - 0.5,
444                      shape_rect.y + shape_rect.height - 0.5);
445       cairo_move_to (cr, shape_rect.x + 0.5,
446                      shape_rect.y + shape_rect.height - 0.5);
447       cairo_line_to (cr, shape_rect.x + shape_rect.width - 0.5,
448                      shape_rect.y + 0.5);
449 
450       cairo_stroke (cr);
451 
452       unset_color (text_renderer);
453     }
454   else if (GDK_IS_PIXBUF (attr->data))
455     {
456       cairo_t *cr = text_renderer->cr;
457       GdkPixbuf *pixbuf = GDK_PIXBUF (attr->data);
458 
459       cairo_save (cr);
460 
461       gdk_cairo_set_source_pixbuf (cr, pixbuf,
462                                    PANGO_PIXELS (x),
463                                    PANGO_PIXELS (y) -  gdk_pixbuf_get_height (pixbuf));
464       cairo_paint (cr);
465 
466       cairo_restore (cr);
467     }
468   else if (GTK_IS_WIDGET (attr->data))
469     {
470       GtkWidget *widget;
471 
472       widget = GTK_WIDGET (attr->data);
473 
474       text_renderer->widgets = g_list_prepend (text_renderer->widgets,
475 					       g_object_ref (widget));
476     }
477   else
478     g_assert_not_reached (); /* not a pixbuf or widget */
479 }
480 
481 static void
gtk_text_renderer_finalize(GObject * object)482 gtk_text_renderer_finalize (GObject *object)
483 {
484   G_OBJECT_CLASS (_gtk_text_renderer_parent_class)->finalize (object);
485 }
486 
487 static void
_gtk_text_renderer_init(GtkTextRenderer * renderer)488 _gtk_text_renderer_init (GtkTextRenderer *renderer)
489 {
490 }
491 
492 static void
_gtk_text_renderer_class_init(GtkTextRendererClass * klass)493 _gtk_text_renderer_class_init (GtkTextRendererClass *klass)
494 {
495   GObjectClass *object_class = G_OBJECT_CLASS (klass);
496 
497   PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
498 
499   renderer_class->prepare_run = gtk_text_renderer_prepare_run;
500   renderer_class->draw_glyphs = gtk_text_renderer_draw_glyphs;
501   renderer_class->draw_glyph_item = gtk_text_renderer_draw_glyph_item;
502   renderer_class->draw_rectangle = gtk_text_renderer_draw_rectangle;
503   renderer_class->draw_trapezoid = gtk_text_renderer_draw_trapezoid;
504   renderer_class->draw_error_underline = gtk_text_renderer_draw_error_underline;
505   renderer_class->draw_shape = gtk_text_renderer_draw_shape;
506 
507   object_class->finalize = gtk_text_renderer_finalize;
508 }
509 
510 static void
text_renderer_set_state(GtkTextRenderer * text_renderer,int state)511 text_renderer_set_state (GtkTextRenderer *text_renderer,
512 			 int              state)
513 {
514   text_renderer->state = state;
515 }
516 
517 static void
text_renderer_begin(GtkTextRenderer * text_renderer,GtkWidget * widget,cairo_t * cr)518 text_renderer_begin (GtkTextRenderer *text_renderer,
519                      GtkWidget       *widget,
520                      cairo_t         *cr)
521 {
522   GtkStyleContext *context;
523   GtkStateFlags state;
524   GdkRGBA color;
525   GtkCssNode *text_node;
526 
527   text_renderer->widget = widget;
528   text_renderer->cr = cr;
529 
530   context = gtk_widget_get_style_context (widget);
531 
532   text_node = gtk_text_view_get_text_node ((GtkTextView *)widget);
533   gtk_style_context_save_to_node (context, text_node);
534 
535   state = gtk_style_context_get_state (context);
536   gtk_style_context_get_color (context, state, &color);
537 
538   cairo_save (cr);
539 
540   gdk_cairo_set_source_rgba (cr, &color);
541 }
542 
543 /* Returns a GSList of (referenced) widgets encountered while drawing.
544  */
545 static GList *
text_renderer_end(GtkTextRenderer * text_renderer)546 text_renderer_end (GtkTextRenderer *text_renderer)
547 {
548   GtkStyleContext *context;
549   GList *widgets = text_renderer->widgets;
550 
551   cairo_restore (text_renderer->cr);
552 
553   context = gtk_widget_get_style_context (text_renderer->widget);
554 
555   gtk_style_context_restore (context);
556 
557   text_renderer->widget = NULL;
558   text_renderer->cr = NULL;
559 
560   text_renderer->widgets = NULL;
561 
562   if (text_renderer->error_color)
563     {
564       gdk_rgba_free (text_renderer->error_color);
565       text_renderer->error_color = NULL;
566     }
567 
568   return widgets;
569 }
570 
571 static cairo_region_t *
get_selected_clip(GtkTextRenderer * text_renderer,PangoLayout * layout,PangoLayoutLine * line,int x,int y,int height,int start_index,int end_index)572 get_selected_clip (GtkTextRenderer    *text_renderer,
573                    PangoLayout        *layout,
574                    PangoLayoutLine    *line,
575                    int                 x,
576                    int                 y,
577                    int                 height,
578                    int                 start_index,
579                    int                 end_index)
580 {
581   gint *ranges;
582   gint n_ranges, i;
583   cairo_region_t *clip_region = cairo_region_create ();
584 
585   pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
586 
587   for (i=0; i < n_ranges; i++)
588     {
589       GdkRectangle rect;
590 
591       rect.x = x + PANGO_PIXELS (ranges[2*i]);
592       rect.y = y;
593       rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
594       rect.height = height;
595 
596       cairo_region_union_rectangle (clip_region, &rect);
597     }
598 
599   g_free (ranges);
600   return clip_region;
601 }
602 
603 static void
render_para(GtkTextRenderer * text_renderer,GtkTextLineDisplay * line_display,int selection_start_index,int selection_end_index)604 render_para (GtkTextRenderer    *text_renderer,
605              GtkTextLineDisplay *line_display,
606              int                 selection_start_index,
607              int                 selection_end_index)
608 {
609   GtkStyleContext *context;
610   PangoLayout *layout = line_display->layout;
611   int byte_offset = 0;
612   PangoLayoutIter *iter;
613   int screen_width;
614   GdkRGBA selection;
615   gboolean first = TRUE;
616   GtkCssNode *selection_node;
617 
618   iter = pango_layout_get_iter (layout);
619   screen_width = line_display->total_width;
620 
621   context = gtk_widget_get_style_context (text_renderer->widget);
622   selection_node = gtk_text_view_get_selection_node ((GtkTextView*)text_renderer->widget);
623   gtk_style_context_save_to_node (context, selection_node);
624 
625 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
626   gtk_style_context_get_background_color (context, gtk_style_context_get_state (context), &selection);
627 G_GNUC_END_IGNORE_DEPRECATIONS
628 
629  gtk_style_context_restore (context);
630 
631   do
632     {
633       PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
634       int selection_y, selection_height;
635       int first_y, last_y;
636       PangoRectangle line_rect;
637       int baseline;
638       gboolean at_last_line;
639 
640       pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
641       baseline = pango_layout_iter_get_baseline (iter);
642       pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
643 
644       /* Adjust for margins */
645 
646       line_rect.x += line_display->x_offset * PANGO_SCALE;
647       line_rect.y += line_display->top_margin * PANGO_SCALE;
648       baseline += line_display->top_margin * PANGO_SCALE;
649 
650       /* Selection is the height of the line, plus top/bottom
651        * margin if we're the first/last line
652        */
653       selection_y = PANGO_PIXELS (first_y) + line_display->top_margin;
654       selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
655 
656       if (first)
657         {
658           selection_y -= line_display->top_margin;
659           selection_height += line_display->top_margin;
660         }
661 
662       at_last_line = pango_layout_iter_at_last_line (iter);
663       if (at_last_line)
664         selection_height += line_display->bottom_margin;
665 
666       first = FALSE;
667 
668       if (selection_start_index < byte_offset &&
669           selection_end_index > line->length + byte_offset) /* All selected */
670         {
671           cairo_t *cr = text_renderer->cr;
672 
673           cairo_save (cr);
674           gdk_cairo_set_source_rgba (cr, &selection);
675           cairo_rectangle (cr,
676                            line_display->left_margin, selection_y,
677                            screen_width, selection_height);
678           cairo_fill (cr);
679           cairo_restore(cr);
680 
681 	  text_renderer_set_state (text_renderer, SELECTED);
682 	  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
683 					   line,
684 					   line_rect.x,
685 					   baseline);
686         }
687       else
688         {
689           if (line_display->pg_bg_rgba)
690             {
691               cairo_t *cr = text_renderer->cr;
692 
693               cairo_save (cr);
694 
695 	      gdk_cairo_set_source_rgba (text_renderer->cr, line_display->pg_bg_rgba);
696               cairo_rectangle (cr,
697                                line_display->left_margin, selection_y,
698                                screen_width, selection_height);
699               cairo_fill (cr);
700 
701               cairo_restore (cr);
702             }
703 
704 	  text_renderer_set_state (text_renderer, NORMAL);
705 	  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
706 					   line,
707 					   line_rect.x,
708 					   baseline);
709 
710 	  /* Check if some part of the line is selected; the newline
711 	   * that is after line->length for the last line of the
712 	   * paragraph counts as part of the line for this
713 	   */
714           if ((selection_start_index < byte_offset + line->length ||
715 	       (selection_start_index == byte_offset + line->length && pango_layout_iter_at_last_line (iter))) &&
716 	      selection_end_index > byte_offset)
717             {
718               cairo_t *cr = text_renderer->cr;
719               cairo_region_t *clip_region = get_selected_clip (text_renderer, layout, line,
720                                                           line_display->x_offset,
721                                                           selection_y,
722                                                           selection_height,
723                                                           selection_start_index, selection_end_index);
724 
725               cairo_save (cr);
726               gdk_cairo_region (cr, clip_region);
727               cairo_clip (cr);
728               cairo_region_destroy (clip_region);
729 
730               gdk_cairo_set_source_rgba (cr, &selection);
731               cairo_rectangle (cr,
732                                PANGO_PIXELS (line_rect.x),
733                                selection_y,
734                                PANGO_PIXELS (line_rect.width),
735                                selection_height);
736               cairo_fill (cr);
737 
738 	      text_renderer_set_state (text_renderer, SELECTED);
739 	      pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
740 					       line,
741 					       line_rect.x,
742 					       baseline);
743 
744               cairo_restore (cr);
745 
746               /* Paint in the ends of the line */
747               if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
748                   ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
749                    (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
750                 {
751                   cairo_save (cr);
752 
753                   gdk_cairo_set_source_rgba (cr, &selection);
754                   cairo_rectangle (cr,
755                                    line_display->left_margin,
756                                    selection_y,
757                                    PANGO_PIXELS (line_rect.x) - line_display->left_margin,
758                                    selection_height);
759                   cairo_fill (cr);
760 
761                   cairo_restore (cr);
762                 }
763 
764               if (line_rect.x + line_rect.width <
765                   (screen_width + line_display->left_margin) * PANGO_SCALE &&
766                   ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) ||
767                    (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
768                 {
769                   int nonlayout_width;
770 
771                   nonlayout_width =
772                     line_display->left_margin + screen_width -
773                     PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
774 
775                   cairo_save (cr);
776 
777                   gdk_cairo_set_source_rgba (cr, &selection);
778                   cairo_rectangle (cr,
779                                    PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
780                                    selection_y,
781                                    nonlayout_width,
782                                    selection_height);
783                   cairo_fill (cr);
784 
785                   cairo_restore (cr);
786                 }
787             }
788 	  else if (line_display->has_block_cursor &&
789 		   gtk_widget_has_focus (text_renderer->widget) &&
790 		   byte_offset <= line_display->insert_index &&
791 		   (line_display->insert_index < byte_offset + line->length ||
792 		    (at_last_line && line_display->insert_index == byte_offset + line->length)))
793 	    {
794 	      GdkRectangle cursor_rect;
795               GdkRGBA cursor_color;
796               cairo_t *cr = text_renderer->cr;
797 
798               /* we draw text using base color on filled cursor rectangle of cursor color
799                * (normally white on black) */
800               _gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
801 
802 	      cursor_rect.x = line_display->x_offset + line_display->block_cursor.x;
803 	      cursor_rect.y = line_display->block_cursor.y + line_display->top_margin;
804 	      cursor_rect.width = line_display->block_cursor.width;
805 	      cursor_rect.height = line_display->block_cursor.height;
806 
807               cairo_save (cr);
808 
809               gdk_cairo_rectangle (cr, &cursor_rect);
810               cairo_clip (cr);
811 
812               gdk_cairo_set_source_rgba (cr, &cursor_color);
813               cairo_paint (cr);
814 
815               /* draw text under the cursor if any */
816               if (!line_display->cursor_at_line_end)
817                 {
818                   GdkRGBA color;
819 
820 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
821                   gtk_style_context_get_background_color (context, gtk_style_context_get_state (context), &color);
822 G_GNUC_END_IGNORE_DEPRECATIONS
823 
824                   gdk_cairo_set_source_rgba (cr, &color);
825 
826 		  text_renderer_set_state (text_renderer, CURSOR);
827 
828 		  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
829 						   line,
830 						   line_rect.x,
831 						   baseline);
832                 }
833 
834               cairo_restore (cr);
835 	    }
836         }
837 
838       byte_offset += line->length;
839     }
840   while (pango_layout_iter_next_line (iter));
841 
842   pango_layout_iter_free (iter);
843 }
844 
845 static GtkTextRenderer *
get_text_renderer(void)846 get_text_renderer (void)
847 {
848   static GtkTextRenderer *text_renderer = NULL;
849 
850   if (!text_renderer)
851     text_renderer = g_object_new (GTK_TYPE_TEXT_RENDERER, NULL);
852 
853   return text_renderer;
854 }
855 
856 void
gtk_text_layout_draw(GtkTextLayout * layout,GtkWidget * widget,cairo_t * cr,GList ** widgets)857 gtk_text_layout_draw (GtkTextLayout *layout,
858                       GtkWidget *widget,
859                       cairo_t *cr,
860                       GList **widgets)
861 {
862   GtkStyleContext *context;
863   gint offset_y;
864   GtkTextRenderer *text_renderer;
865   GtkTextIter selection_start, selection_end;
866   gboolean have_selection;
867   GSList *line_list;
868   GSList *tmp_list;
869   GList *tmp_widgets;
870   GdkRectangle clip;
871 
872   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
873   g_return_if_fail (layout->default_style != NULL);
874   g_return_if_fail (layout->buffer != NULL);
875   g_return_if_fail (cr != NULL);
876 
877   if (!gdk_cairo_get_clip_rectangle (cr, &clip))
878     return;
879 
880   context = gtk_widget_get_style_context (widget);
881 
882   line_list = gtk_text_layout_get_lines (layout, clip.y, clip.y + clip.height, &offset_y);
883 
884   if (line_list == NULL)
885     return; /* nothing on the screen */
886 
887   text_renderer = get_text_renderer ();
888   text_renderer_begin (text_renderer, widget, cr);
889 
890   /* text_renderer_begin/end does cairo_save/restore */
891   cairo_translate (cr, 0, offset_y);
892 
893   gtk_text_layout_wrap_loop_start (layout);
894 
895   have_selection = gtk_text_buffer_get_selection_bounds (layout->buffer,
896                                                          &selection_start,
897                                                          &selection_end);
898 
899   tmp_list = line_list;
900   while (tmp_list != NULL)
901     {
902       GtkTextLineDisplay *line_display;
903       gint selection_start_index = -1;
904       gint selection_end_index = -1;
905 
906       GtkTextLine *line = tmp_list->data;
907 
908       line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
909 
910       if (line_display->height > 0)
911         {
912           g_assert (line_display->layout != NULL);
913 
914           if (have_selection)
915             {
916               GtkTextIter line_start, line_end;
917               gint byte_count;
918 
919               gtk_text_layout_get_iter_at_line (layout,
920                                                 &line_start,
921                                                 line, 0);
922               line_end = line_start;
923 	      if (!gtk_text_iter_ends_line (&line_end))
924 		gtk_text_iter_forward_to_line_end (&line_end);
925               byte_count = gtk_text_iter_get_visible_line_index (&line_end);
926 
927               if (gtk_text_iter_compare (&selection_start, &line_end) <= 0 &&
928                   gtk_text_iter_compare (&selection_end, &line_start) >= 0)
929                 {
930                   if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
931                     selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
932                   else
933                     selection_start_index = -1;
934 
935                   if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
936                     selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
937                   else
938                     selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
939                 }
940             }
941 
942           render_para (text_renderer, line_display,
943                        selection_start_index, selection_end_index);
944 
945           /* We paint the cursors last, because they overlap another chunk
946            * and need to appear on top.
947            */
948           if (line_display->cursors != NULL)
949             {
950               int i;
951 
952               for (i = 0; i < line_display->cursors->len; i++)
953                 {
954                   int index;
955                   PangoDirection dir;
956 
957                   index = g_array_index(line_display->cursors, int, i);
958                   dir = (line_display->direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
959                   gtk_render_insertion_cursor (context, cr,
960                                                line_display->x_offset, line_display->top_margin,
961                                                line_display->layout, index, dir);
962                 }
963             }
964         } /* line_display->height > 0 */
965 
966       cairo_translate (cr, 0, line_display->height);
967       gtk_text_layout_free_line_display (layout, line_display);
968 
969       tmp_list = tmp_list->next;
970     }
971 
972   gtk_text_layout_wrap_loop_end (layout);
973 
974   tmp_widgets = text_renderer_end (text_renderer);
975   if (widgets)
976     *widgets = tmp_widgets;
977   else
978     g_list_free_full (tmp_widgets, g_object_unref);
979 
980   g_slist_free (line_list);
981 }
982