gtktextdisplay.c 31.7 KB
Newer Older
1 2 3 4 5 6 7
/* gtktextdisplay.c - display layed-out text
 *
 * Copyright (c) 1992-1994 The Regents of the University of California.
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
 * Copyright (c) 2000 Red Hat, Inc.
 * Tk->Gtk port by Havoc Pennington
 *
8 9
 * This file can be used under your choice of two licenses, the LGPL
 * and the original Tk license.
10
 *
11
 * LGPL:
12
 *
13 14 15 16 17 18 19 20 21 22 23
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
24
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
25 26
 *
 * Original Tk license:
27 28 29 30 31
 *
 * This software is copyrighted by the Regents of the University of
 * California, Sun Microsystems, Inc., and other parties.  The
 * following terms apply to all files associated with the software
 * unless explicitly disclaimed in individual files.
32
 *
33 34 35 36 37 38 39 40 41
 * The authors hereby grant permission to use, copy, modify,
 * distribute, and license this software and its documentation for any
 * purpose, provided that existing copyright notices are retained in
 * all copies and that this notice is included verbatim in any
 * distributions. No written agreement, license, or royalty fee is
 * required for any of the authorized uses.  Modifications to this
 * software may be copyrighted by their authors and need not follow
 * the licensing terms described here, provided that the new terms are
 * clearly indicated on the first page of each file where they apply.
42
 *
43 44 45 46 47
 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
48
 *
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
 * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * GOVERNMENT USE: If you are acquiring this software on behalf of the
 * U.S. government, the Government shall have only "Restricted Rights"
 * in the software and related documentation as defined in the Federal
 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
 * are acquiring the software on behalf of the Department of Defense,
 * the software shall be classified as "Commercial Computer Software"
 * and the Government shall have only "Restricted Rights" as defined
 * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
 * foregoing, the authors grant the U.S. Government and others acting
 * in its behalf permission to use and distribute the software in
 * accordance with the terms specified in this license.
67 68 69 70 71 72 73
 *
 */
/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * 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 85 86
/* DO NOT go putting private headers in here. This file should only
 * use the semi-public headers, as with gtktextview.c.
 */
87

Matthias Clasen's avatar
Matthias Clasen committed
88
#define GTK_TYPE_TEXT_RENDERER            (_gtk_text_renderer_get_type())
89 90 91 92 93
#define GTK_TEXT_RENDERER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_TEXT_RENDERER, GtkTextRenderer))
#define GTK_IS_TEXT_RENDERER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_TEXT_RENDERER))
#define GTK_TEXT_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
#define GTK_IS_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_RENDERER))
#define GTK_TEXT_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
94

95 96
typedef struct _GtkTextRenderer      GtkTextRenderer;
typedef struct _GtkTextRendererClass GtkTextRendererClass;
97

98 99 100 101 102 103
enum {
  NORMAL,
  SELECTED,
  CURSOR
};

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 123
GType _gtk_text_renderer_get_type (void);

124
G_DEFINE_TYPE (GtkTextRenderer, _gtk_text_renderer, PANGO_TYPE_RENDERER)
125

126 127 128
static void
text_renderer_set_rgba (GtkTextRenderer *text_renderer,
			PangoRenderPart  part,
129
			const GdkRGBA   *rgba)
130 131
{
  PangoRenderer *renderer = PANGO_RENDERER (text_renderer);
132 133
  PangoColor color = { 0, };
  guint16 alpha;
134

135 136
  if (rgba)
    {
137 138 139 140 141 142
      color.red = (guint16)(rgba->red * 65535);
      color.green = (guint16)(rgba->green * 65535);
      color.blue = (guint16)(rgba->blue * 65535);
      alpha = (guint16)(rgba->alpha * 65535);
      pango_renderer_set_color (renderer, part, &color);
      pango_renderer_set_alpha (renderer, part, alpha);
143 144
    }
  else
145 146 147 148
    {
      pango_renderer_set_color (renderer, part, NULL);
      pango_renderer_set_alpha (renderer, part, 0);
    }
149 150
}

