--- gtk+-2.6.4/gtk/gtkentry.c 2005-02-04 17:36:02.000000000 +0200 +++ gtk+-2.6.4/gtk/gtkentry.c 2005-04-06 16:19:36.466994536 +0300 @@ -24,6 +24,10 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ + /* Modified for Nokia Oyj during 2002-2003. See CHANGES file for list + * of changes. + */ + #include #include @@ -53,6 +57,7 @@ #include "gtktreeselection.h" #include "gtkentryprivate.h" #include "gtkcelllayout.h" +#include "gtkscrolledwindow.h" #define GTK_ENTRY_COMPLETION_KEY "gtk-entry-completion-key" @@ -60,6 +65,8 @@ #define DRAW_TIMEOUT 20 #define INNER_BORDER 2 #define COMPLETION_TIMEOUT 300 +#define HILDON_EDITED_CHARACTER_MAX 8 +#define HILDON_EDITED_CHARACTER_MS 600 /* 0.6 seconds */ /* Initial size of buffer, in bytes */ #define MIN_SIZE 16 @@ -75,6 +82,18 @@ { gfloat xalign; gint insert_pos; + /* Hildon additions: + * following variables are needed + * for Hildon password 'preview' + * functionality; last inputted character + * is showed for defined period, before it is + * rendered to '*' + */ + gchar hildon_edited_character[HILDON_EDITED_CHARACTER_MAX]; + gboolean hildon_edited_character_timeout; + gint hildon_edited_character_length; + gboolean keep_focus; + gboolean menu_popped; }; enum { @@ -104,7 +123,9 @@ PROP_WIDTH_CHARS, PROP_SCROLL_OFFSET, PROP_TEXT, - PROP_XALIGN + PROP_XALIGN, + PROP_AUTOCAP, + PROP_INPUT_MODE }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -324,6 +345,23 @@ gint *y, gint *width, gint *height); +static void gtk_entry_update_scrolled_window (GtkEntry *entry); +static void gtk_entry_set_autocap (GtkEntry *entry, + gboolean autocap); +static gboolean gtk_entry_get_autocap (GtkEntry *entry); +static void gtk_entry_set_input_mode (GtkEntry *entry, + gboolean mode); +static gint gtk_entry_get_input_mode (GtkEntry *entry); + +/*Change for Hildon + *returns an iterator to the character at position x,y of the + *layout + *returns NULL if no iterator was found at that position + *Caller must call pango_layout_free_iter on the returned iterator + */ +static PangoLayoutIter *get_char_at_pos( PangoLayout *layout, gint x, gint y ); + +static gboolean hildon_remove_visible_character( gpointer data ); /* Completion */ static gint gtk_entry_completion_timeout (gpointer data); @@ -523,7 +561,25 @@ P_("FALSE displays the \"invisible char\" instead of the actual text (password mode)"), TRUE, G_PARAM_READABLE | G_PARAM_WRITABLE)); - + + g_object_class_install_property (gobject_class, + PROP_AUTOCAP, + g_param_spec_boolean ("autocap", + P_("auto capitalization"), + P_("Enable autocap support"), + TRUE, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, + PROP_INPUT_MODE, + g_param_spec_int ("input_mode", + P_("input mode"), + P_("Define widget's input mode"), + 0, + 9, /* keep me updated */ + 0, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property (gobject_class, PROP_HAS_FRAME, g_param_spec_boolean ("has_frame", @@ -593,6 +649,40 @@ 0.0, G_PARAM_READABLE | G_PARAM_WRITABLE)); + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("horizontal-border", + P_("Horizontal borders for entry"), + P_("Set left/right borders"), + 0, + G_MAXINT, + INNER_BORDER, + G_PARAM_READWRITE)); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("vertical-border", + P_("Vertical borders for entry"), + P_("Set top/bottom borders"), + 0, + G_MAXINT, + INNER_BORDER, + G_PARAM_READWRITE)); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("icon-width", + P_("Icon Width"), + P_("Size of the purpose icon."), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("show-last-char", + P_("Show last char in invisible mode for a while"), + P_("Last char is shown before it is rendered to asterisk"), + FALSE, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + signals[POPULATE_POPUP] = g_signal_new ("populate_popup", G_OBJECT_CLASS_TYPE (gobject_class), @@ -853,6 +943,22 @@ iface->start_editing = gtk_entry_start_editing; } +/* HILDON: Timed function to hide the most recently inputted character in password mode +*/ +static gboolean + hildon_remove_visible_character( gpointer data ) +{ + g_return_val_if_fail (GTK_IS_WIDGET (data), FALSE); + + GtkEntry * entry = GTK_ENTRY( data ); + + /* Force the string to redrawn, but now without a visible character */ + gtk_entry_recompute( entry ); + + /* Return false so this timeout is not called again and destroyed */ + return FALSE; +} + static void gtk_entry_set_property (GObject *object, guint prop_id, @@ -867,9 +973,10 @@ { gboolean new_value = g_value_get_boolean (value); + gtk_widget_set_sensitive( GTK_WIDGET( entry ), entry->editable ); if (new_value != entry->editable) { - if (!new_value) + if (!new_value) { gtk_entry_reset_im_context (entry); if (GTK_WIDGET_HAS_FOCUS (entry)) @@ -896,6 +1003,14 @@ case PROP_VISIBILITY: gtk_entry_set_visibility (entry, g_value_get_boolean (value)); break; + + case PROP_AUTOCAP: + gtk_entry_set_autocap (entry, g_value_get_boolean (value)); + break; + + case PROP_INPUT_MODE: + gtk_entry_set_input_mode (entry, g_value_get_int (value)); + break; case PROP_HAS_FRAME: gtk_entry_set_has_frame (entry, g_value_get_boolean (value)); @@ -954,6 +1069,12 @@ case PROP_VISIBILITY: g_value_set_boolean (value, entry->visible); break; + case PROP_AUTOCAP: + g_value_set_boolean (value, gtk_entry_get_autocap (entry)); + break; + case PROP_INPUT_MODE: + g_value_set_int (value, gtk_entry_get_input_mode (entry)); + break; case PROP_HAS_FRAME: g_value_set_boolean (value, entry->has_frame); break; @@ -1000,7 +1121,20 @@ entry->width_chars = -1; entry->is_cell_renderer = FALSE; entry->editing_canceled = FALSE; - entry->has_frame = TRUE; +#ifdef HILDON_SINGLE_LINE_EDITOR + entry->has_frame = FALSE; +#else + entry->has_frame = TRUE; +#endif + + /* Hildon */ + memset( &priv->hildon_edited_character, 0x00, HILDON_EDITED_CHARACTER_MAX ); + priv->hildon_edited_character_length = 0; + priv->hildon_edited_character_timeout = FALSE; + + priv->keep_focus = FALSE; + priv->menu_popped = FALSE; + priv->xalign = 0.0; gtk_drag_dest_set (GTK_WIDGET (entry), @@ -1013,6 +1147,10 @@ * to it; so we create it here and destroy it in finalize(). */ entry->im_context = gtk_im_multicontext_new (); + /* Set default stuff. */ + gtk_entry_set_autocap (entry, TRUE); + gtk_entry_set_input_mode (entry, 0); /* alpha-numeric-special */ + g_object_set (G_OBJECT (entry->im_context), "use-show-hide", TRUE, NULL); g_signal_connect (entry->im_context, "commit", G_CALLBACK (gtk_entry_commit_cb), entry); @@ -1058,7 +1196,8 @@ gtk_entry_finalize (GObject *object) { GtkEntry *entry = GTK_ENTRY (object); - + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (GTK_WIDGET (entry)); + gtk_entry_set_completion (entry, NULL); if (entry->cached_layout) @@ -1072,6 +1211,9 @@ if (entry->recompute_idle) g_source_remove (entry->recompute_idle); + if (priv->hildon_edited_character_timeout) + g_source_remove (priv->hildon_edited_character_timeout); + entry->text_size = 0; if (entry->text) @@ -1213,7 +1355,14 @@ PangoFontMetrics *metrics; gint xborder, yborder; PangoContext *context; - + gint border_x, border_y; + gint icon_width; + + gtk_widget_style_get (widget, + "horizontal-border", &border_x, + "vertical-border", &border_y, + "icon-width", &icon_width, NULL); + gtk_widget_ensure_style (widget); context = gtk_widget_get_pango_context (widget); metrics = pango_context_get_metrics (context, @@ -1225,21 +1374,22 @@ _gtk_entry_get_borders (entry, &xborder, &yborder); - xborder += INNER_BORDER; - yborder += INNER_BORDER; + xborder += border_x<<1; + yborder += border_y<<1; if (entry->width_chars < 0) - requisition->width = MIN_ENTRY_WIDTH + xborder * 2; + requisition->width = MIN_ENTRY_WIDTH + xborder; else { gint char_width = pango_font_metrics_get_approximate_char_width (metrics); gint digit_width = pango_font_metrics_get_approximate_digit_width (metrics); gint char_pixels = (MAX (char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE; - requisition->width = char_pixels * entry->width_chars + xborder * 2; + requisition->width = char_pixels * entry->width_chars + xborder; } - - requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2; + + requisition->width += icon_width; + requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder; pango_font_metrics_unref (metrics); } @@ -1253,23 +1403,39 @@ { gint xborder, yborder; GtkRequisition requisition; + gint icon_width; GtkWidget *widget = GTK_WIDGET (entry); + gtk_widget_style_get (widget, "icon-width", &icon_width, NULL); + gtk_widget_get_child_requisition (widget, &requisition); _gtk_entry_get_borders (entry, &xborder, &yborder); if (x) - *x = xborder; + *x = xborder + icon_width; if (y) + { *y = yborder; + if( widget->allocation.height < requisition.height ) + *y += ((widget->allocation.height - requisition.height) / 2); + if( *y < yborder ) + *y = yborder; + } if (width) - *width = GTK_WIDGET (entry)->allocation.width - xborder * 2; + *width = GTK_WIDGET (entry)->allocation.width - xborder * 2 - icon_width; if (height) - *height = requisition.height - yborder * 2; + { + if( widget->allocation.height < requisition.height ) + *height = widget->allocation.height - yborder * 2; + else + *height = widget->requisition.height - yborder * 2; + if( *height <=0 ) + *height = 1; + } } static void @@ -1289,10 +1455,9 @@ if (y) { - if (entry->is_cell_renderer) - *y = widget->allocation.y; - else - *y = widget->allocation.y + (widget->allocation.height - requisition.height) / 2; + *y = widget->allocation.y; + if( widget->allocation.height > requisition.height ) + *y += ((widget->allocation.height - requisition.height) / 2); } if (width) @@ -1300,10 +1465,10 @@ if (height) { - if (entry->is_cell_renderer) - *height = widget->allocation.height; - else + if( widget->allocation.height > requisition.height ) *height = requisition.height; + else + *height = widget->allocation.height; } } @@ -1383,20 +1548,19 @@ GdkEventExpose *event) { GtkEntry *entry = GTK_ENTRY (widget); + gint area_width, area_height; + + get_widget_window_size (entry, NULL, NULL, &area_width, &area_height); if (widget->window == event->window) - gtk_entry_draw_frame (widget); + { + gtk_paint_box (widget->style, widget->window, + GTK_WIDGET_STATE (widget), GTK_SHADOW_NONE, + NULL, widget, "entry_frame", + 0, 0, area_width, area_height); + } else if (entry->text_area == event->window) { - gint area_width, area_height; - - get_text_area_size (entry, NULL, NULL, &area_width, &area_height); - - 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); - if ((entry->visible || entry->invisible_char != 0) && GTK_WIDGET_HAS_FOCUS (widget) && entry->selection_bound == entry->current_pos && entry->cursor_visible) @@ -1490,16 +1654,19 @@ return FALSE; entry->button = event->button; - + if (!GTK_WIDGET_HAS_FOCUS (widget)) { entry->in_click = TRUE; gtk_widget_grab_focus (widget); entry->in_click = FALSE; } - + + /* Hildon: we need to reset IM context so pre-edit string can be committed */ + gtk_entry_reset_im_context (entry); + tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset); - + if (event->button == 1) { gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end); @@ -1509,8 +1676,6 @@ if (event->state & GDK_SHIFT_MASK) { - gtk_entry_reset_im_context (entry); - if (!have_selection) /* select from the current position to the clicked position */ sel_start = sel_end = entry->current_pos; @@ -1575,9 +1740,20 @@ entry->drag_start_x = event->x + entry->scroll_offset; entry->drag_start_y = event->y + entry->scroll_offset; } - else - gtk_editable_set_position (editable, tmp_pos); - break; + else { + /* HILDON: do not move the cursor inside the textarea if invisible + * as per the specifications */ + if (!entry->visible) + if(tmp_pos == strlen(gtk_entry_get_text(entry))){ + gtk_editable_set_position( editable, entry->text_length); + } else { + gtk_editable_select_region (editable, 0, -1); + } + else + gtk_editable_set_position (editable, tmp_pos); + } + + break; case GDK_2BUTTON_PRESS: /* We ALWAYS receive a GDK_BUTTON_PRESS immediately before @@ -1614,8 +1790,16 @@ } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { + /* Hildon: if we are in password mode selection and Cut & Copy should + be disabled. */ + if (!entry->visible) + { + gtk_editable_set_position (GTK_EDITABLE(entry), 0); + } + gtk_entry_do_popup (entry, event); entry->button = 0; /* Don't wait for release, since the menu will gtk_grab_add */ + priv->keep_focus = TRUE; return TRUE; } @@ -1632,11 +1816,23 @@ if (event->window != entry->text_area || entry->button != event->button) return FALSE; + if (entry->editable) + gtk_im_context_show (entry->im_context); + if (entry->in_drag) { gint tmp_pos = gtk_entry_find_position (entry, entry->drag_start_x); - gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos); + /* HILDON: If not visible, do not allow cursor to be positioned inside the string */ + if (!entry->visible){ + if(tmp_pos == strlen(gtk_entry_get_text(entry))){ + gtk_editable_set_position( GTK_EDITABLE(entry), entry->text_length); + } else { + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + } + } else { + gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos); + } entry->in_drag = 0; } @@ -1822,6 +2018,13 @@ } } + if (event->keyval == GDK_Return) + return FALSE; + if (event->keyval == GDK_KP_Enter) + g_signal_emit_by_name (G_OBJECT(gtk_widget_get_ancestor (widget, + GTK_TYPE_WINDOW)), "move-focus", + GTK_DIR_TAB_FORWARD); + if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event)) /* Activate key bindings */ @@ -1835,6 +2038,8 @@ GdkEventKey *event) { GtkEntry *entry = GTK_ENTRY (widget); + + gtk_entry_update_scrolled_window( entry ); if (entry->editable) { @@ -1853,7 +2058,35 @@ GdkEventFocus *event) { GtkEntry *entry = GTK_ENTRY (widget); - + GtkEntryPrivate *priv; + + priv = GTK_ENTRY_GET_PRIVATE (widget); + /* Hildon : If the text doesn't fit the entry, upon focusing + * to an text field, move the caret to the end of the entry. + * Force the entry to recompute, otherwise it doesn't update + * if the cursor is currently at the end*/ + /* hildon : If the text has no selection and focus returned with + other means than pointer click, set cursor before first + character of the text, otherwise behave normally */ + + if (!entry->in_click) + { + /*gboolean has_selection; + has_selection = gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), NULL, NULL); + if (!has_selection) + { + gtk_editable_set_position (GTK_EDITABLE (entry), -1); + }*//*FIXME need a better hack here*/ + /* Hildon: If in SecretEditor mode highlight selection if entry got focus + * otherways than mouse/stylus */ + if (!entry->visible && priv->keep_focus) + { + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + } + } + + gtk_entry_recompute (GTK_ENTRY (entry)); + gtk_widget_queue_draw (widget); if (entry->editable) @@ -1876,6 +2109,8 @@ GdkEventFocus *event) { GtkEntry *entry = GTK_ENTRY (widget); + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (widget); + guint tmp_pos; GtkEntryCompletion *completion; gtk_widget_queue_draw (widget); @@ -1886,6 +2121,20 @@ gtk_im_context_focus_out (entry->im_context); } + tmp_pos = gtk_editable_get_position (GTK_EDITABLE (widget)); + if (priv->keep_focus){ + if (!entry->visible) + gtk_editable_set_position (GTK_EDITABLE (widget), tmp_pos); + priv->keep_focus = FALSE; + } + else { + if (priv->menu_popped) + priv->menu_popped = FALSE; + else gtk_editable_set_position (GTK_EDITABLE (widget), tmp_pos); + } + + gtk_widget_queue_draw(widget); + gtk_entry_check_cursor_blink (entry); g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)), @@ -1902,7 +2151,6 @@ static void gtk_entry_grab_focus (GtkWidget *widget) { - GtkEntry *entry = GTK_ENTRY (widget); gboolean select_on_focus; GTK_WIDGET_CLASS (parent_class)->grab_focus (widget); @@ -1912,8 +2160,8 @@ &select_on_focus, NULL); - if (select_on_focus && entry->editable && !entry->in_click) - gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1); +/* Hildon : When focusing to an entry, it shouldn't become + * highlighted. */ } static void @@ -1987,7 +2235,7 @@ if (new_text_length > 63) g_free (text); - + gtk_entry_update_scrolled_window( entry ); g_object_unref (editable); } @@ -2074,7 +2322,7 @@ if (end < 0) end = entry->text_length; - gtk_entry_reset_im_context (entry); + /*gtk_entry_reset_im_context (entry);*//*FIXME tmp kludge, might break something*/ gtk_entry_set_positions (entry, MIN (end, entry->text_length), @@ -2168,13 +2416,19 @@ { gint index; gint n_chars; + gboolean show_last_char = FALSE; GtkEntry *entry = GTK_ENTRY (editable); - + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (GTK_WIDGET (entry)); if (new_text_length < 0) new_text_length = strlen (new_text); n_chars = g_utf8_strlen (new_text, new_text_length); + + + gtk_widget_style_get (GTK_WIDGET (entry), "show-last-char", + &show_last_char, NULL); + if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length) { gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (entry))); @@ -2238,6 +2492,14 @@ if (entry->selection_bound > *position) entry->selection_bound += n_chars; + + /* Hildon: store this addition IF it was only 1 char (user inputted) and we are currently in secret mode (invisible) */ + + if (show_last_char && n_chars == 1 && !entry->visible && (new_text_length < HILDON_EDITED_CHARACTER_MAX)) { + memset( &priv->hildon_edited_character, 0x00, HILDON_EDITED_CHARACTER_MAX ); + priv->hildon_edited_character_length = new_text_length; + memcpy( &priv->hildon_edited_character, new_text, new_text_length ); /* Guaranteed to be < total length */ + } *position += n_chars; @@ -2339,6 +2601,11 @@ gtk_entry_reset_im_context (entry); + /* Hildon, if not visible set the position to the end */ + /* New SecretEditor specs say that with cursor should move + * With left/right arrows + */ + if (entry->current_pos != entry->selection_bound && !extend_selection) { /* If we have a current selection and aren't extending it, move to the @@ -2445,7 +2712,7 @@ gint start_pos = entry->current_pos; gint end_pos = entry->current_pos; - gtk_entry_reset_im_context (entry); + /* Hildon: code removed - backspace should not clear the word completion */ if (!entry->editable) return; @@ -2515,7 +2782,8 @@ GtkEditable *editable = GTK_EDITABLE (entry); gint prev_pos; - gtk_entry_reset_im_context (entry); +/* gtk_entry_reset_im_context (entry); */ /*backspace should not clear + the word completion*/ if (!entry->editable || !entry->text) return; @@ -2883,21 +3151,28 @@ ++i; } } - + +#define HILDON_EDITED_CHARACTER_MS 600 /* 0.6 seconds */ + static PangoLayout * gtk_entry_create_layout (GtkEntry *entry, gboolean include_preedit) { GtkWidget *widget = GTK_WIDGET (entry); + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL); PangoAttrList *tmp_attrs = pango_attr_list_new (); gchar *preedit_string = NULL; gint preedit_length = 0; PangoAttrList *preedit_attrs = NULL; + gboolean show_last_char = FALSE; pango_layout_set_single_paragraph_mode (layout, TRUE); + gtk_widget_style_get (widget, "show-last-char", + &show_last_char, NULL); + if (include_preedit) { gtk_im_context_get_preedit_string (entry->im_context, @@ -3003,7 +3278,49 @@ else invisible_char = ' '; /* just pick a char */ - append_char (str, invisible_char, entry->text_length); + if (!show_last_char) + append_char (str, invisible_char, entry->text_length); + else if (show_last_char) + { + /* Hildon */ + if (priv->hildon_edited_character_length > 0) + { + + /* If we have an outstanding timeout, remove it, because the character it is set to hide + * is already hidden now. We do this first to prevent possible race conditions if the timout + * were to trigger while in here + */ + + if (priv->hildon_edited_character_timeout) + { + g_source_remove( priv->hildon_edited_character_timeout ); + priv->hildon_edited_character_timeout = FALSE; + } + + /* Draw the secret character length-1 times, because the last char will be visible */ + append_char (str, invisible_char, entry->text_length > 1 ? entry->text_length - 1 : 0); + + /* Add our visible char, the most recently inputted one */ + g_string_append_len (str, (char *)&priv->hildon_edited_character, priv->hildon_edited_character_length); + + /* Now remove this last inputted character, don't need it anymore */ + + memset( priv->hildon_edited_character, 0x00, HILDON_EDITED_CHARACTER_MAX ); + priv->hildon_edited_character_length = 0; + + priv->hildon_edited_character_timeout = g_timeout_add( HILDON_EDITED_CHARACTER_MS, (GSourceFunc) + hildon_remove_visible_character, entry ); + + } + else + { + /* No last character known. This could be for example because the application has filled + * in the password already. In that case we of course don't want to view it + */ + append_char (str, invisible_char, entry->text_length); + } + } + pango_layout_set_text (layout, str->str, str->len); g_string_free (str, TRUE); } @@ -3048,12 +3365,17 @@ gint area_width, area_height; gint y_pos; PangoLayoutLine *line; - + gint border_x, border_y; + + gtk_widget_style_get (GTK_WIDGET (entry), "horizontal-border", &border_x, + "vertical-border", &border_y, + NULL); + layout = gtk_entry_ensure_layout (entry, TRUE); get_text_area_size (entry, NULL, NULL, &area_width, &area_height); - area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER); + area_height = PANGO_SCALE * (area_height - 2 * border_y); line = pango_layout_get_lines (layout)->data; pango_layout_line_get_extents (line, NULL, &logical_rect); @@ -3070,10 +3392,10 @@ else if (y_pos + logical_rect.height > area_height) y_pos = area_height - logical_rect.height; - y_pos = INNER_BORDER + y_pos / PANGO_SCALE; + y_pos = border_y + y_pos / PANGO_SCALE; if (x) - *x = INNER_BORDER - entry->scroll_offset; + *x = border_x - entry->scroll_offset; if (y) *y = y_pos; @@ -3083,6 +3405,10 @@ gtk_entry_draw_text (GtkEntry *entry) { GtkWidget *widget; + gint border_y, border_x; + + gtk_widget_style_get (GTK_WIDGET (entry), "horizontal-border", &border_x, + "vertical-border", &border_y, NULL); if (!entry->visible && entry->invisible_char == 0) return; @@ -3092,14 +3418,76 @@ PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE); gint x, y; gint start_pos, end_pos; + GdkRectangle clip_rect; widget = GTK_WIDGET (entry); get_layout_position (entry, &x, &y); + /* Use a clipping rectangle so that we always get enough empty space around + * the text. + */ + clip_rect.x = border_x; + clip_rect.y = 0; + + gdk_drawable_get_size (entry->text_area, &clip_rect.width, &clip_rect.height); + clip_rect.width -= border_x * 2; + + /*changes for Hildon + *Reduce the size of the clip rect, so that only full characters are displayed + */ + + /* NOTE: Commented out because it does not work with bidi text where +the indexes are in random + * left-right or right-left order. Code causes Pango assert aborts. Because gtkentry itself + * is broken with regard to bidi anyway (bug #478) we ignore this requirement of the spec + * until gtkentry itself is fixed. (bug #477) + */ + + /* Better yet, let's enable this only when not in RTL mode */ + + /* Note: BUG #857. patched gtkentry crashed when pasting scalable fonts. This is pango problem + * and we tested patched gtkentry with pango version 1.3.2 and it appears to be fixed. Section is commented + out until we upgrade to new version of pango + if (gtk_widget_get_direction( entry ) != GTK_TEXT_DIR_RTL) + { + PangoRectangle char_rect; // used for getting character's onscreen pos + PangoLayoutIter *iter; // used to iterate over the text + + // get the position of the character currently at the clip border + iter = get_char_at_pos( layout, (clip_rect.x + clip_rect.width + entry->scroll_offset), 0 ); + if ( iter ) + { + // get the position of that character on the screen + pango_layout_iter_get_char_extents( iter, &char_rect ); + char_rect.x /= PANGO_SCALE; + char_rect.x -= entry->scroll_offset; + char_rect.width /= PANGO_SCALE; + + // if the ending position is > the clip rectangle, then the + // character is only partially visible, and we should + // clip the entire character. + + if ( char_rect.x + char_rect.width > clip_rect.x + clip_rect.width ) + { + clip_rect.width = char_rect.x; + } + + pango_layout_iter_free( iter ); + } + + } + */ + /******************************************************************/ + + gdk_gc_set_clip_rectangle (widget->style->text_gc [widget->state], &clip_rect); + gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state], x, y, layout); + + gdk_gc_set_clip_rectangle (widget->style->text_gc [widget->state], NULL); + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos)) { @@ -3128,7 +3516,7 @@ { GdkRectangle rect; - rect.x = INNER_BORDER - entry->scroll_offset + ranges[2 * i]; + rect.x = border_x - entry->scroll_offset + ranges[2 * i]; rect.y = y; rect.width = ranges[2 * i + 1]; rect.height = logical_rect.height; @@ -3177,14 +3565,18 @@ { GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (entry))); PangoDirection keymap_direction = gdk_keymap_get_direction (keymap); + gint border_x, border_y; + gtk_widget_style_get (GTK_WIDGET (entry), "horizontal-border", &border_x, + "vertical-border", &border_y, + NULL); if (GTK_WIDGET_DRAWABLE (entry)) { GtkWidget *widget = GTK_WIDGET (entry); GdkRectangle cursor_location; gboolean split_cursor; - gint xoffset = INNER_BORDER - entry->scroll_offset; + gint xoffset = border_x - entry->scroll_offset; gint strong_x, weak_x; gint text_area_height; PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL; @@ -3221,9 +3613,9 @@ } cursor_location.x = xoffset + x1; - cursor_location.y = INNER_BORDER; + cursor_location.y = border_y; cursor_location.width = 0; - cursor_location.height = text_area_height - 2 * INNER_BORDER ; + cursor_location.height = text_area_height - 2 * border_y; draw_insertion_cursor (entry, &cursor_location, TRUE, dir1, @@ -3249,11 +3641,8 @@ 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); - } + /* Hildon: We want reset to be sent more often */ + gtk_im_context_reset (entry->im_context); } static gint @@ -3266,8 +3655,12 @@ gint pos; gboolean trailing; const gchar *text; - gint cursor_index; - + gint border_x, cursor_index; + + gtk_widget_style_get (GTK_WIDGET (entry), "horizontal-border", &border_x, + NULL); + x -= border_x; + layout = gtk_entry_ensure_layout (entry, TRUE); text = pango_layout_get_text (layout); cursor_index = g_utf8_offset_to_pointer (text, entry->current_pos) - text; @@ -3355,12 +3748,17 @@ PangoLayout *layout; PangoLayoutLine *line; PangoRectangle logical_rect; + gint border_x, border_y; + + gtk_widget_style_get (GTK_WIDGET (entry), "horizontal-border", &border_x, + "vertical-border", &border_y, + NULL); if (!GTK_WIDGET_REALIZED (entry)) return; gdk_drawable_get_size (entry->text_area, &text_area_width, NULL); - text_area_width -= 2 * INNER_BORDER; + text_area_width -= 2 * border_x; layout = gtk_entry_ensure_layout (entry, TRUE); line = pango_layout_get_lines (layout)->data; @@ -3390,13 +3788,13 @@ 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 + * actually drawn one pixel into the border_x 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 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 + * border_x 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 @@ -3430,6 +3828,52 @@ entry->scroll_offset += weak_xoffset - text_area_width; } + /*Changes for Hildon + * now we make it so that if a character is partially visible, + * then we also scroll that off the screen. + */ + + /* NOTE: Commented out because it does not work with bidi text where the indexes are in random + * left-right or right-left order. Code causes Pango assert aborts. Because gtkentry itself + * is broken with regard to bidi anyway (bug #478) we ignore this requirement of the spec + * until gtkentry itself is fixed. (bug #477) + */ + + /* Better yet, let's disable this (for now) only when using RTL text */ + +/*Note: BUG #857. patched gtkentry crashed when pasting scalable fonts. This is pango problem + * and we tested patched gtkentry with pango version 1.3.2 and it appears to be fixed. Section is comment ed out until we upgrade to new version of pango + if (gtk_widget_get_direction( entry ) != GTK_TEXT_DIR_RTL) + { + PangoLayoutIter *iter = get_char_at_pos( layout, entry->scroll_offset, 0 ); + // if we found the char we were looking for + if ( iter ) + { + PangoRectangle char_rect; // used for getting character's onscreen pos + + // get the position of that character on the screen + pango_layout_iter_get_char_extents( iter, &char_rect ); + char_rect.x /= PANGO_SCALE; + + // if the starting position is < the current scroll offset, then the + // character is only partially visible, and we should scroll to the + // start of the next character instead + + if ( char_rect.x < entry->scroll_offset ) + { + if ( pango_layout_iter_next_char( iter ) ) + { + pango_layout_iter_get_char_extents( iter, &char_rect); + entry->scroll_offset = char_rect.x / PANGO_SCALE; + } + } + + pango_layout_iter_free( iter ); + } + + + }*/ + g_object_notify (G_OBJECT (entry), "scroll_offset"); } @@ -3552,8 +3996,9 @@ pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); /* Find the next word end */ + /*Hildon: cursor should seek to the start of the next word*/ new_pos++; - while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end) + while (new_pos < n_attrs && !log_attrs[new_pos].is_word_start) new_pos++; g_free (log_attrs); @@ -3648,14 +4093,9 @@ if (entry->visible) return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end); - else if (!entry->invisible_char) + /*Hildon: when not visible, no chars are public*/ + else return g_strdup (""); - else - { - GString *str = g_string_new (NULL); - append_char (str, entry->invisible_char, end - start); - return g_string_free (str, FALSE); - } } static void @@ -3678,9 +4118,12 @@ if (text) { - gint pos, start, end; + gint pos, start, end, index; GtkEntryCompletion *completion = gtk_entry_get_completion (entry); + /* when pasting multiline text, ignore everything but the first line */ + for (index = 0; text[index] != 0 && text[index] != '\n'; index++); + if (completion) { g_signal_handler_block (entry, completion->priv->changed_id); @@ -3692,7 +4135,7 @@ gtk_editable_delete_text (editable, start, end); pos = entry->current_pos; - gtk_editable_insert_text (editable, text, -1, &pos); + gtk_editable_insert_text (editable, text, index, &pos); gtk_editable_set_position (editable, pos); if (completion) @@ -3888,6 +4331,7 @@ { g_return_if_fail (GTK_IS_ENTRY (entry)); + g_object_set(G_OBJECT(entry->im_context), "visibility", visible, NULL); entry->visible = visible ? TRUE : FALSE; g_object_notify (G_OBJECT (entry), "visibility"); gtk_entry_recompute (entry); @@ -4569,6 +5013,7 @@ GdkEventButton *event) { PopupInfo *info = g_new (PopupInfo, 1); + GtkEntryPrivate *priv; /* In order to know what entries we should make sensitive, we * ask for the current targets of the clipboard, and when @@ -4576,6 +5021,8 @@ */ info->entry = g_object_ref (entry); + priv = GTK_ENTRY_GET_PRIVATE (entry); + if (event) { info->button = event->button; @@ -4591,6 +5038,8 @@ gdk_atom_intern ("TARGETS", FALSE), popup_targets_received, info); + + priv->menu_popped = TRUE; } static gboolean @@ -5389,3 +5838,156 @@ return completion; } + +static PangoLayoutIter *get_char_at_pos( PangoLayout *layout, gint x, gint y ) +{ + gint index = 0; /*the index of the first character */ + gint trailing = 0; /*not used*/ + PangoLayoutIter *iter; /*used to iterate over the text*/ + gboolean valid_char = TRUE; + + /*get the position of the character currently at the scroll offset*/ + pango_layout_xy_to_index( layout, x * PANGO_SCALE, y * PANGO_SCALE, &index, &trailing ); + iter = pango_layout_get_iter( layout ); + + /*iterate until we get to the character at the same index*/ + while ( valid_char && pango_layout_iter_get_index( iter ) != index ) + { + valid_char = pango_layout_iter_next_char( iter ); + } + + if ( valid_char == FALSE ) + iter = NULL; + + return iter; +} + +static void gtk_entry_update_scrolled_window( GtkEntry *entry) +{ + GtkWidget *parent; + GtkWidget *widget; + GtkWidget *direct_parent = NULL; + GtkScrolledWindow *sw = NULL; + gboolean need_update = FALSE; + GtkAdjustment *vadjustment; + gdouble value; + + widget = parent = GTK_WIDGET(entry); + if(parent->parent != NULL) + direct_parent = parent->parent; + while(parent && !GTK_WIDGET_TOPLEVEL(parent)) + { + if(GTK_IS_SCROLLED_WINDOW(parent)) + { + need_update = TRUE; + sw = GTK_SCROLLED_WINDOW(parent); + break; + } + parent = parent->parent; + } + + if(need_update) + { + gint x=0, y=0; + vadjustment = gtk_scrolled_window_get_vadjustment(sw); + value = gtk_adjustment_get_value(vadjustment); + + if(direct_parent != NULL) + gtk_widget_translate_coordinates( direct_parent, parent, + widget->allocation.x, widget->allocation.y, &x, &y ); + + if( (gdouble) y < 0 ) + { + value = value + (gdouble) y; + if (value < vadjustment->lower) + value = vadjustment->lower; + } + else if( (gdouble )y + widget->allocation.height > vadjustment->page_size) + { + value = value + (gdouble)y + widget->allocation.height + - vadjustment->page_size; + if(value > vadjustment->upper - vadjustment->page_size) + value = vadjustment->upper - vadjustment->page_size; + } + gtk_adjustment_set_value(vadjustment, value); + gtk_scrolled_window_set_vadjustment(sw, GTK_ADJUSTMENT(vadjustment)); + } +} + +/* + * gtk_entry_set_autocap: + * @entry: a #GtkEntry + * @autocap: autocap + * + * Sets autocapitalization of the widget. + */ +static void +gtk_entry_set_autocap (GtkEntry *entry, + gboolean autocap) +{ + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (gtk_entry_get_autocap (entry) != autocap) + { + g_object_set (G_OBJECT (entry->im_context), "autocap", autocap, NULL); + g_object_notify (G_OBJECT (entry), "autocap"); + } +} + +/* + * gtk_entry_get_autocap: + * @entry: a #GtkEntry + * + * Gets autocapitalization state of the widget. + * + * Return value: a state + */ +static gboolean +gtk_entry_get_autocap (GtkEntry *entry) +{ + gboolean autocap; + g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE); + + g_object_get (G_OBJECT (entry->im_context), "autocap", &autocap, NULL); + + return autocap; +} + +/* + * gtk_entry_set_input_mode: + * @entry: a #GtkEntry + * @autocap: input mode + * + * Sets autocapitalization of the widget. + */ +static void +gtk_entry_set_input_mode (GtkEntry *entry, + gint mode) +{ + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (gtk_entry_get_input_mode (entry) != mode) + { + g_object_set (G_OBJECT (entry->im_context), "input_mode", mode, NULL); + g_object_notify (G_OBJECT (entry), "input_mode"); + } +} + +/* + * gtk_entry_get_input_mode: + * @entry: a #GtkEntry + * + * Gets input mode of the widget. + * + * Return value: input mode + */ +static gint +gtk_entry_get_input_mode (GtkEntry *entry) +{ + gint mode; + g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE); + + g_object_get (G_OBJECT (entry->im_context), "input_mode", &mode, NULL); + + return mode; +}