gtktextdisplay.c 30.4 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 24 25 26 27
 * 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
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Original Tk license:
28 29 30 31 32
 *
 * 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.
33
 *
34 35 36 37 38 39 40 41 42
 * 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.
43
 *
44 45 46 47 48
 * 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.
49
 *
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
 * 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.
68 69 70 71 72 73 74
 *
 */
/*
 * 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/.
75 76
 */

77
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
78
#include "config.h"
79
#include "gtktextdisplay.h"
80
#include "gtkintl.h"
81 82 83
/* DO NOT go putting private headers in here. This file should only
 * use the semi-public headers, as with gtktextview.c.
 */
84

Matthias Clasen's avatar
Matthias Clasen committed
85
#define GTK_TYPE_TEXT_RENDERER            (_gtk_text_renderer_get_type())
86 87 88 89 90
#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))
91

92 93
typedef struct _GtkTextRenderer      GtkTextRenderer;
typedef struct _GtkTextRendererClass GtkTextRendererClass;
94

95 96 97 98 99 100
enum {
  NORMAL,
  SELECTED,
  CURSOR
};

101
struct _GtkTextRenderer
102
{
103
  PangoRenderer parent_instance;
104 105

  GtkWidget *widget;
106
  cairo_t *cr;
107 108 109 110
  
  GdkColor *error_color;	/* Error underline color for this widget */
  GList *widgets;		/* widgets encountered when drawing */
  
111
  int state;
112 113
};

114
struct _GtkTextRendererClass
115
{
116
  PangoRendererClass parent_class;
117
};
118

119
G_DEFINE_TYPE (GtkTextRenderer, _gtk_text_renderer, PANGO_TYPE_RENDERER)
120

121 122 123 124
static GdkColor *
text_renderer_get_error_color (GtkTextRenderer *text_renderer)
{
  static const GdkColor red = { 0, 0xffff, 0, 0 };
125

126 127
  if (!text_renderer->error_color)
    gtk_widget_style_get (text_renderer->widget,
128
			  "error-underline-color", &text_renderer->error_color,
129 130 131 132 133 134
			  NULL);
  
  if (!text_renderer->error_color)
    text_renderer->error_color = gdk_color_copy (&red);

  return text_renderer->error_color;
135 136 137
}

static void
138 139 140
text_renderer_set_gdk_color (GtkTextRenderer *text_renderer,
			     PangoRenderPart  part,
			     GdkColor        *gdk_color)
141
{
142 143 144 145 146
  PangoRenderer *renderer = PANGO_RENDERER (text_renderer);

  if (gdk_color)
    {
      PangoColor color;
147

148 149 150 151 152 153 154 155 156
      color.red = gdk_color->red;
      color.green = gdk_color->green;
      color.blue = gdk_color->blue;
      
      pango_renderer_set_color (renderer, part, &color);
    }
  else
    pango_renderer_set_color (renderer, part, NULL);
	   
157 158
}

159 160
static GtkTextAppearance *
get_item_appearance (PangoItem *item)
161
{
162 163 164 165 166 167 168 169 170 171 172 173 174
  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;
175 176 177
}

static void
178 179
gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
			       PangoLayoutRun *run)
180
{
181
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
182
  GtkStyle *style;
183 184 185
  GdkColor *bg_color, *fg_color, *underline_color;
  GtkTextAppearance *appearance;

Matthias Clasen's avatar
Matthias Clasen committed
186
  PANGO_RENDERER_CLASS (_gtk_text_renderer_parent_class)->prepare_run (renderer, run);
187 188 189

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

  if (appearance->draw_bg && text_renderer->state == NORMAL)
192 193 194 195 196
    bg_color = &appearance->bg_color;
  else
    bg_color = NULL;
  
  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
197

198
  style = gtk_widget_get_style (text_renderer->widget);
199
  if (text_renderer->state == SELECTED)
200
    {
201
      if (gtk_widget_has_focus (text_renderer->widget))
202
	fg_color = &style->text[GTK_STATE_SELECTED];
203
      else
204
	fg_color = &style->text[GTK_STATE_ACTIVE];
205
    }
206
  else if (text_renderer->state == CURSOR && gtk_widget_has_focus (text_renderer->widget))
207
    fg_color = &style->base[GTK_STATE_NORMAL];
208 209
  else
    fg_color = &appearance->fg_color;
210

211 212
  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_FOREGROUND, fg_color);
  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_color);
213