151 152
static GtkTextAppearance *
get_item_appearance (PangoItem *item)
153
{
154 155 156 157 158 159 160 161 162 163 164 165 166
  GSList *tmp_list = item->analysis.extra_attrs;

  while (tmp_list)
    {
      PangoAttribute *attr = tmp_list->data;

      if (attr->klass->type == gtk_text_attr_appearance_type)
	return &((GtkTextAttrAppearance *)attr)->appearance;

      tmp_list = tmp_list->next;
    }

  return NULL;
167 168 169
}

static void
170 171
gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
			       PangoLayoutRun *run)
172
{
173 174
  GtkStyleContext *context;
  GtkStateFlags state;
175
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
176
  GdkRGBA *bg_rgba = NULL;
177
  GdkRGBA *fg_rgba = NULL;
178 179
  GtkTextAppearance *appearance;

Matthias Clasen's avatar
Matthias Clasen committed
180
  PANGO_RENDERER_CLASS (_gtk_text_renderer_parent_class)->prepare_run (renderer, run);
181 182 183

  appearance = get_item_appearance (run->item);
  g_assert (appearance != NULL);
184

185
  context = gtk_widget_get_style_context (text_renderer->widget);
186
  state   = gtk_widget_get_state_flags (text_renderer->widget);
187

188
  if (appearance->draw_bg && text_renderer->state == NORMAL)
189
    bg_rgba = appearance->rgba[0];
190
  else
191
    bg_rgba = NULL;
192
  
193
  text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_rgba);
194

195
  if (text_renderer->state == SELECTED)
196
    {
197 198
      state |= GTK_STATE_FLAG_SELECTED;

199
      gtk_style_context_get (context, state, "color", &fg_rgba, NULL);
200
    }
201
  else if (text_renderer->state == CURSOR && gtk_widget_has_focus (text_renderer->widget))
202 203 204 205 206
    {
      gtk_style_context_get (context, state,
                             "background-color", &fg_rgba,
                              NULL);
    }
207
  else
208
    fg_rgba = appearance->rgba[1];
209

210
  text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_FOREGROUND, fg_rgba);
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
  if (GTK_TEXT_APPEARANCE_GET_STRIKETHROUGH_RGBA_SET (appearance))
    {
      GdkRGBA rgba;

      GTK_TEXT_APPEARANCE_GET_STRIKETHROUGH_RGBA (appearance, &rgba);
      text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, &rgba);
    }
  else
    text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_rgba);

  if (GTK_TEXT_APPEARANCE_GET_UNDERLINE_RGBA_SET (appearance))
    {
      GdkRGBA rgba;

      GTK_TEXT_APPEARANCE_GET_UNDERLINE_RGBA (appearance, &rgba);
      text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_UNDERLINE, &rgba);
    }
  else if (appearance->underline == PANGO_UNDERLINE_ERROR)
230 231 232
    {
      if (!text_renderer->error_color)
        {
233 234
	  GdkColor *color = NULL;

235
          gtk_style_context_get_style (context,
236
                                       "error-underline-color", &color,
237 238
                                       NULL);

239 240 241 242 243 244 245 246
	  if (color)
	    {
	      GdkRGBA   rgba;

	      rgba.red = color->red / 65535.;
	      rgba.green = color->green / 65535.;
	      rgba.blue = color->blue / 65535.;
	      rgba.alpha = 1;
Matthias Clasen's avatar
Matthias Clasen committed
247
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
248
	      gdk_color_free (color);
Matthias Clasen's avatar
Matthias Clasen committed
249
G_GNUC_END_IGNORE_DEPRECATIONS
250 251 252 253 254 255 256 257

	      text_renderer->error_color = gdk_rgba_copy (&rgba);
	    }
	  else
	    {
	      static const GdkRGBA red = { 1, 0, 0, 1 };
	      text_renderer->error_color = gdk_rgba_copy (&red);
	    }
258
        }
259

260
      text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_UNDERLINE, text_renderer->error_color);
