summaryrefslogtreecommitdiff
path: root/pango
diff options
context:
space:
mode:
Diffstat (limited to 'pango')
-rw-r--r--pango/break.c4
-rw-r--r--pango/pango-layout.c420
-rw-r--r--pango/pango-layout.h38
3 files changed, 407 insertions, 55 deletions
diff --git a/pango/break.c b/pango/break.c
index 811d5482..b171b087 100644
--- a/pango/break.c
+++ b/pango/break.c
@@ -49,7 +49,7 @@ void pango_break (const gchar *text,
{
next = unicode_get_utf8 (cur, &wc);
if (!next)
- return; /* FIXME: ERROR */
+ break; /* FIXME: ERROR */
if (cur == next)
break;
if ((next - text) > length)
@@ -57,7 +57,7 @@ void pango_break (const gchar *text,
cur = next;
attrs[i].is_white = (wc == ' ' || wc == '\t' || wc == '\n') ? 1 : 0;
- attrs[i].is_break = (i > 0 && attrs[i-1].is_white) || attrs[i].is_white;
+ attrs[i].is_break = i == 0 || attrs[i-1].is_white || attrs[i].is_white;
attrs[i].is_char_stop = 1;
attrs[i].is_word_stop = (i == 0) || attrs[i-1].is_white;
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index bc031e89..db807f9b 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -58,8 +58,13 @@ struct _PangoLayoutLinePrivate
static void pango_layout_clear_lines (PangoLayout *layout);
static void pango_layout_check_lines (PangoLayout *layout);
-static PangoLayoutLine * pango_layout_line_new (PangoLayout *layout);
-static void pango_layout_line_reorder (PangoLayoutLine *line);
+static PangoLayoutLine * pango_layout_line_new (PangoLayout *layout);
+static void pango_layout_line_postprocess (PangoLayoutLine *line);
+
+static int *pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
+ gboolean strong);
+static int *pango_layout_line_get_vis2log_map (PangoLayoutLine *line,
+ gboolean strong);
static void pango_layout_get_item_properties (PangoItem *item,
PangoUnderline *uline);
@@ -634,6 +639,160 @@ pango_layout_index_to_line_x (PangoLayout *layout,
}
/**
+ * pango_layout_move_cursor_visually:
+ * @layout: a #PangoLayout.
+ * @old_index: the old cursor byte index
+ * @old_trailing:
+ * @direction: direction to move cursor. A negative
+ * value indicates motion to the left.
+ * @new_index: location to store the new cursor byte index. A value of -1
+ * indicates that the cursor has been moved off the beginning
+ * of the layout. A value of G_MAXINT indicates that
+ * the cursor has been moved off the end of the layout.
+ * @new_trailing:
+ *
+ * Computes a new cursor position from an old position and
+ * a count of positions to move visually. If @count is positive,
+ * then the new strong cursor position will be one position
+ * to the right of the old cursor position. If @count is position
+ * then the new strong cursor position will be one position
+ * to the left of the old cursor position.
+ *
+ * In the presence of bidirection text, the correspondence
+ * between logical and visual order will depend on the direction
+ * of the current run, and there may be jumps when the cursor
+ * is moved off of the end of a run.
+ **/
+void
+pango_layout_move_cursor_visually (PangoLayout *layout,
+ int old_index,
+ int old_trailing,
+ int direction,
+ int *new_index,
+ int *new_trailing)
+{
+ int bytes_seen = 0;
+ PangoDirection base_dir;
+ PangoLayoutLine *line = NULL;
+ PangoLayoutLine *prev_line = NULL;
+ PangoLayoutLine *next_line;
+ GSList *tmp_list;
+
+ int *log2vis_map;
+ int *vis2log_map;
+ int n_vis;
+ int vis_pos;
+
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (old_index >= 0 && old_index <= layout->length);
+ g_return_if_fail (old_index < layout->length || old_trailing == 0);
+ g_return_if_fail (new_index != NULL);
+ g_return_if_fail (new_trailing != NULL);
+
+ pango_layout_check_lines (layout);
+
+ base_dir = pango_context_get_base_dir (layout->context);
+
+ /* Find the line the old cursor is on
+ */
+ tmp_list = layout->lines;
+ while (tmp_list)
+ {
+ line = tmp_list->data;
+
+ if (bytes_seen + line->length > old_index || !tmp_list->next)
+ break;
+
+ tmp_list = tmp_list->next;
+ prev_line = line;
+ bytes_seen += line->length;
+ }
+
+ if (tmp_list->next)
+ next_line = tmp_list->next->data;
+ else
+ next_line = NULL;
+
+ if (old_trailing)
+ old_index = unicode_next_utf8 (layout->text + old_index) - layout->text;
+
+ log2vis_map = pango_layout_line_get_log2vis_map (line, TRUE);
+ n_vis = unicode_strlen (layout->text + bytes_seen, line->length);
+
+ vis_pos = log2vis_map[old_index - bytes_seen];
+ g_free (log2vis_map);
+
+ if (vis_pos == 0 && count < 0)
+ {
+ if (base_dir == PANGO_DIRECTION_LTR)
+ {
+ if (!prev_line)
+ {
+ *new_index = -1;
+ *new_trailing = 0;
+ return;
+ }
+ line = prev_line;
+ bytes_seen -= line->length;
+ }
+ else
+ {
+ if (!next_line)
+ {
+ *new_index = G_MAXINT;
+ *new_trailing = 0;
+ return;
+ }
+ bytes_seen += line->length;
+ line = next_line;
+ }
+
+ vis_pos = unicode_strlen (layout->text + bytes_seen, line->length);
+ }
+ else if (vis_pos == n_vis && count > 0)
+ {
+ if (base_dir == PANGO_DIRECTION_LTR)
+ {
+ if (!next_line)
+ {
+ *new_index = G_MAXINT;
+ *new_trailing = 0;
+ return;
+ }
+ bytes_seen += line->length;
+ line = next_line;
+ }
+ else
+ {
+ if (!prev_line)
+ {
+ *new_index = -1;
+ *new_trailing = 0;
+ return;
+ }
+ line = prev_line;
+ bytes_seen -= line->length;
+ }
+
+ vis_pos = 0;
+ }
+
+ vis_pos += (count > 0) ? 1 : -1;
+
+ vis2log_map = pango_layout_line_get_vis2log_map (line, TRUE);
+ *new_index = bytes_seen + vis2log_map[vis_pos];
+ g_free (vis2log_map);
+
+ if (*new_index == bytes_seen + line->length)
+ {
+ *new_index = unicode_previous_utf8 (layout->text, layout->text + *new_index) - layout->text;
+ *new_trailing = 1;
+ }
+ else
+ *new_trailing = 0;
+}
+
+/**
* pango_layout_xy_to_index:
* @layout: a #PangoLayout
* @x: the X offset (in thousandths of a device unit)
@@ -694,7 +853,8 @@ pango_layout_xy_to_index (PangoLayout *layout,
else
x_offset = 0;
- return pango_layout_line_x_to_index (line, x - x_offset, index, trailing);
+ pango_layout_line_x_to_index (line, x - x_offset, index, trailing);
+ return TRUE;
}
y_offset += logical_rect.height;
@@ -779,6 +939,141 @@ pango_layout_index_to_pos (PangoLayout *layout,
}
}
+static void
+pango_layout_line_get_range (PangoLayoutLine *line,
+ char **start,
+ char **end)
+{
+ char *p;
+ GSList *tmp_list;
+
+ p = line->layout->text;
+ tmp_list = line->layout->lines;
+
+ while (tmp_list->data != line)
+ {
+ p += ((PangoLayoutLine *)tmp_list->data)->length;
+ tmp_list = tmp_list->next;
+ }
+
+ if (start)
+ *start = p;
+ if (end)
+ *end = p + line->length;
+}
+
+int *
+pango_layout_line_get_vis2log_map (PangoLayoutLine *line,
+ gboolean strong)
+{
+ PangoLayout *layout = line->layout;
+ PangoDirection base_dir = pango_context_get_base_dir (layout->context);
+ PangoDirection prev_dir = base_dir;
+ GSList *tmp_list;
+ gchar *start, *end;
+ int *result;
+ int pos = 0;
+ int n_chars;
+
+ pango_layout_line_get_range (line, &start, &end);
+ n_chars = unicode_strlen (start, end - start);
+
+ result = g_new (int, n_chars + 1);
+
+ if (strong)
+ {
+ if (base_dir == PANGO_DIRECTION_LTR)
+ {
+ result[0] = 0;
+ result[n_chars] = end - start;
+ }
+ else
+ {
+ result[0] = end - start;
+ result[n_chars] = 0;
+ }
+ }
+
+ tmp_list = line->runs;
+ while (tmp_list)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+ int run_n_chars = run->item->num_chars;
+ PangoDirection run_dir = (run->item->analysis.level % 2) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
+ char *p = layout->text + run->item->offset;
+ int i;
+
+ if (run_dir == PANGO_DIRECTION_LTR)
+ {
+ if ((strong && base_dir == run_dir) ||
+ (!strong && base_dir != run_dir) ||
+ (prev_dir == run_dir))
+ result[pos] = p - start;
+
+ p = unicode_next_utf8 (p);
+
+ for (i = 1; i < run_n_chars; i++)
+ {
+ result[pos + i] = p - start;
+ p = unicode_next_utf8 (p);
+ }
+
+ if ((strong && base_dir == run_dir) ||
+ (!strong && base_dir != run_dir))
+ result[pos + run_n_chars] = p - start;
+ }
+ else
+ {
+ if ((strong && base_dir == run_dir) ||
+ (!strong && base_dir != run_dir))
+ result[pos + run_n_chars] = p - start;
+
+ p = unicode_next_utf8 (p);
+
+ for (i = 1; i < run_n_chars; i++)
+ {
+ result[pos + run_n_chars - i] = p - start;
+ p = unicode_next_utf8 (p);
+ }
+
+ if ((strong && base_dir == run_dir) ||
+ (!strong && base_dir != run_dir) ||
+ (prev_dir == run_dir))
+ result[pos] = p - start;
+ }
+
+ pos += run_n_chars;
+ prev_dir = run_dir;
+ tmp_list = tmp_list->next;
+ }
+
+ return result;
+}
+
+static int *
+pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
+ gboolean strong)
+{
+ gchar *start, *end;
+ int *reverse_map;
+ int *result;
+ int i;
+ int n_chars;
+
+ pango_layout_line_get_range (line, &start, &end);
+ n_chars = unicode_strlen (start, end - start);
+ result = g_new0 (int, end - start + 1);
+
+ reverse_map = pango_layout_line_get_vis2log_map (line, strong);
+
+ for (i=0; i <= n_chars; i++)
+ result[reverse_map[i]] = i;
+
+ g_free (reverse_map);
+
+ return result;
+}
+
static PangoDirection
pango_layout_line_get_char_direction (PangoLayoutLine *layout_line,
int index)
@@ -1102,6 +1397,7 @@ process_item (PangoLayoutLine *line,
return FALSE;
pango_shape (text + item->offset, item->length, &item->analysis, glyphs);
+
if (*remaining_width < 0)
{
insert_run (line, item, glyphs);
@@ -1126,39 +1422,28 @@ process_item (PangoLayoutLine *line,
{
int length;
int num_chars = item->num_chars;
- int new_width;
PangoGlyphUnit *log_widths = g_new (PangoGlyphUnit, item->num_chars);
pango_glyph_string_get_logical_widths (glyphs, text + item->offset, item->length, item->analysis.level, log_widths);
- new_width = 0;
+ /* Shorten the item by one line break
+ */
while (--num_chars > 0)
{
- /* Shorten the item by one line break
- */
width -= log_widths[num_chars];
+
if (log_attrs[num_chars].is_break && width <= *remaining_width)
break;
}
g_free (log_widths);
- if (num_chars != 0) /* Succesfully broke the item */
+ if (num_chars > 0) /* Succesfully broke the item */
{
- const char *p;
- gint n;
-
PangoItem *new_item = pango_item_copy (item);
-
- /* Determine utf8 length corresponding to num_chars. Slow?
- */
- n = num_chars;
- p = text + item->offset;
- while (n-- > 0)
- p = unicode_next_utf8 (p);
- length = p - (text + item->offset);
+ length = unicode_offset_to_index (text + item->offset, num_chars);
new_item->length = length;
new_item->num_chars = num_chars;
@@ -1166,9 +1451,9 @@ process_item (PangoLayoutLine *line,
item->offset += length;
item->length -= length;
item->num_chars -= num_chars;
-
+
pango_shape (text + new_item->offset, new_item->length, &new_item->analysis, glyphs);
-
+
*remaining_width -= width;
insert_run (line, new_item, glyphs);
@@ -1305,7 +1590,14 @@ pango_layout_check_lines (PangoLayout *layout)
tmp_list = tmp_list->next;
start_offset += old_num_chars;
- if (start_offset < layout->n_chars && !layout->log_attrs[start_offset].is_break)
+ /* We prohibit breaking a line between a non-whitespace char and the whitespace char afterwards
+ * because this would result in the possibility of normal wrapped paragraphs with leading
+ * white-space at the front of lines. We probably should have a mode where we treat all
+ * white-space as zero width - appropriate for typography but not for editing.
+ */
+ if (start_offset < layout->n_chars &&
+ (!layout->log_attrs[start_offset].is_break ||
+ (!layout->log_attrs[start_offset-1].is_white && layout->log_attrs[start_offset].is_white)))
last_cant_end = TRUE;
else
{
@@ -1346,15 +1638,14 @@ pango_layout_check_lines (PangoLayout *layout)
}
else
{
- line->runs = g_slist_reverse (line->runs);
- pango_layout_line_reorder (line);
+ start_offset += old_num_chars - item->num_chars;
+
+ pango_layout_line_postprocess (line);
layout->lines = g_slist_prepend (layout->lines, line);
line = pango_layout_line_new (layout);
remaining_width = (layout->indent >= 0) ? layout->width : layout->indent + layout->indent;
-
- start_offset += old_num_chars - item->num_chars;
}
last_cant_end = FALSE;
@@ -1362,8 +1653,7 @@ pango_layout_check_lines (PangoLayout *layout)
}
}
- line->runs = g_slist_reverse (line->runs);
- pango_layout_line_reorder (line);
+ pango_layout_line_postprocess (line);
layout->lines = g_slist_prepend (layout->lines, line);
@@ -1447,11 +1737,12 @@ pango_layout_line_unref (PangoLayoutLine *line)
* 0 represents the trailing edge of the cluster.
*
* Convert from x offset to the byte index of the corresponding
- * character within the text of the layout.
- *
- * Return value: %TRUE if the x index was within the line
+ * character within the text of the layout. If the @x_pos
+ * is negative or greater than the length of the line, then
+ * then the result will be 0, or the last index in the line
+ * depending on the base direction of the layout.
*/
-gboolean
+void
pango_layout_line_x_to_index (PangoLayoutLine *line,
int x_pos,
int *index,
@@ -1459,11 +1750,48 @@ pango_layout_line_x_to_index (PangoLayoutLine *line,
{
GSList *tmp_list;
gint start_pos = 0;
+ gint first_index = 0, last_index;
+ PangoDirection base_dir;
+ gboolean last_trailing;
- g_return_val_if_fail (LINE_IS_VALID (line), FALSE);
+ g_return_if_fail (line != NULL);
+ g_return_if_fail (LINE_IS_VALID (line));
if (!LINE_IS_VALID (line))
- return FALSE;
+ return;
+
+ base_dir = pango_context_get_base_dir (line->layout->context);
+
+ /* Find the last index in the line
+ */
+ tmp_list = line->layout->lines;
+ while (tmp_list->data != line)
+ {
+ first_index += ((PangoLayoutLine *)tmp_list->data)->length;
+ tmp_list = tmp_list->next;
+ }
+
+ last_index = first_index + line->length;
+ last_index = unicode_previous_utf8 (line->layout->text + first_index, line->layout->text + last_index) - line->layout->text;
+
+ /* This is a HACK. If a program only keeps track if cursor (etc)
+ * indices and not the trailing flag, then the trailing index of the
+ * last character on a wrapped line is identical to the leading
+ * index of the next line. Actually, any program keeping cursor
+ * positions with wrapped lines should distinguish leading and
+ * trailing cursors.
+ */
+ last_trailing = tmp_list->next ? 0 : 1;
+
+ if (x_pos < 0)
+ {
+ if (index)
+ *index = (base_dir == PANGO_DIRECTION_LTR) ? first_index : last_index;
+ if (trailing)
+ *trailing = (base_dir == PANGO_DIRECTION_LTR) ? 0 : last_trailing;
+
+ return;
+ }
tmp_list = line->runs;
while (tmp_list)
@@ -1486,14 +1814,17 @@ pango_layout_line_x_to_index (PangoLayoutLine *line,
if (index)
*index = pos + run->item->offset;
- return TRUE;
+ return;
}
start_pos += logical_rect.width;
tmp_list = tmp_list->next;
}
- return FALSE;
+ if (index)
+ *index = (base_dir == PANGO_DIRECTION_LTR) ? last_index : first_index;
+ if (trailing)
+ *trailing = (base_dir == PANGO_DIRECTION_LTR) ? last_trailing : 0;
}
/**
@@ -1780,7 +2111,7 @@ pango_layout_line_new (PangoLayout *layout)
/*
- * NB: The contents of the file implement the exact same algorithm
+ * NB: This implement the exact same algorithm as
* reorder-items.c:pango_reorder_items().
*/
@@ -1856,6 +2187,21 @@ pango_layout_line_reorder (PangoLayoutLine *line)
g_slist_free (logical_runs);
}
+static void
+pango_layout_line_postprocess (PangoLayoutLine *line)
+{
+ /* NB: the runs are in reverse order at this point, since we prepended them to the list
+ */
+
+ /* Reverse the runs
+ */
+ line->runs = g_slist_reverse (line->runs);
+
+ /* Now convert logical to visual order
+ */
+ pango_layout_line_reorder (line);
+}
+
/* This utility function is duplicated here and in pango-layout.c; should it be
* public? Trouble is - what is the appropriate set of properties?
*/
diff --git a/pango/pango-layout.h b/pango/pango-layout.h
index 02fef3b0..2a01055d 100644
--- a/pango/pango-layout.h
+++ b/pango/pango-layout.h
@@ -88,21 +88,27 @@ void pango_layout_get_log_attrs (PangoLayout *layout,
PangoLogAttr **attrs,
gint *n_attrs);
-void pango_layout_index_to_pos (PangoLayout *layout,
- int index,
- PangoRectangle *pos);
-void pango_layout_get_cursor_pos (PangoLayout *layout,
- int index,
- PangoRectangle *strong_pos,
- PangoRectangle *weak_pos);
-gboolean pango_layout_xy_to_index (PangoLayout *layout,
- int x,
- int y,
- int *index,
- gboolean *trailing);
-void pango_layout_get_extents (PangoLayout *layout,
- PangoRectangle *ink_rect,
- PangoRectangle *logical_rect);
+void pango_layout_index_to_pos (PangoLayout *layout,
+ int index,
+ PangoRectangle *pos);
+void pango_layout_get_cursor_pos (PangoLayout *layout,
+ int index,
+ PangoRectangle *strong_pos,
+ PangoRectangle *weak_pos);
+void pango_layout_move_cursor_visually (PangoLayout *layout,
+ int old_index,
+ int old_trailing,
+ int direction,
+ int *new_index,
+ int *new_trailing);
+gboolean pango_layout_xy_to_index (PangoLayout *layout,
+ int x,
+ int y,
+ int *index,
+ gboolean *trailing);
+void pango_layout_get_extents (PangoLayout *layout,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect);
int pango_layout_get_line_count (PangoLayout *layout);
PangoLayoutLine *pango_layout_get_line (PangoLayout *layout,
@@ -111,7 +117,7 @@ GSList * pango_layout_get_lines (PangoLayout *layout);
void pango_layout_line_ref (PangoLayoutLine *line);
void pango_layout_line_unref (PangoLayoutLine *line);
-gboolean pango_layout_line_x_to_index (PangoLayoutLine *line,
+void pango_layout_line_x_to_index (PangoLayoutLine *line,
int x_pos,
int *index,
int *trailing);