* 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
+ * modify it under the terms of the GNU Lesser 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.
+ * Lesser 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.
+ * You should have received a copy of the GNU Lesser 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 <ctype.h>
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. 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/.
+ */
+
#include <string.h>
-#ifdef USE_XIM
-#include "gdk/gdkx.h"
-#endif
+
+#include <pango/pango.h>
+
#include "gdk/gdkkeysyms.h"
-#include "gdk/gdki18n.h"
+#include "gtkbindings.h"
+#include "gtkclipboard.h"
#include "gtkentry.h"
+#include "gtkimmulticontext.h"
+#include "gtkintl.h"
#include "gtkmain.h"
+#include "gtkmenu.h"
+#include "gtkmenuitem.h"
#include "gtkselection.h"
#include "gtksignal.h"
-#ifdef USE_XIM
-#include "gdk/gdkx.h"
-#endif
-
#define MIN_ENTRY_WIDTH 150
#define DRAW_TIMEOUT 20
#define INNER_BORDER 2
+/* Initial size of buffer, in bytes */
+#define MIN_SIZE 16
+
+/* Maximum size of text buffer, in bytes */
+#define MAX_SIZE G_MAXUSHORT
enum {
INSERT_TEXT,
DELETE_TEXT,
CHANGED,
- SET_TEXT,
ACTIVATE,
+ MOVE_CURSOR,
+ INSERT_AT_CURSOR,
+ DELETE_FROM_CURSOR,
+ CUT_CLIPBOARD,
+ COPY_CLIPBOARD,
+ PASTE_CLIPBOARD,
+ TOGGLE_OVERWRITE,
LAST_SIGNAL
};
+enum {
+ ARG_0,
+ ARG_TEXT_POSITION,
+ ARG_EDITABLE,
+ ARG_MAX_LENGTH,
+ ARG_VISIBILITY,
+ ARG_INVISIBLE_CHAR
+};
-typedef void (*GtkTextFunction) (GtkEntry *entry);
-typedef void (*GtkEntrySignal1) (GtkObject *object,
- gpointer arg1,
- gint arg2,
- gpointer arg3,
- gpointer data);
-typedef void (*GtkEntrySignal2) (GtkObject *object,
- gint arg1,
- gint arg2,
- gpointer data);
-
-
-static void gtk_entry_marshal_signal_1 (GtkObject *object,
- GtkSignalFunc func,
- gpointer func_data,
- GtkArg *args);
-static void gtk_entry_marshal_signal_2 (GtkObject *object,
- GtkSignalFunc func,
- gpointer func_data,
- GtkArg *args);
-
-static void gtk_entry_class_init (GtkEntryClass *klass);
-static void gtk_entry_init (GtkEntry *entry);
-static void gtk_entry_destroy (GtkObject *object);
-static void gtk_entry_realize (GtkWidget *widget);
-static void gtk_entry_unrealize (GtkWidget *widget);
-static void gtk_entry_draw_focus (GtkWidget *widget);
-static void gtk_entry_size_request (GtkWidget *widget,
- GtkRequisition *requisition);
-static void gtk_entry_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation);
-static void gtk_entry_draw (GtkWidget *widget,
- GdkRectangle *area);
-static gint gtk_entry_expose (GtkWidget *widget,
- GdkEventExpose *event);
-static gint gtk_entry_button_press (GtkWidget *widget,
- GdkEventButton *event);
-static gint gtk_entry_button_release (GtkWidget *widget,
- GdkEventButton *event);
-static gint gtk_entry_motion_notify (GtkWidget *widget,
- GdkEventMotion *event);
-static gint gtk_entry_key_press (GtkWidget *widget,
- GdkEventKey *event);
-static gint gtk_entry_focus_in (GtkWidget *widget,
- GdkEventFocus *event);
-static gint gtk_entry_focus_out (GtkWidget *widget,
- GdkEventFocus *event);
-static gint gtk_entry_selection_clear (GtkWidget *widget,
- GdkEventSelection *event);
-static void gtk_entry_selection_handler (GtkWidget *widget,
- GtkSelectionData *selection_data,
- gpointer data);
-static void gtk_entry_selection_received (GtkWidget *widget,
- GtkSelectionData *selection_data);
-static void gtk_entry_draw_text (GtkEntry *entry);
-static void gtk_entry_draw_cursor (GtkEntry *entry);
-static void gtk_entry_queue_draw (GtkEntry *entry);
-static gint gtk_entry_timer (gpointer data);
-static gint gtk_entry_position (GtkEntry *entry,
- gint x);
- void gtk_entry_adjust_scroll (GtkEntry *entry);
-static void gtk_entry_grow_text (GtkEntry *entry);
-static void gtk_entry_insert_text (GtkEntry *entry,
- const gchar *new_text,
- gint new_text_length,
- gint *position);
-static void gtk_entry_delete_text (GtkEntry *entry,
- gint start_pos,
- gint end_pos);
-static void gtk_real_entry_insert_text (GtkEntry *entry,
- const gchar *new_text,
- gint new_text_length,
- gint *position);
-static void gtk_real_entry_delete_text (GtkEntry *entry,
- gint start_pos,
- gint end_pos);
-
-static gint move_backward_character (gchar *str, gint index);
-static void gtk_move_forward_character (GtkEntry *entry);
-static void gtk_move_backward_character (GtkEntry *entry);
-static void gtk_move_forward_word (GtkEntry *entry);
-static void gtk_move_backward_word (GtkEntry *entry);
-static void gtk_move_beginning_of_line (GtkEntry *entry);
-static void gtk_move_end_of_line (GtkEntry *entry);
-static void gtk_delete_forward_character (GtkEntry *entry);
-static void gtk_delete_backward_character (GtkEntry *entry);
-static void gtk_delete_forward_word (GtkEntry *entry);
-static void gtk_delete_backward_word (GtkEntry *entry);
-static void gtk_delete_line (GtkEntry *entry);
-static void gtk_delete_to_line_end (GtkEntry *entry);
-static void gtk_delete_selection (GtkEntry *entry);
-static void gtk_select_word (GtkEntry *entry);
-static void gtk_select_line (GtkEntry *entry);
-static void gtk_select_region (GtkEntry *entry,
- gint start,
- gint end);
+static guint signals[LAST_SIGNAL] = { 0 };
+/* GObject, GtkObject methods
+ */
+static void gtk_entry_class_init (GtkEntryClass *klass);
+static void gtk_entry_editable_init (GtkEditableClass *iface);
+static void gtk_entry_init (GtkEntry *entry);
+static void gtk_entry_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_entry_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_entry_finalize (GObject *object);
+
+/* GtkWidget methods
+ */
+static void gtk_entry_realize (GtkWidget *widget);
+static void gtk_entry_unrealize (GtkWidget *widget);
+static void gtk_entry_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_entry_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gtk_entry_draw_focus (GtkWidget *widget);
+static gint gtk_entry_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gint gtk_entry_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_entry_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_entry_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint gtk_entry_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+static gint gtk_entry_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gtk_entry_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static void gtk_entry_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static void gtk_entry_direction_changed (GtkWidget *widget,
+ GtkTextDirection previous_dir);
+static void gtk_entry_state_changed (GtkWidget *widget,
+ GtkStateType previous_state);
+
+/* GtkEditable method implementations
+ */
+static void gtk_entry_insert_text (GtkEditable *editable,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position);
+static void gtk_entry_delete_text (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos);
+static gchar * gtk_entry_get_chars (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos);
+static void gtk_entry_real_set_position (GtkEditable *editable,
+ gint position);
+static gint gtk_entry_get_position (GtkEditable *editable);
+static void gtk_entry_set_selection_bounds (GtkEditable *editable,
+ gint start,
+ gint end);
+static gboolean gtk_entry_get_selection_bounds (GtkEditable *editable,
+ gint *start,
+ gint *end);
+
+/* Default signal handlers
+ */
+static void gtk_entry_real_insert_text (GtkEntry *entry,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position);
+static void gtk_entry_real_delete_text (GtkEntry *entry,
+ gint start_pos,
+ gint end_pos);
+static void gtk_entry_move_cursor (GtkEntry *entry,
+ GtkMovementStep step,
+ gint count,
+ gboolean extend_selection);
+static void gtk_entry_insert_at_cursor (GtkEntry *entry,
+ const gchar *str);
+static void gtk_entry_delete_from_cursor (GtkEntry *entry,
+ GtkDeleteType type,
+ gint count);
+static void gtk_entry_cut_clipboard (GtkEntry *entry);
+static void gtk_entry_copy_clipboard (GtkEntry *entry);
+static void gtk_entry_paste_clipboard (GtkEntry *entry);
+static void gtk_entry_toggle_overwrite (GtkEntry *entry);
+
+/* IM Context Callbacks
+ */
+static void gtk_entry_commit_cb (GtkIMContext *context,
+ const gchar *str,
+ GtkEntry *entry);
+static void gtk_entry_preedit_changed_cb (GtkIMContext *context,
+ GtkEntry *entry);
+/* Internal routines
+ */
+static void gtk_entry_draw_text (GtkEntry *entry);
+static void gtk_entry_draw_cursor (GtkEntry *entry);
+static PangoLayout *gtk_entry_get_layout (GtkEntry *entry,
+ gboolean include_preedit);
+static void gtk_entry_queue_draw (GtkEntry *entry);
+static void gtk_entry_reset_im_context (GtkEntry *entry);
+static void gtk_entry_recompute (GtkEntry *entry);
+static gint gtk_entry_find_position (GtkEntry *entry,
+ gint x);
+static void gtk_entry_get_cursor_locations (GtkEntry *entry,
+ gint *strong_x,
+ gint *weak_x);
+static void gtk_entry_adjust_scroll (GtkEntry *entry);
+static gint gtk_entry_move_visually (GtkEntry *editable,
+ gint start,
+ gint count);
+static gint gtk_entry_move_forward_word (GtkEntry *entry,
+ gint start);
+static gint gtk_entry_move_backward_word (GtkEntry *entry,
+ gint start);
+static void gtk_entry_delete_whitespace (GtkEntry *entry);
+static void gtk_entry_select_word (GtkEntry *entry);
+static void gtk_entry_select_line (GtkEntry *entry);
+static char * gtk_entry_get_public_chars (GtkEntry *entry,
+ gint start,
+ gint end);
+static void gtk_entry_paste (GtkEntry *entry,
+ GdkAtom selection);
+static void gtk_entry_update_primary_selection (GtkEntry *entry);
+static void gtk_entry_popup_menu (GtkEntry *entry,
+ GdkEventButton *event);
static GtkWidgetClass *parent_class = NULL;
-static gint entry_signals[LAST_SIGNAL] = { 0 };
-static GdkAtom ctext_atom = GDK_NONE;
-static GdkAtom text_atom = GDK_NONE;
-
-static GtkTextFunction control_keys[26] =
-{
- gtk_move_beginning_of_line, /* a */
- gtk_move_backward_character, /* b */
- NULL, /* c */
- gtk_delete_forward_character, /* d */
- gtk_move_end_of_line, /* e */
- gtk_move_forward_character, /* f */
- NULL, /* g */
- gtk_delete_backward_character, /* h */
- NULL, /* i */
- NULL, /* j */
- gtk_delete_to_line_end, /* k */
- NULL, /* l */
- NULL, /* m */
- NULL, /* n */
- NULL, /* o */
- NULL, /* p */
- NULL, /* q */
- NULL, /* r */
- NULL, /* s */
- NULL, /* t */
- gtk_delete_line, /* u */
- NULL, /* v */
- gtk_delete_backward_word, /* w */
- NULL, /* x */
- NULL, /* y */
- NULL, /* z */
-};
-static GtkTextFunction alt_keys[26] =
-{
- NULL, /* a */
- gtk_move_backward_word, /* b */
- NULL, /* c */
- gtk_delete_forward_word, /* d */
- NULL, /* e */
- gtk_move_forward_word, /* f */
- NULL, /* g */
- NULL, /* h */
- NULL, /* i */
- NULL, /* j */
- NULL, /* k */
- NULL, /* l */
- NULL, /* m */
- NULL, /* n */
- NULL, /* o */
- NULL, /* p */
- NULL, /* q */
- NULL, /* r */
- NULL, /* s */
- NULL, /* t */
- NULL, /* u */
- NULL, /* v */
- NULL, /* w */
- NULL, /* x */
- NULL, /* y */
- NULL, /* z */
-};
-
-
-guint
-gtk_entry_get_type ()
+GtkType
+gtk_entry_get_type (void)
{
- static guint entry_type = 0;
+ static GtkType entry_type = 0;
if (!entry_type)
{
- GtkTypeInfo entry_info =
+ static const GtkTypeInfo entry_info =
{
"GtkEntry",
sizeof (GtkEntry),
sizeof (GtkEntryClass),
(GtkClassInitFunc) gtk_entry_class_init,
(GtkObjectInitFunc) gtk_entry_init,
- (GtkArgFunc) NULL,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ static const GInterfaceInfo editable_info =
+ {
+ (GInterfaceInitFunc) gtk_entry_editable_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
};
- entry_type = gtk_type_unique (gtk_widget_get_type (), &entry_info);
+ entry_type = gtk_type_unique (GTK_TYPE_WIDGET, &entry_info);
+ g_type_add_interface_static (entry_type,
+ GTK_TYPE_EDITABLE,
+ &editable_info);
}
return entry_type;
}
+static void
+add_move_binding (GtkBindingSet *binding_set,
+ guint keyval,
+ guint modmask,
+ GtkMovementStep step,
+ gint count)
+{
+ g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
+
+ gtk_binding_entry_add_signal (binding_set, keyval, modmask,
+ "move_cursor", 3,
+ GTK_TYPE_ENUM, step,
+ G_TYPE_INT, count,
+ G_TYPE_BOOLEAN, FALSE);
+
+ /* Selection-extending version */
+ gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
+ "move_cursor", 3,
+ GTK_TYPE_ENUM, step,
+ G_TYPE_INT, count,
+ G_TYPE_BOOLEAN, TRUE);
+}
+
static void
gtk_entry_class_init (GtkEntryClass *class)
{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
+ GtkBindingSet *binding_set;
object_class = (GtkObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
+ parent_class = gtk_type_class (GTK_TYPE_WIDGET);
- parent_class = gtk_type_class (gtk_widget_get_type ());
-
- entry_signals[INSERT_TEXT] =
- gtk_signal_new ("insert_text",
- GTK_RUN_LAST,
- object_class->type,
- GTK_SIGNAL_OFFSET (GtkEntryClass, insert_text),
- gtk_entry_marshal_signal_1,
- GTK_TYPE_NONE, 3,
- GTK_TYPE_STRING, GTK_TYPE_INT,
- GTK_TYPE_POINTER);
- entry_signals[DELETE_TEXT] =
- gtk_signal_new ("delete_text",
- GTK_RUN_LAST,
- object_class->type,
- GTK_SIGNAL_OFFSET (GtkEntryClass, delete_text),
- gtk_entry_marshal_signal_2,
- GTK_TYPE_NONE, 2,
- GTK_TYPE_INT, GTK_TYPE_INT);
- entry_signals[CHANGED] =
- gtk_signal_new ("changed",
- GTK_RUN_LAST,
- object_class->type,
- GTK_SIGNAL_OFFSET (GtkEntryClass, changed),
- gtk_signal_default_marshaller,
- GTK_TYPE_NONE, 0);
- entry_signals[SET_TEXT] =
- gtk_signal_new ("set_text",
- GTK_RUN_LAST,
- object_class->type,
- GTK_SIGNAL_OFFSET (GtkEntryClass, set_text),
- gtk_signal_default_marshaller,
- GTK_TYPE_NONE, 0);
- entry_signals[ACTIVATE] =
- gtk_signal_new ("activate",
- GTK_RUN_LAST,
- object_class->type,
- GTK_SIGNAL_OFFSET (GtkEntryClass, activate),
- gtk_signal_default_marshaller,
- GTK_TYPE_NONE, 0);
-
- gtk_object_class_add_signals (object_class, entry_signals, LAST_SIGNAL);
-
- object_class->destroy = gtk_entry_destroy;
+ gobject_class->finalize = gtk_entry_finalize;
+
+ object_class->set_arg = gtk_entry_set_arg;
+ object_class->get_arg = gtk_entry_get_arg;
widget_class->realize = gtk_entry_realize;
widget_class->unrealize = gtk_entry_unrealize;
widget_class->draw_focus = gtk_entry_draw_focus;
widget_class->size_request = gtk_entry_size_request;
widget_class->size_allocate = gtk_entry_size_allocate;
- widget_class->draw = gtk_entry_draw;
widget_class->expose_event = gtk_entry_expose;
widget_class->button_press_event = gtk_entry_button_press;
widget_class->button_release_event = gtk_entry_button_release;
widget_class->key_press_event = gtk_entry_key_press;
widget_class->focus_in_event = gtk_entry_focus_in;
widget_class->focus_out_event = gtk_entry_focus_out;
- widget_class->selection_clear_event = gtk_entry_selection_clear;
- widget_class->selection_received = gtk_entry_selection_received;
-
- class->insert_text = gtk_real_entry_insert_text;
- class->delete_text = gtk_real_entry_delete_text;
- class->changed = gtk_entry_adjust_scroll;
- class->set_text = NULL; /* user defined handling */
- class->activate = NULL; /* user defined handling */
-}
-
-static void
-gtk_entry_init (GtkEntry *entry)
-{
- GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
-
- entry->text_area = NULL;
- entry->text = NULL;
- entry->text_size = 0;
- entry->text_length = 0;
- entry->current_pos = 0;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
- entry->scroll_offset = 0;
- entry->have_selection = FALSE;
- entry->timer = 0;
- entry->visible = 1;
-
-#ifdef USE_XIM
- entry->ic = NULL;
-#endif
-
- gtk_selection_add_handler (GTK_WIDGET(entry), GDK_SELECTION_PRIMARY,
- GDK_TARGET_STRING, gtk_entry_selection_handler,
- NULL, NULL);
-
- if (!text_atom)
- text_atom = gdk_atom_intern ("TEXT", FALSE);
-
- gtk_selection_add_handler (GTK_WIDGET(entry), GDK_SELECTION_PRIMARY,
- text_atom,
- gtk_entry_selection_handler,
- NULL, NULL);
-
- if (!ctext_atom)
- ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
-
- gtk_selection_add_handler (GTK_WIDGET(entry), GDK_SELECTION_PRIMARY,
- ctext_atom,
- gtk_entry_selection_handler,
- NULL, NULL);
-}
-
-GtkWidget*
-gtk_entry_new ()
-{
- return GTK_WIDGET (gtk_type_new (gtk_entry_get_type ()));
-}
-
-void
-gtk_entry_set_text (GtkEntry *entry,
- const gchar *text)
-{
- gint tmp_pos;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
-
- gtk_real_entry_delete_text (entry, 0, entry->text_length);
+ widget_class->style_set = gtk_entry_style_set;
+ widget_class->direction_changed = gtk_entry_direction_changed;
+ widget_class->state_changed = gtk_entry_state_changed;
+
+ class->insert_text = gtk_entry_real_insert_text;
+ class->delete_text = gtk_entry_real_delete_text;
+ class->move_cursor = gtk_entry_move_cursor;
+ class->insert_at_cursor = gtk_entry_insert_at_cursor;
+ class->delete_from_cursor = gtk_entry_delete_from_cursor;
+ class->cut_clipboard = gtk_entry_cut_clipboard;
+ class->copy_clipboard = gtk_entry_copy_clipboard;
+ class->paste_clipboard = gtk_entry_paste_clipboard;
+ class->toggle_overwrite = gtk_entry_toggle_overwrite;
+
+ gtk_object_add_arg_type ("GtkEntry::text_position", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_TEXT_POSITION);
+ gtk_object_add_arg_type ("GtkEntry::editable", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EDITABLE);
+ gtk_object_add_arg_type ("GtkEntry::max_length", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_MAX_LENGTH);
+ gtk_object_add_arg_type ("GtkEntry::visibility", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_VISIBILITY);
+ gtk_object_add_arg_type ("GtkEntry::invisible_char", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_INVISIBLE_CHAR);
+
+ signals[INSERT_TEXT] =
+ gtk_signal_new ("insert_text",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, insert_text),
+ gtk_marshal_VOID__STRING_INT_POINTER,
+ GTK_TYPE_NONE,
+ 3,
+ GTK_TYPE_STRING,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER);
- tmp_pos = 0;
- gtk_entry_insert_text (entry, text, strlen (text), &tmp_pos);
- entry->current_pos = tmp_pos;
+ signals[DELETE_TEXT] =
+ gtk_signal_new ("delete_text",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, delete_text),
+ gtk_marshal_VOID__INT_INT,
+ GTK_TYPE_NONE,
+ 2,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT);
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ signals[CHANGED] =
+ gtk_signal_new ("changed",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, changed),
+ gtk_marshal_VOID__VOID,
+ GTK_TYPE_NONE, 0);
- if (GTK_WIDGET_DRAWABLE (entry))
- gtk_entry_draw_text (entry);
+ /* Action signals */
+
+ signals[ACTIVATE] =
+ gtk_signal_new ("activate",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, activate),
+ gtk_marshal_VOID__VOID,
+ GTK_TYPE_NONE, 0);
+ widget_class->activate_signal = signals[ACTIVATE];
+
+ signals[MOVE_CURSOR] =
+ gtk_signal_new ("move_cursor",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, move_cursor),
+ gtk_marshal_VOID__ENUM_INT_BOOLEAN,
+ GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
+
+ signals[INSERT_AT_CURSOR] =
+ gtk_signal_new ("insert_at_cursor",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, insert_at_cursor),
+ gtk_marshal_VOID__STRING,
+ GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
+
+ signals[DELETE_FROM_CURSOR] =
+ gtk_signal_new ("delete_from_cursor",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, delete_from_cursor),
+ gtk_marshal_VOID__ENUM_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
+
+ signals[CUT_CLIPBOARD] =
+ gtk_signal_new ("cut_clipboard",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, cut_clipboard),
+ gtk_marshal_VOID__VOID,
+ GTK_TYPE_NONE, 0);
+
+ signals[COPY_CLIPBOARD] =
+ gtk_signal_new ("copy_clipboard",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, copy_clipboard),
+ gtk_marshal_VOID__VOID,
+ GTK_TYPE_NONE, 0);
+
+ signals[PASTE_CLIPBOARD] =
+ gtk_signal_new ("paste_clipboard",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, paste_clipboard),
+ gtk_marshal_VOID__VOID,
+ GTK_TYPE_NONE, 0);
+
+ signals[TOGGLE_OVERWRITE] =
+ gtk_signal_new ("toggle_overwrite",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkEntryClass, toggle_overwrite),
+ gtk_marshal_VOID__VOID,
+ GTK_TYPE_NONE, 0);
+
+ /*
+ * Key bindings
+ */
+
+ binding_set = gtk_binding_set_by_class (class);
+
+ /* Moving the insertion point */
+ add_move_binding (binding_set, GDK_Right, 0,
+ GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+
+ add_move_binding (binding_set, GDK_Left, 0,
+ GTK_MOVEMENT_VISUAL_POSITIONS, -1);
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[SET_TEXT]);
-}
+ add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
+
+ add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
+
+ add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_WORDS, 1);
-void
-gtk_entry_append_text (GtkEntry *entry,
- const gchar *text)
-{
- gint tmp_pos;
+ add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_WORDS, -1);
+
+ add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
- tmp_pos = entry->text_length;
- gtk_entry_insert_text (entry, text, strlen (text), &tmp_pos);
- entry->current_pos = tmp_pos;
+ add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
+ GTK_MOVEMENT_WORDS, 1);
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[SET_TEXT]);
-}
+ add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
+ GTK_MOVEMENT_WORDS, -1);
-void
-gtk_entry_prepend_text (GtkEntry *entry,
- const gchar *text)
-{
- gint tmp_pos;
+ add_move_binding (binding_set, GDK_Home, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ add_move_binding (binding_set, GDK_End, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+
+ add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_BUFFER_ENDS, -1);
+
+ add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+ /* Deleting text */
+ gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_CHARS,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_CHARS,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_CHARS,
+ GTK_TYPE_INT, -1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+ GTK_TYPE_INT, -1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
+ GTK_TYPE_INT, 1);
+ gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
+ "insert_at_cursor", 1,
+ GTK_TYPE_STRING, " ");
+
+ gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
+ "delete_from_cursor", 2,
+ GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
+ GTK_TYPE_INT, 1);
+
+ /* Cut/copy/paste */
- tmp_pos = 0;
- gtk_entry_insert_text (entry, text, strlen (text), &tmp_pos);
- entry->current_pos = tmp_pos;
+ gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
+ "cut_clipboard", 0);
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[SET_TEXT]);
-}
+ gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
+ "cut_clipboard", 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
+ "copy_clipboard", 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
+ "paste_clipboard", 0);
-void
-gtk_entry_set_position (GtkEntry *entry,
- gint position)
-{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
+ "paste_clipboard", 0);
- if ((position == -1) || (position > entry->text_length))
- entry->current_pos = entry->text_length;
- else
- entry->current_pos = position;
+ /* Overwrite */
+ gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
+ "toggle_overwrite", 0);
}
-void
-gtk_entry_set_visibility (GtkEntry *entry,
- gint visible)
+static void
+gtk_entry_editable_init (GtkEditableClass *iface)
{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
-
- entry->visible = visible;
+ iface->insert_text = gtk_entry_insert_text;
+ iface->delete_text = gtk_entry_delete_text;
+ iface->get_chars = gtk_entry_get_chars;
+ iface->set_selection_bounds = gtk_entry_set_selection_bounds;
+ iface->get_selection_bounds = gtk_entry_get_selection_bounds;
+ iface->set_position = gtk_entry_real_set_position;
+ iface->get_position = gtk_entry_get_position;
}
-gchar*
-gtk_entry_get_text (GtkEntry *entry)
+static void
+gtk_entry_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id)
{
- static char empty_str[2] = "";
-
- g_return_val_if_fail (entry != NULL, NULL);
- g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+ GtkEntry *entry = GTK_ENTRY (object);
- if (!entry->text)
- return empty_str;
- return entry->text;
+ switch (arg_id)
+ {
+ case ARG_TEXT_POSITION:
+ gtk_editable_set_position (GTK_EDITABLE (object), GTK_VALUE_INT (*arg));
+ break;
+ case ARG_EDITABLE:
+ {
+ gboolean new_value = GTK_VALUE_BOOL (*arg) != 0;
+ if (new_value != entry->editable)
+ {
+ entry->editable = new_value;
+ gtk_entry_queue_draw (entry);
+ }
+ }
+ break;
+ case ARG_MAX_LENGTH:
+ gtk_entry_set_max_length (entry, GTK_VALUE_UINT (*arg));
+ break;
+ case ARG_VISIBILITY:
+ gtk_entry_set_visibility (entry, GTK_VALUE_BOOL (*arg));
+ break;
+ case ARG_INVISIBLE_CHAR:
+ gtk_entry_set_invisible_char (entry, GTK_VALUE_INT (*arg));
+ break;
+ default:
+ break;
+ }
}
-
static void
-gtk_entry_marshal_signal_1 (GtkObject *object,
- GtkSignalFunc func,
- gpointer func_data,
- GtkArg *args)
+gtk_entry_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id)
{
- GtkEntrySignal1 rfunc;
+ GtkEntry *entry;
- rfunc = (GtkEntrySignal1) func;
+ entry = GTK_ENTRY (object);
- (* rfunc) (object, GTK_VALUE_STRING (args[0]), GTK_VALUE_INT (args[1]),
- GTK_VALUE_POINTER (args[2]), func_data);
+ switch (arg_id)
+ {
+ case ARG_TEXT_POSITION:
+ GTK_VALUE_INT (*arg) = entry->current_pos;
+ break;
+ case ARG_EDITABLE:
+ GTK_VALUE_BOOL (*arg) = entry->editable;
+ break;
+ case ARG_MAX_LENGTH:
+ GTK_VALUE_UINT (*arg) = entry->text_max_length;
+ break;
+ case ARG_VISIBILITY:
+ GTK_VALUE_BOOL (*arg) = entry->visible;
+ break;
+ case ARG_INVISIBLE_CHAR:
+ GTK_VALUE_INT (*arg) = entry->invisible_char;
+ break;
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
}
static void
-gtk_entry_marshal_signal_2 (GtkObject *object,
- GtkSignalFunc func,
- gpointer func_data,
- GtkArg *args)
+gtk_entry_init (GtkEntry *entry)
{
- GtkEntrySignal2 rfunc;
+ GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
- rfunc = (GtkEntrySignal2) func;
+ entry->text_size = MIN_SIZE;
+ entry->text = g_malloc (entry->text_size);
+ entry->text[0] = '\0';
- (* rfunc) (object, GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
- func_data);
+ entry->editable = TRUE;
+ entry->visible = TRUE;
+ entry->invisible_char = '*';
+
+ /* This object is completely private. No external entity can gain a reference
+ * to it; so we create it here and destroy it in finalize().
+ */
+ entry->im_context = gtk_im_multicontext_new ();
+
+ gtk_signal_connect (GTK_OBJECT (entry->im_context), "commit",
+ GTK_SIGNAL_FUNC (gtk_entry_commit_cb), entry);
+ gtk_signal_connect (GTK_OBJECT (entry->im_context), "preedit_changed",
+ GTK_SIGNAL_FUNC (gtk_entry_preedit_changed_cb), entry);
}
static void
-gtk_entry_destroy (GtkObject *object)
+gtk_entry_finalize (GObject *object)
{
GtkEntry *entry;
- g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_ENTRY (object));
entry = GTK_ENTRY (object);
-#ifdef USE_XIM
- if (entry->ic)
- {
- gdk_ic_destroy (entry->ic);
- entry->ic = NULL;
- }
-#endif
+ if (entry->cached_layout)
+ g_object_unref (G_OBJECT (entry->cached_layout));
+
+ gtk_object_unref (GTK_OBJECT (entry->im_context));
if (entry->timer)
- gtk_timeout_remove (entry->timer);
+ g_source_remove (entry->timer);
+
+ if (entry->recompute_idle)
+ g_source_remove (entry->recompute_idle);
+
+ entry->text_size = 0;
if (entry->text)
g_free (entry->text);
entry->text = NULL;
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gtk_entry_realize (GtkWidget *widget)
{
GtkEntry *entry;
+ GtkEditable *editable;
+ GtkRequisition requisition;
GdkWindowAttr attributes;
gint attributes_mask;
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
entry = GTK_ENTRY (widget);
+ editable = GTK_EDITABLE (widget);
+ gtk_widget_get_child_requisition (widget, &requisition);
+
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = widget->allocation.x;
- attributes.y = widget->allocation.y;
+ attributes.y = widget->allocation.y + (widget->allocation.height -
+ requisition.height) / 2;
attributes.width = widget->allocation.width;
- attributes.height = widget->allocation.height;
+ attributes.height = requisition.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON3_MOTION_MASK |
- GDK_POINTER_MOTION_HINT_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK |
- GDK_KEY_PRESS_MASK);
+ GDK_POINTER_MOTION_HINT_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
- widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
gdk_window_set_user_data (widget->window, entry);
- attributes.x = widget->style->klass->xthickness + INNER_BORDER;
- attributes.y = widget->style->klass->ythickness + INNER_BORDER;
+ attributes.x = widget->style->xthickness;
+ attributes.y = widget->style->ythickness;
attributes.width = widget->allocation.width - attributes.x * 2;
- attributes.height = widget->allocation.height - attributes.y * 2;
+ attributes.height = requisition.height - attributes.y * 2;
+ attributes.cursor = gdk_cursor_new (GDK_XTERM);
+ attributes_mask |= GDK_WA_CURSOR;
entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
gdk_window_set_user_data (entry->text_area, entry);
widget->style = gtk_style_attach (widget->style, widget->window);
- gdk_window_set_background (widget->window, &widget->style->white);
- gdk_window_set_background (entry->text_area, &widget->style->white);
-
-#ifdef USE_XIM
- if (gdk_im_ready ())
- {
- GdkPoint spot;
- GdkRectangle rect;
- gint width, height;
- GdkEventMask mask;
- GdkIMStyle style;
- GdkIMStyle supported_style = GdkIMPreeditNone | GdkIMPreeditNothing |
- GdkIMPreeditPosition |
- GdkIMStatusNone | GdkIMStatusNothing;
-
- if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
- supported_style &= ~GdkIMPreeditPosition;
-
- style = gdk_im_decide_style (supported_style);
- switch (style & GdkIMPreeditMask)
- {
- case GdkIMPreeditPosition:
- if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
- {
- g_warning ("over-the-spot style requires fontset");
- break;
- }
- gdk_window_get_size (entry->text_area, &width, &height);
- rect.x = 0;
- rect.y = 0;
- rect.width = width;
- rect.height = height;
- spot.x = 0;
- spot.y = height;
- entry->ic = gdk_ic_new (entry->text_area, entry->text_area,
- style,
- "spotLocation", &spot,
- "area", &rect,
- "fontSet", GDK_FONT_XFONT (widget->style->font),
- NULL);
- break;
- default:
- entry->ic = gdk_ic_new (entry->text_area, entry->text_area,
- style, NULL);
- }
-
- if (entry->ic == NULL)
- g_warning ("Can't create input context.");
- else
- {
- GdkColormap *colormap;
+ gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+ gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
- mask = gdk_window_get_events (entry->text_area);
- mask |= gdk_ic_get_events (entry->ic);
- gdk_window_set_events (entry->text_area, mask);
+ gdk_window_show (entry->text_area);
- if ((colormap = gtk_widget_get_colormap (widget)) !=
- gtk_widget_get_default_colormap ())
- {
- gdk_ic_set_attr (entry->ic, "preeditAttributes",
- "colorMap", GDK_COLORMAP_XCOLORMAP (colormap),
- NULL);
- }
- gdk_ic_set_attr (entry->ic,"preeditAttributes",
- "foreground", widget->style->fg[GTK_STATE_NORMAL].pixel,
- "background", widget->style->white.pixel,
- NULL);
- }
- }
-#endif
+ gtk_im_context_set_client_window (entry->im_context, entry->text_area);
- gdk_window_show (entry->text_area);
+ gtk_entry_adjust_scroll (entry);
}
static void
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ENTRY (widget));
- GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED);
entry = GTK_ENTRY (widget);
- gtk_style_detach (widget->style);
-
+ gtk_im_context_set_client_window (entry->im_context, entry->text_area);
+
if (entry->text_area)
{
gdk_window_set_user_data (entry->text_area, NULL);
gdk_window_destroy (entry->text_area);
+ entry->text_area = NULL;
}
- if (widget->window)
- {
- gdk_window_set_user_data (widget->window, NULL);
- gdk_window_destroy (widget->window);
- }
-
- entry->text_area = NULL;
- widget->window = NULL;
-}
-
-static void
-gtk_entry_draw_focus (GtkWidget *widget)
-{
- gint width, height;
- gint x, y;
-
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_ENTRY (widget));
-
- if (GTK_WIDGET_DRAWABLE (widget))
- {
- x = 0;
- y = 0;
- gdk_window_get_size (widget->window, &width, &height);
-
- if (GTK_WIDGET_HAS_FOCUS (widget))
- {
- x += 1;
- y += 1;
- width -= 2;
- height -= 2;
- }
- else
- {
- gdk_draw_rectangle (widget->window, widget->style->white_gc, FALSE,
- x + 2, y + 2, width - 5, height - 5);
- }
- gtk_draw_shadow (widget->style, widget->window,
- GTK_STATE_NORMAL, GTK_SHADOW_IN,
- x, y, width, height);
-
- if (GTK_WIDGET_HAS_FOCUS (widget))
- {
- gdk_window_get_size (widget->window, &width, &height);
- gdk_draw_rectangle (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
- FALSE, 0, 0, width - 1, height - 1);
- }
+ if (entry->popup_menu)
+ gtk_widget_destroy (entry->popup_menu);
- gtk_entry_draw_cursor (GTK_ENTRY (widget));
- }
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}
static void
gtk_entry_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
+ GtkEntry *entry;
+ PangoFontMetrics metrics;
+ PangoFont *font;
+ gchar *lang;
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ENTRY (widget));
g_return_if_fail (requisition != NULL);
- requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + INNER_BORDER) * 2;
- requisition->height = (widget->style->font->ascent +
- widget->style->font->descent +
- (widget->style->klass->ythickness + INNER_BORDER) * 2);
+ entry = GTK_ENTRY (widget);
+
+ /* hackish for now, get metrics
+ */
+ font = pango_context_load_font (gtk_widget_get_pango_context (widget),
+ widget->style->font_desc);
+ lang = pango_context_get_lang (gtk_widget_get_pango_context (widget));
+ pango_font_get_metrics (font, lang, &metrics);
+ g_free (lang);
+
+ g_object_unref (G_OBJECT (font));
+
+ entry->ascent = metrics.ascent;
+ entry->descent = metrics.descent;
+
+ requisition->width = MIN_ENTRY_WIDTH + (widget->style->xthickness + INNER_BORDER) * 2;
+ requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE +
+ (widget->style->ythickness + INNER_BORDER) * 2);
}
static void
GtkAllocation *allocation)
{
GtkEntry *entry;
+ GtkEditable *editable;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ENTRY (widget));
widget->allocation = *allocation;
entry = GTK_ENTRY (widget);
+ editable = GTK_EDITABLE (widget);
if (GTK_WIDGET_REALIZED (widget))
{
+ /* We call gtk_widget_get_child_requisition, since we want (for
+ * backwards compatibility reasons) the realization here to
+ * be affected by the usize of the entry, if set
+ */
+ GtkRequisition requisition;
+ gtk_widget_get_child_requisition (widget, &requisition);
+
gdk_window_move_resize (widget->window,
allocation->x,
- allocation->y + (allocation->height - widget->requisition.height) / 2,
- allocation->width, widget->requisition.height);
+ allocation->y + (allocation->height - requisition.height) / 2,
+ allocation->width, requisition.height);
gdk_window_move_resize (entry->text_area,
- widget->style->klass->xthickness + INNER_BORDER,
- widget->style->klass->ythickness + INNER_BORDER,
- allocation->width - (widget->style->klass->xthickness + INNER_BORDER) * 2,
- widget->requisition.height - (widget->style->klass->ythickness + INNER_BORDER) * 2);
-
- entry->scroll_offset = 0;
- gtk_entry_adjust_scroll (entry);
-#ifdef USE_XIM
- if (entry->ic && (gdk_ic_get_style (entry->ic) & GdkIMPreeditPosition))
- {
- gint width, height;
- GdkRectangle rect;
-
- gdk_window_get_size (entry->text_area, &width, &height);
- rect.x = 0;
- rect.y = 0;
- rect.width = width;
- rect.height = height;
- gdk_ic_set_attr (entry->ic, "preeditAttributes", "area", &rect, NULL);
- }
-#endif
+ widget->style->xthickness,
+ widget->style->ythickness,
+ allocation->width - widget->style->xthickness * 2,
+ requisition.height - widget->style->ythickness * 2);
+
+ gtk_entry_recompute (entry);
}
}
static void
-gtk_entry_draw (GtkWidget *widget,
- GdkRectangle *area)
+gtk_entry_draw_focus (GtkWidget *widget)
{
+ gint width, height;
+ gint x, y;
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ENTRY (widget));
- g_return_if_fail (area != NULL);
if (GTK_WIDGET_DRAWABLE (widget))
{
- gtk_widget_draw_focus (widget);
- gtk_entry_draw_text (GTK_ENTRY (widget));
- }
-}
+ x = 0;
+ y = 0;
+ gdk_window_get_size (widget->window, &width, &height);
-static gint
+ if (GTK_WIDGET_HAS_FOCUS (widget))
+ {
+ x += 1;
+ y += 1;
+ width -= 2;
+ height -= 2;
+ }
+
+ gtk_paint_shadow (widget->style, widget->window,
+ GTK_STATE_NORMAL, GTK_SHADOW_IN,
+ NULL, widget, "entry",
+ x, y, width, height);
+
+ if (GTK_WIDGET_HAS_FOCUS (widget))
+ {
+ gdk_window_get_size (widget->window, &width, &height);
+ gtk_paint_focus (widget->style, widget->window,
+ NULL, widget, "entry",
+ 0, 0, width - 1, height - 1);
+ }
+ }
+}
+
+static gint
gtk_entry_expose (GtkWidget *widget,
GdkEventExpose *event)
{
if (widget->window == event->window)
gtk_widget_draw_focus (widget);
else if (entry->text_area == event->window)
- gtk_entry_draw_text (GTK_ENTRY (widget));
+ {
+ gtk_entry_draw_text (GTK_ENTRY (widget));
+ gtk_entry_draw_cursor (GTK_ENTRY (widget));
+ }
return FALSE;
}
gtk_entry_button_press (GtkWidget *widget,
GdkEventButton *event)
{
- GtkEntry *entry;
+ GtkEntry *entry = GTK_ENTRY (widget);
+ GtkEditable *editable = GTK_EDITABLE (widget);
gint tmp_pos;
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- if (ctext_atom == GDK_NONE)
- ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
-
entry = GTK_ENTRY (widget);
+ editable = GTK_EDITABLE (widget);
+
+ if (event->window != entry->text_area ||
+ (entry->button && event->button != entry->button))
+ return FALSE;
+
+ entry->button = event->button;
+
if (!GTK_WIDGET_HAS_FOCUS (widget))
gtk_widget_grab_focus (widget);
-
+
+ tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
+
if (event->button == 1)
{
switch (event->type)
{
case GDK_BUTTON_PRESS:
- gtk_grab_add (widget);
+ gtk_entry_reset_im_context (entry);
+
+ entry->current_pos = tmp_pos;
+ entry->selection_bound = tmp_pos;
- tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
- gtk_select_region (entry, tmp_pos, tmp_pos);
- entry->current_pos = entry->selection_start_pos;
- gtk_entry_queue_draw (entry);
+ gtk_entry_recompute (entry);
+
break;
case GDK_2BUTTON_PRESS:
- gtk_select_word (entry);
- gtk_entry_queue_draw (entry);
+ gtk_entry_select_word (entry);
break;
case GDK_3BUTTON_PRESS:
- gtk_select_line (entry);
- gtk_entry_queue_draw (entry);
+ gtk_entry_select_line (entry);
break;
default:
break;
}
+
+ return TRUE;
}
- else if (event->type == GDK_BUTTON_PRESS)
+ else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
{
- if (event->button == 2)
- {
- if (entry->selection_start_pos == entry->selection_end_pos ||
- entry->have_selection)
- entry->current_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
- gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
- ctext_atom, event->time);
- }
- else
- {
- gtk_grab_add (widget);
+ gtk_editable_select_region (editable, tmp_pos, tmp_pos);
+ gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
- tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
- gtk_select_region (entry, tmp_pos, tmp_pos);
- entry->have_selection = FALSE;
- entry->current_pos = entry->selection_start_pos;
- gtk_entry_queue_draw (entry);
- }
+ return TRUE;
+ }
+ else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ {
+ gtk_entry_popup_menu (entry, event);
+ entry->button = 0; /* Don't wait for release, since the menu will gtk_grab_add */
+
+ return TRUE;
}
return FALSE;
gtk_entry_button_release (GtkWidget *widget,
GdkEventButton *event)
{
- GtkEntry *entry;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- if (event->button == 1)
- {
- entry = GTK_ENTRY (widget);
- gtk_grab_remove (widget);
+ GtkEntry *entry = GTK_ENTRY (widget);
+ GtkEditable *editable = GTK_EDITABLE (widget);
- entry->have_selection = FALSE;
- if (entry->selection_start_pos != entry->selection_end_pos)
- {
- if (gtk_selection_owner_set (widget,
- GDK_SELECTION_PRIMARY,
- event->time))
- {
- entry->have_selection = TRUE;
- gtk_entry_queue_draw (entry);
- }
- }
- else
- {
- if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
- gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
- }
- }
- else if (event->button == 3)
- {
- gtk_grab_remove (widget);
- if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
- gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
- }
+ if (event->window != entry->text_area || entry->button != event->button)
+ return FALSE;
+ entry->button = 0;
+
return FALSE;
}
gtk_entry_motion_notify (GtkWidget *widget,
GdkEventMotion *event)
{
- GtkEntry *entry;
- gint x;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
+ GtkEntry *entry = GTK_ENTRY (widget);
+ gint tmp_pos;
- entry = GTK_ENTRY (widget);
+ if (event->window != entry->text_area || entry->button != 1)
+ return FALSE;
- x = event->x;
if (event->is_hint || (entry->text_area != event->window))
- gdk_window_get_pointer (entry->text_area, &x, NULL, NULL);
+ gdk_window_get_pointer (entry->text_area, NULL, NULL, NULL);
- entry->selection_end_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
- entry->current_pos = entry->selection_end_pos;
- gtk_entry_adjust_scroll (entry);
- gtk_entry_queue_draw (entry);
+ tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
- return FALSE;
+ if (tmp_pos != entry->current_pos)
+ {
+ entry->current_pos = tmp_pos;
+ gtk_entry_recompute (entry);
+ }
+
+ return TRUE;
}
static gint
gtk_entry_key_press (GtkWidget *widget,
GdkEventKey *event)
{
- GtkEntry *entry;
- gint return_val;
- gint key;
- gint tmp_pos;
- gchar tmp;
- gint extend_selection;
- gint selection_pos;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- entry = GTK_ENTRY (widget);
- return_val = FALSE;
- extend_selection = FALSE;
+ GtkEntry *entry = GTK_ENTRY (widget);
- if (entry->selection_start_pos == entry->selection_end_pos)
- selection_pos = entry->current_pos;
- else if (entry->selection_start_pos == entry->current_pos)
- selection_pos = entry->selection_end_pos;
- else
- selection_pos = entry->selection_start_pos;
+ if (!entry->editable)
+ return FALSE;
- switch (event->keyval)
+ if (gtk_im_context_filter_keypress (entry->im_context, event))
{
- case GDK_BackSpace:
- return_val = TRUE;
- if (entry->selection_start_pos != entry->selection_end_pos)
- gtk_delete_selection (entry);
- else if (event->state & GDK_CONTROL_MASK)
- gtk_delete_backward_word (entry);
- else
- gtk_delete_backward_character (entry);
- break;
- case GDK_Clear:
- return_val = TRUE;
- gtk_delete_line (entry);
- break;
- case GDK_Insert:
- return_val = TRUE;
- if (event->state & GDK_SHIFT_MASK)
- {
- /* gtk_paste_clipboard(entry) -- NEEDS CLIPBOARD */
- }
- else if (event->state & GDK_CONTROL_MASK)
- {
- /* gtk_copy_clipboard(entry) -- NEEDS CLIPBOARD */
- }
- else
- {
- /* gtk_toggle_insert(entry) -- IMPLEMENT */
- }
- break;
- case GDK_Delete:
- return_val = TRUE;
- if (entry->selection_start_pos != entry->selection_end_pos)
- gtk_delete_selection (entry);
- else
- {
- if (event->state & GDK_CONTROL_MASK)
- gtk_delete_line (entry);
- else if (event->state & GDK_SHIFT_MASK)
- /* gtk_cut_clipboard(entry) -- NEEDS CLIPBOARD */ ;
- else
- gtk_delete_forward_character (entry);
- }
- break;
- case GDK_Home:
- return_val = TRUE;
- if (event->state & GDK_SHIFT_MASK)
- {
- if (entry->selection_start_pos == entry->selection_end_pos)
- entry->selection_start_pos = entry->current_pos;
- entry->current_pos = entry->selection_end_pos = 0;
- }
- else
- gtk_move_beginning_of_line (entry);
- break;
- case GDK_End:
- return_val = TRUE;
- if (event->state & GDK_SHIFT_MASK)
- {
- if (entry->selection_start_pos == entry->selection_end_pos)
- entry->selection_start_pos = entry->current_pos;
- entry->current_pos = entry->selection_end_pos = entry->text_length;
- }
- else
- gtk_move_end_of_line (entry);
- break;
- case GDK_Left:
- return_val = TRUE;
- if (event->state & GDK_SHIFT_MASK)
- {
- if (entry->selection_start_pos == entry->selection_end_pos)
- entry->selection_start_pos = entry->selection_end_pos = entry->current_pos;
- if (entry->selection_end_pos > 0)
- entry->current_pos = --entry->selection_end_pos;
- }
- else
- gtk_move_backward_character (entry);
- break;
- case GDK_Right:
- return_val = TRUE;
- if (event->state & GDK_SHIFT_MASK)
- {
- if (entry->selection_start_pos == entry->selection_end_pos)
- entry->selection_start_pos = entry->selection_end_pos = entry->current_pos;
- if (entry->selection_end_pos < entry->text_length)
- entry->current_pos = ++entry->selection_end_pos;
- }
- else
- gtk_move_forward_character (entry);
- break;
- case GDK_Return:
- return_val = TRUE;
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[ACTIVATE]);
- break;
- /* The next two keys should not be inserted literally. Any others ??? */
- case GDK_Tab:
- case GDK_Escape:
- break;
- default:
- if ((event->keyval >= 0x20) && (event->keyval <= 0xFF))
- {
- key = event->keyval;
-
- if (event->state & GDK_CONTROL_MASK)
- {
- if ((key >= 'A') && (key <= 'Z'))
- key -= 'A' - 'a';
-
- if ((key >= 'a') && (key <= 'z') && control_keys[key - 'a'])
- {
- (* control_keys[key - 'a']) (entry);
- return_val = TRUE;
- }
- break;
- }
- else if (event->state & GDK_MOD1_MASK)
- {
- if ((key >= 'A') && (key <= 'Z'))
- key -= 'A' - 'a';
-
- if ((key >= 'a') && (key <= 'z') && alt_keys[key - 'a'])
- {
- (* alt_keys[key - 'a']) (entry);
- return_val = TRUE;
- }
- break;
- }
- }
- if (event->length > 0)
- {
- gtk_delete_selection (entry);
-
- tmp_pos = entry->current_pos;
- gtk_entry_insert_text (entry, event->string, event->length, &tmp_pos);
- entry->current_pos = tmp_pos;
-
- return_val = TRUE;
- }
- break;
+ entry->need_im_reset = TRUE;
+ return TRUE;
}
-
- /* alex stuff */
- if (entry->selection_start_pos != entry->selection_end_pos)
- {
- if (gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, event->time))
- {
- entry->have_selection = TRUE;
- gtk_entry_queue_draw (entry);
- }
- }
- else
- {
- if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
- gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
- }
- /* end of alex stuff */
-
- if (return_val)
+ else if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+ /* Activate key bindings
+ */
+ return TRUE;
+ else if (event->keyval == GDK_Return)
{
- gtk_entry_adjust_scroll (entry);
- gtk_entry_queue_draw (entry);
+ gtk_widget_activate (widget);
+ return TRUE;
}
- return return_val;
+ return FALSE;
}
static gint
GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
gtk_widget_draw_focus (widget);
-
-#ifdef USE_XIM
- if (GTK_ENTRY(widget)->ic)
- gdk_im_begin (GTK_ENTRY(widget)->ic, GTK_ENTRY(widget)->text_area);
-#endif
+ gtk_entry_queue_draw (GTK_ENTRY (widget));
+
+ GTK_ENTRY (widget)->need_im_reset = TRUE;
+ gtk_im_context_focus_in (GTK_ENTRY (widget)->im_context);
return FALSE;
}
GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
gtk_widget_draw_focus (widget);
+ gtk_entry_queue_draw (GTK_ENTRY (widget));
-#ifdef USE_XIM
- gdk_im_end ();
-#endif
+ GTK_ENTRY (widget)->need_im_reset = TRUE;
+ gtk_im_context_focus_out (GTK_ENTRY (widget)->im_context);
return FALSE;
}
-static gint
-gtk_entry_selection_clear (GtkWidget *widget,
- GdkEventSelection *event)
+static void
+gtk_entry_direction_changed (GtkWidget *widget,
+ GtkTextDirection previous_dir)
{
- GtkEntry *entry;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_ENTRY (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);
-
- entry = GTK_ENTRY (widget);
+ GtkEntry *entry = GTK_ENTRY (widget);
- if (entry->have_selection)
- {
- entry->have_selection = FALSE;
- gtk_entry_queue_draw (entry);
- }
-
- return FALSE;
+ gtk_entry_recompute (entry);
+
+ GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
}
static void
-gtk_entry_selection_handler (GtkWidget *widget,
- GtkSelectionData *selection_data,
- gpointer data)
+gtk_entry_state_changed (GtkWidget *widget,
+ GtkStateType previous_state)
{
- GtkEntry *entry;
- gint selection_start_pos;
- gint selection_end_pos;
-
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ENTRY (widget));
- entry = GTK_ENTRY (widget);
-
- selection_start_pos = MIN (entry->selection_start_pos, entry->selection_end_pos);
- selection_end_pos = MAX (entry->selection_start_pos, entry->selection_end_pos);
-
- if (selection_data->target == GDK_SELECTION_TYPE_STRING)
- {
- gtk_selection_data_set (selection_data,
- GDK_SELECTION_TYPE_STRING,
- 8*sizeof(gchar),
- &entry->text[selection_start_pos],
- selection_end_pos - selection_start_pos);
- }
- else if (selection_data->target == text_atom ||
- selection_data->target == ctext_atom)
+ if (GTK_WIDGET_REALIZED (widget))
{
- gchar *str;
- gint length;
- gchar c;
- GdkAtom encoding;
- gint format;
- guchar *text;
-
- length = selection_end_pos - selection_start_pos;
- c = entry->text[selection_end_pos];
- entry->text[selection_end_pos] = '\0';
- str = entry->text + selection_start_pos;
- gdk_string_to_compound_text (str, &encoding, &format, &text, &length);
- gtk_selection_data_set (selection_data, encoding, format, text, length);
- gdk_free_compound_text (text);
- entry->text[selection_end_pos] = c;
+ gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+ gdk_window_set_background (GTK_ENTRY (widget)->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
}
+
+ gtk_widget_queue_clear (widget);
}
+/* GtkEditable method implementations
+ */
static void
-gtk_entry_selection_received (GtkWidget *widget,
- GtkSelectionData *selection_data)
+gtk_entry_insert_text (GtkEditable *editable,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position)
{
- GtkEntry *entry;
- 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_ENTRY (widget));
-
- entry = GTK_ENTRY (widget);
+ GtkEntry *entry = GTK_ENTRY (editable);
+ gchar buf[64];
+ gchar *text;
- if (selection_data->type == GDK_TARGET_STRING)
- type = STRING;
- else if (selection_data->type == ctext_atom)
- type = CTEXT;
+ if (*position < 0 || *position > entry->text_length)
+ *position = entry->text_length;
+
+ g_object_ref (G_OBJECT (editable));
+
+ if (new_text_length <= 63)
+ text = buf;
else
- type = INVALID;
-
- if (type == INVALID || selection_data->length < 0)
- {
- /* avoid infinite loop */
- if (selection_data->target != GDK_TARGET_STRING)
- gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
- GDK_TARGET_STRING, GDK_CURRENT_TIME);
- return;
- }
-
- reselect = FALSE;
- if ((entry->selection_start_pos != entry->selection_end_pos) &&
- !entry->have_selection)
- {
- reselect = TRUE;
- gtk_delete_selection (entry);
- }
+ text = g_new (gchar, new_text_length + 1);
- tmp_pos = old_pos = entry->current_pos;
+ text[new_text_length] = '\0';
+ strncpy (text, new_text, new_text_length);
- switch (type)
- {
- case STRING:
- selection_data->data[selection_data->length] = 0;
- gtk_entry_insert_text (entry, selection_data->data,
- strlen (selection_data->data), &tmp_pos);
- entry->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_entry_insert_text (entry, list[i], strlen (list[i]), &tmp_pos);
- entry->current_pos = tmp_pos;
- }
- if (count > 0)
- gdk_free_text_list (list);
- }
- break;
- }
+ gtk_signal_emit (GTK_OBJECT (editable), signals[INSERT_TEXT], text, new_text_length, position);
+ gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
- if (reselect)
- {
- gtk_select_region (entry, old_pos, entry->current_pos);
- entry->have_selection = FALSE;
- }
+ if (new_text_length > 63)
+ g_free (text);
- gtk_entry_queue_draw (entry);
+ g_object_unref (G_OBJECT (editable));
}
static void
-gtk_entry_draw_text (GtkEntry *entry)
+gtk_entry_delete_text (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos)
{
- GtkWidget *widget;
- GtkStateType selected_state;
- gint selection_start_pos;
- gint selection_end_pos;
- gint selection_start_xoffset;
- gint selection_end_xoffset;
- gint width, height;
- gint y;
+ GtkEntry *entry = GTK_ENTRY (editable);
+
+ if (end_pos < 0 || end_pos > entry->text_length)
+ end_pos = entry->text_length;
+ if (start_pos < 0)
+ start_pos = 0;
+ if (start_pos > end_pos)
+ start_pos = end_pos;
+
+ g_object_ref (G_OBJECT (editable));
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ gtk_signal_emit (GTK_OBJECT (editable), signals[DELETE_TEXT], start_pos, end_pos);
+ gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
- if (entry->timer)
- {
- gtk_timeout_remove (entry->timer);
- entry->timer = 0;
- }
+ g_object_unref (G_OBJECT (editable));
+}
- if (!entry->visible)
- {
- gtk_entry_draw_cursor (entry);
- return;
- }
+static gchar *
+gtk_entry_get_chars (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos)
+{
+ GtkEntry *entry;
+ gint start_index, end_index;
+
+ g_return_val_if_fail (editable != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
- if (GTK_WIDGET_DRAWABLE (entry))
- {
- widget = GTK_WIDGET (entry);
+ entry = GTK_ENTRY (editable);
- gdk_window_clear (entry->text_area);
+ if (end_pos < 0)
+ end_pos = entry->text_length;
- if (entry->text)
- {
- gdk_window_get_size (entry->text_area, &width, &height);
- y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2;
- y += widget->style->font->ascent;
-
- if (entry->selection_start_pos != entry->selection_end_pos)
- {
- selected_state = GTK_STATE_SELECTED;
- if (!entry->have_selection)
- selected_state = GTK_STATE_ACTIVE;
-
- selection_start_pos = MIN (entry->selection_start_pos, entry->selection_end_pos);
- selection_end_pos = MAX (entry->selection_start_pos, entry->selection_end_pos);
-
- selection_start_xoffset = gdk_text_width (widget->style->font,
- entry->text,
- selection_start_pos);
- selection_end_xoffset = gdk_text_width (widget->style->font,
- entry->text,
- selection_end_pos);
-
- if (selection_start_pos > 0)
- gdk_draw_text (entry->text_area, widget->style->font,
- widget->style->fg_gc[GTK_STATE_NORMAL],
- -entry->scroll_offset, y,
- entry->text, selection_start_pos);
-
- gdk_draw_rectangle (entry->text_area,
- widget->style->bg_gc[selected_state],
- TRUE,
- -entry->scroll_offset + selection_start_xoffset,
- 0,
- selection_end_xoffset - selection_start_xoffset,
- -1);
-
- gdk_draw_text (entry->text_area, widget->style->font,
- widget->style->fg_gc[selected_state],
- -entry->scroll_offset + selection_start_xoffset, y,
- entry->text + selection_start_pos,
- selection_end_pos - selection_start_pos);
-
- if (selection_end_pos < entry->text_length)
- gdk_draw_string (entry->text_area, widget->style->font,
- widget->style->fg_gc[GTK_STATE_NORMAL],
- -entry->scroll_offset + selection_end_xoffset, y,
- entry->text + selection_end_pos);
- }
- else
- {
- GdkGCValues values;
-
- gdk_gc_get_values (widget->style->fg_gc[GTK_STATE_NORMAL], &values);
- gdk_draw_string (entry->text_area, widget->style->font,
- widget->style->fg_gc[GTK_STATE_NORMAL],
- -entry->scroll_offset, y,
- entry->text);
- }
- }
+ start_pos = MIN (entry->text_length, start_pos);
+ end_pos = MIN (entry->text_length, end_pos);
- gtk_entry_draw_cursor (entry);
- }
+ start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
+ end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
+
+ return g_strndup (entry->text + start_index, end_index - start_index);
}
static void
-gtk_entry_draw_cursor (GtkEntry *entry)
+gtk_entry_real_set_position (GtkEditable *editable,
+ gint position)
{
- GtkWidget *widget;
- GdkGC *gc;
- gint xoffset;
- gint text_area_height;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ GtkEntry *entry = GTK_ENTRY (editable);
+
+ if (position < 0 || position > entry->text_length)
+ position = entry->text_length;
- if (GTK_WIDGET_DRAWABLE (entry))
+ if (position != entry->current_pos)
{
- widget = GTK_WIDGET (entry);
-
- if (entry->current_pos > 0 && entry->visible)
- xoffset = gdk_text_width (widget->style->font, entry->text, entry->current_pos);
- else
- xoffset = 0;
- xoffset -= entry->scroll_offset;
-
- if (GTK_WIDGET_HAS_FOCUS (widget) &&
- (entry->selection_start_pos == entry->selection_end_pos))
- gc = widget->style->fg_gc[GTK_STATE_NORMAL];
- else
- gc = widget->style->white_gc;
+ gtk_entry_reset_im_context (entry);
- gdk_window_get_size (entry->text_area, NULL, &text_area_height);
- gdk_draw_line (entry->text_area, gc, xoffset, 0, xoffset, text_area_height);
-#ifdef USE_XIM
- if (gdk_im_ready() && entry->ic &&
- gdk_ic_get_style (entry->ic) & GdkIMPreeditPosition)
- {
- GdkPoint spot;
-
- spot.x = xoffset;
- spot.y = (text_area_height + (widget->style->font->ascent - widget->style->font->descent) + 1) / 2;
- gdk_ic_set_attr (entry->ic, "preeditAttributes", "spotLocation", &spot, NULL);
- }
-#endif
+ entry->current_pos = entry->selection_bound = position;
+ gtk_entry_recompute (entry);
}
}
-static void
-gtk_entry_queue_draw (GtkEntry *entry)
+static gint
+gtk_entry_get_position (GtkEditable *editable)
{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
-
- if (!entry->timer)
- entry->timer = gtk_timeout_add (DRAW_TIMEOUT, gtk_entry_timer, entry);
+ return GTK_ENTRY (editable)->current_pos;
}
-static gint
-gtk_entry_timer (gpointer data)
+static void
+gtk_entry_set_selection_bounds (GtkEditable *editable,
+ gint start,
+ gint end)
{
- GtkEntry *entry;
+ GtkEntry *entry = GTK_ENTRY (editable);
- g_return_val_if_fail (data != NULL, FALSE);
+ if (start < 0)
+ start = entry->text_length;
+ if (end < 0)
+ end = entry->text_length;
+
+ gtk_entry_reset_im_context (entry);
- entry = GTK_ENTRY (data);
- entry->timer = 0;
- gtk_entry_draw_text (entry);
+ entry->selection_bound = MIN (start, entry->text_length);
+ entry->current_pos = MIN (end, entry->text_length);
- return FALSE;
+ gtk_entry_update_primary_selection (entry);
+
+ gtk_entry_recompute (entry);
}
-static gint
-gtk_entry_position (GtkEntry *entry,
- gint x)
+static gboolean
+gtk_entry_get_selection_bounds (GtkEditable *editable,
+ gint *start,
+ gint *end)
{
- gint return_val;
- gint char_width;
- gint sum;
- gint i;
- gint len;
+ GtkEntry *entry = GTK_ENTRY (editable);
- g_return_val_if_fail (entry != NULL, 0);
- g_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
+ *start = entry->selection_bound;
+ *end = entry->current_pos;
- i = 0;
- sum = 0;
+ return (entry->selection_bound != entry->current_pos);
+}
- if (x > sum)
- {
- for (; i < entry->text_length; i+=len)
- {
- len = mblen (entry->text+i, MB_CUR_MAX);
- /* character not supported in current locale is included */
- if (len < 1)
- len = 1;
- char_width = gdk_text_width (GTK_WIDGET (entry)->style->font,
- entry->text + i, len);
-
- if (x < (sum + char_width / 2))
- break;
- sum += char_width;
- }
- }
+static void
+gtk_entry_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ GtkEntry *entry = GTK_ENTRY (widget);
- return_val = i;
+ if (previous_style && GTK_WIDGET_REALIZED (widget))
+ {
+ gtk_entry_recompute (entry);
- return return_val;
+ gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+ gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+ }
}
-void
-gtk_entry_adjust_scroll (GtkEntry *entry)
+/* Default signal handlers
+ */
+static void
+gtk_entry_real_insert_text (GtkEntry *entry,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position)
{
- gint xoffset;
- gint text_area_width;
+ gint index;
+ gint n_chars;
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ if (new_text_length < 0)
+ new_text_length = strlen (new_text);
- if (!entry->text_area)
- return;
+ n_chars = g_utf8_strlen (new_text, new_text_length);
+ if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
+ {
+ gdk_beep ();
+ n_chars = entry->text_max_length - entry->text_length;
+ }
- gdk_window_get_size (entry->text_area, &text_area_width, NULL);
+ if (new_text_length + entry->n_bytes + 1 > entry->text_size)
+ {
+ while (new_text_length + entry->n_bytes + 1 > entry->text_size)
+ {
+ if (entry->text_size == 0)
+ entry->text_size = MIN_SIZE;
+ else
+ {
+ if (2 * (guint)entry->text_size < MAX_SIZE &&
+ 2 * (guint)entry->text_size > entry->text_size)
+ entry->text_size *= 2;
+ else
+ {
+ entry->text_size = MAX_SIZE;
+ new_text_length = entry->text_size - new_text_length - 1;
+ break;
+ }
+ }
+ }
- if (entry->current_pos > 0)
- xoffset = gdk_text_width (GTK_WIDGET (entry)->style->font, entry->text, entry->current_pos);
- else
- xoffset = 0;
- xoffset -= entry->scroll_offset;
+ entry->text = g_realloc (entry->text, entry->text_size);
+ }
+
+ index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
+
+ g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
+ memcpy (entry->text + index, new_text, new_text_length);
+
+ entry->n_bytes += new_text_length;
+ entry->text_length += n_chars;
+
+ /* NUL terminate for safety and convenience */
+ entry->text[entry->n_bytes] = '\0';
+
+ if (entry->current_pos > *position)
+ entry->current_pos += n_chars;
+
+ if (entry->selection_bound > *position)
+ entry->selection_bound += n_chars;
+
+ *position += n_chars;
- if (xoffset < 0)
- entry->scroll_offset += xoffset;
- else if (xoffset > text_area_width)
- entry->scroll_offset += (xoffset - text_area_width) + 1;
+ gtk_entry_recompute (entry);
}
static void
-gtk_entry_grow_text (GtkEntry *entry)
+gtk_entry_real_delete_text (GtkEntry *entry,
+ gint start_pos,
+ gint end_pos)
{
- gint previous_size;
- gint i;
+ if (start_pos < 0)
+ start_pos = 0;
+ if (end_pos < 0 || end_pos > entry->text_length)
+ end_pos = entry->text_length;
+
+ if (start_pos < end_pos)
+ {
+ gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
+ gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
+
+ g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes - end_index);
+ entry->text_length -= (end_pos - start_pos);
+ entry->n_bytes -= (end_index - start_index);
+
+ if (entry->current_pos > start_pos)
+ entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos;
+
+ if (entry->selection_bound > start_pos)
+ entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
+ }
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ /* We might have deleted the selection
+ */
+ gtk_entry_update_primary_selection (entry);
+
+ gtk_entry_recompute (entry);
+}
+
+
+static void
+gtk_entry_move_cursor (GtkEntry *entry,
+ GtkMovementStep step,
+ gint count,
+ gboolean extend_selection)
+{
+ gint new_pos = entry->current_pos;
+
+ gtk_entry_reset_im_context (entry);
+
+ switch (step)
+ {
+ case GTK_MOVEMENT_LOGICAL_POSITIONS:
+ new_pos = CLAMP (new_pos + count, 0, entry->text_length);
+ break;
+ case GTK_MOVEMENT_VISUAL_POSITIONS:
+ new_pos = gtk_entry_move_visually (entry, new_pos, count);
+ break;
+ case GTK_MOVEMENT_WORDS:
+ while (count > 0)
+ {
+ new_pos = gtk_entry_move_forward_word (entry, new_pos);
+ count--;
+ }
+ while (count < 0)
+ {
+ new_pos = gtk_entry_move_backward_word (entry, new_pos);
+ count++;
+ }
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+ case GTK_MOVEMENT_PARAGRAPH_ENDS:
+ case GTK_MOVEMENT_BUFFER_ENDS:
+ new_pos = count < 0 ? 0 : entry->text_length;
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ case GTK_MOVEMENT_PARAGRAPHS:
+ case GTK_MOVEMENT_PAGES:
+ break;
+ }
- previous_size = entry->text_size;
- if (!entry->text_size)
- entry->text_size = 128;
+ if (extend_selection)
+ gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
else
- entry->text_size *= 2;
- entry->text = g_realloc (entry->text, entry->text_size);
+ gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
+}
- for (i = previous_size; i < entry->text_size; i++)
- entry->text[i] = '\0';
+static void
+gtk_entry_insert_at_cursor (GtkEntry *entry,
+ const gchar *str)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint pos = entry->current_pos;
+
+ gtk_entry_reset_im_context (entry);
+
+ gtk_editable_insert_text (editable, str, -1, &pos);
+ gtk_editable_set_position (editable, pos);
}
static void
-gtk_entry_insert_text (GtkEntry *entry,
- const gchar *new_text,
- gint new_text_length,
- gint *position)
+gtk_entry_delete_from_cursor (GtkEntry *entry,
+ GtkDeleteType type,
+ gint count)
{
- gchar buf[64];
- gchar *text;
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint start_pos = entry->current_pos;
+ gint end_pos = entry->current_pos;
+
+ gtk_entry_reset_im_context (entry);
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ if (!entry->editable)
+ return;
- if (new_text_length <= 64)
- text = buf;
- else
- text = g_new (gchar, new_text_length);
+ if (entry->selection_bound != entry->current_pos)
+ {
+ gtk_editable_delete_selection (editable);
+ return;
+ }
+
+ switch (type)
+ {
+ case GTK_DELETE_CHARS:
+ end_pos = entry->current_pos + count;
+ gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
+ break;
+ case GTK_DELETE_WORDS:
+ if (count < 0)
+ {
+ /* Move to end of current word, or if not on a word, end of previous word */
+ end_pos = gtk_entry_move_backward_word (entry, end_pos);
+ end_pos = gtk_entry_move_forward_word (entry, end_pos);
+ }
+ else if (count > 0)
+ {
+ /* Move to beginning of current word, or if not on a word, begining of next word */
+ start_pos = gtk_entry_move_forward_word (entry, start_pos);
+ start_pos = gtk_entry_move_backward_word (entry, start_pos);
+ }
+
+ /* Fall through */
+ case GTK_DELETE_WORD_ENDS:
+ while (count < 0)
+ {
+ start_pos = gtk_entry_move_backward_word (entry, start_pos);
+ count++;
+ }
+ while (count > 0)
+ {
+ end_pos = gtk_entry_move_forward_word (entry, end_pos);
+ count--;
+ }
+ gtk_editable_delete_text (editable, start_pos, end_pos);
+ break;
+ case GTK_DELETE_DISPLAY_LINE_ENDS:
+ case GTK_DELETE_PARAGRAPH_ENDS:
+ if (count < 0)
+ gtk_editable_delete_text (editable, 0, entry->current_pos);
+ else
+ gtk_editable_delete_text (editable, entry->current_pos, -1);
+ break;
+ case GTK_DELETE_DISPLAY_LINES:
+ case GTK_DELETE_PARAGRAPHS:
+ gtk_editable_delete_text (editable, 0, -1);
+ break;
+ case GTK_DELETE_WHITESPACE:
+ gtk_entry_delete_whitespace (entry);
+ break;
+ }
+}
- strncpy (text, new_text, new_text_length);
+static void
+gtk_entry_copy_clipboard (GtkEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint start, end;
+
+ if (gtk_editable_get_selection_bounds (editable, &start, &end))
+ {
+ gchar *str = gtk_entry_get_public_chars (entry, start, end);
+ gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
+ g_free (str);
+ }
+}
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[INSERT_TEXT],
- text, new_text_length, position);
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[CHANGED]);
+static void
+gtk_entry_cut_clipboard (GtkEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint start, end;
- if (new_text_length > 64)
- g_free (text);
+ gtk_entry_copy_clipboard (entry);
+ if (gtk_editable_get_selection_bounds (editable, &start, &end))
+ gtk_editable_delete_text (editable, start, end);
}
static void
-gtk_entry_delete_text (GtkEntry *entry,
- gint start_pos,
- gint end_pos)
+gtk_entry_paste_clipboard (GtkEntry *entry)
{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ gtk_entry_paste (entry, GDK_NONE);
+}
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[DELETE_TEXT],
- start_pos, end_pos);
- gtk_signal_emit (GTK_OBJECT (entry), entry_signals[CHANGED]);
+static void
+gtk_entry_toggle_overwrite (GtkEntry *entry)
+{
+ entry->overwrite_mode = !entry->overwrite_mode;
}
+/* IM Context Callbacks
+ */
+
static void
-gtk_real_entry_insert_text (GtkEntry *entry,
- const gchar *new_text,
- gint new_text_length,
- gint *position)
+gtk_entry_commit_cb (GtkIMContext *context,
+ const gchar *str,
+ GtkEntry *entry)
{
- gchar *text;
- gint start_pos;
- gint end_pos;
- gint last_pos;
- gint i;
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint tmp_pos = entry->current_pos;
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
+ gtk_editable_set_position (editable, tmp_pos);
+}
- start_pos = *position;
- end_pos = start_pos + new_text_length;
- last_pos = new_text_length + entry->text_length;
+static void
+gtk_entry_preedit_changed_cb (GtkIMContext *context,
+ GtkEntry *entry)
+{
+ gchar *preedit_string;
+ gint cursor_pos;
+
+ gtk_im_context_get_preedit_string (entry->im_context,
+ &preedit_string, NULL,
+ &cursor_pos);
+ entry->preedit_length = strlen (preedit_string);
+ cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
+ entry->preedit_cursor = cursor_pos;
+ g_free (preedit_string);
+
+ gtk_entry_recompute (entry);
+}
- if (entry->selection_start_pos >= *position)
- entry->selection_start_pos += new_text_length;
- if (entry->selection_end_pos >= *position)
- entry->selection_end_pos += new_text_length;
+/* Internal functions
+ */
+
+static void
+gtk_entry_reset_layout (GtkEntry *entry)
+{
+ if (entry->cached_layout)
+ {
+ g_object_unref (G_OBJECT (entry->cached_layout));
+ entry->cached_layout = NULL;
+ }
+}
- while (last_pos >= entry->text_size)
- gtk_entry_grow_text (entry);
+static gboolean
+recompute_idle_func (gpointer data)
+{
+ GtkEntry *entry = GTK_ENTRY (data);
- text = entry->text;
- for (i = last_pos - 1; i >= end_pos; i--)
- text[i] = text[i- (end_pos - start_pos)];
- for (i = start_pos; i < end_pos; i++)
- text[i] = new_text[i - start_pos];
+ gtk_entry_adjust_scroll (entry);
+ gtk_entry_queue_draw (entry);
- entry->text_length += new_text_length;
- *position = end_pos;
+ entry->recompute_idle = FALSE;
+
+ return FALSE;
}
static void
-gtk_real_entry_delete_text (GtkEntry *entry,
- gint start_pos,
- gint end_pos)
+gtk_entry_recompute (GtkEntry *entry)
+{
+ gtk_entry_reset_layout (entry);
+
+ if (!entry->recompute_idle)
+ {
+ entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
+ recompute_idle_func, entry, NULL);
+ }
+}
+
+static void
+append_char (GString *str,
+ gunichar ch,
+ gint count)
{
- gchar *text;
- gint deletion_length;
gint i;
+ gint char_len;
+ gchar buf[7];
+
+ char_len = g_unichar_to_utf8 (ch, buf);
+
+ i = 0;
+ while (i < count)
+ {
+ g_string_append_len (str, buf, char_len);
+ ++i;
+ }
+}
+
+static PangoLayout *
+gtk_entry_create_layout (GtkEntry *entry,
+ gboolean include_preedit)
+{
+ PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL);
+ PangoAttrList *tmp_attrs = pango_attr_list_new ();
+
+ gchar *preedit_string = NULL;
+ gint preedit_length = 0;
+ PangoAttrList *preedit_attrs = NULL;
+
+ if (include_preedit)
+ {
+ gtk_im_context_get_preedit_string (entry->im_context,
+ &preedit_string, &preedit_attrs, NULL);
+ preedit_length = entry->preedit_length;
+ }
+
+ if (preedit_length)
+ {
+ GString *tmp_string = g_string_new (NULL);
+
+ gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
+
+ if (entry->visible)
+ {
+ g_string_prepend_len (tmp_string, entry->text, entry->n_bytes);
+ g_string_insert (tmp_string, cursor_index, preedit_string);
+ }
+ else
+ {
+ gint ch_len;
+ gint preedit_len_chars;
+ gunichar invisible_char;
+
+ ch_len = g_utf8_strlen (entry->text, entry->n_bytes);
+ preedit_len_chars = g_utf8_strlen (preedit_string, -1);
+ ch_len += preedit_len_chars;
+
+ if (entry->invisible_char != 0)
+ invisible_char = entry->invisible_char;
+ else
+ invisible_char = ' '; /* just pick a char */
+
+ append_char (tmp_string, invisible_char, ch_len);
+
+ /* Fix cursor index to point to invisible char corresponding
+ * to the preedit, fix preedit_length to be the length of
+ * the invisible chars representing the preedit
+ */
+ cursor_index =
+ g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) -
+ tmp_string->str;
+ preedit_length =
+ preedit_len_chars *
+ g_unichar_to_utf8 (invisible_char, NULL);
+ }
+
+ pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
+
+ pango_attr_list_splice (tmp_attrs, preedit_attrs,
+ cursor_index, preedit_length);
+
+ g_string_free (tmp_string, TRUE);
+ }
+ else
+ {
+ if (entry->visible)
+ {
+ pango_layout_set_text (layout, entry->text, entry->n_bytes);
+ }
+ else
+ {
+ GString *str = g_string_new (NULL);
+ gunichar invisible_char;
+
+ if (entry->invisible_char != 0)
+ invisible_char = entry->invisible_char;
+ else
+ invisible_char = ' '; /* just pick a char */
+
+ append_char (str, invisible_char, entry->text_length);
+ pango_layout_set_text (layout, str->str, str->len);
+ g_string_free (str, TRUE);
+ }
+ }
+
+ pango_layout_set_attributes (layout, tmp_attrs);
+
+ if (preedit_string)
+ g_free (preedit_string);
+ if (preedit_attrs)
+ pango_attr_list_unref (preedit_attrs);
+
+ pango_attr_list_unref (tmp_attrs);
+
+ return layout;
+}
+
+static PangoLayout *
+gtk_entry_get_layout (GtkEntry *entry,
+ gboolean include_preedit)
+{
+ if (entry->preedit_length > 0 &&
+ !include_preedit != !entry->cache_includes_preedit)
+ gtk_entry_reset_layout (entry);
+ if (!entry->cached_layout)
+ {
+ entry->cached_layout = gtk_entry_create_layout (entry, include_preedit);
+ entry->cache_includes_preedit = include_preedit;
+ }
+
+ g_object_ref (G_OBJECT (entry->cached_layout));
+ return entry->cached_layout;
+}
+
+static void
+gtk_entry_draw_text (GtkEntry *entry)
+{
+ GtkWidget *widget;
+ PangoLayoutLine *line;
+
g_return_if_fail (entry != NULL);
g_return_if_fail (GTK_IS_ENTRY (entry));
- if ((start_pos < end_pos) &&
- (start_pos >= 0) &&
- (end_pos <= entry->text_length))
+ if (!entry->visible && entry->invisible_char == 0)
+ return;
+
+ if (GTK_WIDGET_DRAWABLE (entry))
{
- text = entry->text;
- deletion_length = end_pos - start_pos;
+ PangoLayout *layout = gtk_entry_get_layout (entry, TRUE);
+ PangoRectangle logical_rect;
+ gint area_width, area_height;
+ gint start_pos, end_pos;
+ gint y_pos;
+
+ gdk_window_get_size (entry->text_area, &area_width, &area_height);
+ area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
+
+ widget = GTK_WIDGET (entry);
- for (i = end_pos; i < entry->text_length; i++)
- text[i - deletion_length] = text[i];
+ gtk_paint_flat_box (widget->style, entry->text_area,
+ GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
+ NULL, widget, "entry_bg",
+ 0, 0, area_width, area_height);
+
+ line = pango_layout_get_lines (layout)->data;
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
+
+ /* Align primarily for locale's ascent/descent */
+ y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
+ entry->ascent + logical_rect.y);
+
+ /* Now see if we need to adjust to fit in actual drawn string */
+ if (logical_rect.height > area_height)
+ y_pos = (area_height - logical_rect.height) / 2;
+ else if (y_pos < 0)
+ y_pos = 0;
+ else if (y_pos + logical_rect.height > area_height)
+ y_pos = area_height - logical_rect.height;
+
+ y_pos = INNER_BORDER + y_pos / PANGO_SCALE;
+
+ gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state],
+ INNER_BORDER - entry->scroll_offset, y_pos,
+ layout);
+
+ if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
+ {
+ gint *ranges;
+ gint n_ranges, i;
+ gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
+ gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
+ GdkRegion *clip_region = gdk_region_new ();
+
+ pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
+
+ for (i=0; i < n_ranges; i++)
+ {
+ GdkRectangle rect;
+
+ rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
+ rect.y = y_pos;
+ rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
+ rect.height = logical_rect.height / PANGO_SCALE;
+
+ gdk_draw_rectangle (entry->text_area, widget->style->bg_gc [GTK_STATE_SELECTED], TRUE,
+ rect.x, rect.y, rect.width, rect.height);
+
+ gdk_region_union_with_rect (clip_region, &rect);
+ }
- for (i = entry->text_length - deletion_length; i < entry->text_length; i++)
- text[i] = '\0';
+ gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], clip_region);
+ gdk_draw_layout (entry->text_area, widget->style->fg_gc [GTK_STATE_SELECTED],
+ INNER_BORDER - entry->scroll_offset, y_pos,
+ layout);
+ gdk_gc_set_clip_region (widget->style->fg_gc [GTK_STATE_SELECTED], NULL);
+
+ gdk_region_destroy (clip_region);
+ g_free (ranges);
+ }
- entry->text_length -= deletion_length;
- entry->current_pos = start_pos;
+ g_object_unref (G_OBJECT (layout));
}
}
+static void
+gtk_entry_draw_cursor (GtkEntry *entry)
+{
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ if (!entry->visible && entry->invisible_char == 0)
+ return;
+
+ if (GTK_WIDGET_DRAWABLE (entry))
+ {
+ GtkWidget *widget = GTK_WIDGET (entry);
+
+ if (GTK_WIDGET_HAS_FOCUS (widget) &&
+ (entry->selection_bound == entry->current_pos))
+ {
+ gint xoffset = INNER_BORDER - entry->scroll_offset;
+ gint strong_x, weak_x;
+ gint text_area_height;
+
+ gdk_window_get_size (entry->text_area, NULL, &text_area_height);
+
+ gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
+
+ gdk_draw_line (entry->text_area, widget->style->bg_gc[GTK_STATE_SELECTED],
+ xoffset + strong_x, INNER_BORDER,
+ xoffset + strong_x, text_area_height - INNER_BORDER);
+
+ if (weak_x != strong_x)
+ gdk_draw_line (entry->text_area, widget->style->fg_gc[GTK_STATE_NORMAL],
+ xoffset + weak_x, INNER_BORDER,
+ xoffset + weak_x, text_area_height - INNER_BORDER);
+
+ }
+ }
+}
static void
-gtk_move_forward_character (GtkEntry *entry)
+gtk_entry_queue_draw (GtkEntry *entry)
{
- gint len;
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
- if (entry->current_pos < entry->text_length)
+ if (GTK_WIDGET_REALIZED (entry))
{
- len = mblen (entry->text+entry->current_pos, MB_CUR_MAX);
- entry->current_pos += (len>0)? len:1;
+ GdkRectangle rect = { 0 };
+
+ gdk_window_get_size (entry->text_area, &rect.width, &rect.height);
+ gdk_window_invalidate_rect (entry->text_area, &rect, FALSE);
}
- if (entry->current_pos > entry->text_length)
- entry->current_pos = entry->text_length;
+}
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+static void
+gtk_entry_reset_im_context (GtkEntry *entry)
+{
+ if (entry->need_im_reset)
+ {
+ entry->need_im_reset = 0;
+ gtk_im_context_reset (entry->im_context);
+ }
}
static gint
-move_backward_character (gchar *str, gint index)
+gtk_entry_find_position (GtkEntry *entry,
+ gint x)
{
- gint i;
- gint len;
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+ gint index;
+ gint pos;
+ gboolean trailing;
+ gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
+
+ layout = gtk_entry_get_layout (entry, TRUE);
+
+ line = pango_layout_get_lines (layout)->data;
+ pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
+
+ g_object_unref (G_OBJECT (layout));
- if (index <= 0)
- return -1;
- for (i=0,len=0; i<index; i+=len)
+ if (index >= cursor_index && entry->preedit_length)
{
- len = mblen (str+i, MB_CUR_MAX);
- if (len<1)
- return i;
+ if (index >= cursor_index + entry->preedit_length)
+ index -= entry->preedit_length;
+ else
+ {
+ index = cursor_index;
+ trailing = 0;
+ }
}
- return i-len;
+
+ pos = g_utf8_pointer_to_offset (entry->text, entry->text + index);
+
+ if (trailing)
+ pos += 1;
+
+ return pos;
}
static void
-gtk_move_backward_character (GtkEntry *entry)
+gtk_entry_get_cursor_locations (GtkEntry *entry,
+ gint *strong_x,
+ gint *weak_x)
{
- gint i;
- gint diff = 0;
- /* this routine is correct only if string is state-independent-encoded */
+ PangoLayout *layout = gtk_entry_get_layout (entry, TRUE);
+ const gchar *text;
+ PangoRectangle strong_pos, weak_pos;
+ gint index;
+
+ text = pango_layout_get_text (layout);
- if (0 < entry->current_pos)
- {
- entry->current_pos = move_backward_character (entry->text,
- entry->current_pos);
- if (entry->current_pos < 0)
- entry->current_pos = 0;
- }
+ index =
+ g_utf8_offset_to_pointer (text,
+ entry->current_pos +
+ entry->preedit_cursor) - text;
+
+ pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
+ g_object_unref (G_OBJECT (layout));
+
+ if (strong_x)
+ *strong_x = strong_pos.x / PANGO_SCALE;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ if (weak_x)
+ *weak_x = weak_pos.x / PANGO_SCALE;
}
static void
-gtk_move_forward_word (GtkEntry *entry)
+gtk_entry_adjust_scroll (GtkEntry *entry)
{
- gchar *text;
- gint i;
- wchar_t c;
- gint len;
+ GtkWidget *widget;
+ gint min_offset, max_offset;
+ gint text_area_width;
+ gint strong_x, weak_x;
+ gint strong_xoffset, weak_xoffset;
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+ PangoRectangle logical_rect;
- if (entry->text)
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ widget = GTK_WIDGET (entry);
+
+ if (!GTK_WIDGET_REALIZED (entry))
+ return;
+
+ gdk_window_get_size (entry->text_area, &text_area_width, NULL);
+ text_area_width -= 2 * INNER_BORDER;
+
+ layout = gtk_entry_get_layout (entry, TRUE);
+ line = pango_layout_get_lines (layout)->data;
+
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
+ g_object_unref (G_OBJECT (layout));
+
+ /* Display as much text as we can */
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
{
- text = entry->text;
- i = entry->current_pos;
+ min_offset = 0;
+ max_offset = MAX (min_offset, logical_rect.width / PANGO_SCALE - text_area_width);
+ }
+ else
+ {
+ max_offset = logical_rect.width / PANGO_SCALE - text_area_width;
+ min_offset = MIN (0, max_offset);
+ }
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (!(!isascii(c) || c == '_' || iswalnum (c)))
- for (; i < entry->text_length; i+=len)
- {
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (len < 1 || !isascii(c) || c == '_' || iswalnum (c))
- break;
- }
+ entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
+
+ /* And make sure cursors are on screen. Note that the cursor is
+ * actually drawn one pixel into the INNER_BORDER space on
+ * the right, when the scroll is at the utmost right. This
+ * looks better to to me than confining the cursor inside the
+ * border entirely, though it means that the cursor gets one
+ * pixel closer to the the edge of the widget on the right than
+ * on the left. This might need changing if one changed
+ * INNER_BORDER from 2 to 1, as one would do on a
+ * small-screen-real-estate display.
+ *
+ * We always make sure that the strong cursor is on screen, and
+ * put the weak cursor on screen if possible.
+ */
+
+ gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
- for (; i < entry->text_length; i+=len)
- {
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (len < 1 || !(!isascii(c) || c == '_' || iswalnum (c)))
- break;
- }
+ strong_xoffset = strong_x - entry->scroll_offset;
- entry->current_pos = i;
- if (entry->current_pos > entry->text_length)
- entry->current_pos = entry->text_length;
+ if (strong_xoffset < 0)
+ {
+ entry->scroll_offset += strong_xoffset;
+ strong_xoffset = 0;
}
+ else if (strong_xoffset > text_area_width)
+ {
+ entry->scroll_offset += strong_xoffset - text_area_width;
+ strong_xoffset = text_area_width;
+ }
+
+ weak_xoffset = weak_x - entry->scroll_offset;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
+ {
+ entry->scroll_offset += weak_xoffset;
+ }
+ else if (weak_xoffset > text_area_width &&
+ strong_xoffset - (weak_xoffset - text_area_width) >= 0)
+ {
+ entry->scroll_offset += weak_xoffset - text_area_width;
+ }
}
-static void
-gtk_move_backward_word (GtkEntry *entry)
+static gint
+gtk_entry_move_visually (GtkEntry *entry,
+ gint start,
+ gint count)
{
- gchar *text;
- gint i;
- wchar_t c;
- gint len;
+ gint index;
+ PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
+ const gchar *text;
- if (entry->text)
+ text = pango_layout_get_text (layout);
+
+ index = g_utf8_offset_to_pointer (text, start) - text;
+
+ while (count != 0)
{
- text = entry->text;
- i = entry->current_pos - 1;
- if (i < 0) /* Per */
+ int new_index, new_trailing;
+
+ if (count > 0)
{
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
- return;
+ pango_layout_move_cursor_visually (layout, index, 0, 1, &new_index, &new_trailing);
+ count--;
}
-
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (!(!isascii(c) || (c == '_') || iswalnum (c)))
- for (; i >= 0; i=move_backward_character(text, i))
- {
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (!isascii(c) || (c == '_') || iswalnum (c))
- break;
- }
-
- for (; i >= 0; i=move_backward_character(text, i))
+ else
{
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (!(!isascii(c) || (c == '_') || iswalnum (c)))
- {
- i += len;
- break;
- }
+ pango_layout_move_cursor_visually (layout, index, 0, -1, &new_index, &new_trailing);
+ count++;
}
- entry->current_pos = i;
- if (entry->current_pos < 0)
- entry->current_pos = 0;
+ if (new_index < 0 || new_index == G_MAXINT)
+ break;
+
+ if (new_trailing)
+ index = g_utf8_next_char (entry->text + new_index) - entry->text;
+ else
+ index = new_index;
+ }
- if (text[entry->current_pos] == ' ')
- entry->current_pos += 1;
+ g_object_unref (G_OBJECT (layout));
+
+ return g_utf8_pointer_to_offset (text, text + index);
+}
+
+static gint
+gtk_entry_move_forward_word (GtkEntry *entry,
+ gint start)
+{
+ gint new_pos = start;
+
+ /* Prevent any leak of information */
+ if (!entry->visible)
+ {
+ new_pos = entry->text_length;
+ }
+ else if (entry->text && (new_pos < entry->text_length))
+ {
+ PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
+ PangoLogAttr *log_attrs;
+ gint n_attrs;
+
+ pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
+
+ /* Find the next word end */
+ new_pos++;
+ while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
+ new_pos++;
+
+ g_free (log_attrs);
+ g_object_unref (G_OBJECT (layout));
+ }
+
+ return new_pos;
+}
+
+
+static gint
+gtk_entry_move_backward_word (GtkEntry *entry,
+ gint start)
+{
+ gint new_pos = start;
+
+ /* Prevent any leak of information */
+ if (!entry->visible)
+ {
+ new_pos = 0;
+ }
+ else if (entry->text && start > 0)
+ {
+ PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
+ PangoLogAttr *log_attrs;
+ gint n_attrs;
+
+ pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
+
+ new_pos = start - 1;
+
+ /* Find the previous word beginning */
+ while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
+ new_pos--;
+
+ g_free (log_attrs);
+ g_object_unref (G_OBJECT (layout));
}
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ return new_pos;
}
static void
-gtk_move_beginning_of_line (GtkEntry *entry)
+gtk_entry_delete_whitespace (GtkEntry *entry)
{
- entry->current_pos = 0;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
+ PangoLogAttr *log_attrs;
+ gint n_attrs;
+ gint start, end;
+
+ pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
+
+ start = end = entry->current_pos;
+
+ while (start > 0 && log_attrs[start-1].is_white)
+ start--;
+
+ while (end < n_attrs && log_attrs[start-1].is_white)
+ end++;
+
+ g_free (log_attrs);
+ g_object_unref (G_OBJECT (layout));
+
+ if (start != end)
+ gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
}
+
static void
-gtk_move_end_of_line (GtkEntry *entry)
+gtk_entry_select_word (GtkEntry *entry)
{
- entry->current_pos = entry->text_length;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ gint start_pos = gtk_entry_move_backward_word (entry, entry->current_pos);
+ gint end_pos = gtk_entry_move_forward_word (entry, entry->current_pos);
+
+ gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
}
static void
-gtk_delete_forward_character (GtkEntry *entry)
+gtk_entry_select_line (GtkEntry *entry)
{
- gint old_pos;
+ gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+}
- old_pos = entry->current_pos;
- gtk_move_forward_character (entry);
- gtk_entry_delete_text (entry, old_pos, entry->current_pos);
+/*
+ * Like gtk_editable_get_chars, but if the editable is not
+ * visible, return asterisks; also convert result to UTF-8.
+ */
+static char *
+gtk_entry_get_public_chars (GtkEntry *entry,
+ gint start,
+ gint end)
+{
+ if (end < 0)
+ end = entry->text_length;
+
+ if (entry->visible)
+ return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
+ else
+ {
+ gchar *str;
+ gint i;
+ gint n_chars = end - start;
+
+ str = g_malloc (n_chars + 1);
+ for (i = 0; i < n_chars; i++)
+ str[i] = '*';
+ str[i] = '\0';
+
+ return str;
+ }
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
}
static void
-gtk_delete_backward_character (GtkEntry *entry)
+paste_received (GtkClipboard *clipboard,
+ const gchar *text,
+ gpointer data)
{
- gint old_pos;
+ GtkEntry *entry = GTK_ENTRY (data);
+ GtkEditable *editable = GTK_EDITABLE (entry);
+
+ if (text)
+ {
+ gint pos = entry->current_pos;
+
+ gtk_editable_insert_text (editable, text, -1, &pos);
+ gtk_editable_set_position (editable, pos);
+ }
- old_pos = entry->current_pos;
- gtk_move_backward_character (entry);
- gtk_entry_delete_text (entry, entry->current_pos, old_pos);
+ g_object_unref (G_OBJECT (entry));
+}
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+static void
+gtk_entry_paste (GtkEntry *entry,
+ GdkAtom selection)
+{
+ g_object_ref (G_OBJECT (entry));
+ gtk_clipboard_request_text (gtk_clipboard_get (selection),
+ paste_received, entry);
}
static void
-gtk_delete_forward_word (GtkEntry *entry)
+primary_get_cb (GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ guint info,
+ gpointer data)
{
- gint old_pos;
+ GtkEntry *entry = GTK_ENTRY (data);
+ gint start, end;
+
+ if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
+ {
+ gchar *str = gtk_entry_get_public_chars (entry, start, end);
+ gtk_selection_data_set_text (selection_data, str);
+ g_free (str);
+ }
+}
- old_pos = entry->current_pos;
- gtk_move_forward_word (entry);
- gtk_entry_delete_text (entry, old_pos, entry->current_pos);
+static void
+primary_clear_cb (GtkClipboard *clipboard,
+ gpointer data)
+{
+ GtkEntry *entry = GTK_ENTRY (data);
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
}
static void
-gtk_delete_backward_word (GtkEntry *entry)
+gtk_entry_update_primary_selection (GtkEntry *entry)
+{
+ static const GtkTargetEntry targets[] = {
+ { "UTF8_STRING", 0, 0 },
+ { "STRING", 0, 0 },
+ { "TEXT", 0, 0 },
+ { "COMPOUND_TEXT", 0, 0 }
+ };
+
+ GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+ gint start, end;
+
+ if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
+ {
+ if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
+ primary_get_cb, primary_clear_cb, G_OBJECT (entry)))
+ primary_clear_cb (clipboard, entry);
+ }
+ else
+ {
+ if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
+ gtk_clipboard_clear (clipboard);
+ }
+}
+
+/* Public API
+ */
+
+GtkWidget*
+gtk_entry_new (void)
+{
+ return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
+}
+
+GtkWidget*
+gtk_entry_new_with_max_length (guint16 max)
{
- gint old_pos;
+ GtkEntry *entry;
- old_pos = entry->current_pos;
- gtk_move_backward_word (entry);
- gtk_entry_delete_text (entry, entry->current_pos, old_pos);
+ entry = gtk_type_new (GTK_TYPE_ENTRY);
+ entry->text_max_length = max;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ return GTK_WIDGET (entry);
}
-static void
-gtk_delete_line (GtkEntry *entry)
+void
+gtk_entry_set_text (GtkEntry *entry,
+ const gchar *text)
{
- gtk_entry_delete_text (entry, 0, entry->text_length);
+ gint tmp_pos;
+
+ GtkEditable *editable;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (text != NULL);
+
+ editable = GTK_EDITABLE (entry);
+
+ gtk_editable_delete_text (GTK_EDITABLE(entry), 0, -1);
+
+ tmp_pos = 0;
+ gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
}
-static void
-gtk_delete_to_line_end (GtkEntry *entry)
+void
+gtk_entry_append_text (GtkEntry *entry,
+ const gchar *text)
{
- gtk_entry_delete_text (entry, entry->current_pos, entry->text_length);
+ gint tmp_pos;
+
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (text != NULL);
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ tmp_pos = entry->text_length;
+ gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
}
-static void
-gtk_delete_selection (GtkEntry *entry)
+void
+gtk_entry_prepend_text (GtkEntry *entry,
+ const gchar *text)
{
- if (entry->selection_start_pos != entry->selection_end_pos)
- gtk_entry_delete_text (entry,
- MIN (entry->selection_start_pos, entry->selection_end_pos),
- MAX (entry->selection_start_pos, entry->selection_end_pos));
+ gint tmp_pos;
- entry->selection_start_pos = 0;
- entry->selection_end_pos = 0;
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (text != NULL);
- if (entry->have_selection)
- {
- entry->have_selection = FALSE;
- gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
- }
+ tmp_pos = 0;
+ gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
}
-static void
-gtk_select_word (GtkEntry *entry)
+void
+gtk_entry_set_position (GtkEntry *entry,
+ gint position)
{
- gint start_pos;
- gint end_pos;
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
- gtk_move_backward_word (entry);
- start_pos = entry->current_pos;
+ gtk_editable_set_position (GTK_EDITABLE (entry), position);
+}
- gtk_move_forward_word (entry);
- end_pos = entry->current_pos;
+void
+gtk_entry_set_visibility (GtkEntry *entry,
+ gboolean visible)
+{
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ entry->visible = visible ? TRUE : FALSE;
+
+ gtk_entry_recompute (entry);
+}
+
+void
+gtk_entry_set_invisible_char (GtkEntry *entry,
+ gunichar ch)
+{
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ if (ch == entry->invisible_char)
+ return;
+
+ entry->invisible_char = ch;
+
+ gtk_entry_recompute (entry);
+}
+
+void
+gtk_entry_set_editable(GtkEntry *entry,
+ gboolean editable)
+{
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ gtk_editable_set_editable (GTK_EDITABLE (entry), editable);
+}
+
+gchar*
+gtk_entry_get_text (GtkEntry *entry)
+{
+ g_return_val_if_fail (entry != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
- gtk_select_region (entry, start_pos, end_pos);
+ return entry->text;
}
+void
+gtk_entry_select_region (GtkEntry *entry,
+ gint start,
+ gint end)
+{
+ gtk_editable_select_region (GTK_EDITABLE (entry), start, end);
+}
+
+void
+gtk_entry_set_max_length (GtkEntry *entry,
+ guint16 max)
+{
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ if (max && entry->text_length > max)
+ gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
+
+ entry->text_max_length = max;
+}
+
+/* Quick hack of a popup menu
+ */
static void
-gtk_select_line (GtkEntry *entry)
+activate_cb (GtkWidget *menuitem,
+ GtkEntry *entry)
{
- gtk_select_region (entry, 0, entry->text_length);
- entry->current_pos = entry->selection_end_pos;
+ const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
+ gtk_signal_emit_by_name (GTK_OBJECT (entry), signal);
}
static void
-gtk_select_region (GtkEntry *entry,
- gint start,
- gint end)
+append_action_signal (GtkEntry *entry,
+ GtkWidget *menu,
+ const gchar *label,
+ const gchar *signal)
{
- entry->have_selection = TRUE;
- entry->selection_start_pos = start;
- entry->selection_end_pos = end;
+ GtkWidget *menuitem = gtk_menu_item_new_with_label (label);
+
+ gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-signal", (char *)signal);
+ gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
+ activate_cb, entry);
+
+ gtk_widget_show (menuitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+}
+
+static void
+popup_menu_detach (GtkWidget *attach_widget,
+ GtkMenu *menu)
+{
+ GTK_ENTRY (attach_widget)->popup_menu = NULL;
+}
+
+static void
+gtk_entry_popup_menu (GtkEntry *entry,
+ GdkEventButton *event)
+{
+ if (!entry->popup_menu)
+ {
+ GtkWidget *menuitem;
+
+ entry->popup_menu = gtk_menu_new ();
+
+ gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu),
+ GTK_WIDGET (entry),
+ popup_menu_detach);
+
+ append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard");
+ append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard");
+ append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard");
+
+ menuitem = gtk_menu_item_new (); /* Separator */
+ gtk_widget_show (menuitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
+
+ gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context),
+ GTK_MENU_SHELL (entry->popup_menu));
+ }
+
+ gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
+ NULL, NULL,
+ event->button, event->time);
}