diff options
Diffstat (limited to 'gtk/gtkentry.c')
-rw-r--r-- | gtk/gtkentry.c | 1286 |
1 files changed, 486 insertions, 800 deletions
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index 3f9eec91b..1f11a5a0c 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -29,17 +29,26 @@ #include "gdk/gdkkeysyms.h" #include "gdk/gdki18n.h" #include "gtkentry.h" +#include "gtkimmulticontext.h" #include "gtkmain.h" #include "gtkselection.h" #include "gtksignal.h" #include "gtkstyle.h" +#include <pango/pango.h> +#include <unicode.h> +#include <glib-object.h> + #define MIN_ENTRY_WIDTH 150 #define DRAW_TIMEOUT 20 - -/* If you are going to change this, see the note in entry_adjust_scroll */ #define INNER_BORDER 2 +/* Initial size of buffer, in bytes */ +#define MIN_SIZE 16 + +/* Maximum size of text buffer, in bytes */ +#define MAX_SIZE G_MAXUSHORT + enum { ARG_0, ARG_MAX_LENGTH, @@ -47,71 +56,68 @@ enum { }; -static void gtk_entry_class_init (GtkEntryClass *klass); -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); -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_state_changed (GtkWidget *widget, - GtkStateType previous_state); -#ifdef USE_XIM -static void gtk_entry_update_ic_attr (GtkWidget *widget); -#endif -static void gtk_entry_queue_draw (GtkEntry *entry); -static gint gtk_entry_timer (gpointer data); -static gint gtk_entry_position (GtkEntry *entry, - gint x); -static void 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); +static void gtk_entry_class_init (GtkEntryClass *klass); +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); +static void gtk_entry_realize (GtkWidget *widget); +static void gtk_entry_unrealize (GtkWidget *widget); +static void gtk_entry_draw_focus (GtkWidget *widget); +static void gtk_entry_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_entry_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gtk_entry_draw (GtkWidget *widget, + GdkRectangle *area); +static gint gtk_entry_expose (GtkWidget *widget, + GdkEventExpose *event); +static gint gtk_entry_button_press (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_entry_button_release (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_entry_motion_notify (GtkWidget *widget, + GdkEventMotion *event); +static gint gtk_entry_key_press (GtkWidget *widget, + GdkEventKey *event); +static gint gtk_entry_focus_in (GtkWidget *widget, + GdkEventFocus *event); +static gint gtk_entry_focus_out (GtkWidget *widget, + GdkEventFocus *event); +static void gtk_entry_draw_text (GtkEntry *entry); +static void gtk_entry_ensure_layout (GtkEntry *entry); +static void gtk_entry_draw_cursor (GtkEntry *entry); +static void gtk_entry_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void gtk_entry_state_changed (GtkWidget *widget, + GtkStateType previous_state); +static void gtk_entry_queue_draw (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 entry_adjust_scroll (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, @@ -151,12 +157,14 @@ 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_position (GtkEntry *entry, - gint position); static void gtk_entry_set_position_from_editable (GtkEditable *editable, gint position); +static void gtk_entry_commit_cb (GtkIMContext *context, + const gchar *str, + GtkEntry *entry); + + static GtkWidgetClass *parent_class = NULL; static GdkAtom ctext_atom = GDK_NONE; @@ -352,22 +360,26 @@ 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_size = MIN_SIZE; + entry->text = g_malloc (entry->text_size); + entry->text[0] = '\0'; + entry->text_length = 0; entry->text_max_length = 0; + entry->n_bytes = 0; entry->scroll_offset = 0; entry->timer = 0; entry->button = 0; - entry->visible = 1; + entry->ascent = 0; - entry->char_offset = NULL; - entry->text_mb = NULL; - entry->text_mb_dirty = TRUE; - entry->use_wchar = FALSE; - - gtk_entry_grow_text (entry); + /* 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); } GtkWidget* @@ -406,12 +418,6 @@ gtk_entry_set_text (GtkEntry *entry, 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; - - if (GTK_WIDGET_DRAWABLE (entry)) - gtk_entry_draw_text (entry); } void @@ -426,7 +432,6 @@ gtk_entry_append_text (GtkEntry *entry, tmp_pos = entry->text_length; gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos); - GTK_EDITABLE(entry)->current_pos = tmp_pos; } void @@ -441,7 +446,6 @@ gtk_entry_prepend_text (GtkEntry *entry, tmp_pos = 0; gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos); - GTK_EDITABLE(entry)->current_pos = tmp_pos; } void @@ -472,10 +476,9 @@ gtk_entry_set_visibility (GtkEntry *entry, g_return_if_fail (entry != NULL); g_return_if_fail (GTK_IS_ENTRY (entry)); - entry->visible = visible ? TRUE : FALSE; GTK_EDITABLE (entry)->visible = visible ? TRUE : FALSE; - gtk_entry_recompute_offsets (entry); - gtk_widget_queue_draw (GTK_WIDGET (entry)); + + gtk_entry_queue_draw (entry); } void @@ -494,24 +497,7 @@ gtk_entry_get_text (GtkEntry *entry) g_return_val_if_fail (entry != NULL, NULL); g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); - if (!entry->text_mb_dirty) - return entry->text_mb; - - if (entry->text_mb) - g_free(entry->text_mb); - - if (!entry->text) - { - entry->text_mb = g_new(gchar, 1); - entry->text_mb[0] = 0; - } - else - { - entry->text_mb = gtk_entry_get_chars(GTK_EDITABLE(entry), 0, -1); - } - entry->text_mb_dirty = 0; - - return entry->text_mb; + return entry->text; } static void @@ -523,6 +509,8 @@ gtk_entry_finalize (GObject *object) entry = GTK_ENTRY (object); + gtk_object_unref (GTK_OBJECT (entry->im_context)); + if (entry->timer) gtk_timeout_remove (entry->timer); @@ -530,17 +518,8 @@ gtk_entry_finalize (GObject *object) if (entry->text) g_free (entry->text); - if (entry->char_offset) - g_free (entry->char_offset); entry->text = NULL; - if (entry->text_mb) - g_free (entry->text_mb); - entry->text_mb = NULL; - - if (entry->backing_pixmap) - gdk_pixmap_unref (entry->backing_pixmap); - G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -601,82 +580,12 @@ gtk_entry_realize (GtkWidget *widget) 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)]); -#ifdef USE_XIM - if (gdk_im_ready () && (editable->ic_attr = gdk_ic_attr_new ()) != NULL) - { - gint width, height; - GdkEventMask mask; - GdkColormap *colormap; - GdkICAttr *attr = editable->ic_attr; - GdkICAttributesType attrmask = GDK_IC_ALL_REQ; - GdkIMStyle style; - GdkIMStyle supported_style = GDK_IM_PREEDIT_NONE | - GDK_IM_PREEDIT_NOTHING | - GDK_IM_PREEDIT_POSITION | - GDK_IM_STATUS_NONE | - GDK_IM_STATUS_NOTHING; - - if (widget->style && widget->style->font->type != GDK_FONT_FONTSET) - supported_style &= ~GDK_IM_PREEDIT_POSITION; - - attr->style = style = gdk_im_decide_style (supported_style); - attr->client_window = entry->text_area; - - if ((colormap = gtk_widget_get_colormap (widget)) != - gtk_widget_get_default_colormap ()) - { - attrmask |= GDK_IC_PREEDIT_COLORMAP; - attr->preedit_colormap = colormap; - } - attrmask |= GDK_IC_PREEDIT_FOREGROUND; - attrmask |= GDK_IC_PREEDIT_BACKGROUND; - attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL]; - attr->preedit_background = widget->style->base[GTK_STATE_NORMAL]; - - switch (style & GDK_IM_PREEDIT_MASK) - { - case GDK_IM_PREEDIT_POSITION: - 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); - - attrmask |= GDK_IC_PREEDIT_POSITION_REQ; - attr->spot_location.x = 0; - attr->spot_location.y = height; - attr->preedit_area.x = 0; - attr->preedit_area.y = 0; - attr->preedit_area.width = width; - attr->preedit_area.height = height; - attr->preedit_fontset = widget->style->font; - - break; - } - editable->ic = gdk_ic_new (attr, attrmask); - - if (editable->ic == NULL) - g_warning ("Can't create input context."); - else - { - mask = gdk_window_get_events (entry->text_area); - mask |= gdk_ic_get_events (editable->ic); - gdk_window_set_events (entry->text_area, mask); - - if (GTK_WIDGET_HAS_FOCUS(widget)) - gdk_im_begin (editable->ic, entry->text_area); - } - } -#endif - 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_entry_recompute_offsets (entry); + gtk_im_context_set_client_window (entry->im_context, entry->text_area); } static void @@ -689,19 +598,8 @@ gtk_entry_unrealize (GtkWidget *widget) entry = GTK_ENTRY (widget); -#ifdef USE_XIM - if (GTK_EDITABLE (widget)->ic) - { - gdk_ic_destroy (GTK_EDITABLE (widget)->ic); - GTK_EDITABLE (widget)->ic = NULL; - } - if (GTK_EDITABLE (widget)->ic_attr) - { - gdk_ic_attr_destroy (GTK_EDITABLE (widget)->ic_attr); - GTK_EDITABLE (widget)->ic_attr = NULL; - } -#endif - + 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); @@ -750,9 +648,6 @@ gtk_entry_draw_focus (GtkWidget *widget) NULL, widget, "entry", 0, 0, width - 1, height - 1); } - - if (GTK_EDITABLE (widget)->editable) - gtk_entry_draw_cursor (GTK_ENTRY (widget)); } } @@ -760,13 +655,41 @@ 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); + entry = GTK_ENTRY (widget); + + /* We do this to deal with direction changes - should that be a signal? + */ + if (entry->layout) + { + pango_layout_unref (entry->layout); + entry->layout = NULL; + } + + gtk_entry_ensure_layout (entry); + + /* hackish for now, get metrics + */ + font = pango_context_load_font (pango_layout_get_context (entry->layout), + widget->style->font_desc); + lang = pango_context_get_lang (pango_layout_get_context (entry->layout)); + pango_font_get_metrics (font, lang, &metrics); + g_free (lang); + + g_object_unref (G_OBJECT (font)); + + entry->ascent = metrics.ascent; + requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + INNER_BORDER) * 2; - requisition->height = (widget->style->font->ascent + - widget->style->font->descent + + requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE + (widget->style->klass->ythickness + INNER_BORDER) * 2); } @@ -804,23 +727,10 @@ gtk_entry_size_allocate (GtkWidget *widget, allocation->width - widget->style->klass->xthickness * 2, requisition.height - widget->style->klass->ythickness * 2); - /* And make sure the cursor is on screen */ - entry_adjust_scroll (entry); - -#ifdef USE_XIM - if (editable->ic && - (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION)) - { - gint width, height; - - gdk_window_get_size (entry->text_area, &width, &height); - editable->ic_attr->preedit_area.width = width; - editable->ic_attr->preedit_area.height = height; - gdk_ic_set_attr (editable->ic, editable->ic_attr, - GDK_IC_PREEDIT_AREA); - } -#endif } + + /* And make sure the cursor is on screen */ + entry_adjust_scroll (entry); } static void @@ -845,6 +755,7 @@ gtk_entry_draw (GtkWidget *widget, gdk_window_begin_paint_rect (entry->text_area, &tmp_area); gtk_widget_draw_focus (widget); gtk_entry_draw_text (GTK_ENTRY (widget)); + gtk_entry_draw_cursor (GTK_ENTRY (widget)); gdk_window_end_paint (entry->text_area); } } @@ -864,7 +775,10 @@ gtk_entry_expose (GtkWidget *widget, 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; } @@ -902,7 +816,7 @@ gtk_entry_button_press (GtkWidget *widget, case GDK_BUTTON_PRESS: gtk_grab_add (widget); - tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset); + tmp_pos = gtk_entry_find_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; @@ -930,7 +844,7 @@ gtk_entry_button_press (GtkWidget *widget, { if (editable->selection_start_pos == editable->selection_end_pos || editable->has_selection) - editable->current_pos = gtk_entry_position (entry, event->x + entry->scroll_offset); + editable->current_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset); gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, ctext_atom, event->time); } @@ -938,7 +852,7 @@ gtk_entry_button_press (GtkWidget *widget, { gtk_grab_add (widget); - tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset); + tmp_pos = gtk_entry_find_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; @@ -1024,7 +938,7 @@ gtk_entry_motion_notify (GtkWidget *widget, if (event->is_hint || (entry->text_area != event->window)) gdk_window_get_pointer (entry->text_area, &x, NULL, NULL); - GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_position (entry, x + entry->scroll_offset); + GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_find_position (entry, x + entry->scroll_offset); GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(entry)->selection_end_pos; entry_adjust_scroll (entry); gtk_entry_queue_draw (entry); @@ -1173,19 +1087,8 @@ gtk_entry_key_press (GtkWidget *widget, 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; - } + gtk_im_context_filter_keypress (entry->im_context, event); + break; } @@ -1237,11 +1140,9 @@ gtk_entry_focus_in (GtkWidget *widget, 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_im_context_focus_in (GTK_ENTRY (widget)->im_context); return FALSE; } @@ -1256,38 +1157,24 @@ gtk_entry_focus_out (GtkWidget *widget, 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_im_context_focus_out (GTK_ENTRY (widget)->im_context); return FALSE; } static void -gtk_entry_make_backing_pixmap (GtkEntry *entry, gint width, gint height) +gtk_entry_ensure_layout (GtkEntry *entry) { - gint pixmap_width, pixmap_height; + GtkWidget *widget = GTK_WIDGET (entry); + + PangoAttrList *attrs; - if (!entry->backing_pixmap) - { - /* allocate */ - entry->backing_pixmap = gdk_pixmap_new (entry->text_area, - width, height, - -1); - } - else + if (!entry->layout) { - /* 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); - } + entry->layout = gtk_widget_create_pango_layout (widget); + pango_layout_set_text (entry->layout, entry->text, entry->n_bytes); } } @@ -1295,131 +1182,72 @@ static void gtk_entry_draw_text (GtkEntry *entry) { GtkWidget *widget; - GtkEditable *editable; - GtkStateType selected_state; - gint start_pos; - gint end_pos; - gint start_xoffset; - gint selection_start_pos; - gint selection_end_pos; - gint selection_start_xoffset; - gint selection_end_xoffset; - gint width, height; - gint y; - GdkDrawable *drawable; - GdkWChar *stars; - GdkWChar *toprint; - + PangoLayoutLine *line; + GtkEditable *editable = GTK_EDITABLE (entry); + g_return_if_fail (entry != NULL); g_return_if_fail (GTK_IS_ENTRY (entry)); - if (entry->timer) - { - gtk_timeout_remove (entry->timer); - entry->timer = 0; - } - if (GTK_WIDGET_DRAWABLE (entry)) { - widget = GTK_WIDGET (entry); - editable = GTK_EDITABLE (entry); - - if (!entry->text) - { - gtk_paint_flat_box (widget->style, entry->text_area, - GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE, - NULL, widget, "entry_bg", - 0, 0, -1, -1); - - if (editable->editable) - gtk_entry_draw_cursor (entry); - return; - } - - gdk_window_get_size (entry->text_area, &width, &height); + PangoRectangle logical_rect; + int area_height; - gtk_paint_flat_box (widget->style, entry->text_area, - GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE, - NULL, widget, "entry_bg", - 0, 0, width, height); + gdk_window_get_size (entry->text_area, NULL, &area_height); + area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER); + + widget = GTK_WIDGET (entry); - y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2; - y += widget->style->font->ascent; + gtk_entry_ensure_layout (entry); - start_pos = gtk_entry_find_position (entry, entry->scroll_offset); - start_xoffset = entry->char_offset[start_pos] - entry->scroll_offset; + line = pango_layout_get_lines (entry->layout)->data; + pango_layout_line_get_extents (line, NULL, &logical_rect); - end_pos = gtk_entry_find_position (entry, entry->scroll_offset + width); - if (end_pos < entry->text_length) - end_pos += 1; + gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], + INNER_BORDER - entry->scroll_offset, + INNER_BORDER + ((area_height - logical_rect.height) / 2 + + entry->ascent + logical_rect.y) / PANGO_SCALE, + entry->layout); - selected_state = GTK_STATE_SELECTED; - if (!editable->has_selection) - selected_state = GTK_STATE_ACTIVE; + if (editable->selection_start_pos != editable->selection_end_pos) + { + gint *ranges; + gint n_ranges, i; + gint start_index = unicode_offset_to_index (entry->text, + MIN (editable->selection_start_pos, editable->selection_end_pos)); + gint end_index = unicode_offset_to_index (entry->text, + MAX (editable->selection_start_pos, editable->selection_end_pos)); + GtkStateType selected_state = editable->has_selection ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE; + GdkRegion *clip_region = gdk_region_new (); + + pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); + + for (i=0; i < n_ranges; i++) + { + GdkRectangle rect; - 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); + rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE; + rect.y = INNER_BORDER + (entry->ascent + logical_rect.y) / PANGO_SCALE; + 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 [selected_state], TRUE, + rect.x, rect.y, rect.width, rect.height); - selection_start_xoffset = - entry->char_offset[selection_start_pos] - entry->scroll_offset; - selection_end_xoffset = - entry->char_offset[selection_end_pos] -entry->scroll_offset; + gdk_region_union_with_rect (clip_region, &rect); + } - /* if editable->visible, print a bunch of stars. If not, print the standard text. */ - if (editable->visible) - { - toprint = entry->text + start_pos; - } - else - { - gint i; + gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], clip_region); + gdk_draw_layout (entry->text_area, widget->style->fg_gc [selected_state], + INNER_BORDER - entry->scroll_offset, + INNER_BORDER + ((area_height - logical_rect.height) / 2 + + entry->ascent + logical_rect.y) / PANGO_SCALE, + entry->layout); + gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], NULL); - stars = g_new (GdkWChar, end_pos - start_pos); - for (i = 0; i < end_pos - start_pos; i++) - stars[i] = '*'; - toprint = stars; + gdk_region_destroy (clip_region); + g_free (ranges); } - - if (selection_start_pos > start_pos) - gdk_draw_text_wc (entry->text_area, widget->style->font, - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], - INNER_BORDER + 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)) - { - gtk_paint_flat_box (widget->style, entry->text_area, - selected_state, GTK_SHADOW_NONE, - NULL, widget, "text", - INNER_BORDER + selection_start_xoffset, - INNER_BORDER, - selection_end_xoffset - selection_start_xoffset, - height - 2*INNER_BORDER); - gdk_draw_text_wc (entry->text_area, widget->style->font, - widget->style->fg_gc[selected_state], - INNER_BORDER + 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_wc (entry->text_area, widget->style->font, - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], - INNER_BORDER + 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 (!editable->visible) - g_free (toprint); - - if (editable->editable) - gtk_entry_draw_cursor_on_drawable (entry, entry->text_area); } } @@ -1429,89 +1257,32 @@ gtk_entry_draw_cursor (GtkEntry *entry) g_return_if_fail (entry != NULL); g_return_if_fail (GTK_IS_ENTRY (entry)); - gtk_entry_draw_cursor_on_drawable (entry, entry->text_area); -} - -static void -gtk_entry_draw_cursor_on_drawable (GtkEntry *entry, GdkDrawable *drawable) -{ - GtkWidget *widget; - GtkEditable *editable; - gint xoffset; - gint text_area_height; - - g_return_if_fail (entry != NULL); - g_return_if_fail (GTK_IS_ENTRY (entry)); - if (GTK_WIDGET_DRAWABLE (entry)) { - widget = GTK_WIDGET (entry); - editable = GTK_EDITABLE (entry); - - xoffset = INNER_BORDER + entry->char_offset[editable->current_pos]; - xoffset -= entry->scroll_offset; - - gdk_window_get_size (entry->text_area, NULL, &text_area_height); + GtkWidget *widget = GTK_WIDGET (entry); + GtkEditable *editable = GTK_EDITABLE (entry); if (GTK_WIDGET_HAS_FOCUS (widget) && (editable->selection_start_pos == editable->selection_end_pos)) { - gdk_draw_line (drawable, widget->style->fg_gc[GTK_STATE_NORMAL], - xoffset, INNER_BORDER, - xoffset, text_area_height - INNER_BORDER); - } - else - { - int width, height; - GdkRectangle area; - - gint yoffset = - (text_area_height - - (widget->style->font->ascent + widget->style->font->descent)) / 2 - + widget->style->font->ascent; - - area.x = xoffset; - area.y = INNER_BORDER; - area.width = 1; - area.height = text_area_height - INNER_BORDER; - - gdk_window_get_size (entry->text_area, &width, &height); - - gtk_paint_flat_box (widget->style, drawable, - GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE, - &area, widget, "entry_bg", - 0, 0, width, height); + gint xoffset = INNER_BORDER - entry->scroll_offset; + gint strong_x, weak_x; + gint text_area_height; - - /* Draw the character under the cursor again - */ - if ((editable->current_pos < entry->text_length) && - (editable->selection_start_pos == editable->selection_end_pos)) - { - GdkWChar c = editable->visible ? - *(entry->text + editable->current_pos) : - '*'; - - gdk_draw_text_wc (drawable, widget->style->font, - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], - xoffset, yoffset, &c, 1); - } - } + gdk_window_get_size (entry->text_area, NULL, &text_area_height); + gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x); -#ifdef USE_XIM - if (GTK_WIDGET_HAS_FOCUS(widget) && gdk_im_ready() && editable->ic && - (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION)) - { - editable->ic_attr->spot_location.x = xoffset; - editable->ic_attr->spot_location.y = - (text_area_height + (widget->style->font->ascent - - widget->style->font->descent) + 1) / 2; + 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); - gdk_ic_set_attr (editable->ic, - editable->ic_attr, GDK_IC_SPOT_LOCATION); } -#endif } } @@ -1521,10 +1292,16 @@ gtk_entry_queue_draw (GtkEntry *entry) g_return_if_fail (entry != NULL); g_return_if_fail (GTK_IS_ENTRY (entry)); - if (!entry->timer) - entry->timer = gtk_timeout_add (DRAW_TIMEOUT, gtk_entry_timer, entry); + 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, 0); + } } +#if 0 static gint gtk_entry_timer (gpointer data) { @@ -1534,71 +1311,94 @@ gtk_entry_timer (gpointer data) entry = GTK_ENTRY (data); entry->timer = 0; - gtk_entry_draw_text (entry); GDK_THREADS_LEAVE (); return FALSE; } +#endif static gint gtk_entry_find_position (GtkEntry *entry, gint x) { - gint start = 0; - gint end = entry->text_length; - gint half; - - if (x <= 0) - return 0; - if (x >= entry->char_offset[end]) - return end; + PangoLayoutLine *line; + gint index; + gboolean trailing; - /* invariant - char_offset[start] <= x < char_offset[end] */ + gtk_entry_ensure_layout (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; - } + line = pango_layout_get_lines (entry->layout)->data; + pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing); - return start; + if (trailing) + index = unicode_next_utf8 (entry->text + index) - entry->text; + + return index; } -static gint -gtk_entry_position (GtkEntry *entry, - gint x) +static void +gtk_entry_get_cursor_locations (GtkEntry *entry, + gint *strong_x, + gint *weak_x) { - return gtk_entry_find_position(entry, x); + GtkEditable *editable = GTK_EDITABLE (entry); + int index; + + PangoRectangle strong_pos, weak_pos; + + gtk_entry_ensure_layout (entry); + + index = unicode_offset_to_index (entry->text, editable->current_pos); + pango_layout_get_cursor_pos (entry->layout, index, &strong_pos, &weak_pos); + + if (strong_x) + *strong_x = strong_pos.x / PANGO_SCALE; + + if (weak_x) + *weak_x = weak_pos.x / PANGO_SCALE; } static void entry_adjust_scroll (GtkEntry *entry) { - gint xoffset, max_offset; + GtkWidget *widget; + gint min_offset, max_offset; gint text_area_width; + gint strong_x, weak_x; + gint strong_xoffset, weak_xoffset; + PangoLayoutLine *line; + PangoRectangle logical_rect; g_return_if_fail (entry != NULL); g_return_if_fail (GTK_IS_ENTRY (entry)); - if (!entry->text_area) + widget = GTK_WIDGET (entry); + text_area_width = widget->allocation.width - 2 * (widget->style->klass->xthickness + INNER_BORDER); + + if (!entry->layout) return; + + line = pango_layout_get_lines (entry->layout)->data; + + /* Display as much text as we can */ - gdk_window_get_size (entry->text_area, &text_area_width, NULL); - text_area_width -= 2 * INNER_BORDER; + pango_layout_line_get_extents (line, NULL, &logical_rect); - /* Display as much text as we can */ - max_offset = MAX(0, entry->char_offset[entry->text_length] - text_area_width); + 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); + } - if (entry->scroll_offset > max_offset) - entry->scroll_offset = max_offset; + entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset); - /* And make sure cursor is on screen. Note that the cursor is + /* 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 @@ -1607,43 +1407,39 @@ entry_adjust_scroll (GtkEntry *entry) * 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. */ - xoffset = entry->char_offset[GTK_EDITABLE(entry)->current_pos]; - xoffset -= entry->scroll_offset; - - if (xoffset < 0) - entry->scroll_offset += xoffset; - else if (xoffset > text_area_width) - entry->scroll_offset += xoffset - text_area_width; - - gtk_widget_queue_draw (GTK_WIDGET (entry)); -} -static void -gtk_entry_grow_text (GtkEntry *entry) -{ - gint previous_size; - gint i; + gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x); + + strong_xoffset = strong_x - entry->scroll_offset; - g_return_if_fail (entry != NULL); - g_return_if_fail (GTK_IS_ENTRY (entry)); + 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; + } - 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 * sizeof(GdkWChar)); - entry->char_offset = g_realloc (entry->char_offset, - entry->text_size * sizeof(guint)); + weak_xoffset = weak_x - entry->scroll_offset; - if (entry->text_length == 0) /* initial allocation */ + if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width) { - entry->char_offset[0] = 0; + 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; } - for (i = previous_size; i < entry->text_size; i++) - entry->text[i] = '\0'; + gtk_widget_queue_draw (GTK_WIDGET (entry)); } static void @@ -1652,165 +1448,78 @@ gtk_entry_insert_text (GtkEditable *editable, gint new_text_length, gint *position) { - GdkWChar *text; - gint start_pos; - gint end_pos; - gint last_pos; - gint max_length; - gint i; - - guchar *new_text_nt; - gint insertion_length; - GdkWChar *insertion_text; - + gint index; + gint n_chars; GtkEntry *entry; GtkWidget *widget; g_return_if_fail (editable != NULL); g_return_if_fail (GTK_IS_ENTRY (editable)); + g_return_if_fail (position != NULL); + g_return_if_fail (*position >= 0 || *position < GTK_ENTRY (editable)->text_size); entry = GTK_ENTRY (editable); widget = GTK_WIDGET (editable); - if ((entry->text_length == 0) && (entry->use_wchar == FALSE)) - { - if (!GTK_WIDGET_REALIZED (widget)) - gtk_widget_ensure_style (widget); - if ((widget->style) && (widget->style->font->type == GDK_FONT_FONTSET)) - entry->use_wchar = TRUE; - } - if (new_text_length < 0) - { - new_text_nt = (gchar *)new_text; - new_text_length = strlen (new_text); - if (new_text_length <= 0) return; - } - else if (new_text_length == 0) - { - return; - } - else - { - /* make a null-terminated copy of new_text */ - new_text_nt = g_new (gchar, new_text_length + 1); - memcpy (new_text_nt, new_text, new_text_length); - new_text_nt[new_text_length] = 0; - } - - /* 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; - else - max_length = MIN (2047, entry->text_max_length); - - /* Convert to wide characters */ - insertion_text = g_new (GdkWChar, new_text_length); - if (entry->use_wchar) - insertion_length = gdk_mbstowcs (insertion_text, new_text_nt, - new_text_length); - else - for (insertion_length=0; new_text_nt[insertion_length]; insertion_length++) - insertion_text[insertion_length] = new_text_nt[insertion_length]; - if (new_text_nt != (guchar *)new_text) - g_free (new_text_nt); + new_text_length = strlen (new_text); - /* Make sure we do not exceed the maximum size of the entry. */ - if (insertion_length + entry->text_length > max_length) - insertion_length = max_length - entry->text_length; - - /* Don't insert anything, if there was nothing to insert. */ - if (insertion_length <= 0) + n_chars = unicode_strlen (new_text, new_text_length); + if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length) { - g_free(insertion_text); - return; + gdk_beep (); + n_chars = entry->text_max_length - entry->text_length; } - /* Make sure we are inserting at integral character position */ - start_pos = *position; - if (start_pos < 0) - start_pos = 0; - else if (start_pos > entry->text_length) - start_pos = entry->text_length; - - end_pos = start_pos + insertion_length; - last_pos = insertion_length + entry->text_length; - - if (editable->selection_start_pos >= *position) - editable->selection_start_pos += insertion_length; - if (editable->selection_end_pos >= *position) - editable->selection_end_pos += insertion_length; - - while (last_pos >= entry->text_size) - gtk_entry_grow_text (entry); - - 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] = insertion_text[i - start_pos]; - g_free (insertion_text); - - /* Fix up the the character offsets */ - - if (GTK_WIDGET_REALIZED (entry)) + if (new_text_length + entry->n_bytes + 1 > entry->text_size) { - gint offset = 0; - - for (i = last_pos; i >= end_pos; i--) - entry->char_offset[i] - = entry->char_offset[i - insertion_length]; - - for (i=start_pos; i<end_pos; i++) + while (new_text_length + entry->n_bytes + 1 > entry->text_size) { - entry->char_offset[i] = entry->char_offset[start_pos] + offset; - if (editable->visible) - { - offset += gdk_char_width_wc (GTK_WIDGET (entry)->style->font, - entry->text[i]); - } + if (entry->text_size == 0) + entry->text_size = MIN_SIZE; else { - offset += gdk_char_width (GTK_WIDGET (entry)->style->font, '*'); + 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; + } } } - for (i = end_pos; i <= last_pos; i++) - entry->char_offset[i] += offset; + + entry->text = g_realloc (entry->text, entry->text_size); } - entry->text_length += insertion_length; - *position = end_pos; + index = unicode_offset_to_index (entry->text, *position); - entry->text_mb_dirty = 1; - gtk_entry_queue_draw (entry); -} + g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index); + memcpy (entry->text + index, new_text, new_text_length); -/* Recompute the x offsets of all characters in the buffer */ -static void -gtk_entry_recompute_offsets (GtkEntry *entry) -{ - gint i; - gint offset = 0; + entry->n_bytes += new_text_length; + entry->text_length += n_chars; - for (i=0; i<entry->text_length; i++) - { - entry->char_offset[i] = offset; - if (GTK_EDITABLE (entry)->visible) - { - offset += gdk_char_width_wc (GTK_WIDGET (entry)->style->font, - entry->text[i]); - } - else - { - offset += gdk_char_width (GTK_WIDGET (entry)->style->font, '*'); - } - } + /* NUL terminate for safety and convenience */ + entry->text[entry->n_bytes] = '\0'; + + if (editable->current_pos > *position) + editable->current_pos += n_chars; + + if (editable->selection_start_pos > *position) + editable->selection_start_pos += n_chars; + + if (editable->selection_end_pos > *position) + editable->selection_end_pos += n_chars; + + *position += n_chars; - entry->char_offset[i] = offset; + if (entry->layout) + pango_layout_set_text (entry->layout, entry->text, entry->n_bytes); + + gtk_entry_queue_draw (entry); } static void @@ -1818,10 +1527,6 @@ gtk_entry_delete_text (GtkEditable *editable, gint start_pos, gint end_pos) { - GdkWChar *text; - gint deletion_length; - gint i; - GtkEntry *entry; g_return_if_fail (editable != NULL); @@ -1831,41 +1536,33 @@ gtk_entry_delete_text (GtkEditable *editable, if (end_pos < 0) end_pos = entry->text_length; - - 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; + gint start_index = unicode_offset_to_index (entry->text, start_pos); + gint end_index = unicode_offset_to_index (entry->text, end_pos); - /* Fix up the character offsets */ - if (GTK_WIDGET_REALIZED (entry)) - { - gint deletion_width = - entry->char_offset[end_pos] - entry->char_offset[start_pos]; - - for (i = 0 ; i <= entry->text_length - end_pos; i++) - entry->char_offset[start_pos+i] = entry->char_offset[end_pos+i] - deletion_width; - } + 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 (editable->current_pos > start_pos) + editable->current_pos -= MIN (editable->current_pos, end_pos) - start_pos; - for (i = end_pos; i < entry->text_length; i++) - text[i - deletion_length] = text[i]; + if (editable->selection_start_pos > start_pos) + editable->selection_start_pos -= MIN (editable->selection_start_pos, end_pos) - start_pos; - for (i = entry->text_length - deletion_length; i < entry->text_length; i++) - text[i] = '\0'; + if (editable->selection_end_pos > start_pos) + editable->selection_end_pos -= MIN (editable->selection_end_pos, end_pos) - start_pos; - entry->text_length -= deletion_length; - editable->current_pos = start_pos; } - entry->text_mb_dirty = 1; gtk_entry_queue_draw (entry); + + if (entry->layout) + pango_layout_set_text (entry->layout, entry->text, entry->n_bytes); } static void @@ -1873,7 +1570,9 @@ gtk_entry_update_text (GtkEditable *editable, gint start_pos, gint end_pos) { - gtk_entry_queue_draw (GTK_ENTRY(editable)); + GtkEntry *entry = GTK_ENTRY (editable); + + gtk_entry_queue_draw (entry); } static gchar * @@ -1882,6 +1581,7 @@ gtk_entry_get_chars (GtkEditable *editable, gint end_pos) { GtkEntry *entry; + gint start_index, end_index; g_return_val_if_fail (editable != NULL, NULL); g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL); @@ -1891,35 +1591,13 @@ gtk_entry_get_chars (GtkEditable *editable, if (end_pos < 0) end_pos = entry->text_length; - start_pos = MIN(entry->text_length, start_pos); - end_pos = MIN(entry->text_length, end_pos); + start_pos = MIN (entry->text_length, start_pos); + end_pos = MIN (entry->text_length, end_pos); - if (start_pos <= end_pos) - { - guchar *mbstr; - if (entry->use_wchar) - { - GdkWChar ch; - if (end_pos >= entry->text_size) - gtk_entry_grow_text(entry); - ch = entry->text[end_pos]; - entry->text[end_pos] = 0; - mbstr = gdk_wcstombs (entry->text + start_pos); - entry->text[end_pos] = ch; - return (gchar *)mbstr; - } - else - { - gint i; - mbstr = g_new (gchar, end_pos - start_pos + 1); - for (i=0; i<end_pos-start_pos; i++) - mbstr[i] = entry->text[start_pos + i]; - mbstr[i] = 0; - return (gchar *)mbstr; - } - } - else - return NULL; + start_index = unicode_offset_to_index (entry->text, start_pos); + end_index = unicode_offset_to_index (entry->text, end_pos); + + return g_strndup (entry->text + start_index, end_index - start_index); } static void @@ -1928,9 +1606,14 @@ gtk_entry_move_cursor (GtkEditable *editable, gint y) { GtkEntry *entry; + gint index; + entry = GTK_ENTRY (editable); + index = unicode_offset_to_index (entry->text, editable->current_pos); + /* Horizontal motion */ + if ((gint)editable->current_pos < -x) editable->current_pos = 0; else if (editable->current_pos + x > entry->text_length) @@ -1941,16 +1624,56 @@ gtk_entry_move_cursor (GtkEditable *editable, /* Ignore vertical motion */ } +static void +gtk_entry_move_cursor_visually (GtkEditable *editable, + gint count) +{ + GtkEntry *entry; + gint index; + + entry = GTK_ENTRY (editable); + + index = unicode_offset_to_index (entry->text, editable->current_pos); + + gtk_entry_ensure_layout (entry); + + while (count != 0) + { + int new_index, new_trailing; + + if (count > 0) + { + pango_layout_move_cursor_visually (entry->layout, index, 0, 1, &new_index, &new_trailing); + count--; + } + else + { + pango_layout_move_cursor_visually (entry->layout, index, 0, -1, &new_index, &new_trailing); + count++; + } + + if (new_index < 0 || new_index == G_MAXINT) + break; + + if (new_trailing) + index = unicode_next_utf8 (entry->text + new_index) - entry->text; + else + index = new_index; + } + + editable->current_pos = unicode_index_to_offset (entry->text, index); +} + static void gtk_move_forward_character (GtkEntry *entry) { - gtk_entry_move_cursor (GTK_EDITABLE (entry), 1, 0); + gtk_entry_move_cursor_visually (GTK_EDITABLE (entry), 1); } static void gtk_move_backward_character (GtkEntry *entry) { - gtk_entry_move_cursor (GTK_EDITABLE (entry), -1, 0); + gtk_entry_move_cursor_visually (GTK_EDITABLE (entry), -1); } static void @@ -1967,7 +1690,6 @@ static void gtk_move_forward_word (GtkEntry *entry) { GtkEditable *editable; - GdkWChar *text; gint i; editable = GTK_EDITABLE (entry); @@ -1981,23 +1703,33 @@ gtk_move_forward_word (GtkEntry *entry) if (entry->text && (editable->current_pos < entry->text_length)) { - text = entry->text; - i = editable->current_pos; - - if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) - for (; i < entry->text_length; i++) - { - if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i])) - break; - } + PangoLogAttr *log_attrs; + gint n_attrs, old_pos; - for (; i < entry->text_length; i++) - { - if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) - break; - } + gtk_entry_ensure_layout (entry); + pango_layout_get_log_attrs (entry->layout, &log_attrs, &n_attrs); + + i = old_pos = editable->current_pos; + + /* Advance over white space */ + while (i < n_attrs && log_attrs[i].is_white) + i++; + + /* Find the next word beginning */ + i++; + while (i < n_attrs && !log_attrs[i].is_word_stop) + i++; + + editable->current_pos = MAX (entry->text_length, i); + + /* Back up over white space */ + while (i > 0 && log_attrs[i - 1].is_white) + i--; - editable->current_pos = i; + if (i != old_pos) + editable->current_pos = i; + + g_free (log_attrs); } } @@ -2005,7 +1737,6 @@ static void gtk_move_backward_word (GtkEntry *entry) { GtkEditable *editable; - GdkWChar *text; gint i; editable = GTK_EDITABLE (entry); @@ -2019,27 +1750,19 @@ gtk_move_backward_word (GtkEntry *entry) if (entry->text && editable->current_pos > 0) { - text = entry->text; + PangoLogAttr *log_attrs; + gint n_attrs; + + gtk_entry_ensure_layout (entry); + pango_layout_get_log_attrs (entry->layout, &log_attrs, &n_attrs); + i = editable->current_pos - 1; - if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) - for (; i >= 0; i--) - { - if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i])) - break; - } - for (; i >= 0; i--) - { - if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) - { - i++; - break; - } - } - - if (i < 0) - i = 0; - - editable->current_pos = i; + + /* Find the previous word beginning */ + while (i > 0 && !log_attrs[i].is_word_stop) + i--; + + g_free (log_attrs); } } @@ -2207,9 +1930,13 @@ gtk_entry_set_selection (GtkEditable *editable, gint start, gint end) { + GtkEntry *entry; + g_return_if_fail (editable != NULL); g_return_if_fail (GTK_IS_ENTRY (editable)); + entry = GTK_ENTRY (editable); + if (end < 0) end = GTK_ENTRY (editable)->text_length; @@ -2235,60 +1962,17 @@ gtk_entry_set_max_length (GtkEntry *entry, 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; } -#ifdef USE_XIM -static void -gtk_entry_update_ic_attr (GtkWidget *widget) -{ - GtkEditable *editable = (GtkEditable *) widget; - GdkICAttributesType mask = 0; - - if (editable->ic == NULL) - return; - - gdk_ic_get_attr (editable->ic, editable->ic_attr, - GDK_IC_PREEDIT_FOREGROUND | - GDK_IC_PREEDIT_BACKGROUND | - GDK_IC_PREEDIT_FONTSET); - - if (editable->ic_attr->preedit_foreground.pixel != - widget->style->fg[GTK_STATE_NORMAL].pixel) - { - mask |= GDK_IC_PREEDIT_FOREGROUND; - editable->ic_attr->preedit_foreground - = widget->style->fg[GTK_STATE_NORMAL]; - } - if (editable->ic_attr->preedit_background.pixel != - widget->style->base[GTK_STATE_NORMAL].pixel) - { - mask |= GDK_IC_PREEDIT_BACKGROUND; - editable->ic_attr->preedit_background - = widget->style->base[GTK_STATE_NORMAL]; - } - if ((gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION) && - widget->style->font != NULL && - widget->style->font->type == GDK_FONT_FONTSET && - !gdk_font_equal (editable->ic_attr->preedit_fontset, - widget->style->font)) - { - mask |= GDK_IC_PREEDIT_FONTSET; - editable->ic_attr->preedit_fontset = widget->style->font; - } - - if (mask) - gdk_ic_set_attr (editable->ic, editable->ic_attr, mask); -} -#endif /* USE_XIM */ static void gtk_entry_style_set (GtkWidget *widget, GtkStyle *previous_style) { GtkEntry *entry; - gint scroll_char; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_ENTRY (widget)); @@ -2297,17 +1981,10 @@ gtk_entry_style_set (GtkWidget *widget, { 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]; entry_adjust_scroll (entry); 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)]); - -#ifdef USE_XIM - gtk_entry_update_ic_attr (widget); -#endif } } @@ -2322,12 +1999,21 @@ gtk_entry_state_changed (GtkWidget *widget, { 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)]); - -#ifdef USE_XIM - gtk_entry_update_ic_attr (widget); -#endif } if (GTK_WIDGET_DRAWABLE (widget)) gtk_widget_queue_clear(widget); } + +static void +gtk_entry_commit_cb (GtkIMContext *context, + const gchar *str, + GtkEntry *entry) +{ + GtkEditable *editable = GTK_EDITABLE (entry); + gint tmp_pos = editable->current_pos; + + gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos); + editable->current_pos = tmp_pos; +} + |