*/
#include "gtktextdisplay.h"
-#include "gtktextiterprivate.h"
+/* DO NOT go putting private headers in here. This file should only
+ * use the semi-public headers, as with gtktextview.c.
+ */
#include <pango/pango.h>
GtkWidget *widget;
GtkTextAppearance *last_appearance;
+ GtkTextAppearance *last_bg_appearance;
GdkGC *fg_gc;
GdkGC *bg_gc;
GdkRectangle clip_rect;
if (new_appearance->draw_bg)
{
- if (!state->last_appearance ||
- !gdk_color_equal (&new_appearance->bg_color, &state->last_appearance->bg_color))
+ if (!state->last_bg_appearance ||
+ !gdk_color_equal (&new_appearance->bg_color, &state->last_bg_appearance->bg_color))
gtk_text_render_state_set_color (state, state->bg_gc, &new_appearance->bg_color);
- if (!state->last_appearance ||
- new_appearance->bg_stipple != state->last_appearance->bg_stipple)
+ if (!state->last_bg_appearance ||
+ new_appearance->bg_stipple != state->last_bg_appearance->bg_stipple)
{
if (new_appearance->bg_stipple)
{
gdk_gc_set_fill (state->bg_gc, GDK_SOLID);
}
}
+
+ state->last_bg_appearance = new_appearance;
}
state->last_appearance = new_appearance;
}
+static void
+get_shape_extents (PangoLayoutRun *run,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ GSList *tmp_list = run->item->extra_attrs;
+
+ while (tmp_list)
+ {
+ PangoAttribute *attr = tmp_list->data;
+
+ if (attr->klass->type == PANGO_ATTR_SHAPE)
+ {
+ if (logical_rect)
+ *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
+
+ if (ink_rect)
+ *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
+
+ return;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+
+ g_assert_not_reached ();
+}
+
static void
render_layout_line (GdkDrawable *drawable,
GtkTextRenderState *render_state,
PangoRectangle overall_rect;
PangoRectangle logical_rect;
PangoRectangle ink_rect;
-
gint x_off = 0;
-
+ GdkGC *fg_gc;
+
pango_layout_line_get_extents (line, NULL, &overall_rect);
while (tmp_list)
{
PangoLayoutRun *run = tmp_list->data;
GtkTextAppearance *appearance;
-
+ gint risen_y;
+ gint shaped_width_pixels = 0;
+ gboolean need_ink = FALSE;
+
tmp_list = tmp_list->next;
get_item_properties (run->item, &appearance);
- if (appearance) /* A text segment */
+ g_assert (appearance != NULL);
+
+ risen_y = y - PANGO_PIXELS (appearance->rise);
+
+ if (selected)
{
- GdkGC *fg_gc;
-
- if (selected)
- {
- fg_gc = render_state->widget->style->fg_gc[GTK_STATE_SELECTED];
- }
+ fg_gc = render_state->widget->style->fg_gc[GTK_STATE_SELECTED];
+ }
+ else
+ {
+ gtk_text_render_state_update (render_state, appearance);
+
+ fg_gc = render_state->fg_gc;
+ }
+
+ if (appearance->underline != PANGO_UNDERLINE_NONE ||
+ appearance->strikethrough)
+ need_ink = TRUE;
+
+ if (appearance->is_text)
+ {
+ if (need_ink)
+ pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
+ &ink_rect, &logical_rect);
else
- {
- gtk_text_render_state_update (render_state, appearance);
-
- fg_gc = render_state->fg_gc;
- }
-
- if (appearance->underline == PANGO_UNDERLINE_NONE && !appearance->strikethrough)
pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
NULL, &logical_rect);
+ }
+ else
+ {
+ if (need_ink)
+ get_shape_extents (run, &ink_rect, &logical_rect);
else
- pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
- &ink_rect, &logical_rect);
-
- if (appearance->draw_bg && !selected)
- gdk_draw_rectangle (drawable, render_state->bg_gc, TRUE,
- x + (x_off + logical_rect.x) / PANGO_SCALE,
- y + logical_rect.y / PANGO_SCALE,
- logical_rect.width / PANGO_SCALE,
- logical_rect.height / PANGO_SCALE);
-
- gdk_draw_glyphs (drawable, fg_gc,
- run->item->analysis.font,
- x + x_off / PANGO_SCALE, y, run->glyphs);
-
- switch (appearance->underline)
- {
- case PANGO_UNDERLINE_NONE:
- break;
- case PANGO_UNDERLINE_DOUBLE:
- gdk_draw_line (drawable, fg_gc,
- x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 4,
- x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 4);
- /* Fall through */
- case PANGO_UNDERLINE_SINGLE:
- gdk_draw_line (drawable, fg_gc,
- x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 2,
- x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 2);
- break;
- case PANGO_UNDERLINE_LOW:
- gdk_draw_line (drawable, fg_gc,
- x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2,
- x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2);
- break;
- }
-
- if (appearance->strikethrough)
- {
- gint strikethrough_y = y + (0.3 * logical_rect.y) / PANGO_SCALE;
- gdk_draw_line (drawable, fg_gc,
- x + (x_off + ink_rect.x) / PANGO_SCALE - 1, strikethrough_y,
- x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, strikethrough_y);
- }
-
- x_off += logical_rect.width;
+ get_shape_extents (run, NULL, &logical_rect);
}
- else /* Pixbuf or widget segment */
+
+ if (appearance->draw_bg && !selected)
+ gdk_draw_rectangle (drawable, render_state->bg_gc, TRUE,
+ x + PANGO_PIXELS (x_off) + PANGO_PIXELS (logical_rect.x),
+ risen_y + PANGO_PIXELS (logical_rect.y),
+ PANGO_PIXELS (logical_rect.width),
+ PANGO_PIXELS (logical_rect.height));
+
+ if (appearance->is_text)
+ gdk_draw_glyphs (drawable, fg_gc,
+ run->item->analysis.font,
+ x + PANGO_PIXELS (x_off),
+ risen_y, run->glyphs);
+ else
{
GObject *shaped = (*shaped_pointer)->data;
height = gdk_pixbuf_get_height (pixbuf);
pixbuf_rect.x = x + x_off / PANGO_SCALE;
- pixbuf_rect.y = y - height;
+ pixbuf_rect.y = risen_y - height;
pixbuf_rect.width = width;
pixbuf_rect.height = height;
}
}
- x_off += width * PANGO_SCALE;
+ shaped_width_pixels = width;
}
else if (GTK_IS_WIDGET (shaped))
{
gint width, height;
GdkRectangle draw_rect;
GtkWidget *widget;
-
+
widget = GTK_WIDGET (shaped);
width = widget->allocation.width;
height = widget->allocation.height;
+ g_print ("widget allocation at %d,%d %d x %d\n",
+ widget->allocation.x,
+ widget->allocation.y,
+ widget->allocation.width,
+ widget->allocation.height);
+
if (GTK_WIDGET_DRAWABLE (widget) &&
- gtk_widget_intersect (widget,
- &render_state->clip_rect,
- &draw_rect))
+ gdk_rectangle_intersect (&widget->allocation,
+ &render_state->clip_rect,
+ &draw_rect))
+
{
+ g_print ("drawing widget area %d,%d %d x %d\n",
+ draw_rect.x,
+ draw_rect.y,
+ draw_rect.width,
+ draw_rect.height);
+
gtk_widget_draw (widget, &draw_rect);
}
- x_off += width * PANGO_SCALE;
+ shaped_width_pixels = width;
}
else
g_assert_not_reached (); /* not a pixbuf or widget */
}
+
+ switch (appearance->underline)
+ {
+ case PANGO_UNDERLINE_NONE:
+ break;
+ case PANGO_UNDERLINE_DOUBLE:
+ g_assert (need_ink);
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
+ risen_y + 3,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
+ risen_y + 3);
+ /* Fall through */
+ case PANGO_UNDERLINE_SINGLE:
+ g_assert (need_ink);
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
+ risen_y + 1,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
+ risen_y + 1);
+ break;
+ case PANGO_UNDERLINE_LOW:
+ g_assert (need_ink);
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
+ risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
+ risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1);
+ break;
+ }
+
+ if (appearance->strikethrough)
+ {
+ gint strikethrough_y = risen_y + (0.3 * logical_rect.y) / PANGO_SCALE;
+
+ g_assert (need_ink);
+
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1, strikethrough_y,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, strikethrough_y);
+ }
+
+ if (appearance->is_text)
+ x_off += logical_rect.width;
+ else
+ x_off += shaped_width_pixels * PANGO_SCALE;
}
}
render_para (GdkDrawable *drawable,
GtkTextRenderState *render_state,
GtkTextLineDisplay *line_display,
+ /* Top-left corner of paragraph including all margins */
int x,
int y,
int selection_start_index,
int selection_end_index)
{
- PangoRectangle logical_rect;
GSList *shaped_pointer = line_display->shaped_objects;
- GSList *tmp_list;
- PangoAlignment align;
PangoLayout *layout = line_display->layout;
- int indent;
- int total_width;
- int y_offset = 0;
int byte_offset = 0;
-
+ PangoLayoutIter *iter;
+ PangoRectangle layout_logical;
+ int screen_width;
+
gboolean first = TRUE;
- indent = pango_layout_get_indent (layout);
- total_width = pango_layout_get_width (layout);
- align = pango_layout_get_alignment (layout);
+ iter = pango_layout_get_iter (layout);
- if (total_width < 0)
- total_width = line_display->total_width * PANGO_SCALE;
+ pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical);
- tmp_list = pango_layout_get_lines (layout);
- while (tmp_list)
+ /* 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;
+
+ do
{
- PangoLayoutLine *line = tmp_list->data;
- int x_offset;
+ PangoLayoutLine *line = pango_layout_iter_get_line (iter);
int selection_y, selection_height;
-
- pango_layout_line_get_extents (line, NULL, &logical_rect);
-
- x_offset = line_display->left_margin * PANGO_SCALE;
-
- switch (align)
- {
- case PANGO_ALIGN_RIGHT:
- x_offset += total_width - logical_rect.width;
- break;
- case PANGO_ALIGN_CENTER:
- x_offset += (total_width - logical_rect.width) / 2;
- break;
- default:
- break;
- }
-
- if (first)
- {
- if (indent > 0)
- {
- if (align == PANGO_ALIGN_LEFT)
- x_offset += indent;
- else
- x_offset -= indent;
- }
- }
- else
- {
- if (indent < 0)
- {
- if (align == PANGO_ALIGN_LEFT)
- x_offset -= indent;
- else
- x_offset += indent;
- }
- }
-
- selection_y = y + y_offset / PANGO_SCALE;
- selection_height = logical_rect.height / PANGO_SCALE;
+ int first_y, last_y;
+ PangoRectangle line_rect;
+ int baseline;
+
+ 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);
if (first)
{
selection_y -= line_display->top_margin;
selection_height += line_display->top_margin;
}
- if (!tmp_list->next)
+
+ if (pango_layout_iter_at_last_line (iter))
selection_height += line_display->bottom_margin;
-
+
first = FALSE;
if (selection_start_index < byte_offset &&
gdk_draw_rectangle (drawable,
render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
TRUE,
- x + line_display->left_margin, selection_y,
- total_width / PANGO_SCALE, selection_height);
+ x + line_display->left_margin,
+ selection_y,
+ screen_width,
+ selection_height);
+
render_layout_line (drawable, render_state, line, &shaped_pointer,
- x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE,
+ x + PANGO_PIXELS (line_rect.x),
+ y + PANGO_PIXELS (baseline),
TRUE);
}
else
{
GSList *shaped_pointer_tmp = shaped_pointer;
- render_layout_line (drawable, render_state, line, &shaped_pointer,
- x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE,
+ render_layout_line (drawable, render_state,
+ line, &shaped_pointer,
+ x + PANGO_PIXELS (line_rect.x),
+ y + PANGO_PIXELS (baseline),
FALSE);
if (selection_start_index < byte_offset + line->length &&
{
GdkRegion *clip_region = get_selected_clip (render_state, layout, line,
x + line_display->x_offset,
- selection_y, selection_height,
+ selection_y,
+ selection_height,
selection_start_index, selection_end_index);
gdk_gc_set_clip_region (render_state->widget->style->fg_gc [GTK_STATE_SELECTED], clip_region);
gdk_draw_rectangle (drawable,
render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
TRUE,
- x + x_offset / PANGO_SCALE, selection_y,
- logical_rect.width / PANGO_SCALE,
+ x + PANGO_PIXELS (line_rect.x),
+ selection_y,
+ PANGO_PIXELS (line_rect.width),
selection_height);
render_layout_line (drawable, render_state, line, &shaped_pointer_tmp,
- x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE,
+ x + PANGO_PIXELS (line_rect.x),
+ y + PANGO_PIXELS (baseline),
TRUE);
gdk_gc_set_clip_region (render_state->widget->style->fg_gc [GTK_STATE_SELECTED], NULL);
gdk_region_destroy (clip_region);
/* Paint in the ends of the line */
- if (x_offset > line_display->left_margin * PANGO_SCALE &&
+ if (line_rect.x > line_display->left_margin * PANGO_SCALE &&
((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)))
{
gdk_draw_rectangle (drawable,
render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
TRUE,
- x + line_display->left_margin, selection_y,
- x + x_offset / PANGO_SCALE - line_display->left_margin,
+ x + line_display->left_margin,
+ selection_y,
+ PANGO_PIXELS (line_rect.x) - line_display->left_margin,
selection_height);
}
- if (x_offset + logical_rect.width < line_display->left_margin * PANGO_SCALE + total_width &&
+ if (line_rect.x + line_rect.width <
+ (screen_width + line_display->left_margin) * PANGO_SCALE &&
((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)))
{
+ int nonlayout_width;
+ nonlayout_width =
+ line_display->left_margin + screen_width -
+ PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
gdk_draw_rectangle (drawable,
render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
TRUE,
- x + (x_offset + logical_rect.width) / PANGO_SCALE,
+ x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
selection_y,
- x + (line_display->left_margin * PANGO_SCALE + total_width - x_offset - logical_rect.width) / PANGO_SCALE,
+ nonlayout_width,
selection_height);
}
}
}
byte_offset += line->length;
- y_offset += logical_rect.height;
- tmp_list = tmp_list->next;
}
+ while (pango_layout_iter_next_line (iter));
+
+ pango_layout_iter_free (iter);
}
static GdkRegion *
gint n_ranges, i;
GdkRegion *clip_region = gdk_region_new ();
GdkRegion *tmp_region;
- PangoRectangle logical_rect;
- pango_layout_line_get_extents (line, NULL, &logical_rect);
pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
for (i=0; i < n_ranges; i++)
{
GdkRectangle rect;
- rect.x = x + ranges[2*i] / PANGO_SCALE;
+ rect.x = x + PANGO_PIXELS (ranges[2*i]);
rect.y = y;
- rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
+ rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
rect.height = height;
-
+
gdk_region_union_with_rect (clip_region, &rect);
}
gtk_text_layout_draw (GtkTextLayout *layout,
GtkWidget *widget,
GdkDrawable *drawable,
- /* Location of the layout
- in buffer coordinates */
+ /* Location of the drawable
+ in layout coordinates */
gint x_offset,
gint y_offset,
/* Region of the layout to
{
GdkRectangle clip;
gint current_y;
- GSList *line_list;
- GSList *tmp_list;
GSList *cursor_list;
GtkTextRenderState *render_state;
GtkTextIter selection_start, selection_end;
gboolean have_selection = FALSE;
-
+ GSList *line_list;
+ GSList *tmp_list;
+
g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
g_return_if_fail (layout->default_style != NULL);
g_return_if_fail (layout->buffer != NULL);
GtkTextLine *line = tmp_list->data;
line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
-
+
if (have_selection)
{
GtkTextIter line_start, line_end;
- gint byte_count = gtk_text_line_byte_count (line);
+ gint byte_count;
- gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
- &line_start,
- line, 0);
- gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
- &line_end,
- line, byte_count - 1);
+ gtk_text_layout_get_iter_at_line (layout,
+ &line_start,
+ line, 0);
+ byte_count = gtk_text_iter_get_bytes_in_line (&line_start);
+
+ /* FIXME the -1 assumes a newline I think */
+ gtk_text_layout_get_iter_at_line (layout,
+ &line_end,
+ line, byte_count - 1);
if (gtk_text_iter_compare (&selection_start, &line_end) < 0 &&
gtk_text_iter_compare (&selection_end, &line_start) > 0)
render_para (drawable, render_state, line_display,
- x_offset,
- current_y + line_display->top_margin,
+ current_y,
selection_start_index, selection_end_index);
gc = widget->style->fg_gc[GTK_STATE_NORMAL];
gdk_draw_line (drawable, gc,
- line_display->x_offset + cursor->x,
+ line_display->x_offset + cursor->x - x_offset,
current_y + line_display->top_margin + cursor->y,
- line_display->x_offset + cursor->x,
- current_y + line_display->top_margin + cursor->y + cursor->height);
+ line_display->x_offset + cursor->x - x_offset,
+ current_y + line_display->top_margin + cursor->y + cursor->height - 1);
cursor_list = cursor_list->next;
}
current_y += line_display->height;
gtk_text_layout_free_line_display (layout, line_display);
-
+ render_state->last_appearance = NULL;
+ render_state->last_bg_appearance = NULL;
+
tmp_list = g_slist_next (tmp_list);
}