diff options
author | Chris Lahey <clahey@src.gnome.org> | 2000-01-14 13:18:59 +0800 |
---|---|---|
committer | Chris Lahey <clahey@src.gnome.org> | 2000-01-14 13:18:59 +0800 |
commit | 4ac908c6ffa505ae3978eec809bf50fc7cb88e90 (patch) | |
tree | ec44e0269b86ab31e5b3c0a4aa5e10d591fb0d7c /widgets | |
parent | f21a1a70bdcdf055e889a3ab736d76153ca2f760 (diff) | |
download | gsoc2013-evolution-4ac908c6ffa505ae3978eec809bf50fc7cb88e90.tar.gz gsoc2013-evolution-4ac908c6ffa505ae3978eec809bf50fc7cb88e90.tar.zst gsoc2013-evolution-4ac908c6ffa505ae3978eec809bf50fc7cb88e90.zip |
Added selection and clipboard support. Added up and down arrow keys. Fixed
* widgets/e-text-event-processor-types.h,
widgets/e-text-event-processor-emacs-like.c, widgets/e-text.c,
widgets/e-text.h: Added selection and clipboard support. Added up
and down arrow keys. Fixed choice of font colors for the
selection to be based on the current style.
* widgets/e-minicard.c: Caused a click to grab the focus. Changed
the fake information added.
* widgets/e-minicard-label.c: Forward mouse events to the field
EText item.
svn path=/trunk/; revision=1568
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/e-minicard-label.c | 43 | ||||
-rw-r--r-- | widgets/e-minicard.c | 7 | ||||
-rw-r--r-- | widgets/e-minicard/e-minicard-label.c | 43 | ||||
-rw-r--r-- | widgets/e-minicard/e-minicard.c | 7 | ||||
-rw-r--r-- | widgets/e-text-event-processor-emacs-like.c | 27 | ||||
-rw-r--r-- | widgets/e-text-event-processor-types.h | 2 | ||||
-rw-r--r-- | widgets/e-text.c | 271 | ||||
-rw-r--r-- | widgets/e-text.h | 7 | ||||
-rw-r--r-- | widgets/e-text/e-text-event-processor-emacs-like.c | 27 | ||||
-rw-r--r-- | widgets/e-text/e-text-event-processor-types.h | 2 | ||||
-rw-r--r-- | widgets/e-text/e-text.c | 271 | ||||
-rw-r--r-- | widgets/e-text/e-text.h | 7 | ||||
-rw-r--r-- | widgets/text/e-text-event-processor-emacs-like.c | 27 | ||||
-rw-r--r-- | widgets/text/e-text-event-processor-types.h | 2 | ||||
-rw-r--r-- | widgets/text/e-text.c | 271 | ||||
-rw-r--r-- | widgets/text/e-text.h | 7 |
16 files changed, 968 insertions, 53 deletions
diff --git a/widgets/e-minicard-label.c b/widgets/e-minicard-label.c index a550031123..7e32acad59 100644 --- a/widgets/e-minicard-label.c +++ b/widgets/e-minicard-label.c @@ -126,7 +126,6 @@ e_minicard_label_class_init (EMinicardLabelClass *klass) static void e_minicard_label_init (EMinicardLabel *minicard_label) { - GnomeCanvasGroup *group = GNOME_CANVAS_GROUP( minicard_label ); minicard_label->width = 10; minicard_label->height = 10; minicard_label->rect = NULL; @@ -215,7 +214,6 @@ e_minicard_label_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) static void e_minicard_label_realize (GnomeCanvasItem *item) { - double ascent, descent; EMinicardLabel *e_minicard_label; GnomeCanvasGroup *group; @@ -339,7 +337,46 @@ e_minicard_label_event (GnomeCanvasItem *item, GdkEvent *event) } } break; - default: + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_MOTION_NOTIFY: { + GnomeCanvasItem *field; + ArtPoint p; + double inv[6], affine[6]; + gboolean return_val; + + field = e_minicard_label->field; + art_affine_identity (affine); + + if (field->xform != NULL) { + if (field->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) { + art_affine_multiply (affine, affine, field->xform); + } else { + affine[4] += field->xform[0]; + affine[5] += field->xform[1]; + } + } + + art_affine_invert (inv, affine); + if (event->type == GDK_MOTION_NOTIFY) { + p.x = event->motion.x; + p.y = event->motion.y; + art_affine_point (&p, &p, inv); + event->motion.x = p.x; + event->motion.y = p.y; + } else { + p.x = event->button.x; + p.y = event->button.y; + art_affine_point (&p, &p, inv); + event->button.x = p.x; + event->button.y = p.y; + } + + gtk_signal_emit_by_name(GTK_OBJECT(e_minicard_label->field), "event", event, &return_val); + return return_val; + break; + } + default: break; } diff --git a/widgets/e-minicard.c b/widgets/e-minicard.c index 52d241d11e..bef929b36c 100644 --- a/widgets/e-minicard.c +++ b/widgets/e-minicard.c @@ -279,7 +279,7 @@ e_minicard_realize (GnomeCanvasItem *item) "y", e_minicard->height, "width", e_minicard->width - 4.0, "fieldname", "Email:", - "field", "clahey@helixcode.com", + "field", "clahey@address.com", NULL ); e_minicard->fields = g_list_append( e_minicard->fields, new_item); @@ -349,6 +349,11 @@ e_minicard_event (GnomeCanvasItem *item, GdkEvent *event) } } break; + case GDK_BUTTON_PRESS: + if (event->button.button == 1) { + gnome_canvas_item_grab_focus(item); + } + break; case GDK_KEY_PRESS: if (event->key.length == 1 && event->key.string[0] == '\t') { GList *list; diff --git a/widgets/e-minicard/e-minicard-label.c b/widgets/e-minicard/e-minicard-label.c index a550031123..7e32acad59 100644 --- a/widgets/e-minicard/e-minicard-label.c +++ b/widgets/e-minicard/e-minicard-label.c @@ -126,7 +126,6 @@ e_minicard_label_class_init (EMinicardLabelClass *klass) static void e_minicard_label_init (EMinicardLabel *minicard_label) { - GnomeCanvasGroup *group = GNOME_CANVAS_GROUP( minicard_label ); minicard_label->width = 10; minicard_label->height = 10; minicard_label->rect = NULL; @@ -215,7 +214,6 @@ e_minicard_label_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) static void e_minicard_label_realize (GnomeCanvasItem *item) { - double ascent, descent; EMinicardLabel *e_minicard_label; GnomeCanvasGroup *group; @@ -339,7 +337,46 @@ e_minicard_label_event (GnomeCanvasItem *item, GdkEvent *event) } } break; - default: + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_MOTION_NOTIFY: { + GnomeCanvasItem *field; + ArtPoint p; + double inv[6], affine[6]; + gboolean return_val; + + field = e_minicard_label->field; + art_affine_identity (affine); + + if (field->xform != NULL) { + if (field->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) { + art_affine_multiply (affine, affine, field->xform); + } else { + affine[4] += field->xform[0]; + affine[5] += field->xform[1]; + } + } + + art_affine_invert (inv, affine); + if (event->type == GDK_MOTION_NOTIFY) { + p.x = event->motion.x; + p.y = event->motion.y; + art_affine_point (&p, &p, inv); + event->motion.x = p.x; + event->motion.y = p.y; + } else { + p.x = event->button.x; + p.y = event->button.y; + art_affine_point (&p, &p, inv); + event->button.x = p.x; + event->button.y = p.y; + } + + gtk_signal_emit_by_name(GTK_OBJECT(e_minicard_label->field), "event", event, &return_val); + return return_val; + break; + } + default: break; } diff --git a/widgets/e-minicard/e-minicard.c b/widgets/e-minicard/e-minicard.c index 52d241d11e..bef929b36c 100644 --- a/widgets/e-minicard/e-minicard.c +++ b/widgets/e-minicard/e-minicard.c @@ -279,7 +279,7 @@ e_minicard_realize (GnomeCanvasItem *item) "y", e_minicard->height, "width", e_minicard->width - 4.0, "fieldname", "Email:", - "field", "clahey@helixcode.com", + "field", "clahey@address.com", NULL ); e_minicard->fields = g_list_append( e_minicard->fields, new_item); @@ -349,6 +349,11 @@ e_minicard_event (GnomeCanvasItem *item, GdkEvent *event) } } break; + case GDK_BUTTON_PRESS: + if (event->button.button == 1) { + gnome_canvas_item_grab_focus(item); + } + break; case GDK_KEY_PRESS: if (event->key.length == 1 && event->key.string[0] == '\t') { GList *list; diff --git a/widgets/e-text-event-processor-emacs-like.c b/widgets/e-text-event-processor-emacs-like.c index 6c7c86afdc..d2bf524401 100644 --- a/widgets/e-text-event-processor-emacs-like.c +++ b/widgets/e-text-event-processor-emacs-like.c @@ -57,7 +57,7 @@ static const ETextEventProcessorCommand control_keys[26] = { E_TEP_START_OF_LINE, E_TEP_DELETE, 0, "" }, /* u */ { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* v */ { E_TEP_BACKWARD_WORD, E_TEP_DELETE, 0, "" }, /* w */ - { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* x */ + { E_TEP_SELECTION, E_TEP_DELETE, 0, "" }, /* x */ { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* y */ { E_TEP_SELECTION, E_TEP_NOP, 0, "" } /* z */ }; @@ -142,6 +142,7 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro { ETextEventProcessorCommand command; ETextEventProcessorEmacsLike *tep_el = E_TEXT_EVENT_PROCESSOR_EMACS_LIKE(tep); + command.action = E_TEP_NOP; switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1) { @@ -151,24 +152,39 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro command.action = E_TEP_MOVE; command.position = E_TEP_VALUE; command.value = event->button.position; + command.time = event->button.time; tep_el->mouse_down = TRUE; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1) { + command.time = event->button.time; tep_el->mouse_down = FALSE; + } else if (event->button.button == 2) { + command.action = E_TEP_MOVE; + command.position = E_TEP_VALUE; + command.value = event->button.position; + command.time = event->button.time; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); + + command.action = E_TEP_GET_SELECTION; + command.position = E_TEP_SELECTION; + command.value = 0; + command.time = event->button.time; } break; case GDK_MOTION_NOTIFY: if (tep_el->mouse_down) { command.action = E_TEP_SELECT; command.position = E_TEP_VALUE; + command.time = event->motion.time; command.value = event->motion.position; } break; case GDK_KEY_PRESS: { ETextEventProcessorEventKey key = event->key; + command.time = event->key.time; if (key.state & GDK_SHIFT_MASK) command.action = E_TEP_SELECT; else @@ -232,6 +248,8 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro command.position = E_TEP_FORWARD_WORD; } else if (key.state & GDK_SHIFT_MASK) { command.action = E_TEP_COPY; + command.position = E_TEP_SELECTION; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); command.action = E_TEP_DELETE; command.position = E_TEP_SELECTION; @@ -276,6 +294,12 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro } if (key.keyval == 'x') { + command.action = E_TEP_COPY; + command.position = E_TEP_SELECTION; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); + + command.action = E_TEP_DELETE; + command.position = E_TEP_SELECTION; } break; @@ -302,6 +326,7 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro } break; case GDK_KEY_RELEASE: + command.time = event->key.time; command.action = E_TEP_NOP; break; default: diff --git a/widgets/e-text-event-processor-types.h b/widgets/e-text-event-processor-types.h index 30b7bcafc9..3795058225 100644 --- a/widgets/e-text-event-processor-types.h +++ b/widgets/e-text-event-processor-types.h @@ -79,6 +79,7 @@ enum _ETextEventProcessorCommandAction { E_TEP_INSERT, E_TEP_COPY, E_TEP_PASTE, + E_TEP_GET_SELECTION, E_TEP_SET_SELECT_BY_WORD, E_TEP_ACTIVATE, @@ -90,6 +91,7 @@ struct _ETextEventProcessorCommand { ETextEventProcessorCommandAction action; int value; char *string; + guint32 time; }; struct _ETextEventProcessorEventButton { diff --git a/widgets/e-text.c b/widgets/e-text.c index 99e35348b9..cb3456cd24 100644 --- a/widgets/e-text.c +++ b/widgets/e-text.c @@ -24,6 +24,7 @@ #include <libart_lgpl/art_affine.h> #include <libart_lgpl/art_rgb.h> #include <libart_lgpl/art_rgb_bitmap_affine.h> +#include <gtk/gtkinvisible.h> #include "e-text-event-processor-emacs-like.h" @@ -76,6 +77,10 @@ enum { enum { + E_SELECTION_PRIMARY, + E_SELECTION_CLIPBOARD +}; +enum { TARGET_STRING, TARGET_TEXT, TARGET_COMPOUND_TEXT @@ -104,11 +109,15 @@ static void e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand static guint32 e_text_get_event_time (EText *text); +static void e_text_get_selection(EText *text, GdkAtom selection, guint32 time); +static void e_text_supply_selection (EText *text, guint time, GdkAtom selection, guchar *data, gint length); + static ETextSuckFont *e_suck_font (GdkFont *font); static void e_suck_font_free (ETextSuckFont *suckfont); static GnomeCanvasItemClass *parent_class; +static GdkAtom clipboard_atom = GDK_NONE; @@ -221,6 +230,11 @@ e_text_class_init (ETextClass *klass) gtk_object_add_arg_type ("EText::ellipsis", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_ELLIPSIS); + if (!clipboard_atom) + clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); + + + klass->resize = NULL; klass->change = NULL; @@ -275,6 +289,14 @@ e_text_init (EText *text) text->button_down = FALSE; text->tep = NULL; + + text->invisible = NULL; + text->has_selection = FALSE; + + text->primary_selection = NULL; + text->primary_length = 0; + text->clipboard_selection = NULL; + text->clipboard_length = 0; } /* Destroy handler for the text item */ @@ -1145,6 +1167,8 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, text = E_TEXT (item); canvas = GNOME_CANVAS_ITEM(text)->canvas; + fg_gc = GTK_WIDGET(canvas)->style->fg_gc[text->has_selection ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE]; + if (!text->text || !text->font) return; @@ -1156,7 +1180,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); + gdk_gc_set_clip_rectangle (fg_gc, &rect); clip_rect = ▭ } lines = text->lines; @@ -1194,7 +1218,9 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, sel_rect.height = text->font->ascent + text->font->descent; gtk_paint_flat_box(GTK_WIDGET(item->canvas)->style, drawable, - GTK_STATE_SELECTED, + text->has_selection ? + GTK_STATE_SELECTED : + GTK_STATE_ACTIVE, GTK_SHADOW_NONE, clip_rect, GTK_WIDGET(item->canvas), @@ -1210,7 +1236,6 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 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, @@ -1287,7 +1312,7 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 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); + gdk_gc_set_clip_rectangle (fg_gc, NULL); } } @@ -1515,6 +1540,34 @@ e_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2 = *y1 + height; } +static void +_get_xy_from_position (EText *text, gint position, gint *xp, gint *yp) +{ + if (xp || yp) { + struct line *lines; + int x, y; + int j; + x = get_line_xpos (text, lines); + y = text->cy; + for (j = 0, lines = text->lines; j < text->num_lines; lines++, j++) { + if (lines->text > text->text + position) + break; + y += text->font->ascent + text->font->descent; + } + lines --; + y -= text->font->descent; + + x += gdk_text_width (text->font, + lines->text, + position - (lines->text - text->text)); + x -= text->xofs_edit; + if (xp) + *xp = x; + if (yp) + *yp = y; + } +} + static gint _get_position_from_xy (EText *text, gint x, gint y) { @@ -1523,11 +1576,10 @@ _get_position_from_xy (EText *text, gint x, gint y) int xpos; struct line *lines; j = 0; - while (y > ypos) - { - ypos += text->font->ascent + text->font->descent; - j ++; - } + while (y > ypos) { + ypos += text->font->ascent + text->font->descent; + j ++; + } j--; if (j >= text->num_lines) j = text->num_lines - 1; @@ -1543,8 +1595,9 @@ _get_position_from_xy (EText *text, gint x, gint y) lines->text + i, 1); xpos += charwidth / 2; - if (xpos > x) + if (xpos > x) { break; + } xpos += (charwidth + 1) / 2; } return lines->text + i - text->text; @@ -1619,7 +1672,7 @@ e_text_event (GnomeCanvasItem *item, GdkEvent *event) EText *text = E_TEXT(item); ETextEventProcessorEvent e_tep_event; - gint return_val; + gint return_val = 0; e_tep_event.type = event->type; switch (event->type) { @@ -1730,6 +1783,7 @@ _get_position(EText *text, ETextEventProcessorCommand *command) { int i; int length; + int x, y; switch (command->position) { @@ -1793,7 +1847,13 @@ _get_position(EText *text, ETextEventProcessorCommand *command) return i; case E_TEP_FORWARD_LINE: + _get_xy_from_position(text, text->selection_end, &x, &y); + y += text->font->ascent + text->font->descent; + return _get_position_from_xy(text, x, y); case E_TEP_BACKWARD_LINE: + _get_xy_from_position(text, text->selection_end, &x, &y); + y -= text->font->ascent + text->font->descent; + return _get_position_from_xy(text, x, y); case E_TEP_FORWARD_PARAGRAPH: case E_TEP_BACKWARD_PARAGRAPH: @@ -1845,6 +1905,7 @@ static void e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gpointer data) { EText *text = E_TEXT(data); + int sel_start, sel_end; switch (command->action) { case E_TEP_MOVE: text->selection_start = _get_position(text, command); @@ -1852,6 +1913,11 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp break; case E_TEP_SELECT: text->selection_end = _get_position(text, command); + sel_start = MIN(text->selection_start, text->selection_end); + sel_end = MAX(text->selection_start, text->selection_end); + if (sel_start != sel_end) { + e_text_supply_selection (text, command->time, GDK_SELECTION_PRIMARY, text->text + sel_start, sel_end - sel_start); + } break; case E_TEP_DELETE: if (text->selection_end == text->selection_start) { @@ -1871,10 +1937,17 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp recalc_bounds (text); break; case E_TEP_COPY: - if (text->selection_end != text->selection_start) { + sel_start = MIN(text->selection_start, text->selection_end); + sel_end = MAX(text->selection_start, text->selection_end); + if (sel_start != sel_end) { + e_text_supply_selection (text, command->time, clipboard_atom, text->text + sel_start, sel_end - sel_start); } break; case E_TEP_PASTE: + e_text_get_selection (text, clipboard_atom, command->time); + break; + case E_TEP_GET_SELECTION: + e_text_get_selection (text, GDK_SELECTION_PRIMARY, command->time); break; case E_TEP_ACTIVATE: break; @@ -1911,6 +1984,178 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(text)); } + +static void +_selection_clear_event (GtkInvisible *invisible, + GdkEventSelection *event, + EText *text) +{ + if (event->selection == GDK_SELECTION_PRIMARY) { + g_free (text->primary_selection); + text->primary_selection = NULL; + text->primary_length = 0; + gtk_object_unref (GTK_OBJECT(invisible)); + + text->has_selection = FALSE; + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(text)); + + } else if (event->selection == clipboard_atom) { + g_free (text->clipboard_selection); + text->clipboard_selection = NULL; + text->clipboard_length = 0; + gtk_object_unref (GTK_OBJECT(invisible)); + } +} + +static void +_selection_get (GtkInvisible *invisible, + GtkSelectionData *selection_data, + guint info, + guint time_stamp, + EText *text) +{ + switch (info) { + case E_SELECTION_PRIMARY: + gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, + 8, text->primary_selection, text->primary_length); + break; + case E_SELECTION_CLIPBOARD: + gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, + 8, text->clipboard_selection, text->clipboard_length); + break; + } +} + +static void +_selection_received (GtkInvisible *invisible, + GtkSelectionData *selection_data, + guint time, + EText *text) +{ + if (selection_data->length < 0 || selection_data->type != GDK_SELECTION_TYPE_STRING) { + gtk_object_unref (GTK_OBJECT(invisible)); + return; + } else { + ETextEventProcessorCommand command; + command.action = E_TEP_INSERT; + command.position = E_TEP_SELECTION; + command.string = selection_data->data; + command.value = selection_data->length; + command.time = time; + e_text_command(text->tep, &command, text); + gtk_object_unref (GTK_OBJECT(invisible)); + } +} + +static void _invisible_destroy (GtkInvisible *invisible, + EText *text) +{ + text->invisible = NULL; +} + +static void e_text_supply_selection (EText *text, guint time, GdkAtom selection, guchar *data, gint length) +{ + gboolean successful; + GtkWidget *invisible; + + if (text->invisible) { + invisible = text->invisible; + gtk_object_ref (GTK_OBJECT(invisible)); + } else { + invisible = gtk_invisible_new(); + text->invisible = invisible; + + gtk_selection_add_target (invisible, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_PRIMARY); + gtk_selection_add_target (invisible, + clipboard_atom, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_CLIPBOARD); + + gtk_signal_connect (GTK_OBJECT(invisible), "selection_get", + GTK_SIGNAL_FUNC (_selection_get), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_clear_event", + GTK_SIGNAL_FUNC (_selection_clear_event), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_received", + GTK_SIGNAL_FUNC (_selection_received), + text); + + gtk_signal_connect (GTK_OBJECT(invisible), "destroy", + GTK_SIGNAL_FUNC (_invisible_destroy), + text); + } + + if (selection == GDK_SELECTION_PRIMARY ) { + if (text->primary_selection) { + gtk_object_unref (GTK_OBJECT(invisible)); + g_free (text->primary_selection); + } + text->primary_selection = g_strndup(data, length); + text->primary_length = length; + } else if (selection == clipboard_atom) { + if (text->clipboard_selection) { + gtk_object_unref (GTK_OBJECT(invisible)); + g_free (text->clipboard_selection); + } + text->clipboard_selection = g_strndup(data, length); + text->clipboard_length = length; + } + + successful = gtk_selection_owner_set (invisible, + selection, + time); + + if (selection == GDK_SELECTION_PRIMARY) + text->has_selection = successful; + + if (!successful) + gtk_object_unref(GTK_OBJECT(invisible)); +} + +static void +e_text_get_selection(EText *text, GdkAtom selection, guint32 time) +{ + GtkWidget *invisible; + if (text->invisible) { + invisible = text->invisible; + gtk_object_ref (GTK_OBJECT(invisible)); + } else { + invisible = gtk_invisible_new(); + text->invisible = invisible; + + gtk_selection_add_target (invisible, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_PRIMARY); + gtk_selection_add_target (invisible, + clipboard_atom, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_CLIPBOARD); + + gtk_signal_connect (GTK_OBJECT(invisible), "selection_get", + GTK_SIGNAL_FUNC (_selection_get), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_clear_event", + GTK_SIGNAL_FUNC (_selection_clear_event), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_received", + GTK_SIGNAL_FUNC (_selection_received), + text); + + gtk_signal_connect (GTK_OBJECT(invisible), "destroy", + GTK_SIGNAL_FUNC (_invisible_destroy), + text); + } + gtk_selection_convert(invisible, + selection, + GDK_SELECTION_TYPE_STRING, + time); +} + #if 0 static void e_text_real_copy_clipboard (EText *text) @@ -2011,7 +2256,7 @@ e_suck_font (GdkFont *font) GdkColor black, white; GdkImage *image; GdkGC *gc; - guchar *bitmap, *line; + guchar *line; int width, height; int black_pixel, pixel; diff --git a/widgets/e-text.h b/widgets/e-text.h index de7d28fd72..11a7a42375 100644 --- a/widgets/e-text.h +++ b/widgets/e-text.h @@ -161,7 +161,12 @@ struct _EText { ETextEventProcessor *tep; /* Text Event Processor */ - GtkWidget *invisible; /* For selection handling. */ + GtkWidget *invisible; /* For selection handling */ + gboolean has_selection; /* TRUE if we have the selection */ + gchar *primary_selection; /* Primary selection text */ + gint primary_length; /* Primary selection text length */ + gchar *clipboard_selection; /* Clipboard selection text */ + gint clipboard_length; /* Clipboard selection text length*/ }; struct _ETextClass { diff --git a/widgets/e-text/e-text-event-processor-emacs-like.c b/widgets/e-text/e-text-event-processor-emacs-like.c index 6c7c86afdc..d2bf524401 100644 --- a/widgets/e-text/e-text-event-processor-emacs-like.c +++ b/widgets/e-text/e-text-event-processor-emacs-like.c @@ -57,7 +57,7 @@ static const ETextEventProcessorCommand control_keys[26] = { E_TEP_START_OF_LINE, E_TEP_DELETE, 0, "" }, /* u */ { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* v */ { E_TEP_BACKWARD_WORD, E_TEP_DELETE, 0, "" }, /* w */ - { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* x */ + { E_TEP_SELECTION, E_TEP_DELETE, 0, "" }, /* x */ { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* y */ { E_TEP_SELECTION, E_TEP_NOP, 0, "" } /* z */ }; @@ -142,6 +142,7 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro { ETextEventProcessorCommand command; ETextEventProcessorEmacsLike *tep_el = E_TEXT_EVENT_PROCESSOR_EMACS_LIKE(tep); + command.action = E_TEP_NOP; switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1) { @@ -151,24 +152,39 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro command.action = E_TEP_MOVE; command.position = E_TEP_VALUE; command.value = event->button.position; + command.time = event->button.time; tep_el->mouse_down = TRUE; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1) { + command.time = event->button.time; tep_el->mouse_down = FALSE; + } else if (event->button.button == 2) { + command.action = E_TEP_MOVE; + command.position = E_TEP_VALUE; + command.value = event->button.position; + command.time = event->button.time; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); + + command.action = E_TEP_GET_SELECTION; + command.position = E_TEP_SELECTION; + command.value = 0; + command.time = event->button.time; } break; case GDK_MOTION_NOTIFY: if (tep_el->mouse_down) { command.action = E_TEP_SELECT; command.position = E_TEP_VALUE; + command.time = event->motion.time; command.value = event->motion.position; } break; case GDK_KEY_PRESS: { ETextEventProcessorEventKey key = event->key; + command.time = event->key.time; if (key.state & GDK_SHIFT_MASK) command.action = E_TEP_SELECT; else @@ -232,6 +248,8 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro command.position = E_TEP_FORWARD_WORD; } else if (key.state & GDK_SHIFT_MASK) { command.action = E_TEP_COPY; + command.position = E_TEP_SELECTION; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); command.action = E_TEP_DELETE; command.position = E_TEP_SELECTION; @@ -276,6 +294,12 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro } if (key.keyval == 'x') { + command.action = E_TEP_COPY; + command.position = E_TEP_SELECTION; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); + + command.action = E_TEP_DELETE; + command.position = E_TEP_SELECTION; } break; @@ -302,6 +326,7 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro } break; case GDK_KEY_RELEASE: + command.time = event->key.time; command.action = E_TEP_NOP; break; default: diff --git a/widgets/e-text/e-text-event-processor-types.h b/widgets/e-text/e-text-event-processor-types.h index 30b7bcafc9..3795058225 100644 --- a/widgets/e-text/e-text-event-processor-types.h +++ b/widgets/e-text/e-text-event-processor-types.h @@ -79,6 +79,7 @@ enum _ETextEventProcessorCommandAction { E_TEP_INSERT, E_TEP_COPY, E_TEP_PASTE, + E_TEP_GET_SELECTION, E_TEP_SET_SELECT_BY_WORD, E_TEP_ACTIVATE, @@ -90,6 +91,7 @@ struct _ETextEventProcessorCommand { ETextEventProcessorCommandAction action; int value; char *string; + guint32 time; }; struct _ETextEventProcessorEventButton { diff --git a/widgets/e-text/e-text.c b/widgets/e-text/e-text.c index 99e35348b9..cb3456cd24 100644 --- a/widgets/e-text/e-text.c +++ b/widgets/e-text/e-text.c @@ -24,6 +24,7 @@ #include <libart_lgpl/art_affine.h> #include <libart_lgpl/art_rgb.h> #include <libart_lgpl/art_rgb_bitmap_affine.h> +#include <gtk/gtkinvisible.h> #include "e-text-event-processor-emacs-like.h" @@ -76,6 +77,10 @@ enum { enum { + E_SELECTION_PRIMARY, + E_SELECTION_CLIPBOARD +}; +enum { TARGET_STRING, TARGET_TEXT, TARGET_COMPOUND_TEXT @@ -104,11 +109,15 @@ static void e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand static guint32 e_text_get_event_time (EText *text); +static void e_text_get_selection(EText *text, GdkAtom selection, guint32 time); +static void e_text_supply_selection (EText *text, guint time, GdkAtom selection, guchar *data, gint length); + static ETextSuckFont *e_suck_font (GdkFont *font); static void e_suck_font_free (ETextSuckFont *suckfont); static GnomeCanvasItemClass *parent_class; +static GdkAtom clipboard_atom = GDK_NONE; @@ -221,6 +230,11 @@ e_text_class_init (ETextClass *klass) gtk_object_add_arg_type ("EText::ellipsis", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_ELLIPSIS); + if (!clipboard_atom) + clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); + + + klass->resize = NULL; klass->change = NULL; @@ -275,6 +289,14 @@ e_text_init (EText *text) text->button_down = FALSE; text->tep = NULL; + + text->invisible = NULL; + text->has_selection = FALSE; + + text->primary_selection = NULL; + text->primary_length = 0; + text->clipboard_selection = NULL; + text->clipboard_length = 0; } /* Destroy handler for the text item */ @@ -1145,6 +1167,8 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, text = E_TEXT (item); canvas = GNOME_CANVAS_ITEM(text)->canvas; + fg_gc = GTK_WIDGET(canvas)->style->fg_gc[text->has_selection ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE]; + if (!text->text || !text->font) return; @@ -1156,7 +1180,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); + gdk_gc_set_clip_rectangle (fg_gc, &rect); clip_rect = ▭ } lines = text->lines; @@ -1194,7 +1218,9 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, sel_rect.height = text->font->ascent + text->font->descent; gtk_paint_flat_box(GTK_WIDGET(item->canvas)->style, drawable, - GTK_STATE_SELECTED, + text->has_selection ? + GTK_STATE_SELECTED : + GTK_STATE_ACTIVE, GTK_SHADOW_NONE, clip_rect, GTK_WIDGET(item->canvas), @@ -1210,7 +1236,6 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 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, @@ -1287,7 +1312,7 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 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); + gdk_gc_set_clip_rectangle (fg_gc, NULL); } } @@ -1515,6 +1540,34 @@ e_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2 = *y1 + height; } +static void +_get_xy_from_position (EText *text, gint position, gint *xp, gint *yp) +{ + if (xp || yp) { + struct line *lines; + int x, y; + int j; + x = get_line_xpos (text, lines); + y = text->cy; + for (j = 0, lines = text->lines; j < text->num_lines; lines++, j++) { + if (lines->text > text->text + position) + break; + y += text->font->ascent + text->font->descent; + } + lines --; + y -= text->font->descent; + + x += gdk_text_width (text->font, + lines->text, + position - (lines->text - text->text)); + x -= text->xofs_edit; + if (xp) + *xp = x; + if (yp) + *yp = y; + } +} + static gint _get_position_from_xy (EText *text, gint x, gint y) { @@ -1523,11 +1576,10 @@ _get_position_from_xy (EText *text, gint x, gint y) int xpos; struct line *lines; j = 0; - while (y > ypos) - { - ypos += text->font->ascent + text->font->descent; - j ++; - } + while (y > ypos) { + ypos += text->font->ascent + text->font->descent; + j ++; + } j--; if (j >= text->num_lines) j = text->num_lines - 1; @@ -1543,8 +1595,9 @@ _get_position_from_xy (EText *text, gint x, gint y) lines->text + i, 1); xpos += charwidth / 2; - if (xpos > x) + if (xpos > x) { break; + } xpos += (charwidth + 1) / 2; } return lines->text + i - text->text; @@ -1619,7 +1672,7 @@ e_text_event (GnomeCanvasItem *item, GdkEvent *event) EText *text = E_TEXT(item); ETextEventProcessorEvent e_tep_event; - gint return_val; + gint return_val = 0; e_tep_event.type = event->type; switch (event->type) { @@ -1730,6 +1783,7 @@ _get_position(EText *text, ETextEventProcessorCommand *command) { int i; int length; + int x, y; switch (command->position) { @@ -1793,7 +1847,13 @@ _get_position(EText *text, ETextEventProcessorCommand *command) return i; case E_TEP_FORWARD_LINE: + _get_xy_from_position(text, text->selection_end, &x, &y); + y += text->font->ascent + text->font->descent; + return _get_position_from_xy(text, x, y); case E_TEP_BACKWARD_LINE: + _get_xy_from_position(text, text->selection_end, &x, &y); + y -= text->font->ascent + text->font->descent; + return _get_position_from_xy(text, x, y); case E_TEP_FORWARD_PARAGRAPH: case E_TEP_BACKWARD_PARAGRAPH: @@ -1845,6 +1905,7 @@ static void e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gpointer data) { EText *text = E_TEXT(data); + int sel_start, sel_end; switch (command->action) { case E_TEP_MOVE: text->selection_start = _get_position(text, command); @@ -1852,6 +1913,11 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp break; case E_TEP_SELECT: text->selection_end = _get_position(text, command); + sel_start = MIN(text->selection_start, text->selection_end); + sel_end = MAX(text->selection_start, text->selection_end); + if (sel_start != sel_end) { + e_text_supply_selection (text, command->time, GDK_SELECTION_PRIMARY, text->text + sel_start, sel_end - sel_start); + } break; case E_TEP_DELETE: if (text->selection_end == text->selection_start) { @@ -1871,10 +1937,17 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp recalc_bounds (text); break; case E_TEP_COPY: - if (text->selection_end != text->selection_start) { + sel_start = MIN(text->selection_start, text->selection_end); + sel_end = MAX(text->selection_start, text->selection_end); + if (sel_start != sel_end) { + e_text_supply_selection (text, command->time, clipboard_atom, text->text + sel_start, sel_end - sel_start); } break; case E_TEP_PASTE: + e_text_get_selection (text, clipboard_atom, command->time); + break; + case E_TEP_GET_SELECTION: + e_text_get_selection (text, GDK_SELECTION_PRIMARY, command->time); break; case E_TEP_ACTIVATE: break; @@ -1911,6 +1984,178 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(text)); } + +static void +_selection_clear_event (GtkInvisible *invisible, + GdkEventSelection *event, + EText *text) +{ + if (event->selection == GDK_SELECTION_PRIMARY) { + g_free (text->primary_selection); + text->primary_selection = NULL; + text->primary_length = 0; + gtk_object_unref (GTK_OBJECT(invisible)); + + text->has_selection = FALSE; + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(text)); + + } else if (event->selection == clipboard_atom) { + g_free (text->clipboard_selection); + text->clipboard_selection = NULL; + text->clipboard_length = 0; + gtk_object_unref (GTK_OBJECT(invisible)); + } +} + +static void +_selection_get (GtkInvisible *invisible, + GtkSelectionData *selection_data, + guint info, + guint time_stamp, + EText *text) +{ + switch (info) { + case E_SELECTION_PRIMARY: + gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, + 8, text->primary_selection, text->primary_length); + break; + case E_SELECTION_CLIPBOARD: + gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, + 8, text->clipboard_selection, text->clipboard_length); + break; + } +} + +static void +_selection_received (GtkInvisible *invisible, + GtkSelectionData *selection_data, + guint time, + EText *text) +{ + if (selection_data->length < 0 || selection_data->type != GDK_SELECTION_TYPE_STRING) { + gtk_object_unref (GTK_OBJECT(invisible)); + return; + } else { + ETextEventProcessorCommand command; + command.action = E_TEP_INSERT; + command.position = E_TEP_SELECTION; + command.string = selection_data->data; + command.value = selection_data->length; + command.time = time; + e_text_command(text->tep, &command, text); + gtk_object_unref (GTK_OBJECT(invisible)); + } +} + +static void _invisible_destroy (GtkInvisible *invisible, + EText *text) +{ + text->invisible = NULL; +} + +static void e_text_supply_selection (EText *text, guint time, GdkAtom selection, guchar *data, gint length) +{ + gboolean successful; + GtkWidget *invisible; + + if (text->invisible) { + invisible = text->invisible; + gtk_object_ref (GTK_OBJECT(invisible)); + } else { + invisible = gtk_invisible_new(); + text->invisible = invisible; + + gtk_selection_add_target (invisible, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_PRIMARY); + gtk_selection_add_target (invisible, + clipboard_atom, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_CLIPBOARD); + + gtk_signal_connect (GTK_OBJECT(invisible), "selection_get", + GTK_SIGNAL_FUNC (_selection_get), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_clear_event", + GTK_SIGNAL_FUNC (_selection_clear_event), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_received", + GTK_SIGNAL_FUNC (_selection_received), + text); + + gtk_signal_connect (GTK_OBJECT(invisible), "destroy", + GTK_SIGNAL_FUNC (_invisible_destroy), + text); + } + + if (selection == GDK_SELECTION_PRIMARY ) { + if (text->primary_selection) { + gtk_object_unref (GTK_OBJECT(invisible)); + g_free (text->primary_selection); + } + text->primary_selection = g_strndup(data, length); + text->primary_length = length; + } else if (selection == clipboard_atom) { + if (text->clipboard_selection) { + gtk_object_unref (GTK_OBJECT(invisible)); + g_free (text->clipboard_selection); + } + text->clipboard_selection = g_strndup(data, length); + text->clipboard_length = length; + } + + successful = gtk_selection_owner_set (invisible, + selection, + time); + + if (selection == GDK_SELECTION_PRIMARY) + text->has_selection = successful; + + if (!successful) + gtk_object_unref(GTK_OBJECT(invisible)); +} + +static void +e_text_get_selection(EText *text, GdkAtom selection, guint32 time) +{ + GtkWidget *invisible; + if (text->invisible) { + invisible = text->invisible; + gtk_object_ref (GTK_OBJECT(invisible)); + } else { + invisible = gtk_invisible_new(); + text->invisible = invisible; + + gtk_selection_add_target (invisible, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_PRIMARY); + gtk_selection_add_target (invisible, + clipboard_atom, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_CLIPBOARD); + + gtk_signal_connect (GTK_OBJECT(invisible), "selection_get", + GTK_SIGNAL_FUNC (_selection_get), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_clear_event", + GTK_SIGNAL_FUNC (_selection_clear_event), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_received", + GTK_SIGNAL_FUNC (_selection_received), + text); + + gtk_signal_connect (GTK_OBJECT(invisible), "destroy", + GTK_SIGNAL_FUNC (_invisible_destroy), + text); + } + gtk_selection_convert(invisible, + selection, + GDK_SELECTION_TYPE_STRING, + time); +} + #if 0 static void e_text_real_copy_clipboard (EText *text) @@ -2011,7 +2256,7 @@ e_suck_font (GdkFont *font) GdkColor black, white; GdkImage *image; GdkGC *gc; - guchar *bitmap, *line; + guchar *line; int width, height; int black_pixel, pixel; diff --git a/widgets/e-text/e-text.h b/widgets/e-text/e-text.h index de7d28fd72..11a7a42375 100644 --- a/widgets/e-text/e-text.h +++ b/widgets/e-text/e-text.h @@ -161,7 +161,12 @@ struct _EText { ETextEventProcessor *tep; /* Text Event Processor */ - GtkWidget *invisible; /* For selection handling. */ + GtkWidget *invisible; /* For selection handling */ + gboolean has_selection; /* TRUE if we have the selection */ + gchar *primary_selection; /* Primary selection text */ + gint primary_length; /* Primary selection text length */ + gchar *clipboard_selection; /* Clipboard selection text */ + gint clipboard_length; /* Clipboard selection text length*/ }; struct _ETextClass { diff --git a/widgets/text/e-text-event-processor-emacs-like.c b/widgets/text/e-text-event-processor-emacs-like.c index 6c7c86afdc..d2bf524401 100644 --- a/widgets/text/e-text-event-processor-emacs-like.c +++ b/widgets/text/e-text-event-processor-emacs-like.c @@ -57,7 +57,7 @@ static const ETextEventProcessorCommand control_keys[26] = { E_TEP_START_OF_LINE, E_TEP_DELETE, 0, "" }, /* u */ { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* v */ { E_TEP_BACKWARD_WORD, E_TEP_DELETE, 0, "" }, /* w */ - { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* x */ + { E_TEP_SELECTION, E_TEP_DELETE, 0, "" }, /* x */ { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* y */ { E_TEP_SELECTION, E_TEP_NOP, 0, "" } /* z */ }; @@ -142,6 +142,7 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro { ETextEventProcessorCommand command; ETextEventProcessorEmacsLike *tep_el = E_TEXT_EVENT_PROCESSOR_EMACS_LIKE(tep); + command.action = E_TEP_NOP; switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button == 1) { @@ -151,24 +152,39 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro command.action = E_TEP_MOVE; command.position = E_TEP_VALUE; command.value = event->button.position; + command.time = event->button.time; tep_el->mouse_down = TRUE; } break; case GDK_BUTTON_RELEASE: if (event->button.button == 1) { + command.time = event->button.time; tep_el->mouse_down = FALSE; + } else if (event->button.button == 2) { + command.action = E_TEP_MOVE; + command.position = E_TEP_VALUE; + command.value = event->button.position; + command.time = event->button.time; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); + + command.action = E_TEP_GET_SELECTION; + command.position = E_TEP_SELECTION; + command.value = 0; + command.time = event->button.time; } break; case GDK_MOTION_NOTIFY: if (tep_el->mouse_down) { command.action = E_TEP_SELECT; command.position = E_TEP_VALUE; + command.time = event->motion.time; command.value = event->motion.position; } break; case GDK_KEY_PRESS: { ETextEventProcessorEventKey key = event->key; + command.time = event->key.time; if (key.state & GDK_SHIFT_MASK) command.action = E_TEP_SELECT; else @@ -232,6 +248,8 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro command.position = E_TEP_FORWARD_WORD; } else if (key.state & GDK_SHIFT_MASK) { command.action = E_TEP_COPY; + command.position = E_TEP_SELECTION; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); command.action = E_TEP_DELETE; command.position = E_TEP_SELECTION; @@ -276,6 +294,12 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro } if (key.keyval == 'x') { + command.action = E_TEP_COPY; + command.position = E_TEP_SELECTION; + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); + + command.action = E_TEP_DELETE; + command.position = E_TEP_SELECTION; } break; @@ -302,6 +326,7 @@ e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventPro } break; case GDK_KEY_RELEASE: + command.time = event->key.time; command.action = E_TEP_NOP; break; default: diff --git a/widgets/text/e-text-event-processor-types.h b/widgets/text/e-text-event-processor-types.h index 30b7bcafc9..3795058225 100644 --- a/widgets/text/e-text-event-processor-types.h +++ b/widgets/text/e-text-event-processor-types.h @@ -79,6 +79,7 @@ enum _ETextEventProcessorCommandAction { E_TEP_INSERT, E_TEP_COPY, E_TEP_PASTE, + E_TEP_GET_SELECTION, E_TEP_SET_SELECT_BY_WORD, E_TEP_ACTIVATE, @@ -90,6 +91,7 @@ struct _ETextEventProcessorCommand { ETextEventProcessorCommandAction action; int value; char *string; + guint32 time; }; struct _ETextEventProcessorEventButton { diff --git a/widgets/text/e-text.c b/widgets/text/e-text.c index 99e35348b9..cb3456cd24 100644 --- a/widgets/text/e-text.c +++ b/widgets/text/e-text.c @@ -24,6 +24,7 @@ #include <libart_lgpl/art_affine.h> #include <libart_lgpl/art_rgb.h> #include <libart_lgpl/art_rgb_bitmap_affine.h> +#include <gtk/gtkinvisible.h> #include "e-text-event-processor-emacs-like.h" @@ -76,6 +77,10 @@ enum { enum { + E_SELECTION_PRIMARY, + E_SELECTION_CLIPBOARD +}; +enum { TARGET_STRING, TARGET_TEXT, TARGET_COMPOUND_TEXT @@ -104,11 +109,15 @@ static void e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand static guint32 e_text_get_event_time (EText *text); +static void e_text_get_selection(EText *text, GdkAtom selection, guint32 time); +static void e_text_supply_selection (EText *text, guint time, GdkAtom selection, guchar *data, gint length); + static ETextSuckFont *e_suck_font (GdkFont *font); static void e_suck_font_free (ETextSuckFont *suckfont); static GnomeCanvasItemClass *parent_class; +static GdkAtom clipboard_atom = GDK_NONE; @@ -221,6 +230,11 @@ e_text_class_init (ETextClass *klass) gtk_object_add_arg_type ("EText::ellipsis", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_ELLIPSIS); + if (!clipboard_atom) + clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); + + + klass->resize = NULL; klass->change = NULL; @@ -275,6 +289,14 @@ e_text_init (EText *text) text->button_down = FALSE; text->tep = NULL; + + text->invisible = NULL; + text->has_selection = FALSE; + + text->primary_selection = NULL; + text->primary_length = 0; + text->clipboard_selection = NULL; + text->clipboard_length = 0; } /* Destroy handler for the text item */ @@ -1145,6 +1167,8 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, text = E_TEXT (item); canvas = GNOME_CANVAS_ITEM(text)->canvas; + fg_gc = GTK_WIDGET(canvas)->style->fg_gc[text->has_selection ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE]; + if (!text->text || !text->font) return; @@ -1156,7 +1180,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); + gdk_gc_set_clip_rectangle (fg_gc, &rect); clip_rect = ▭ } lines = text->lines; @@ -1194,7 +1218,9 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, sel_rect.height = text->font->ascent + text->font->descent; gtk_paint_flat_box(GTK_WIDGET(item->canvas)->style, drawable, - GTK_STATE_SELECTED, + text->has_selection ? + GTK_STATE_SELECTED : + GTK_STATE_ACTIVE, GTK_SHADOW_NONE, clip_rect, GTK_WIDGET(item->canvas), @@ -1210,7 +1236,6 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 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, @@ -1287,7 +1312,7 @@ e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 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); + gdk_gc_set_clip_rectangle (fg_gc, NULL); } } @@ -1515,6 +1540,34 @@ e_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2 = *y1 + height; } +static void +_get_xy_from_position (EText *text, gint position, gint *xp, gint *yp) +{ + if (xp || yp) { + struct line *lines; + int x, y; + int j; + x = get_line_xpos (text, lines); + y = text->cy; + for (j = 0, lines = text->lines; j < text->num_lines; lines++, j++) { + if (lines->text > text->text + position) + break; + y += text->font->ascent + text->font->descent; + } + lines --; + y -= text->font->descent; + + x += gdk_text_width (text->font, + lines->text, + position - (lines->text - text->text)); + x -= text->xofs_edit; + if (xp) + *xp = x; + if (yp) + *yp = y; + } +} + static gint _get_position_from_xy (EText *text, gint x, gint y) { @@ -1523,11 +1576,10 @@ _get_position_from_xy (EText *text, gint x, gint y) int xpos; struct line *lines; j = 0; - while (y > ypos) - { - ypos += text->font->ascent + text->font->descent; - j ++; - } + while (y > ypos) { + ypos += text->font->ascent + text->font->descent; + j ++; + } j--; if (j >= text->num_lines) j = text->num_lines - 1; @@ -1543,8 +1595,9 @@ _get_position_from_xy (EText *text, gint x, gint y) lines->text + i, 1); xpos += charwidth / 2; - if (xpos > x) + if (xpos > x) { break; + } xpos += (charwidth + 1) / 2; } return lines->text + i - text->text; @@ -1619,7 +1672,7 @@ e_text_event (GnomeCanvasItem *item, GdkEvent *event) EText *text = E_TEXT(item); ETextEventProcessorEvent e_tep_event; - gint return_val; + gint return_val = 0; e_tep_event.type = event->type; switch (event->type) { @@ -1730,6 +1783,7 @@ _get_position(EText *text, ETextEventProcessorCommand *command) { int i; int length; + int x, y; switch (command->position) { @@ -1793,7 +1847,13 @@ _get_position(EText *text, ETextEventProcessorCommand *command) return i; case E_TEP_FORWARD_LINE: + _get_xy_from_position(text, text->selection_end, &x, &y); + y += text->font->ascent + text->font->descent; + return _get_position_from_xy(text, x, y); case E_TEP_BACKWARD_LINE: + _get_xy_from_position(text, text->selection_end, &x, &y); + y -= text->font->ascent + text->font->descent; + return _get_position_from_xy(text, x, y); case E_TEP_FORWARD_PARAGRAPH: case E_TEP_BACKWARD_PARAGRAPH: @@ -1845,6 +1905,7 @@ static void e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gpointer data) { EText *text = E_TEXT(data); + int sel_start, sel_end; switch (command->action) { case E_TEP_MOVE: text->selection_start = _get_position(text, command); @@ -1852,6 +1913,11 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp break; case E_TEP_SELECT: text->selection_end = _get_position(text, command); + sel_start = MIN(text->selection_start, text->selection_end); + sel_end = MAX(text->selection_start, text->selection_end); + if (sel_start != sel_end) { + e_text_supply_selection (text, command->time, GDK_SELECTION_PRIMARY, text->text + sel_start, sel_end - sel_start); + } break; case E_TEP_DELETE: if (text->selection_end == text->selection_start) { @@ -1871,10 +1937,17 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp recalc_bounds (text); break; case E_TEP_COPY: - if (text->selection_end != text->selection_start) { + sel_start = MIN(text->selection_start, text->selection_end); + sel_end = MAX(text->selection_start, text->selection_end); + if (sel_start != sel_end) { + e_text_supply_selection (text, command->time, clipboard_atom, text->text + sel_start, sel_end - sel_start); } break; case E_TEP_PASTE: + e_text_get_selection (text, clipboard_atom, command->time); + break; + case E_TEP_GET_SELECTION: + e_text_get_selection (text, GDK_SELECTION_PRIMARY, command->time); break; case E_TEP_ACTIVATE: break; @@ -1911,6 +1984,178 @@ e_text_command(ETextEventProcessor *tep, ETextEventProcessorCommand *command, gp gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(text)); } + +static void +_selection_clear_event (GtkInvisible *invisible, + GdkEventSelection *event, + EText *text) +{ + if (event->selection == GDK_SELECTION_PRIMARY) { + g_free (text->primary_selection); + text->primary_selection = NULL; + text->primary_length = 0; + gtk_object_unref (GTK_OBJECT(invisible)); + + text->has_selection = FALSE; + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM(text)); + + } else if (event->selection == clipboard_atom) { + g_free (text->clipboard_selection); + text->clipboard_selection = NULL; + text->clipboard_length = 0; + gtk_object_unref (GTK_OBJECT(invisible)); + } +} + +static void +_selection_get (GtkInvisible *invisible, + GtkSelectionData *selection_data, + guint info, + guint time_stamp, + EText *text) +{ + switch (info) { + case E_SELECTION_PRIMARY: + gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, + 8, text->primary_selection, text->primary_length); + break; + case E_SELECTION_CLIPBOARD: + gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, + 8, text->clipboard_selection, text->clipboard_length); + break; + } +} + +static void +_selection_received (GtkInvisible *invisible, + GtkSelectionData *selection_data, + guint time, + EText *text) +{ + if (selection_data->length < 0 || selection_data->type != GDK_SELECTION_TYPE_STRING) { + gtk_object_unref (GTK_OBJECT(invisible)); + return; + } else { + ETextEventProcessorCommand command; + command.action = E_TEP_INSERT; + command.position = E_TEP_SELECTION; + command.string = selection_data->data; + command.value = selection_data->length; + command.time = time; + e_text_command(text->tep, &command, text); + gtk_object_unref (GTK_OBJECT(invisible)); + } +} + +static void _invisible_destroy (GtkInvisible *invisible, + EText *text) +{ + text->invisible = NULL; +} + +static void e_text_supply_selection (EText *text, guint time, GdkAtom selection, guchar *data, gint length) +{ + gboolean successful; + GtkWidget *invisible; + + if (text->invisible) { + invisible = text->invisible; + gtk_object_ref (GTK_OBJECT(invisible)); + } else { + invisible = gtk_invisible_new(); + text->invisible = invisible; + + gtk_selection_add_target (invisible, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_PRIMARY); + gtk_selection_add_target (invisible, + clipboard_atom, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_CLIPBOARD); + + gtk_signal_connect (GTK_OBJECT(invisible), "selection_get", + GTK_SIGNAL_FUNC (_selection_get), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_clear_event", + GTK_SIGNAL_FUNC (_selection_clear_event), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_received", + GTK_SIGNAL_FUNC (_selection_received), + text); + + gtk_signal_connect (GTK_OBJECT(invisible), "destroy", + GTK_SIGNAL_FUNC (_invisible_destroy), + text); + } + + if (selection == GDK_SELECTION_PRIMARY ) { + if (text->primary_selection) { + gtk_object_unref (GTK_OBJECT(invisible)); + g_free (text->primary_selection); + } + text->primary_selection = g_strndup(data, length); + text->primary_length = length; + } else if (selection == clipboard_atom) { + if (text->clipboard_selection) { + gtk_object_unref (GTK_OBJECT(invisible)); + g_free (text->clipboard_selection); + } + text->clipboard_selection = g_strndup(data, length); + text->clipboard_length = length; + } + + successful = gtk_selection_owner_set (invisible, + selection, + time); + + if (selection == GDK_SELECTION_PRIMARY) + text->has_selection = successful; + + if (!successful) + gtk_object_unref(GTK_OBJECT(invisible)); +} + +static void +e_text_get_selection(EText *text, GdkAtom selection, guint32 time) +{ + GtkWidget *invisible; + if (text->invisible) { + invisible = text->invisible; + gtk_object_ref (GTK_OBJECT(invisible)); + } else { + invisible = gtk_invisible_new(); + text->invisible = invisible; + + gtk_selection_add_target (invisible, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_PRIMARY); + gtk_selection_add_target (invisible, + clipboard_atom, + GDK_SELECTION_TYPE_STRING, + E_SELECTION_CLIPBOARD); + + gtk_signal_connect (GTK_OBJECT(invisible), "selection_get", + GTK_SIGNAL_FUNC (_selection_get), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_clear_event", + GTK_SIGNAL_FUNC (_selection_clear_event), + text); + gtk_signal_connect (GTK_OBJECT(invisible), "selection_received", + GTK_SIGNAL_FUNC (_selection_received), + text); + + gtk_signal_connect (GTK_OBJECT(invisible), "destroy", + GTK_SIGNAL_FUNC (_invisible_destroy), + text); + } + gtk_selection_convert(invisible, + selection, + GDK_SELECTION_TYPE_STRING, + time); +} + #if 0 static void e_text_real_copy_clipboard (EText *text) @@ -2011,7 +2256,7 @@ e_suck_font (GdkFont *font) GdkColor black, white; GdkImage *image; GdkGC *gc; - guchar *bitmap, *line; + guchar *line; int width, height; int black_pixel, pixel; diff --git a/widgets/text/e-text.h b/widgets/text/e-text.h index de7d28fd72..11a7a42375 100644 --- a/widgets/text/e-text.h +++ b/widgets/text/e-text.h @@ -161,7 +161,12 @@ struct _EText { ETextEventProcessor *tep; /* Text Event Processor */ - GtkWidget *invisible; /* For selection handling. */ + GtkWidget *invisible; /* For selection handling */ + gboolean has_selection; /* TRUE if we have the selection */ + gchar *primary_selection; /* Primary selection text */ + gint primary_length; /* Primary selection text length */ + gchar *clipboard_selection; /* Clipboard selection text */ + gint clipboard_length; /* Clipboard selection text length*/ }; struct _ETextClass { |