aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--widgets/text/e-text-model.c144
-rw-r--r--widgets/text/e-text.c531
2 files changed, 391 insertions, 284 deletions
diff --git a/widgets/text/e-text-model.c b/widgets/text/e-text-model.c
index 0233f14aa7..d995d81396 100644
--- a/widgets/text/e-text-model.c
+++ b/widgets/text/e-text-model.c
@@ -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 @@ enum {
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 @@ static void
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 @@ e_text_model_dispose (GObject *object)
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 @@ e_text_model_dispose (GObject *object)
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 @@ static const gchar *
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 @@ e_text_model_real_get_text (ETextModel *model)
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 @@ e_text_model_real_set_text (ETextModel *model, const gchar *text)
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 @@ e_text_model_real_set_text (ETextModel *model, const gchar *text)
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 @@ e_text_model_real_insert_length (ETextModel *model, gint position, const gchar *
{
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 @@ static void
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 @@ e_text_model_get_text_length (ETextModel *model)
#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 @@ e_text_model_get_text_length (ETextModel *model)
} 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 @@ e_text_model_strdup_nth_object (ETextModel *model, gint n)
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 @@ e_text_model_get_nth_object_bounds (ETextModel *model, gint n, gint *start, gint
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
diff --git a/widgets/text/e-text.c b/widgets/text/e-text.c
index 3c20d97407..fb2d330baf 100644
--- a/widgets/text/e-text.c
+++ b/widgets/text/e-text.c
@@ -136,7 +136,7 @@ static void e_text_do_popup (EText *text, GdkEventButton *button, int position);
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 @@ reset_layout_attrs (EText *text)
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 @@ create_layout (EText *text)
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 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
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 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
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 = &rect;
- }
-
if (text->stipple)
gnome_canvas_set_stipple_origin (item->canvas, main_gc);
@@ -1470,6 +1444,17 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
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 = &rect;
+ }
+
if (text->editing) {
xpos -= text->xofs_edit;
ypos -= text->yofs_edit;
@@ -1481,17 +1466,18 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
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 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
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 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
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 @@ get_position_from_xy (EText *text, gint x, gint y)
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 @@ e_text_copy_clipboard (EText *text)
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 @@ primary_get_cb (GtkClipboard *clipboard,
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 @@ paste_received (GtkClipboard *clipboard,
{
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 @@ e_text_reset_im_context (EText *text)
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 @@ _get_position(EText *text, ETextEventProcessorCommand *command)
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 @@ _get_position(EText *text, ETextEventProcessorCommand *command)
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 @@ _get_position(EText *text, ETextEventProcessorCommand *command)
}
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 @@ _get_position(EText *text, ETextEventProcessorCommand *command)
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 @@ _get_position(EText *text, ETextEventProcessorCommand *command)
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 @@ _get_position(EText *text, ETextEventProcessorCommand *command)
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 @@ _get_position(EText *text, ETextEventProcessorCommand *command)
}
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 @@ static void
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 @@ capitalize (EText *text, int start, int end, ETextEventProcessorCaps type)
}
*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 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp
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 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp
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 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp
}
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);
- xpos = PANGO_PIXELS (xpos);
+ 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;
- if (xpos < text->xofs_edit) {
+ /* scroll for X */
+ xpos = pango_pos.x; /* + (trailing ? 0 : pango_pos.width);*/
+
+ if (xpos + 2 < text->xofs_edit) {
text->xofs_edit = xpos;
}
@@ -3095,10 +3205,37 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp
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 @@ e_text_commit_cb (GtkIMContext *context,
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