214 215 216 217
  if (appearance->underline == PANGO_UNDERLINE_ERROR)
    underline_color = text_renderer_get_error_color (text_renderer);
  else
    underline_color = fg_color;
218

219
  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_UNDERLINE, underline_color);
220 221
}

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 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
static void
set_color (GtkTextRenderer *text_renderer,
           PangoRenderPart  part)
{
  PangoColor *color;

  cairo_save (text_renderer->cr);

  color = pango_renderer_get_color (PANGO_RENDERER (text_renderer), part);
  if (color)
    cairo_set_source_rgb (text_renderer->cr,
                          color->red / 65535.,
                          color->green / 65535.,
                          color->blue / 65535.);
}

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);
}

350 351 352 353 354
static void
gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
			      PangoAttrShape  *attr,
			      int              x,
			      int              y)
355
{
356
  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
357

358
  if (attr->data == NULL)
359
    {
360 361 362
      /* This happens if we have an empty widget anchor. Draw
       * something empty-looking.
       */
363 364
      GdkRectangle shape_rect;
      cairo_t *cr;
365

366 367 368 369
      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;
370

371 372 373
      set_color (text_renderer, PANGO_RENDER_PART_FOREGROUND);

      cr = text_renderer->cr;
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389

      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);
390 391

      unset_color (text_renderer);
392 393 394
    }
  else if (GDK_IS_PIXBUF (attr->data))
    {
395
      cairo_t *cr = text_renderer->cr;
396
      GdkPixbuf *pixbuf = GDK_PIXBUF (attr->data);
397
      
398
      cairo_save (cr);
399

400 401 402 403
      gdk_cairo_set_source_pixbuf (cr, pixbuf,
                                   PANGO_PIXELS (x),
                                   PANGO_PIXELS (y) -  gdk_pixbuf_get_height (pixbuf));
      cairo_paint (cr);
404

405
      cairo_restore (cr);
406
    }
407 408 409 410 411
  else if (GTK_IS_WIDGET (attr->data))
    {
      GtkWidget *widget;
      
      widget = GTK_WIDGET (attr->data);
412

413 414 415 416 417
      text_renderer->widgets = g_list_prepend (text_renderer->widgets,
					       g_object_ref (widget));
    }
  else
    g_assert_not_reached (); /* not a pixbuf or widget */
418 419
}

420
static void
421
gtk_text_renderer_finalize (GObject *object)
422
{
Matthias Clasen's avatar
Matthias Clasen committed
423
  G_OBJECT_CLASS (_gtk_text_renderer_parent_class)->finalize (object);
424
}
425

426
static void
Matthias Clasen's avatar
Matthias Clasen committed
427
_gtk_text_renderer_init (GtkTextRenderer *renderer)
428
{
429 430
}

431
static void
Matthias Clasen's avatar
Matthias Clasen committed
432
_gtk_text_renderer_class_init (GtkTextRendererClass *klass)
433
{
434
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
435
  
436 437 438
  PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
  
  renderer_class->prepare_run = gtk_text_renderer_prepare_run;
439 440 441 442 443
  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;
444
  renderer_class->draw_shape = gtk_text_renderer_draw_shape;
445

446 447
  object_class->finalize = gtk_text_renderer_finalize;
}
448

449
static void
450 451
text_renderer_set_state (GtkTextRenderer *text_renderer,
			 int              state)
452
{
453
  text_renderer->state = state;
454
}
455

456 457
static void
text_renderer_begin (GtkTextRenderer *text_renderer,
458 459
                     GtkWidget       *widget,
                     cairo_t         *cr)
460 461
{
  text_renderer->widget = widget;
462
  text_renderer->cr = cr;
463
}
464

465 466 467 468 469 470
/* Returns a GSList of (referenced) widgets encountered while drawing.
 */
static GList *
text_renderer_end (GtkTextRenderer *text_renderer)
{
  GList *widgets = text_renderer->widgets;
471

472
  text_renderer->widget = NULL;
473
  text_renderer->cr = NULL;
474

475
  text_renderer->widgets = NULL;
476

477 478 479 480 481
  if (text_renderer->error_color)
    {
      gdk_color_free (text_renderer->error_color);
      text_renderer->error_color = NULL;
    }
482

483 484
  return widgets;
}
485

486

487
static cairo_region_t *
488 489 490 491 492 493 494 495 496 497 498
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;
499
  cairo_region_t *clip_region = cairo_region_create ();
500

501
  pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
502

503 504 505
  for (i=0; i < n_ranges; i++)
    {
      GdkRectangle rect;
506

507 508 509 510 511
      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;
      
512
      cairo_region_union_rectangle (clip_region, &rect);
513
    }
