From a01de808cd46baea31419a42b221cb65974a9fb1 Mon Sep 17 00:00:00 2001 From: Chris Lahey Date: Tue, 11 Jan 2000 06:44:57 +0000 Subject: Removed some code which got in the way of testing properly. * widgets/test-minicard.c: Removed some code which got in the way of testing properly. * widgets/e-minicard-label.c (e_minicard_label_realize): Made the field text item editable. * widgets/Makefile.am: Added e-text-event-process*.[ch]. * widgets/e-text.c, widgets/e-text.h: Changed these to support editing. * widgets/e-text-event-processor.c, widgets/e-text-event-processor.h, widgets/e-text-event-processor-types.h, widgets/e-text-event-processor-emacs-like.c, widgets/e-text-event-processor-emacs-like.h: These are a new pair of classes which handle all events from the text item and convert them into commands. svn path=/trunk/; revision=1553 --- widgets/text/e-text-event-processor-emacs-like.c | 327 ++++++++++++++++ widgets/text/e-text-event-processor-emacs-like.h | 68 ++++ widgets/text/e-text-event-processor-types.h | 131 +++++++ widgets/text/e-text-event-processor.c | 103 +++++ widgets/text/e-text-event-processor.h | 74 ++++ widgets/text/e-text.c | 464 +++++++++++++++++++++-- widgets/text/e-text.h | 20 + 7 files changed, 1160 insertions(+), 27 deletions(-) create mode 100644 widgets/text/e-text-event-processor-emacs-like.c create mode 100644 widgets/text/e-text-event-processor-emacs-like.h create mode 100644 widgets/text/e-text-event-processor-types.h create mode 100644 widgets/text/e-text-event-processor.c create mode 100644 widgets/text/e-text-event-processor.h (limited to 'widgets/text') diff --git a/widgets/text/e-text-event-processor-emacs-like.c b/widgets/text/e-text-event-processor-emacs-like.c new file mode 100644 index 0000000000..1d7d36d45f --- /dev/null +++ b/widgets/text/e-text-event-processor-emacs-like.c @@ -0,0 +1,327 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-text-event-processor.c + * Copyright (C) 2000 Helix Code, Inc. + * Author: Chris Lahey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "e-text-event-processor-emacs-like.h" +static void e_text_event_processor_emacs_like_init (ETextEventProcessorEmacsLike *card); +static void e_text_event_processor_emacs_like_class_init (ETextEventProcessorEmacsLikeClass *klass); +static gint e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventProcessorEvent *event); + +static ETextEventProcessorClass *parent_class = NULL; + +/* The arguments we take */ +enum { + ARG_0 +}; + +static const ETextEventProcessorCommand control_keys[26] = +{ + { E_TEP_START_OF_LINE, E_TEP_MOVE, 0, "" }, /* a */ + { E_TEP_BACKWARD_CHARACTER, E_TEP_MOVE, 0, "" }, /* b */ + { E_TEP_SELECTION, E_TEP_COPY, 0, "" }, /* c */ + { E_TEP_FORWARD_CHARACTER, E_TEP_DELETE, 0, "" }, /* d */ + { E_TEP_END_OF_LINE, E_TEP_MOVE, 0, "" }, /* e */ + { E_TEP_FORWARD_CHARACTER, E_TEP_MOVE, 0, "" }, /* f */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* g */ + { E_TEP_BACKWARD_CHARACTER, E_TEP_DELETE, 0, "" }, /* h */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* i */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* j */ + { E_TEP_END_OF_LINE, E_TEP_DELETE, 0, "" }, /* k */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* l */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* m */ + { E_TEP_FORWARD_LINE, E_TEP_MOVE, 0, "" }, /* n */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* o */ + { E_TEP_BACKWARD_LINE, E_TEP_MOVE, 0, "" }, /* p */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* q */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* r */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* s */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* t */ + { 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_NOP, 0, "" }, /* y */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" } /* z */ +}; + +static const ETextEventProcessorCommand alt_keys[26] = +{ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* a */ + { E_TEP_BACKWARD_WORD, E_TEP_MOVE, 0, "" }, /* b */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* c */ + { E_TEP_FORWARD_WORD, E_TEP_DELETE, 0, "" }, /* d */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* e */ + { E_TEP_FORWARD_WORD, E_TEP_MOVE, 0, "" }, /* f */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* g */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* h */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* i */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* j */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* k */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* l */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* m */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* n */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* o */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* p */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* q */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* r */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* s */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* t */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* u */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* v */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* w */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* x */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* y */ + { E_TEP_SELECTION, E_TEP_NOP, 0, "" } /* z */ + +}; + +GtkType +e_text_event_processor_emacs_like_get_type (void) +{ + static GtkType text_event_processor_emacs_like_type = 0; + + if (!text_event_processor_emacs_like_type) + { + static const GtkTypeInfo text_event_processor_emacs_like_info = + { + "ETextEventProcessorEmacsLike", + sizeof (ETextEventProcessorEmacsLike), + sizeof (ETextEventProcessorEmacsLikeClass), + (GtkClassInitFunc) e_text_event_processor_emacs_like_class_init, + (GtkObjectInitFunc) e_text_event_processor_emacs_like_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + text_event_processor_emacs_like_type = gtk_type_unique (e_text_event_processor_get_type (), &text_event_processor_emacs_like_info); + } + + return text_event_processor_emacs_like_type; +} + +static void +e_text_event_processor_emacs_like_class_init (ETextEventProcessorEmacsLikeClass *klass) +{ + GtkObjectClass *object_class; + ETextEventProcessorClass *processor_class; + + object_class = (GtkObjectClass*) klass; + processor_class = (ETextEventProcessorClass*) klass; + + parent_class = gtk_type_class (e_text_event_processor_get_type ()); + + processor_class->event = e_text_event_processor_emacs_like_event; +} + +static void +e_text_event_processor_emacs_like_init (ETextEventProcessorEmacsLike *tep) +{ +} + +static gint +e_text_event_processor_emacs_like_event (ETextEventProcessor *tep, ETextEventProcessorEvent *event) +{ + ETextEventProcessorCommand command; + ETextEventProcessorEmacsLike *tep_el = E_TEXT_EVENT_PROCESSOR_EMACS_LIKE(tep); + switch (event->type) { + case GDK_BUTTON_PRESS: + if (event->button.button == 1) { + if (event->button.state & GDK_SHIFT_MASK) + command.action = E_TEP_SELECT; + else + command.action = E_TEP_MOVE; + command.position = E_TEP_VALUE; + command.value = event->button.position; + tep_el->mouse_down = TRUE; + } + break; + case GDK_BUTTON_RELEASE: + if (event->button.button == 1) { + tep_el->mouse_down = FALSE; + } + break; + case GDK_MOTION_NOTIFY: + if (tep_el->mouse_down) { + command.action = E_TEP_SELECT; + command.position = E_TEP_VALUE; + command.value = event->motion.position; + } + break; + case GDK_KEY_PRESS: + { + ETextEventProcessorEventKey key = event->key; + if (key.state & GDK_SHIFT_MASK) + command.action = E_TEP_SELECT; + else + command.action = E_TEP_MOVE; + switch(key.keyval) { + case GDK_Home: + if (key.state & GDK_CONTROL_MASK) + command.position = E_TEP_START_OF_BUFFER; + else + command.position = E_TEP_START_OF_LINE; + break; + case GDK_End: + if (key.state & GDK_CONTROL_MASK) + command.position = E_TEP_END_OF_BUFFER; + else + command.position = E_TEP_END_OF_LINE; + break; + case GDK_Page_Up: command.position = E_TEP_BACKWARD_PAGE; break; + case GDK_Page_Down: command.position = E_TEP_FORWARD_PAGE; break; + /* CUA has Ctrl-Up/Ctrl-Down as paragraph up down */ + case GDK_Up: command.position = E_TEP_BACKWARD_LINE; break; + case GDK_Down: command.position = E_TEP_FORWARD_LINE; break; + case GDK_Left: + if (key.state & GDK_CONTROL_MASK) + command.position = E_TEP_BACKWARD_WORD; + else + command.position = E_TEP_BACKWARD_CHARACTER; + break; + case GDK_Right: + if (key.state & GDK_CONTROL_MASK) + command.position = E_TEP_FORWARD_WORD; + else + command.position = E_TEP_FORWARD_CHARACTER; + break; + + case GDK_BackSpace: + command.action = E_TEP_DELETE; + if (key.state & GDK_CONTROL_MASK) + command.position = E_TEP_BACKWARD_WORD; + else + command.position = E_TEP_BACKWARD_CHARACTER; + break; + case GDK_Clear: + command.action = E_TEP_DELETE; + command.position = E_TEP_END_OF_LINE; + break; + case GDK_Insert: + if (key.state & GDK_SHIFT_MASK) { + command.action = E_TEP_PASTE; + command.position = E_TEP_SELECTION; + } else if (key.state & GDK_CONTROL_MASK) { + command.action = E_TEP_COPY; + command.position = E_TEP_SELECTION; + } else { + /* gtk_toggle_insert(text) -- IMPLEMENT */ + } + break; + case GDK_Delete: + if (key.state & GDK_CONTROL_MASK){ + command.action = E_TEP_DELETE; + command.position = E_TEP_FORWARD_WORD; + } else if (key.state & GDK_SHIFT_MASK) { + command.action = E_TEP_COPY; + + command.action = E_TEP_DELETE; + command.position = E_TEP_SELECTION; + } else { + command.action = E_TEP_DELETE; + command.position = E_TEP_FORWARD_CHARACTER; + } + break; + case GDK_Tab: + command.action = E_TEP_INSERT; + command.position = E_TEP_SELECTION; + command.value = 1; + command.string = "\t"; + break; + case GDK_Return: + if (key.state & GDK_CONTROL_MASK) { + command.action = E_TEP_ACTIVATE; + command.position = E_TEP_SELECTION; + } else { + command.action = E_TEP_INSERT; + command.position = E_TEP_SELECTION; + command.value = 1; + command.string = "\n"; + } + break; + case GDK_Escape: + command.action = E_TEP_NOP; + command.position = E_TEP_SELECTION; + /* Don't insert literally */ + break; + + default: + if (key.state & GDK_CONTROL_MASK) { + if ((key.keyval >= 'A') && (key.keyval <= 'Z')) + key.keyval -= 'A' - 'a'; + + if ((key.keyval >= 'a') && (key.keyval <= 'z')) { + command.position = control_keys[(int) (key.keyval - 'a')].position; + if (control_keys[(int) (key.keyval - 'a')].action != E_TEP_MOVE) + command.action = control_keys[(int) (key.keyval - 'a')].action; + command.value = control_keys[(int) (key.keyval - 'a')].value; + command.string = control_keys[(int) (key.keyval - 'a')].string; + } + + if (key.keyval == 'x') { + } + + break; + } else if (key.state & GDK_MOD1_MASK) { + if ((key.keyval >= 'A') && (key.keyval <= 'Z')) + key.keyval -= 'A' - 'a'; + + if ((key.keyval >= 'a') && (key.keyval <= 'z')) { + command.position = alt_keys[(int) (key.keyval - 'a')].position; + if (alt_keys[(int) (key.keyval - 'a')].action != E_TEP_MOVE) + command.action = alt_keys[(int) (key.keyval - 'a')].action; + command.value = alt_keys[(int) (key.keyval - 'a')].value; + command.string = alt_keys[(int) (key.keyval - 'a')].string; + } + } else if (key.length > 0) { + command.action = E_TEP_INSERT; + command.position = E_TEP_SELECTION; + command.value = strlen(key.string); + command.string = key.string; + + } else { + command.action = E_TEP_NOP; + } + } + break; + case GDK_KEY_RELEASE: + command.action = E_TEP_NOP; + break; + default: + command.action = E_TEP_NOP; + break; + } + } + if (command.action != E_TEP_NOP) { + gtk_signal_emit_by_name (GTK_OBJECT (tep), "command", &command); + return 1; + } + else + return 0; +} + +ETextEventProcessor * +e_text_event_processor_emacs_like_new (void) +{ + ETextEventProcessorEmacsLike *retval = gtk_type_new (e_text_event_processor_emacs_like_get_type ()); + return E_TEXT_EVENT_PROCESSOR (retval); +} + diff --git a/widgets/text/e-text-event-processor-emacs-like.h b/widgets/text/e-text-event-processor-emacs-like.h new file mode 100644 index 0000000000..651bb552b3 --- /dev/null +++ b/widgets/text/e-text-event-processor-emacs-like.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-text-event-processor-emacs-like.h + * Copyright (C) 2000 Helix Code, Inc. + * Author: Chris Lahey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_H__ +#define __E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_H__ + +#include +#include "e-text-event-processor.h" + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +/* ETextEventProcessorEmacsLike - Turns events on a text widget into commands. Uses an emacs-ish interface. + * + */ + +#define E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_TYPE (e_text_event_processor_emacs_like_get_type ()) +#define E_TEXT_EVENT_PROCESSOR_EMACS_LIKE(obj) (GTK_CHECK_CAST ((obj), E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_TYPE, ETextEventProcessorEmacsLike)) +#define E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_TYPE, ETextEventProcessorEmacsLikeClass)) +#define E_IS_TEXT_EVENT_PROCESSOR_EMACS_LIKE(obj) (GTK_CHECK_TYPE ((obj), E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_TYPE)) +#define E_IS_TEXT_EVENT_PROCESSOR_EMACS_LIKE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_TYPE)) + + +typedef struct _ETextEventProcessorEmacsLike ETextEventProcessorEmacsLike; +typedef struct _ETextEventProcessorEmacsLikeClass ETextEventProcessorEmacsLikeClass; + +struct _ETextEventProcessorEmacsLike +{ + ETextEventProcessor parent; + + /* object specific fields */ + gboolean mouse_down; +}; + +struct _ETextEventProcessorEmacsLikeClass +{ + ETextEventProcessorClass parent_class; +}; + + +GtkType e_text_event_processor_emacs_like_get_type (void); +ETextEventProcessor *e_text_event_processor_emacs_like_new (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_H__ */ diff --git a/widgets/text/e-text-event-processor-types.h b/widgets/text/e-text-event-processor-types.h new file mode 100644 index 0000000000..30b7bcafc9 --- /dev/null +++ b/widgets/text/e-text-event-processor-types.h @@ -0,0 +1,131 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-text-event-processor.h + * Copyright (C) 2000 Helix Code, Inc. + * Author: Chris Lahey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __E_TEXT_EVENT_PROCESSOR_TYPES_H__ +#define __E_TEXT_EVENT_PROCESSOR_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#include + +typedef enum _ETextEventProcessorCommandPosition ETextEventProcessorCommandPosition; +typedef enum _ETextEventProcessorCommandAction ETextEventProcessorCommandAction; +typedef struct _ETextEventProcessorCommand ETextEventProcessorCommand; + +typedef union _ETextEventProcessorEvent ETextEventProcessorEvent; +typedef struct _ETextEventProcessorEventButton ETextEventProcessorEventButton; +typedef struct _ETextEventProcessorEventKey ETextEventProcessorEventKey; +typedef struct _ETextEventProcessorEventMotion ETextEventProcessorEventMotion; + +enum _ETextEventProcessorCommandPosition { + E_TEP_VALUE, + E_TEP_SELECTION, + + E_TEP_START_OF_BUFFER, + E_TEP_END_OF_BUFFER, + + E_TEP_START_OF_LINE, + E_TEP_END_OF_LINE, + + E_TEP_FORWARD_CHARACTER, + E_TEP_BACKWARD_CHARACTER, + + E_TEP_FORWARD_WORD, + E_TEP_BACKWARD_WORD, + + E_TEP_FORWARD_LINE, + E_TEP_BACKWARD_LINE, + + E_TEP_FORWARD_PARAGRAPH, + E_TEP_BACKWARD_PARAGRAPH, + + E_TEP_FORWARD_PAGE, + E_TEP_BACKWARD_PAGE +}; + +enum _ETextEventProcessorCommandAction { + E_TEP_MOVE, + E_TEP_SELECT, + E_TEP_DELETE, + + E_TEP_INSERT, + E_TEP_COPY, + E_TEP_PASTE, + E_TEP_SET_SELECT_BY_WORD, + E_TEP_ACTIVATE, + + E_TEP_NOP +}; + +struct _ETextEventProcessorCommand { + ETextEventProcessorCommandPosition position; + ETextEventProcessorCommandAction action; + int value; + char *string; +}; + +struct _ETextEventProcessorEventButton { + GdkEventType type; + guint32 time; + guint state; + guint button; + gint position; +}; + +struct _ETextEventProcessorEventKey { + GdkEventType type; + guint32 time; + guint state; + guint keyval; + gint length; + gchar *string; +}; + +struct _ETextEventProcessorEventMotion { + GdkEventType type; + guint32 time; + guint state; + gint position; +}; + +union _ETextEventProcessorEvent { + GdkEventType type; + ETextEventProcessorEventButton button; + ETextEventProcessorEventKey key; + ETextEventProcessorEventMotion motion; +}; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __E_TEXT_EVENT_PROCESSOR_TYPES_H__ */ diff --git a/widgets/text/e-text-event-processor.c b/widgets/text/e-text-event-processor.c new file mode 100644 index 0000000000..47f028ca62 --- /dev/null +++ b/widgets/text/e-text-event-processor.c @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-text-event-processor.c + * Copyright (C) 2000 Helix Code, Inc. + * Author: Chris Lahey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "e-text-event-processor.h" +static void e_text_event_processor_init (ETextEventProcessor *card); +static void e_text_event_processor_class_init (ETextEventProcessorClass *klass); + +static GtkObjectClass *parent_class = NULL; + +/* The arguments we take */ +enum { + ARG_0 +}; + +enum { + E_TEP_EVENT, + E_TEP_LAST_SIGNAL +}; + +static guint e_tep_signals[E_TEP_LAST_SIGNAL] = { 0 }; + +GtkType +e_text_event_processor_get_type (void) +{ + static GtkType text_event_processor_type = 0; + + if (!text_event_processor_type) + { + static const GtkTypeInfo text_event_processor_info = + { + "ETextEventProcessor", + sizeof (ETextEventProcessor), + sizeof (ETextEventProcessorClass), + (GtkClassInitFunc) e_text_event_processor_class_init, + (GtkObjectInitFunc) e_text_event_processor_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + text_event_processor_type = gtk_type_unique (gtk_object_get_type (), &text_event_processor_info); + } + + return text_event_processor_type; +} + +static void +e_text_event_processor_class_init (ETextEventProcessorClass *klass) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass*) klass; + + parent_class = gtk_type_class (gtk_object_get_type ()); + + e_tep_signals[E_TEP_EVENT] = + gtk_signal_new ("command", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (ETextEventProcessorClass, command), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + + gtk_object_class_add_signals (object_class, e_tep_signals, E_TEP_LAST_SIGNAL); + + klass->event = NULL; + klass->command = NULL; +} + +static void +e_text_event_processor_init (ETextEventProcessor *tep) +{ +} + +gint +e_text_event_processor_handle_event (ETextEventProcessor *tep, ETextEventProcessorEvent *event) +{ + if (E_TEXT_EVENT_PROCESSOR_CLASS(GTK_OBJECT(tep)->klass)->event) { + return E_TEXT_EVENT_PROCESSOR_CLASS(GTK_OBJECT(tep)->klass)->event(tep, event); + } else { + return 0; + } +} diff --git a/widgets/text/e-text-event-processor.h b/widgets/text/e-text-event-processor.h new file mode 100644 index 0000000000..1fc79f3f70 --- /dev/null +++ b/widgets/text/e-text-event-processor.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-text-event-processor.h + * Copyright (C) 2000 Helix Code, Inc. + * Author: Chris Lahey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __E_TEXT_EVENT_PROCESSOR_H__ +#define __E_TEXT_EVENT_PROCESSOR_H__ + +#include +#include "e-text-event-processor-types.h" + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +/* ETextEventProcessor - Turns events on a text widget into commands. + * + */ + +#define E_TEXT_EVENT_PROCESSOR_TYPE (e_text_event_processor_get_type ()) +#define E_TEXT_EVENT_PROCESSOR(obj) (GTK_CHECK_CAST ((obj), E_TEXT_EVENT_PROCESSOR_TYPE, ETextEventProcessor)) +#define E_TEXT_EVENT_PROCESSOR_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TEXT_EVENT_PROCESSOR_TYPE, ETextEventProcessorClass)) +#define E_IS_TEXT_EVENT_PROCESSOR(obj) (GTK_CHECK_TYPE ((obj), E_TEXT_EVENT_PROCESSOR_TYPE)) +#define E_IS_TEXT_EVENT_PROCESSOR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TEXT_EVENT_PROCESSOR_TYPE)) + + +typedef struct _ETextEventProcessor ETextEventProcessor; +typedef struct _ETextEventProcessorClass ETextEventProcessorClass; + +struct _ETextEventProcessor +{ + GtkObject parent; + + /* object specific fields */ + +}; + +struct _ETextEventProcessorClass +{ + GtkObjectClass parent_class; + + /* signals */ + void (* command) (ETextEventProcessor *tep, ETextEventProcessorCommand *command); + + /* virtual functions */ + gint (* event) (ETextEventProcessor *tep, ETextEventProcessorEvent *event); +}; + + +GtkType e_text_event_processor_get_type (void); +gint e_text_event_processor_handle_event (ETextEventProcessor *tep, ETextEventProcessorEvent *event); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __E_TEXT_EVENT_PROCESSOR_H__ */ diff --git a/widgets/text/e-text.c b/widgets/text/e-text.c index b88130e3c8..f6daa3f3cf 100644 --- a/widgets/text/e-text.c +++ b/widgets/text/e-text.c @@ -18,12 +18,15 @@ #include #include +#include #include "e-text.h" #include /* for BlackPixel */ #include #include #include +#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 */ diff --git a/widgets/text/e-text.h b/widgets/text/e-text.h index e14199f16e..b18b93d7f2 100644 --- a/widgets/text/e-text.h +++ b/widgets/text/e-text.h @@ -20,6 +20,7 @@ #define E_TEXT_H #include +#include "e-text-event-processor.h" BEGIN_GNOME_DECLS @@ -59,8 +60,15 @@ BEGIN_GNOME_DECLS * text_height double R Used to query the rendered height of the text * * These are ignored in the AA version: + * editable boolean RW Can this item be edited * use_ellipsis boolean RW Whether to use ellipsises if text gets cut off. Meaningless if clip == false. * ellipsis string RW The characters to use as ellipsis. NULL = "...". + * + * These are not implemented yet: + * multi_line boolean RW Line wrap when not editing. + * multi_line_on_edit boolean RW Switch to line wrap when editing. + * background boolean RW Draw a background rectangle. + * background_on_edit boolean RW Draw a background when editing. */ #define E_TYPE_TEXT (e_text_get_type ()) @@ -129,6 +137,18 @@ struct _EText { char *ellipsis; /* The ellipsis characters. NULL = "...". */ double ellipsis_width; /* The width of the ellipsis. */ gboolean use_ellipsis; /* Whether to use the ellipsis. */ + + gboolean editable; /* Item is editable */ + gboolean editing; /* Item is currently being edited */ + + int xofs_edit; /* Offset because of editing */ + + /* This needs to be reworked a bit once we get line wrapping. */ + int selection_start; /* Start of selection */ + int selection_end; /* End of selection */ + gboolean select_by_word; /* Current selection is by word */ + + ETextEventProcessor *tep; /* Text Event Processor */ }; struct _ETextClass { -- cgit v1.2.3