261 262
    }
  else
263 264 265 266
    text_renderer_set_rgba (text_renderer, PANGO_RENDER_PART_UNDERLINE, fg_rgba);

  if (fg_rgba != appearance->rgba[1])
    gdk_rgba_free (fg_rgba);
267 268
}

269 270 271 272
static void
set_color (GtkTextRenderer *text_renderer,
           PangoRenderPart  part)
{
273 274 275 276
  PangoColor *color;
  GdkRGBA rgba;
  guint16 alpha;

277 278
  cairo_save (text_renderer->cr);

279 280 281 282 283 284 285 286 287 288
  color = pango_renderer_get_color (PANGO_RENDERER (text_renderer), part);
  alpha = pango_renderer_get_alpha (PANGO_RENDERER (text_renderer), part);
  if (color)
    {
      rgba.red = color->red / 65535.;
      rgba.green = color->green / 65535.;
      rgba.blue = color->blue / 65535.;
      rgba.alpha = alpha / 65535.;
      gdk_cairo_set_source_rgba (text_renderer->cr, &rgba);
    }
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
}

static void
unset_color (GtkTextRenderer *text_renderer)
{
  cairo_restore (text_renderer->cr);
}

static void
gtk_text_renderer_draw_glyphs (PangoRenderer     *renderer,
                               PangoFont         *font,
                               PangoGlyphString  *glyphs,
                               int                x,
                               int                y)
{
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);

  set_color (text_renderer, PANGO_RENDER_PART_FOREGROUND);

  cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
  pango_cairo_show_glyph_string (text_renderer->cr, font, glyphs);

  unset_color (text_renderer);
}

static void
gtk_text_renderer_draw_glyph_item (PangoRenderer     *renderer,
                                   const char        *text,
                                   PangoGlyphItem    *glyph_item,
                                   int                x,
                                   int                y)
{
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);

  set_color (text_renderer, PANGO_RENDER_PART_FOREGROUND);

  cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
  pango_cairo_show_glyph_item (text_renderer->cr, text, glyph_item);

  unset_color (text_renderer);
}

