* 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
+ * 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"
-#include "gtkprivate.h"
#define MIN_ENTRY_WIDTH 150
#define DRAW_TIMEOUT 20
#define INNER_BORDER 2
-static void gtk_entry_class_init (GtkEntryClass *klass);
-static void gtk_entry_init (GtkEntry *entry);
-static void gtk_entry_finalize (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_make_backing_pixmap (GtkEntry *entry,
- gint width, gint height);
-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 void gtk_entry_draw_text (GtkEntry *entry);
-static void gtk_entry_draw_cursor (GtkEntry *entry);
-static void gtk_entry_draw_cursor_on_drawable
- (GtkEntry *entry,
- GdkDrawable *drawable);
-static void gtk_entry_style_set (GtkWidget *widget,
- GtkStyle *previous_style);
-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 (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 void gtk_entry_update_text (GtkEditable *editable,
- gint start_pos,
- gint end_pos);
-static gchar *gtk_entry_get_chars (GtkEditable *editable,
- gint start_pos,
- gint end_pos);
-
-/* Binding actions */
-static void gtk_entry_move_cursor (GtkEditable *editable,
- gint x,
- gint y);
-static void gtk_entry_move_word (GtkEditable *editable,
- gint n);
-static void gtk_entry_move_to_column (GtkEditable *editable,
- gint row);
-static void gtk_entry_kill_char (GtkEditable *editable,
- gint direction);
-static void gtk_entry_kill_word (GtkEditable *editable,
- gint direction);
-static void gtk_entry_kill_line (GtkEditable *editable,
- gint direction);
-
-/* To be removed */
-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_select_word (GtkEntry *entry,
- guint32 time);
-static void gtk_select_line (GtkEntry *entry,
- guint32 time);
-
-
-static void gtk_entry_set_selection (GtkEditable *editable,
- gint start,
- gint end);
-
-static void gtk_entry_recompute_offsets (GtkEntry *entry);
-static gint gtk_entry_find_char (GtkEntry *entry,
- gint position);
-static gint gtk_entry_find_position (GtkEntry *entry,
- gint position);
-static void gtk_entry_set_position_from_editable (GtkEditable *editable,
- gint position);
-
-static GtkWidgetClass *parent_class = NULL;
-static GdkAtom ctext_atom = GDK_NONE;
-
-static GtkTextFunction control_keys[26] =
-{
- (GtkTextFunction)gtk_move_beginning_of_line, /* a */
- (GtkTextFunction)gtk_move_backward_character, /* b */
- (GtkTextFunction)gtk_editable_copy_clipboard, /* c */
- (GtkTextFunction)gtk_delete_forward_character, /* d */
- (GtkTextFunction)gtk_move_end_of_line, /* e */
- (GtkTextFunction)gtk_move_forward_character, /* f */
- NULL, /* g */
- (GtkTextFunction)gtk_delete_backward_character, /* h */
- NULL, /* i */
- NULL, /* j */
- (GtkTextFunction)gtk_delete_to_line_end, /* k */
- NULL, /* l */
- NULL, /* m */
- NULL, /* n */
- NULL, /* o */
- NULL, /* p */
- NULL, /* q */
- NULL, /* r */
- NULL, /* s */
- NULL, /* t */
- (GtkTextFunction)gtk_delete_line, /* u */
- (GtkTextFunction)gtk_editable_paste_clipboard, /* v */
- (GtkTextFunction)gtk_delete_backward_word, /* w */
- (GtkTextFunction)gtk_editable_cut_clipboard, /* x */
- NULL, /* y */
- NULL, /* z */
+/* 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,
+ ACTIVATE,
+ MOVE_CURSOR,
+ INSERT_AT_CURSOR,
+ DELETE_FROM_CURSOR,
+ CUT_CLIPBOARD,
+ COPY_CLIPBOARD,
+ PASTE_CLIPBOARD,
+ TOGGLE_OVERWRITE,
+ LAST_SIGNAL
};
-static GtkTextFunction alt_keys[26] =
-{
- NULL, /* a */
- (GtkTextFunction)gtk_move_backward_word, /* b */
- NULL, /* c */
- (GtkTextFunction)gtk_delete_forward_word, /* d */
- NULL, /* e */
- (GtkTextFunction)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 */
+enum {
+ ARG_0,
+ ARG_TEXT_POSITION,
+ ARG_EDITABLE,
+ ARG_MAX_LENGTH,
+ ARG_VISIBILITY,
+ ARG_INVISIBLE_CHAR
};
+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;
-guint
+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,
- (GtkArgSetFunc) NULL,
- (GtkArgGetFunc) 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_editable_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;
- GtkEditableClass *editable_class;
+ GtkBindingSet *binding_set;
object_class = (GtkObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
- editable_class = (GtkEditableClass*) class;
-
- parent_class = gtk_type_class (gtk_editable_get_type ());
+ parent_class = gtk_type_class (GTK_TYPE_WIDGET);
- object_class->finalize = gtk_entry_finalize;
+ 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->style_set = gtk_entry_style_set;
-
- editable_class->insert_text = gtk_entry_insert_text;
- editable_class->delete_text = gtk_entry_delete_text;
- editable_class->changed = (void (*)(GtkEditable *)) gtk_entry_adjust_scroll;
-
- editable_class->move_cursor = gtk_entry_move_cursor;
- editable_class->move_word = gtk_entry_move_word;
- editable_class->move_to_column = gtk_entry_move_to_column;
-
- editable_class->kill_char = gtk_entry_kill_char;
- editable_class->kill_word = gtk_entry_kill_word;
- editable_class->kill_line = gtk_entry_kill_line;
-
- editable_class->update_text = gtk_entry_update_text;
- editable_class->get_chars = gtk_entry_get_chars;
- editable_class->set_selection = gtk_entry_set_selection;
- editable_class->set_position = gtk_entry_set_position_from_editable;
-}
-
-static void
-gtk_entry_init (GtkEntry *entry)
-{
- GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
-
- entry->text_area = NULL;
- entry->backing_pixmap = NULL;
- entry->text = NULL;
- entry->text_size = 0;
- entry->text_length = 0;
- entry->text_max_length = 0;
- entry->scroll_offset = 0;
- entry->timer = 0;
- entry->button = 0;
- entry->visible = 1;
-
- entry->nchars = 0;
- entry->char_pos = NULL;
- entry->char_offset = NULL;
+ 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);
+
+ 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);
+
+ 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);
+
+ /* 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_entry_grow_text (entry);
-}
+ 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);
-GtkWidget*
-gtk_entry_new (void)
-{
- return GTK_WIDGET (gtk_type_new (gtk_entry_get_type ()));
-}
+ 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);
-GtkWidget*
-gtk_entry_new_with_max_length (guint16 max)
-{
- GtkEntry *entry;
- entry = gtk_type_new (gtk_entry_get_type ());
- entry->text_max_length = max;
- return GTK_WIDGET (entry);
-}
+ add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
+ GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
-void
-gtk_entry_set_text (GtkEntry *entry,
- const gchar *text)
-{
- gint tmp_pos;
+ add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
+ GTK_MOVEMENT_WORDS, 1);
- GtkEditable *editable;
+ add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
+ GTK_MOVEMENT_WORDS, -1);
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
- g_return_if_fail (text != NULL);
+ add_move_binding (binding_set, GDK_Home, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
- editable = GTK_EDITABLE (entry);
+ add_move_binding (binding_set, GDK_End, 0,
+ GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
- gtk_entry_delete_text (GTK_EDITABLE(entry), 0, entry->text_length);
-
- tmp_pos = 0;
- gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
- editable->current_pos = tmp_pos;
-
- editable->selection_start_pos = 0;
- editable->selection_end_pos = 0;
+ 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 */
- if (GTK_WIDGET_DRAWABLE (entry))
- gtk_entry_draw_text (entry);
-}
+ gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
+ "cut_clipboard", 0);
-void
-gtk_entry_append_text (GtkEntry *entry,
- const gchar *text)
-{
- gint tmp_pos;
+ 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);
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
- g_return_if_fail (text != NULL);
+ gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
+ "paste_clipboard", 0);
- tmp_pos = entry->text_length;
- gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
- GTK_EDITABLE(entry)->current_pos = tmp_pos;
+ /* Overwrite */
+ gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
+ "toggle_overwrite", 0);
}
-void
-gtk_entry_prepend_text (GtkEntry *entry,
- const gchar *text)
+static void
+gtk_entry_editable_init (GtkEditableClass *iface)
{
- gint tmp_pos;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
- g_return_if_fail (text != NULL);
-
- tmp_pos = 0;
- gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
- GTK_EDITABLE(entry)->current_pos = tmp_pos;
+ 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;
}
-void
-gtk_entry_set_position (GtkEntry *entry,
- gint position)
+static void
+gtk_entry_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id)
{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ GtkEntry *entry = GTK_ENTRY (object);
- if ((position == -1) || (position > entry->text_length))
- GTK_EDITABLE(entry)->current_pos = entry->text_length;
- else
- GTK_EDITABLE(entry)->current_pos = position;
+ 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_set_position_from_editable (GtkEditable *editable,
- gint position)
-{
- gtk_entry_set_position (GTK_ENTRY (editable), position);
-}
-
-void
-gtk_entry_set_visibility (GtkEntry *entry,
- gboolean visible)
+gtk_entry_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id)
{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
-
- entry->visible = visible;
- gtk_entry_recompute_offsets (entry);
- gtk_widget_queue_draw (GTK_WIDGET (entry));
-}
+ GtkEntry *entry;
-void
-gtk_entry_set_editable(GtkEntry *entry,
- gboolean editable)
-{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ entry = GTK_ENTRY (object);
- GTK_EDITABLE (entry)->editable = editable;
- gtk_entry_queue_draw (entry);
+ 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;
+ }
}
-gchar*
-gtk_entry_get_text (GtkEntry *entry)
+static void
+gtk_entry_init (GtkEntry *entry)
{
- static char empty_str[2] = "";
+ GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
- g_return_val_if_fail (entry != NULL, NULL);
- g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+ entry->text_size = MIN_SIZE;
+ entry->text = g_malloc (entry->text_size);
+ entry->text[0] = '\0';
- if (!entry->text)
- return empty_str;
- return entry->text;
+ 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_finalize (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 (GTK_EDITABLE(entry)->ic)
- {
- gdk_ic_destroy (GTK_EDITABLE(entry)->ic);
- GTK_EDITABLE(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);
- if (entry->char_pos)
- g_free (entry->char_pos);
- if (entry->char_offset)
- g_free (entry->char_offset);
entry->text = NULL;
- if (entry->backing_pixmap)
- gdk_pixmap_unref (entry->backing_pixmap);
-
- (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
{
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 + (widget->allocation.height -
- widget->requisition.height) / 2;
+ requisition.height) / 2;
attributes.width = widget->allocation.width;
- attributes.height = widget->requisition.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 (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->requisition.height - attributes.y * 2;
- attributes.cursor = entry->cursor = gdk_cursor_new (GDK_XTERM);
+ 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);
widget->style = gtk_style_attach (widget->style, widget->window);
- gdk_window_set_background (widget->window, &widget->style->base[GTK_STATE_NORMAL]);
- gdk_window_set_background (entry->text_area, &widget->style->base[GTK_STATE_NORMAL]);
-
-#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;
- editable->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:
- editable->ic = gdk_ic_new (entry->text_area, entry->text_area,
- style, NULL);
- }
-
- if (editable->ic == NULL)
- g_warning ("Can't create input context.");
- else
- {
- GdkColormap *colormap;
-
- mask = gdk_window_get_events (entry->text_area);
- mask |= gdk_ic_get_events (editable->ic);
- gdk_window_set_events (entry->text_area, mask);
-
- if ((colormap = gtk_widget_get_colormap (widget)) !=
- gtk_widget_get_default_colormap ())
- {
- gdk_ic_set_attr (editable->ic, "preeditAttributes",
- "colorMap", GDK_COLORMAP_XCOLORMAP (colormap),
- NULL);
- }
- gdk_ic_set_attr (editable->ic,"preeditAttributes",
- "foreground", widget->style->fg[GTK_STATE_NORMAL].pixel,
- "background", widget->style->base[GTK_STATE_NORMAL].pixel,
- NULL);
- }
- }
-#endif
+ 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)]);
gdk_window_show (entry->text_area);
- if (editable->selection_start_pos != editable->selection_end_pos)
- gtk_editable_claim_selection (editable, TRUE, GDK_CURRENT_TIME);
+ gtk_im_context_set_client_window (entry->im_context, entry->text_area);
- gtk_entry_recompute_offsets (entry);
+ gtk_entry_adjust_scroll (entry);
}
static void
entry = GTK_ENTRY (widget);
+ 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;
- gdk_cursor_destroy (entry->cursor);
- entry->cursor = NULL;
}
+ if (entry->popup_menu)
+ gtk_widget_destroy (entry->popup_menu);
+
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}
-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->base_gc[GTK_WIDGET_STATE(widget)],
- 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 (GTK_EDITABLE (widget)->editable)
- gtk_entry_draw_cursor (GTK_ENTRY (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
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);
+ widget->style->xthickness,
+ widget->style->ythickness,
+ allocation->width - widget->style->xthickness * 2,
+ requisition.height - widget->style->ythickness * 2);
- /* And make sure the cursor is on screen */
- gtk_entry_adjust_scroll (entry);
-
-#ifdef USE_XIM
- if (editable->ic && (gdk_ic_get_style (editable->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 (editable->ic, "preeditAttributes", "area", &rect, NULL);
- }
-#endif
+ 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);
+
+ 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);
+ }
}
}
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;
- GtkEditable *editable;
+ 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 (entry->button && (event->button != entry->button))
+ 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);
-
- tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
- /* Set it now, so we display things right. We'll unset it
- * later if things don't work out */
- editable->has_selection = TRUE;
- gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
- editable->current_pos = editable->selection_start_pos;
+ gtk_entry_reset_im_context (entry);
+
+ entry->current_pos = tmp_pos;
+ entry->selection_bound = tmp_pos;
+
+ gtk_entry_recompute (entry);
+
break;
case GDK_2BUTTON_PRESS:
- gtk_select_word (entry, event->time);
+ gtk_entry_select_word (entry);
break;
case GDK_3BUTTON_PRESS:
- gtk_select_line (entry, event->time);
+ 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) && editable->editable)
- {
- if (editable->selection_start_pos == editable->selection_end_pos ||
- editable->has_selection)
- editable->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_entry_set_selection (editable, tmp_pos, tmp_pos);
- editable->has_selection = FALSE;
- editable->current_pos = editable->selection_start_pos;
+ 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 */
- if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
- gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time);
- }
+ return TRUE;
}
return FALSE;
gtk_entry_button_release (GtkWidget *widget,
GdkEventButton *event)
{
- GtkEntry *entry;
- GtkEditable *editable;
+ GtkEntry *entry = GTK_ENTRY (widget);
+ GtkEditable *editable = GTK_EDITABLE (widget);
- 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);
- editable = GTK_EDITABLE (widget);
-
- if (entry->button != event->button)
+ if (event->window != entry->text_area || entry->button != event->button)
return FALSE;
entry->button = 0;
- if (event->button == 1)
- {
- gtk_grab_remove (widget);
-
- editable->has_selection = FALSE;
- if (editable->selection_start_pos != editable->selection_end_pos)
- {
- if (gtk_selection_owner_set (widget,
- GDK_SELECTION_PRIMARY,
- event->time))
- editable->has_selection = TRUE;
- else
- 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);
- }
-
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);
-
- entry = GTK_ENTRY (widget);
+ GtkEntry *entry = GTK_ENTRY (widget);
+ gint tmp_pos;
- if (entry->button == 0)
+ 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);
- GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_position (entry, x + entry->scroll_offset);
- GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(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;
- GtkEditable *editable;
-
- gint return_val;
- gint key;
- guint initial_pos;
- gint extend_selection;
- gint extend_start;
-
- 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);
- editable = GTK_EDITABLE (widget);
- return_val = FALSE;
+ GtkEntry *entry = GTK_ENTRY (widget);
- if(editable->editable == FALSE)
+ if (!entry->editable)
return FALSE;
- initial_pos = editable->current_pos;
-
- extend_selection = event->state & GDK_SHIFT_MASK;
- extend_start = FALSE;
-
- if (extend_selection)
- {
- if (editable->selection_start_pos == editable->selection_end_pos)
- {
- editable->selection_start_pos = editable->current_pos;
- editable->selection_end_pos = editable->current_pos;
- }
-
- extend_start = (editable->current_pos == editable->selection_start_pos);
- }
-
- switch (event->keyval)
+ if (gtk_im_context_filter_keypress (entry->im_context, event))
{
- case GDK_BackSpace:
- return_val = TRUE;
- 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)
- {
- extend_selection = FALSE;
- gtk_editable_paste_clipboard (editable);
- }
- else if (event->state & GDK_CONTROL_MASK)
- {
- gtk_editable_copy_clipboard (editable);
- }
- else
- {
- /* gtk_toggle_insert(entry) -- IMPLEMENT */
- }
- break;
- case GDK_Delete:
- return_val = TRUE;
- if (event->state & GDK_CONTROL_MASK)
- gtk_delete_forward_word (entry);
- else if (event->state & GDK_SHIFT_MASK)
- {
- extend_selection = FALSE;
- gtk_editable_cut_clipboard (editable);
- }
- else
- gtk_delete_forward_character (entry);
- break;
- case GDK_Home:
- return_val = TRUE;
- gtk_move_beginning_of_line (entry);
- break;
- case GDK_End:
- return_val = TRUE;
- gtk_move_end_of_line (entry);
- break;
- case GDK_Left:
- return_val = TRUE;
- if (event->state & GDK_CONTROL_MASK)
- gtk_move_backward_word (entry);
- else
- gtk_move_backward_character (entry);
- break;
- case GDK_Right:
- return_val = TRUE;
- if (event->state & GDK_CONTROL_MASK)
- gtk_move_forward_word (entry);
- else
- gtk_move_forward_character (entry);
- break;
- case GDK_Return:
- return_val = TRUE;
- gtk_signal_emit_by_name (GTK_OBJECT (entry), "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']) (editable, event->time);
- 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']) (editable, event->time);
- return_val = TRUE;
- }
- break;
- }
- }
- if (event->length > 0)
- {
- gint tmp_pos;
-
- extend_selection = FALSE;
- gtk_editable_delete_selection (editable);
-
- tmp_pos = editable->current_pos;
- gtk_editable_insert_text (editable, event->string, event->length, &tmp_pos);
- editable->current_pos = tmp_pos;
-
- return_val = TRUE;
- }
- break;
+ entry->need_im_reset = TRUE;
+ return TRUE;
}
-
- if (return_val && (editable->current_pos != initial_pos))
+ else if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+ /* Activate key bindings
+ */
+ return TRUE;
+ else if (event->keyval == GDK_Return)
{
- if (extend_selection)
- {
- if (editable->current_pos < editable->selection_start_pos)
- editable->selection_start_pos = editable->current_pos;
- else if (editable->current_pos > editable->selection_end_pos)
- editable->selection_end_pos = editable->current_pos;
- else
- {
- if (extend_start)
- editable->selection_start_pos = editable->current_pos;
- else
- editable->selection_end_pos = editable->current_pos;
- }
- }
- else
- {
- editable->selection_start_pos = 0;
- editable->selection_end_pos = 0;
- }
-
- gtk_editable_claim_selection (editable,
- editable->selection_start_pos != editable->selection_end_pos,
- event->time);
-
- 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_EDITABLE(widget)->ic)
- gdk_im_begin (GTK_EDITABLE(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 void
-gtk_entry_make_backing_pixmap (GtkEntry *entry, gint width, gint height)
+static void
+gtk_entry_direction_changed (GtkWidget *widget,
+ GtkTextDirection previous_dir)
{
- gint pixmap_width, pixmap_height;
+ GtkEntry *entry = GTK_ENTRY (widget);
- if (!entry->backing_pixmap)
- {
- /* allocate */
- entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
- width, height,
- -1);
- }
- else
- {
- /* reallocate if sizes don't match */
- gdk_window_get_size (entry->backing_pixmap,
- &pixmap_width, &pixmap_height);
- if ((pixmap_width != width) || (pixmap_height != height))
- {
- gdk_pixmap_unref (entry->backing_pixmap);
- entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
- width, height,
- -1);
- }
- }
+ gtk_entry_recompute (entry);
+
+ GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
}
static void
-gtk_entry_draw_text (GtkEntry *entry)
+gtk_entry_state_changed (GtkWidget *widget,
+ GtkStateType previous_state)
{
- GtkWidget *widget;
- GtkEditable *editable;
- GtkStateType selected_state;
- gint start_char;
- gint start_pos;
- gint end_pos;
- gint end_char;
- gint start_xoffset;
- gint selection_start_char;
- gint selection_end_char;
- gint selection_start_pos;
- gint selection_end_pos;
- gint selection_start_xoffset;
- gint selection_end_xoffset;
- gint width, height;
- gint y;
- GdkDrawable *drawable;
- gint use_backing_pixmap;
- gchar *stars;
- gchar *toprint;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (widget));
- if (entry->timer)
+ if (GTK_WIDGET_REALIZED (widget))
{
- gtk_timeout_remove (entry->timer);
- entry->timer = 0;
+ 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)]);
}
- if (GTK_WIDGET_DRAWABLE (entry))
- {
- widget = GTK_WIDGET (entry);
- editable = GTK_EDITABLE (entry);
-
- if (!entry->text)
- {
- gdk_window_clear (entry->text_area);
- if (editable->editable)
- gtk_entry_draw_cursor (entry);
- return;
- }
-
- gdk_window_get_size (entry->text_area, &width, &height);
-
- /*
- If the widget has focus, draw on a backing pixmap to avoid flickering
- and copy it to the text_area.
- Otherwise draw to text_area directly for better speed.
- */
- use_backing_pixmap = GTK_WIDGET_HAS_FOCUS (widget) && (entry->text != NULL);
- if (use_backing_pixmap)
- {
- gtk_entry_make_backing_pixmap (entry, width, height);
- drawable = entry->backing_pixmap;
- gdk_draw_rectangle (drawable,
- widget->style->base_gc[GTK_WIDGET_STATE(widget)],
- TRUE,
- 0, 0,
- width,
- height);
- }
- else
- {
- drawable = entry->text_area;
- gdk_window_clear (entry->text_area);
- }
-
- y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2;
- y += widget->style->font->ascent;
-
- start_char = gtk_entry_find_position (entry, entry->scroll_offset);
- start_pos = entry->char_pos[start_char];
- start_xoffset = entry->char_offset[start_char] - entry->scroll_offset;
+ gtk_widget_queue_clear (widget);
+}
- end_char = gtk_entry_find_position (entry, entry->scroll_offset + width);
- end_pos = entry->char_pos[end_char];
- if (end_pos < entry->text_length)
- end_pos += 1;
+/* GtkEditable method implementations
+ */
+static void
+gtk_entry_insert_text (GtkEditable *editable,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position)
+{
+ GtkEntry *entry = GTK_ENTRY (editable);
+ gchar buf[64];
+ gchar *text;
- selected_state = GTK_STATE_SELECTED;
- if (!editable->has_selection)
- selected_state = GTK_STATE_ACTIVE;
+ 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
+ text = g_new (gchar, new_text_length + 1);
- selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos);
- selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos);
-
- selection_start_pos = CLAMP (selection_start_pos, start_pos, end_pos);
- selection_end_pos = CLAMP (selection_end_pos, start_pos, end_pos);
+ text[new_text_length] = '\0';
+ strncpy (text, new_text, new_text_length);
- selection_start_char = gtk_entry_find_char(entry,selection_start_pos);
- selection_end_char = gtk_entry_find_char(entry,selection_end_pos);
+ gtk_signal_emit (GTK_OBJECT (editable), signals[INSERT_TEXT], text, new_text_length, position);
+ gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
- selection_start_xoffset =
- entry->char_offset[selection_start_char] - entry->scroll_offset;
- selection_end_xoffset =
- entry->char_offset[selection_end_char] -entry->scroll_offset;
+ if (new_text_length > 63)
+ g_free (text);
- /* if entry->visible, print a bunch of stars. If not, print the standard text. */
- if (entry->visible)
- {
- toprint = entry->text + start_pos;
- }
- else
- {
- gint i;
-
- stars = g_malloc (end_char - start_char);
- for (i = 0; i < end_char - start_char; i++)
- stars[i] = '*';
- toprint = stars;
-
- /* Since '*' is always one byte, work in bytes */
- start_pos = start_char;
- selection_start_pos = selection_start_char;
- selection_end_pos = selection_end_char;
- end_pos = end_char;
- }
-
- if (selection_start_pos > start_pos)
- gdk_draw_text (drawable, widget->style->font,
- widget->style->fg_gc[GTK_STATE_NORMAL],
- start_xoffset, y,
- toprint,
- selection_start_pos - start_pos);
-
- if ((selection_end_pos >= start_pos) &&
- (selection_start_pos < end_pos) &&
- (selection_start_pos != selection_end_pos))
- {
- gdk_draw_rectangle (drawable,
- widget->style->bg_gc[selected_state],
- TRUE,
- selection_start_xoffset,
- 0,
- selection_end_xoffset - selection_start_xoffset,
- -1);
-
- gdk_draw_text (drawable, widget->style->font,
- widget->style->fg_gc[selected_state],
- selection_start_xoffset, y,
- toprint + selection_start_pos - start_pos,
- selection_end_pos - selection_start_pos);
- }
-
- if (selection_end_pos < end_pos)
- gdk_draw_text (drawable, widget->style->font,
- widget->style->fg_gc[GTK_STATE_NORMAL],
- selection_end_xoffset, y,
- toprint + selection_end_pos - start_pos,
- end_pos - selection_end_pos);
-
- /* free the space allocated for the stars if it's neccessary. */
- if (!entry->visible)
- g_free (toprint);
-
- if (editable->editable)
- gtk_entry_draw_cursor_on_drawable (entry, drawable);
-
- if (use_backing_pixmap)
- gdk_draw_pixmap(entry->text_area,
- widget->style->fg_gc[GTK_STATE_NORMAL],
- entry->backing_pixmap,
- 0, 0, 0, 0, width, height);
- }
+ g_object_unref (G_OBJECT (editable));
}
static void
-gtk_entry_draw_cursor (GtkEntry *entry)
+gtk_entry_delete_text (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos)
{
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ 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));
+
+ gtk_signal_emit (GTK_OBJECT (editable), signals[DELETE_TEXT], start_pos, end_pos);
+ gtk_signal_emit (GTK_OBJECT (editable), signals[CHANGED]);
- gtk_entry_draw_cursor_on_drawable (entry, entry->text_area);
+ g_object_unref (G_OBJECT (editable));
}
-static void
-gtk_entry_draw_cursor_on_drawable (GtkEntry *entry, GdkDrawable *drawable)
+static gchar *
+gtk_entry_get_chars (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos)
{
- GtkWidget *widget;
- GtkEditable *editable;
- GdkGC *gc;
- gint xoffset;
- gint text_area_height;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ 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);
- editable = GTK_EDITABLE (entry);
+ entry = GTK_ENTRY (editable);
- xoffset = entry->char_offset[gtk_entry_find_char (entry, editable->current_pos)];
- xoffset -= entry->scroll_offset;
+ if (end_pos < 0)
+ end_pos = entry->text_length;
- if (GTK_WIDGET_HAS_FOCUS (widget) &&
- (editable->selection_start_pos == editable->selection_end_pos))
- gc = widget->style->fg_gc[GTK_STATE_NORMAL];
- else
- gc = widget->style->base_gc[GTK_WIDGET_STATE(widget)];
+ start_pos = MIN (entry->text_length, start_pos);
+ end_pos = MIN (entry->text_length, end_pos);
- gdk_window_get_size (entry->text_area, NULL, &text_area_height);
- gdk_draw_line (drawable, gc, xoffset, 0, xoffset, text_area_height);
-#ifdef USE_XIM
- if (gdk_im_ready() && editable->ic &&
- gdk_ic_get_style (editable->ic) & GdkIMPreeditPosition)
- {
- GdkPoint spot;
+ 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;
- spot.x = xoffset;
- spot.y = (text_area_height + (widget->style->font->ascent - widget->style->font->descent) + 1) / 2;
- gdk_ic_set_attr (editable->ic, "preeditAttributes", "spotLocation", &spot, NULL);
- }
-#endif
- }
+ return g_strndup (entry->text + start_index, end_index - start_index);
}
static void
-gtk_entry_queue_draw (GtkEntry *entry)
+gtk_entry_real_set_position (GtkEditable *editable,
+ gint position)
{
- 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 (position != entry->current_pos)
+ {
+ gtk_entry_reset_im_context (entry);
- if (!entry->timer)
- entry->timer = gtk_timeout_add (DRAW_TIMEOUT, gtk_entry_timer, entry);
+ entry->current_pos = entry->selection_bound = position;
+ gtk_entry_recompute (entry);
+ }
}
static gint
-gtk_entry_timer (gpointer data)
+gtk_entry_get_position (GtkEditable *editable)
{
- GtkEntry *entry;
-
- g_return_val_if_fail (data != NULL, FALSE);
-
- entry = GTK_ENTRY (data);
- entry->timer = 0;
- gtk_entry_draw_text (entry);
-
- return FALSE;
+ return GTK_ENTRY (editable)->current_pos;
}
-static gint
-gtk_entry_find_position (GtkEntry *entry,
- gint x)
+static void
+gtk_entry_set_selection_bounds (GtkEditable *editable,
+ gint start,
+ gint end)
{
- gint start = 0;
- gint end = entry->nchars;
- gint half;
+ GtkEntry *entry = GTK_ENTRY (editable);
- if (x <= 0)
- return 0;
- if (x >= entry->char_offset[end])
- return end;
+ if (start < 0)
+ start = entry->text_length;
+ if (end < 0)
+ end = entry->text_length;
- /* invariant - char_offset[start] <= x < char_offset[end] */
+ gtk_entry_reset_im_context (entry);
- while (start != end)
- {
- half = (start+end)/2;
- if (half == start)
- return half;
- else if (entry->char_offset[half] <= x)
- start = half;
- else
- end = half;
- }
+ entry->selection_bound = MIN (start, entry->text_length);
+ entry->current_pos = MIN (end, entry->text_length);
- return start;
+ 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)
{
- return entry->char_pos[gtk_entry_find_position(entry, x)];
+ GtkEntry *entry = GTK_ENTRY (editable);
+
+ *start = entry->selection_bound;
+ *end = entry->current_pos;
+
+ return (entry->selection_bound != entry->current_pos);
}
-void
-gtk_entry_adjust_scroll (GtkEntry *entry)
+static void
+gtk_entry_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
{
- gint xoffset, max_offset;
- gint text_area_width;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ GtkEntry *entry = GTK_ENTRY (widget);
- if (!entry->text_area)
- return;
+ if (previous_style && GTK_WIDGET_REALIZED (widget))
+ {
+ gtk_entry_recompute (entry);
- gdk_window_get_size (entry->text_area, &text_area_width, NULL);
+ 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)]);
+ }
+}
- /* Display as much text as we can */
- max_offset = MAX(0, entry->char_offset[entry->nchars] - text_area_width);
+/* Default signal handlers
+ */
+static void
+gtk_entry_real_insert_text (GtkEntry *entry,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position)
+{
+ gint index;
+ gint n_chars;
- if (entry->scroll_offset > max_offset)
- entry->scroll_offset = max_offset;
+ if (new_text_length < 0)
+ new_text_length = strlen (new_text);
- /* And make sure cursor is on screen */
- xoffset = entry->char_offset[gtk_entry_find_char (entry, GTK_EDITABLE(entry)->current_pos)];
- xoffset -= entry->scroll_offset;
+ 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;
+ }
- if (xoffset < 0)
- entry->scroll_offset += xoffset;
+ 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;
+ }
+ }
+ }
+
+ 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);
- else if (xoffset > text_area_width)
- entry->scroll_offset += xoffset - text_area_width + 1;
+ 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;
+
+ 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_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ 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;
- previous_size = entry->text_size;
- if (!entry->text_size)
- entry->text_size = 128;
- else
- entry->text_size *= 2;
- entry->text = g_realloc (entry->text, entry->text_size);
- entry->char_pos = g_realloc (entry->char_pos,
- entry->text_size * sizeof(guint16));
- entry->char_offset = g_realloc (entry->char_offset,
- entry->text_size * sizeof(guint));
-
- if (entry->text_length == 0) /* initial allocation */
- {
- entry->char_pos[0] = 0;
- entry->char_offset[0] = 0;
+ if (entry->selection_bound > start_pos)
+ entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
}
- for (i = previous_size; i < entry->text_size; i++)
- entry->text[i] = '\0';
+ /* We might have deleted the selection
+ */
+ gtk_entry_update_primary_selection (entry);
+
+ gtk_entry_recompute (entry);
}
+
static void
-gtk_entry_insert_text (GtkEditable *editable,
- const gchar *new_text,
- gint new_text_length,
- gint *position)
+gtk_entry_move_cursor (GtkEntry *entry,
+ GtkMovementStep step,
+ gint count,
+ gboolean extend_selection)
{
- gchar *text;
- gint start_char;
- gint end_char;
- gint start_pos;
- gint last_char;
- gint end_pos;
- gint last_pos;
- gint max_length;
- gint i;
+ gint new_pos = entry->current_pos;
- gint insertion_chars;
- guint16 *insertion_pos = NULL; /* Quiet the compiler */
-
- GtkEntry *entry;
+ gtk_entry_reset_im_context (entry);
- g_return_if_fail (editable != NULL);
- g_return_if_fail (GTK_IS_ENTRY (editable));
-
- entry = GTK_ENTRY (editable);
+ 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;
+ }
- if (new_text_length < 0)
- new_text_length = strlen (new_text);
-
- /* The algorithms here will work as long as, the text size (a
- * multiple of 2), fits into a guint16 but we specify a shorter
- * maximum length so that if the user pastes a very long text, there
- * is not a long hang from the slow X_LOCALE functions. */
-
- if (entry->text_max_length == 0)
- max_length = 2047;
+ if (extend_selection)
+ gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
else
- max_length = MIN (2047, entry->text_max_length);
+ gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
+}
+
+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);
- /* Make sure we do not exceed the maximum size of the entry. */
- if (new_text_length + entry->text_length > max_length)
- new_text_length = max_length - entry->text_length;
+ gtk_editable_insert_text (editable, str, -1, &pos);
+ gtk_editable_set_position (editable, pos);
+}
+
+static void
+gtk_entry_delete_from_cursor (GtkEntry *entry,
+ GtkDeleteType type,
+ gint count)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint start_pos = entry->current_pos;
+ gint end_pos = entry->current_pos;
+
+ gtk_entry_reset_im_context (entry);
- /* Don't insert anything, if there was nothing to insert. */
- if (new_text_length <= 0)
+ if (!entry->editable)
return;
- /* Find the length of the inserted text in characters, chop off
- partial/invalid characters */
- if (gtk_use_mb)
+ if (entry->selection_bound != entry->current_pos)
{
- gint len = 0;
-
- insertion_pos = g_new (guint16, new_text_length+1);
- insertion_chars = 0;
-
- for (i=0; i<new_text_length; i+=len)
+ 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)
{
- len = mblen (&new_text[i], MIN(MB_CUR_MAX,new_text_length-i));
- if (len < 0)
- break;
- insertion_pos[insertion_chars] = i;
- insertion_chars++;
+ end_pos = gtk_entry_move_forward_word (entry, end_pos);
+ count--;
}
- insertion_pos[insertion_chars] = i;
+ 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;
+ }
+}
- new_text_length = i;
+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);
}
- else
- insertion_chars = new_text_length;
+}
+
+static void
+gtk_entry_cut_clipboard (GtkEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint start, end;
+
+ 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_paste_clipboard (GtkEntry *entry)
+{
+ gtk_entry_paste (entry, GDK_NONE);
+}
+
+static void
+gtk_entry_toggle_overwrite (GtkEntry *entry)
+{
+ entry->overwrite_mode = !entry->overwrite_mode;
+}
- /* Make sure we are inserting at integral character position */
- start_char = gtk_entry_find_char (entry, *position);
- start_pos = entry->char_pos[start_char];
+/* IM Context Callbacks
+ */
+
+static void
+gtk_entry_commit_cb (GtkIMContext *context,
+ const gchar *str,
+ GtkEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint tmp_pos = entry->current_pos;
+
+ gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
+ gtk_editable_set_position (editable, tmp_pos);
+}
+
+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);
- end_pos = start_pos + new_text_length;
- last_pos = new_text_length + entry->text_length;
+ gtk_entry_recompute (entry);
+}
+
+/* Internal functions
+ */
- if (editable->selection_start_pos >= *position)
- editable->selection_start_pos += new_text_length;
- if (editable->selection_end_pos >= *position)
- editable->selection_end_pos += new_text_length;
+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->recompute_idle = FALSE;
+
+ return FALSE;
+}
+
+static void
+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)
+{
+ 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 (gtk_use_mb)
+ if (include_preedit)
{
- /* Fix up the character positions */
+ gtk_im_context_get_preedit_string (entry->im_context,
+ &preedit_string, &preedit_attrs, NULL);
+ preedit_length = entry->preedit_length;
+ }
- end_char = start_char + insertion_chars;
- last_char = entry->nchars + insertion_chars;
+ if (preedit_length)
+ {
+ GString *tmp_string = g_string_new (NULL);
- for (i = last_char; i >= end_char; i--)
- entry->char_pos[i]
- = entry->char_pos[i - insertion_chars] + new_text_length;
+ gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
- for (i = 1; i < insertion_chars ; i++)
- entry->char_pos[start_char+i] =
- entry->char_pos[start_char] + insertion_pos[i];
-
- g_free (insertion_pos);
+ 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
{
- end_char = end_pos;
- last_char = last_pos;
+ 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);
- for (i = start_char ; i <= last_char ; i++)
- entry->char_pos[i] = i;
+ 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;
+}
- /* Fix up the the character offsets */
+static void
+gtk_entry_draw_text (GtkEntry *entry)
+{
+ GtkWidget *widget;
+ PangoLayoutLine *line;
- if (GTK_WIDGET_REALIZED (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))
{
- gint offset = 0;
+ 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);
+
+ 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);
- for (i = last_char; i >= end_char; i--)
- entry->char_offset[i]
- = entry->char_offset[i - insertion_chars];
+ 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;
- for (i=start_char; i<end_char; i++)
+ 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))
{
- entry->char_offset[i] = entry->char_offset[start_char] + offset;
- if (entry->visible)
- {
- offset += gdk_text_width (GTK_WIDGET (entry)->style->font,
- entry->text + entry->char_pos[i],
- entry->char_pos[i+1] - entry->char_pos[i]);
- }
- else
+ 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++)
{
- offset += gdk_text_width (GTK_WIDGET (entry)->style->font, "*", 1);
+ 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 = end_char ; i <= last_char ; i++)
- entry->char_offset[i] += offset;
- }
- entry->text_length += new_text_length;
- entry->nchars += insertion_chars;
- *position = end_pos;
+ 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);
+ }
- gtk_entry_queue_draw (entry);
+ g_object_unref (G_OBJECT (layout));
+ }
}
-/* Recompute the x offsets of all characters in the buffer */
static void
-gtk_entry_recompute_offsets (GtkEntry *entry)
+gtk_entry_draw_cursor (GtkEntry *entry)
{
- gint i;
- gint offset = 0;
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
- for (i=0; i<entry->nchars; i++)
+ if (!entry->visible && entry->invisible_char == 0)
+ return;
+
+ if (GTK_WIDGET_DRAWABLE (entry))
{
- entry->char_offset[i] = offset;
- if (entry->visible)
- {
- offset += gdk_text_width (GTK_WIDGET (entry)->style->font,
- entry->text + entry->char_pos[i],
- entry->char_pos[i+1] - entry->char_pos[i]);
- }
- else
+ GtkWidget *widget = GTK_WIDGET (entry);
+
+ if (GTK_WIDGET_HAS_FOCUS (widget) &&
+ (entry->selection_bound == entry->current_pos))
{
- offset += gdk_text_width (GTK_WIDGET (entry)->style->font, "*", 1);
+ 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_entry_queue_draw (GtkEntry *entry)
+{
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ if (GTK_WIDGET_REALIZED (entry))
+ {
+ GdkRectangle rect = { 0 };
+
+ gdk_window_get_size (entry->text_area, &rect.width, &rect.height);
+ gdk_window_invalidate_rect (entry->text_area, &rect, FALSE);
+ }
+}
- entry->char_offset[i] = offset;
+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);
+ }
}
-/* Given a position in the entry, find the character index of the
- * last character with position <= the given position
- */
static gint
-gtk_entry_find_char (GtkEntry *entry, gint position)
+gtk_entry_find_position (GtkEntry *entry,
+ gint x)
{
- gint start = 0;
- gint end = entry->nchars;
- gint half;
-
- if (position >= entry->char_pos[end])
- return end;
- if (position < 0)
- return 0;
+ 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);
- /* invariant - char_pos[start] <= position < char_pos[end] */
+ g_object_unref (G_OBJECT (layout));
- while (start != end)
+ if (index >= cursor_index && entry->preedit_length)
{
- half = (start+end)/2;
- if (half == start)
- return half;
- else if (entry->char_pos[half] <= position)
- start = half;
+ if (index >= cursor_index + entry->preedit_length)
+ index -= entry->preedit_length;
else
- end = half;
+ {
+ index = cursor_index;
+ trailing = 0;
+ }
}
- return start;
+ pos = g_utf8_pointer_to_offset (entry->text, entry->text + index);
+
+ if (trailing)
+ pos += 1;
+
+ return pos;
}
static void
-gtk_entry_delete_text (GtkEditable *editable,
- gint start_pos,
- gint end_pos)
-{
- gchar *text;
- gint deletion_length;
- gint start_char;
- gint end_char;
- gint i;
+gtk_entry_get_cursor_locations (GtkEntry *entry,
+ gint *strong_x,
+ gint *weak_x)
+{
+ PangoLayout *layout = gtk_entry_get_layout (entry, TRUE);
+ const gchar *text;
+ PangoRectangle strong_pos, weak_pos;
+ gint index;
+
+ text = pango_layout_get_text (layout);
- GtkEntry *entry;
+ index =
+ g_utf8_offset_to_pointer (text,
+ entry->current_pos +
+ entry->preedit_cursor) - text;
- g_return_if_fail (editable != NULL);
- g_return_if_fail (GTK_IS_ENTRY (editable));
+ pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
+ g_object_unref (G_OBJECT (layout));
- entry = GTK_ENTRY (editable);
+ if (strong_x)
+ *strong_x = strong_pos.x / PANGO_SCALE;
- if (end_pos < 0)
- end_pos = entry->text_length;
+ if (weak_x)
+ *weak_x = weak_pos.x / PANGO_SCALE;
+}
- start_char = gtk_entry_find_char (entry, start_pos);
- end_char = gtk_entry_find_char (entry, end_pos);
- start_pos = entry->char_pos[start_char];
- end_pos = entry->char_pos[end_char];
+static void
+gtk_entry_adjust_scroll (GtkEntry *entry)
+{
+ 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 (editable->selection_start_pos > start_pos)
- editable->selection_start_pos -= MIN(end_pos, editable->selection_start_pos) - start_pos;
- if (editable->selection_end_pos > start_pos)
- editable->selection_end_pos -= MIN(end_pos, editable->selection_end_pos) - start_pos;
-
- if ((start_pos < end_pos) &&
- (start_pos >= 0) &&
- (end_pos <= entry->text_length))
- {
- text = entry->text;
- deletion_length = end_pos - start_pos;
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
- /* Fix up the character offsets */
- if (GTK_WIDGET_REALIZED (entry))
- {
- gint deletion_width =
- entry->char_offset[end_char] - entry->char_offset[start_char];
+ widget = GTK_WIDGET (entry);
- for (i = 0 ; i <= entry->nchars - end_char; i++)
- entry->char_offset[start_char+i] = entry->char_offset[end_char+i] - deletion_width;
- }
+ if (!GTK_WIDGET_REALIZED (entry))
+ return;
+
+ gdk_window_get_size (entry->text_area, &text_area_width, NULL);
+ text_area_width -= 2 * INNER_BORDER;
- for (i = end_pos; i < entry->text_length; i++)
- text[i - deletion_length] = text[i];
+ layout = gtk_entry_get_layout (entry, TRUE);
+ line = pango_layout_get_lines (layout)->data;
- for (i = entry->text_length - deletion_length; i < entry->text_length; i++)
- text[i] = '\0';
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
+ g_object_unref (G_OBJECT (layout));
- for (i = 0 ; i <= entry->nchars - end_char; i++)
- entry->char_pos[start_char+i] = entry->char_pos[end_char+i] - deletion_length;
+ /* Display as much text as we can */
- entry->nchars -= end_char - start_char;
-
- entry->text_length -= deletion_length;
- editable->current_pos = start_pos;
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ {
+ 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);
}
- gtk_entry_queue_draw (entry);
-}
-
-static void
-gtk_entry_update_text (GtkEditable *editable,
- gint start_pos,
- gint end_pos)
-{
- gtk_entry_queue_draw (GTK_ENTRY(editable));
-}
-
-static gchar *
-gtk_entry_get_chars (GtkEditable *editable,
- gint start_pos,
- gint end_pos)
-{
- gchar *retval;
- GtkEntry *entry;
- gchar c;
+ 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);
- g_return_val_if_fail (editable != NULL, NULL);
- g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
+ strong_xoffset = strong_x - entry->scroll_offset;
- entry = GTK_ENTRY (editable);
-
- if (end_pos < 0)
- end_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;
+ }
- start_pos = MIN(entry->text_length, start_pos);
- end_pos = MIN(entry->text_length, end_pos);
+ weak_xoffset = weak_x - entry->scroll_offset;
- if (start_pos <= end_pos)
+ if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
{
- c = entry->text[end_pos];
- entry->text[end_pos] = '\0';
-
- retval = g_strdup (&entry->text[start_pos]);
-
- entry->text[end_pos] = c;
-
- return retval;
+ 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;
}
- else
- return NULL;
}
-static void
-gtk_entry_move_cursor (GtkEditable *editable,
- gint x,
- gint y)
+static gint
+gtk_entry_move_visually (GtkEntry *entry,
+ gint start,
+ gint count)
{
- gint len;
+ gint index;
+ PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
+ const gchar *text;
- GtkEntry *entry;
- entry = GTK_ENTRY (editable);
+ text = pango_layout_get_text (layout);
+
+ index = g_utf8_offset_to_pointer (text, start) - text;
- /* Horizontal motion */
- if (x > 0)
+ while (count != 0)
{
- while (x-- != 0)
+ int new_index, new_trailing;
+
+ if (count > 0)
{
- if (gtk_use_mb)
- {
- if (editable->current_pos < entry->text_length)
- {
- len = mblen (entry->text+editable->current_pos, MB_CUR_MAX);
- editable->current_pos += (len>0)? len:1;
- }
- if (editable->current_pos > entry->text_length)
- editable->current_pos = entry->text_length;
- }
- else
- {
- if (editable->current_pos < entry->text_length)
- editable->current_pos ++;
- }
+ pango_layout_move_cursor_visually (layout, index, 0, 1, &new_index, &new_trailing);
+ count--;
}
- }
- else if (x < 0)
- {
- while (x++ != 0)
+ else
{
- if (0 < editable->current_pos)
- {
- if (gtk_use_mb)
- editable->current_pos =
- entry->char_pos[gtk_entry_find_char (entry, editable->current_pos - 1)];
- else
- editable->current_pos--;
- }
+ pango_layout_move_cursor_visually (layout, index, 0, -1, &new_index, &new_trailing);
+ count++;
}
- }
- /* Ignore vertical motion */
-}
+ 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;
+ }
-static void
-gtk_move_forward_character (GtkEntry *entry)
-{
- gtk_entry_move_cursor (GTK_EDITABLE (entry), 1, 0);
+ g_object_unref (G_OBJECT (layout));
+
+ return g_utf8_pointer_to_offset (text, text + index);
}
-static void
-gtk_move_backward_character (GtkEntry *entry)
+static gint
+gtk_entry_move_forward_word (GtkEntry *entry,
+ gint start)
{
- gtk_entry_move_cursor (GTK_EDITABLE (entry), -1, 0);
-}
+ gint new_pos = start;
-static void
-gtk_entry_move_word (GtkEditable *editable,
- gint n)
-{
- if (n > 0)
+ /* Prevent any leak of information */
+ if (!entry->visible)
{
- while (n-- != 0)
- gtk_move_forward_word (GTK_ENTRY (editable));
+ new_pos = entry->text_length;
}
- else if (n < 0)
+ else if (entry->text && (new_pos < entry->text_length))
{
- while (n++ != 0)
- gtk_move_backward_word (GTK_ENTRY (editable));
+ 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 void
-gtk_move_forward_word (GtkEntry *entry)
-{
- GtkEditable *editable;
- gchar *text;
- gint i;
- wchar_t c;
- gint len;
- editable = GTK_EDITABLE (entry);
+static gint
+gtk_entry_move_backward_word (GtkEntry *entry,
+ gint start)
+{
+ gint new_pos = start;
- if (entry->text && (editable->current_pos < entry->text_length))
+ /* Prevent any leak of information */
+ if (!entry->visible)
{
- text = entry->text;
- i = editable->current_pos;
-
- if (gtk_use_mb)
- {
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (!iswalnum(c))
- for (; i < entry->text_length; i+=len)
- {
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (len < 1 || iswalnum(c))
- break;
- }
-
- for (; i < entry->text_length; i+=len)
- {
- len = mbtowc (&c, text+i, MB_CUR_MAX);
- if (len < 1 || !iswalnum(c))
- break;
- }
-
- editable->current_pos = i;
- if (editable->current_pos > entry->text_length)
- editable->current_pos = entry->text_length;
- }
- else
- {
- if (!isalnum (text[i]))
- for (; i < entry->text_length; i++)
- {
- if (isalnum(text[i]))
- break;
- }
-
- for (; i < entry->text_length; i++)
- {
- if (!isalnum(text[i]))
- break;
- }
-
- editable->current_pos = i;
- }
+ new_pos = 0;
}
-}
-
-static void
-gtk_move_backward_word (GtkEntry *entry)
-{
- GtkEditable *editable;
- gchar *text;
- gint i;
- wchar_t c;
+ else if (entry->text && start > 0)
+ {
+ PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
+ PangoLogAttr *log_attrs;
+ gint n_attrs;
- editable = GTK_EDITABLE (entry);
+ pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
- if (entry->text && editable->current_pos > 0)
- {
- text = entry->text;
+ new_pos = start - 1;
- if (gtk_use_mb)
- {
- i = gtk_entry_find_char (entry, editable->current_pos - 1);
-
- mbtowc (&c, text+entry->char_pos[i], MB_CUR_MAX);
- if (!iswalnum(c))
- for (; i >= 0; i--)
- {
- mbtowc (&c, text+entry->char_pos[i], MB_CUR_MAX);
- if (iswalnum(c))
- break;
- }
-
- for (; i >= 0; i--)
- {
- mbtowc (&c, text+entry->char_pos[i], MB_CUR_MAX);
- if (!iswalnum(c))
- {
- i++;
- break;
- }
- }
+ /* Find the previous word beginning */
+ while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
+ new_pos--;
- if (i < 0)
- i = 0;
-
- editable->current_pos = entry->char_pos[i];
- }
- else
- {
- i = editable->current_pos - 1;
-
- if (!isalnum(text[i]))
- for (; i >= 0; i--)
- {
- if (isalnum(text[i]))
- break;
- }
-
- for (; i >= 0; i--)
- {
- if (!isalnum(text[i]))
- {
- i++;
- break;
- }
- }
-
- if (i < 0)
- i = 0;
-
- editable->current_pos = i;
- }
-
+ g_free (log_attrs);
+ g_object_unref (G_OBJECT (layout));
}
+
+ return new_pos;
}
static void
-gtk_entry_move_to_column (GtkEditable *editable, gint column)
+gtk_entry_delete_whitespace (GtkEntry *entry)
{
- GtkEntry *entry;
+ PangoLayout *layout = gtk_entry_get_layout (entry, FALSE);
+ PangoLogAttr *log_attrs;
+ gint n_attrs;
+ gint start, end;
- entry = GTK_ENTRY (editable);
+ pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
+
+ start = end = entry->current_pos;
- if (column < 0 || column > entry->nchars)
- editable->current_pos = entry->text_length;
- else
- editable->current_pos = entry->char_pos[column];
+ 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_beginning_of_line (GtkEntry *entry)
+gtk_entry_select_word (GtkEntry *entry)
{
- gtk_entry_move_to_column (GTK_EDITABLE (entry), 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_move_end_of_line (GtkEntry *entry)
+gtk_entry_select_line (GtkEntry *entry)
{
- gtk_entry_move_to_column (GTK_EDITABLE (entry), -1);
+ gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
}
-static void
-gtk_entry_kill_char (GtkEditable *editable,
- gint direction)
+/*
+ * 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 (editable->selection_start_pos != editable->selection_end_pos)
- gtk_editable_delete_selection (editable);
+ if (end < 0)
+ end = entry->text_length;
+
+ if (entry->visible)
+ return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
else
{
- gint old_pos = editable->current_pos;
- if (direction >= 0)
- {
- gtk_entry_move_cursor (editable, 1, 0);
- gtk_editable_delete_text (editable, old_pos, editable->current_pos);
- }
- else
- {
- gtk_entry_move_cursor (editable, -1, 0);
- gtk_editable_delete_text (editable, editable->current_pos, old_pos);
- }
+ 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;
}
+
}
static void
-gtk_delete_forward_character (GtkEntry *entry)
+paste_received (GtkClipboard *clipboard,
+ const gchar *text,
+ gpointer data)
{
- gtk_entry_kill_char (GTK_EDITABLE (entry), 1);
+ 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);
+ }
+
+ g_object_unref (G_OBJECT (entry));
}
static void
-gtk_delete_backward_character (GtkEntry *entry)
+gtk_entry_paste (GtkEntry *entry,
+ GdkAtom selection)
{
- gtk_entry_kill_char (GTK_EDITABLE (entry), -1);
+ g_object_ref (G_OBJECT (entry));
+ gtk_clipboard_request_text (gtk_clipboard_get (selection),
+ paste_received, entry);
}
static void
-gtk_entry_kill_word (GtkEditable *editable,
- gint direction)
+primary_get_cb (GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ guint info,
+ gpointer data)
{
- if (editable->selection_start_pos != editable->selection_end_pos)
- gtk_editable_delete_selection (editable);
- else
+ GtkEntry *entry = GTK_ENTRY (data);
+ gint start, end;
+
+ if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
{
- gint old_pos = editable->current_pos;
- if (direction >= 0)
- {
- gtk_entry_move_word (editable, 1);
- gtk_editable_delete_text (editable, old_pos, editable->current_pos);
- }
- else
- {
- gtk_entry_move_word (editable, -1);
- gtk_editable_delete_text (editable, editable->current_pos, old_pos);
- }
+ gchar *str = gtk_entry_get_public_chars (entry, start, end);
+ gtk_selection_data_set_text (selection_data, str);
+ g_free (str);
}
}
static void
-gtk_delete_forward_word (GtkEntry *entry)
+primary_clear_cb (GtkClipboard *clipboard,
+ gpointer data)
{
- gtk_entry_kill_word (GTK_EDITABLE (entry), 1);
-}
+ GtkEntry *entry = GTK_ENTRY (data);
-static void
-gtk_delete_backward_word (GtkEntry *entry)
-{
- gtk_entry_kill_word (GTK_EDITABLE (entry), -1);
+ gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
}
static void
-gtk_entry_kill_line (GtkEditable *editable,
- gint direction)
-{
- gint old_pos = editable->current_pos;
- if (direction >= 0)
+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))
{
- gtk_entry_move_to_column (editable, -1);
- gtk_editable_delete_text (editable, old_pos, editable->current_pos);
+ 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
{
- gtk_entry_move_to_column (editable, 0);
- gtk_editable_delete_text (editable, editable->current_pos, old_pos);
+ if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
+ gtk_clipboard_clear (clipboard);
}
}
-static void
-gtk_delete_line (GtkEntry *entry)
+/* Public API
+ */
+
+GtkWidget*
+gtk_entry_new (void)
{
- gtk_entry_move_to_column (GTK_EDITABLE (entry), 0);
- gtk_entry_kill_line (GTK_EDITABLE (entry), 1);
+ return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY));
}
-static void
-gtk_delete_to_line_end (GtkEntry *entry)
+GtkWidget*
+gtk_entry_new_with_max_length (guint16 max)
{
- gtk_editable_delete_text (GTK_EDITABLE(entry), GTK_EDITABLE(entry)->current_pos, entry->text_length);
+ GtkEntry *entry;
+
+ entry = gtk_type_new (GTK_TYPE_ENTRY);
+ entry->text_max_length = max;
+
+ return GTK_WIDGET (entry);
}
-static void
-gtk_select_word (GtkEntry *entry,
- guint32 time)
+void
+gtk_entry_set_text (GtkEntry *entry,
+ const gchar *text)
{
+ gint tmp_pos;
+
GtkEditable *editable;
- gint start_pos;
- gint end_pos;
+
+ 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);
+}
- gtk_move_backward_word (entry);
- start_pos = editable->current_pos;
+void
+gtk_entry_append_text (GtkEntry *entry,
+ const gchar *text)
+{
+ gint tmp_pos;
- gtk_move_forward_word (entry);
- end_pos = editable->current_pos;
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (text != NULL);
- editable->has_selection = TRUE;
- gtk_entry_set_selection (editable, start_pos, end_pos);
- gtk_editable_claim_selection (editable, start_pos != end_pos, time);
+ tmp_pos = entry->text_length;
+ gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
}
-static void
-gtk_select_line (GtkEntry *entry,
- guint32 time)
+void
+gtk_entry_prepend_text (GtkEntry *entry,
+ const gchar *text)
{
- GtkEditable *editable;
+ gint tmp_pos;
- editable = GTK_EDITABLE (entry);
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+ g_return_if_fail (text != NULL);
+
+ tmp_pos = 0;
+ gtk_editable_insert_text (GTK_EDITABLE(entry), text, -1, &tmp_pos);
+}
+
+void
+gtk_entry_set_position (GtkEntry *entry,
+ gint position)
+{
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ gtk_editable_set_position (GTK_EDITABLE (entry), position);
+}
+
+void
+gtk_entry_set_visibility (GtkEntry *entry,
+ gboolean visible)
+{
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
- editable->has_selection = TRUE;
- gtk_entry_set_selection (editable, 0, entry->text_length);
- gtk_editable_claim_selection (editable, entry->text_length != 0, time);
+ entry->visible = visible ? TRUE : FALSE;
- editable->current_pos = editable->selection_end_pos;
+ gtk_entry_recompute (entry);
}
-static void
-gtk_entry_set_selection (GtkEditable *editable,
- gint start,
- gint end)
+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 (editable != NULL);
- g_return_if_fail (GTK_IS_ENTRY (editable));
+ g_return_if_fail (entry != NULL);
+ g_return_if_fail (GTK_IS_ENTRY (entry));
- if (end < 0)
- end = GTK_ENTRY (editable)->text_length;
-
- editable->selection_start_pos = start;
- editable->selection_end_pos = end;
+ 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_entry_queue_draw (GTK_ENTRY (editable));
+ return entry->text;
}
void
g_return_if_fail (GTK_IS_ENTRY (entry));
if (max && entry->text_length > max)
- gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1);
+ gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
+
entry->text_max_length = max;
}
-static void
-gtk_entry_style_set (GtkWidget *widget,
- GtkStyle *previous_style)
+/* Quick hack of a popup menu
+ */
+static void
+activate_cb (GtkWidget *menuitem,
+ GtkEntry *entry)
{
- GtkEntry *entry;
- gint scroll_char;
+ const gchar *signal = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-signal");
+ gtk_signal_emit_by_name (GTK_OBJECT (entry), signal);
+}
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_ENTRY (widget));
+static void
+append_action_signal (GtkEntry *entry,
+ GtkWidget *menu,
+ const gchar *label,
+ const gchar *signal)
+{
+ GtkWidget *menuitem = gtk_menu_item_new_with_label (label);
- if (previous_style && GTK_WIDGET_REALIZED (widget))
+ 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)
{
- entry = GTK_ENTRY (widget);
-
- scroll_char = gtk_entry_find_position (entry, entry->scroll_offset);
- gtk_entry_recompute_offsets (GTK_ENTRY (widget));
- entry->scroll_offset = entry->char_offset[scroll_char];
- gtk_entry_adjust_scroll (entry);
+ GtkWidget *menuitem;
+
+ entry->popup_menu = gtk_menu_new ();
- gdk_window_set_background (widget->window, &widget->style->base[GTK_STATE_NORMAL]);
- gdk_window_set_background (entry->text_area, &widget->style->base[GTK_STATE_NORMAL]);
+ 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));
}
- if (GTK_WIDGET_DRAWABLE (widget))
- gdk_window_clear (widget->window);
+ gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
+ NULL, NULL,
+ event->button, event->time);
}