514 515 516

  g_free (ranges);
  return clip_region;
517 518
}

519
static void
520
render_para (GtkTextRenderer    *text_renderer,
521
             GtkTextLineDisplay *line_display,
522
             /* Top-left corner of paragraph including all margins */
523 524 525
             int                 x,
             int                 y,
             int                 selection_start_index,
526
             int                 selection_end_index)
527
{
528
  GtkStyle *style;
529
  PangoLayout *layout = line_display->layout;
530
  int byte_offset = 0;
531 532 533
  PangoLayoutIter *iter;
  PangoRectangle layout_logical;
  int screen_width;
534
  GdkColor *selection;
535
  gint state;
536
  gboolean first = TRUE;
537

538 539
  style = gtk_widget_get_style (text_renderer->widget);

540
  iter = pango_layout_get_iter (layout);
541

542
  pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical);
543

544 545 546 547 548 549 550
  /* 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;
  
551
  if (gtk_widget_has_focus (text_renderer->widget))
552 553 554 555
    state = GTK_STATE_SELECTED;
  else
    state = GTK_STATE_ACTIVE;

556
  selection = &style->base [state];
557

558 559
  do
    {
560
      PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
561
      int selection_y, selection_height;
562 563 564
      int first_y, last_y;
      PangoRectangle line_rect;
      int baseline;
565
      gboolean at_last_line;
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
      
      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
       */
      selection_y = y + PANGO_PIXELS (first_y) + line_display->top_margin;
      selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y);
582 583

      if (first)
584 585 586 587
        {
          selection_y -= line_display->top_margin;
          selection_height += line_display->top_margin;
        }
588 589 590

      at_last_line = pango_layout_iter_at_last_line (iter);
      if (at_last_line)
591
        selection_height += line_display->bottom_margin;
592
      
593
      first = FALSE;
594

595
      if (selection_start_index < byte_offset &&
596 597
          selection_end_index > line->length + byte_offset) /* All selected */
        {
598 599 600
          cairo_t *cr = text_renderer->cr;

          cairo_save (cr);
601 602 603 604 605
          gdk_cairo_set_source_color (cr, selection);
          cairo_rectangle (cr, 
                           x + line_display->left_margin, selection_y,
                           screen_width, selection_height);
          cairo_fill (cr);
606
          cairo_restore(cr);
607

608
	  text_renderer_set_state (text_renderer, SELECTED);
609 610 611 612
	  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
					   line, 
					   PANGO_SCALE * x + line_rect.x,
					   PANGO_SCALE * y + baseline);
613
        }
614
      else
615
        {
616 617
          if (line_display->pg_bg_color)
            {
618 619 620
              cairo_t *cr = text_renderer->cr;

              cairo_save (cr);
621 622 623 624 625 626 627

              gdk_cairo_set_source_color (cr, line_display->pg_bg_color);
              cairo_rectangle (cr, 
                               x + line_display->left_margin, selection_y,
                               screen_width, selection_height);
              cairo_fill (cr);

628
              cairo_restore (cr);
629 630
            }
        
631
	  text_renderer_set_state (text_renderer, NORMAL);
632 633 634 635
	  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
					   line, 
					   PANGO_SCALE * x + line_rect.x,
					   PANGO_SCALE * y + baseline);
636

637 638 639 640 641 642 643
	  /* 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)
644
            {
645
              cairo_t *cr = text_renderer->cr;
646
              cairo_region_t *clip_region = get_selected_clip (text_renderer, layout, line,
647
                                                          x + line_display->x_offset,
648 649
                                                          selection_y,
                                                          selection_height,
650
                                                          selection_start_index, selection_end_index);
651

652
              cairo_save (cr);
653 654
              gdk_cairo_region (cr, clip_region);
              cairo_clip (cr);
655
              cairo_region_destroy (clip_region);
656 657 658 659 660 661 662 663 664

              gdk_cairo_set_source_color (cr, selection);
              cairo_rectangle (cr,
                               x + PANGO_PIXELS (line_rect.x),
                               selection_y,
                               PANGO_PIXELS (line_rect.width),
                               selection_height);
              cairo_fill (cr);

665
	      text_renderer_set_state (text_renderer, SELECTED);
666 667 668 669
	      pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
					       line, 
					       PANGO_SCALE * x + line_rect.x,
					       PANGO_SCALE * y + baseline);
670

671
              cairo_restore (cr);
672 673

              /* Paint in the ends of the line */