static void
gtk_text_renderer_draw_rectangle (PangoRenderer     *renderer,
				  PangoRenderPart    part,
				  int                x,
				  int                y,
				  int                width,
				  int                height)
{
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);

  set_color (text_renderer, part);

  cairo_rectangle (text_renderer->cr,
                   (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
		   (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
  cairo_fill (text_renderer->cr);

  unset_color (text_renderer);
}

static void
gtk_text_renderer_draw_trapezoid (PangoRenderer     *renderer,
				  PangoRenderPart    part,
				  double             y1_,
				  double             x11,
				  double             x21,
				  double             y2,
				  double             x12,
				  double             x22)
{
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
  cairo_t *cr;
  cairo_matrix_t matrix;

  set_color (text_renderer, part);

  cr = text_renderer->cr;

  cairo_get_matrix (cr, &matrix);
  matrix.xx = matrix.yy = 1.0;
  matrix.xy = matrix.yx = 0.0;
  cairo_set_matrix (cr, &matrix);

  cairo_move_to (cr, x11, y1_);
  cairo_line_to (cr, x21, y1_);
  cairo_line_to (cr, x22, y2);
  cairo_line_to (cr, x12, y2);
  cairo_close_path (cr);

  cairo_fill (cr);

  unset_color (text_renderer);
}

static void
gtk_text_renderer_draw_error_underline (PangoRenderer *renderer,
					int            x,
					int            y,
					int            width,
					int            height)
{
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);

  set_color (text_renderer, PANGO_RENDER_PART_UNDERLINE);

  pango_cairo_show_error_underline (text_renderer->cr,
                                    (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
                                    (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);

  unset_color (text_renderer);
}

403 404 405 406 407
static void
gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
			      PangoAttrShape  *attr,
			      int              x,
			      int              y)
408
{
409
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
410

411
  if (attr->data == NULL)
412
    {
413 414 415
      /* This happens if we have an empty widget anchor. Draw
       * something empty-looking.
       */
416 417
      GdkRectangle shape_rect;
      cairo_t *cr;
418

419 420 421 422
      shape_rect.x = PANGO_PIXELS (x);
      shape_rect.y = PANGO_PIXELS (y + attr->logical_rect.y);
      shape_rect.width = PANGO_PIXELS (x + attr->logical_rect.width) - shape_rect.x;
      shape_rect.height = PANGO_PIXELS (y + attr->logical_rect.y + attr->logical_rect.height) - shape_rect.y;
423

424 425 426
      set_color (text_renderer, PANGO_RENDER_PART_FOREGROUND);

      cr = text_renderer->cr;
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442

      cairo_set_line_width (cr, 1.0);

      cairo_rectangle (cr,
                       shape_rect.x + 0.5, shape_rect.y + 0.5,
                       shape_rect.width - 1, shape_rect.height - 1);
      cairo_move_to (cr, shape_rect.x + 0.5, shape_rect.y + 0.5);
      cairo_line_to (cr, 
                     shape_rect.x + shape_rect.width - 0.5,
                     shape_rect.y + shape_rect.height - 0.5);
      cairo_move_to (cr, shape_rect.x + 0.5,
                     shape_rect.y + shape_rect.height - 0.5);
      cairo_line_to (cr, shape_rect.x + shape_rect.width - 0.5,
                     shape_rect.y + 0.5);

      cairo_stroke (cr);
443 444

      unset_color (text_renderer);
445 446 447
    }
  else if (GDK_IS_PIXBUF (attr->data))
    {
448
      cairo_t *cr = text_renderer->cr;
449
      GdkPixbuf *pixbuf = GDK_PIXBUF (attr->data);
450
      
451
      cairo_save (cr);
452

453 454 455 456
      gdk_cairo_set_source_pixbuf (cr, pixbuf,
                                   PANGO_PIXELS (x),
                                   PANGO_PIXELS (y) -  gdk_pixbuf_get_height (pixbuf));
      cairo_paint (cr);
457

458
      cairo_restore (cr);
459
    }
460 461
  else if (GTK_IS_WIDGET (attr->data))
    {
462 463 464 465 466 467
      GtkWidget *widget;
      
      widget = GTK_WIDGET (attr->data);

      text_renderer->widgets = g_list_prepend (text_renderer->widgets,
					       g_object_ref (widget));
468 469 470
    }
  else
    g_assert_not_reached (); /* not a pixbuf or widget */
471 472
}

473
static void
474
gtk_text_renderer_finalize (GObject *object)
475
{
Matthias Clasen's avatar
Matthias Clasen committed
476
  G_OBJECT_CLASS (_gtk_text_renderer_parent_class)->finalize (object);
477
}
478

479
static void
Matthias Clasen's avatar
Matthias Clasen committed
480
_gtk_text_renderer_init (GtkTextRenderer *renderer)
481
{
482 483
}

484
static void
Matthias Clasen's avatar
Matthias Clasen committed
485
_gtk_text_renderer_class_init (GtkTextRendererClass *klass)
486
{
487
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
488
  
489 490 491
  PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
  
  renderer_class->prepare_run = gtk_text_renderer_prepare_run;
492 493 494 495 496
  renderer_class->draw_glyphs = gtk_text_renderer_draw_glyphs;
  renderer_class->draw_glyph_item = gtk_text_renderer_draw_glyph_item;
  renderer_class->draw_rectangle = gtk_text_renderer_draw_rectangle;
  renderer_class->draw_trapezoid = gtk_text_renderer_draw_trapezoid;
  renderer_class->draw_error_underline = gtk_text_renderer_draw_error_underline;
497
  renderer_class->draw_shape = gtk_text_renderer_draw_shape;
498

499 500
  object_class->finalize = gtk_text_renderer_finalize;
}
501

502
static void
503 504
text_renderer_set_state (GtkTextRenderer *text_renderer,
			 int              state)
505
{
506
  text_renderer->state = state;
507
}
508

509 510
static void
text_renderer_begin (GtkTextRenderer *text_renderer,
511 512
                     GtkWidget       *widget,
                     cairo_t         *cr)
513
{
514 515 516 517
  GtkStyleContext *context;
  GtkStateFlags state;
  GdkRGBA color;

518
  text_renderer->widget = widget;
519
  text_renderer->cr = cr;
520 521 522 523 524 525 526 527 528 529 530 531

  context = gtk_widget_get_style_context (widget);

  gtk_style_context_save (context);
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);

  state = gtk_widget_get_state_flags (widget);
  gtk_style_context_get_color (context, state, &color);

  cairo_save (cr);

  gdk_cairo_set_source_rgba (cr, &color);
532
}
533

534 535
/* Returns a GSList of (referenced) widgets encountered while drawing.
 */
536
static GList *
537 538
text_renderer_end (GtkTextRenderer *text_renderer)
{
539
  GtkStyleContext *context;
540
  GList *widgets = text_renderer->widgets;
541

542 543 544 545 546 547
  cairo_restore (text_renderer->cr);

  context = gtk_widget_get_style_context (text_renderer->widget);

  gtk_style_context_restore (context);

548
  text_renderer->widget = NULL;
549
  text_renderer->cr = NULL;
550

551 552
  text_renderer->widgets = NULL;

553 554
  if (text_renderer->error_color)
    {
555
      gdk_rgba_free (text_renderer->error_color);
556 557
      text_renderer->error_color = NULL;
    }
558 559

  return widgets;
560
}
561

562
static cairo_region_t *
563 564 565 566 567 568 569 570 571 572 573
get_selected_clip (GtkTextRenderer    *text_renderer,
                   PangoLayout        *layout,
                   PangoLayoutLine    *line,
                   int                 x,
                   int                 y,
                   int                 height,
                   int                 start_index,
                   int                 end_index)
{
  gint *ranges;
  gint n_ranges, i;
574
  cairo_region_t *clip_region = cairo_region_create ();
575

576
  pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
577

578 579 580
  for (i=0; i < n_ranges; i++)
    {
      GdkRectangle rect;
581

582 583 584 585 586
      rect.x = x + PANGO_PIXELS (ranges[2*i]);
      rect.y = y;
      rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
      rect.height = height;
      
587
      cairo_region_union_rectangle (clip_region, &rect);
588
    }
589 590 591

  g_free (ranges);
  return clip_region;
592 593
}

594
static void
595
render_para (GtkTextRenderer    *text_renderer,
596 597
             GtkTextLineDisplay *line_display,
             int                 selection_start_index,
598
             int                 selection_end_index)
599
{
600 601
  GtkStyleContext *context;
  GtkStateFlags state;
602
  PangoLayout *layout = line_display->layout;
603
  int byte_offset = 0;
604 605 606
  PangoLayoutIter *iter;
  PangoRectangle layout_logical;
  int screen_width;
607
  GdkRGBA selection;
608
  gboolean first = TRUE;
609

610
  iter = pango_layout_get_iter (layout);
611

612
  pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical);
613

614 615 616 617 618 619
  /* Adjust for margins */
  
  layout_logical.x += line_display->x_offset * PANGO_SCALE;
  layout_logical.y += line_display->top_margin * PANGO_SCALE;

  screen_width = line_display->total_width;
620 621

  context = gtk_widget_get_style_context (text_renderer->widget);
622
  gtk_style_context_save (context);
623

624
  state = gtk_style_context_get_state (context);
625
  state |= GTK_STATE_FLAG_SELECTED;
626
  gtk_style_context_set_state (context, state);
627

628
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
629
  gtk_style_context_get_background_color (context, state, &selection);
630
G_GNUC_END_IGNORE_DEPRECATIONS
631

632 633
 gtk_style_context_restore (context);

634 635
  do
    {
636
      PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
637
      int selection_y, selection_height;
638 639 640
      int first_y, last_y;
      PangoRectangle line_rect;
      int baseline;
641
      gboolean at_last_line;
642 643 644 645 646 647 648 649 650 651 652 653 654 655
      
      pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
      baseline = pango_layout_iter_get_baseline (iter);
      pango_layout_iter_get_line_yrange (iter, &first_y, &last_y);
      
      /* Adjust for margins */

      line_rect.x += line_display->x_offset * PANGO_SCALE;
      line_rect.y += line_display->top_margin * PANGO_SCALE;
      baseline += line_display->top_margin * PANGO_SCALE;

      /* Selection is the height of the line, plus top/bottom
       * margin if we're the first/last line
       */
656
      selection_y = PANGO_PIXELS (first_y) + line_display->top_margin;
657
      selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
658 659

      if (first)
660 661 662 663
        {
          selection_y -= line_display->top_margin;
          selection_height += line_display->top_margin;
        }
664 665 666

      at_last_line = pango_layout_iter_at_last_line (iter);
      if (at_last_line)
667
        selection_height += line_display->bottom_margin;
668
      
669
      first = FALSE;
670

671
      if (selection_start_index < byte_offset &&
672 673
          selection_end_index > line->length + byte_offset) /* All selected */
        {
674 675 676
          cairo_t *cr = text_renderer->cr;

          cairo_save (cr);
677
          gdk_cairo_set_source_rgba (cr, &selection);
678
          cairo_rectangle (cr, 
679
                           line_display->left_margin, selection_y,
680 681
                           screen_width, selection_height);
          cairo_fill (cr);
682
          cairo_restore(cr);
683

684
	  text_renderer_set_state (text_renderer, SELECTED);
685 686
	  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
					   line, 
687 688
					   line_rect.x,
					   baseline);
689
        }
690
      else
691
        {
692
          if (line_display->pg_bg_rgba)
693
            {
694 695 696
              cairo_t *cr = text_renderer->cr;

              cairo_save (cr);
697 698
 
	      gdk_cairo_set_source_rgba (text_renderer->cr, line_display->pg_bg_rgba);
699
              cairo_rectangle (cr, 
700
                               line_display->left_margin, selection_y,
701 702 703
                               screen_width, selection_height);
              cairo_fill (cr);

704
              cairo_restore (cr);
705 706
            }
        
707
	  text_renderer_set_state (text_renderer, NORMAL);
708 709
	  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
					   line, 
710 711
					   line_rect.x,
					   baseline);
712

713 714 715 716 717 718 719
	  /* Check if some part of the line is selected; the newline
	   * that is after line->length for the last line of the
	   * paragraph counts as part of the line for this
	   */
          if ((selection_start_index < byte_offset + line->length ||
	       (selection_start_index == byte_offset + line->length && pango_layout_iter_at_last_line (iter))) &&
	      selection_end_index > byte_offset)
720
            {
721
              cairo_t *cr = text_renderer->cr;
722
              cairo_region_t *clip_region = get_selected_clip (text_renderer, layout, line,
723
                                                          line_display->x_offset,
724 725
                                                          selection_y,
                                                          selection_height,
726
                                                          selection_start_index, selection_end_index);
727

728
              cairo_save (cr);
729 730
              gdk_cairo_region (cr, clip_region);
              cairo_clip (cr);
731
              cairo_region_destroy (clip_region);
732

733
              gdk_cairo_set_source_rgba (cr, &selection);
734
              cairo_rectangle (cr,
735
                               PANGO_PIXELS (line_rect.x),
736 737 738 739 740
                               selection_y,
                               PANGO_PIXELS (line_rect.width),
                               selection_height);
              cairo_fill (cr);

741
	      text_renderer_set_state (text_renderer, SELECTED);
742 743
	      pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
					       line, 
744 745
					       line_rect.x,
					       baseline);
746

747
              cairo_restore (cr);
748 749

              /* Paint in the ends of the line */
750
              if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
751 752 753
                  ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
                   (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
                {
754
                  cairo_save (cr);
755

756
                  gdk_cairo_set_source_rgba (cr, &selection);
757
                  cairo_rectangle (cr,
758
                                   line_display->left_margin,
759 760 761 762
                                   selection_y,
                                   PANGO_PIXELS (line_rect.x) - line_display->left_margin,
                                   selection_height);
                  cairo_fill (cr);
763 764

                  cairo_restore (cr);
765 766
                }

767 768
              if (line_rect.x + line_rect.width <
                  (screen_width + line_display->left_margin) * PANGO_SCALE &&
769 770 771
                  ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) ||
                   (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
                {
772
                  int nonlayout_width;
773

774 775 776
                  nonlayout_width =
                    line_display->left_margin + screen_width -
                    PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
777

778 779
                  cairo_save (cr);

780
                  gdk_cairo_set_source_rgba (cr, &selection);
781
                  cairo_rectangle (cr,
782
                                   PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
783 784 785 786
                                   selection_y,
                                   nonlayout_width,
                                   selection_height);
                  cairo_fill (cr);
787 788

                  cairo_restore (cr);
789 790
                }
            }
791
	  else if (line_display->has_block_cursor &&
792
		   gtk_widget_has_focus (text_renderer->widget) &&
793 794 795 796 797
		   byte_offset <= line_display->insert_index &&
		   (line_display->insert_index < byte_offset + line->length ||
		    (at_last_line && line_display->insert_index == byte_offset + line->length)))
	    {
	      GdkRectangle cursor_rect;
798
              GdkRGBA cursor_color;
799
              cairo_t *cr = text_renderer->cr;
800

801 802 803
              /* we draw text using base color on filled cursor rectangle of cursor color
               * (normally white on black) */
              _gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
804

805 806
	      cursor_rect.x = line_display->x_offset + line_display->block_cursor.x;
	      cursor_rect.y = line_display->block_cursor.y + line_display->top_margin;
807
	      cursor_rect.width = line_display->block_cursor.width;
808
	      cursor_rect.height = line_display->block_cursor.height;
809

810 811
              cairo_save (cr);

812 813
              gdk_cairo_rectangle (cr, &cursor_rect);
              cairo_clip (cr);
814

815
              gdk_cairo_set_source_rgba (cr, &cursor_color);
816
              cairo_paint (cr);
817

818 819 820 821 822 823
              /* draw text under the cursor if any */
              if (!line_display->cursor_at_line_end)
                {
                  GdkRGBA color;

                  state = gtk_widget_get_state_flags (text_renderer->widget);
824
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
825
                  gtk_style_context_get_background_color (context, state, &color);
826
G_GNUC_END_IGNORE_DEPRECATIONS
827

828
                  gdk_cairo_set_source_rgba (cr, &color);
829 830 831 832 833

		  text_renderer_set_state (text_renderer, CURSOR);

		  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
						   line,
834 835
						   line_rect.x,
						   baseline);
836
                }
837 838

              cairo_restore (cr);
839
	    }
840
        }
