diff options
author | Owen Taylor <owt1@cornell.edu> | 1998-03-01 05:11:05 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 1998-03-01 05:11:05 +0000 |
commit | 9205edae41b7fdcdda29e8a47739c56f34834f0f (patch) | |
tree | 8f2a329df7aaa39bc746eadf55b45c42fe4bf3d2 /gtk/gtkeditable.c | |
parent | d491547e869c5b918e998e1c68449d2d0e742ada (diff) | |
download | gdk-pixbuf-9205edae41b7fdcdda29e8a47739c56f34834f0f.tar.gz |
gtk/gtkentry.[ch] gtktext.c gtkeditable.[ch]
Sat Feb 28 23:58:54 1998 Owen Taylor <owt1@cornell.edu>
* gtk/gtkentry.[ch] gtktext.c gtkeditable.[ch]
Created a new base widget type Editable for the entry and
text widgets, which encapsulates most of the selection and
clipboard handling stuff, plus some common signals.
Changed the Entry widget extensively to support this,
but the interface and appearance should be the same.
Changed the Text widget moderately to support this.
It now supports:
- Selection style cut and paste
- Clipboard style cut and paste
- Emacs style key bindings (~same as Entry)
- Word motion
- "changed" signal
There are definitely still some bugs in the new stuff.
* gtkfilesel.c gtkspinbutton.c testgtk.c: small changes
to fit the new interface more exactly.
Diffstat (limited to 'gtk/gtkeditable.c')
-rw-r--r-- | gtk/gtkeditable.c | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/gtk/gtkeditable.c b/gtk/gtkeditable.c new file mode 100644 index 000000000..93ec740ad --- /dev/null +++ b/gtk/gtkeditable.c @@ -0,0 +1,664 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <ctype.h> +#include <string.h> +#ifdef USE_XIM +#include "gdk/gdkx.h" +#endif +#include "gdk/gdkkeysyms.h" +#include "gdk/gdki18n.h" +#include "gtkeditable.h" +#include "gtkmain.h" +#include "gtkselection.h" +#include "gtksignal.h" + +#define MIN_EDITABLE_WIDTH 150 +#define DRAW_TIMEOUT 20 +#define INNER_BORDER 2 + +enum { + INSERT_TEXT, + DELETE_TEXT, + UPDATE_TEXT, + GET_CHARS, + SET_SELECTION, + CHANGED, + LAST_SIGNAL +}; + + +typedef void (*GtkEditableSignal1) (GtkObject *object, + gpointer arg1, + gint arg2, + gpointer arg3, + gpointer data); +typedef void (*GtkEditableSignal2) (GtkObject *object, + gint arg1, + gint arg2, + gpointer data); + +typedef gpointer (*GtkEditableSignal3) (GtkObject *object, + gint arg1, + gint arg2, + gpointer data); + + +static void gtk_editable_marshal_signal_1 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); +static void gtk_editable_marshal_signal_2 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); +static void gtk_editable_marshal_signal_3 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); + +static void gtk_editable_class_init (GtkEditableClass *klass); +static void gtk_editable_init (GtkEditable *editable); +static void gtk_editable_finalize (GtkObject *object); +static gint gtk_editable_selection_clear (GtkWidget *widget, + GdkEventSelection *event); +static void gtk_editable_selection_handler (GtkWidget *widget, + GtkSelectionData *selection_data, + gpointer data); +static void gtk_editable_selection_received (GtkWidget *widget, + GtkSelectionData *selection_data); + +static void gtk_editable_set_selection (GtkEditable *editable, + gint start, + gint end); + +static GtkWidgetClass *parent_class = NULL; +static gint editable_signals[LAST_SIGNAL] = { 0 }; +static GdkAtom ctext_atom = GDK_NONE; +static GdkAtom text_atom = GDK_NONE; +static GdkAtom clipboard_atom = GDK_NONE; + +guint +gtk_editable_get_type () +{ + static guint editable_type = 0; + + if (!editable_type) + { + GtkTypeInfo editable_info = + { + "GtkEditable", + sizeof (GtkEditable), + sizeof (GtkEditableClass), + (GtkClassInitFunc) gtk_editable_class_init, + (GtkObjectInitFunc) gtk_editable_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL, + }; + + editable_type = gtk_type_unique (gtk_widget_get_type (), &editable_info); + } + + return editable_type; +} + +static void +gtk_editable_class_init (GtkEditableClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + + parent_class = gtk_type_class (gtk_widget_get_type ()); + + editable_signals[INSERT_TEXT] = + gtk_signal_new ("insert_text", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkEditableClass, insert_text), + gtk_editable_marshal_signal_1, + GTK_TYPE_NONE, 3, + GTK_TYPE_STRING, GTK_TYPE_INT, + GTK_TYPE_POINTER); + editable_signals[DELETE_TEXT] = + gtk_signal_new ("delete_text", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkEditableClass, delete_text), + gtk_editable_marshal_signal_2, + GTK_TYPE_NONE, 2, + GTK_TYPE_INT, GTK_TYPE_INT); + editable_signals[UPDATE_TEXT] = + gtk_signal_new ("update_text", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkEditableClass, update_text), + gtk_editable_marshal_signal_2, + GTK_TYPE_NONE, 2, + GTK_TYPE_INT, GTK_TYPE_INT); + editable_signals[GET_CHARS] = + gtk_signal_new ("get_chars", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkEditableClass, get_chars), + gtk_editable_marshal_signal_3, + GTK_TYPE_POINTER, 2, + GTK_TYPE_INT, GTK_TYPE_INT); + editable_signals[SET_SELECTION] = + gtk_signal_new ("set_selection", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkEditableClass, set_selection), + gtk_editable_marshal_signal_2, + GTK_TYPE_NONE, 2, + GTK_TYPE_INT, GTK_TYPE_INT); + editable_signals[CHANGED] = + gtk_signal_new ("changed", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkEditableClass, changed), + gtk_signal_default_marshaller, + GTK_TYPE_NONE, 0); + + gtk_object_class_add_signals (object_class, editable_signals, LAST_SIGNAL); + + object_class->finalize = gtk_editable_finalize; + + widget_class->selection_clear_event = gtk_editable_selection_clear; + widget_class->selection_received = gtk_editable_selection_received; + + class->insert_text = NULL; + class->delete_text = NULL; + class->update_text = NULL; + class->get_chars = NULL; + class->set_selection = NULL; + class->changed = NULL; +} + +static void +gtk_editable_init (GtkEditable *editable) +{ + GTK_WIDGET_SET_FLAGS (editable, GTK_CAN_FOCUS); + + editable->selection_start_pos = 0; + editable->selection_end_pos = 0; + editable->has_selection = FALSE; + editable->editable = 1; + editable->clipboard_text = NULL; + +#ifdef USE_XIM + editable->ic = NULL; +#endif + + if (!clipboard_atom) + clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); + + gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, + GDK_TARGET_STRING, gtk_editable_selection_handler, + NULL); + gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom, + GDK_TARGET_STRING, gtk_editable_selection_handler, + NULL); + + if (!text_atom) + text_atom = gdk_atom_intern ("TEXT", FALSE); + + gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, + text_atom, + gtk_editable_selection_handler, + NULL); + gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom, + text_atom, + gtk_editable_selection_handler, + NULL); + + if (!ctext_atom) + ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); + + gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, + ctext_atom, + gtk_editable_selection_handler, + NULL); + gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom, + ctext_atom, + gtk_editable_selection_handler, + NULL); +} + +static void +gtk_editable_marshal_signal_1 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkEditableSignal1 rfunc; + + rfunc = (GtkEditableSignal1) func; + + (* rfunc) (object, GTK_VALUE_STRING (args[0]), GTK_VALUE_INT (args[1]), + GTK_VALUE_POINTER (args[2]), func_data); +} + +static void +gtk_editable_marshal_signal_2 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkEditableSignal2 rfunc; + + rfunc = (GtkEditableSignal2) func; + + (* rfunc) (object, GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]), + func_data); +} + +static void +gtk_editable_marshal_signal_3 (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkEditableSignal3 rfunc; + gpointer *return_val; + + rfunc = (GtkEditableSignal3) func; + + return_val = GTK_RETLOC_POINTER (args[2]); + + *return_val = (* rfunc) (object, + GTK_VALUE_INT (args[0]), + GTK_VALUE_INT (args[1]), + func_data); +} + +static void +gtk_editable_finalize (GtkObject *object) +{ + GtkEditable *editable; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_EDITABLE (object)); + + editable = GTK_EDITABLE (object); + +#ifdef USE_XIM + if (editable->ic) + { + gdk_ic_destroy (editable->ic); + editable->ic = NULL; + } +#endif + + (* GTK_OBJECT_CLASS (parent_class)->finalize) (object); +} + +void +gtk_editable_insert_text (GtkEditable *editable, + const gchar *new_text, + guint new_text_length, + guint *position) +{ + gchar buf[64]; + gchar *text; + + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_EDITABLE (editable)); + + if (new_text_length <= 64) + text = buf; + else + text = g_new (gchar, new_text_length); + + strncpy (text, new_text, new_text_length); + + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[INSERT_TEXT], + text, new_text_length, position); + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[CHANGED]); + + if (new_text_length > 64) + g_free (text); +} + +void +gtk_editable_delete_text (GtkEditable *editable, + guint start_pos, + guint end_pos) +{ + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_EDITABLE (editable)); + + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[DELETE_TEXT], + start_pos, end_pos); + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[CHANGED]); +} + +static void +gtk_editable_update_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_EDITABLE (editable)); + + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[UPDATE_TEXT], + start_pos, end_pos); +} + +gchar * +gtk_editable_get_chars (GtkEditable *editable, + guint start, + guint end) +{ + gchar *retval = NULL; + + g_return_val_if_fail (editable != NULL, NULL); + g_return_val_if_fail (GTK_IS_EDITABLE (editable), NULL); + + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[GET_CHARS], + start, end, &retval); + + return retval; +} + +static void +gtk_editable_set_selection (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_EDITABLE (editable)); + + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[SET_SELECTION], + start_pos, end_pos); +} + +static gint +gtk_editable_selection_clear (GtkWidget *widget, + GdkEventSelection *event) +{ + GtkEditable *editable; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_EDITABLE (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + /* Let the selection handling code know that the selection + * has been changed, since we've overriden the default handler */ + gtk_selection_clear (widget, event); + + editable = GTK_EDITABLE (widget); + + if (event->selection == GDK_SELECTION_PRIMARY) + { + if (editable->has_selection) + { + editable->has_selection = FALSE; + gtk_editable_update_text (editable, editable->selection_start_pos, + editable->selection_end_pos); + } + } + else if (event->selection == clipboard_atom) + { + g_free (editable->clipboard_text); + editable->clipboard_text = NULL; + } + + return FALSE; +} + +static void +gtk_editable_selection_handler (GtkWidget *widget, + GtkSelectionData *selection_data, + gpointer data) +{ + GtkEditable *editable; + gint selection_start_pos; + gint selection_end_pos; + + gchar *str; + gint length; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_EDITABLE (widget)); + + editable = GTK_EDITABLE (widget); + + if (selection_data->selection == GDK_SELECTION_PRIMARY) + { + selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos); + selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos); + str = gtk_editable_get_chars(editable, selection_start_pos, + selection_end_pos); + length = selection_end_pos - selection_start_pos; + } + else /* CLIPBOARD */ + { + if (!editable->clipboard_text) + return; /* Refuse */ + + str = editable->clipboard_text; + length = strlen (editable->clipboard_text); + } + + if (selection_data->target == GDK_SELECTION_TYPE_STRING) + { + gtk_selection_data_set (selection_data, + GDK_SELECTION_TYPE_STRING, + 8*sizeof(gchar), str, length); + } + else if (selection_data->target == text_atom || + selection_data->target == ctext_atom) + { + guchar *text; + gchar c; + GdkAtom encoding; + gint format; + gint new_length; + + c = str[length]; + str[length] = '\0'; + gdk_string_to_compound_text (str, &encoding, &format, &text, &new_length); + gtk_selection_data_set (selection_data, encoding, format, text, new_length); + gdk_free_compound_text (text); + str[length] = c; + } + + if (str != editable->clipboard_text) + g_free (str); +} + +static void +gtk_editable_selection_received (GtkWidget *widget, + GtkSelectionData *selection_data) +{ + GtkEditable *editable; + gint reselect; + gint old_pos; + gint tmp_pos; + enum {INVALID, STRING, CTEXT} type; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_EDITABLE (widget)); + + editable = GTK_EDITABLE (widget); + + if (selection_data->type == GDK_TARGET_STRING) + type = STRING; + else if (selection_data->type == ctext_atom) + type = CTEXT; + else + type = INVALID; + + if (type == INVALID || selection_data->length < 0) + { + /* avoid infinite loop */ + if (selection_data->target != GDK_TARGET_STRING) + gtk_selection_convert (widget, selection_data->selection, + GDK_TARGET_STRING, GDK_CURRENT_TIME); + return; + } + + reselect = FALSE; + + if ((editable->selection_start_pos != editable->selection_end_pos) && + (!editable->has_selection || + (selection_data->selection == clipboard_atom))) + { + reselect = TRUE; + + /* Don't want to call gtk_editable_delete_selection here if we are going + * to reclaim the selection to avoid extra server traffic */ + if (editable->has_selection) + { + gtk_editable_delete_text (editable, + MIN (editable->selection_start_pos, editable->selection_end_pos), + MAX (editable->selection_start_pos, editable->selection_end_pos)); + } + else + gtk_editable_delete_selection (editable); + } + + tmp_pos = old_pos = editable->current_pos; + + switch (type) + { + case STRING: + selection_data->data[selection_data->length] = 0; + gtk_editable_insert_text (editable, selection_data->data, + strlen (selection_data->data), &tmp_pos); + editable->current_pos = tmp_pos; + break; + case CTEXT: + { + gchar **list; + gint count; + gint i; + + count = gdk_text_property_to_text_list (selection_data->type, + selection_data->format, + selection_data->data, + selection_data->length, + &list); + for (i=0; i<count; i++) + { + gtk_editable_insert_text (editable, list[i], strlen (list[i]), &tmp_pos); + editable->current_pos = tmp_pos; + } + if (count > 0) + gdk_free_text_list (list); + } + break; + case INVALID: /* quiet compiler */ + break; + } + + if (reselect) + gtk_editable_set_selection (editable, old_pos, editable->current_pos); +} + +void +gtk_editable_delete_selection (GtkEditable *editable) +{ + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_delete_text (editable, + MIN (editable->selection_start_pos, editable->selection_end_pos), + MAX (editable->selection_start_pos, editable->selection_end_pos)); + + editable->selection_start_pos = 0; + editable->selection_end_pos = 0; + + if (editable->has_selection) + { + editable->has_selection = FALSE; + if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == GTK_WIDGET (editable)->window) + gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME); + } +} + +void +gtk_editable_claim_selection (GtkEditable *editable, + gboolean claim, + guint32 time) +{ + g_return_if_fail (GTK_WIDGET_REALIZED (editable)); + + if (claim) + { + if (gtk_selection_owner_set (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, time)) + editable->has_selection = TRUE; + } + else + { + if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == + GTK_WIDGET(editable)->window) + gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, time); + } +} + +void +gtk_editable_select_region (GtkEditable *editable, + guint start, + guint end) +{ + if (GTK_WIDGET_REALIZED (editable)) + gtk_editable_claim_selection (editable, start != end, GDK_CURRENT_TIME); + + gtk_editable_set_selection (editable, start, end); +} + +void +gtk_editable_cut_clipboard (GtkEditable *editable, GdkEventKey *event) +{ + gtk_editable_copy_clipboard (editable, event); + gtk_editable_delete_selection (editable); +} + +void +gtk_editable_copy_clipboard (GtkEditable *editable, GdkEventKey *event) +{ + gint selection_start_pos; + gint selection_end_pos; + + selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos); + selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos); + + if (selection_start_pos != selection_end_pos) + { + if (gtk_selection_owner_set (GTK_WIDGET (editable), + clipboard_atom, + event->time)) + editable->clipboard_text = gtk_editable_get_chars (editable, + editable->selection_start_pos, + editable->selection_end_pos); + } +} + +void +gtk_editable_paste_clipboard (GtkEditable *editable, GdkEventKey *event) +{ + gtk_selection_convert (GTK_WIDGET(editable), + clipboard_atom, ctext_atom, event->time); +} + +void +gtk_editable_changed (GtkEditable *editable) +{ + gtk_signal_emit (GTK_OBJECT (editable), editable_signals[CHANGED]); +} |