674
              if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
675 676 677
                  ((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)))
                {
678
                  cairo_save (cr);
679 680 681 682 683 684 685 686

                  gdk_cairo_set_source_color (cr, selection);
                  cairo_rectangle (cr,
                                   x + line_display->left_margin,
                                   selection_y,
                                   PANGO_PIXELS (line_rect.x) - line_display->left_margin,
                                   selection_height);
                  cairo_fill (cr);
687 688

                  cairo_restore (cr);
689 690
                }

691 692
              if (line_rect.x + line_rect.width <
                  (screen_width + line_display->left_margin) * PANGO_SCALE &&
693 694 695
                  ((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)))
                {
696
                  int nonlayout_width;
697

698 699 700
                  nonlayout_width =
                    line_display->left_margin + screen_width -
                    PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
701

702 703
                  cairo_save (cr);

704 705 706 707 708 709 710
                  gdk_cairo_set_source_color (cr, selection);
                  cairo_rectangle (cr,
                                   x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
                                   selection_y,
                                   nonlayout_width,
                                   selection_height);
                  cairo_fill (cr);
711 712

                  cairo_restore (cr);
713 714
                }
            }
715
	  else if (line_display->has_block_cursor &&
716
		   gtk_widget_has_focus (text_renderer->widget) &&
717 718 719 720 721
		   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;
722
              GdkColor cursor_color;
723
              cairo_t *cr = text_renderer->cr;
724 725 726

	      /* we draw text using base color on filled cursor rectangle of cursor color
	       * (normally white on black) */
727
	      _gtk_widget_get_cursor_color (text_renderer->widget, &cursor_color);
728 729

	      cursor_rect.x = x + line_display->x_offset + line_display->block_cursor.x;
730
	      cursor_rect.y = y + line_display->block_cursor.y + line_display->top_margin;
731
	      cursor_rect.width = line_display->block_cursor.width;
732
	      cursor_rect.height = line_display->block_cursor.height;
733

734 735
              cairo_save (cr);

736 737
              gdk_cairo_rectangle (cr, &cursor_rect);
              cairo_clip (cr);
738

739 740
              gdk_cairo_set_source_color (cr, &cursor_color);
              cairo_paint (cr);
741 742 743 744

	      /* draw text under the cursor if any */
	      if (!line_display->cursor_at_line_end)
		{
745 746 747 748 749 750
                  GtkStateType state;
                  GtkStyle *style;

                  style = gtk_widget_get_style (text_renderer->widget);
                  state = gtk_widget_get_state (text_renderer->widget);
                  gdk_cairo_set_source_color (cr, &style->base[state]);
751 752 753 754 755 756 757 758 759

		  text_renderer_set_state (text_renderer, CURSOR);

		  pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
						   line,
						   PANGO_SCALE * x + line_rect.x,
						   PANGO_SCALE * y + baseline);

		}
760 761

              cairo_restore (cr);
762
	    }
763
        }
764 765 766

      byte_offset += line->length;
    }
767 768 769
  while (pango_layout_iter_next_line (iter));

  pango_layout_iter_free (iter);
770 771
}

772
static GtkTextRenderer *
773
get_text_renderer (void)
774
{
775
  static GtkTextRenderer *text_renderer = NULL;
776

777
  if (!text_renderer)
778
    text_renderer = g_object_new (GTK_TYPE_TEXT_RENDERER, NULL);
779 780

  return text_renderer;
781 782 783 784
}

void
gtk_text_layout_draw (GtkTextLayout *layout,
785
                      GtkWidget *widget,
786
                      cairo_t *cr,
787
                      GList **widgets)