841 842 843

      byte_offset += line->length;
    }
844 845 846
  while (pango_layout_iter_next_line (iter));

  pango_layout_iter_free (iter);
847 848
}

849
static GtkTextRenderer *
850
get_text_renderer (void)
851
{
852
  static GtkTextRenderer *text_renderer = NULL;
853

854
  if (!text_renderer)
855
    text_renderer = g_object_new (GTK_TYPE_TEXT_RENDERER, NULL);
856 857

  return text_renderer;
858 859 860 861
}

void
gtk_text_layout_draw (GtkTextLayout *layout,
862
                      GtkWidget *widget,
863 864
                      cairo_t *cr,
                      GList **widgets)
865
{
866
  GtkStyleContext *context;
867
  gint offset_y;
868
  GtkTextRenderer *text_renderer;
869
  GtkTextIter selection_start, selection_end;
Paolo Borelli's avatar
Paolo Borelli committed
870
  gboolean have_selection;
871 872
  GSList *line_list;
  GSList *tmp_list;
873
  GList *tmp_widgets;
José Aliste's avatar
José Aliste committed
874
  GdkRectangle clip;
875

876 877 878
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (layout->default_style != NULL);
  g_return_if_fail (layout->buffer != NULL);
879
  g_return_if_fail (cr != NULL);
880

José Aliste's avatar
José Aliste committed
881 882
  if (!gdk_cairo_get_clip_rectangle (cr, &clip))
    return;
883

884 885
  context = gtk_widget_get_style_context (widget);

886
  line_list = gtk_text_layout_get_lines (layout, clip.y, clip.y + clip.height, &offset_y);
887 888 889 890

  if (line_list == NULL)
    return; /* nothing on the screen */

891 892
  text_renderer = get_text_renderer ();
  text_renderer_begin (text_renderer, widget, cr);
893

894 895 896
  /* text_renderer_begin/end does cairo_save/restore */
  cairo_translate (cr, 0, offset_y);

897
  gtk_text_layout_wrap_loop_start (layout);
898

Paolo Borelli's avatar
Paolo Borelli committed
899 900 901
  have_selection = gtk_text_buffer_get_selection_bounds (layout->buffer,
                                                         &selection_start,
                                                         &selection_end);
902

903 904 905 906 907 908 909 910
  tmp_list = line_list;
  while (tmp_list != NULL)
    {
      GtkTextLineDisplay *line_display;
      gint selection_start_index = -1;
      gint selection_end_index = -1;

      GtkTextLine *line = tmp_list->data;
911

912
      line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
913

914 915 916
      if (line_display->height > 0)
        {
          g_assert (line_display->layout != NULL);
917
          
918
          if (have_selection)
919
            {
920 921
              GtkTextIter line_start, line_end;
              gint byte_count;
922
              
923 924 925
              gtk_text_layout_get_iter_at_line (layout,
                                                &line_start,
                                                line, 0);
926
              line_end = line_start;
927 928
	      if (!gtk_text_iter_ends_line (&line_end))
		gtk_text_iter_forward_to_line_end (&line_end);
929
              byte_count = gtk_text_iter_get_visible_line_index (&line_end);     
930

931 932
              if (gtk_text_iter_compare (&selection_start, &line_end) <= 0 &&
                  gtk_text_iter_compare (&selection_end, &line_start) >= 0)
933 934
                {
                  if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
935
                    selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
936 937 938 939
                  else
                    selection_start_index = -1;

                  if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
940
                    selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
941
                  else
942
                    selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
943
                }
944 945
            }

946 947
          render_para (text_renderer, line_display,
                       selection_start_index, selection_end_index);
948

949
          /* We paint the cursors last, because they overlap another chunk
950 951 952
           * and need to appear on top.
           */
          if (line_display->cursors != NULL)
953
            {
954
              int i;
955

956 957 958
              for (i = 0; i < line_display->cursors->len; i++)
                {
                  int index;
959
                  PangoDirection dir;
960 961

                  index = g_array_index(line_display->cursors, int, i);
962 963 964 965
                  dir = (line_display->direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
                  gtk_render_insertion_cursor (context, cr,
                                               line_display->x_offset, line_display->top_margin,
                                               line_display->layout, index, dir);
966
                }
967 968
            }
        } /* line_display->height > 0 */
969

970
      cairo_translate (cr, 0, line_display->height);
971
      gtk_text_layout_free_line_display (layout, line_display);
972
      
973 974 975 976
      tmp_list = g_slist_next (tmp_list);
    }

  gtk_text_layout_wrap_loop_end (layout);
977

978 979 980 981 982
  tmp_widgets = text_renderer_end (text_renderer);
  if (widgets)
    *widgets = tmp_widgets;
  else
    g_list_free_full (tmp_widgets, g_object_unref);
983 984 985

  g_slist_free (line_list);
}