aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/e-text/e-text.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/e-text/e-text.c')
-rw-r--r--widgets/e-text/e-text.c464
1 files changed, 437 insertions, 27 deletions
diff --git a/widgets/e-text/e-text.c b/widgets/e-text/e-text.c
index b88130e3c8..f6daa3f3cf 100644
--- a/widgets/e-text/e-text.c
+++ b/widgets/e-text/e-text.c
@@ -18,12 +18,15 @@
#include <config.h>
#include <math.h>
+#include <ctype.h>
#include "e-text.h"
#include <gdk/gdkx.h> /* for BlackPixel */
#include <libart_lgpl/art_affine.h>
#include <libart_lgpl/art_rgb.h>
#include <libart_lgpl/art_rgb_bitmap_affine.h>
+#include "e-text-event-processor-emacs-like.h"
+
/* This defines a line of text */
@@ -58,6 +61,7 @@ enum {
ARG_FILL_STIPPLE,
ARG_TEXT_WIDTH,
ARG_TEXT_HEIGHT,
+ ARG_EDITABLE,
ARG_USE_ELLIPSIS,
ARG_ELLIPSIS
};
@@ -80,6 +84,9 @@ static double e_text_point (GnomeCanvasItem *item, double x, double y, int cx, i
static void e_text_bounds (GnomeCanvasItem *item,
double *x1, double *y1, double *x2, double *y2);
static void e_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static gint e_text_event (GnomeCanvasItem *item, GdkEvent *event);
+
+static void e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gpointer data);
static ETextSuckFont *e_suck_font (GdkFont *font);
static void e_suck_font_free (ETextSuckFont *suckfont);
@@ -171,6 +178,8 @@ e_text_class_init (ETextClass *class)
GTK_TYPE_DOUBLE, GTK_ARG_READABLE, ARG_TEXT_WIDTH);
gtk_object_add_arg_type ("EText::text_height",
GTK_TYPE_DOUBLE, GTK_ARG_READABLE, ARG_TEXT_HEIGHT);
+ gtk_object_add_arg_type ("EText::editable",
+ GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EDITABLE);
gtk_object_add_arg_type ("EText::use_ellipsis",
GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_USE_ELLIPSIS);
gtk_object_add_arg_type ("EText::ellipsis",
@@ -187,6 +196,7 @@ e_text_class_init (ETextClass *class)
item_class->point = e_text_point;
item_class->bounds = e_text_bounds;
item_class->render = e_text_render;
+ item_class->event = e_text_event;
}
/* Object initialization function for the text item */
@@ -201,9 +211,18 @@ e_text_init (EText *text)
text->clip_height = 0.0;
text->xofs = 0.0;
text->yofs = 0.0;
+
text->ellipsis = NULL;
text->use_ellipsis = FALSE;
text->ellipsis_width = 0;
+
+ text->editable = FALSE;
+ text->editing = FALSE;
+ text->xofs_edit = 0;
+
+ text->selection_start = 0;
+ text->selection_end = 0;
+ text->select_by_word = FALSE;
}
/* Destroy handler for the text item */
@@ -456,7 +475,8 @@ calc_line_widths (EText *text)
}
if (text->clip &&
- text->use_ellipsis &&
+ text->use_ellipsis &&
+ ! text->editing &&
lines->width > text->clip_width) {
if (text->font) {
lines->ellipsis_length = 0;
@@ -729,6 +749,10 @@ e_text_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
set_stipple (text, GTK_VALUE_BOXED (*arg), FALSE);
break;
+ case ARG_EDITABLE:
+ text->editable = GTK_VALUE_BOOL (*arg);
+ break;
+
case ARG_USE_ELLIPSIS:
text->use_ellipsis = GTK_VALUE_BOOL (*arg);
calc_line_widths (text);
@@ -839,6 +863,10 @@ e_text_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
GTK_VALUE_DOUBLE (*arg) = text->height / text->item.canvas->pixels_per_unit;
break;
+ case ARG_EDITABLE:
+ GTK_VALUE_BOOL (*arg) = text->editable;
+ break;
+
case ARG_USE_ELLIPSIS:
GTK_VALUE_BOOL (*arg) = text->use_ellipsis;
break;
@@ -1016,6 +1044,19 @@ get_line_xpos (EText *text, struct line *line)
return x;
}
+static void
+_get_tep(EText *text)
+{
+ if (!text->tep) {
+ text->tep = e_text_event_processor_emacs_like_new();
+ gtk_signal_connect(GTK_OBJECT(text->tep),
+ "command",
+ GTK_SIGNAL_FUNC(e_text_command),
+ (gpointer) text);
+
+ }
+}
+
/* Draw handler for the text item */
static void
e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
@@ -1026,8 +1067,14 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
struct line *lines;
int i;
int xpos, ypos;
+ int start_char, end_char;
+ int sel_start, sel_end;
+ GdkRectangle sel_rect;
+ GdkGC *fg_gc;
+ GnomeCanvas *canvas;
text = E_TEXT (item);
+ canvas = GNOME_CANVAS_ITEM(text)->canvas;
if (!text->text || !text->font)
return;
@@ -1039,6 +1086,7 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
rect.height = text->clip_cheight;
gdk_gc_set_clip_rectangle (text->gc, &rect);
+ gdk_gc_set_clip_rectangle (GTK_WIDGET(canvas)->style->fg_gc[GTK_STATE_SELECTED], &rect);
}
lines = text->lines;
ypos = text->cy + text->font->ascent;
@@ -1049,39 +1097,122 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
for (i = 0; i < text->num_lines; i++) {
if (lines->length != 0) {
xpos = get_line_xpos (text, lines);
- if ( text->clip && text->use_ellipsis && lines->ellipsis_length < lines->length) {
- gdk_draw_text (drawable,
- text->font,
- text->gc,
- xpos - x,
- ypos - y,
- lines->text,
- lines->ellipsis_length);
- gdk_draw_text (drawable,
- text->font,
- text->gc,
- xpos - x +
- lines->width - text->ellipsis_width,
- ypos - y,
- text->ellipsis ? text->ellipsis : "...",
- text->ellipsis ? strlen (text->ellipsis) : 3);
- } else
-
- gdk_draw_text (drawable,
- text->font,
- text->gc,
- xpos - x,
- ypos - y,
- lines->text,
- lines->length);
+ if (text->editing) {
+ xpos -= text->xofs_edit;
+ start_char = lines->text - text->text;
+ end_char = start_char + lines->length;
+ sel_start = text->selection_start;
+ sel_end = text->selection_end;
+ if (sel_start > sel_end ) {
+ sel_start ^= sel_end;
+ sel_end ^= sel_start;
+ sel_start ^= sel_end;
+ }
+ if ( sel_start < start_char )
+ sel_start = start_char;
+ if ( sel_end > end_char )
+ sel_end = end_char;
+ if ( sel_start < sel_end ) {
+ sel_rect.x = xpos - x + gdk_text_width (text->font,
+ lines->text,
+ sel_start - start_char);
+ sel_rect.y = ypos - y - text->font->ascent;
+ sel_rect.width = gdk_text_width (text->font,
+ lines->text + sel_start - start_char,
+ sel_end - sel_start);
+ sel_rect.height = text->font->ascent + text->font->descent;
+ gdk_draw_rectangle (drawable,
+ text->gc,
+ TRUE,
+ sel_rect.x,
+ sel_rect.y,
+ sel_rect.width,
+ sel_rect.height);
+ gdk_draw_text (drawable,
+ text->font,
+ text->gc,
+ xpos - x,
+ ypos - y,
+ lines->text,
+ sel_start - start_char);
+ fg_gc = GTK_WIDGET(canvas)->style->fg_gc[GTK_STATE_SELECTED];
+ gdk_draw_text (drawable,
+ text->font,
+ fg_gc,
+ xpos - x + gdk_text_width (text->font,
+ lines->text,
+ sel_start - start_char),
+ ypos - y,
+ lines->text + sel_start - start_char,
+ sel_end - sel_start);
+ gdk_draw_text (drawable,
+ text->font,
+ text->gc,
+ xpos - x + gdk_text_width (text->font,
+ lines->text,
+ sel_end - start_char),
+ ypos - y,
+ lines->text + sel_end - start_char,
+ end_char - sel_end);
+ } else {
+ gdk_draw_text (drawable,
+ text->font,
+ text->gc,
+ xpos - x,
+ ypos - y,
+ lines->text,
+ lines->length);
+ }
+ if (text->selection_start == text->selection_end &&
+ text->selection_start >= start_char &&
+ text->selection_start <= end_char) {
+ gdk_draw_rectangle (drawable,
+ text->gc,
+ TRUE,
+ xpos - x + gdk_text_width (text->font,
+ lines->text,
+ sel_start - start_char),
+ ypos - y - text->font->ascent,
+ 1,
+ text->font->ascent + text->font->descent);
+ }
+ } else {
+ if ( text->clip && text->use_ellipsis && lines->ellipsis_length < lines->length) {
+ gdk_draw_text (drawable,
+ text->font,
+ text->gc,
+ xpos - x,
+ ypos - y,
+ lines->text,
+ lines->ellipsis_length);
+ gdk_draw_text (drawable,
+ text->font,
+ text->gc,
+ xpos - x +
+ lines->width - text->ellipsis_width,
+ ypos - y,
+ text->ellipsis ? text->ellipsis : "...",
+ text->ellipsis ? strlen (text->ellipsis) : 3);
+ } else
+
+ gdk_draw_text (drawable,
+ text->font,
+ text->gc,
+ xpos - x,
+ ypos - y,
+ lines->text,
+ lines->length);
+ }
}
ypos += text->font->ascent + text->font->descent;
lines++;
}
- if (text->clip)
+ if (text->clip) {
gdk_gc_set_clip_rectangle (text->gc, NULL);
+ gdk_gc_set_clip_rectangle (GTK_WIDGET(GNOME_CANVAS_ITEM(text)->canvas)->style->fg_gc[GTK_STATE_SELECTED], NULL);
+ }
}
/* Render handler for the text item */
@@ -1308,6 +1439,285 @@ e_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double
*y2 = *y1 + height;
}
+static gint
+_get_position_from_xy (EText *text, gint x, gint y)
+{
+ int i, j;
+ int ypos = text->cy;
+ int xpos;
+ struct line *lines;
+ j = 0;
+ while (y > ypos)
+ {
+ ypos += text->font->ascent + text->font->descent;
+ j ++;
+ }
+ j--;
+ if (j >= text->num_lines)
+ j = text->num_lines - 1;
+ if (j < 0)
+ j = 0;
+ i = 0;
+ lines = text->lines;
+ lines += j;
+ xpos = get_line_xpos (text, lines);
+ for(i = 0; i < lines->length; i++) {
+ int charwidth = gdk_text_width(text->font,
+ lines->text + i,
+ 1);
+ xpos += charwidth / 2;
+ if (xpos > x)
+ break;
+ xpos += (charwidth + 1) / 2;
+ }
+ return lines->text + i - text->text;
+}
+
+static gint
+e_text_event (GnomeCanvasItem *item, GdkEvent *event)
+{
+ EText *text = E_TEXT(item);
+ ETextEventProcessorEvent e_tep_event;
+
+ gint return_val;
+
+ e_tep_event.type = event->type;
+ switch (event->type) {
+ case GDK_FOCUS_CHANGE:
+ if (text->editable) {
+ GdkEventFocus *focus_event;
+ focus_event = (GdkEventFocus *) event;
+ if (focus_event->in) {
+ if(!text->editing) {
+ text->editing = TRUE;
+ text->selection_start = 0;
+ text->selection_end = 0;
+ text->select_by_word = FALSE;
+ text->xofs_edit = 0;
+ }
+ } else {
+ text->editing = FALSE;
+ }
+ calc_line_widths (text);
+ }
+ return_val = 0;
+ break;
+ case GDK_KEY_PRESS: /* Fall Through */
+ case GDK_KEY_RELEASE:
+ if (text->editing) {
+ GdkEventKey key = event->key;
+ e_tep_event.key.time = key.time;
+ e_tep_event.key.state = key.state;
+ e_tep_event.key.keyval = key.keyval;
+ e_tep_event.key.length = key.length;
+ e_tep_event.key.string = key.string;
+ _get_tep(text);
+ return e_text_event_processor_handle_event (text->tep,
+ &e_tep_event);
+ }
+ else
+ return 0;
+ break;
+ case GDK_BUTTON_PRESS: /* Fall Through */
+ case GDK_BUTTON_RELEASE:
+ if (text->editing) {
+ GdkEventButton button = event->button;
+ e_tep_event.button.time = button.time;
+ e_tep_event.button.state = button.state;
+ e_tep_event.button.button = button.button;
+ e_tep_event.button.position = _get_position_from_xy(text, button.x, button.y);
+ _get_tep(text);
+ return_val = e_text_event_processor_handle_event (text->tep,
+ &e_tep_event);
+ } else if (text->editable && event->type == GDK_BUTTON_RELEASE) {
+ gnome_canvas_item_grab_focus (item);
+ return 1;
+ }
+ break;
+ case GDK_MOTION_NOTIFY:
+ if (text->editing) {
+ GdkEventMotion motion = event->motion;
+ e_tep_event.motion.time = motion.time;
+ e_tep_event.motion.state = motion.state;
+ e_tep_event.motion.position = _get_position_from_xy(text, motion.x, motion.y);
+ _get_tep(text);
+ return_val = e_text_event_processor_handle_event (text->tep,
+ &e_tep_event);
+ }
+ break;
+ default:
+ break;
+ }
+ if (return_val)
+ return return_val;
+ if (GNOME_CANVAS_ITEM_CLASS(parent_class)->event)
+ return GNOME_CANVAS_ITEM_CLASS(parent_class)->event(item, event);
+ else
+ return 0;
+}
+
+static int
+_get_position(EText *text, ETextEventProcessorCommand *command)
+{
+ int i;
+ int length;
+
+ switch (command->position) {
+
+ case E_TEP_VALUE:
+ return command->value;
+
+ case E_TEP_SELECTION:
+ return text->selection_end;
+
+ case E_TEP_START_OF_BUFFER:
+ return 0;
+ case E_TEP_END_OF_BUFFER:
+ return strlen(text->text);
+
+ case E_TEP_START_OF_LINE:
+ for (i = text->selection_end - 2; i > 0; i--)
+ if (text->text[i] == '\n') {
+ i++;
+ break;
+ }
+ return i;
+ case E_TEP_END_OF_LINE:
+ length = strlen(text->text);
+ for (i = text->selection_end + 1; i < length; i++)
+ if (text->text[i] == '\n') {
+ break;
+ }
+ if (i > length)
+ i = length;
+ return i;
+
+ case E_TEP_FORWARD_CHARACTER:
+ length = strlen(text->text);
+ i = text->selection_end + 1;
+ if (i > length)
+ i = length;
+ return i;
+ case E_TEP_BACKWARD_CHARACTER:
+ i = text->selection_end - 1;
+ if (i < 0)
+ i = 0;
+ return i;
+
+ case E_TEP_FORWARD_WORD:
+ length = strlen(text->text);
+ for (i = text->selection_end + 1; i < length; i++)
+ if (isspace(text->text[i])) {
+ break;
+ }
+ if (i > length)
+ i = length;
+ return i;
+ case E_TEP_BACKWARD_WORD:
+ for (i = text->selection_end - 2; i > 0; i--)
+ if (isspace(text->text[i])) {
+ i++;
+ break;
+ }
+ if (i < 0)
+ i = 0;
+ return i;
+
+ case E_TEP_FORWARD_LINE:
+ case E_TEP_BACKWARD_LINE:
+
+ case E_TEP_FORWARD_PARAGRAPH:
+ case E_TEP_BACKWARD_PARAGRAPH:
+
+ case E_TEP_FORWARD_PAGE:
+ case E_TEP_BACKWARD_PAGE:
+ return text->selection_end;
+ default:
+ return text->selection_end;
+ }
+}
+
+static void
+_delete_selection(EText *text)
+{
+ gint length = strlen(text->text);
+ if (text->selection_end == text->selection_start)
+ return;
+ if (text->selection_end < text->selection_start) {
+ text->selection_end ^= text->selection_start;
+ text->selection_start ^= text->selection_end;
+ text->selection_end ^= text->selection_start;
+ }
+ memmove( text->text + text->selection_start,
+ text->text + text->selection_end,
+ length - text->selection_end + 1 );
+ length -= text->selection_end - text->selection_start;
+ text->selection_end = text->selection_start;
+}
+
+static void
+_insert(EText *text, char *string, int value)
+{
+ if (value > 0) {
+ char *temp;
+ gint length = strlen(text->text);
+ temp = g_new(gchar, length + value + 1);
+ strncpy(temp, text->text, text->selection_start);
+ strncpy(temp + text->selection_start, string, value);
+ strcpy(temp + text->selection_start + value, text->text + text->selection_start);
+ g_free(text->text);
+ text->text = temp;
+ text->selection_start += value;
+ text->selection_end = text->selection_start;
+ }
+}
+
+static void
+e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gpointer data)
+{
+ EText *text = E_TEXT(data);
+ switch (command->action) {
+ case E_TEP_MOVE:
+ text->selection_start = _get_position(text, command);
+ text->selection_end = text->selection_start;
+ break;
+ case E_TEP_SELECT:
+ text->selection_end = _get_position(text, command);
+ break;
+ case E_TEP_DELETE:
+ if (text->selection_end == text->selection_start) {
+ text->selection_end = _get_position(text, command);
+ }
+ _delete_selection(text);
+ split_into_lines (text);
+ recalc_bounds (text);
+ break;
+
+ case E_TEP_INSERT:
+ if (text->selection_end != text->selection_start) {
+ _delete_selection(text);
+ }
+ _insert(text, command->string, command->value);
+ split_into_lines (text);
+ recalc_bounds (text);
+ break;
+ case E_TEP_COPY:
+ if (text->selection_end != text->selection_start) {
+ }
+ break;
+ case E_TEP_PASTE:
+ break;
+ case E_TEP_ACTIVATE:
+ break;
+ case E_TEP_SET_SELECT_BY_WORD:
+ text->select_by_word = command->value;
+ break;
+ case E_TEP_NOP:
+ break;
+ }
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(text));
+}
+
/* Routines for sucking fonts from the X server */