788 789 790
{
  gint current_y;
  GSList *cursor_list;
791
  GtkTextRenderer *text_renderer;
792 793
  GtkTextIter selection_start, selection_end;
  gboolean have_selection = FALSE;
794 795
  GSList *line_list;
  GSList *tmp_list;
796
  GList *tmp_widgets;
José Aliste's avatar
José Aliste committed
797
  GdkRectangle clip;
798
  
799 800 801
  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
  g_return_if_fail (layout->default_style != NULL);
  g_return_if_fail (layout->buffer != NULL);
802
  g_return_if_fail (cr != NULL);
803

José Aliste's avatar
José Aliste committed
804 805
  if (!gdk_cairo_get_clip_rectangle (cr, &clip))
    return;
806

José Aliste's avatar
José Aliste committed
807
  line_list =  gtk_text_layout_get_lines (layout, clip.y, clip.y + clip.height, &current_y);
808 809 810 811

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

812
  cairo_save (cr);
813

814
  gdk_cairo_set_source_color (cr, &gtk_widget_get_style (widget)->text[gtk_widget_get_state (widget)]);
815

816 817
  text_renderer = get_text_renderer ();
  text_renderer_begin (text_renderer, widget, cr);
818 819

  gtk_text_layout_wrap_loop_start (layout);
820

821
  if (gtk_text_buffer_get_selection_bounds (layout->buffer,
822 823
                                            &selection_start,
                                            &selection_end))
824
    have_selection = TRUE;
825

826 827 828 829 830 831
  tmp_list = line_list;
  while (tmp_list != NULL)
    {
      GtkTextLineDisplay *line_display;
      gint selection_start_index = -1;
      gint selection_end_index = -1;
832 833
      gboolean have_strong;
      gboolean have_weak;
834 835

      GtkTextLine *line = tmp_list->data;
836

837
      line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
838

839 840 841
      if (line_display->height > 0)
        {
          g_assert (line_display->layout != NULL);
842
          
843
          if (have_selection)
844
            {
845 846
              GtkTextIter line_start, line_end;
              gint byte_count;
847
              
848 849 850
              gtk_text_layout_get_iter_at_line (layout,
                                                &line_start,
                                                line, 0);
851
              line_end = line_start;
852 853
	      if (!gtk_text_iter_ends_line (&line_end))
		gtk_text_iter_forward_to_line_end (&line_end);
854
              byte_count = gtk_text_iter_get_visible_line_index (&line_end);     
855

856 857
              if (gtk_text_iter_compare (&selection_start, &line_end) <= 0 &&
                  gtk_text_iter_compare (&selection_end, &line_start) >= 0)
858 859
                {
                  if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
860
                    selection_start_index = gtk_text_iter_get_visible_line_index (&selection_start);
861 862 863 864
                  else
                    selection_start_index = -1;

                  if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
865
                    selection_end_index = gtk_text_iter_get_visible_line_index (&selection_end);
866
                  else
867
                    selection_end_index = byte_count + 1; /* + 1 to flag past-the-end */
868
                }
869 870
            }

871
          render_para (text_renderer, line_display,
872
                       0, current_y,
873
                       selection_start_index, selection_end_index);
874

875
          /* We paint the cursors last, because they overlap another chunk
876 877
         and need to appear on top. */

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
 	  have_strong = FALSE;
 	  have_weak = FALSE;
	  
	  cursor_list = line_display->cursors;
	  while (cursor_list)
	    {
	      GtkTextCursorDisplay *cursor = cursor_list->data;
 	      if (cursor->is_strong)
 		have_strong = TRUE;
 	      else
 		have_weak = TRUE;
	      
	      cursor_list = cursor_list->next;
 	    }
	  
893 894 895 896
          cursor_list = line_display->cursors;
          while (cursor_list)
            {
              GtkTextCursorDisplay *cursor = cursor_list->data;
897 898
	      GtkTextDirection dir;
 	      GdkRectangle cursor_location;
899

900
              dir = line_display->direction;
901 902 903 904 905 906
 	      if (have_strong && have_weak)
 		{
 		  if (!cursor->is_strong)
 		    dir = (dir == GTK_TEXT_DIR_RTL) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
 		}
 
907
 	      cursor_location.x = line_display->x_offset + cursor->x;
908 909 910
 	      cursor_location.y = current_y + line_display->top_margin + cursor->y;
 	      cursor_location.width = 0;
 	      cursor_location.height = cursor->height;
911

912 913 914
	      gtk_draw_insertion_cursor (widget, cr, &cursor_location,
                                         cursor->is_strong,
                                         dir, have_strong && have_weak);
915

916 917 918 919
              cursor_list = cursor_list->next;
            }
        } /* line_display->height > 0 */
          
920 921
      current_y += line_display->height;
      gtk_text_layout_free_line_display (layout, line_display);
922
      
923 924 925 926
      tmp_list = g_slist_next (tmp_list);
    }

  gtk_text_layout_wrap_loop_end (layout);
927 928 929 930 931 932 933 934 935

  tmp_widgets = text_renderer_end (text_renderer);
  if (widgets)
    *widgets = tmp_widgets;
  else
    {
      g_list_foreach (tmp_widgets, (GFunc)g_object_unref, NULL);
      g_list_free (tmp_widgets);
    }
936

937
  cairo_restore (cr);
938

939 940
  g_slist_free (line_list);
}