[evolution-patches] fairly involved e-text patch
- From: Chris Toshok <toshok ximian com>
- To: evolution-patches ximian com
- Subject: [evolution-patches] fairly involved e-text patch
- Date: 09 May 2003 14:53:18 -0700
The included patch makes e-text use utf8 offsets throughout, converting
to byte offsets only when it needs to copy or needs them for a
particular function (certain pango_layout functions, for instance.)
This fixes at the very least 42188, and also fixes a host of other
issues involving strings made up of non-ascii utf8 characters.
There are other fixes too - scrolling should work better as you move the
cursor around (and now works horizontally *and* vertically), selection
works properly in multi-lined EText's, and down arrow/up arrow actually
take you to the next/previous lines instead of the next/previous
character.
Don't expect to apply this patch and use it with evolution, though..
The model interface has change as well (position/length are now in terms
of utf8 characters, not bytes) so there will be an accompanying
addressbook-only patch later today.
Chris
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gal/ChangeLog,v
retrieving revision 1.770
diff -u -r1.770 ChangeLog
--- ChangeLog 8 May 2003 14:58:56 -0000 1.770
+++ ChangeLog 9 May 2003 21:45:14 -0000
@@ -1,3 +1,79 @@
+2003-05-09 Chris Toshok <toshok ximian com>
+
+ * gal/e-text/e-text.c (reset_layout_attrs): we need to convert the
+ start/end bounds of the object to byte indices for the attribute.
+ (reset_layout): in the layout == NULL case don't create the layout
+ then immediately set it again with the same text. also, we need
+ to convert selection_start to a byte index before calling
+ pango_layout_get_cursor_pos.
+ (e_text_draw): remove some #ifdef 0'd code, move the calculation
+ of our initial clip_rect below the xpos/ypos assignments so we
+ don't duplicate the expression. Fix the selection drawing in the
+ multiline case so that it actually works, instead of assuming that
+ all ETexts only have 1 line *boggle*.
+ (get_position_from_xy): this needs to return a utf8 offset.
+ (e_text_copy_clipboard): convert sel_start/sel_end to byte indices
+ before copying.
+ (primary_get_cb): same.
+ (paste_received): validate the input here, and drop the length
+ parameter from e_text_insert.
+ (next_word): convert from an utf8 offset on entry to this
+ function, and return a utf8 offset when we're done. also, remove
+ the call the g_unichar_validate. we validate at all points where
+ text is inserted.
+ (find_offset_into_line): new function used in the backward/forward
+ line code. find the utf8 offset into a line (the number of utf8
+ characters from a prior \n or beginning of the string.)
+ (_get_position): in general there are lots of changes here because
+ text->selection_start/text->selection_end are utf8 offsets, note
+ byte offsets. fix E_TEP_START_OF_LINE so that hitting Ctrl-a when
+ you're at the beginning of a line doesn't take you to the
+ beginning of the previous line. fix E_TEP_END_OF_LINE in an
+ analogous fashion. for E_TEP_FORWARD_CHARACTER we just increment
+ by 1. for E_TEP_BACKWARD_CHARACTER we just decrement by 1. for
+ E_TEP_BACKWARD_WORD we drop the g_unichar_validate call and
+ simplify things a bit. reimplement
+ E_TEP_FORWARD_LINE/E_TEP_BACKWARD_LINE so they find the current
+ offset into the line, then scan forward/backward for the next/prev
+ line, and put us at the right offset on that line. fix
+ E_TEP_SELECT_WORD so double clicking in the space between words
+ doesn't select both words - if you double click on the trailing
+ edge of the space, it selects the next word. leading edge selects
+ the previous one. for E_TEP_SELECT_ALL use g_utf8_strlen.
+ (e_text_insert): everything that calls this passes a \0 terminated
+ string, so we assume it's \0 terminated (the old code did as well,
+ with calls to strlen) and drop the length parameter. also make
+ sure this is all utf8 happy.
+ (capitalize): use g_utf8_offset_to_pointer instead of just adding
+ text->text and start/end, and remove the validate call. also fix
+ the call to e_text_model_delete and use e_text_model_insert_length
+ instead of e_text_model_insert.
+ (e_text_command): for E_TEP_INSERT, validate the input. for
+ E_TEP_CAPS just use MAX instead of the neat little hack. also,
+ fix the scrolling so that it scrolls properly in both X and Y
+ directions (there are still some hiccups but it's much much better
+ than previously).
+ (e_text_commit_cb): validate the input here.
+
+ * gal/e-text/e-text-model.c (struct _ETextModelPrivate): just use
+ a GString here and get rid of MAX_LENGTH.
+ (e_text_model_dispose): free GString.
+ (e_text_model_real_validate_position): clean this up a bit.
+ (e_text_model_real_get_text): return the contents of the GString.
+ (e_text_model_real_get_text_length): use g_utf8_strlen here.
+ (e_text_model_real_set_text): convert to GString
+ (e_text_model_real_insert): just call e_text_model_insert_length
+ here instead of duplicating the function.
+ (e_text_model_real_insert_length): convert to utf8/gstring.
+ i.e. convert @position and @length to a bytes and use
+ g_string_insert_len.
+ (e_text_model_real_delete): same, with g_string_erase.
+ (e_text_model_get_text_length): use g_utf8_strlen
+ (e_text_model_strdup_nth_object): convert the length of the object
+ to bytes before copying.
+ (e_text_model_get_nth_object_bounds): calculate start/end properly
+ for utf8.
+
2003-05-08 Radek Doulik <rodo ximian com>
* gal/util/e-util.c (e_gettext): use E_I18N_DOMAIN
Index: gal/e-text/e-text-model.c
===================================================================
RCS file: /cvs/gnome/gal/gal/e-text/e-text-model.c,v
retrieving revision 1.16
diff -u -r1.16 e-text-model.c
--- gal/e-text/e-text-model.c 17 Nov 2002 05:40:12 -0000 1.16
+++ gal/e-text/e-text-model.c 9 May 2003 21:45:14 -0000
@@ -32,8 +32,6 @@
#include "e-text-model.h"
#include "gal/util/e-util.h"
-#define MAX_LENGTH (2047)
-
enum {
E_TEXT_MODEL_CHANGED,
E_TEXT_MODEL_REPOSITION,
@@ -45,8 +43,7 @@
static guint e_text_model_signals[E_TEXT_MODEL_LAST_SIGNAL] = { 0 };
struct _ETextModelPrivate {
- gchar *text;
- gint len;
+ GString *text;
};
static void e_text_model_class_init (ETextModelClass *class);
@@ -157,8 +154,7 @@
e_text_model_init (ETextModel *model)
{
model->priv = g_new0 (struct _ETextModelPrivate, 1);
- model->priv->text = g_strdup ("");
- model->priv->len = 0;
+ model->priv->text = g_string_new ("");
}
/* Dispose handler for the text item */
@@ -173,7 +169,7 @@
model = E_TEXT_MODEL (object);
if (model->priv) {
- g_free (model->priv->text);
+ g_string_free (model->priv->text, TRUE);
g_free (model->priv);
model->priv = NULL;
@@ -186,11 +182,11 @@
static gint
e_text_model_real_validate_position (ETextModel *model, gint pos)
{
- gint len;
+ gint len = e_text_model_get_text_length (model);
if (pos < 0)
pos = 0;
- else if (pos > ( len = e_text_model_get_text_length (model) ))
+ else if (pos > len)
pos = len;
return pos;
@@ -200,7 +196,7 @@
e_text_model_real_get_text (ETextModel *model)
{
if (model->priv->text)
- return model->priv->text;
+ return model->priv->text->str;
else
return "";
}
@@ -208,10 +204,7 @@
static gint
e_text_model_real_get_text_length (ETextModel *model)
{
- if (model->priv->len < 0)
- model->priv->len = strlen (e_text_model_get_text (model));
-
- return model->priv->len;
+ return g_utf8_strlen (model->priv->text->str, -1);
}
static void
@@ -221,18 +214,13 @@
gboolean changed = FALSE;
if (text == NULL) {
+ changed = (*model->priv->text->str != '\0');
- changed = (model->priv->text != NULL);
-
- g_free (model->priv->text);
- model->priv->text = NULL;
- model->priv->len = -1;
+ g_string_set_size (model->priv->text, 0);
- } else if (model->priv->text == NULL || strcmp (model->priv->text, text)) {
+ } else if (*model->priv->text->str == '\0' || strcmp (model->priv->text->str, text)) {
- g_free (model->priv->text);
- model->priv->text = g_strndup (text, MAX_LENGTH);
- model->priv->len = -1;
+ g_string_assign (model->priv->text, text);
changed = TRUE;
}
@@ -248,41 +236,7 @@
static void
e_text_model_real_insert (ETextModel *model, gint position, const gchar *text)
{
- EReposInsertShift repos;
- gchar *new_text;
- gint length;
-
- if (model->priv->len < 0)
- e_text_model_real_get_text_length (model);
- length = strlen(text);
-
- if (length + model->priv->len > MAX_LENGTH)
- length = MAX_LENGTH - model->priv->len;
- if (length <= 0)
- return;
-
- /* Can't use g_strdup_printf here because on some systems
- printf ("%.*s"); is locale dependent. */
- new_text = e_strdup_append_strings (model->priv->text, position,
- text, length,
- model->priv->text + position, -1,
- NULL);
-
- if (model->priv->text)
- g_free (model->priv->text);
-
- model->priv->text = new_text;
-
- if (model->priv->len >= 0)
- model->priv->len += length;
-
- e_text_model_changed (model);
-
- repos.model = model;
- repos.pos = position;
- repos.len = length;
-
- e_text_model_reposition (model, e_repos_insert_shift, &repos);
+ e_text_model_insert_length (model, position, text, strlen (text));
}
static void
@@ -290,34 +244,31 @@
{
EReposInsertShift repos;
gchar *new_text;
+ int model_len = e_text_model_real_get_text_length (model);
+ char *offs;
+ const char *p;
+ int byte_length, l;
- if (model->priv->len < 0)
- e_text_model_real_get_text_length (model);
-
- if (length + model->priv->len > MAX_LENGTH)
- length = MAX_LENGTH - model->priv->len;
- if (length <= 0)
+ if (position > model_len)
return;
- /* Can't use g_strdup_printf here because on some systems
- printf ("%.*s"); is locale dependent. */
- new_text = e_strdup_append_strings (model->priv->text, position,
- text, length,
- model->priv->text + position, -1,
- NULL);
+ offs = g_utf8_offset_to_pointer (model->priv->text->str, position);
- if (model->priv->text)
- g_free (model->priv->text);
- model->priv->text = new_text;
+ for (p = text, l = 0;
+ l < length;
+ p = g_utf8_next_char (p), l ++) ;
- if (model->priv->len >= 0)
- model->priv->len += length;
+ byte_length = p - text;
+
+ g_string_insert_len (model->priv->text,
+ offs - model->priv->text->str,
+ text, byte_length);
e_text_model_changed (model);
repos.model = model;
- repos.pos = position;
- repos.len = length;
+ repos.pos = position;
+ repos.len = length;
e_text_model_reposition (model, e_repos_insert_shift, &repos);
}
@@ -326,11 +277,21 @@
e_text_model_real_delete (ETextModel *model, gint position, gint length)
{
EReposDeleteShift repos;
-
- memmove (model->priv->text + position, model->priv->text + position + length, strlen (model->priv->text + position + length) + 1);
-
- if (model->priv->len >= 0)
- model->priv->len -= length;
+ int byte_position, byte_length;
+ char *offs, *p;
+ int l;
+
+ offs = g_utf8_offset_to_pointer (model->priv->text->str, position);
+ byte_position = offs - model->priv->text->str;
+
+ for (p = offs, l = 0;
+ l < length;
+ p = g_utf8_next_char (p), l ++) ;
+
+ byte_length = p - offs;
+
+ g_string_erase (model->priv->text,
+ byte_position, byte_length);
e_text_model_changed (model);
@@ -415,7 +376,7 @@
#ifdef PARANOID_DEBUGGING
const gchar *str = e_text_model_get_text (model);
- gint len2 = str ? strlen (str) : 0;
+ gint len2 = str ? g_utf8_strlen (str, -1) : 0;
if (len != len)
g_error ("\"%s\" length reported as %d, not %d.", str, len, len2);
#endif
@@ -425,7 +386,7 @@
} else {
/* Calculate length the old-fashioned way... */
const gchar *str = e_text_model_get_text (model);
- return str ? strlen (str) : 0;
+ return str ? g_utf8_strlen (str, -1) : 0;
}
}
@@ -548,8 +509,15 @@
g_return_val_if_fail (E_IS_TEXT_MODEL (model), NULL);
obj = e_text_model_get_nth_object (model, n, &len);
-
- return obj ? g_strndup (obj, n) : NULL;
+
+ if (obj) {
+ gint byte_len;
+ byte_len = g_utf8_offset_to_pointer (obj, len) - obj;
+ return g_strndup (obj, byte_len);
+ }
+ else {
+ return NULL;
+ }
}
void
@@ -567,9 +535,9 @@
g_return_if_fail (obj != NULL);
if (start)
- *start = obj - txt;
+ *start = g_utf8_pointer_to_offset (txt, obj);
if (end)
- *end = obj - txt + len;
+ *end = *start + len;
}
gint
Index: gal/e-text/e-text.c
===================================================================
RCS file: /cvs/gnome/gal/gal/e-text/e-text.c,v
retrieving revision 1.144
diff -u -r1.144 e-text.c
--- gal/e-text/e-text.c 5 May 2003 20:37:20 -0000 1.144
+++ gal/e-text/e-text.c 9 May 2003 21:45:15 -0000
@@ -136,7 +136,7 @@
static void e_text_update_primary_selection (EText *text);
static void e_text_paste (EText *text, GdkAtom selection);
-static void e_text_insert(EText *text, const char *string, int value);
+static void e_text_insert(EText *text, const char *string);
/* GtkEditable Methods */
static void e_text_editable_do_insert_text (GtkEditable *editable,
@@ -300,8 +300,8 @@
e_text_model_get_nth_object_bounds (text->model, i, &start_pos, &end_pos);
- attr->start_index = start_pos;
- attr->end_index = end_pos;
+ attr->start_index = g_utf8_offset_to_pointer (text->text, start_pos) - text->text;
+ attr->end_index = g_utf8_offset_to_pointer (text->text, end_pos) - text->text;
pango_attr_list_insert (attrs, attr);
}
@@ -347,15 +347,19 @@
static void
reset_layout (EText *text)
{
- create_layout (text);
-
- pango_layout_set_text (text->layout, text->text, -1);
- reset_layout_attrs (text);
+ if (text->layout == NULL) {
+ create_layout (text);
+ }
+ else {
+ pango_layout_set_text (text->layout, text->text, -1);
+ reset_layout_attrs (text);
+ }
if (!text->button_down) {
PangoRectangle strong_pos, weak_pos;
+ char *offs = g_utf8_offset_to_pointer (text->text, text->selection_start);
- pango_layout_get_cursor_pos (text->layout, text->selection_end, &strong_pos, &weak_pos);
+ pango_layout_get_cursor_pos (text->layout, offs - text->text, &strong_pos, &weak_pos);
if (strong_pos.x != weak_pos.x ||
strong_pos.y != weak_pos.y ||
@@ -1321,26 +1325,7 @@
GTK_STATE_NORMAL, GTK_SHADOW_IN,
NULL, widget, "entry",
thisx, thisy, thiswidth, thisheight);
-
-#if 0
- if (text->editing) {
- thisx += 1;
- thisy += 1;
- thiswidth -= 2;
- thisheight -= 2;
- /*
- * Chris: I am here "filling in" for the additions
- * and substractions done in the previous if (text->editing).
- * but you might have other plans for this. Please enlighten
- * me as to whether it should be:
- * thiswidth + 2 or thiswidth + 1.
- */
- gtk_paint_focus (widget->style, drawable, GTK_STATE_NORMAL,
- NULL, widget, "entry",
- thisx, thisy, thiswidth, thisheight);
- }
-#endif
}
if (text->draw_background) {
@@ -1450,17 +1435,6 @@
if (!text->text)
return;
- clip_rect = NULL;
- if (text->clip) {
- rect.x = text->clip_cx - x;
- rect.y = text->clip_cy - y;
- rect.width = text->clip_cwidth;
- rect.height = text->clip_cheight;
-
- gdk_gc_set_clip_rectangle (main_gc, &rect);
- clip_rect = ▭
- }
-
if (text->stipple)
gnome_canvas_set_stipple_origin (item->canvas, main_gc);
@@ -1470,6 +1444,17 @@
xpos -= x;
ypos -= y;
+ clip_rect = NULL;
+ if (text->clip) {
+ rect.x = xpos;
+ rect.y = ypos;
+ rect.width = text->clip_cwidth;
+ rect.height = text->clip_cheight;
+
+ gdk_gc_set_clip_rectangle (main_gc, &rect);
+ clip_rect = ▭
+ }
+
if (text->editing) {
xpos -= text->xofs_edit;
ypos -= text->yofs_edit;
@@ -1481,17 +1466,18 @@
if (text->editing) {
if (text->selection_start != text->selection_end) {
- int start_index, end_index;
- PangoLayoutLine *line;
- gint *ranges;
- gint n_ranges, i;
- PangoRectangle logical_rect;
+ PangoLayoutIter *iter;
GdkRegion *clip_region = gdk_region_new ();
GdkGC *selection_gc;
GdkGC *text_gc;
+ int start_index, end_index;
start_index = MIN (text->selection_start, text->selection_end);
- end_index = text->selection_start ^ text->selection_end ^ start_index;
+ end_index = MAX (text->selection_start, text->selection_end);
+
+ /* convert these into byte indices */
+ start_index = g_utf8_offset_to_pointer(text->text, start_index) - text->text;
+ end_index = g_utf8_offset_to_pointer(text->text, end_index) - text->text;
if (text->has_selection) {
selection_gc = widget->style->base_gc [GTK_STATE_SELECTED];
@@ -1503,25 +1489,50 @@
gdk_gc_set_clip_rectangle (selection_gc, clip_rect);
- line = pango_layout_get_lines (text->layout)->data;
+ iter = pango_layout_get_iter (text->layout);
+
+ do {
+ PangoLayoutLine *line = pango_layout_iter_get_line (iter);
+ gint n_ranges, i;
+ gint *ranges;
+ int y0, y1;
+ int s, e;
- pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
+ if (start_index < line->start_index + line->length
+ && end_index > line->start_index) {
+
+ if (start_index <= line->start_index)
+ s = line->start_index;
+ else
+ s = start_index;
- pango_layout_get_extents (text->layout, NULL, &logical_rect);
+ if (end_index > line->start_index + line->length)
+ e = line->start_index + line->length;
+ else
+ e = end_index;
- for (i=0; i < n_ranges; i++) {
- GdkRectangle sel_rect;
+ pango_layout_line_get_x_ranges (line, s, e, &ranges, &n_ranges);
- sel_rect.x = xpos + ranges[2*i] / PANGO_SCALE;
- sel_rect.y = ypos;
- sel_rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
- sel_rect.height = logical_rect.height / PANGO_SCALE;
+ pango_layout_iter_get_line_yrange (iter, &y0, &y1);
- gdk_draw_rectangle (drawable, selection_gc, TRUE,
- sel_rect.x, sel_rect.y, sel_rect.width, sel_rect.height);
+ for (i=0; i < n_ranges; i++) {
+ GdkRectangle sel_rect;
- gdk_region_union_with_rect (clip_region, &sel_rect);
- }
+ sel_rect.x = xpos + PANGO_PIXELS (ranges[2*i]);
+ sel_rect.y = ypos + PANGO_PIXELS (y0);
+ sel_rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
+ sel_rect.height = (y1 - y0 + PANGO_SCALE / 2) / PANGO_SCALE;
+
+ gdk_draw_rectangle (drawable, selection_gc, TRUE,
+ sel_rect.x, sel_rect.y, sel_rect.width, sel_rect.height);
+
+ gdk_region_union_with_rect (clip_region, &sel_rect);
+ }
+ g_free (ranges);
+ }
+ } while (pango_layout_iter_next_line (iter));
+
+ pango_layout_iter_free (iter);
if (clip_rect) {
GdkRegion *rect_region = gdk_region_rectangle (clip_rect);
@@ -1538,11 +1549,12 @@
gdk_gc_set_clip_region (selection_gc, NULL);
gdk_region_destroy (clip_region);
- g_free (ranges);
} else {
if (text->show_cursor) {
PangoRectangle strong_pos, weak_pos;
- pango_layout_get_cursor_pos (text->layout, text->selection_start, &strong_pos, &weak_pos);
+ char *offs = g_utf8_offset_to_pointer (text->text, text->selection_start);
+
+ pango_layout_get_cursor_pos (text->layout, offs - text->text, &strong_pos, &weak_pos);
draw_pango_rectangle (drawable, main_gc, xpos, ypos, strong_pos);
if (strong_pos.x != weak_pos.x ||
strong_pos.y != weak_pos.y ||
@@ -1700,7 +1712,7 @@
pango_layout_xy_to_index (text->layout, x * PANGO_SCALE, y * PANGO_SCALE, &index, &trailing);
- return g_utf8_offset_to_pointer (text->text + index, trailing) - text->text;
+ return g_utf8_pointer_to_offset (text->text, text->text + index + trailing);
}
#define SCROLL_WAIT_TIME 30000
@@ -2333,6 +2345,10 @@
selection_start_pos = MIN (text->selection_start, text->selection_end);
selection_end_pos = MAX (text->selection_start, text->selection_end);
+ /* convert sel_start/sel_end to byte indices */
+ selection_start_pos = g_utf8_offset_to_pointer (text->text, selection_start_pos) - text->text;
+ selection_end_pos = g_utf8_offset_to_pointer (text->text, selection_end_pos) - text->text;
+
str = g_strndup (text->text + selection_start_pos,
selection_end_pos - selection_start_pos);
@@ -2403,6 +2419,10 @@
sel_start = MIN(text->selection_start, text->selection_end);
sel_end = MAX(text->selection_start, text->selection_end);
+ /* convert sel_start/sel_end to byte indices */
+ sel_start = g_utf8_offset_to_pointer (text->text, sel_start) - text->text;
+ sel_end = g_utf8_offset_to_pointer (text->text, sel_end) - text->text;
+
if (sel_start != sel_end) {
gchar *str = g_strndup (text->text + sel_start,
sel_end - sel_start);
@@ -2459,11 +2479,11 @@
{
EText *etext = E_TEXT (data);
- if (text) {
+ if (text && g_utf8_validate (text, strlen (text), NULL)) {
if (etext->selection_end != etext->selection_start)
e_text_delete_selection (etext);
- e_text_insert (etext, text, strlen (text));
+ e_text_insert (etext, text);
}
g_object_unref (etext);
@@ -2611,26 +2631,60 @@
static int
next_word (EText *text, int start)
{
- char *p;
+ char *p = g_utf8_offset_to_pointer (text->text, start);
int length;
- length = strlen (text->text);
+ length = g_utf8_strlen (text->text, -1);
if (start >= length) {
return length;
} else {
- p = g_utf8_next_char (text->text + start);
+ p = g_utf8_next_char (p);
+ start++;
- while (p && *p && g_unichar_validate (g_utf8_get_char (p))) {
+ while (p && *p) {
gunichar unival = g_utf8_get_char (p);
if (g_unichar_isspace (unival)) {
- return p - text->text;
- } else
+ return start + 1;
+ }
+ else {
p = g_utf8_next_char (p);
+ start++;
+ }
}
}
- return p - text->text;
+ return g_utf8_pointer_to_offset (text->text, p);
+}
+
+static int
+find_offset_into_line (EText *text, int offset_into_text, char **start_of_line)
+{
+ char *p;
+
+ p = g_utf8_offset_to_pointer (text->text, offset_into_text);
+
+ if (p == text->text) {
+ if (start_of_line)
+ *start_of_line = (char*)text->text;
+ return 0;
+ }
+ else {
+ p = g_utf8_find_prev_char (text->text, p);
+
+ while (p && p > text->text) {
+ if (*p == '\n') {
+ if (start_of_line)
+ *start_of_line = p+1;
+ return offset_into_text - g_utf8_pointer_to_offset (text->text, p + 1);
+ }
+ p = g_utf8_find_prev_char (text->text, p);
+ }
+
+ if (start_of_line)
+ *start_of_line = (char*)text->text;
+ return offset_into_text;
+ }
}
static int
@@ -2662,17 +2716,16 @@
case E_TEP_START_OF_LINE:
- new_pos = 0;
-
if (text->selection_end >= 1) {
- p = g_utf8_find_prev_char (text->text, text->text + text->selection_end);
+ p = g_utf8_offset_to_pointer (text->text, text->selection_end);
if (p != text->text) {
p = g_utf8_find_prev_char (text->text, p);
-
- while (p && p > text->text && !new_pos) {
- if (*p == '\n')
- new_pos = p - text->text + 1;
+ while (p && p > text->text) {
+ if (*p == '\n') {
+ new_pos = g_utf8_pointer_to_offset (text->text, p) + 1;
+ break;
+ }
p = g_utf8_find_prev_char (text->text, p);
}
}
@@ -2682,17 +2735,17 @@
case E_TEP_END_OF_LINE:
new_pos = -1;
- length = strlen (text->text);
+ length = g_utf8_strlen (text->text, -1);
if (text->selection_end >= length) {
new_pos = length;
} else {
- p = g_utf8_next_char (text->text + text->selection_end);
+ p = g_utf8_offset_to_pointer (text->text, text->selection_end);
- while (p && *p && g_unichar_validate (g_utf8_get_char (p))) {
+ while (p && *p) {
if (*p == '\n') {
- new_pos = p - text->text;
+ new_pos = g_utf8_pointer_to_offset (text->text, p);
p = NULL;
} else
p = g_utf8_next_char (p);
@@ -2700,29 +2753,24 @@
}
if (new_pos == -1)
- new_pos = p - text->text;
+ new_pos = g_utf8_pointer_to_offset (text->text, p);
break;
case E_TEP_FORWARD_CHARACTER:
- length = strlen (text->text);
+ length = g_utf8_strlen (text->text, -1);
- if (text->selection_end >= length) {
+ if (text->selection_end >= length)
new_pos = length;
- } else {
- p = g_utf8_next_char (text->text + text->selection_end);
- new_pos = p - text->text;
- }
+ else
+ new_pos = text->selection_end + 1;
break;
case E_TEP_BACKWARD_CHARACTER:
new_pos = 0;
if (text->selection_end >= 1) {
- p = g_utf8_find_prev_char (text->text, text->text + text->selection_end);
-
- if (p != NULL)
- new_pos = p - text->text;
+ new_pos = text->selection_end - 1;
}
break;
@@ -2734,61 +2782,105 @@
case E_TEP_BACKWARD_WORD:
new_pos = 0;
if (text->selection_end >= 1) {
- p = g_utf8_find_prev_char (text->text, text->text + text->selection_end);
+ int pos = text->selection_end;
+
+ p = g_utf8_find_prev_char (text->text, g_utf8_offset_to_pointer (text->text, text->selection_end));
+ pos --;
+
if (p != text->text) {
p = g_utf8_find_prev_char (text->text, p);
+ pos --;
- while (p && p > text->text && g_unichar_validate (g_utf8_get_char (p))) {
+ while (p && p > text->text) {
unival = g_utf8_get_char (p);
if (g_unichar_isspace (unival)) {
- new_pos = g_utf8_next_char (p) - text->text;
+ new_pos = pos + 1;
p = NULL;
- } else
+ }
+ else {
p = g_utf8_find_prev_char (text->text, p);
+ pos --;
+ }
}
}
}
break;
- case E_TEP_FORWARD_LINE:
- pango_layout_move_cursor_visually (text->layout,
- TRUE,
- text->selection_end, 0,
- 1,
- &index, &trailing);
- index = g_utf8_offset_to_pointer (text->text + index, trailing) - text->text;
- if (index < 0) {
- new_pos = 0;
- } else {
- length = strlen (text->text);
- if (index >= length)
- new_pos = length;
- else
- new_pos = index;
+ case E_TEP_FORWARD_LINE: {
+ int l;
+ PangoLayoutLine *line, *next_line;
+ int offset_into_line;
+ int next_line_length;
+ char *p;
+
+ offset_into_line = find_offset_into_line (text, text->selection_end, NULL);
+ if (offset_into_line == -1)
+ return text->selection_end;
+
+ /* now we search forward til we hit a \n, and then
+ offset_into_line more characters */
+ p = g_utf8_offset_to_pointer (text->text, text->selection_end);
+ while (p && *p) {
+ if (*p == '\n')
+ break;
+ p = g_utf8_next_char (p);
}
- break;
+ if (p && *p == '\n') {
+ /* now we loop forward offset_into_line
+ characters, or until we hit \n or \0 */
- case E_TEP_BACKWARD_LINE:
- pango_layout_move_cursor_visually (text->layout,
- TRUE,
- text->selection_end, 0,
- -1,
- &index, &trailing);
- index = g_utf8_offset_to_pointer (text->text + index, trailing) - text->text;
- if (index < 0) {
- new_pos = 0;
- } else {
- length = strlen (text->text);
- if (index >= length)
- new_pos = length;
- else
- new_pos = index;
+ p = g_utf8_next_char (p);
+ while (offset_into_line > 0 && p && *p != '\n' && *p != '\0') {
+ p = g_utf8_next_char (p);
+ offset_into_line --;
+ }
}
+
+ /* at this point, p points to the new location,
+ convert it to an offset and we're done */
+ new_pos = g_utf8_pointer_to_offset (text->text, p);
break;
+ }
+ case E_TEP_BACKWARD_LINE: {
+ char *p, *prev = NULL;
+ int offset_into_line = find_offset_into_line (text, text->selection_end, &p);
- case E_TEP_SELECT_WORD:
+ if (offset_into_line == -1)
+ return text->selection_end;
+
+ /* p points to the first character on our line. if we
+ have a \n before it, skip it and scan til we hit
+ the next one */
+ if (p != text->text) {
+ p = g_utf8_find_prev_char (text->text, p);
+ if (*p == '\n') {
+ p = g_utf8_find_prev_char (text->text, p);
+ while (p > text->text) {
+ if (*p == '\n') {
+ p ++;
+ break;
+ }
+ p = g_utf8_find_prev_char (text->text, p);
+ }
+ }
+ }
+
+ /* at this point 'p' points to the start of the
+ previous line, move forward 'offset_into_line'
+ times. */
+
+ while (offset_into_line > 0 && p && *p != '\n' && *p != '\0') {
+ p = g_utf8_next_char (p);
+ offset_into_line --;
+ }
+ /* at this point, p points to the new location,
+ convert it to an offset and we're done */
+ new_pos = g_utf8_pointer_to_offset (text->text, p);
+ break;
+ }
+ case E_TEP_SELECT_WORD:
/* This is a silly hack to cause double-clicking on an object
to activate that object.
(Normally, double click == select word, which is why this is here.) */
@@ -2800,20 +2892,16 @@
break;
}
-
if (text->selection_end < 1) {
new_pos = 0;
break;
}
- p = g_utf8_find_prev_char (text->text, text->text + text->selection_end);
- if (p == text->text) {
- new_pos = 0;
- break;
- }
+ p = g_utf8_offset_to_pointer (text->text, text->selection_end);
+
p = g_utf8_find_prev_char (text->text, p);
- while (p && p > text->text && g_unichar_validate (g_utf8_get_char (p))) {
+ while (p && p > text->text) {
unival = g_utf8_get_char (p);
if (g_unichar_isspace (unival)) {
p = g_utf8_next_char (p);
@@ -2825,36 +2913,35 @@
if (!p)
text->selection_start = 0;
else
- text->selection_start = p - text->text;
+ text->selection_start = g_utf8_pointer_to_offset (text->text, p);
text->selection_start = e_text_model_validate_position (text->model, text->selection_start);
- length = strlen (text->text);
+ length = g_utf8_strlen (text->text, -1);
if (text->selection_end >= length) {
new_pos = length;
break;
}
- p = g_utf8_next_char (text->text + text->selection_end);
-
- while (p && *p && g_unichar_validate (g_utf8_get_char (p))) {
+ p = g_utf8_offset_to_pointer (text->text, text->selection_end);
+ while (p && *p) {
unival = g_utf8_get_char (p);
if (g_unichar_isspace (unival)) {
- new_pos = p - text->text;
- p = NULL;
+ new_pos = g_utf8_pointer_to_offset (text->text, p);
+ break;
} else
p = g_utf8_next_char (p);
}
- if (p)
- new_pos = p - text->text;
+ if (!new_pos)
+ new_pos = g_utf8_strlen (text->text, -1);
return new_pos;
case E_TEP_SELECT_ALL:
text->selection_start = 0;
- new_pos = strlen (text->text);
+ new_pos = g_utf8_strlen (text->text, -1);
break;
case E_TEP_FORWARD_PARAGRAPH:
@@ -2876,32 +2963,37 @@
}
static void
-e_text_insert(EText *text, const char *string, int value)
+e_text_insert(EText *text, const char *string)
{
- if (value > 0) {
+ int len = strlen (string);
+
+ if (len > 0) {
+ int utf8len = 0;
+
if (!text->allow_newlines) {
const char *i;
- for (i = string; *i; i++) {
- if (*i == '\n') {
- char *new_string = g_malloc (strlen (string) + 1);
- char *j = new_string;
- for (i = string; *i; i++) {
- if (*i != '\n')
- *(j++) = *i;
- }
- *j = 0;
- e_text_model_insert_length(text->model, text->selection_start, new_string, j - new_string);
- g_free (new_string);
- return;
+ char *new_string = g_malloc (len + 1);
+ char *j = new_string;
+
+ for (i = string; *i; i = g_utf8_next_char(i)) {
+ if (*i != '\n') {
+ gunichar c;
+ int charlen;
+
+ c = g_utf8_get_char (i);
+ charlen = g_unichar_to_utf8 (c, j);
+ j += charlen;
+ utf8len++;
}
}
+ *j = 0;
+ e_text_model_insert_length(text->model, text->selection_start, new_string, utf8len);
+ g_free (new_string);
+ }
+ else {
+ utf8len = g_utf8_strlen (string, -1);
+ e_text_model_insert_length(text->model, text->selection_start, string, utf8len);
}
- e_text_model_insert_length(text->model, text->selection_start, string, value);
-
-#if 0
- text->selection_start += value;
- text->selection_end = text->selection_start;
-#endif
}
}
@@ -2909,12 +3001,13 @@
capitalize (EText *text, int start, int end, ETextEventProcessorCaps type)
{
gboolean first = TRUE;
- const char *p = text->text + start;
- const char *text_end = text->text + end;
- char *new_text = g_new0 (char, g_utf8_strlen (text->text + start, start - end) * 6);
+ const char *p = g_utf8_offset_to_pointer (text->text, start);
+ const char *text_end = g_utf8_offset_to_pointer (text->text, end);
+ int utf8len = text_end - p;
+ char *new_text = g_new0 (char, utf8len * 6);
char *output = new_text;
- while (p && *p && p < text_end && g_unichar_validate (g_utf8_get_char (p))) {
+ while (p && *p && p < text_end) {
gunichar unival = g_utf8_get_char (p);
gunichar newval = unival;
@@ -2944,8 +3037,8 @@
}
*output = 0;
- e_text_model_delete (text->model, start, end - start);
- e_text_model_insert (text->model, start, new_text);
+ e_text_model_delete (text->model, start, utf8len);
+ e_text_model_insert_length (text->model, start, new_text, utf8len);
g_free (new_text);
}
@@ -2990,12 +3083,14 @@
break;
case E_TEP_INSERT:
- if (text->selection_end != text->selection_start) {
- e_text_delete_selection(text);
- }
- e_text_insert(text, command->string, command->value);
- if (text->timer) {
- g_timer_reset(text->timer);
+ if (g_utf8_validate (command->string, command->value, NULL)) {
+ if (text->selection_end != text->selection_start) {
+ e_text_delete_selection(text);
+ }
+ e_text_insert(text, command->string);
+ if (text->timer) {
+ g_timer_reset(text->timer);
+ }
}
break;
case E_TEP_COPY:
@@ -3045,7 +3140,7 @@
capitalize (text, text->selection_start, next_word (text, text->selection_start), command->value);
} else {
int selection_start = MIN (text->selection_start, text->selection_end);
- int selection_end = text->selection_start + text->selection_end - selection_start; /* Slightly faster than MAX */
+ int selection_end = MAX (text->selection_start, text->selection_end);
capitalize (text, selection_start, selection_end, command->value);
}
break;
@@ -3055,36 +3150,51 @@
}
if (scroll && !text->button_down) {
+ /* XXX do we really need the @trailing logic here? if
+ we don't we can scrap the loop and just use
+ pango_layout_index_to_pos */
int i;
- int count = pango_layout_get_line_count (text->layout);
PangoLayoutLine *cur_line = NULL;
- int selection_index = use_start ? text->selection_start : text->selection_end;
+ int selection_index;
+ PangoLayoutIter *iter = pango_layout_get_iter (text->layout);
+
+ selection_index = use_start ? text->selection_start : text->selection_end;
+ /* convert to a byte index */
+ selection_index = g_utf8_offset_to_pointer (text->text, selection_index) - text->text;
+
+ do {
+ PangoLayoutLine *line = pango_layout_iter_get_line (iter);
- for (i = 0; i < count; i ++) {
- PangoLayoutLine *line = pango_layout_get_line (text->layout, i);
if (selection_index >= line->start_index && selection_index <= line->start_index + line->length) {
/* found the line with the start of the selection */
cur_line = line;
break;
}
- }
+
+ } while (pango_layout_iter_next_line (iter));
if (cur_line) {
- int xpos;
- double clip_width;
+ int xpos, ypos;
+ double clip_width, clip_height;
gboolean trailing = FALSE;
+ PangoRectangle pango_pos;
- if (selection_index == cur_line->start_index + cur_line->length) {
+ if (selection_index > 0 && selection_index == cur_line->start_index + cur_line->length) {
selection_index--;
trailing = TRUE;
}
- pango_layout_line_index_to_x (cur_line, selection_index - cur_line->start_index,
- trailing, &xpos);
+ pango_layout_index_to_pos (text->layout, selection_index, &pango_pos);
+
+ pango_pos.x = PANGO_PIXELS (pango_pos.x);
+ pango_pos.y = PANGO_PIXELS (pango_pos.y);
+ pango_pos.width = (pango_pos.width + PANGO_SCALE / 2) / PANGO_SCALE;
+ pango_pos.height = (pango_pos.height + PANGO_SCALE / 2) / PANGO_SCALE;
- xpos = PANGO_PIXELS (xpos);
+ /* scroll for X */
+ xpos = pango_pos.x; /* + (trailing ? 0 : pango_pos.width);*/
- if (xpos < text->xofs_edit) {
+ if (xpos + 2 < text->xofs_edit) {
text->xofs_edit = xpos;
}
@@ -3095,10 +3205,37 @@
clip_width = 0;
}
- if (2 + xpos - clip_width > text->xofs_edit) {
- text->xofs_edit = 2 + xpos - clip_width;
+ if (xpos + pango_pos.width - clip_width > text->xofs_edit) {
+ text->xofs_edit = xpos + pango_pos.width - clip_width;
+ }
+
+ /* scroll for Y */
+ if (pango_pos.y + 2 < text->yofs_edit) {
+ ypos = pango_pos.y;
+ text->yofs_edit = ypos;
+ }
+ else {
+ ypos = pango_pos.y + pango_pos.height;
+ }
+
+ if ( text->clip_height < 0 )
+ clip_height = text->height;
+ else
+ clip_height = text->clip_height;
+
+ if (clip_height >= 0 && text->draw_borders) {
+ clip_height -= 6;
+ if (clip_height < 0)
+ clip_height = 0;
}
+
+ if (ypos - clip_height > text->yofs_edit) {
+ text->yofs_edit = ypos - clip_height;
+ }
+
}
+
+ pango_layout_iter_free (iter);
}
text->needs_redraw = 1;
@@ -3529,11 +3666,13 @@
const gchar *str,
EText *text)
{
- if (text->selection_end != text->selection_start)
- e_text_delete_selection (text);
- e_text_insert (text, str, strlen (str));
- g_signal_emit (text, e_text_signals[E_TEXT_KEYPRESS], 0,
- 0 /* XXX ugh */, 0 /* XXX ugh */);
+ if (g_utf8_validate (str, strlen (str), NULL)) {
+ if (text->selection_end != text->selection_start)
+ e_text_delete_selection (text);
+ e_text_insert (text, str);
+ g_signal_emit (text, e_text_signals[E_TEXT_KEYPRESS], 0,
+ 0 /* XXX ugh */, 0 /* XXX ugh */);
+ }
}
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]