Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
DISCONTINUED:openSUSE:11.1:Update
gtk
gtk-cvs.dif
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File gtk-cvs.dif of Package gtk
diff -ruN gtk+-1.2.10/gdk/gdkevents.c gtk-n/gdk/gdkevents.c --- gtk+-1.2.10/gdk/gdkevents.c Sun Dec 3 17:02:49 2000 +++ gtk-n/gdk/gdkevents.c Thu Jan 31 08:56:05 2002 @@ -383,6 +383,7 @@ struct _GdkExposeInfo { Window window; + GdkWindowPrivate *toplevel_window; gboolean seen_nonmatching; }; @@ -400,10 +401,21 @@ * we'll get a whole bunch of them interspersed with * expose events. */ - if (xevent->xany.type != Expose && - xevent->xany.type != GravityNotify) + switch (xevent->xany.type) { + case Expose: + case GravityNotify: + break; + case ConfigureNotify: + if (xevent->xconfigure.window != info->toplevel_window->xwindow) + break; + if (xevent->xconfigure.width == info->toplevel_window->width && + xevent->xconfigure.height == info->toplevel_window->height) + break; + /* Fall through */ + default: info->seen_nonmatching = TRUE; + break; } if (info->seen_nonmatching || @@ -429,6 +441,7 @@ GdkEvent event; info.window = xevent->xany.window; + info.toplevel_window = (GdkWindowPrivate *) gdk_window_get_toplevel (window); info.seen_nonmatching = FALSE; rect1.x = xevent->xexpose.x; diff -ruN gtk+-1.2.10/gdk/gdkfont.c gtk-n/gdk/gdkfont.c --- gtk+-1.2.10/gdk/gdkfont.c Mon Apr 2 04:31:25 2001 +++ gtk-n/gdk/gdkfont.c Thu Jan 31 08:56:23 2002 @@ -26,6 +26,7 @@ #include <X11/Xlib.h> #include <X11/Xos.h> +#include <langinfo.h> #include "gdk.h" #include "gdkprivate.h" @@ -173,9 +174,24 @@ if (missing_charset_count) { gint i; - g_warning ("Missing charsets in FontSet creation\n"); - for (i=0;i<missing_charset_count;i++) - g_warning (" %s\n", missing_charset_list[i]); + const char *codeset; + + codeset = nl_langinfo (CODESET); + + /* Hack - UTF-8 is likely to be rendered with a list of + * possible legacy fallback charsets, so a failure here + * shouldn't be warned about. But we don't want to suppress + * this warning in general, since for other character sets + * it gives a useful indication of what went wrong. + */ + if (g_strcasecmp (codeset, "utf-8") != 0 && + g_strcasecmp (codeset, "utf8") != 0) + { + g_warning ("Missing charsets in FontSet creation\n"); + for (i=0;i<missing_charset_count;i++) + g_warning (" %s\n", missing_charset_list[i]); + } + XFreeStringList (missing_charset_list); } @@ -445,7 +461,6 @@ GdkFontPrivate *private; XCharStruct *chars; gint width; - guint ch = character & 0xff; /* get rid of sign-extension */ XFontStruct *xfont; XFontSet fontset; @@ -458,21 +473,7 @@ case GDK_FONT_FONT: /* only 8 bits characters are considered here */ xfont = (XFontStruct *) private->xfont; - if ((xfont->min_byte1 == 0) && - (xfont->max_byte1 == 0) && - (ch >= xfont->min_char_or_byte2) && - (ch <= xfont->max_char_or_byte2)) - { - chars = xfont->per_char; - if (chars) - width = chars[ch - xfont->min_char_or_byte2].width; - else - width = xfont->min_bounds.width; - } - else - { - width = XTextWidth (xfont, &character, 1); - } + width = XTextWidth (xfont, &character, 1); break; case GDK_FONT_FONTSET: fontset = (XFontSet) private->xfont; diff -ruN gtk+-1.2.10/gdk/gdkfont.c.orig gtk-n/gdk/gdkfont.c.orig --- gtk+-1.2.10/gdk/gdkfont.c.orig Thu Jan 1 01:00:00 1970 +++ gtk-n/gdk/gdkfont.c.orig Mon Apr 2 04:31:25 2001 @@ -0,0 +1,897 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. 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 <X11/Xlib.h> +#include <X11/Xos.h> +#include "gdk.h" +#include "gdkprivate.h" + +#if HAVE_CONFIG_H +# include <config.h> +# if STDC_HEADERS +# include <string.h> +# endif +#endif + +#ifdef USE_NATIVE_LOCALE +#include <stdlib.h> +#endif + +static GHashTable *font_name_hash = NULL; +static GHashTable *fontset_name_hash = NULL; + +#define FONT_XFONT(private) ((XFontStruct *)(private)->xfont) +#define FONT_IS_8BIT(private) ((FONT_XFONT(private)->min_byte1 == 0) && \ + (FONT_XFONT(private)->max_byte1 == 0)) + +static void +gdk_font_hash_insert (GdkFontType type, GdkFont *font, const gchar *font_name) +{ + GdkFontPrivate *private = (GdkFontPrivate *)font; + GHashTable **hashp = (type == GDK_FONT_FONT) ? + &font_name_hash : &fontset_name_hash; + + if (!*hashp) + *hashp = g_hash_table_new (g_str_hash, g_str_equal); + + private->names = g_slist_prepend (private->names, g_strdup (font_name)); + g_hash_table_insert (*hashp, private->names->data, font); +} + +static void +gdk_font_hash_remove (GdkFontType type, GdkFont *font) +{ + GdkFontPrivate *private = (GdkFontPrivate *)font; + GSList *tmp_list; + GHashTable *hash = (type == GDK_FONT_FONT) ? + font_name_hash : fontset_name_hash; + + tmp_list = private->names; + while (tmp_list) + { + g_hash_table_remove (hash, tmp_list->data); + g_free (tmp_list->data); + + tmp_list = tmp_list->next; + } + + g_slist_free (private->names); + private->names = NULL; +} + +static GdkFont * +gdk_font_hash_lookup (GdkFontType type, const gchar *font_name) +{ + GdkFont *result; + GHashTable *hash = (type == GDK_FONT_FONT) ? + font_name_hash : fontset_name_hash; + + if (!hash) + return NULL; + else + { + result = g_hash_table_lookup (hash, font_name); + if (result) + gdk_font_ref (result); + + return result; + } +} + +GdkFont* +gdk_font_load (const gchar *font_name) +{ + GdkFont *font; + GdkFontPrivate *private; + XFontStruct *xfont; + + g_return_val_if_fail (font_name != NULL, NULL); + + font = gdk_font_hash_lookup (GDK_FONT_FONT, font_name); + if (font) + return font; + + xfont = XLoadQueryFont (gdk_display, font_name); + if (xfont == NULL) + return NULL; + + font = gdk_font_lookup (xfont->fid); + if (font != NULL) + { + private = (GdkFontPrivate *) font; + if (xfont != private->xfont) + XFreeFont (gdk_display, xfont); + + gdk_font_ref (font); + } + else + { + private = g_new (GdkFontPrivate, 1); + private->xdisplay = gdk_display; + private->xfont = xfont; + private->ref_count = 1; + private->names = NULL; + + font = (GdkFont*) private; + font->type = GDK_FONT_FONT; + font->ascent = xfont->ascent; + font->descent = xfont->descent; + + gdk_xid_table_insert (&xfont->fid, font); + } + + gdk_font_hash_insert (GDK_FONT_FONT, font, font_name); + + return font; +} + +GdkFont* +gdk_fontset_load (const gchar *fontset_name) +{ + GdkFont *font; + GdkFontPrivate *private; + XFontSet fontset; + gint missing_charset_count; + gchar **missing_charset_list; + gchar *def_string; + + font = gdk_font_hash_lookup (GDK_FONT_FONTSET, fontset_name); + if (font) + return font; + + private = g_new (GdkFontPrivate, 1); + font = (GdkFont*) private; + + private->xdisplay = gdk_display; + fontset = XCreateFontSet (gdk_display, fontset_name, + &missing_charset_list, &missing_charset_count, + &def_string); + + if (missing_charset_count) + { + gint i; + g_warning ("Missing charsets in FontSet creation\n"); + for (i=0;i<missing_charset_count;i++) + g_warning (" %s\n", missing_charset_list[i]); + XFreeStringList (missing_charset_list); + } + + private->ref_count = 1; + + if (!fontset) + { + g_free (font); + return NULL; + } + else + { + gint num_fonts; + gint i; + XFontStruct **font_structs; + gchar **font_names; + + private->xfont = fontset; + font->type = GDK_FONT_FONTSET; + num_fonts = XFontsOfFontSet (fontset, &font_structs, &font_names); + + font->ascent = font->descent = 0; + + for (i = 0; i < num_fonts; i++) + { + font->ascent = MAX (font->ascent, font_structs[i]->ascent); + font->descent = MAX (font->descent, font_structs[i]->descent); + } + + private->names = NULL; + gdk_font_hash_insert (GDK_FONT_FONTSET, font, fontset_name); + + return font; + } +} + +GdkFont* +gdk_font_ref (GdkFont *font) +{ + GdkFontPrivate *private; + + g_return_val_if_fail (font != NULL, NULL); + + private = (GdkFontPrivate*) font; + private->ref_count += 1; + return font; +} + +void +gdk_font_unref (GdkFont *font) +{ + GdkFontPrivate *private; + private = (GdkFontPrivate*) font; + + g_return_if_fail (font != NULL); + g_return_if_fail (private->ref_count > 0); + + private->ref_count -= 1; + if (private->ref_count == 0) + { + gdk_font_hash_remove (font->type, font); + + switch (font->type) + { + case GDK_FONT_FONT: + gdk_xid_table_remove (((XFontStruct *) private->xfont)->fid); + XFreeFont (private->xdisplay, (XFontStruct *) private->xfont); + break; + case GDK_FONT_FONTSET: + XFreeFontSet (private->xdisplay, (XFontSet) private->xfont); + break; + default: + g_error ("unknown font type."); + break; + } + g_free (font); + } +} + +gint +gdk_font_id (const GdkFont *font) +{ + const GdkFontPrivate *font_private; + + g_return_val_if_fail (font != NULL, 0); + + font_private = (const GdkFontPrivate*) font; + + if (font->type == GDK_FONT_FONT) + { + return ((XFontStruct *) font_private->xfont)->fid; + } + else + { + return 0; + } +} + +gboolean +gdk_font_equal (const GdkFont *fonta, + const GdkFont *fontb) +{ + const GdkFontPrivate *privatea; + const GdkFontPrivate *privateb; + + g_return_val_if_fail (fonta != NULL, FALSE); + g_return_val_if_fail (fontb != NULL, FALSE); + + privatea = (const GdkFontPrivate*) fonta; + privateb = (const GdkFontPrivate*) fontb; + + if (fonta->type == GDK_FONT_FONT && fontb->type == GDK_FONT_FONT) + { + return (((XFontStruct *) privatea->xfont)->fid == + ((XFontStruct *) privateb->xfont)->fid); + } + else if (fonta->type == GDK_FONT_FONTSET && fontb->type == GDK_FONT_FONTSET) + { + gchar *namea, *nameb; + + namea = XBaseFontNameListOfFontSet((XFontSet) privatea->xfont); + nameb = XBaseFontNameListOfFontSet((XFontSet) privateb->xfont); + + return (strcmp(namea, nameb) == 0); + } + else + /* fontset != font */ + return FALSE; +} + +gint +gdk_string_width (GdkFont *font, + const gchar *string) +{ + GdkFontPrivate *font_private; + gint width; + XFontStruct *xfont; + XFontSet fontset; + + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (string != NULL, -1); + + font_private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + xfont = (XFontStruct *) font_private->xfont; + if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0)) + { + width = XTextWidth (xfont, string, strlen (string)); + } + else + { + width = XTextWidth16 (xfont, (XChar2b *) string, strlen (string) / 2); + } + break; + case GDK_FONT_FONTSET: + fontset = (XFontSet) font_private->xfont; + width = XmbTextEscapement (fontset, string, strlen(string)); + break; + default: + width = 0; + } + + return width; +} + +gint +gdk_text_width (GdkFont *font, + const gchar *text, + gint text_length) +{ + GdkFontPrivate *private; + gint width; + XFontStruct *xfont; + XFontSet fontset; + + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (text != NULL, -1); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + xfont = (XFontStruct *) private->xfont; + if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0)) + { + width = XTextWidth (xfont, text, text_length); + } + else + { + width = XTextWidth16 (xfont, (XChar2b *) text, text_length / 2); + } + break; + case GDK_FONT_FONTSET: + fontset = (XFontSet) private->xfont; + width = XmbTextEscapement (fontset, text, text_length); + break; + default: + width = 0; + } + return width; +} + +gint +gdk_text_width_wc (GdkFont *font, + const GdkWChar *text, + gint text_length) +{ + GdkFontPrivate *private; + gint width; + XFontSet fontset; + + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (text != NULL, -1); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + { + gchar *glyphs; + int glyphs_len; + + if (_gdk_font_wc_to_glyphs (font, text, text_length, + &glyphs, &glyphs_len)) + { + width = gdk_text_width (font, glyphs, glyphs_len); + g_free (glyphs); + } + else + width = 0; + + break; + } + case GDK_FONT_FONTSET: + if (sizeof(GdkWChar) == sizeof(wchar_t)) + { + fontset = (XFontSet) private->xfont; + width = XwcTextEscapement (fontset, (wchar_t *)text, text_length); + } + else + { + wchar_t *text_wchar; + gint i; + fontset = (XFontSet) private->xfont; + text_wchar = g_new(wchar_t, text_length); + for (i=0; i<text_length; i++) text_wchar[i] = text[i]; + width = XwcTextEscapement (fontset, text_wchar, text_length); + g_free (text_wchar); + } + break; + default: + width = 0; + } + return width; +} + +/* Problem: What if a character is a 16 bits character ?? */ +gint +gdk_char_width (GdkFont *font, + gchar character) +{ + GdkFontPrivate *private; + XCharStruct *chars; + gint width; + guint ch = character & 0xff; /* get rid of sign-extension */ + XFontStruct *xfont; + XFontSet fontset; + + g_return_val_if_fail (font != NULL, -1); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + /* only 8 bits characters are considered here */ + xfont = (XFontStruct *) private->xfont; + if ((xfont->min_byte1 == 0) && + (xfont->max_byte1 == 0) && + (ch >= xfont->min_char_or_byte2) && + (ch <= xfont->max_char_or_byte2)) + { + chars = xfont->per_char; + if (chars) + width = chars[ch - xfont->min_char_or_byte2].width; + else + width = xfont->min_bounds.width; + } + else + { + width = XTextWidth (xfont, &character, 1); + } + break; + case GDK_FONT_FONTSET: + fontset = (XFontSet) private->xfont; + width = XmbTextEscapement (fontset, &character, 1) ; + break; + default: + width = 0; + } + return width; +} + +gint +gdk_char_width_wc (GdkFont *font, + GdkWChar character) +{ + GdkFontPrivate *private; + gint width; + XFontSet fontset; + + g_return_val_if_fail (font != NULL, -1); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: +#ifdef USE_NATIVE_LOCALE + if (MB_CUR_MAX == 1 && FONT_IS_8BIT(private)) + { + char c; + g_assert (wctomb(&c,character) == 1); + + return gdk_char_width (font, c); + } + else +#endif /* USE_NATIVE_LOCALE */ + { + gchar *glyphs; + int glyphs_len; + + if (_gdk_font_wc_to_glyphs (font, &character, 1, &glyphs, &glyphs_len)) + { + width = gdk_text_width (font, glyphs, glyphs_len); + g_free (glyphs); + } + else + width = 0; + + break; + } + case GDK_FONT_FONTSET: + fontset = (XFontSet) private->xfont; + { + wchar_t char_wc = character; + width = XwcTextEscapement (fontset, &char_wc, 1) ; + } + break; + default: + width = 0; + } + return width; +} + +gint +gdk_string_measure (GdkFont *font, + const gchar *string) +{ + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (string != NULL, -1); + + return gdk_text_measure (font, string, strlen (string)); +} + +void +gdk_text_extents (GdkFont *font, + const gchar *text, + gint text_length, + gint *lbearing, + gint *rbearing, + gint *width, + gint *ascent, + gint *descent) +{ + GdkFontPrivate *private; + XCharStruct overall; + XFontStruct *xfont; + XFontSet fontset; + XRectangle ink, logical; + int direction; + int font_ascent; + int font_descent; + + g_return_if_fail (font != NULL); + g_return_if_fail (text != NULL); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + xfont = (XFontStruct *) private->xfont; + if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0)) + { + XTextExtents (xfont, text, text_length, + &direction, &font_ascent, &font_descent, + &overall); + } + else + { + XTextExtents16 (xfont, (XChar2b *) text, text_length / 2, + &direction, &font_ascent, &font_descent, + &overall); + } + if (lbearing) + *lbearing = overall.lbearing; + if (rbearing) + *rbearing = overall.rbearing; + if (width) + *width = overall.width; + if (ascent) + *ascent = overall.ascent; + if (descent) + *descent = overall.descent; + break; + case GDK_FONT_FONTSET: + fontset = (XFontSet) private->xfont; + XmbTextExtents (fontset, text, text_length, &ink, &logical); + if (lbearing) + *lbearing = ink.x; + if (rbearing) + *rbearing = ink.x + ink.width; + if (width) + *width = logical.width; + if (ascent) + *ascent = -ink.y; + if (descent) + *descent = ink.y + ink.height; + break; + } + +} + +void +gdk_text_extents_wc (GdkFont *font, + const GdkWChar *text, + gint text_length, + gint *lbearing, + gint *rbearing, + gint *width, + gint *ascent, + gint *descent) +{ + GdkFontPrivate *private; + XFontSet fontset; + XRectangle ink, logical; + + g_return_if_fail (font != NULL); + g_return_if_fail (text != NULL); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + { + gchar *glyphs; + int glyphs_len; + + if (_gdk_font_wc_to_glyphs (font, text, text_length, + &glyphs, &glyphs_len)) + { + gdk_text_extents (font, glyphs, glyphs_len, + lbearing, rbearing, width, ascent, descent); + g_free (glyphs); + } + else + { + if (lbearing) + *lbearing = 0; + if (rbearing) + *rbearing = 0; + if (width) + *width = 0; + if (ascent) + *ascent = 0; + if (descent) + *descent = 0; + } + + break; + } + case GDK_FONT_FONTSET: + fontset = (XFontSet) private->xfont; + + if (sizeof(GdkWChar) == sizeof(wchar_t)) + XwcTextExtents (fontset, (wchar_t *)text, text_length, &ink, &logical); + else + { + wchar_t *text_wchar; + gint i; + + text_wchar = g_new (wchar_t, text_length); + for (i = 0; i < text_length; i++) + text_wchar[i] = text[i]; + XwcTextExtents (fontset, text_wchar, text_length, &ink, &logical); + g_free (text_wchar); + } + if (lbearing) + *lbearing = ink.x; + if (rbearing) + *rbearing = ink.x + ink.width; + if (width) + *width = logical.width; + if (ascent) + *ascent = -ink.y; + if (descent) + *descent = ink.y + ink.height; + break; + } + +} + +void +gdk_string_extents (GdkFont *font, + const gchar *string, + gint *lbearing, + gint *rbearing, + gint *width, + gint *ascent, + gint *descent) +{ + g_return_if_fail (font != NULL); + g_return_if_fail (string != NULL); + + gdk_text_extents (font, string, strlen (string), + lbearing, rbearing, width, ascent, descent); +} + + +gint +gdk_text_measure (GdkFont *font, + const gchar *text, + gint text_length) +{ + GdkFontPrivate *private; + XCharStruct overall; + XFontStruct *xfont; + XFontSet fontset; + XRectangle ink, log; + int direction; + int font_ascent; + int font_descent; + gint width; + + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (text != NULL, -1); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + xfont = (XFontStruct *) private->xfont; + if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0)) + { + XTextExtents (xfont, text, text_length, + &direction, &font_ascent, &font_descent, + &overall); + } + else + { + XTextExtents16 (xfont, (XChar2b *) text, text_length / 2, + &direction, &font_ascent, &font_descent, + &overall); + } + width = overall.rbearing; + break; + case GDK_FONT_FONTSET: + fontset = (XFontSet) private->xfont; + XmbTextExtents (fontset, text, text_length, &ink, &log); + width = ink.x + ink.width; + break; + default: + width = 0; + } + return width; +} + +gint +gdk_char_measure (GdkFont *font, + gchar character) +{ + g_return_val_if_fail (font != NULL, -1); + + return gdk_text_measure (font, &character, 1); +} + +gint +gdk_string_height (GdkFont *font, + const gchar *string) +{ + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (string != NULL, -1); + + return gdk_text_height (font, string, strlen (string)); +} + +gint +gdk_text_height (GdkFont *font, + const gchar *text, + gint text_length) +{ + GdkFontPrivate *private; + XCharStruct overall; + XFontStruct *xfont; + XFontSet fontset; + XRectangle ink, log; + int direction; + int font_ascent; + int font_descent; + gint height; + + g_return_val_if_fail (font != NULL, -1); + g_return_val_if_fail (text != NULL, -1); + + private = (GdkFontPrivate*) font; + + switch (font->type) + { + case GDK_FONT_FONT: + xfont = (XFontStruct *) private->xfont; + if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0)) + { + XTextExtents (xfont, text, text_length, + &direction, &font_ascent, &font_descent, + &overall); + } + else + { + XTextExtents16 (xfont, (XChar2b *) text, text_length / 2, + &direction, &font_ascent, &font_descent, + &overall); + } + height = overall.ascent + overall.descent; + break; + case GDK_FONT_FONTSET: + fontset = (XFontSet) private->xfont; + XmbTextExtents (fontset, text, text_length, &ink, &log); + height = log.height; + break; + default: + height = 0; + } + return height; +} + +gint +gdk_char_height (GdkFont *font, + gchar character) +{ + g_return_val_if_fail (font != NULL, -1); + + return gdk_text_height (font, &character, 1); +} + +gboolean +_gdk_font_wc_to_glyphs (GdkFont *font, + const GdkWChar *text, + gint text_length, + gchar **result, + gint *result_length) +{ + XFontStruct *xfont; + GdkFontPrivate *font_private = (GdkFontPrivate*) font; + + g_return_val_if_fail (font != NULL, FALSE); + g_return_val_if_fail (font->type == GDK_FONT_FONT, FALSE); + + xfont = (XFontStruct *) font_private->xfont; + + if (FONT_IS_8BIT (font_private)) + { + /* 8-bit font, assume that we are in a 8-bit locale, + * and convert to bytes using wcstombs. + */ + char *mbstr = _gdk_wcstombs_len (text, text_length); + + if (result_length) + *result_length = mbstr ? strlen (mbstr) : 0; + + if (result) + *result = mbstr; + else + g_free (mbstr); + + return mbstr != NULL; + } + else + { + /* 16-bit font. Who knows what was intended? Make a random + * guess. + */ + XChar2b *result2b = g_new (XChar2b, text_length + 1); + gint i; + + for (i = 0; i < text_length; i++) + { + result2b[i].byte1 = text[i] / 256; + result2b[i].byte2 = text[i] % 256; + } + + result2b[i].byte1 = result2b[i].byte2 = 0; + + if (result) + *result = (gchar *)result2b; + + if (result_length) + *result_length = text_length; + + return TRUE; + } +} diff -ruN gtk+-1.2.10/gdk/gdki18n.h gtk-n/gdk/gdki18n.h --- gtk+-1.2.10/gdk/gdki18n.h Mon Jan 24 03:58:21 2000 +++ gtk-n/gdk/gdki18n.h Thu Jan 31 08:56:46 2002 @@ -51,4 +51,32 @@ # define gdk_iswspace(c) ((wchar_t)(c) <= 0xFF && isspace(c)) #endif +/* The following 9 macros are added in gtk+ 1.2.X. Don't use them without + * checking GTK_CHECK_VERSION. For example, + * #if GTK_CHECK_VERSION (1,2,X) + * ... code which uses gdk_iswalpha(), gdk_iswcntrl(), etc. ... + * #endif + */ +#if !defined(G_HAVE_BROKEN_WCTYPE) && (defined(G_HAVE_WCTYPE_H) || defined(G_HAVE_WCHAR_H)) && !defined(X_LOCALE) +# define gdk_iswalpha(c) iswalpha(c) +# define gdk_iswcntrl(c) iswcntrl(c) +# define gdk_iswdigit(c) iswdigit(c) +# define gdk_iswlower(c) iswlower(c) +# define gdk_iswgraph(c) iswgraph(c) +# define gdk_iswprint(c) iswprint(c) +# define gdk_iswpunct(c) iswpunct(c) +# define gdk_iswupper(c) iswupper(c) +# define gdk_iswxdigit(c) iswxdigit(c) +#else +# define gdk_iswalpha(c) ((wchar_t)(c) <= 0xFF && isalpha(c)) +# define gdk_iswcntrl(c) ((wchar_t)(c) <= 0xFF && iscntrl(c)) +# define gdk_iswdigit(c) ((wchar_t)(c) <= 0xFF && isdigit(c)) +# define gdk_iswlower(c) ((wchar_t)(c) <= 0xFF && islower(c)) +# define gdk_iswgraph(c) ((wchar_t)(c) > 0xFF || isgraph(c)) +# define gdk_iswprint(c) ((wchar_t)(c) > 0xFF || isprint(c)) +# define gdk_iswpunct(c) ((wchar_t)(c) <= 0xFF && ispunct(c)) +# define gdk_iswupper(c) ((wchar_t)(c) <= 0xFF && isupper(c)) +# define gdk_iswxdigit(c) ((wchar_t)(c) <= 0xFF && isxdigit(c)) +#endif + #endif /* __GDK_I18N_H__ */ diff -ruN gtk+-1.2.10/gdk/gdki18n.h.orig gtk-n/gdk/gdki18n.h.orig --- gtk+-1.2.10/gdk/gdki18n.h.orig Thu Jan 1 01:00:00 1970 +++ gtk-n/gdk/gdki18n.h.orig Mon Jan 24 03:58:21 2000 @@ -0,0 +1,54 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. 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/. + */ + +#ifndef __GDK_I18N_H__ +#define __GDK_I18N_H__ + +/* GDK uses "glib". (And so does GTK). + */ +#include <glib.h> + +/* international string support */ + +#include <stdlib.h> + +#if !defined(G_HAVE_BROKEN_WCTYPE) && (defined(G_HAVE_WCTYPE_H) || defined(G_HAVE_WCHAR_H)) && !defined(X_LOCALE) +# ifdef G_HAVE_WCTYPE_H +# include <wctype.h> +# else +# ifdef G_HAVE_WCHAR_H +# include <wchar.h> +# endif +# endif +# define gdk_iswalnum(c) iswalnum(c) +# define gdk_iswspace(c) iswspace(c) +#else +# include <ctype.h> +# define gdk_iswalnum(c) ((wchar_t)(c) <= 0xFF && isalnum(c)) +# define gdk_iswspace(c) ((wchar_t)(c) <= 0xFF && isspace(c)) +#endif + +#endif /* __GDK_I18N_H__ */ diff -ruN gtk+-1.2.10/gdk/gdkselection.c gtk-n/gdk/gdkselection.c --- gtk+-1.2.10/gdk/gdkselection.c Thu Mar 15 20:09:36 2001 +++ gtk-n/gdk/gdkselection.c Thu Jan 31 08:56:28 2002 @@ -191,73 +191,6 @@ gdk_send_xevent (requestor, False, NoEventMask, (XEvent*) &xevent); } - -/* The output of XmbTextPropertyToTextList may include stuff not valid - * for COMPOUND_TEXT. This routine tries to correct this by: - * - * a) Canonicalizing CR LF and CR to LF - * b) Stripping out all other non-allowed control characters - * - * See the COMPOUND_TEXT spec distributed with X for explanations - * what is allowed. - */ -static gchar * -sanitize_ctext (const char *str, - gint *length) -{ - gchar *result = g_malloc (*length + 1); - gint out_length = 0; - gint i; - const guchar *ustr = (const guchar *)str; - - for (i=0; i < *length; i++) - { - guchar c = ustr[i]; - - if (c == '\r') - { - result[out_length++] = '\n'; - if (i + 1 < *length && ustr[i + 1] == '\n') - i++; - } - else if (c == 27 /* ESC */) - { - /* Check for "extended segments, which can contain arbitrary - * octets. See CTEXT spec, section 6. - */ - - if (i + 5 < *length && - ustr[i + 1] == '%' && - ustr[i + 2] == '/' && - (ustr[i + 3] >= 48 && ustr[i + 3] <= 52) && - ustr[i + 4] >= 128 && - ustr[i + 5] >= 128) - { - int extra_len = 6 + (ustr[i + 4] - 128) * 128 + ustr[i + 5] - 128; - extra_len = MAX (extra_len, *length - i); - - memcpy (result + out_length, ustr + i, extra_len); - out_length += extra_len; - i += extra_len - 1; - } - else - result[out_length++] = c; - } - else if (c == '\n' || c == '\t' || c == 27 /* ESC */ || - (c >= 32 && c <= 127) || /* GL */ - c == 155 /* CONTROL SEQUENCE INTRODUCER */ || - (c >= 160 && c <= 255)) /* GR */ - { - result[out_length++] = c; - } - } - - result[out_length] = '\0'; - *length = out_length; - - return result; -} - gint gdk_text_property_to_text_list (GdkAtom encoding, gint format, guchar *text, gint length, @@ -266,32 +199,16 @@ XTextProperty property; gint count = 0; gint res; - gchar *sanitized_text = NULL; if (!list) return 0; property.encoding = encoding; property.format = format; - - if (encoding == gdk_atom_intern ("COMPOUND_TEXT", FALSE) && format == 8) - { - gint sanitized_text_length = length; - - property.value = sanitized_text = sanitize_ctext (text, &sanitized_text_length); - property.nitems = sanitized_text_length; - } - else - { - property.value = text; - property.nitems = length; - } - + property.value = text; + property.nitems = length; res = XmbTextPropertyToTextList (GDK_DISPLAY(), &property, list, &count); - if (sanitized_text) - g_free (sanitized_text); - if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound) return 0; @@ -314,8 +231,6 @@ { gint res; XTextProperty property; - gint sanitized_text_length; - gchar *sanitized_text; res = XmbTextListToTextProperty (GDK_DISPLAY(), (char **)&str, 1, XCompoundTextStyle, @@ -334,17 +249,10 @@ *encoding = property.encoding; if (format) *format = property.format; - - sanitized_text_length = property.nitems; - sanitized_text = sanitize_ctext (property.value, &sanitized_text_length); - if (ctext) - *ctext = sanitized_text; - else - g_free (sanitized_text); - + *ctext = g_strdup (property.value); if (length) - *length = sanitized_text_length; + *length = property.nitems; if (property.value) XFree (property.value); diff -ruN gtk+-1.2.10/gtk/Makefile.am gtk-n/gtk/Makefile.am --- gtk+-1.2.10/gtk/Makefile.am Fri Feb 2 18:09:02 2001 +++ gtk-n/gtk/Makefile.am Thu Jan 31 08:56:58 2002 @@ -374,10 +374,12 @@ gtkconfdir = $(sysconfdir)/gtk -gtkconf_DATA = gtkrc.az gtkrc.el gtkrc.eo gtkrc.he gtkrc.hy gtkrc.ja \ - gtkrc.ko gtkrc.ru gtkrc.tr gtkrc.th gtkrc.uk gtkrc.iso-8859-2 \ - gtkrc.iso-8859-5 gtkrc.iso-8859-13 gtkrc.iso-8859-14 \ - gtkrc.iso-8859-15 gtkrc.zh_CN gtkrc.zh_TW.big5 \ +gtkconf_DATA = gtkrc.az gtkrc.he gtkrc.hy gtkrc.ja \ + gtkrc.ko gtkrc.ru gtkrc.th gtkrc.uk \ + gtkrc.utf8 gtkrc.iso88592 \ + gtkrc.iso88593 gtkrc.iso88595 gtkrc.iso88597 \ + gtkrc.iso88599 gtkrc.iso885913 gtkrc.iso885914 \ + gtkrc.iso885915 gtkrc.zh_CN gtkrc.zh_TW.big5 \ gtkrc.ka_GE.georgianacademy gtkrc.ka_GE.georgianps \ gtkrc.vi_VN.tcvn gtkrc.vi_VN.viscii gtkrc.cp1251 gtkrc.cp1255 @@ -390,11 +392,11 @@ cd $(DESTDIR)$(gtkconfdir) && \ for i in cs hr hu pl ro sk sl sq sr ; do \ rm -f gtkrc.$$i ; \ - ln -s gtkrc.iso-8859-2 gtkrc.$$i ; \ + ln -s gtkrc.iso88592 gtkrc.$$i ; \ done ; \ for i in bg_BG.iso88595 mk sp ru_RU.iso88595 ; do \ rm -f gtkrc.$$i ; \ - ln -s gtkrc.iso-8859-5 gtkrc.$$i ; \ + ln -s gtkrc.iso88595 gtkrc.$$i ; \ done ; \ for i in he_IL.cp1255 he_IL.microsoftcp1255 yi ; do \ rm -f gtkrc.$$i ; \ @@ -403,12 +405,12 @@ rm -f gtkrc.lt gtkrc.lv gtkrc.cy gtkrc.ga gtkrc.et gtkrc.ka \ gtkrc.vi_VN.viscii111 gtkrc.vi_VN.tcvn5712 gtkrc.vi \ gtkrc.be gtkrc.bg gtkrc.mi ; \ - ln -s gtkrc.iso-8859-13 gtkrc.mi ; \ - ln -s gtkrc.iso-8859-13 gtkrc.lt ; \ - ln -s gtkrc.iso-8859-13 gtkrc.lv ; \ - ln -s gtkrc.iso-8859-14 gtkrc.cy ; \ - ln -s gtkrc.iso-8859-14 gtkrc.ga ; \ - ln -s gtkrc.iso-8859-15 gtkrc.et ; \ + ln -s gtkrc.iso885913 gtkrc.mi ; \ + ln -s gtkrc.iso885913 gtkrc.lt ; \ + ln -s gtkrc.iso885913 gtkrc.lv ; \ + ln -s gtkrc.iso885914 gtkrc.cy ; \ + ln -s gtkrc.iso885914 gtkrc.ga ; \ + ln -s gtkrc.iso885915 gtkrc.et ; \ ln -s gtkrc.ka_GE.georgianps gtkrc.ka ; \ ln -s gtkrc.vi_VN.viscii gtkrc.vi_VN.viscii111 ; \ ln -s gtkrc.vi_VN.tcvn gtkrc.vi ; \ diff -ruN gtk+-1.2.10/gtk/gtkentry.c gtk-n/gtk/gtkentry.c --- gtk+-1.2.10/gtk/gtkentry.c Mon Apr 2 04:14:54 2001 +++ gtk-n/gtk/gtkentry.c Thu Jan 31 08:57:05 2002 @@ -1184,6 +1184,7 @@ } break; case GDK_Return: + case GDK_KP_Enter: return_val = TRUE; gtk_widget_activate (widget); break; @@ -2036,11 +2037,21 @@ } } +static gboolean +alnum_or_ideogram (GtkEntry *entry, guint index) +{ + GdkWChar ch; + ch = entry->text[index]; + if (entry->use_wchar) + return !(gdk_iswpunct (ch) || gdk_iswcntrl (ch) || gdk_iswspace (ch)); + else + return !(ispunct (ch) || iscntrl (ch) || isspace (ch)); +} + static void gtk_move_forward_word (GtkEntry *entry) { GtkEditable *editable; - GdkWChar *text; gint i; editable = GTK_EDITABLE (entry); @@ -2054,21 +2065,12 @@ 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; - } - + for (i = editable->current_pos; i < entry->text_length; i++) + if (alnum_or_ideogram (entry, i)) + break; for (; i < entry->text_length; i++) - { - if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) - break; - } + if (!alnum_or_ideogram (entry, i)) + break; editable->current_pos = i; } @@ -2078,7 +2080,6 @@ gtk_move_backward_word (GtkEntry *entry) { GtkEditable *editable; - GdkWChar *text; gint i; editable = GTK_EDITABLE (entry); @@ -2092,26 +2093,19 @@ if (entry->text && editable->current_pos > 0) { - text = entry->text; - i = editable->current_pos - 1; - if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) - for (; i >= 0; i--) + for (i = editable->current_pos - 1; i >= 0; i--) + if (alnum_or_ideogram (entry, i)) + break; + for (; i >= 0; i--) + if (!alnum_or_ideogram (entry, i)) { - if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i])) - break; + 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; } } diff -ruN gtk+-1.2.10/gtk/gtkentry.c.orig gtk-n/gtk/gtkentry.c.orig --- gtk+-1.2.10/gtk/gtkentry.c.orig Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkentry.c.orig Mon Apr 2 04:14:54 2001 @@ -0,0 +1,2408 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. 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 <ctype.h> +#include <string.h> +#include "gdk/gdkkeysyms.h" +#include "gdk/gdki18n.h" +#include "gtkentry.h" +#include "gtkmain.h" +#include "gtkselection.h" +#include "gtksignal.h" +#include "gtkstyle.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 + +enum { + ARG_0, + ARG_MAX_LENGTH, + ARG_VISIBILITY +}; + + +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 (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_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); + +/* 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_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 const 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 */ +}; + +static const 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 */ +}; + + +GtkType +gtk_entry_get_type (void) +{ + static GtkType entry_type = 0; + + if (!entry_type) + { + static const GtkTypeInfo entry_info = + { + "GtkEntry", + sizeof (GtkEntry), + sizeof (GtkEntryClass), + (GtkClassInitFunc) gtk_entry_class_init, + (GtkObjectInitFunc) gtk_entry_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + entry_type = gtk_type_unique (GTK_TYPE_EDITABLE, &entry_info); + } + + return entry_type; +} + +static void +gtk_entry_class_init (GtkEntryClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkEditableClass *editable_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + editable_class = (GtkEditableClass*) class; + parent_class = gtk_type_class (GTK_TYPE_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); + + object_class->set_arg = gtk_entry_set_arg; + object_class->get_arg = gtk_entry_get_arg; + object_class->finalize = gtk_entry_finalize; + + 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->motion_notify_event = gtk_entry_motion_notify; + 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; + widget_class->state_changed = gtk_entry_state_changed; + + editable_class->insert_text = gtk_entry_insert_text; + editable_class->delete_text = gtk_entry_delete_text; + editable_class->changed = (void (*)(GtkEditable *)) 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_set_arg (GtkObject *object, + GtkArg *arg, + guint arg_id) +{ + GtkEntry *entry; + + entry = GTK_ENTRY (object); + + switch (arg_id) + { + 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; + default: + break; + } +} + +static void +gtk_entry_get_arg (GtkObject *object, + GtkArg *arg, + guint arg_id) +{ + GtkEntry *entry; + + entry = GTK_ENTRY (object); + + switch (arg_id) + { + case ARG_MAX_LENGTH: + GTK_VALUE_UINT (*arg) = entry->text_max_length; + break; + case ARG_VISIBILITY: + GTK_VALUE_BOOL (*arg) = GTK_EDITABLE (entry)->visible; + break; + default: + arg->type = GTK_TYPE_INVALID; + break; + } +} + +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->char_offset = NULL; + entry->text_mb = NULL; + entry->text_mb_dirty = TRUE; + entry->use_wchar = FALSE; + + gtk_entry_grow_text (entry); +} + +GtkWidget* +gtk_entry_new (void) +{ + return GTK_WIDGET (gtk_type_new (GTK_TYPE_ENTRY)); +} + +static GdkWChar +gtk_entry_get_invisible_char (GtkEntry *entry) +{ + GdkWChar ch = 0; + + if (entry->use_wchar) + gdk_mbstowcs (&ch, "*", 1); + else + ch = '*'; + + return ch; +} + +/* + * Draws the string, noting that if entry->use_wchar is false, then + * the text is not really wide characters, but narrow characters + * stored as wide characters. + */ +static void +gtk_entry_draw_wchars (GtkEntry *entry, + GdkDrawable *drawable, + GdkFont *font, + GdkGC *gc, + gint x, + gint y, + const GdkWChar *text, + gint text_length) +{ + if (entry->use_wchar) + gdk_draw_text_wc (drawable, font, gc, x, y, text, text_length); + else + { + gint i; + gchar *mbstr = g_new (gchar, text_length); + + for (i = 0; i < text_length; i++) + mbstr[i] = text[i]; + gdk_draw_text (drawable, font, gc, x, y, mbstr, text_length); + g_free(mbstr); + } +} + +GtkWidget* +gtk_entry_new_with_max_length (guint16 max) +{ + GtkEntry *entry; + + entry = gtk_type_new (GTK_TYPE_ENTRY); + entry->text_max_length = max; + + return GTK_WIDGET (entry); +} + +void +gtk_entry_set_text (GtkEntry *entry, + const gchar *text) +{ + gint tmp_pos; + + GtkEditable *editable; + + 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_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; + + if (GTK_WIDGET_DRAWABLE (entry)) + gtk_entry_draw_text (entry); +} + +void +gtk_entry_append_text (GtkEntry *entry, + const gchar *text) +{ + 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 = entry->text_length; + gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos); + GTK_EDITABLE(entry)->current_pos = tmp_pos; +} + +void +gtk_entry_prepend_text (GtkEntry *entry, + const gchar *text) +{ + 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; +} + +void +gtk_entry_set_position (GtkEntry *entry, + gint position) +{ + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if ((position == -1) || (position > entry->text_length)) + GTK_EDITABLE(entry)->current_pos = entry->text_length; + else + GTK_EDITABLE(entry)->current_pos = position; + entry_adjust_scroll (entry); +} + +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) +{ + 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)); +} + +void +gtk_entry_set_editable(GtkEntry *entry, + gboolean editable) +{ + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + + gtk_editable_set_editable (GTK_EDITABLE (entry), editable); +} + +gchar* +gtk_entry_get_text (GtkEntry *entry) +{ + g_return_val_if_fail (entry != NULL, NULL); + g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); + + 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; +} + +static void +gtk_entry_finalize (GtkObject *object) +{ + GtkEntry *entry; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_ENTRY (object)); + + entry = GTK_ENTRY (object); + + if (entry->timer) + gtk_timeout_remove (entry->timer); + + entry->text_size = 0; + + 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); + + (* GTK_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +gtk_entry_realize (GtkWidget *widget) +{ + GtkEntry *entry; + GtkEditable *editable; + GtkRequisition requisition; + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ENTRY (widget)); + + 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 - + requisition.height) / 2; + attributes.width = widget->allocation.width; + attributes.height = requisition.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_BUTTON_PRESS_MASK | + 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); + 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; + attributes.y = widget->style->klass->ythickness; + attributes.width = widget->allocation.width - attributes.x * 2; + attributes.height = requisition.height - attributes.y * 2; + attributes.cursor = entry->cursor = gdk_cursor_new (GDK_XTERM); + attributes_mask |= GDK_WA_CURSOR; + + entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (entry->text_area, entry); + + widget->style = gtk_style_attach (widget->style, widget->window); + + gdk_window_set_background (widget->window, &widget->style->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); +} + +static void +gtk_entry_unrealize (GtkWidget *widget) +{ + GtkEntry *entry; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ENTRY (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 + + 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 (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; + } + + 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 (GTK_EDITABLE (widget)->editable) + gtk_entry_draw_cursor (GTK_ENTRY (widget)); + } +} + +static void +gtk_entry_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + 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); +} + +static void +gtk_entry_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkEntry *entry; + GtkEditable *editable; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ENTRY (widget)); + g_return_if_fail (allocation != NULL); + + widget->allocation = *allocation; + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + /* We call gtk_widget_get_child_requisition, since we want (for + * backwards compatibility reasons) the realization here to + * be affected by the usize of the entry, if set + */ + GtkRequisition requisition; + gtk_widget_get_child_requisition (widget, &requisition); + + gdk_window_move_resize (widget->window, + allocation->x, + allocation->y + (allocation->height - requisition.height) / 2, + allocation->width, requisition.height); + gdk_window_move_resize (entry->text_area, + widget->style->klass->xthickness, + widget->style->klass->ythickness, + 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 + } +} + +static void +gtk_entry_draw (GtkWidget *widget, + GdkRectangle *area) +{ + 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)); + } +} + +static gint +gtk_entry_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkEntry *entry; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_ENTRY (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + entry = GTK_ENTRY (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)); + + return FALSE; +} + +static gint +gtk_entry_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + GtkEntry *entry; + GtkEditable *editable; + 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)) + return FALSE; + + entry->button = event->button; + + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + + 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; + break; + + case GDK_2BUTTON_PRESS: + gtk_select_word (entry, event->time); + break; + + case GDK_3BUTTON_PRESS: + gtk_select_line (entry, event->time); + break; + + default: + break; + } + + return TRUE; + } + else if (event->type == GDK_BUTTON_PRESS) + { + 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); + + 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; + + if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window) + gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time); + } + + return TRUE; + } + + return FALSE; +} + +static gint +gtk_entry_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GtkEntry *entry; + GtkEditable *editable; + + 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) + 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); + } + + return TRUE; + } + else if (event->button == 3) + { + gtk_grab_remove (widget); + + return TRUE; + } + + return FALSE; +} + +static gint +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); + + if (entry->button == 0) + return FALSE; + + x = event->x; + 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)->current_pos = GTK_EDITABLE(entry)->selection_end_pos; + entry_adjust_scroll (entry); + gtk_entry_queue_draw (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; + + if(editable->editable == FALSE) + 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) + { + 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 (!extend_selection && + editable->selection_start_pos != editable->selection_end_pos) + { + editable->current_pos = MIN (editable->selection_start_pos, editable->selection_end_pos); + initial_pos = (guint)-1; /* Force redraw below */ + } + else + { + 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 (!extend_selection && + editable->selection_start_pos != editable->selection_end_pos) + { + editable->current_pos = MAX (editable->selection_start_pos, editable->selection_end_pos); + initial_pos = (guint)-1; /* Force redraw below */ + } + else + { + 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_widget_activate (widget); + 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; + } + + /* since we emit signals from within the above code, + * the widget might already be destroyed or at least + * unrealized. + */ + if (GTK_WIDGET_REALIZED (editable) && + return_val && (editable->current_pos != initial_pos)) + { + 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); + + entry_adjust_scroll (entry); + gtk_entry_queue_draw (entry); + } + + return return_val; +} + +static gint +gtk_entry_focus_in (GtkWidget *widget, + GdkEventFocus *event) +{ + 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); + + 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 + + return FALSE; +} + +static gint +gtk_entry_focus_out (GtkWidget *widget, + GdkEventFocus *event) +{ + 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); + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + gtk_widget_draw_focus (widget); + +#ifdef USE_XIM + gdk_im_end (); +#endif + + return FALSE; +} + +static void +gtk_entry_make_backing_pixmap (GtkEntry *entry, gint width, gint height) +{ + gint pixmap_width, pixmap_height; + + 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); + } + } +} + +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; + gint use_backing_pixmap; + GdkWChar *stars; + GdkWChar *toprint; + + 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); + + /* + 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; + } + else + { + drawable = entry->text_area; + } + gtk_paint_flat_box (widget->style, drawable, + GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE, + NULL, widget, "entry_bg", + 0, 0, width, height); + + y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2; + y += widget->style->font->ascent; + + start_pos = gtk_entry_find_position (entry, entry->scroll_offset); + start_xoffset = entry->char_offset[start_pos] - entry->scroll_offset; + + end_pos = gtk_entry_find_position (entry, entry->scroll_offset + width); + if (end_pos < entry->text_length) + end_pos += 1; + + selected_state = GTK_STATE_SELECTED; + if (!editable->has_selection) + selected_state = GTK_STATE_ACTIVE; + + 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); + + selection_start_xoffset = + entry->char_offset[selection_start_pos] - entry->scroll_offset; + selection_end_xoffset = + entry->char_offset[selection_end_pos] -entry->scroll_offset; + + /* 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; + GdkWChar invisible_char = gtk_entry_get_invisible_char (entry); + + stars = g_new (GdkWChar, end_pos - start_pos); + for (i = 0; i < end_pos - start_pos; i++) + stars[i] = invisible_char; + toprint = stars; + } + + if (selection_start_pos > start_pos) + gtk_entry_draw_wchars (entry, drawable, 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, drawable, + 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); + gtk_entry_draw_wchars (entry, drawable, 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) + gtk_entry_draw_wchars (entry, drawable, 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, 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); + } +} + +static void +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); + + 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 + { + gint yoffset = + (text_area_height - + (widget->style->font->ascent + widget->style->font->descent)) / 2 + + widget->style->font->ascent; + + gtk_paint_flat_box (widget->style, drawable, + GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE, + NULL, widget, "entry_bg", + xoffset, INNER_BORDER, + 1, text_area_height - INNER_BORDER); + + /* 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) : + '*'; + + gtk_entry_draw_wchars (entry, drawable, widget->style->font, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + xoffset, yoffset, &c, 1); + } + } + + +#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_ic_set_attr (editable->ic, + editable->ic_attr, GDK_IC_SPOT_LOCATION); + } +#endif + } +} + +static void +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); +} + +static gint +gtk_entry_timer (gpointer data) +{ + GtkEntry *entry; + + GDK_THREADS_ENTER (); + + entry = GTK_ENTRY (data); + entry->timer = 0; + gtk_entry_draw_text (entry); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +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; + + /* invariant - char_offset[start] <= x < char_offset[end] */ + + while (start != end) + { + half = (start+end)/2; + if (half == start) + return half; + else if (entry->char_offset[half] <= x) + start = half; + else + end = half; + } + + return start; +} + +static gint +gtk_entry_position (GtkEntry *entry, + gint x) +{ + return gtk_entry_find_position(entry, x); +} + +static void +entry_adjust_scroll (GtkEntry *entry) +{ + gint xoffset, max_offset; + gint text_area_width; + + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (!entry->text_area) + return; + + gdk_window_get_size (entry->text_area, &text_area_width, NULL); + text_area_width -= 2 * INNER_BORDER; + + /* Display as much text as we can */ + max_offset = MAX(0, entry->char_offset[entry->text_length] - text_area_width); + + if (entry->scroll_offset > max_offset) + entry->scroll_offset = max_offset; + + /* And make sure cursor is 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. + */ + 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; + + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + + 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)); + + if (entry->text_length == 0) /* initial allocation */ + { + entry->char_offset[0] = 0; + } + + for (i = previous_size; i < entry->text_size; i++) + entry->text[i] = '\0'; +} + +static void +gtk_entry_insert_text (GtkEditable *editable, + const gchar *new_text, + 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; + + GtkEntry *entry; + GtkWidget *widget; + + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_ENTRY (editable)); + + 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); + + /* 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) + { + g_free(insertion_text); + return; + } + + /* 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)) + { + 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++) + { + GdkWChar ch; + + entry->char_offset[i] = entry->char_offset[start_pos] + offset; + + if (editable->visible) + ch = entry->text[i]; + else + ch = gtk_entry_get_invisible_char (entry); + + if (entry->use_wchar) + offset += gdk_char_width_wc (GTK_WIDGET (entry)->style->font, ch); + else + offset += gdk_char_width (GTK_WIDGET (entry)->style->font, ch); + } + for (i = end_pos; i <= last_pos; i++) + entry->char_offset[i] += offset; + } + + entry->text_length += insertion_length; + *position = end_pos; + + entry->text_mb_dirty = 1; + gtk_entry_queue_draw (entry); +} + +/* Recompute the x offsets of all characters in the buffer */ +static void +gtk_entry_recompute_offsets (GtkEntry *entry) +{ + gint i; + gint offset = 0; + GtkEditable *editable = GTK_EDITABLE (entry); + + for (i=0; i<entry->text_length; i++) + { + GdkWChar ch; + + entry->char_offset[i] = offset; + + if (editable->visible) + ch = entry->text[i]; + else + ch = gtk_entry_get_invisible_char (entry); + + if (entry->use_wchar) + offset += gdk_char_width_wc (GTK_WIDGET (entry)->style->font, ch); + else + offset += gdk_char_width (GTK_WIDGET (entry)->style->font, ch); + } + + entry->char_offset[i] = offset; +} + +static void +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); + g_return_if_fail (GTK_IS_ENTRY (editable)); + + entry = GTK_ENTRY (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; + + /* 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; + } + + for (i = end_pos; i < entry->text_length; i++) + text[i - deletion_length] = text[i]; + + for (i = entry->text_length - deletion_length; i < entry->text_length; i++) + text[i] = '\0'; + + entry->text_length -= deletion_length; + editable->current_pos = start_pos; + } + + entry->text_mb_dirty = 1; + 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) +{ + GtkEntry *entry; + + g_return_val_if_fail (editable != NULL, NULL); + g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL); + + entry = GTK_ENTRY (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); + + 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; +} + +static void +gtk_entry_move_cursor (GtkEditable *editable, + gint x, + gint y) +{ + GtkEntry *entry; + entry = GTK_ENTRY (editable); + + /* Horizontal motion */ + if ((gint)editable->current_pos < -x) + editable->current_pos = 0; + else if (editable->current_pos + x > entry->text_length) + editable->current_pos = entry->text_length; + else + editable->current_pos += x; + + /* Ignore vertical motion */ +} + +static void +gtk_move_forward_character (GtkEntry *entry) +{ + gtk_entry_move_cursor (GTK_EDITABLE (entry), 1, 0); +} + +static void +gtk_move_backward_character (GtkEntry *entry) +{ + gtk_entry_move_cursor (GTK_EDITABLE (entry), -1, 0); +} + +static void +gtk_entry_move_word (GtkEditable *editable, + gint n) +{ + while (n > 0) + { + gtk_move_forward_word (GTK_ENTRY (editable)); + n--; + } + while (n < 0) + { + gtk_move_backward_word (GTK_ENTRY (editable)); + n++; + } +} + +static void +gtk_move_forward_word (GtkEntry *entry) +{ + GtkEditable *editable; + GdkWChar *text; + gint i; + + editable = GTK_EDITABLE (entry); + + /* Prevent any leak of information */ + if (!editable->visible) + { + editable->current_pos = entry->text_length; + return; + } + + 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; + } + + for (; i < entry->text_length; i++) + { + if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i]))) + break; + } + + editable->current_pos = i; + } +} + +static void +gtk_move_backward_word (GtkEntry *entry) +{ + GtkEditable *editable; + GdkWChar *text; + gint i; + + editable = GTK_EDITABLE (entry); + + /* Prevent any leak of information */ + if (!editable->visible) + { + editable->current_pos = 0; + return; + } + + if (entry->text && editable->current_pos > 0) + { + text = entry->text; + 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; + } +} + +static void +gtk_entry_move_to_column (GtkEditable *editable, gint column) +{ + GtkEntry *entry; + + entry = GTK_ENTRY (editable); + + if (column < 0 || column > entry->text_length) + editable->current_pos = entry->text_length; + else + editable->current_pos = column; +} + +static void +gtk_move_beginning_of_line (GtkEntry *entry) +{ + gtk_entry_move_to_column (GTK_EDITABLE (entry), 0); +} + +static void +gtk_move_end_of_line (GtkEntry *entry) +{ + gtk_entry_move_to_column (GTK_EDITABLE (entry), -1); +} + +static void +gtk_entry_kill_char (GtkEditable *editable, + gint direction) +{ + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_delete_selection (editable); + 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); + } + } +} + +static void +gtk_delete_forward_character (GtkEntry *entry) +{ + gtk_entry_kill_char (GTK_EDITABLE (entry), 1); +} + +static void +gtk_delete_backward_character (GtkEntry *entry) +{ + gtk_entry_kill_char (GTK_EDITABLE (entry), -1); +} + +static void +gtk_entry_kill_word (GtkEditable *editable, + gint direction) +{ + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_delete_selection (editable); + else + { + 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); + } + } +} + +static void +gtk_delete_forward_word (GtkEntry *entry) +{ + gtk_entry_kill_word (GTK_EDITABLE (entry), 1); +} + +static void +gtk_delete_backward_word (GtkEntry *entry) +{ + gtk_entry_kill_word (GTK_EDITABLE (entry), -1); +} + +static void +gtk_entry_kill_line (GtkEditable *editable, + gint direction) +{ + gint old_pos = editable->current_pos; + if (direction >= 0) + { + gtk_entry_move_to_column (editable, -1); + gtk_editable_delete_text (editable, old_pos, editable->current_pos); + } + else + { + gtk_entry_move_to_column (editable, 0); + gtk_editable_delete_text (editable, editable->current_pos, old_pos); + } +} + +static void +gtk_delete_line (GtkEntry *entry) +{ + gtk_entry_move_to_column (GTK_EDITABLE (entry), 0); + gtk_entry_kill_line (GTK_EDITABLE (entry), 1); +} + +static void +gtk_delete_to_line_end (GtkEntry *entry) +{ + gtk_editable_delete_text (GTK_EDITABLE(entry), GTK_EDITABLE(entry)->current_pos, entry->text_length); +} + +static void +gtk_select_word (GtkEntry *entry, + guint32 time) +{ + GtkEditable *editable; + gint start_pos; + gint end_pos; + + editable = GTK_EDITABLE (entry); + + gtk_move_backward_word (entry); + start_pos = editable->current_pos; + + gtk_move_forward_word (entry); + end_pos = editable->current_pos; + + editable->has_selection = TRUE; + gtk_entry_set_selection (editable, start_pos, end_pos); + gtk_editable_claim_selection (editable, start_pos != end_pos, time); +} + +static void +gtk_select_line (GtkEntry *entry, + guint32 time) +{ + GtkEditable *editable; + + editable = GTK_EDITABLE (entry); + + editable->has_selection = TRUE; + gtk_entry_set_selection (editable, 0, entry->text_length); + gtk_editable_claim_selection (editable, entry->text_length != 0, time); + + editable->current_pos = editable->selection_end_pos; +} + +static void +gtk_entry_set_selection (GtkEditable *editable, + gint start, + gint end) +{ + gint length = GTK_ENTRY (editable)->text_length; + + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_ENTRY (editable)); + + if (end < 0) + end = length; + + editable->selection_start_pos = CLAMP (start, 0, length); + editable->selection_end_pos = MIN (end, length); + + gtk_entry_queue_draw (GTK_ENTRY (editable)); +} + +void +gtk_entry_select_region (GtkEntry *entry, + gint start, + gint end) +{ + gtk_editable_select_region (GTK_EDITABLE (entry), start, end); +} + +void +gtk_entry_set_max_length (GtkEntry *entry, + guint16 max) +{ + g_return_if_fail (entry != NULL); + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (max && entry->text_length > max) + gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1); + entry->text_max_length = max; +} + +#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)); + + if (previous_style && GTK_WIDGET_REALIZED (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 + } +} + +static void +gtk_entry_state_changed (GtkWidget *widget, + GtkStateType previous_state) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_ENTRY (widget)); + + if (GTK_WIDGET_REALIZED (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); +} diff -ruN gtk+-1.2.10/gtk/gtklabel.c gtk-n/gtk/gtklabel.c --- gtk+-1.2.10/gtk/gtklabel.c Mon Apr 2 05:12:38 2001 +++ gtk-n/gtk/gtklabel.c Thu Jan 31 08:57:14 2002 @@ -56,6 +56,7 @@ GtkLabelWord *next; gint uline_y; GtkLabelULine *uline; + gboolean paragraph_break; }; struct _GtkLabelULine @@ -396,6 +397,7 @@ word->beginning = NULL; word->next = NULL; word->uline = NULL; + word->paragraph_break = FALSE; return word; } @@ -441,6 +443,7 @@ if (str == label->label_wc || str[-1] == '\n') { /* Paragraph break */ + word->paragraph_break = TRUE; word->space = 0; max_line_width = MAX (line_width, max_line_width); @@ -488,6 +491,7 @@ { word = gtk_label_word_alloc (); + word->paragraph_break = TRUE; word->space = 0; word->beginning = str; word->length = 0; @@ -500,6 +504,16 @@ return MAX (line_width, max_line_width); } +static gboolean +is_ideogram (GdkWChar wc) +{ + if (gdk_iswalpha (wc) && (!gdk_iswupper (wc) && !gdk_iswlower (wc))) + return TRUE; + + return !(gdk_iswspace (wc) || gdk_iswalnum (wc) || + gdk_iswpunct (wc) || gdk_iswcntrl (wc)); +} + /* this needs to handle white space better. */ static gint gtk_label_split_text_wrapped (GtkLabel *label) @@ -526,6 +540,7 @@ if (str == label->label_wc || str[-1] == '\n') { /* Paragraph break */ + word->paragraph_break = TRUE; word->space = 0; max_line_width = MAX (line_width, max_line_width); @@ -546,24 +561,30 @@ else word->space = space_width * nspaces; } - else + else if (gdk_iswspace (str[-1])) { /* Regular inter-word space */ word->space = space_width; } + else + { + word->space = 0; + } word->beginning = str; word->length = 0; p = word->beginning; while (*p && !gdk_iswspace (*p)) { + if (word->length > 0 && (is_ideogram (p[-1]) || is_ideogram (*p))) + break; word->length++; p++; } word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length); str += word->length; - if (*str) + if (*str && gdk_iswspace (*str)) str++; line_width += word->space + word->width; @@ -600,7 +621,7 @@ width = 0; for (word = label->words; word; word = word->next) { - if (word->space == 0 + if (word->paragraph_break || (line_width && (line_width >= min_width || line_width + word->width + word->space > max_width))) @@ -716,7 +737,8 @@ GtkLabelWord *word, *line, *next_line; GtkWidget *widget; gchar *ptrn; - gint x, y, space, extra_width, add_space, baseline_skip; + gint x, y, space, num_words, extra_width, add_space, baseline_skip; + gboolean deliver_equivalently; g_return_if_fail (label->wrap); @@ -724,20 +746,24 @@ y = 0; baseline_skip = (GTK_WIDGET (label)->style->font->ascent + GTK_WIDGET (label)->style->font->descent + 1); + deliver_equivalently = FALSE; for (line = label->words; line != 0; line = next_line) { - space = 0; + space = num_words = 0; extra_width = max_line_width - line->width; for (next_line = line->next; next_line; next_line = next_line->next) { - if (next_line->space == 0) + if (next_line->paragraph_break) break; /* New paragraph */ if (next_line->space + next_line->width > extra_width) break; + if (next_line->space == 0) + deliver_equivalently = TRUE; /* An ideogram is found. */ extra_width -= next_line->space + next_line->width; space += next_line->space; + num_words++; } line->x = 0; @@ -747,14 +773,18 @@ for (word = line->next; word != next_line; word = word->next) { - if (next_line && next_line->space) + if (next_line && !next_line->paragraph_break && + label->jtype == GTK_JUSTIFY_FILL && + (deliver_equivalently ? num_words : space) > 0) { - /* Not last line of paragraph --- fill line if needed */ - if (label->jtype == GTK_JUSTIFY_FILL) { + /* Not last line of paragraph --- fill line */ + if (deliver_equivalently) + add_space = (extra_width + num_words / 2) / num_words; + else add_space = (extra_width * word->space + space / 2) / space; - extra_width -= add_space; - space -= word->space; - } + extra_width -= add_space; + space -= word->space; + num_words--; } word->x = x + word->space + add_space; @@ -925,7 +955,7 @@ for (word = label->words; word; word = word->next) { - gchar save = word->beginning[word->length]; + GdkWChar save = word->beginning[word->length]; word->beginning[word->length] = '\0'; gtk_label_paint_word (label, x, y, word, &event->area); word->beginning[word->length] = save; diff -ruN gtk+-1.2.10/gtk/gtkmenushell.c gtk-n/gtk/gtkmenushell.c --- gtk+-1.2.10/gtk/gtkmenushell.c Mon Mar 5 22:38:05 2001 +++ gtk-n/gtk/gtkmenushell.c Thu Jan 31 08:57:05 2002 @@ -258,6 +258,11 @@ GTK_TYPE_BOOL, TRUE); gtk_binding_entry_add_signal (binding_set, + GDK_KP_Enter, 0, + "activate_current", 1, + GTK_TYPE_BOOL, + TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_space, 0, "activate_current", 1, GTK_TYPE_BOOL, diff -ruN gtk+-1.2.10/gtk/gtknotebook.c gtk-n/gtk/gtknotebook.c --- gtk+-1.2.10/gtk/gtknotebook.c Wed Jan 31 00:27:26 2001 +++ gtk-n/gtk/gtknotebook.c Thu Jan 31 08:57:05 2002 @@ -1369,6 +1369,7 @@ gtk_notebook_switch_focus_tab (notebook, list); return TRUE; case GDK_Return: + case GDK_KP_Enter: case GDK_space: gtk_notebook_page_select (GTK_NOTEBOOK (widget)); return TRUE; diff -ruN gtk+-1.2.10/gtk/gtkrc.c gtk-n/gtk/gtkrc.c --- gtk+-1.2.10/gtk/gtkrc.c Thu Mar 15 19:41:40 2001 +++ gtk-n/gtk/gtkrc.c Thu Jan 31 08:56:58 2002 @@ -33,6 +33,7 @@ #include <string.h> #include <stdio.h> #include <stdlib.h> +#include <langinfo.h> #include "gtkrc.h" #include "gtkbindings.h" @@ -440,7 +441,7 @@ void gtk_rc_init (void) { - static gchar *locale_suffixes[3]; + static gchar *locale_suffixes[8]; static gint n_locale_suffixes = 0; gint i, j; @@ -449,9 +450,7 @@ if (!initted) { - gint length; - - char *locale = setlocale (LC_CTYPE, NULL); + char *locale = g_strdup (setlocale (LC_CTYPE, NULL)); char *p; initted = TRUE; @@ -470,39 +469,84 @@ * We normalize the charset into a standard form, * which has all '-' and '_' characters removed, * and is lowercase. + * + * the search is done in that order: + * gtkrc.ll_cc.lowercasecodeset + * gtkrc.ll_cc.normalizedcodeset + * gtkrc.ll.lowercasecodeset + * gtkrc.ll.normalizedcodeset + * gtkrc.lowercasecodeset + * gtkrc.normalizedcodeset + * gtkrc.ll_cc + * gtkrc.ll + * */ - gchar *normalized_locale; + char *codeset = NULL; + char *normalized_codeset = NULL; + char *cc = NULL; + char *ll; p = strchr (locale, '@'); - length = p ? (p -locale) : strlen (locale); + if (p) + *p = '\0'; + codeset = nl_langinfo (CODESET); + p = strchr (locale, '.'); + if (!codeset && p) + codeset = p + 1; if (p) + *p = '\0'; + + if (codeset) { - gchar *tmp1 = g_strndup (locale, p - locale + 1); - gchar *tmp2 = _gtk_normalize_codeset (p + 1, length - (p - locale + 1)); + codeset = g_strdup (codeset); - normalized_locale = g_strconcat (tmp1, tmp2, NULL); - g_free (tmp1); - g_free (tmp2); - - locale_suffixes[n_locale_suffixes++] = g_strdup (normalized_locale); - length = p - locale; + p = codeset; + while (*p) + { + /* tolower not used, because some locales are not + * compatible with C locale in lowercasing ascii + */ + if (*p >= 'A' && *p <= 'Z') + *p = (*p) - 'A' + 'a'; + p++; + } + + normalized_codeset = _gtk_normalize_codeset(codeset, strlen (codeset)); + if (strcmp (normalized_codeset, codeset) == 0) + normalized_codeset = NULL; } - else - normalized_locale = g_strndup (locale, length); - p = strchr (normalized_locale, '_'); + p = strchr (locale, '_'); if (p) { - locale_suffixes[n_locale_suffixes++] = g_strndup (normalized_locale, length); - length = p - normalized_locale; + cc = p + 1; + *p = '\0'; } - - locale_suffixes[n_locale_suffixes++] = g_strndup (normalized_locale, length); - g_free (normalized_locale); + ll = locale; + + if (cc && codeset) + locale_suffixes[n_locale_suffixes++] = g_strconcat (ll, "_", cc, ".", codeset, NULL); + if (cc && normalized_codeset) + locale_suffixes[n_locale_suffixes++] = g_strconcat (ll, "_", cc, ".", normalized_codeset, NULL); + if (codeset) + locale_suffixes[n_locale_suffixes++] = g_strconcat (ll, ".", codeset, NULL); + if (normalized_codeset) + locale_suffixes[n_locale_suffixes++] = g_strconcat (ll, ".", normalized_codeset, NULL); + if (codeset) + locale_suffixes[n_locale_suffixes++] = g_strdup (codeset); + if (normalized_codeset) + locale_suffixes[n_locale_suffixes++] = g_strdup (normalized_codeset); + if (cc) + locale_suffixes[n_locale_suffixes++] = g_strconcat (ll, "_", cc, NULL); + locale_suffixes[n_locale_suffixes++] = g_strdup (ll); + + g_free (codeset); } + + g_free (locale); } i = 0; diff -ruN gtk+-1.2.10/gtk/gtkrc.iso885913 gtk-n/gtk/gtkrc.iso885913 --- gtk+-1.2.10/gtk/gtkrc.iso885913 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso885913 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,7 @@ +style "gtk-default-iso-8859-13" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-13,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-13,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-13" diff -ruN gtk+-1.2.10/gtk/gtkrc.iso885914 gtk-n/gtk/gtkrc.iso885914 --- gtk+-1.2.10/gtk/gtkrc.iso885914 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso885914 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,8 @@ +style "gtk-default-iso-8859-14" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-14,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-14,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-14" + diff -ruN gtk+-1.2.10/gtk/gtkrc.iso885915 gtk-n/gtk/gtkrc.iso885915 --- gtk+-1.2.10/gtk/gtkrc.iso885915 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso885915 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,8 @@ +style "gtk-default-iso-8859-15" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-15,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-15,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-15" + diff -ruN gtk+-1.2.10/gtk/gtkrc.iso88592 gtk-n/gtk/gtkrc.iso88592 --- gtk+-1.2.10/gtk/gtkrc.iso88592 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso88592 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,14 @@ +#$(gtkconfigdir)/gtkrc.iso-8859-2 +# +# This file defines the fontsets for iso-8859-2 encoding +# make symliks or hardlinks to gtkrc.$LANG if your language uses iso-8859-2 +# and a gtkrc.$LANG doesn't exist yet. + +style "gtk-default-iso-8859-2" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-2,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-2,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-2" + diff -ruN gtk+-1.2.10/gtk/gtkrc.iso88593 gtk-n/gtk/gtkrc.iso88593 --- gtk+-1.2.10/gtk/gtkrc.iso88593 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso88593 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,8 @@ +style "gtk-default-iso-8859-3" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-3,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-3,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-3" + diff -ruN gtk+-1.2.10/gtk/gtkrc.iso88595 gtk-n/gtk/gtkrc.iso88595 --- gtk+-1.2.10/gtk/gtkrc.iso88595 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso88595 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,14 @@ +#$(gtkconfigdir)/gtkrc.iso-8859-5 +# +# This file defines the fontsets for iso-8859-5 encoding +# make symliks or hardlinks to gtkrc.$LANG if your language uses iso-8859-5 +# and a gtkrc.$LANG doesn't exist yet. + +style "gtk-default-iso-8859-5" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-5,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-5,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-5" + diff -ruN gtk+-1.2.10/gtk/gtkrc.iso88597 gtk-n/gtk/gtkrc.iso88597 --- gtk+-1.2.10/gtk/gtkrc.iso88597 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso88597 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,8 @@ +style "gtk-default-iso-8859-7" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-7,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-7,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-7" + diff -ruN gtk+-1.2.10/gtk/gtkrc.iso88599 gtk-n/gtk/gtkrc.iso88599 --- gtk+-1.2.10/gtk/gtkrc.iso88599 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.iso88599 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,8 @@ +style "gtk-default-iso-8859-9" { + fontset = "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1,\ + -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-9,\ + -*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-9,*-r-*" +} +class "GtkWidget" style "gtk-default-iso-8859-9" + diff -ruN gtk+-1.2.10/gtk/gtkrc.utf8 gtk-n/gtk/gtkrc.utf8 --- gtk+-1.2.10/gtk/gtkrc.utf8 Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtkrc.utf8 Thu Jan 31 08:56:58 2002 @@ -0,0 +1,7 @@ +style "default-text" { + fontset = "-*-helvetica-medium-r-normal--*-120-*-*-*-*-*-*" + +} + +class "GtkWidget" style "default-text" + diff -ruN gtk+-1.2.10/gtk/gtkstyle.c gtk-n/gtk/gtkstyle.c --- gtk+-1.2.10/gtk/gtkstyle.c Tue Feb 20 17:46:58 2001 +++ gtk-n/gtk/gtkstyle.c Thu Jan 31 08:56:36 2002 @@ -348,8 +348,9 @@ new_style->bg[i] = style->bg[i]; new_style->text[i] = style->text[i]; new_style->base[i] = style->base[i]; - - new_style->bg_pixmap[i] = style->bg_pixmap[i]; + + if (style->bg_pixmap[i] && !(style->rc_style && style->rc_style->bg_pixmap_name[i])) + new_style->bg_pixmap[i] = gdk_pixmap_ref (style->bg_pixmap[i]); } gdk_font_unref (new_style->font); diff -ruN gtk+-1.2.10/gtk/gtktext.c gtk-n/gtk/gtktext.c --- gtk+-1.2.10/gtk/gtktext.c Thu Mar 15 21:15:12 2001 +++ gtk-n/gtk/gtktext.c Thu Jan 31 08:57:05 2002 @@ -101,6 +101,13 @@ ARG_WORD_WRAP }; +typedef enum { + CHAR_CLASS_SPACE, + CHAR_CLASS_ALNUM, + CHAR_CLASS_IDEOGRAM, + CHAR_CLASS_OTHERS /* punct, cntrl */ +} CharClass; + typedef struct _TextProperty TextProperty; typedef struct _TabStopMark TabStopMark; typedef struct _PrevTabCont PrevTabCont; @@ -300,6 +307,8 @@ const GtkPropertyMark *mark, const PrevTabCont *tab_cont, PrevTabCont *next_cont); +static void find_word_wrap_position (GtkText* text, LineParams *lp); +static CharClass char_class (GtkText* text, guint index); static void recompute_geometry (GtkText* text); static void insert_expose (GtkText* text, guint old_pixels, gint nchars, guint new_line_count); static void delete_expose (GtkText* text, @@ -2046,6 +2055,7 @@ case GDK_Up: scroll_int (text, -KEY_SCROLL_PIXELS); break; case GDK_Down: scroll_int (text, +KEY_SCROLL_PIXELS); break; case GDK_Return: + case GDK_KP_Enter: if (event->state & GDK_CONTROL_MASK) gtk_signal_emit_by_name (GTK_OBJECT (text), "activate"); else @@ -2152,6 +2162,7 @@ gtk_editable_insert_text (editable, "\t", 1, &position); break; case GDK_Return: + case GDK_KP_Enter: if (event->state & GDK_CONTROL_MASK) gtk_signal_emit_by_name (GTK_OBJECT (text), "activate"); else @@ -4111,27 +4122,21 @@ undraw_cursor (text, FALSE); - if (text->use_wchar) + while (!LAST_INDEX (text, text->cursor_mark)) { - while (!LAST_INDEX (text, text->cursor_mark) && - !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) - advance_mark (&text->cursor_mark); - - while (!LAST_INDEX (text, text->cursor_mark) && - gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) - advance_mark (&text->cursor_mark); + CharClass cc = char_class (text, text->cursor_mark.index); + if (cc == CHAR_CLASS_ALNUM || cc == CHAR_CLASS_IDEOGRAM) + break; + advance_mark (&text->cursor_mark); } - else + while (!LAST_INDEX (text, text->cursor_mark)) { - while (!LAST_INDEX (text, text->cursor_mark) && - !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) - advance_mark (&text->cursor_mark); - - while (!LAST_INDEX (text, text->cursor_mark) && - isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) - advance_mark (&text->cursor_mark); + CharClass cc = char_class (text, text->cursor_mark.index); + if (cc != CHAR_CLASS_ALNUM && cc != CHAR_CLASS_IDEOGRAM) + break; + advance_mark (&text->cursor_mark); } - + find_cursor (text, TRUE); draw_cursor (text, FALSE); } @@ -4143,25 +4148,19 @@ undraw_cursor (text, FALSE); - if (text->use_wchar) + while (text->cursor_mark.index > 0) { - while ((text->cursor_mark.index > 0) && - !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) - decrement_mark (&text->cursor_mark); - - while ((text->cursor_mark.index > 0) && - gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) - decrement_mark (&text->cursor_mark); + CharClass cc = char_class (text, text->cursor_mark.index - 1); + if (cc == CHAR_CLASS_ALNUM || cc == CHAR_CLASS_IDEOGRAM) + break; + decrement_mark (&text->cursor_mark); } - else + while (text->cursor_mark.index > 0) { - while ((text->cursor_mark.index > 0) && - !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) - decrement_mark (&text->cursor_mark); - - while ((text->cursor_mark.index > 0) && - isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) - decrement_mark (&text->cursor_mark); + CharClass cc = char_class (text, text->cursor_mark.index - 1); + if (cc != CHAR_CLASS_ALNUM && cc != CHAR_CLASS_IDEOGRAM) + break; + decrement_mark (&text->cursor_mark); } find_cursor (text, TRUE); @@ -4782,27 +4781,8 @@ GtkPropertyMark saved_mark = lp.end; guint saved_characters = lp.displayable_chars; - lp.displayable_chars += 1; - - if (text->use_wchar) - { - while (!gdk_iswspace (GTK_TEXT_INDEX (text, lp.end.index)) && - (lp.end.index > lp.start.index)) - { - decrement_mark (&lp.end); - lp.displayable_chars -= 1; - } - } - else - { - while (!isspace(GTK_TEXT_INDEX (text, lp.end.index)) && - (lp.end.index > lp.start.index)) - { - decrement_mark (&lp.end); - lp.displayable_chars -= 1; - } - } - + find_word_wrap_position (text, &lp); + /* If whole line is one word, revert to char wrapping */ if (lp.end.index == lp.start.index) { @@ -4848,6 +4828,54 @@ lp.tab_cont_next = *next_cont; return lp; +} + +static CharClass +char_class (GtkText* text, guint index) +{ + GdkWChar wc; + wc = GTK_TEXT_INDEX (text, index); + if (text->use_wchar) + { + if (gdk_iswspace (wc)) + return CHAR_CLASS_SPACE; + else if (gdk_iswalnum (wc)) + return CHAR_CLASS_ALNUM; + else if (gdk_iswpunct (wc) || gdk_iswcntrl (wc)) + return CHAR_CLASS_OTHERS; + else + return CHAR_CLASS_IDEOGRAM; + } + else + { + if (isspace (wc)) + return CHAR_CLASS_SPACE; + else if (isalnum (wc)) + return CHAR_CLASS_ALNUM; + else if (ispunct (wc) || iscntrl (wc)) + return CHAR_CLASS_OTHERS; + else + return CHAR_CLASS_IDEOGRAM; + } +} + +static void +find_word_wrap_position (GtkText* text, LineParams *lp) +{ + while (lp->end.index > lp->start.index && + char_class (text, lp->end.index) != CHAR_CLASS_SPACE && + char_class (text, lp->end.index) != CHAR_CLASS_IDEOGRAM && + char_class (text, lp->end.index - 1) != CHAR_CLASS_IDEOGRAM) + { + decrement_mark (&lp->end); + lp->displayable_chars -= 1; + } + + /* lp->end.index points the position to be cut just now. If it's not a + * space, move it to the next display line. */ + if (lp->end.index > lp->start.index && + char_class (text, lp->end.index) != CHAR_CLASS_SPACE) + decrement_mark (&lp->end); } static void diff -ruN gtk+-1.2.10/gtk/gtktext.c.orig gtk-n/gtk/gtktext.c.orig --- gtk+-1.2.10/gtk/gtktext.c.orig Thu Jan 1 01:00:00 1970 +++ gtk-n/gtk/gtktext.c.orig Thu Mar 15 21:15:12 2001 @@ -0,0 +1,5684 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. 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 <ctype.h> +#include <string.h> +#include "gdk/gdkkeysyms.h" +#include "gdk/gdki18n.h" +#include "gtkmain.h" +#include "gtkselection.h" +#include "gtksignal.h" +#include "gtktext.h" +#include "line-wrap.xbm" +#include "line-arrow.xbm" + + +#define INITIAL_BUFFER_SIZE 1024 +#define INITIAL_LINE_CACHE_SIZE 256 +#define MIN_GAP_SIZE 256 +#define LINE_DELIM '\n' +#define MIN_TEXT_WIDTH_LINES 20 +#define MIN_TEXT_HEIGHT_LINES 10 +#define TEXT_BORDER_ROOM 1 +#define LINE_WRAP_ROOM 8 /* The bitmaps are 6 wide. */ +#define DEFAULT_TAB_STOP_WIDTH 4 +#define SCROLL_PIXELS 5 +#define KEY_SCROLL_PIXELS 10 +#define SCROLL_TIME 100 +#define FREEZE_LENGTH 1024 +/* Freeze text when inserting or deleting more than this many characters */ + +#define SET_PROPERTY_MARK(m, p, o) do { \ + (m)->property = (p); \ + (m)->offset = (o); \ + } while (0) +#define MARK_CURRENT_PROPERTY(mark) ((TextProperty*)(mark)->property->data) +#define MARK_NEXT_PROPERTY(mark) ((TextProperty*)(mark)->property->next->data) +#define MARK_PREV_PROPERTY(mark) ((TextProperty*)((mark)->property->prev ? \ + (mark)->property->prev->data \ + : NULL)) +#define MARK_PREV_LIST_PTR(mark) ((mark)->property->prev) +#define MARK_LIST_PTR(mark) ((mark)->property) +#define MARK_NEXT_LIST_PTR(mark) ((mark)->property->next) +#define MARK_OFFSET(mark) ((mark)->offset) +#define MARK_PROPERTY_LENGTH(mark) (MARK_CURRENT_PROPERTY(mark)->length) + + +#define MARK_CURRENT_FONT(text, mark) \ + ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_FONT) ? \ + MARK_CURRENT_PROPERTY(mark)->font->gdk_font : \ + GTK_WIDGET (text)->style->font) +#define MARK_CURRENT_FORE(text, mark) \ + ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_FOREGROUND) ? \ + &MARK_CURRENT_PROPERTY(mark)->fore_color : \ + &((GtkWidget *)text)->style->text[((GtkWidget *)text)->state]) +#define MARK_CURRENT_BACK(text, mark) \ + ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_BACKGROUND) ? \ + &MARK_CURRENT_PROPERTY(mark)->back_color : \ + &((GtkWidget *)text)->style->base[((GtkWidget *)text)->state]) +#define MARK_CURRENT_TEXT_FONT(text, mark) \ + ((MARK_CURRENT_PROPERTY(mark)->flags & PROPERTY_FONT) ? \ + MARK_CURRENT_PROPERTY(mark)->font : \ + text->current_font) + +#define TEXT_LENGTH(t) ((t)->text_end - (t)->gap_size) +#define FONT_HEIGHT(f) ((f)->ascent + (f)->descent) +#define LINE_HEIGHT(l) ((l).font_ascent + (l).font_descent) +#define LINE_CONTAINS(l, i) ((l).start.index <= (i) && (l).end.index >= (i)) +#define LINE_STARTS_AT(l, i) ((l).start.index == (i)) +#define LINE_START_PIXEL(l) ((l).tab_cont.pixel_offset) +#define LAST_INDEX(t, m) ((m).index == TEXT_LENGTH(t)) +#define CACHE_DATA(c) (*(LineParams*)(c)->data) + +enum { + ARG_0, + ARG_HADJUSTMENT, + ARG_VADJUSTMENT, + ARG_LINE_WRAP, + ARG_WORD_WRAP +}; + +typedef struct _TextProperty TextProperty; +typedef struct _TabStopMark TabStopMark; +typedef struct _PrevTabCont PrevTabCont; +typedef struct _FetchLinesData FetchLinesData; +typedef struct _LineParams LineParams; +typedef struct _SetVerticalScrollData SetVerticalScrollData; + +typedef gint (*LineIteratorFunction) (GtkText* text, LineParams* lp, void* data); + +typedef enum +{ + FetchLinesPixels, + FetchLinesCount +} FLType; + +struct _SetVerticalScrollData { + gint pixel_height; + gint last_didnt_wrap; + gint last_line_start; + GtkPropertyMark mark; +}; + +struct _GtkTextFont +{ + /* The actual font. */ + GdkFont *gdk_font; + guint ref_count; + + gint16 char_widths[256]; +}; + +typedef enum { + PROPERTY_FONT = 1 << 0, + PROPERTY_FOREGROUND = 1 << 1, + PROPERTY_BACKGROUND = 1 << 2 +} TextPropertyFlags; + +struct _TextProperty +{ + /* Font. */ + GtkTextFont* font; + + /* Background Color. */ + GdkColor back_color; + + /* Foreground Color. */ + GdkColor fore_color; + + /* Show which properties are set */ + TextPropertyFlags flags; + + /* Length of this property. */ + guint length; +}; + +struct _TabStopMark +{ + GList* tab_stops; /* Index into list containing the next tab position. If + * NULL, using default widths. */ + gint to_next_tab; +}; + +struct _PrevTabCont +{ + guint pixel_offset; + TabStopMark tab_start; +}; + +struct _FetchLinesData +{ + GList* new_lines; + FLType fl_type; + gint data; + gint data_max; +}; + +struct _LineParams +{ + guint font_ascent; + guint font_descent; + guint pixel_width; + guint displayable_chars; + guint wraps : 1; + + PrevTabCont tab_cont; + PrevTabCont tab_cont_next; + + GtkPropertyMark start; + GtkPropertyMark end; +}; + + +static void gtk_text_class_init (GtkTextClass *klass); +static void gtk_text_set_arg (GtkObject *object, + GtkArg *arg, + guint arg_id); +static void gtk_text_get_arg (GtkObject *object, + GtkArg *arg, + guint arg_id); +static void gtk_text_init (GtkText *text); +static void gtk_text_destroy (GtkObject *object); +static void gtk_text_finalize (GtkObject *object); +static void gtk_text_realize (GtkWidget *widget); +static void gtk_text_unrealize (GtkWidget *widget); +static void gtk_text_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void gtk_text_state_changed (GtkWidget *widget, + GtkStateType previous_state); +static void gtk_text_draw_focus (GtkWidget *widget); +static void gtk_text_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_text_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gtk_text_adjustment (GtkAdjustment *adjustment, + GtkText *text); +static void gtk_text_disconnect (GtkAdjustment *adjustment, + GtkText *text); + +static void gtk_text_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position); +static void gtk_text_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos); +static void gtk_text_update_text (GtkEditable *editable, + gint start_pos, + gint end_pos); +static gchar *gtk_text_get_chars (GtkEditable *editable, + gint start, + gint end); +static void gtk_text_set_selection (GtkEditable *editable, + gint start, + gint end); +static void gtk_text_real_set_editable (GtkEditable *editable, + gboolean is_editable); + +/* Event handlers */ +static void gtk_text_draw (GtkWidget *widget, + GdkRectangle *area); +static gint gtk_text_expose (GtkWidget *widget, + GdkEventExpose *event); +static gint gtk_text_button_press (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_text_button_release (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_text_motion_notify (GtkWidget *widget, + GdkEventMotion *event); +static gint gtk_text_key_press (GtkWidget *widget, + GdkEventKey *event); +static gint gtk_text_focus_in (GtkWidget *widget, + GdkEventFocus *event); +static gint gtk_text_focus_out (GtkWidget *widget, + GdkEventFocus *event); + +static void move_gap (GtkText* text, guint index); +static void make_forward_space (GtkText* text, guint len); + +/* Property management */ +static GtkTextFont* get_text_font (GdkFont* gfont); +static void text_font_unref (GtkTextFont *text_font); + +static void insert_text_property (GtkText* text, GdkFont* font, + GdkColor *fore, GdkColor* back, guint len); +static TextProperty* new_text_property (GtkText *text, GdkFont* font, + GdkColor* fore, GdkColor* back, guint length); +static void destroy_text_property (TextProperty *prop); +static void init_properties (GtkText *text); +static void realize_property (GtkText *text, TextProperty *prop); +static void realize_properties (GtkText *text); +static void unrealize_property (GtkText *text, TextProperty *prop); +static void unrealize_properties (GtkText *text); + +static void delete_text_property (GtkText* text, guint len); + +static guint pixel_height_of (GtkText* text, GList* cache_line); + +/* Property Movement and Size Computations */ +static void advance_mark (GtkPropertyMark* mark); +static void decrement_mark (GtkPropertyMark* mark); +static void advance_mark_n (GtkPropertyMark* mark, gint n); +static void decrement_mark_n (GtkPropertyMark* mark, gint n); +static void move_mark_n (GtkPropertyMark* mark, gint n); +static GtkPropertyMark find_mark (GtkText* text, guint mark_position); +static GtkPropertyMark find_mark_near (GtkText* text, guint mark_position, const GtkPropertyMark* near); +static void find_line_containing_point (GtkText* text, guint point, + gboolean scroll); + +/* Display */ +static void compute_lines_pixels (GtkText* text, guint char_count, + guint *lines, guint *pixels); + +static gint total_line_height (GtkText* text, + GList* line, + gint line_count); +static LineParams find_line_params (GtkText* text, + const GtkPropertyMark *mark, + const PrevTabCont *tab_cont, + PrevTabCont *next_cont); +static void recompute_geometry (GtkText* text); +static void insert_expose (GtkText* text, guint old_pixels, gint nchars, guint new_line_count); +static void delete_expose (GtkText* text, + guint nchars, + guint old_lines, + guint old_pixels); +static GdkGC *create_bg_gc (GtkText *text); +static void clear_area (GtkText *text, GdkRectangle *area); +static void draw_line (GtkText* text, + gint pixel_height, + LineParams* lp); +static void draw_line_wrap (GtkText* text, + guint height); +static void draw_cursor (GtkText* text, gint absolute); +static void undraw_cursor (GtkText* text, gint absolute); +static gint drawn_cursor_min (GtkText* text); +static gint drawn_cursor_max (GtkText* text); +static void expose_text (GtkText* text, GdkRectangle *area, gboolean cursor); + +/* Search and Placement. */ +static void find_cursor (GtkText* text, + gboolean scroll); +static void find_cursor_at_line (GtkText* text, + const LineParams* start_line, + gint pixel_height); +static void find_mouse_cursor (GtkText* text, gint x, gint y); + +/* Scrolling. */ +static void adjust_adj (GtkText* text, GtkAdjustment* adj); +static void scroll_up (GtkText* text, gint diff); +static void scroll_down (GtkText* text, gint diff); +static void scroll_int (GtkText* text, gint diff); + +static void process_exposes (GtkText *text); + +/* Cache Management. */ +static void free_cache (GtkText* text); +static GList* remove_cache_line (GtkText* text, GList* list); + +/* Key Motion. */ +static void move_cursor_buffer_ver (GtkText *text, int dir); +static void move_cursor_page_ver (GtkText *text, int dir); +static void move_cursor_ver (GtkText *text, int count); +static void move_cursor_hor (GtkText *text, int count); + +/* Binding actions */ +static void gtk_text_move_cursor (GtkEditable *editable, + gint x, + gint y); +static void gtk_text_move_word (GtkEditable *editable, + gint n); +static void gtk_text_move_page (GtkEditable *editable, + gint x, + gint y); +static void gtk_text_move_to_row (GtkEditable *editable, + gint row); +static void gtk_text_move_to_column (GtkEditable *editable, + gint row); +static void gtk_text_kill_char (GtkEditable *editable, + gint direction); +static void gtk_text_kill_word (GtkEditable *editable, + gint direction); +static void gtk_text_kill_line (GtkEditable *editable, + gint direction); + +/* To be removed */ +static void gtk_text_move_forward_character (GtkText *text); +static void gtk_text_move_backward_character (GtkText *text); +static void gtk_text_move_forward_word (GtkText *text); +static void gtk_text_move_backward_word (GtkText *text); +static void gtk_text_move_beginning_of_line (GtkText *text); +static void gtk_text_move_end_of_line (GtkText *text); +static void gtk_text_move_next_line (GtkText *text); +static void gtk_text_move_previous_line (GtkText *text); + +static void gtk_text_delete_forward_character (GtkText *text); +static void gtk_text_delete_backward_character (GtkText *text); +static void gtk_text_delete_forward_word (GtkText *text); +static void gtk_text_delete_backward_word (GtkText *text); +static void gtk_text_delete_line (GtkText *text); +static void gtk_text_delete_to_line_end (GtkText *text); +static void gtk_text_select_word (GtkText *text, + guint32 time); +static void gtk_text_select_line (GtkText *text, + guint32 time); + +static void gtk_text_set_position (GtkEditable *editable, + gint position); + +/* #define DEBUG_GTK_TEXT */ + +#if defined(DEBUG_GTK_TEXT) && defined(__GNUC__) +/* Debugging utilities. */ +static void gtk_text_assert_mark (GtkText *text, + GtkPropertyMark *mark, + GtkPropertyMark *before, + GtkPropertyMark *after, + const gchar *msg, + const gchar *where, + gint line); + +static void gtk_text_assert (GtkText *text, + const gchar *msg, + gint line); +static void gtk_text_show_cache_line (GtkText *text, GList *cache, + const char* what, const char* func, gint line); +static void gtk_text_show_cache (GtkText *text, const char* func, gint line); +static void gtk_text_show_adj (GtkText *text, + GtkAdjustment *adj, + const char* what, + const char* func, + gint line); +static void gtk_text_show_props (GtkText* test, + const char* func, + int line); + +#define TDEBUG(args) g_message args +#define TEXT_ASSERT(text) gtk_text_assert (text,__PRETTY_FUNCTION__,__LINE__) +#define TEXT_ASSERT_MARK(text,mark,msg) gtk_text_assert_mark (text,mark, \ + __PRETTY_FUNCTION__,msg,__LINE__) +#define TEXT_SHOW(text) gtk_text_show_cache (text, __PRETTY_FUNCTION__,__LINE__) +#define TEXT_SHOW_LINE(text,line,msg) gtk_text_show_cache_line (text,line,msg,\ + __PRETTY_FUNCTION__,__LINE__) +#define TEXT_SHOW_ADJ(text,adj,msg) gtk_text_show_adj (text,adj,msg, \ + __PRETTY_FUNCTION__,__LINE__) +#else +#define TDEBUG(args) +#define TEXT_ASSERT(text) +#define TEXT_ASSERT_MARK(text,mark,msg) +#define TEXT_SHOW(text) +#define TEXT_SHOW_LINE(text,line,msg) +#define TEXT_SHOW_ADJ(text,adj,msg) +#endif + +/* Memory Management. */ +static GMemChunk *params_mem_chunk = NULL; +static GMemChunk *text_property_chunk = NULL; + +static GtkWidgetClass *parent_class = NULL; + + +static const GtkTextFunction control_keys[26] = +{ + (GtkTextFunction)gtk_text_move_beginning_of_line, /* a */ + (GtkTextFunction)gtk_text_move_backward_character, /* b */ + (GtkTextFunction)gtk_editable_copy_clipboard, /* c */ + (GtkTextFunction)gtk_text_delete_forward_character, /* d */ + (GtkTextFunction)gtk_text_move_end_of_line, /* e */ + (GtkTextFunction)gtk_text_move_forward_character, /* f */ + NULL, /* g */ + (GtkTextFunction)gtk_text_delete_backward_character, /* h */ + NULL, /* i */ + NULL, /* j */ + (GtkTextFunction)gtk_text_delete_to_line_end, /* k */ + NULL, /* l */ + NULL, /* m */ + (GtkTextFunction)gtk_text_move_next_line, /* n */ + NULL, /* o */ + (GtkTextFunction)gtk_text_move_previous_line, /* p */ + NULL, /* q */ + NULL, /* r */ + NULL, /* s */ + NULL, /* t */ + (GtkTextFunction)gtk_text_delete_line, /* u */ + (GtkTextFunction)gtk_editable_paste_clipboard, /* v */ + (GtkTextFunction)gtk_text_delete_backward_word, /* w */ + (GtkTextFunction)gtk_editable_cut_clipboard, /* x */ + NULL, /* y */ + NULL, /* z */ +}; + +static const GtkTextFunction alt_keys[26] = +{ + NULL, /* a */ + (GtkTextFunction)gtk_text_move_backward_word, /* b */ + NULL, /* c */ + (GtkTextFunction)gtk_text_delete_forward_word, /* d */ + NULL, /* e */ + (GtkTextFunction)gtk_text_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 */ +}; + + +/**********************************************************************/ +/* Widget Crap */ +/**********************************************************************/ + +GtkType +gtk_text_get_type (void) +{ + static GtkType text_type = 0; + + if (!text_type) + { + static const GtkTypeInfo text_info = + { + "GtkText", + sizeof (GtkText), + sizeof (GtkTextClass), + (GtkClassInitFunc) gtk_text_class_init, + (GtkObjectInitFunc) gtk_text_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + text_type = gtk_type_unique (GTK_TYPE_EDITABLE, &text_info); + } + + return text_type; +} + +static void +gtk_text_class_init (GtkTextClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkEditableClass *editable_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + editable_class = (GtkEditableClass*) class; + parent_class = gtk_type_class (GTK_TYPE_EDITABLE); + + gtk_object_add_arg_type ("GtkText::hadjustment", + GTK_TYPE_ADJUSTMENT, + GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT, + ARG_HADJUSTMENT); + gtk_object_add_arg_type ("GtkText::vadjustment", + GTK_TYPE_ADJUSTMENT, + GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT, + ARG_VADJUSTMENT); + gtk_object_add_arg_type ("GtkText::line_wrap", + GTK_TYPE_BOOL, + GTK_ARG_READWRITE, + ARG_LINE_WRAP); + gtk_object_add_arg_type ("GtkText::word_wrap", + GTK_TYPE_BOOL, + GTK_ARG_READWRITE, + ARG_WORD_WRAP); + + object_class->set_arg = gtk_text_set_arg; + object_class->get_arg = gtk_text_get_arg; + object_class->destroy = gtk_text_destroy; + object_class->finalize = gtk_text_finalize; + + widget_class->realize = gtk_text_realize; + widget_class->unrealize = gtk_text_unrealize; + widget_class->style_set = gtk_text_style_set; + widget_class->state_changed = gtk_text_state_changed; + widget_class->draw_focus = gtk_text_draw_focus; + widget_class->size_request = gtk_text_size_request; + widget_class->size_allocate = gtk_text_size_allocate; + widget_class->draw = gtk_text_draw; + widget_class->expose_event = gtk_text_expose; + widget_class->button_press_event = gtk_text_button_press; + widget_class->button_release_event = gtk_text_button_release; + widget_class->motion_notify_event = gtk_text_motion_notify; + widget_class->key_press_event = gtk_text_key_press; + widget_class->focus_in_event = gtk_text_focus_in; + widget_class->focus_out_event = gtk_text_focus_out; + + widget_class->set_scroll_adjustments_signal = + gtk_signal_new ("set_scroll_adjustments", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkTextClass, set_scroll_adjustments), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + + editable_class->set_editable = gtk_text_real_set_editable; + editable_class->insert_text = gtk_text_insert_text; + editable_class->delete_text = gtk_text_delete_text; + + editable_class->move_cursor = gtk_text_move_cursor; + editable_class->move_word = gtk_text_move_word; + editable_class->move_page = gtk_text_move_page; + editable_class->move_to_row = gtk_text_move_to_row; + editable_class->move_to_column = gtk_text_move_to_column; + + editable_class->kill_char = gtk_text_kill_char; + editable_class->kill_word = gtk_text_kill_word; + editable_class->kill_line = gtk_text_kill_line; + + editable_class->update_text = gtk_text_update_text; + editable_class->get_chars = gtk_text_get_chars; + editable_class->set_selection = gtk_text_set_selection; + editable_class->set_position = gtk_text_set_position; + + class->set_scroll_adjustments = gtk_text_set_adjustments; +} + +static void +gtk_text_set_arg (GtkObject *object, + GtkArg *arg, + guint arg_id) +{ + GtkText *text; + + text = GTK_TEXT (object); + + switch (arg_id) + { + case ARG_HADJUSTMENT: + gtk_text_set_adjustments (text, + GTK_VALUE_POINTER (*arg), + text->vadj); + break; + case ARG_VADJUSTMENT: + gtk_text_set_adjustments (text, + text->hadj, + GTK_VALUE_POINTER (*arg)); + break; + case ARG_LINE_WRAP: + gtk_text_set_line_wrap (text, GTK_VALUE_BOOL (*arg)); + break; + case ARG_WORD_WRAP: + gtk_text_set_word_wrap (text, GTK_VALUE_BOOL (*arg)); + break; + default: + break; + } +} + +static void +gtk_text_get_arg (GtkObject *object, + GtkArg *arg, + guint arg_id) +{ + GtkText *text; + + text = GTK_TEXT (object); + + switch (arg_id) + { + case ARG_HADJUSTMENT: + GTK_VALUE_POINTER (*arg) = text->hadj; + break; + case ARG_VADJUSTMENT: + GTK_VALUE_POINTER (*arg) = text->vadj; + break; + case ARG_LINE_WRAP: + GTK_VALUE_BOOL (*arg) = text->line_wrap; + break; + case ARG_WORD_WRAP: + GTK_VALUE_BOOL (*arg) = text->word_wrap; + break; + default: + arg->type = GTK_TYPE_INVALID; + break; + } +} + +static void +gtk_text_init (GtkText *text) +{ + GTK_WIDGET_SET_FLAGS (text, GTK_CAN_FOCUS); + + text->text_area = NULL; + text->hadj = NULL; + text->vadj = NULL; + text->gc = NULL; + text->bg_gc = NULL; + text->line_wrap_bitmap = NULL; + text->line_arrow_bitmap = NULL; + + text->use_wchar = FALSE; + text->text.ch = g_new (guchar, INITIAL_BUFFER_SIZE); + text->text_len = INITIAL_BUFFER_SIZE; + + text->scratch_buffer.ch = NULL; + text->scratch_buffer_len = 0; + + text->freeze_count = 0; + + if (!params_mem_chunk) + params_mem_chunk = g_mem_chunk_new ("LineParams", + sizeof (LineParams), + 256 * sizeof (LineParams), + G_ALLOC_AND_FREE); + + text->default_tab_width = 4; + text->tab_stops = NULL; + + text->tab_stops = g_list_prepend (text->tab_stops, (void*)8); + text->tab_stops = g_list_prepend (text->tab_stops, (void*)8); + + text->line_start_cache = NULL; + text->first_cut_pixels = 0; + + text->line_wrap = TRUE; + text->word_wrap = FALSE; + + text->timer = 0; + text->button = 0; + + text->current_font = NULL; + + init_properties (text); + + GTK_EDITABLE (text)->editable = FALSE; + + gtk_editable_set_position (GTK_EDITABLE (text), 0); +} + +GtkWidget* +gtk_text_new (GtkAdjustment *hadj, + GtkAdjustment *vadj) +{ + GtkWidget *text; + + if (hadj) + g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadj), NULL); + if (vadj) + g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadj), NULL); + + text = gtk_widget_new (GTK_TYPE_TEXT, + "hadjustment", hadj, + "vadjustment", vadj, + NULL); + + return text; +} + +void +gtk_text_set_word_wrap (GtkText *text, + gint word_wrap) +{ + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + + text->word_wrap = (word_wrap != FALSE); + + if (GTK_WIDGET_REALIZED (text)) + { + recompute_geometry (text); + gtk_widget_queue_draw (GTK_WIDGET (text)); + } +} + +void +gtk_text_set_line_wrap (GtkText *text, + gint line_wrap) +{ + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + + text->line_wrap = (line_wrap != FALSE); + + if (GTK_WIDGET_REALIZED (text)) + { + recompute_geometry (text); + gtk_widget_queue_draw (GTK_WIDGET (text)); + } +} + +void +gtk_text_set_editable (GtkText *text, + gboolean is_editable) +{ + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + + gtk_editable_set_editable (GTK_EDITABLE (text), is_editable); +} + +static void +gtk_text_real_set_editable (GtkEditable *editable, + gboolean is_editable) +{ + GtkText *text; + + g_return_if_fail (editable != NULL); + g_return_if_fail (GTK_IS_TEXT (editable)); + + text = GTK_TEXT (editable); + + editable->editable = (is_editable != FALSE); + + if (GTK_WIDGET_REALIZED (text)) + { + recompute_geometry (text); + gtk_widget_queue_draw (GTK_WIDGET (text)); + } +} + +void +gtk_text_set_adjustments (GtkText *text, + GtkAdjustment *hadj, + GtkAdjustment *vadj) +{ + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + if (hadj) + g_return_if_fail (GTK_IS_ADJUSTMENT (hadj)); + else + hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + if (vadj) + g_return_if_fail (GTK_IS_ADJUSTMENT (vadj)); + else + vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + + if (text->hadj && (text->hadj != hadj)) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (text->hadj), text); + gtk_object_unref (GTK_OBJECT (text->hadj)); + } + + if (text->vadj && (text->vadj != vadj)) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (text->vadj), text); + gtk_object_unref (GTK_OBJECT (text->vadj)); + } + + if (text->hadj != hadj) + { + text->hadj = hadj; + gtk_object_ref (GTK_OBJECT (text->hadj)); + gtk_object_sink (GTK_OBJECT (text->hadj)); + + gtk_signal_connect (GTK_OBJECT (text->hadj), "changed", + (GtkSignalFunc) gtk_text_adjustment, + text); + gtk_signal_connect (GTK_OBJECT (text->hadj), "value_changed", + (GtkSignalFunc) gtk_text_adjustment, + text); + gtk_signal_connect (GTK_OBJECT (text->hadj), "disconnect", + (GtkSignalFunc) gtk_text_disconnect, + text); + gtk_text_adjustment (hadj, text); + } + + if (text->vadj != vadj) + { + text->vadj = vadj; + gtk_object_ref (GTK_OBJECT (text->vadj)); + gtk_object_sink (GTK_OBJECT (text->vadj)); + + gtk_signal_connect (GTK_OBJECT (text->vadj), "changed", + (GtkSignalFunc) gtk_text_adjustment, + text); + gtk_signal_connect (GTK_OBJECT (text->vadj), "value_changed", + (GtkSignalFunc) gtk_text_adjustment, + text); + gtk_signal_connect (GTK_OBJECT (text->vadj), "disconnect", + (GtkSignalFunc) gtk_text_disconnect, + text); + gtk_text_adjustment (vadj, text); + } +} + +void +gtk_text_set_point (GtkText *text, + guint index) +{ + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + g_return_if_fail (index <= TEXT_LENGTH (text)); + + text->point = find_mark (text, index); +} + +guint +gtk_text_get_point (GtkText *text) +{ + g_return_val_if_fail (text != NULL, 0); + g_return_val_if_fail (GTK_IS_TEXT (text), 0); + + return text->point.index; +} + +guint +gtk_text_get_length (GtkText *text) +{ + g_return_val_if_fail (text != NULL, 0); + g_return_val_if_fail (GTK_IS_TEXT (text), 0); + + return TEXT_LENGTH (text); +} + +void +gtk_text_freeze (GtkText *text) +{ + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + + text->freeze_count++; + undraw_cursor (text, FALSE); +} + +void +gtk_text_thaw (GtkText *text) +{ + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + + if (text->freeze_count) + if (!(--text->freeze_count) && GTK_WIDGET_REALIZED (text)) + { + recompute_geometry (text); + gtk_widget_queue_draw (GTK_WIDGET (text)); + } + draw_cursor (text, FALSE); +} + +void +gtk_text_insert (GtkText *text, + GdkFont *font, + GdkColor *fore, + GdkColor *back, + const char *chars, + gint nchars) +{ + GtkEditable *editable = GTK_EDITABLE (text); + gboolean frozen = FALSE; + + gint new_line_count = 1; + guint old_height = 0; + guint length; + guint i; + gint numwcs; + + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + if (nchars > 0) + g_return_if_fail (chars != NULL); + else + { + if (!nchars || !chars) + return; + nchars = strlen (chars); + } + length = nchars; + + if (!text->freeze_count && (length > FREEZE_LENGTH)) + { + gtk_text_freeze (text); + frozen = TRUE; + } + + if (!text->freeze_count && (text->line_start_cache != NULL)) + { + find_line_containing_point (text, text->point.index, TRUE); + old_height = total_line_height (text, text->current_line, 1); + } + + if ((TEXT_LENGTH (text) == 0) && (text->use_wchar == FALSE)) + { + GtkWidget *widget; + widget = GTK_WIDGET (text); + gtk_widget_ensure_style (widget); + if ((widget->style) && (widget->style->font->type == GDK_FONT_FONTSET)) + { + text->use_wchar = TRUE; + g_free (text->text.ch); + text->text.wc = g_new (GdkWChar, INITIAL_BUFFER_SIZE); + text->text_len = INITIAL_BUFFER_SIZE; + if (text->scratch_buffer.ch) + g_free (text->scratch_buffer.ch); + text->scratch_buffer.wc = NULL; + text->scratch_buffer_len = 0; + } + } + + move_gap (text, text->point.index); + make_forward_space (text, length); + + if (text->use_wchar) + { + char *chars_nt = (char *)chars; + if (nchars > 0) + { + chars_nt = g_new (char, length+1); + memcpy (chars_nt, chars, length); + chars_nt[length] = 0; + } + numwcs = gdk_mbstowcs (text->text.wc + text->gap_position, chars_nt, + length); + if (chars_nt != chars) + g_free(chars_nt); + if (numwcs < 0) + numwcs = 0; + } + else + { + numwcs = length; + memcpy(text->text.ch + text->gap_position, chars, length); + } + + if (!text->freeze_count && (text->line_start_cache != NULL)) + { + if (text->use_wchar) + { + for (i=0; i<numwcs; i++) + if (text->text.wc[text->gap_position + i] == '\n') + new_line_count++; + } + else + { + for (i=0; i<numwcs; i++) + if (text->text.ch[text->gap_position + i] == '\n') + new_line_count++; + } + } + + if (numwcs > 0) + { + insert_text_property (text, font, fore, back, numwcs); + + text->gap_size -= numwcs; + text->gap_position += numwcs; + + if (text->point.index < text->first_line_start_index) + text->first_line_start_index += numwcs; + if (text->point.index < editable->selection_start_pos) + editable->selection_start_pos += numwcs; + if (text->point.index < editable->selection_end_pos) + editable->selection_end_pos += numwcs; + /* We'll reset the cursor later anyways if we aren't frozen */ + if (text->point.index < text->cursor_mark.index) + text->cursor_mark.index += numwcs; + + advance_mark_n (&text->point, numwcs); + + if (!text->freeze_count && (text->line_start_cache != NULL)) + insert_expose (text, old_height, numwcs, new_line_count); + } + + if (frozen) + gtk_text_thaw (text); +} + +gint +gtk_text_backward_delete (GtkText *text, + guint nchars) +{ + g_return_val_if_fail (text != NULL, 0); + g_return_val_if_fail (GTK_IS_TEXT (text), 0); + + if (nchars > text->point.index || nchars <= 0) + return FALSE; + + gtk_text_set_point (text, text->point.index - nchars); + + return gtk_text_forward_delete (text, nchars); +} + +gint +gtk_text_forward_delete (GtkText *text, + guint nchars) +{ + guint old_lines, old_height; + GtkEditable *editable = GTK_EDITABLE (text); + gboolean frozen = FALSE; + + g_return_val_if_fail (text != NULL, 0); + g_return_val_if_fail (GTK_IS_TEXT (text), 0); + + if (text->point.index + nchars > TEXT_LENGTH (text) || nchars <= 0) + return FALSE; + + if (!text->freeze_count && nchars > FREEZE_LENGTH) + { + gtk_text_freeze (text); + frozen = TRUE; + } + + if (!text->freeze_count && text->line_start_cache != NULL) + { + /* We need to undraw the cursor here, since we may later + * delete the cursor's property + */ + undraw_cursor (text, FALSE); + find_line_containing_point (text, text->point.index, TRUE); + compute_lines_pixels (text, nchars, &old_lines, &old_height); + } + + /* FIXME, or resizing after deleting will be odd */ + if (text->point.index < text->first_line_start_index) + { + if (text->point.index + nchars >= text->first_line_start_index) + { + text->first_line_start_index = text->point.index; + while ((text->first_line_start_index > 0) && + (GTK_TEXT_INDEX (text, text->first_line_start_index - 1) + != LINE_DELIM)) + text->first_line_start_index -= 1; + + } + else + text->first_line_start_index -= nchars; + } + + if (text->point.index < editable->selection_start_pos) + editable->selection_start_pos -= + MIN(nchars, editable->selection_start_pos - text->point.index); + if (text->point.index < editable->selection_end_pos) + editable->selection_end_pos -= + MIN(nchars, editable->selection_end_pos - text->point.index); + /* We'll reset the cursor later anyways if we aren't frozen */ + if (text->point.index < text->cursor_mark.index) + move_mark_n (&text->cursor_mark, + -MIN(nchars, text->cursor_mark.index - text->point.index)); + + move_gap (text, text->point.index); + + text->gap_size += nchars; + + delete_text_property (text, nchars); + + if (!text->freeze_count && (text->line_start_cache != NULL)) + { + delete_expose (text, nchars, old_lines, old_height); + draw_cursor (text, FALSE); + } + + if (frozen) + gtk_text_thaw (text); + + return TRUE; +} + +static void +gtk_text_set_position (GtkEditable *editable, + gint position) +{ + GtkText *text = (GtkText *) editable; + + undraw_cursor (text, FALSE); + text->cursor_mark = find_mark (text, position); + find_cursor (text, TRUE); + draw_cursor (text, FALSE); + gtk_editable_select_region (editable, 0, 0); +} + +static gchar * +gtk_text_get_chars (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + GtkText *text; + + gchar *retval; + + g_return_val_if_fail (editable != NULL, NULL); + g_return_val_if_fail (GTK_IS_TEXT (editable), NULL); + text = GTK_TEXT (editable); + + if (end_pos < 0) + end_pos = TEXT_LENGTH (text); + + if ((start_pos < 0) || + (end_pos > TEXT_LENGTH (text)) || + (end_pos < start_pos)) + return NULL; + + move_gap (text, TEXT_LENGTH (text)); + make_forward_space (text, 1); + + if (text->use_wchar) + { + GdkWChar ch; + ch = text->text.wc[end_pos]; + text->text.wc[end_pos] = 0; + retval = gdk_wcstombs (text->text.wc + start_pos); + text->text.wc[end_pos] = ch; + } + else + { + guchar ch; + ch = text->text.ch[end_pos]; + text->text.ch[end_pos] = 0; + retval = g_strdup (text->text.ch + start_pos); + text->text.ch[end_pos] = ch; + } + + return retval; +} + + +static void +gtk_text_destroy (GtkObject *object) +{ + GtkText *text; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_TEXT (object)); + + text = (GtkText*) object; + + gtk_signal_disconnect_by_data (GTK_OBJECT (text->hadj), text); + gtk_signal_disconnect_by_data (GTK_OBJECT (text->vadj), text); + + if (text->timer) + { + gtk_timeout_remove (text->timer); + text->timer = 0; + } + + GTK_OBJECT_CLASS(parent_class)->destroy (object); +} + +static void +gtk_text_finalize (GtkObject *object) +{ + GtkText *text; + GList *tmp_list; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_TEXT (object)); + + text = (GtkText *)object; + + gtk_object_unref (GTK_OBJECT (text->hadj)); + gtk_object_unref (GTK_OBJECT (text->vadj)); + + /* Clean up the internal structures */ + if (text->use_wchar) + g_free (text->text.wc); + else + g_free (text->text.ch); + + tmp_list = text->text_properties; + while (tmp_list) + { + destroy_text_property (tmp_list->data); + tmp_list = tmp_list->next; + } + + if (text->current_font) + text_font_unref (text->current_font); + + g_list_free (text->text_properties); + + if (text->use_wchar) + { + if (text->scratch_buffer.wc) + g_free (text->scratch_buffer.wc); + } + else + { + if (text->scratch_buffer.ch) + g_free (text->scratch_buffer.ch); + } + + g_list_free (text->tab_stops); + + GTK_OBJECT_CLASS(parent_class)->finalize (object); +} + +static void +gtk_text_realize (GtkWidget *widget) +{ + GtkText *text; + GtkEditable *editable; + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_TEXT (widget)); + + text = GTK_TEXT (widget); + editable = GTK_EDITABLE (widget); + GTK_WIDGET_SET_FLAGS (text, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_KEY_PRESS_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, text); + + attributes.x = (widget->style->klass->xthickness + TEXT_BORDER_ROOM); + attributes.y = (widget->style->klass->ythickness + TEXT_BORDER_ROOM); + attributes.width = MAX (1, (gint)widget->allocation.width - (gint)attributes.x * 2); + attributes.height = MAX (1, (gint)widget->allocation.height - (gint)attributes.y * 2); + + attributes.cursor = gdk_cursor_new (GDK_XTERM); + attributes_mask |= GDK_WA_CURSOR; + + text->text_area = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (text->text_area, text); + + gdk_cursor_destroy (attributes.cursor); /* The X server will keep it around as long as necessary */ + + widget->style = gtk_style_attach (widget->style, widget->window); + + /* Can't call gtk_style_set_background here because it's handled specially */ + gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]); + gdk_window_set_background (text->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]); + + if (widget->style->bg_pixmap[GTK_STATE_NORMAL]) + text->bg_gc = create_bg_gc (text); + + text->line_wrap_bitmap = gdk_bitmap_create_from_data (text->text_area, + (gchar*) line_wrap_bits, + line_wrap_width, + line_wrap_height); + + text->line_arrow_bitmap = gdk_bitmap_create_from_data (text->text_area, + (gchar*) line_arrow_bits, + line_arrow_width, + line_arrow_height); + + text->gc = gdk_gc_new (text->text_area); + gdk_gc_set_exposures (text->gc, TRUE); + gdk_gc_set_foreground (text->gc, &widget->style->text[GTK_STATE_NORMAL]); + +#ifdef USE_XIM + if (gdk_im_ready () && (editable->ic_attr = gdk_ic_attr_new ()) != NULL) + { + gint width, height; + GdkColormap *colormap; + GdkEventMask mask; + 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 = text->text_area; + + if ((colormap = gtk_widget_get_colormap (widget)) != + gtk_widget_get_default_colormap ()) + { + attrmask |= GDK_IC_PREEDIT_COLORMAP; + attr->preedit_colormap = colormap; + } + + 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; + } + + attrmask |= GDK_IC_PREEDIT_POSITION_REQ; + gdk_window_get_size (text->text_area, &width, &height); + 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 (text->text_area); + mask |= gdk_ic_get_events (editable->ic); + gdk_window_set_events (text->text_area, mask); + + if (GTK_WIDGET_HAS_FOCUS (widget)) + gdk_im_begin (editable->ic, text->text_area); + } + } +#endif + + realize_properties (text); + gdk_window_show (text->text_area); + init_properties (text); + + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_claim_selection (editable, TRUE, GDK_CURRENT_TIME); + + recompute_geometry (text); +} + +static void +gtk_text_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + GtkText *text = GTK_TEXT (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]); + gdk_window_set_background (text->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]); + + if (text->bg_gc) + { + gdk_gc_destroy (text->bg_gc); + text->bg_gc = NULL; + } + + if (widget->style->bg_pixmap[GTK_STATE_NORMAL]) + text->bg_gc = create_bg_gc (text); + + recompute_geometry (text); + } + + if (text->current_font) + text_font_unref (text->current_font); + text->current_font = get_text_font (widget->style->font); +} + +static void +gtk_text_state_changed (GtkWidget *widget, + GtkStateType previous_state) +{ + GtkText *text = GTK_TEXT (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]); + gdk_window_set_background (text->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]); + } +} + +static void +gtk_text_unrealize (GtkWidget *widget) +{ + GtkText *text; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_TEXT (widget)); + + text = GTK_TEXT (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 + + gdk_window_set_user_data (text->text_area, NULL); + gdk_window_destroy (text->text_area); + text->text_area = NULL; + + gdk_gc_destroy (text->gc); + text->gc = NULL; + + if (text->bg_gc) + { + gdk_gc_destroy (text->bg_gc); + text->bg_gc = NULL; + } + + gdk_pixmap_unref (text->line_wrap_bitmap); + gdk_pixmap_unref (text->line_arrow_bitmap); + + unrealize_properties (text); + + free_cache (text); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +clear_focus_area (GtkText *text, gint area_x, gint area_y, gint area_width, gint area_height) +{ + GtkWidget *widget = GTK_WIDGET (text); + GdkGC *gc; + + gint ythick = TEXT_BORDER_ROOM + widget->style->klass->ythickness; + gint xthick = TEXT_BORDER_ROOM + widget->style->klass->xthickness; + + gint width, height; + + if (area_width == 0 || area_height == 0) + return; + + if (widget->style->bg_pixmap[GTK_STATE_NORMAL]) + { + gdk_window_get_size (widget->style->bg_pixmap[GTK_STATE_NORMAL], &width, &height); + + gdk_gc_set_ts_origin (text->bg_gc, + (- (gint)text->first_onscreen_hor_pixel + xthick) % width, + (- (gint)text->first_onscreen_ver_pixel + ythick) % height); + + gc = text->bg_gc; + } + else + gc = widget->style->bg_gc[widget->state]; + + + gdk_draw_rectangle (GTK_WIDGET (text)->window, gc, TRUE, + area_x, area_y, area_width, area_height); +} + +static void +gtk_text_draw_focus (GtkWidget *widget) +{ + GtkText *text; + gint width, height; + gint x, y; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_TEXT (widget)); + + text = GTK_TEXT (widget); + + if (GTK_WIDGET_DRAWABLE (widget)) + { + gint ythick = widget->style->klass->ythickness; + gint xthick = widget->style->klass->xthickness; + gint xextra = TEXT_BORDER_ROOM; + gint yextra = TEXT_BORDER_ROOM; + + TDEBUG (("in gtk_text_draw_focus\n")); + + x = 0; + y = 0; + width = widget->allocation.width; + height = widget->allocation.height; + + if (GTK_WIDGET_HAS_FOCUS (widget)) + { + x += 1; + y += 1; + width -= 2; + height -= 2; + xextra -= 1; + yextra -= 1; + + gtk_paint_focus (widget->style, widget->window, + NULL, widget, "text", + 0, 0, + widget->allocation.width - 1, + widget->allocation.height - 1); + } + + gtk_paint_shadow (widget->style, widget->window, + GTK_STATE_NORMAL, GTK_SHADOW_IN, + NULL, widget, "text", + x, y, width, height); + + x += xthick; + y += ythick; + width -= 2 * xthick; + height -= 2 * ythick; + + /* top rect */ + clear_focus_area (text, x, y, width, yextra); + /* left rect */ + clear_focus_area (text, x, y + yextra, + xextra, y + height - 2 * yextra); + /* right rect */ + clear_focus_area (text, x + width - xextra, y + yextra, + xextra, height - 2 * ythick); + /* bottom rect */ + clear_focus_area (text, x, x + height - yextra, width, yextra); + } + else + { + TDEBUG (("in gtk_text_draw_focus (undrawable !!!)\n")); + } +} + +static void +gtk_text_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + gint xthickness; + gint ythickness; + gint char_height; + gint char_width; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_TEXT (widget)); + g_return_if_fail (requisition != NULL); + + xthickness = widget->style->klass->xthickness + TEXT_BORDER_ROOM; + ythickness = widget->style->klass->ythickness + TEXT_BORDER_ROOM; + + char_height = MIN_TEXT_HEIGHT_LINES * (widget->style->font->ascent + + widget->style->font->descent); + + char_width = MIN_TEXT_WIDTH_LINES * (gdk_text_width (widget->style->font, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + 26) + / 26); + + requisition->width = char_width + xthickness * 2; + requisition->height = char_height + ythickness * 2; +} + +static void +gtk_text_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkText *text; + GtkEditable *editable; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_TEXT (widget)); + g_return_if_fail (allocation != NULL); + + text = GTK_TEXT (widget); + editable = GTK_EDITABLE (widget); + + widget->allocation = *allocation; + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_move_resize (widget->window, + allocation->x, allocation->y, + allocation->width, allocation->height); + + gdk_window_move_resize (text->text_area, + widget->style->klass->xthickness + TEXT_BORDER_ROOM, + widget->style->klass->ythickness + TEXT_BORDER_ROOM, + MAX (1, (gint)widget->allocation.width - (gint)(widget->style->klass->xthickness + + (gint)TEXT_BORDER_ROOM) * 2), + MAX (1, (gint)widget->allocation.height - (gint)(widget->style->klass->ythickness + + (gint)TEXT_BORDER_ROOM) * 2)); + +#ifdef USE_XIM + if (editable->ic && (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION)) + { + gint width, height; + + gdk_window_get_size (text->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 + + recompute_geometry (text); + } +} + +static void +gtk_text_draw (GtkWidget *widget, + GdkRectangle *area) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_TEXT (widget)); + g_return_if_fail (area != NULL); + + if (GTK_WIDGET_DRAWABLE (widget)) + { + expose_text (GTK_TEXT (widget), area, TRUE); + gtk_widget_draw_focus (widget); + } +} + +static gint +gtk_text_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (event->window == GTK_TEXT (widget)->text_area) + { + TDEBUG (("in gtk_text_expose (expose)\n")); + expose_text (GTK_TEXT (widget), &event->area, TRUE); + } + else if (event->count == 0) + { + TDEBUG (("in gtk_text_expose (focus)\n")); + gtk_widget_draw_focus (widget); + } + + return FALSE; +} + +static gint +gtk_text_scroll_timeout (gpointer data) +{ + GtkText *text; + GdkEventMotion event; + gint x, y; + GdkModifierType mask; + + GDK_THREADS_ENTER (); + + text = GTK_TEXT (data); + + text->timer = 0; + gdk_window_get_pointer (text->text_area, &x, &y, &mask); + + if (mask & (GDK_BUTTON1_MASK | GDK_BUTTON3_MASK)) + { + event.is_hint = 0; + event.x = x; + event.y = y; + event.state = mask; + + gtk_text_motion_notify (GTK_WIDGET (text), &event); + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gtk_text_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + GtkText *text; + GtkEditable *editable; + static GdkAtom ctext_atom = GDK_NONE; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (ctext_atom == GDK_NONE) + ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); + + text = GTK_TEXT (widget); + editable = GTK_EDITABLE (widget); + + if (text->button && (event->button != text->button)) + return FALSE; + + text->button = event->button; + + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + + if (event->button == 1) + { + switch (event->type) + { + case GDK_BUTTON_PRESS: + gtk_grab_add (widget); + + undraw_cursor (text, FALSE); + find_mouse_cursor (text, (gint)event->x, (gint)event->y); + draw_cursor (text, FALSE); + + /* Set it now, so we display things right. We'll unset it + * later if things don't work out */ + editable->has_selection = TRUE; + gtk_text_set_selection (GTK_EDITABLE(text), + text->cursor_mark.index, + text->cursor_mark.index); + + break; + + case GDK_2BUTTON_PRESS: + gtk_text_select_word (text, event->time); + break; + + case GDK_3BUTTON_PRESS: + gtk_text_select_line (text, event->time); + break; + + default: + break; + } + } + else if (event->type == GDK_BUTTON_PRESS) + { + if ((event->button == 2) && editable->editable) + { + if (editable->selection_start_pos == editable->selection_end_pos || + editable->has_selection) + { + undraw_cursor (text, FALSE); + find_mouse_cursor (text, (gint)event->x, (gint)event->y); + draw_cursor (text, FALSE); + + } + + gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, + ctext_atom, event->time); + } + else + { + gtk_grab_add (widget); + + undraw_cursor (text, FALSE); + find_mouse_cursor (text, event->x, event->y); + draw_cursor (text, FALSE); + + gtk_text_set_selection (GTK_EDITABLE(text), + text->cursor_mark.index, + text->cursor_mark.index); + + editable->has_selection = FALSE; + if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window) + gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, event->time); + } + } + + return FALSE; +} + +static gint +gtk_text_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GtkText *text; + GtkEditable *editable; + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + text = GTK_TEXT (widget); + + gtk_grab_remove (widget); + + if (text->button != event->button) + return FALSE; + + text->button = 0; + + if (text->timer) + { + gtk_timeout_remove (text->timer); + text->timer = 0; + } + + if (event->button == 1) + { + text = GTK_TEXT (widget); + editable = GTK_EDITABLE (widget); + + 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_text_update_text (editable, editable->selection_start_pos, + editable->selection_end_pos); + } + 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); + } + + undraw_cursor (text, FALSE); + find_cursor (text, TRUE); + draw_cursor (text, FALSE); + + return FALSE; +} + +static gint +gtk_text_motion_notify (GtkWidget *widget, + GdkEventMotion *event) +{ + GtkText *text; + gint x, y; + gint height; + GdkModifierType mask; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + text = GTK_TEXT (widget); + + x = event->x; + y = event->y; + mask = event->state; + if (event->is_hint || (text->text_area != event->window)) + { + gdk_window_get_pointer (text->text_area, &x, &y, &mask); + } + + if ((text->button == 0) || + !(mask & (GDK_BUTTON1_MASK | GDK_BUTTON3_MASK))) + return FALSE; + + gdk_window_get_size (text->text_area, NULL, &height); + + if ((y < 0) || (y > height)) + { + if (text->timer == 0) + { + text->timer = gtk_timeout_add (SCROLL_TIME, + gtk_text_scroll_timeout, + text); + + if (y < 0) + scroll_int (text, y/2); + else + scroll_int (text, (y - height)/2); + } + else + return FALSE; + } + + undraw_cursor (GTK_TEXT (widget), FALSE); + find_mouse_cursor (GTK_TEXT (widget), x, y); + draw_cursor (GTK_TEXT (widget), FALSE); + + gtk_text_set_selection (GTK_EDITABLE(text), + GTK_EDITABLE(text)->selection_start_pos, + text->cursor_mark.index); + + return FALSE; +} + +static void +gtk_text_insert_text (GtkEditable *editable, + const gchar *new_text, + gint new_text_length, + gint *position) +{ + GtkText *text = GTK_TEXT (editable); + GdkFont *font; + GdkColor *fore, *back; + + TextProperty *property; + + gtk_text_set_point (text, *position); + + property = MARK_CURRENT_PROPERTY (&text->point); + font = property->flags & PROPERTY_FONT ? property->font->gdk_font : NULL; + fore = property->flags & PROPERTY_FOREGROUND ? &property->fore_color : NULL; + back = property->flags & PROPERTY_BACKGROUND ? &property->back_color : NULL; + + gtk_text_insert (text, font, fore, back, new_text, new_text_length); + + *position = text->point.index; +} + +static void +gtk_text_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + GtkText *text; + + g_return_if_fail (start_pos >= 0); + + text = GTK_TEXT (editable); + + gtk_text_set_point (text, start_pos); + if (end_pos < 0) + end_pos = TEXT_LENGTH (text); + + if (end_pos > start_pos) + gtk_text_forward_delete (text, end_pos - start_pos); +} + +static gint +gtk_text_key_press (GtkWidget *widget, + GdkEventKey *event) +{ + GtkText *text; + GtkEditable *editable; + gchar key; + gint return_val; + gint position; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + return_val = FALSE; + + text = GTK_TEXT (widget); + editable = GTK_EDITABLE (widget); + + key = event->keyval; + return_val = TRUE; + + if ((GTK_EDITABLE(text)->editable == FALSE)) + { + switch (event->keyval) + { + case GDK_Home: + if (event->state & GDK_CONTROL_MASK) + scroll_int (text, -text->vadj->value); + else + return_val = FALSE; + break; + case GDK_End: + if (event->state & GDK_CONTROL_MASK) + scroll_int (text, +text->vadj->upper); + else + return_val = FALSE; + break; + case GDK_Page_Up: scroll_int (text, -text->vadj->page_increment); break; + case GDK_Page_Down: scroll_int (text, +text->vadj->page_increment); break; + case GDK_Up: scroll_int (text, -KEY_SCROLL_PIXELS); break; + case GDK_Down: scroll_int (text, +KEY_SCROLL_PIXELS); break; + case GDK_Return: + if (event->state & GDK_CONTROL_MASK) + gtk_signal_emit_by_name (GTK_OBJECT (text), "activate"); + else + return_val = FALSE; + break; + default: + return_val = FALSE; + break; + } + } + else + { + gint extend_selection; + gint extend_start; + guint initial_pos = editable->current_pos; + + text->point = find_mark (text, text->cursor_mark.index); + + extend_selection = event->state & GDK_SHIFT_MASK; + extend_start = FALSE; + + if (extend_selection) + { + editable->has_selection = TRUE; + + if (editable->selection_start_pos == editable->selection_end_pos) + { + editable->selection_start_pos = text->point.index; + editable->selection_end_pos = text->point.index; + } + + extend_start = (text->point.index == editable->selection_start_pos); + } + + switch (event->keyval) + { + case GDK_Home: + if (event->state & GDK_CONTROL_MASK) + move_cursor_buffer_ver (text, -1); + else + gtk_text_move_beginning_of_line (text); + break; + case GDK_End: + if (event->state & GDK_CONTROL_MASK) + move_cursor_buffer_ver (text, +1); + else + gtk_text_move_end_of_line (text); + break; + case GDK_Page_Up: move_cursor_page_ver (text, -1); break; + case GDK_Page_Down: move_cursor_page_ver (text, +1); break; + /* CUA has Ctrl-Up/Ctrl-Down as paragraph up down */ + case GDK_Up: move_cursor_ver (text, -1); break; + case GDK_Down: move_cursor_ver (text, +1); break; + case GDK_Left: + if (event->state & GDK_CONTROL_MASK) + gtk_text_move_backward_word (text); + else + move_cursor_hor (text, -1); + break; + case GDK_Right: + if (event->state & GDK_CONTROL_MASK) + gtk_text_move_forward_word (text); + else + move_cursor_hor (text, +1); + break; + + case GDK_BackSpace: + if (event->state & GDK_CONTROL_MASK) + gtk_text_delete_backward_word (text); + else + gtk_text_delete_backward_character (text); + break; + case GDK_Clear: + gtk_text_delete_line (text); + break; + case GDK_Insert: + 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(text) -- IMPLEMENT */ + } + break; + case GDK_Delete: + if (event->state & GDK_CONTROL_MASK) + gtk_text_delete_forward_word (text); + else if (event->state & GDK_SHIFT_MASK) + { + extend_selection = FALSE; + gtk_editable_cut_clipboard (editable); + } + else + gtk_text_delete_forward_character (text); + break; + case GDK_Tab: + position = text->point.index; + gtk_editable_insert_text (editable, "\t", 1, &position); + break; + case GDK_Return: + if (event->state & GDK_CONTROL_MASK) + gtk_signal_emit_by_name (GTK_OBJECT (text), "activate"); + else + { + position = text->point.index; + gtk_editable_insert_text (editable, "\n", 1, &position); + } + break; + case GDK_Escape: + /* Don't insert literally */ + return_val = FALSE; + break; + + default: + return_val = FALSE; + + if (event->state & GDK_CONTROL_MASK) + { + if ((key >= 'A') && (key <= 'Z')) + key -= 'A' - 'a'; + + if ((key >= 'a') && (key <= 'z') && control_keys[(int) (key - 'a')]) + { + (* control_keys[(int) (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[(int) (key - 'a')]) + { + (* alt_keys[(int) (key - 'a')]) (editable, event->time); + return_val = TRUE; + } + + break; + } + else if (event->length > 0) + { + extend_selection = FALSE; + + gtk_editable_delete_selection (editable); + position = text->point.index; + gtk_editable_insert_text (editable, event->string, event->length, &position); + + return_val = TRUE; + } + else + return_val = FALSE; + } + + if (return_val && (editable->current_pos != initial_pos)) + { + if (extend_selection) + { + if (editable->current_pos < editable->selection_start_pos) + gtk_text_set_selection (editable, editable->current_pos, + editable->selection_end_pos); + else if (editable->current_pos > editable->selection_end_pos) + gtk_text_set_selection (editable, editable->selection_start_pos, + editable->current_pos); + else + { + if (extend_start) + gtk_text_set_selection (editable, editable->current_pos, + editable->selection_end_pos); + else + gtk_text_set_selection (editable, editable->selection_start_pos, + editable->current_pos); + } + } + else + gtk_text_set_selection (editable, 0, 0); + + gtk_editable_claim_selection (editable, + editable->selection_start_pos != editable->selection_end_pos, + event->time); + } + } + + return return_val; +} + +static gint +gtk_text_focus_in (GtkWidget *widget, + GdkEventFocus *event) +{ + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + TDEBUG (("in gtk_text_focus_in\n")); + + 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_TEXT(widget)->text_area); +#endif + + draw_cursor (GTK_TEXT(widget), TRUE); + + return FALSE; +} + +static gint +gtk_text_focus_out (GtkWidget *widget, + GdkEventFocus *event) +{ + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TEXT (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + TDEBUG (("in gtk_text_focus_out\n")); + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + gtk_widget_draw_focus (widget); + + undraw_cursor (GTK_TEXT(widget), TRUE); + +#ifdef USE_XIM + gdk_im_end (); +#endif + + return FALSE; +} + +static void +gtk_text_adjustment (GtkAdjustment *adjustment, + GtkText *text) +{ + gfloat old_val; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + + /* Clamp the value here, because we'll get really confused + * if someone tries to move the adjusment outside of the + * allowed bounds + */ + old_val = adjustment->value; + + adjustment->value = MIN (adjustment->value, adjustment->upper - adjustment->page_size); + adjustment->value = MAX (adjustment->value, 0.0); + + if (adjustment->value != old_val) + { + gtk_signal_handler_block_by_func (GTK_OBJECT (adjustment), + GTK_SIGNAL_FUNC (gtk_text_adjustment), + text); + gtk_adjustment_changed (adjustment); + gtk_signal_handler_unblock_by_func (GTK_OBJECT (adjustment), + GTK_SIGNAL_FUNC (gtk_text_adjustment), + text); + } + + /* Just ignore it if we haven't been size-allocated and realized yet */ + if (text->line_start_cache == NULL) + return; + + if (adjustment == text->hadj) + { + g_warning ("horizontal scrolling not implemented"); + } + else + { + gint diff = ((gint)adjustment->value) - text->last_ver_value; + + if (diff != 0) + { + undraw_cursor (text, FALSE); + + if (diff > 0) + scroll_down (text, diff); + else /* if (diff < 0) */ + scroll_up (text, diff); + + draw_cursor (text, FALSE); + + text->last_ver_value = adjustment->value; + } + } +} + +static void +gtk_text_disconnect (GtkAdjustment *adjustment, + GtkText *text) +{ + g_return_if_fail (adjustment != NULL); + g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + g_return_if_fail (text != NULL); + g_return_if_fail (GTK_IS_TEXT (text)); + + if (adjustment == text->hadj) + gtk_text_set_adjustments (text, NULL, text->vadj); + if (adjustment == text->vadj) + gtk_text_set_adjustments (text, text->hadj, NULL); +} + + +static GtkPropertyMark +find_this_line_start_mark (GtkText* text, guint point_position, const GtkPropertyMark* near) +{ + GtkPropertyMark mark; + + mark = find_mark_near (text, point_position, near); + + while (mark.index > 0 && + GTK_TEXT_INDEX (text, mark.index - 1) != LINE_DELIM) + decrement_mark (&mark); + + return mark; +} + +static void +init_tab_cont (GtkText* text, PrevTabCont* tab_cont) +{ + tab_cont->pixel_offset = 0; + tab_cont->tab_start.tab_stops = text->tab_stops; + tab_cont->tab_start.to_next_tab = (gulong) text->tab_stops->data; + + if (!tab_cont->tab_start.to_next_tab) + tab_cont->tab_start.to_next_tab = text->default_tab_width; +} + +static void +line_params_iterate (GtkText* text, + const GtkPropertyMark* mark0, + const PrevTabCont* tab_mark0, + gint8 alloc, + void* data, + LineIteratorFunction iter) + /* mark0 MUST be a real line start. if ALLOC, allocate line params + * from a mem chunk. DATA is passed to ITER_CALL, which is called + * for each line following MARK, iteration continues unless ITER_CALL + * returns TRUE. */ +{ + GtkPropertyMark mark = *mark0; + PrevTabCont tab_conts[2]; + LineParams *lp, lpbuf; + gint tab_cont_index = 0; + + if (tab_mark0) + tab_conts[0] = *tab_mark0; + else + init_tab_cont (text, tab_conts); + + for (;;) + { + if (alloc) + lp = g_chunk_new (LineParams, params_mem_chunk); + else + lp = &lpbuf; + + *lp = find_line_params (text, &mark, tab_conts + tab_cont_index, + tab_conts + (tab_cont_index + 1) % 2); + + if ((*iter) (text, lp, data)) + return; + + if (LAST_INDEX (text, lp->end)) + break; + + mark = lp->end; + advance_mark (&mark); + tab_cont_index = (tab_cont_index + 1) % 2; + } +} + +static gint +fetch_lines_iterator (GtkText* text, LineParams* lp, void* data) +{ + FetchLinesData *fldata = (FetchLinesData*) data; + + fldata->new_lines = g_list_prepend (fldata->new_lines, lp); + + switch (fldata->fl_type) + { + case FetchLinesCount: + if (!text->line_wrap || !lp->wraps) + fldata->data += 1; + + if (fldata->data >= fldata->data_max) + return TRUE; + + break; + case FetchLinesPixels: + + fldata->data += LINE_HEIGHT(*lp); + + if (fldata->data >= fldata->data_max) + return TRUE; + + break; + } + + return FALSE; +} + +static GList* +fetch_lines (GtkText* text, + const GtkPropertyMark* mark0, + const PrevTabCont* tab_cont0, + FLType fl_type, + gint data) +{ + FetchLinesData fl_data; + + fl_data.new_lines = NULL; + fl_data.data = 0; + fl_data.data_max = data; + fl_data.fl_type = fl_type; + + line_params_iterate (text, mark0, tab_cont0, TRUE, &fl_data, fetch_lines_iterator); + + return g_list_reverse (fl_data.new_lines); +} + +static void +fetch_lines_backward (GtkText* text) +{ + GList* new_lines = NULL, *new_line_start; + GtkPropertyMark mark; + + if (CACHE_DATA(text->line_start_cache).start.index == 0) + return; + + mark = find_this_line_start_mark (text, + CACHE_DATA(text->line_start_cache).start.index - 1, + &CACHE_DATA(text->line_start_cache).start); + + new_line_start = new_lines = fetch_lines (text, &mark, NULL, FetchLinesCount, 1); + + while (new_line_start->next) + new_line_start = new_line_start->next; + + new_line_start->next = text->line_start_cache; + text->line_start_cache->prev = new_line_start; +} + +static void +fetch_lines_forward (GtkText* text, gint line_count) +{ + GtkPropertyMark mark; + GList* line = text->line_start_cache; + + while(line->next) + line = line->next; + + mark = CACHE_DATA(line).end; + + if (LAST_INDEX (text, mark)) + return; + + advance_mark(&mark); + + line->next = fetch_lines (text, &mark, &CACHE_DATA(line).tab_cont_next, FetchLinesCount, line_count); + + if (line->next) + line->next->prev = line; +} + +/* Compute the number of lines, and vertical pixels for n characters + * starting from the point + */ +static void +compute_lines_pixels (GtkText* text, guint char_count, + guint *lines, guint *pixels) +{ + GList *line = text->current_line; + gint chars_left = char_count; + + *lines = 0; + *pixels = 0; + + /* If chars_left == 0, that means we're joining two lines in a + * deletion, so add in the values for the next line as well + */ + for (; line && chars_left >= 0; line = line->next) + { + *pixels += LINE_HEIGHT(CACHE_DATA(line)); + + if (line == text->current_line) + chars_left -= CACHE_DATA(line).end.index - text->point.index + 1; + else + chars_left -= CACHE_DATA(line).end.index - CACHE_DATA(line).start.index + 1; + + if (!text->line_wrap || !CACHE_DATA(line).wraps) + *lines += 1; + else + if (chars_left < 0) + chars_left = 0; /* force another loop */ + + if (!line->next) + fetch_lines_forward (text, 1); + } +} + +static gint +total_line_height (GtkText* text, GList* line, gint line_count) +{ + gint height = 0; + + for (; line && line_count > 0; line = line->next) + { + height += LINE_HEIGHT(CACHE_DATA(line)); + + if (!text->line_wrap || !CACHE_DATA(line).wraps) + line_count -= 1; + + if (!line->next) + fetch_lines_forward (text, line_count); + } + + return height; +} + +static void +swap_lines (GtkText* text, GList* old, GList* new, guint old_line_count) +{ + if (old == text->line_start_cache) + { + GList* last; + + for (; old_line_count > 0; old_line_count -= 1) + { + while (text->line_start_cache && + text->line_wrap && + CACHE_DATA(text->line_start_cache).wraps) + remove_cache_line(text, text->line_start_cache); + + remove_cache_line(text, text->line_start_cache); + } + + last = g_list_last (new); + + last->next = text->line_start_cache; + + if (text->line_start_cache) + text->line_start_cache->prev = last; + + text->line_start_cache = new; + } + else + { + GList *last; + + g_assert (old->prev); + + last = old->prev; + + for (; old_line_count > 0; old_line_count -= 1) + { + while (old && text->line_wrap && CACHE_DATA(old).wraps) + old = remove_cache_line (text, old); + + old = remove_cache_line (text, old); + } + + last->next = new; + new->prev = last; + + last = g_list_last (new); + + last->next = old; + + if (old) + old->prev = last; + } +} + +static void +correct_cache_delete (GtkText* text, gint nchars, gint lines) +{ + GList* cache = text->current_line; + gint i; + + for (i = 0; cache && i < lines; i += 1, cache = cache->next) + /* nothing */; + + for (; cache; cache = cache->next) + { + GtkPropertyMark *start = &CACHE_DATA(cache).start; + GtkPropertyMark *end = &CACHE_DATA(cache).end; + + start->index -= nchars; + end->index -= nchars; + + if (LAST_INDEX (text, text->point) && + start->index == text->point.index) + *start = text->point; + else if (start->property == text->point.property) + start->offset = start->index - (text->point.index - text->point.offset); + + if (LAST_INDEX (text, text->point) && + end->index == text->point.index) + *end = text->point; + if (end->property == text->point.property) + end->offset = end->index - (text->point.index - text->point.offset); + + /*TEXT_ASSERT_MARK(text, start, "start");*/ + /*TEXT_ASSERT_MARK(text, end, "end");*/ + } +} + +static void +delete_expose (GtkText* text, guint nchars, guint old_lines, guint old_pixels) +{ + GtkWidget *widget = GTK_WIDGET (text); + + gint pixel_height; + guint new_pixels = 0; + GdkRectangle rect; + GList* new_line = NULL; + gint width, height; + + text->cursor_virtual_x = 0; + + correct_cache_delete (text, nchars, old_lines); + + pixel_height = pixel_height_of(text, text->current_line) - + LINE_HEIGHT(CACHE_DATA(text->current_line)); + + if (CACHE_DATA(text->current_line).start.index == text->point.index) + CACHE_DATA(text->current_line).start = text->point; + + new_line = fetch_lines (text, + &CACHE_DATA(text->current_line).start, + &CACHE_DATA(text->current_line).tab_cont, + FetchLinesCount, + 1); + + swap_lines (text, text->current_line, new_line, old_lines); + + text->current_line = new_line; + + new_pixels = total_line_height (text, new_line, 1); + + gdk_window_get_size (text->text_area, &width, &height); + + if (old_pixels != new_pixels) + { + if (!widget->style->bg_pixmap[GTK_STATE_NORMAL]) + { + gdk_draw_pixmap (text->text_area, + text->gc, + text->text_area, + 0, + pixel_height + old_pixels, + 0, + pixel_height + new_pixels, + width, + height); + } + text->vadj->upper += new_pixels; + text->vadj->upper -= old_pixels; + adjust_adj (text, text->vadj); + } + + rect.x = 0; + rect.y = pixel_height; + rect.width = width; + rect.height = new_pixels; + + expose_text (text, &rect, FALSE); + gtk_text_draw_focus ( (GtkWidget *) text); + + text->cursor_mark = text->point; + + find_cursor (text, TRUE); + + if (old_pixels != new_pixels) + { + if (widget->style->bg_pixmap[GTK_STATE_NORMAL]) + { + rect.x = 0; + rect.y = pixel_height + new_pixels; + rect.width = width; + rect.height = height - rect.y; + + expose_text (text, &rect, FALSE); + } + else + process_exposes (text); + } + + TEXT_ASSERT (text); + TEXT_SHOW(text); +} + +/* note, the point has already been moved forward */ +static void +correct_cache_insert (GtkText* text, gint nchars) +{ + GList *cache; + GtkPropertyMark *start; + GtkPropertyMark *end; + + /* If we inserted a property exactly at the beginning of the + * line, we have to correct here, or fetch_lines will + * fetch junk. + */ + start = &CACHE_DATA(text->current_line).start; + if (start->index == text->point.index - nchars) + { + *start = text->point; + move_mark_n (start, -nchars); + } + + /* Now correct the offsets, and check for start or end marks that + * are after the point, yet point to a property before the point's + * property. This indicates that they are meant to point to the + * second half of a property we split in insert_text_property(), so + * we fix them up that way. + */ + cache = text->current_line->next; + + for (; cache; cache = cache->next) + { + start = &CACHE_DATA(cache).start; + end = &CACHE_DATA(cache).end; + + if (LAST_INDEX (text, text->point) && + start->index == text->point.index) + *start = text->point; + else + { + if (start->property == text->point.property) + { + start->offset += nchars; + start->index += nchars; + } + else if (start->property->next && + (start->property->next->next == text->point.property)) + { + /* We split the property, and this is the second half */ + start->offset -= MARK_CURRENT_PROPERTY (start)->length; + start->index += nchars; + start->property = text->point.property; + } + else + start->index += nchars; + } + + if (LAST_INDEX (text, text->point) && + end->index == text->point.index) + *end = text->point; + else + { + if (end->property == text->point.property) + { + end->offset += nchars; + end->index += nchars; + } + else if (end->property->next && + (end->property->next->next == text->point.property)) + { + /* We split the property, and this is the second half */ + end->offset -= MARK_CURRENT_PROPERTY (end)->length; + end->index += nchars; + end->property = text->point.property; + } + else + end->index += nchars; + } + + /*TEXT_ASSERT_MARK(text, start, "start");*/ + /*TEXT_ASSERT_MARK(text, end, "end");*/ + } +} + + +static void +insert_expose (GtkText* text, guint old_pixels, gint nchars, + guint new_line_count) +{ + GtkWidget *widget = GTK_WIDGET (text); + + gint pixel_height; + guint new_pixels = 0; + GdkRectangle rect; + GList* new_lines = NULL; + gint width, height; + + text->cursor_virtual_x = 0; + + undraw_cursor (text, FALSE); + + correct_cache_insert (text, nchars); + + TEXT_SHOW_ADJ (text, text->vadj, "vadj"); + + pixel_height = pixel_height_of(text, text->current_line) - + LINE_HEIGHT(CACHE_DATA(text->current_line)); + + new_lines = fetch_lines (text, + &CACHE_DATA(text->current_line).start, + &CACHE_DATA(text->current_line).tab_cont, + FetchLinesCount, + new_line_count); + + swap_lines (text, text->current_line, new_lines, 1); + + text->current_line = new_lines; + + new_pixels = total_line_height (text, new_lines, new_line_count); + + gdk_window_get_size (text->text_area, &width, &height); + + if (old_pixels != new_pixels) + { + if (!widget->style->bg_pixmap[GTK_STATE_NORMAL]) + { + gdk_draw_pixmap (text->text_area, + text->gc, + text->text_area, + 0, + pixel_height + old_pixels, + 0, + pixel_height + new_pixels, + width, + height + (old_pixels - new_pixels) - pixel_height); + + } + text->vadj->upper += new_pixels; + text->vadj->upper -= old_pixels; + adjust_adj (text, text->vadj); + } + + rect.x = 0; + rect.y = pixel_height; + rect.width = width; + rect.height = new_pixels; + + expose_text (text, &rect, FALSE); + gtk_text_draw_focus ( (GtkWidget *) text); + + text->cursor_mark = text->point; + + find_cursor (text, TRUE); + + draw_cursor (text, FALSE); + + if (old_pixels != new_pixels) + { + if (widget->style->bg_pixmap[GTK_STATE_NORMAL]) + { + rect.x = 0; + rect.y = pixel_height + new_pixels; + rect.width = width; + rect.height = height - rect.y; + + expose_text (text, &rect, FALSE); + } + else + process_exposes (text); + } + + TEXT_SHOW_ADJ (text, text->vadj, "vadj"); + TEXT_ASSERT (text); + TEXT_SHOW(text); +} + +/* Text property functions */ + +static guint +font_hash (gconstpointer font) +{ + return gdk_font_id ((const GdkFont*) font); +} + +static GHashTable *font_cache_table = NULL; + +static GtkTextFont* +get_text_font (GdkFont* gfont) +{ + GtkTextFont* tf; + gint i; + + if (!font_cache_table) + font_cache_table = g_hash_table_new (font_hash, (GCompareFunc) gdk_font_equal); + + tf = g_hash_table_lookup (font_cache_table, gfont); + + if (tf) + { + tf->ref_count++; + return tf; + } + + tf = g_new (GtkTextFont, 1); + tf->ref_count = 1; + + tf->gdk_font = gfont; + gdk_font_ref (gfont); + + for(i = 0; i < 256; i += 1) + tf->char_widths[i] = gdk_char_width (gfont, (char)i); + + g_hash_table_insert (font_cache_table, gfont, tf); + + return tf; +} + +static void +text_font_unref (GtkTextFont *text_font) +{ + text_font->ref_count--; + if (text_font->ref_count == 0) + { + g_hash_table_remove (font_cache_table, text_font->gdk_font); + gdk_font_unref (text_font->gdk_font); + g_free (text_font); + } +} + +static gint +text_properties_equal (TextProperty* prop, GdkFont* font, GdkColor *fore, GdkColor *back) +{ + if (prop->flags & PROPERTY_FONT) + { + gboolean retval; + GtkTextFont *text_font; + + if (!font) + return FALSE; + + text_font = get_text_font (font); + + retval = (prop->font == text_font); + text_font_unref (text_font); + + if (!retval) + return FALSE; + } + else + if (font != NULL) + return FALSE; + + if (prop->flags & PROPERTY_FOREGROUND) + { + if (!fore || !gdk_color_equal (&prop->fore_color, fore)) + return FALSE; + } + else + if (fore != NULL) + return FALSE; + + if (prop->flags & PROPERTY_BACKGROUND) + { + if (!back || !gdk_color_equal (&prop->back_color, back)) + return FALSE; + } + else + if (back != NULL) + return FALSE; + + return TRUE; +} + +static void +realize_property (GtkText *text, TextProperty *prop) +{ + GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (text)); + + if (prop->flags & PROPERTY_FOREGROUND) + gdk_colormap_alloc_color (colormap, &prop->fore_color, FALSE, FALSE); + + if (prop->flags & PROPERTY_BACKGROUND) + gdk_colormap_alloc_color (colormap, &prop->back_color, FALSE, FALSE); +} + +static void +realize_properties (GtkText *text) +{ + GList *tmp_list = text->text_properties; + + while (tmp_list) + { + realize_property (text, tmp_list->data); + + tmp_list = tmp_list->next; + } +} + +static void +unrealize_property (GtkText *text, TextProperty *prop) +{ + GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (text)); + + if (prop->flags & PROPERTY_FOREGROUND) + gdk_colormap_free_colors (colormap, &prop->fore_color, 1); + + if (prop->flags & PROPERTY_BACKGROUND) + gdk_colormap_free_colors (colormap, &prop->back_color, 1); +} + +static void +unrealize_properties (GtkText *text) +{ + GList *tmp_list = text->text_properties; + + while (tmp_list) + { + unrealize_property (text, tmp_list->data); + + tmp_list = tmp_list->next; + } +} + +static TextProperty* +new_text_property (GtkText *text, GdkFont *font, GdkColor* fore, + GdkColor* back, guint length) +{ + TextProperty *prop; + + if (text_property_chunk == NULL) + { + text_property_chunk = g_mem_chunk_new ("text property mem chunk", + sizeof(TextProperty), + 1024*sizeof(TextProperty), + G_ALLOC_AND_FREE); + } + + prop = g_chunk_new(TextProperty, text_property_chunk); + + prop->flags = 0; + if (font) + { + prop->flags |= PROPERTY_FONT; + prop->font = get_text_font (font); + } + else + prop->font = NULL; + + if (fore) + { + prop->flags |= PROPERTY_FOREGROUND; + prop->fore_color = *fore; + } + + if (back) + { + prop->flags |= PROPERTY_BACKGROUND; + prop->back_color = *back; + } + + prop->length = length; + + if (GTK_WIDGET_REALIZED (text)) + realize_property (text, prop); + + return prop; +} + +static void +destroy_text_property (TextProperty *prop) +{ + if (prop->font) + text_font_unref (prop->font); + + g_mem_chunk_free (text_property_chunk, prop); +} + +/* Flop the memory between the point and the gap around like a + * dead fish. */ +static void +move_gap (GtkText* text, guint index) +{ + if (text->gap_position < index) + { + gint diff = index - text->gap_position; + + if (text->use_wchar) + g_memmove (text->text.wc + text->gap_position, + text->text.wc + text->gap_position + text->gap_size, + diff*sizeof (GdkWChar)); + else + g_memmove (text->text.ch + text->gap_position, + text->text.ch + text->gap_position + text->gap_size, + diff); + + text->gap_position = index; + } + else if (text->gap_position > index) + { + gint diff = text->gap_position - index; + + if (text->use_wchar) + g_memmove (text->text.wc + index + text->gap_size, + text->text.wc + index, + diff*sizeof (GdkWChar)); + else + g_memmove (text->text.ch + index + text->gap_size, + text->text.ch + index, + diff); + + text->gap_position = index; + } +} + +/* Increase the gap size. */ +static void +make_forward_space (GtkText* text, guint len) +{ + if (text->gap_size < len) + { + guint sum = MAX(2*len, MIN_GAP_SIZE) + text->text_end; + + if (sum >= text->text_len) + { + guint i = 1; + + while (i <= sum) i <<= 1; + + if (text->use_wchar) + text->text.wc = (GdkWChar *)g_realloc(text->text.wc, + i*sizeof(GdkWChar)); + else + text->text.ch = (guchar *)g_realloc(text->text.ch, i); + text->text_len = i; + } + + if (text->use_wchar) + g_memmove (text->text.wc + text->gap_position + text->gap_size + 2*len, + text->text.wc + text->gap_position + text->gap_size, + (text->text_end - (text->gap_position + text->gap_size)) + *sizeof(GdkWChar)); + else + g_memmove (text->text.ch + text->gap_position + text->gap_size + 2*len, + text->text.ch + text->gap_position + text->gap_size, + text->text_end - (text->gap_position + text->gap_size)); + + text->text_end += len*2; + text->gap_size += len*2; + } +} + +/* Inserts into the text property list a list element that guarantees + * that for len characters following the point, text has the correct + * property. does not move point. adjusts text_properties_point and + * text_properties_point_offset relative to the current value of + * point. */ +static void +insert_text_property (GtkText* text, GdkFont* font, + GdkColor *fore, GdkColor* back, guint len) +{ + GtkPropertyMark *mark = &text->point; + TextProperty* forward_prop = MARK_CURRENT_PROPERTY(mark); + TextProperty* backward_prop = MARK_PREV_PROPERTY(mark); + + if (MARK_OFFSET(mark) == 0) + { + /* Point is on the boundary of two properties. + * If it is the same as either, grow, else insert + * a new one. */ + + if (text_properties_equal(forward_prop, font, fore, back)) + { + /* Grow the property in front of us. */ + + MARK_PROPERTY_LENGTH(mark) += len; + } + else if (backward_prop && + text_properties_equal(backward_prop, font, fore, back)) + { + /* Grow property behind us, point property and offset + * change. */ + + SET_PROPERTY_MARK (&text->point, + MARK_PREV_LIST_PTR (mark), + backward_prop->length); + + backward_prop->length += len; + } + else if ((MARK_NEXT_LIST_PTR(mark) == NULL) && + (forward_prop->length == 1)) + { + /* Next property just has last position, take it over */ + + if (GTK_WIDGET_REALIZED (text)) + unrealize_property (text, forward_prop); + + forward_prop->flags = 0; + if (font) + { + forward_prop->flags |= PROPERTY_FONT; + forward_prop->font = get_text_font (font); + } + else + forward_prop->font = NULL; + + if (fore) + { + forward_prop->flags |= PROPERTY_FOREGROUND; + forward_prop->fore_color = *fore; + } + if (back) + { + forward_prop->flags |= PROPERTY_BACKGROUND; + forward_prop->back_color = *back; + } + forward_prop->length += len; + + if (GTK_WIDGET_REALIZED (text)) + realize_property (text, forward_prop); + } + else + { + /* Splice a new property into the list. */ + + GList* new_prop = g_list_alloc(); + + new_prop->next = MARK_LIST_PTR(mark); + new_prop->prev = MARK_PREV_LIST_PTR(mark); + new_prop->next->prev = new_prop; + + if (new_prop->prev) + new_prop->prev->next = new_prop; + + new_prop->data = new_text_property (text, font, fore, back, len); + + SET_PROPERTY_MARK (mark, new_prop, 0); + } + } + else + { + /* The following will screw up the line_start cache, + * we'll fix it up in correct_cache_insert + */ + + /* In the middle of forward_prop, if properties are equal, + * just add to its length, else split it into two and splice + * in a new one. */ + if (text_properties_equal (forward_prop, font, fore, back)) + { + forward_prop->length += len; + } + else if ((MARK_NEXT_LIST_PTR(mark) == NULL) && + (MARK_OFFSET(mark) + 1 == forward_prop->length)) + { + /* Inserting before only the last position in the text */ + + GList* new_prop; + forward_prop->length -= 1; + + new_prop = g_list_alloc(); + new_prop->data = new_text_property (text, font, fore, back, len+1); + new_prop->prev = MARK_LIST_PTR(mark); + new_prop->next = NULL; + MARK_NEXT_LIST_PTR(mark) = new_prop; + + SET_PROPERTY_MARK (mark, new_prop, 0); + } + else + { + GList* new_prop = g_list_alloc(); + GList* new_prop_forward = g_list_alloc(); + gint old_length = forward_prop->length; + GList* next = MARK_NEXT_LIST_PTR(mark); + + /* Set the new lengths according to where they are split. Construct + * two new properties. */ + forward_prop->length = MARK_OFFSET(mark); + + new_prop_forward->data = + new_text_property(text, + forward_prop->flags & PROPERTY_FONT ? + forward_prop->font->gdk_font : NULL, + forward_prop->flags & PROPERTY_FOREGROUND ? + &forward_prop->fore_color : NULL, + forward_prop->flags & PROPERTY_BACKGROUND ? + &forward_prop->back_color : NULL, + old_length - forward_prop->length); + + new_prop->data = new_text_property(text, font, fore, back, len); + + /* Now splice things in. */ + MARK_NEXT_LIST_PTR(mark) = new_prop; + new_prop->prev = MARK_LIST_PTR(mark); + + new_prop->next = new_prop_forward; + new_prop_forward->prev = new_prop; + + new_prop_forward->next = next; + + if (next) + next->prev = new_prop_forward; + + SET_PROPERTY_MARK (mark, new_prop, 0); + } + } + + while (text->text_properties_end->next) + text->text_properties_end = text->text_properties_end->next; + + while (text->text_properties->prev) + text->text_properties = text->text_properties->prev; +} + +static void +delete_text_property (GtkText* text, guint nchars) +{ + /* Delete nchars forward from point. */ + + /* Deleting text properties is problematical, because we + * might be storing around marks pointing to a property. + * + * The marks in question and how we handle them are: + * + * point: We know the new value, since it will be at the + * end of the deleted text, and we move it there + * first. + * cursor: We just remove the mark and set it equal to the + * point after the operation. + * line-start cache: We replace most affected lines. + * The current line gets used to fetch the new + * lines so, if necessary, (delete at the beginning + * of a line) we fix it up by setting it equal to the + * point. + */ + + TextProperty *prop; + GList *tmp; + gint is_first; + + for(; nchars; nchars -= 1) + { + prop = MARK_CURRENT_PROPERTY(&text->point); + + prop->length -= 1; + + if (prop->length == 0) + { + tmp = MARK_LIST_PTR (&text->point); + + is_first = tmp == text->text_properties; + + MARK_LIST_PTR (&text->point) = g_list_remove_link (tmp, tmp); + text->point.offset = 0; + + if (GTK_WIDGET_REALIZED (text)) + unrealize_property (text, prop); + + destroy_text_property (prop); + g_list_free_1 (tmp); + + prop = MARK_CURRENT_PROPERTY (&text->point); + + if (is_first) + text->text_properties = MARK_LIST_PTR (&text->point); + + g_assert (prop->length != 0); + } + else if (prop->length == text->point.offset) + { + MARK_LIST_PTR (&text->point) = MARK_NEXT_LIST_PTR (&text->point); + text->point.offset = 0; + } + } + + /* Check to see if we have just the single final position remaining + * along in a property; if so, combine it with the previous property + */ + if (LAST_INDEX (text, text->point) && + (MARK_OFFSET (&text->point) == 0) && + (MARK_PREV_LIST_PTR(&text->point) != NULL)) + { + tmp = MARK_LIST_PTR (&text->point); + prop = MARK_CURRENT_PROPERTY(&text->point); + + MARK_LIST_PTR (&text->point) = MARK_PREV_LIST_PTR (&text->point); + MARK_CURRENT_PROPERTY(&text->point)->length += 1; + MARK_NEXT_LIST_PTR(&text->point) = NULL; + + text->point.offset = MARK_CURRENT_PROPERTY(&text->point)->length - 1; + + if (GTK_WIDGET_REALIZED (text)) + unrealize_property (text, prop); + + destroy_text_property (prop); + g_list_free_1 (tmp); + } +} + +static void +init_properties (GtkText *text) +{ + if (!text->text_properties) + { + text->text_properties = g_list_alloc(); + text->text_properties->next = NULL; + text->text_properties->prev = NULL; + text->text_properties->data = new_text_property (text, NULL, NULL, NULL, 1); + text->text_properties_end = text->text_properties; + + SET_PROPERTY_MARK (&text->point, text->text_properties, 0); + + text->point.index = 0; + } +} + + +/**********************************************************************/ +/* Property Movement */ +/**********************************************************************/ + +static void +move_mark_n (GtkPropertyMark* mark, gint n) +{ + if (n > 0) + advance_mark_n(mark, n); + else if (n < 0) + decrement_mark_n(mark, -n); +} + +static void +advance_mark (GtkPropertyMark* mark) +{ + TextProperty* prop = MARK_CURRENT_PROPERTY (mark); + + mark->index += 1; + + if (prop->length > mark->offset + 1) + mark->offset += 1; + else + { + mark->property = MARK_NEXT_LIST_PTR (mark); + mark->offset = 0; + } +} + +static void +advance_mark_n (GtkPropertyMark* mark, gint n) +{ + gint i; + TextProperty* prop; + + g_assert (n > 0); + + i = 0; /* otherwise it migth not be init. */ + prop = MARK_CURRENT_PROPERTY(mark); + + if ((prop->length - mark->offset - 1) < n) { /* if we need to change prop. */ + /* to make it easier */ + n += (mark->offset); + mark->index -= mark->offset; + mark->offset = 0; + /* first we take seven-mile-leaps to get to the right text + * property. */ + while ((n-i) > prop->length - 1) { + i += prop->length; + mark->index += prop->length; + mark->property = MARK_NEXT_LIST_PTR (mark); + prop = MARK_CURRENT_PROPERTY (mark); + } + } + + /* and then the rest */ + mark->index += n - i; + mark->offset += n - i; +} + +static void +decrement_mark (GtkPropertyMark* mark) +{ + mark->index -= 1; + + if (mark->offset > 0) + mark->offset -= 1; + else + { + mark->property = MARK_PREV_LIST_PTR (mark); + mark->offset = MARK_CURRENT_PROPERTY (mark)->length - 1; + } +} + +static void +decrement_mark_n (GtkPropertyMark* mark, gint n) +{ + g_assert (n > 0); + + while (mark->offset < n) { + /* jump to end of prev */ + n -= mark->offset + 1; + mark->index -= mark->offset + 1; + mark->property = MARK_PREV_LIST_PTR (mark); + mark->offset = MARK_CURRENT_PROPERTY (mark)->length - 1; + } + + /* and the rest */ + mark->index -= n; + mark->offset -= n; +} + +static GtkPropertyMark +find_mark (GtkText* text, guint mark_position) +{ + return find_mark_near (text, mark_position, &text->point); +} + +/* + * You can also start from the end, what a drag. + */ +static GtkPropertyMark +find_mark_near (GtkText* text, guint mark_position, const GtkPropertyMark* near) +{ + gint diffa; + gint diffb; + + GtkPropertyMark mark; + + if (!near) + diffa = mark_position + 1; + else + diffa = mark_position - near->index; + + diffb = mark_position; + + if (diffa < 0) + diffa = -diffa; + + if (diffa <= diffb) + { + mark = *near; + } + else + { + mark.index = 0; + mark.property = text->text_properties; + mark.offset = 0; + } + + move_mark_n (&mark, mark_position - mark.index); + + return mark; +} + +/* This routine must be called with scroll == FALSE, only when + * point is at least partially on screen + */ + +static void +find_line_containing_point (GtkText* text, guint point, + gboolean scroll) +{ + GList* cache; + gint height; + + text->current_line = NULL; + + TEXT_SHOW (text); + + /* Scroll backwards until the point is on screen + */ + while (CACHE_DATA(text->line_start_cache).start.index > point) + scroll_int (text, - LINE_HEIGHT(CACHE_DATA(text->line_start_cache))); + + /* Now additionally try to make sure that the point is fully on screen + */ + if (scroll) + { + while (text->first_cut_pixels != 0 && + text->line_start_cache->next && + CACHE_DATA(text->line_start_cache->next).start.index > point) + scroll_int (text, - LINE_HEIGHT(CACHE_DATA(text->line_start_cache->next))); + } + + gdk_window_get_size (text->text_area, NULL, &height); + + for (cache = text->line_start_cache; cache; cache = cache->next) + { + guint lph; + + if (CACHE_DATA(cache).end.index >= point || + LAST_INDEX(text, CACHE_DATA(cache).end)) + { + text->current_line = cache; /* LOOK HERE, this proc has an + * important side effect. */ + return; + } + + TEXT_SHOW_LINE (text, cache, "cache"); + + if (cache->next == NULL) + fetch_lines_forward (text, 1); + + if (scroll) + { + lph = pixel_height_of (text, cache->next); + + /* Scroll the bottom of the line is on screen, or until + * the line is the first onscreen line. + */ + while (cache->next != text->line_start_cache && lph > height) + { + TEXT_SHOW_LINE (text, cache, "cache"); + TEXT_SHOW_LINE (text, cache->next, "cache->next"); + scroll_int (text, LINE_HEIGHT(CACHE_DATA(cache->next))); + lph = pixel_height_of (text, cache->next); + } + } + } + + g_assert_not_reached (); /* Must set text->current_line here */ +} + +static guint +pixel_height_of (GtkText* text, GList* cache_line) +{ + gint pixels = - text->first_cut_pixels; + GList *cache = text->line_start_cache; + + while (TRUE) { + pixels += LINE_HEIGHT (CACHE_DATA(cache)); + + if (cache->data == cache_line->data) + break; + + cache = cache->next; + } + + return pixels; +} + +/**********************************************************************/ +/* Search and Placement */ +/**********************************************************************/ + +static gint +find_char_width (GtkText* text, const GtkPropertyMark *mark, const TabStopMark *tab_mark) +{ + GdkWChar ch; + gint16* char_widths; + + if (LAST_INDEX (text, *mark)) + return 0; + + ch = GTK_TEXT_INDEX (text, mark->index); + char_widths = MARK_CURRENT_TEXT_FONT (text, mark)->char_widths; + + if (ch == '\t') + { + return tab_mark->to_next_tab * char_widths[' ']; + } + else if (!text->use_wchar) + { + return char_widths[ch]; + } + else + { + return gdk_char_width_wc(MARK_CURRENT_TEXT_FONT(text, mark)->gdk_font, ch); + } +} + +static void +advance_tab_mark (GtkText* text, TabStopMark* tab_mark, GdkWChar ch) +{ + if (tab_mark->to_next_tab == 1 || ch == '\t') + { + if (tab_mark->tab_stops->next) + { + tab_mark->tab_stops = tab_mark->tab_stops->next; + tab_mark->to_next_tab = (gulong) tab_mark->tab_stops->data; + } + else + { + tab_mark->to_next_tab = text->default_tab_width; + } + } + else + { + tab_mark->to_next_tab -= 1; + } +} + +static void +advance_tab_mark_n (GtkText* text, TabStopMark* tab_mark, gint n) + /* No tabs! */ +{ + while (n--) + advance_tab_mark (text, tab_mark, 0); +} + +static void +find_cursor_at_line (GtkText* text, const LineParams* start_line, gint pixel_height) +{ + GdkWChar ch; + GtkEditable *editable = (GtkEditable *)text; + + GtkPropertyMark mark = start_line->start; + TabStopMark tab_mark = start_line->tab_cont.tab_start; + gint pixel_width = LINE_START_PIXEL (*start_line); + + while (mark.index < text->cursor_mark.index) + { + pixel_width += find_char_width (text, &mark, &tab_mark); + + advance_tab_mark (text, &tab_mark, GTK_TEXT_INDEX(text, mark.index)); + advance_mark (&mark); + } + + text->cursor_pos_x = pixel_width; + text->cursor_pos_y = pixel_height; + text->cursor_char_offset = start_line->font_descent; + text->cursor_mark = mark; + + ch = LAST_INDEX (text, mark) ? + LINE_DELIM : GTK_TEXT_INDEX (text, mark.index); + + if ((text->use_wchar) ? gdk_iswspace (ch) : isspace (ch)) + text->cursor_char = 0; + else + text->cursor_char = ch; + +#ifdef USE_XIM + if (GTK_WIDGET_HAS_FOCUS(text) && gdk_im_ready() && editable->ic && + (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION)) + { + GdkICAttributesType mask = GDK_IC_SPOT_LOCATION | + GDK_IC_PREEDIT_FOREGROUND | + GDK_IC_PREEDIT_BACKGROUND; + + editable->ic_attr->spot_location.x = text->cursor_pos_x; + editable->ic_attr->spot_location.y + = text->cursor_pos_y - text->cursor_char_offset; + editable->ic_attr->preedit_foreground = *MARK_CURRENT_FORE (text, &mark); + editable->ic_attr->preedit_background = *MARK_CURRENT_BACK (text, &mark); + + if (MARK_CURRENT_FONT (text, &mark)->type == GDK_FONT_FONTSET) + { + mask |= GDK_IC_PREEDIT_FONTSET; + editable->ic_attr->preedit_fontset = MARK_CURRENT_FONT (text, &mark); + } + + gdk_ic_set_attr (editable->ic, editable->ic_attr, mask); + } +#endif +} + +static void +find_cursor (GtkText* text, gboolean scroll) +{ + if (GTK_WIDGET_REALIZED (text)) + { + find_line_containing_point (text, text->cursor_mark.index, scroll); + + if (text->current_line) + find_cursor_at_line (text, + &CACHE_DATA(text->current_line), + pixel_height_of(text, text->current_line)); + } + + GTK_EDITABLE (text)->current_pos = text->cursor_mark.index; +} + +static void +find_mouse_cursor_at_line (GtkText *text, const LineParams* lp, + guint line_pixel_height, + gint button_x) +{ + GtkPropertyMark mark = lp->start; + TabStopMark tab_mark = lp->tab_cont.tab_start; + + gint char_width = find_char_width(text, &mark, &tab_mark); + gint pixel_width = LINE_START_PIXEL (*lp) + (char_width+1)/2; + + text->cursor_pos_y = line_pixel_height; + + for (;;) + { + GdkWChar ch = LAST_INDEX (text, mark) ? + LINE_DELIM : GTK_TEXT_INDEX (text, mark.index); + + if (button_x < pixel_width || mark.index == lp->end.index) + { + text->cursor_pos_x = pixel_width - (char_width+1)/2; + text->cursor_mark = mark; + text->cursor_char_offset = lp->font_descent; + + if ((text->use_wchar) ? gdk_iswspace (ch) : isspace (ch)) + text->cursor_char = 0; + else + text->cursor_char = ch; + + break; + } + + advance_tab_mark (text, &tab_mark, ch); + advance_mark (&mark); + + pixel_width += char_width/2; + + char_width = find_char_width (text, &mark, &tab_mark); + + pixel_width += (char_width+1)/2; + } +} + +static void +find_mouse_cursor (GtkText* text, gint x, gint y) +{ + gint pixel_height; + GList* cache = text->line_start_cache; + + g_assert (cache); + + pixel_height = - text->first_cut_pixels; + + for (; cache; cache = cache->next) + { + pixel_height += LINE_HEIGHT(CACHE_DATA(cache)); + + if (y < pixel_height || !cache->next) + { + find_mouse_cursor_at_line (text, &CACHE_DATA(cache), pixel_height, x); + + find_cursor (text, FALSE); + + return; + } + } +} + +/**********************************************************************/ +/* Cache Manager */ +/**********************************************************************/ + +static void +free_cache (GtkText* text) +{ + GList* cache = text->line_start_cache; + + if (cache) + { + while (cache->prev) + cache = cache->prev; + + text->line_start_cache = cache; + } + + for (; cache; cache = cache->next) + g_mem_chunk_free (params_mem_chunk, cache->data); + + g_list_free (text->line_start_cache); + + text->line_start_cache = NULL; +} + +static GList* +remove_cache_line (GtkText* text, GList* member) +{ + GList *list; + + if (member == NULL) + return NULL; + + if (member == text->line_start_cache) + text->line_start_cache = text->line_start_cache->next; + + if (member->prev) + member->prev->next = member->next; + + if (member->next) + member->next->prev = member->prev; + + list = member->next; + + g_mem_chunk_free (params_mem_chunk, member->data); + g_list_free_1 (member); + + return list; +} + +/**********************************************************************/ +/* Key Motion */ +/**********************************************************************/ + +static void +move_cursor_buffer_ver (GtkText *text, int dir) +{ + undraw_cursor (text, FALSE); + + if (dir > 0) + { + scroll_int (text, text->vadj->upper); + text->cursor_mark = find_this_line_start_mark (text, + TEXT_LENGTH (text), + &text->cursor_mark); + } + else + { + scroll_int (text, - text->vadj->value); + text->cursor_mark = find_this_line_start_mark (text, + 0, + &text->cursor_mark); + } + + find_cursor (text, TRUE); + draw_cursor (text, FALSE); +} + +static void +move_cursor_page_ver (GtkText *text, int dir) +{ + scroll_int (text, dir * text->vadj->page_increment); +} + +static void +move_cursor_ver (GtkText *text, int count) +{ + gint i; + GtkPropertyMark mark; + gint offset; + + mark = find_this_line_start_mark (text, text->cursor_mark.index, &text->cursor_mark); + offset = text->cursor_mark.index - mark.index; + + if (offset > text->cursor_virtual_x) + text->cursor_virtual_x = offset; + + if (count < 0) + { + if (mark.index == 0) + return; + + decrement_mark (&mark); + mark = find_this_line_start_mark (text, mark.index, &mark); + } + else + { + mark = text->cursor_mark; + + while (!LAST_INDEX(text, mark) && GTK_TEXT_INDEX(text, mark.index) != LINE_DELIM) + advance_mark (&mark); + + if (LAST_INDEX(text, mark)) + return; + + advance_mark (&mark); + } + + for (i=0; i < text->cursor_virtual_x; i += 1, advance_mark(&mark)) + if (LAST_INDEX(text, mark) || + GTK_TEXT_INDEX(text, mark.index) == LINE_DELIM) + break; + + undraw_cursor (text, FALSE); + + text->cursor_mark = mark; + + find_cursor (text, TRUE); + + draw_cursor (text, FALSE); +} + +static void +move_cursor_hor (GtkText *text, int count) +{ + /* count should be +-1. */ + if ( (count > 0 && text->cursor_mark.index + count > TEXT_LENGTH(text)) || + (count < 0 && text->cursor_mark.index < (- count)) || + (count == 0) ) + return; + + text->cursor_virtual_x = 0; + + undraw_cursor (text, FALSE); + + move_mark_n (&text->cursor_mark, count); + + find_cursor (text, TRUE); + + draw_cursor (text, FALSE); +} + +static void +gtk_text_move_cursor (GtkEditable *editable, + gint x, + gint y) +{ + if (x > 0) + { + while (x-- != 0) + move_cursor_hor (GTK_TEXT (editable), 1); + } + else if (x < 0) + { + while (x++ != 0) + move_cursor_hor (GTK_TEXT (editable), -1); + } + + if (y > 0) + { + while (y-- != 0) + move_cursor_ver (GTK_TEXT (editable), 1); + } + else if (y < 0) + { + while (y++ != 0) + move_cursor_ver (GTK_TEXT (editable), -1); + } +} + +static void +gtk_text_move_forward_character (GtkText *text) +{ + move_cursor_hor (text, 1); +} + +static void +gtk_text_move_backward_character (GtkText *text) +{ + move_cursor_hor (text, -1); +} + +static void +gtk_text_move_next_line (GtkText *text) +{ + move_cursor_ver (text, 1); +} + +static void +gtk_text_move_previous_line (GtkText *text) +{ + move_cursor_ver (text, -1); +} + +static void +gtk_text_move_word (GtkEditable *editable, + gint n) +{ + if (n > 0) + { + while (n-- != 0) + gtk_text_move_forward_word (GTK_TEXT (editable)); + } + else if (n < 0) + { + while (n++ != 0) + gtk_text_move_backward_word (GTK_TEXT (editable)); + } +} + +static void +gtk_text_move_forward_word (GtkText *text) +{ + text->cursor_virtual_x = 0; + + undraw_cursor (text, FALSE); + + if (text->use_wchar) + { + while (!LAST_INDEX (text, text->cursor_mark) && + !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) + advance_mark (&text->cursor_mark); + + while (!LAST_INDEX (text, text->cursor_mark) && + gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) + advance_mark (&text->cursor_mark); + } + else + { + while (!LAST_INDEX (text, text->cursor_mark) && + !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) + advance_mark (&text->cursor_mark); + + while (!LAST_INDEX (text, text->cursor_mark) && + isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index))) + advance_mark (&text->cursor_mark); + } + + find_cursor (text, TRUE); + draw_cursor (text, FALSE); +} + +static void +gtk_text_move_backward_word (GtkText *text) +{ + text->cursor_virtual_x = 0; + + undraw_cursor (text, FALSE); + + if (text->use_wchar) + { + while ((text->cursor_mark.index > 0) && + !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) + decrement_mark (&text->cursor_mark); + + while ((text->cursor_mark.index > 0) && + gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) + decrement_mark (&text->cursor_mark); + } + else + { + while ((text->cursor_mark.index > 0) && + !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) + decrement_mark (&text->cursor_mark); + + while ((text->cursor_mark.index > 0) && + isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1))) + decrement_mark (&text->cursor_mark); + } + + find_cursor (text, TRUE); + draw_cursor (text, FALSE); +} + +static void +gtk_text_move_page (GtkEditable *editable, + gint x, + gint y) +{ + if (y != 0) + scroll_int (GTK_TEXT (editable), + y * GTK_TEXT(editable)->vadj->page_increment); +} + +static void +gtk_text_move_to_row (GtkEditable *editable, + gint row) +{ +} + +static void +gtk_text_move_to_column (GtkEditable *editable, + gint column) +{ + GtkText *text; + + text = GTK_TEXT (editable); + + text->cursor_virtual_x = 0; /* FIXME */ + + undraw_cursor (text, FALSE); + + /* Move to the beginning of the line */ + while ((text->cursor_mark.index > 0) && + (GTK_TEXT_INDEX (text, text->cursor_mark.index - 1) != LINE_DELIM)) + decrement_mark (&text->cursor_mark); + + while (!LAST_INDEX (text, text->cursor_mark) && + (GTK_TEXT_INDEX (text, text->cursor_mark.index) != LINE_DELIM)) + { + if (column > 0) + column--; + else if (column == 0) + break; + + advance_mark (&text->cursor_mark); + } + + find_cursor (text, TRUE); + draw_cursor (text, FALSE); +} + +static void +gtk_text_move_beginning_of_line (GtkText *text) +{ + gtk_text_move_to_column (GTK_EDITABLE (text), 0); + +} + +static void +gtk_text_move_end_of_line (GtkText *text) +{ + gtk_text_move_to_column (GTK_EDITABLE (text), -1); +} + +static void +gtk_text_kill_char (GtkEditable *editable, + gint direction) +{ + GtkText *text; + + text = GTK_TEXT (editable); + + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_delete_selection (editable); + else + { + if (direction >= 0) + { + if (text->point.index + 1 <= TEXT_LENGTH (text)) + gtk_editable_delete_text (editable, text->point.index, text->point.index + 1); + } + else + { + if (text->point.index > 0) + gtk_editable_delete_text (editable, text->point.index - 1, text->point.index); + } + } +} + +static void +gtk_text_delete_forward_character (GtkText *text) +{ + gtk_text_kill_char (GTK_EDITABLE (text), 1); +} + +static void +gtk_text_delete_backward_character (GtkText *text) +{ + gtk_text_kill_char (GTK_EDITABLE (text), -1); +} + +static void +gtk_text_kill_word (GtkEditable *editable, + gint direction) +{ + if (editable->selection_start_pos != editable->selection_end_pos) + gtk_editable_delete_selection (editable); + else + { + gint old_pos = editable->current_pos; + if (direction >= 0) + { + gtk_text_move_word (editable, 1); + gtk_editable_delete_text (editable, old_pos, editable->current_pos); + } + else + { + gtk_text_move_word (editable, -1); + gtk_editable_delete_text (editable, editable->current_pos, old_pos); + } + } +} + +static void +gtk_text_delete_forward_word (GtkText *text) +{ + gtk_text_kill_word (GTK_EDITABLE (text), 1); +} + +static void +gtk_text_delete_backward_word (GtkText *text) +{ + gtk_text_kill_word (GTK_EDITABLE (text), -1); +} + +static void +gtk_text_kill_line (GtkEditable *editable, + gint direction) +{ + gint old_pos = editable->current_pos; + if (direction >= 0) + { + gtk_text_move_to_column (editable, -1); + gtk_editable_delete_text (editable, old_pos, editable->current_pos); + } + else + { + gtk_text_move_to_column (editable, 0); + gtk_editable_delete_text (editable, editable->current_pos, old_pos); + } +} + +static void +gtk_text_delete_line (GtkText *text) +{ + gtk_text_move_to_column (GTK_EDITABLE (text), 0); + gtk_text_kill_line (GTK_EDITABLE (text), 1); +} + +static void +gtk_text_delete_to_line_end (GtkText *text) +{ + gtk_text_kill_line (GTK_EDITABLE (text), 1); +} + +static void +gtk_text_select_word (GtkText *text, guint32 time) +{ + gint start_pos; + gint end_pos; + + GtkEditable *editable; + editable = GTK_EDITABLE (text); + + gtk_text_move_backward_word (text); + start_pos = text->cursor_mark.index; + + gtk_text_move_forward_word (text); + end_pos = text->cursor_mark.index; + + editable->has_selection = TRUE; + gtk_text_set_selection (editable, start_pos, end_pos); + gtk_editable_claim_selection (editable, start_pos != end_pos, time); +} + +static void +gtk_text_select_line (GtkText *text, guint32 time) +{ + gint start_pos; + gint end_pos; + + GtkEditable *editable; + editable = GTK_EDITABLE (text); + + gtk_text_move_beginning_of_line (text); + start_pos = text->cursor_mark.index; + + gtk_text_move_end_of_line (text); + gtk_text_move_forward_character (text); + end_pos = text->cursor_mark.index; + + editable->has_selection = TRUE; + gtk_text_set_selection (editable, start_pos, end_pos); + gtk_editable_claim_selection (editable, start_pos != end_pos, time); +} + +/**********************************************************************/ +/* Scrolling */ +/**********************************************************************/ + +static void +adjust_adj (GtkText* text, GtkAdjustment* adj) +{ + gint height; + + gdk_window_get_size (text->text_area, NULL, &height); + + adj->step_increment = MIN (adj->upper, (float) SCROLL_PIXELS); + adj->page_increment = MIN (adj->upper, height - (float) KEY_SCROLL_PIXELS); + adj->page_size = MIN (adj->upper, height); + adj->value = MIN (adj->value, adj->upper - adj->page_size); + adj->value = MAX (adj->value, 0.0); + + gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed"); +} + +static gint +set_vertical_scroll_iterator (GtkText* text, LineParams* lp, void* data) +{ + SetVerticalScrollData *svdata = (SetVerticalScrollData *) data; + + if ((text->first_line_start_index >= lp->start.index) && + (text->first_line_start_index <= lp->end.index)) + { + svdata->mark = lp->start; + + if (text->first_line_start_index == lp->start.index) + { + text->first_onscreen_ver_pixel = svdata->pixel_height + text->first_cut_pixels; + } + else + { + text->first_onscreen_ver_pixel = svdata->pixel_height; + text->first_cut_pixels = 0; + } + + text->vadj->value = (float) text->first_onscreen_ver_pixel; + } + + svdata->pixel_height += LINE_HEIGHT (*lp); + + return FALSE; +} + +static gint +set_vertical_scroll_find_iterator (GtkText* text, LineParams* lp, void* data) +{ + SetVerticalScrollData *svdata = (SetVerticalScrollData *) data; + gint return_val; + + if (svdata->pixel_height <= (gint) text->vadj->value && + svdata->pixel_height + LINE_HEIGHT(*lp) > (gint) text->vadj->value) + { + svdata->mark = lp->start; + + text->first_cut_pixels = (gint)text->vadj->value - svdata->pixel_height; + text->first_onscreen_ver_pixel = svdata->pixel_height; + text->first_line_start_index = lp->start.index; + + return_val = TRUE; + } + else + { + svdata->pixel_height += LINE_HEIGHT (*lp); + + return_val = FALSE; + } + + return return_val; +} + +static GtkPropertyMark +set_vertical_scroll (GtkText* text) +{ + GtkPropertyMark mark = find_mark (text, 0); + SetVerticalScrollData data; + gint height; + gint orig_value; + + data.pixel_height = 0; + line_params_iterate (text, &mark, NULL, FALSE, &data, set_vertical_scroll_iterator); + + text->vadj->upper = (float) data.pixel_height; + orig_value = (gint) text->vadj->value; + + gdk_window_get_size (text->text_area, NULL, &height); + + text->vadj->step_increment = MIN (text->vadj->upper, (float) SCROLL_PIXELS); + text->vadj->page_increment = MIN (text->vadj->upper, height - (float) KEY_SCROLL_PIXELS); + text->vadj->page_size = MIN (text->vadj->upper, height); + text->vadj->value = MIN (text->vadj->value, text->vadj->upper - text->vadj->page_size); + text->vadj->value = MAX (text->vadj->value, 0.0); + + text->last_ver_value = (gint)text->vadj->value; + + gtk_signal_emit_by_name (GTK_OBJECT (text->vadj), "changed"); + + if (text->vadj->value != orig_value) + { + /* We got clipped, and don't really know which line to put first. */ + data.pixel_height = 0; + data.last_didnt_wrap = TRUE; + + line_params_iterate (text, &mark, NULL, + FALSE, &data, + set_vertical_scroll_find_iterator); + } + + return data.mark; +} + +static void +scroll_int (GtkText* text, gint diff) +{ + gfloat upper; + + text->vadj->value += diff; + + upper = text->vadj->upper - text->vadj->page_size; + text->vadj->value = MIN (text->vadj->value, upper); + text->vadj->value = MAX (text->vadj->value, 0.0); + + gtk_signal_emit_by_name (GTK_OBJECT (text->vadj), "value_changed"); +} + +static void +process_exposes (GtkText *text) +{ + GdkEvent *event; + + /* Make sure graphics expose events are processed before scrolling + * again */ + + while ((event = gdk_event_get_graphics_expose (text->text_area)) != NULL) + { + gtk_widget_event (GTK_WIDGET (text), event); + if (event->expose.count == 0) + { + gdk_event_free (event); + break; + } + gdk_event_free (event); + } +} + +static gint last_visible_line_height (GtkText* text) +{ + GList *cache = text->line_start_cache; + gint height; + + gdk_window_get_size (text->text_area, NULL, &height); + + for (; cache->next; cache = cache->next) + if (pixel_height_of(text, cache->next) > height) + break; + + if (cache) + return pixel_height_of(text, cache) - 1; + else + return 0; +} + +static gint first_visible_line_height (GtkText* text) +{ + if (text->first_cut_pixels) + return pixel_height_of(text, text->line_start_cache) + 1; + else + return 1; +} + +static void +scroll_down (GtkText* text, gint diff0) +{ + GdkRectangle rect; + gint real_diff = 0; + gint width, height; + + text->first_onscreen_ver_pixel += diff0; + + while (diff0-- > 0) + { + if (text->first_cut_pixels < LINE_HEIGHT(CACHE_DATA(text->line_start_cache)) - 1) + { + text->first_cut_pixels += 1; + } + else + { + text->first_cut_pixels = 0; + + text->line_start_cache = text->line_start_cache->next; + g_assert (text->line_start_cache); + + text->first_line_start_index = + CACHE_DATA(text->line_start_cache).start.index; + + if (!text->line_start_cache->next) + fetch_lines_forward (text, 1); + } + + real_diff += 1; + } + + gdk_window_get_size (text->text_area, &width, &height); + if (height > real_diff) + gdk_draw_pixmap (text->text_area, + text->gc, + text->text_area, + 0, + real_diff, + 0, + 0, + width, + height - real_diff); + + rect.x = 0; + rect.y = MAX (0, height - real_diff); + rect.width = width; + rect.height = MIN (height, real_diff); + + expose_text (text, &rect, FALSE); + gtk_text_draw_focus ( (GtkWidget *) text); + + if (text->current_line) + { + gint cursor_min; + + text->cursor_pos_y -= real_diff; + cursor_min = drawn_cursor_min(text); + + if (cursor_min < 0) + find_mouse_cursor (text, text->cursor_pos_x, + first_visible_line_height (text)); + } + + if (height > real_diff) + process_exposes (text); +} + +static void +scroll_up (GtkText* text, gint diff0) +{ + gint real_diff = 0; + GdkRectangle rect; + gint width, height; + + text->first_onscreen_ver_pixel += diff0; + + while (diff0++ < 0) + { + g_assert (text->line_start_cache); + + if (text->first_cut_pixels > 0) + { + text->first_cut_pixels -= 1; + } + else + { + if (!text->line_start_cache->prev) + fetch_lines_backward (text); + + text->line_start_cache = text->line_start_cache->prev; + + text->first_line_start_index = + CACHE_DATA(text->line_start_cache).start.index; + + text->first_cut_pixels = LINE_HEIGHT(CACHE_DATA(text->line_start_cache)) - 1; + } + + real_diff += 1; + } + + gdk_window_get_size (text->text_area, &width, &height); + if (height > real_diff) + gdk_draw_pixmap (text->text_area, + text->gc, + text->text_area, + 0, + 0, + 0, + real_diff, + width, + height - real_diff); + + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = MIN (height, real_diff); + + expose_text (text, &rect, FALSE); + gtk_text_draw_focus ( (GtkWidget *) text); + + if (text->current_line) + { + gint cursor_max; + gint height; + + text->cursor_pos_y += real_diff; + cursor_max = drawn_cursor_max(text); + gdk_window_get_size (text->text_area, NULL, &height); + + if (cursor_max >= height) + find_mouse_cursor (text, text->cursor_pos_x, + last_visible_line_height (text)); + } + + if (height > real_diff) + process_exposes (text); +} + +/**********************************************************************/ +/* Display Code */ +/**********************************************************************/ + +/* Assumes mark starts a line. Calculates the height, width, and + * displayable character count of a single DISPLAYABLE line. That + * means that in line-wrap mode, this does may not compute the + * properties of an entire line. */ +static LineParams +find_line_params (GtkText* text, + const GtkPropertyMark* mark, + const PrevTabCont *tab_cont, + PrevTabCont *next_cont) +{ + LineParams lp; + TabStopMark tab_mark = tab_cont->tab_start; + guint max_display_pixels; + GdkWChar ch; + gint ch_width; + GdkFont *font; + + gdk_window_get_size (text->text_area, (gint*) &max_display_pixels, NULL); + if (GTK_EDITABLE (text)->editable || !text->word_wrap) + max_display_pixels -= LINE_WRAP_ROOM; + + lp.wraps = 0; + lp.tab_cont = *tab_cont; + lp.start = *mark; + lp.end = *mark; + lp.pixel_width = tab_cont->pixel_offset; + lp.displayable_chars = 0; + lp.font_ascent = 0; + lp.font_descent = 0; + + init_tab_cont (text, next_cont); + + while (!LAST_INDEX(text, lp.end)) + { + g_assert (lp.end.property); + + ch = GTK_TEXT_INDEX (text, lp.end.index); + font = MARK_CURRENT_FONT (text, &lp.end); + + if (ch == LINE_DELIM) + { + /* Newline doesn't count in computation of line height, even + * if its in a bigger font than the rest of the line. Unless, + * of course, there are no other characters. */ + + if (!lp.font_ascent && !lp.font_descent) + { + lp.font_ascent = font->ascent; + lp.font_descent = font->descent; + } + + lp.tab_cont_next = *next_cont; + + return lp; + } + + ch_width = find_char_width (text, &lp.end, &tab_mark); + + if ((ch_width + lp.pixel_width > max_display_pixels) && + (lp.end.index > lp.start.index)) + { + lp.wraps = 1; + + if (text->line_wrap) + { + next_cont->tab_start = tab_mark; + next_cont->pixel_offset = 0; + + if (ch == '\t') + { + /* Here's the tough case, a tab is wrapping. */ + gint pixels_avail = max_display_pixels - lp.pixel_width; + gint space_width = MARK_CURRENT_TEXT_FONT(text, &lp.end)->char_widths[' ']; + gint spaces_avail = pixels_avail / space_width; + + if (spaces_avail == 0) + { + decrement_mark (&lp.end); + } + else + { + advance_tab_mark (text, &next_cont->tab_start, '\t'); + next_cont->pixel_offset = space_width * (tab_mark.to_next_tab - + spaces_avail); + lp.displayable_chars += 1; + } + } + else + { + if (text->word_wrap) + { + GtkPropertyMark saved_mark = lp.end; + guint saved_characters = lp.displayable_chars; + + lp.displayable_chars += 1; + + if (text->use_wchar) + { + while (!gdk_iswspace (GTK_TEXT_INDEX (text, lp.end.index)) && + (lp.end.index > lp.start.index)) + { + decrement_mark (&lp.end); + lp.displayable_chars -= 1; + } + } + else + { + while (!isspace(GTK_TEXT_INDEX (text, lp.end.index)) && + (lp.end.index > lp.start.index)) + { + decrement_mark (&lp.end); + lp.displayable_chars -= 1; + } + } + + /* If whole line is one word, revert to char wrapping */ + if (lp.end.index == lp.start.index) + { + lp.end = saved_mark; + lp.displayable_chars = saved_characters; + decrement_mark (&lp.end); + } + } + else + { + /* Don't include this character, it will wrap. */ + decrement_mark (&lp.end); + } + } + + lp.tab_cont_next = *next_cont; + + return lp; + } + } + else + { + lp.displayable_chars += 1; + } + + lp.font_ascent = MAX (font->ascent, lp.font_ascent); + lp.font_descent = MAX (font->descent, lp.font_descent); + lp.pixel_width += ch_width; + + advance_mark(&lp.end); + advance_tab_mark (text, &tab_mark, ch); + } + + if (LAST_INDEX(text, lp.start)) + { + /* Special case, empty last line. */ + font = MARK_CURRENT_FONT (text, &lp.end); + + lp.font_ascent = font->ascent; + lp.font_descent = font->descent; + } + + lp.tab_cont_next = *next_cont; + + return lp; +} + +static void +expand_scratch_buffer (GtkText* text, guint len) +{ + if (len >= text->scratch_buffer_len) + { + guint i = 1; + + while (i <= len && i < MIN_GAP_SIZE) i <<= 1; + + if (text->use_wchar) + { + if (!text->scratch_buffer.wc) + text->scratch_buffer.wc = g_new (GdkWChar, i); + else + text->scratch_buffer.wc = g_realloc (text->scratch_buffer.wc, + i * sizeof (GdkWChar)); + } + else + { + if (!text->scratch_buffer.ch) + text->scratch_buffer.ch = g_new (guchar, i); + else + text->scratch_buffer.ch = g_realloc (text->scratch_buffer.ch, i); + } + + text->scratch_buffer_len = i; + } +} + +/* Side effect: modifies text->gc + */ + +static void +draw_bg_rect (GtkText* text, GtkPropertyMark *mark, + gint x, gint y, gint width, gint height, + gboolean already_cleared) +{ + GtkEditable *editable = GTK_EDITABLE(text); + + if ((mark->index >= MIN(editable->selection_start_pos, editable->selection_end_pos) && + mark->index < MAX(editable->selection_start_pos, editable->selection_end_pos))) + { + gtk_paint_flat_box(GTK_WIDGET(text)->style, text->text_area, + editable->has_selection ? + GTK_STATE_SELECTED : GTK_STATE_ACTIVE, + GTK_SHADOW_NONE, + NULL, GTK_WIDGET(text), "text", + x, y, width, height); + } + else if (!gdk_color_equal(MARK_CURRENT_BACK (text, mark), + >K_WIDGET(text)->style->base[GTK_WIDGET_STATE (text)])) + { + gdk_gc_set_foreground (text->gc, MARK_CURRENT_BACK (text, mark)); + + gdk_draw_rectangle (text->text_area, + text->gc, + TRUE, x, y, width, height); + } + else if (GTK_WIDGET (text)->style->bg_pixmap[GTK_STATE_NORMAL]) + { + GdkRectangle rect; + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + clear_area (text, &rect); + } + else if (!already_cleared) + gdk_window_clear_area (text->text_area, x, y, width, height); +} + +static void +draw_line (GtkText* text, + gint pixel_start_height, + LineParams* lp) +{ + GdkGCValues gc_values; + gint i; + gint len = 0; + guint running_offset = lp->tab_cont.pixel_offset; + union { GdkWChar *wc; guchar *ch; } buffer; + GdkGC *fg_gc; + + GtkEditable *editable = GTK_EDITABLE(text); + + guint selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos); + guint selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos); + + GtkPropertyMark mark = lp->start; + TabStopMark tab_mark = lp->tab_cont.tab_start; + gint pixel_height = pixel_start_height + lp->font_ascent; + guint chars = lp->displayable_chars; + + /* First provide a contiguous segment of memory. This makes reading + * the code below *much* easier, and only incurs the cost of copying + * when the line being displayed spans the gap. */ + if (mark.index <= text->gap_position && + mark.index + chars > text->gap_position) + { + expand_scratch_buffer (text, chars); + + if (text->use_wchar) + { + for (i = 0; i < chars; i += 1) + text->scratch_buffer.wc[i] = GTK_TEXT_INDEX(text, mark.index + i); + buffer.wc = text->scratch_buffer.wc; + } + else + { + for (i = 0; i < chars; i += 1) + text->scratch_buffer.ch[i] = GTK_TEXT_INDEX(text, mark.index + i); + buffer.ch = text->scratch_buffer.ch; + } + } + else + { + if (text->use_wchar) + { + if (mark.index >= text->gap_position) + buffer.wc = text->text.wc + mark.index + text->gap_size; + else + buffer.wc = text->text.wc + mark.index; + } + else + { + if (mark.index >= text->gap_position) + buffer.ch = text->text.ch + mark.index + text->gap_size; + else + buffer.ch = text->text.ch + mark.index; + } + } + + + if (running_offset > 0) + { + draw_bg_rect (text, &mark, 0, pixel_start_height, running_offset, + LINE_HEIGHT (*lp), TRUE); + } + + while (chars > 0) + { + len = 0; + if ((text->use_wchar && buffer.wc[0] != '\t') || + (!text->use_wchar && buffer.ch[0] != '\t')) + { + union { GdkWChar *wc; guchar *ch; } next_tab; + gint pixel_width; + GdkFont *font; + + next_tab.wc = NULL; + if (text->use_wchar) + for (i=0; i<chars; i++) + { + if (buffer.wc[i] == '\t') + { + next_tab.wc = buffer.wc + i; + break; + } + } + else + next_tab.ch = memchr (buffer.ch, '\t', chars); + + len = MIN (MARK_CURRENT_PROPERTY (&mark)->length - mark.offset, chars); + + if (text->use_wchar) + { + if (next_tab.wc) + len = MIN (len, next_tab.wc - buffer.wc); + } + else + { + if (next_tab.ch) + len = MIN (len, next_tab.ch - buffer.ch); + } + + if (mark.index < selection_start_pos) + len = MIN (len, selection_start_pos - mark.index); + else if (mark.index < selection_end_pos) + len = MIN (len, selection_end_pos - mark.index); + + font = MARK_CURRENT_FONT (text, &mark); + if (font->type == GDK_FONT_FONT) + { + gdk_gc_set_font (text->gc, font); + gdk_gc_get_values (text->gc, &gc_values); + if (text->use_wchar) + pixel_width = gdk_text_width_wc (gc_values.font, + buffer.wc, len); + else + pixel_width = gdk_text_width (gc_values.font, + buffer.ch, len); + } + else + { + if (text->use_wchar) + pixel_width = gdk_text_width_wc (font, buffer.wc, len); + else + pixel_width = gdk_text_width (font, buffer.ch, len); + } + + draw_bg_rect (text, &mark, running_offset, pixel_start_height, + pixel_width, LINE_HEIGHT (*lp), TRUE); + + if ((mark.index >= selection_start_pos) && + (mark.index < selection_end_pos)) + { + if (editable->has_selection) + fg_gc = GTK_WIDGET(text)->style->fg_gc[GTK_STATE_SELECTED]; + else + fg_gc = GTK_WIDGET(text)->style->fg_gc[GTK_STATE_ACTIVE]; + } + else + { + gdk_gc_set_foreground (text->gc, MARK_CURRENT_FORE (text, &mark)); + fg_gc = text->gc; + } + + if (text->use_wchar) + gdk_draw_text_wc (text->text_area, MARK_CURRENT_FONT (text, &mark), + fg_gc, + running_offset, + pixel_height, + buffer.wc, + len); + else + gdk_draw_text (text->text_area, MARK_CURRENT_FONT (text, &mark), + fg_gc, + running_offset, + pixel_height, + buffer.ch, + len); + + running_offset += pixel_width; + + advance_tab_mark_n (text, &tab_mark, len); + } + else + { + gint pixels_remaining; + gint space_width; + gint spaces_avail; + + len = 1; + + gdk_window_get_size (text->text_area, &pixels_remaining, NULL); + if (GTK_EDITABLE (text)->editable || !text->word_wrap) + pixels_remaining -= (LINE_WRAP_ROOM + running_offset); + else + pixels_remaining -= running_offset; + + space_width = MARK_CURRENT_TEXT_FONT(text, &mark)->char_widths[' ']; + + spaces_avail = pixels_remaining / space_width; + spaces_avail = MIN (spaces_avail, tab_mark.to_next_tab); + + draw_bg_rect (text, &mark, running_offset, pixel_start_height, + spaces_avail * space_width, LINE_HEIGHT (*lp), TRUE); + + running_offset += tab_mark.to_next_tab * + MARK_CURRENT_TEXT_FONT(text, &mark)->char_widths[' ']; + + advance_tab_mark (text, &tab_mark, '\t'); + } + + advance_mark_n (&mark, len); + if (text->use_wchar) + buffer.wc += len; + else + buffer.ch += len; + chars -= len; + } +} + +static void +draw_line_wrap (GtkText* text, guint height /* baseline height */) +{ + gint width; + GdkPixmap *bitmap; + gint bitmap_width; + gint bitmap_height; + + if (!GTK_EDITABLE (text)->editable && text->word_wrap) + return; + + if (text->line_wrap) + { + bitmap = text->line_wrap_bitmap; + bitmap_width = line_wrap_width; + bitmap_height = line_wrap_height; + } + else + { + bitmap = text->line_arrow_bitmap; + bitmap_width = line_arrow_width; + bitmap_height = line_arrow_height; + } + + gdk_window_get_size (text->text_area, &width, NULL); + width -= LINE_WRAP_ROOM; + + gdk_gc_set_stipple (text->gc, + bitmap); + + gdk_gc_set_fill (text->gc, GDK_STIPPLED); + + gdk_gc_set_foreground (text->gc, >K_WIDGET (text)->style->text[GTK_STATE_NORMAL]); + + gdk_gc_set_ts_origin (text->gc, + width + 1, + height - bitmap_height - 1); + + gdk_draw_rectangle (text->text_area, + text->gc, + TRUE, + width + 1, + height - bitmap_height - 1 /* one pixel above the baseline. */, + bitmap_width, + bitmap_height); + + gdk_gc_set_ts_origin (text->gc, 0, 0); + + gdk_gc_set_fill (text->gc, GDK_SOLID); +} + +static void +undraw_cursor (GtkText* text, gint absolute) +{ + GtkEditable *editable = (GtkEditable *)text; + + TDEBUG (("in undraw_cursor\n")); + + if (absolute) + text->cursor_drawn_level = 0; + + if ((text->cursor_drawn_level ++ == 0) && + (editable->selection_start_pos == editable->selection_end_pos) && + GTK_WIDGET_DRAWABLE (text) && text->line_start_cache) + { + GdkFont* font; + + g_assert(text->cursor_mark.property); + + font = MARK_CURRENT_FONT(text, &text->cursor_mark); + + draw_bg_rect (text, &text->cursor_mark, + text->cursor_pos_x, + text->cursor_pos_y - text->cursor_char_offset - font->ascent, + 1, font->ascent + 1, FALSE); + + if (text->cursor_char) + { + if (font->type == GDK_FONT_FONT) + gdk_gc_set_font (text->gc, font); + + gdk_gc_set_foreground (text->gc, MARK_CURRENT_FORE (text, &text->cursor_mark)); + + if (text->use_wchar) + gdk_draw_text_wc (text->text_area, font, + text->gc, + text->cursor_pos_x, + text->cursor_pos_y - text->cursor_char_offset, + &text->cursor_char, + 1); + else + { + guchar ch = text->cursor_char; + gdk_draw_text (text->text_area, font, + text->gc, + text->cursor_pos_x, + text->cursor_pos_y - text->cursor_char_offset, + (gchar *)&ch, + 1); + } + } + } +} + +static gint +drawn_cursor_min (GtkText* text) +{ + GdkFont* font; + + g_assert(text->cursor_mark.property); + + font = MARK_CURRENT_FONT(text, &text->cursor_mark); + + return text->cursor_pos_y - text->cursor_char_offset - font->ascent; +} + +static gint +drawn_cursor_max (GtkText* text) +{ + GdkFont* font; + + g_assert(text->cursor_mark.property); + + font = MARK_CURRENT_FONT(text, &text->cursor_mark); + + return text->cursor_pos_y - text->cursor_char_offset; +} + +static void +draw_cursor (GtkText* text, gint absolute) +{ + GtkEditable *editable = (GtkEditable *)text; + + TDEBUG (("in draw_cursor\n")); + + if (absolute) + text->cursor_drawn_level = 1; + + if ((--text->cursor_drawn_level == 0) && + editable->editable && + (editable->selection_start_pos == editable->selection_end_pos) && + GTK_WIDGET_DRAWABLE (text) && text->line_start_cache) + { + GdkFont* font; + + g_assert (text->cursor_mark.property); + + font = MARK_CURRENT_FONT (text, &text->cursor_mark); + + gdk_gc_set_foreground (text->gc, >K_WIDGET (text)->style->text[GTK_STATE_NORMAL]); + + gdk_draw_line (text->text_area, text->gc, text->cursor_pos_x, + text->cursor_pos_y - text->cursor_char_offset, + text->cursor_pos_x, + text->cursor_pos_y - text->cursor_char_offset - font->ascent); + } +} + +static GdkGC * +create_bg_gc (GtkText *text) +{ + GdkGCValues values; + + values.tile = GTK_WIDGET (text)->style->bg_pixmap[GTK_STATE_NORMAL]; + values.fill = GDK_TILED; + + return gdk_gc_new_with_values (text->text_area, &values, + GDK_GC_FILL | GDK_GC_TILE); +} + +static void +clear_area (GtkText *text, GdkRectangle *area) +{ + GtkWidget *widget = GTK_WIDGET (text); + + if (text->bg_gc) + { + gint width, height; + + gdk_window_get_size (widget->style->bg_pixmap[GTK_STATE_NORMAL], &width, &height); + + gdk_gc_set_ts_origin (text->bg_gc, + (- (gint)text->first_onscreen_hor_pixel) % width, + (- (gint)text->first_onscreen_ver_pixel) % height); + + gdk_draw_rectangle (text->text_area, text->bg_gc, TRUE, + area->x, area->y, area->width, area->height); + } + else + gdk_window_clear_area (text->text_area, area->x, area->y, area->width, area->height); +} + +static void +expose_text (GtkText* text, GdkRectangle *area, gboolean cursor) +{ + GList *cache = text->line_start_cache; + gint pixels = - text->first_cut_pixels; + gint min_y = MAX (0, area->y); + gint max_y = MAX (0, area->y + area->height); + gint height; + + gdk_window_get_size (text->text_area, NULL, &height); + max_y = MIN (max_y, height); + + TDEBUG (("in expose x=%d y=%d w=%d h=%d\n", area->x, area->y, area->width, area->height)); + + clear_area (text, area); + + for (; pixels < height; cache = cache->next) + { + if (pixels < max_y && (pixels + (gint)LINE_HEIGHT(CACHE_DATA(cache))) >= min_y) + { + draw_line (text, pixels, &CACHE_DATA(cache)); + + if (CACHE_DATA(cache).wraps) + draw_line_wrap (text, pixels + CACHE_DATA(cache).font_ascent); + } + + if (cursor && GTK_WIDGET_HAS_FOCUS (text)) + { + if (CACHE_DATA(cache).start.index <= text->cursor_mark.index && + CACHE_DATA(cache).end.index >= text->cursor_mark.index) + { + /* We undraw and draw the cursor here to get the drawn + * level right ... FIXME - maybe the second parameter + * of draw_cursor should work differently + */ + undraw_cursor (text, FALSE); + draw_cursor (text, FALSE); + } + } + + pixels += LINE_HEIGHT(CACHE_DATA(cache)); + + if (!cache->next) + { + fetch_lines_forward (text, 1); + + if (!cache->next) + break; + } + } +} + +static void +gtk_text_update_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + GtkText *text = GTK_TEXT (editable); + + GList *cache = text->line_start_cache; + gint pixels = - text->first_cut_pixels; + GdkRectangle area; + gint width; + gint height; + + if (end_pos < 0) + end_pos = TEXT_LENGTH (text); + + if (end_pos < start_pos) + return; + + gdk_window_get_size (text->text_area, &width, &height); + area.x = 0; + area.y = -1; + area.width = width; + area.height = 0; + + TDEBUG (("in expose span start=%d stop=%d\n", start_pos, end_pos)); + + for (; pixels < height; cache = cache->next) + { + if (CACHE_DATA(cache).start.index < end_pos) + { + if (CACHE_DATA(cache).end.index >= start_pos) + { + if (area.y < 0) + area.y = MAX(0,pixels); + area.height = pixels + LINE_HEIGHT(CACHE_DATA(cache)) - area.y; + } + } + else + break; + + pixels += LINE_HEIGHT(CACHE_DATA(cache)); + + if (!cache->next) + { + fetch_lines_forward (text, 1); + + if (!cache->next) + break; + } + } + + if (area.y >= 0) + expose_text (text, &area, TRUE); +} + +static void +recompute_geometry (GtkText* text) +{ + GtkPropertyMark mark, start_mark; + GList *new_lines; + gint height; + gint width; + + free_cache (text); + + mark = start_mark = set_vertical_scroll (text); + + /* We need a real start of a line when calling fetch_lines(). + * not the start of a wrapped line. + */ + while (mark.index > 0 && + GTK_TEXT_INDEX (text, mark.index - 1) != LINE_DELIM) + decrement_mark (&mark); + + gdk_window_get_size (text->text_area, &width, &height); + + /* Fetch an entire line, to make sure that we get all the text + * we backed over above, in addition to enough text to fill up + * the space vertically + */ + + new_lines = fetch_lines (text, + &mark, + NULL, + FetchLinesCount, + 1); + + mark = CACHE_DATA (g_list_last (new_lines)).end; + if (!LAST_INDEX (text, mark)) + { + advance_mark (&mark); + + new_lines = g_list_concat (new_lines, + fetch_lines (text, + &mark, + NULL, + FetchLinesPixels, + height + text->first_cut_pixels)); + } + + /* Now work forward to the actual first onscreen line */ + + while (CACHE_DATA (new_lines).start.index < start_mark.index) + new_lines = new_lines->next; + + text->line_start_cache = new_lines; + + find_cursor (text, TRUE); +} + +/**********************************************************************/ +/* Selection */ +/**********************************************************************/ + +static void +gtk_text_set_selection (GtkEditable *editable, + gint start, + gint end) +{ + GtkText *text = GTK_TEXT (editable); + + guint start1, end1, start2, end2; + + if (end < 0) + end = TEXT_LENGTH (text); + + start1 = MIN(start,end); + end1 = MAX(start,end); + start2 = MIN(editable->selection_start_pos, editable->selection_end_pos); + end2 = MAX(editable->selection_start_pos, editable->selection_end_pos); + + if (start2 < start1) + { + guint tmp; + + tmp = start1; start1 = start2; start2 = tmp; + tmp = end1; end1 = end2; end2 = tmp; + } + + undraw_cursor (text, FALSE); + editable->selection_start_pos = start; + editable->selection_end_pos = end; + draw_cursor (text, FALSE); + + /* Expose only what changed */ + + if (start1 < start2) + gtk_text_update_text (editable, start1, MIN(end1, start2)); + + if (end2 > end1) + gtk_text_update_text (editable, MAX(end1, start2), end2); + else if (end2 < end1) + gtk_text_update_text (editable, end2, end1); +} + + +/**********************************************************************/ +/* Debug */ +/**********************************************************************/ + +#ifdef DEBUG_GTK_TEXT +static void +gtk_text_show_cache_line (GtkText *text, GList *cache, + const char* what, const char* func, gint line) +{ + LineParams *lp = &CACHE_DATA(cache); + gint i; + + if (cache == text->line_start_cache) + g_message ("Line Start Cache: "); + + if (cache == text->current_line) + g_message("Current Line: "); + + g_message ("%s:%d: cache line %s s=%d,e=%d,lh=%d (", + func, + line, + what, + lp->start.index, + lp->end.index, + LINE_HEIGHT(*lp)); + + for (i = lp->start.index; i < (lp->end.index + lp->wraps); i += 1) + g_message ("%c", GTK_TEXT_INDEX (text, i)); + + g_message (")\n"); +} + +static void +gtk_text_show_cache (GtkText *text, const char* func, gint line) +{ + GList *l = text->line_start_cache; + + if (!l) { + return; + } + + /* back up to the absolute beginning of the line cache */ + while (l->prev) + l = l->prev; + + g_message ("*** line cache ***\n"); + for (; l; l = l->next) + gtk_text_show_cache_line (text, l, "all", func, line); +} + +static void +gtk_text_assert_mark (GtkText *text, + GtkPropertyMark *mark, + GtkPropertyMark *before, + GtkPropertyMark *after, + const gchar *msg, + const gchar *where, + gint line) +{ + GtkPropertyMark correct_mark = find_mark (text, mark->index); + + if (mark->offset != correct_mark.offset || + mark->property != correct_mark.property) + g_warning ("incorrect %s text property marker in %s:%d, index %d -- bad!", where, msg, line, mark->index); +} + +static void +gtk_text_assert (GtkText *text, + const gchar *msg, + gint line) +{ + GList* cache = text->line_start_cache; + GtkPropertyMark* before_mark = NULL; + GtkPropertyMark* after_mark = NULL; + + gtk_text_show_props (text, msg, line); + + for (; cache->prev; cache = cache->prev) + /* nothing */; + + g_message ("*** line markers ***\n"); + + for (; cache; cache = cache->next) + { + after_mark = &CACHE_DATA(cache).end; + gtk_text_assert_mark (text, &CACHE_DATA(cache).start, before_mark, after_mark, msg, "start", line); + before_mark = &CACHE_DATA(cache).start; + + if (cache->next) + after_mark = &CACHE_DATA(cache->next).start; + else + after_mark = NULL; + + gtk_text_assert_mark (text, &CACHE_DATA(cache).end, before_mark, after_mark, msg, "end", line); + before_mark = &CACHE_DATA(cache).end; + } +} + +static void +gtk_text_show_adj (GtkText *text, + GtkAdjustment *adj, + const char* what, + const char* func, + gint line) +{ + g_message ("*** adjustment ***\n"); + + g_message ("%s:%d: %s adjustment l=%.1f u=%.1f v=%.1f si=%.1f pi=%.1f ps=%.1f\n", + func, + line, + what, + adj->lower, + adj->upper, + adj->value, + adj->step_increment, + adj->page_increment, + adj->page_size); +} + +static void +gtk_text_show_props (GtkText *text, + const char* msg, + int line) +{ + GList* props = text->text_properties; + int proplen = 0; + + g_message ("%s:%d: ", msg, line); + + for (; props; props = props->next) + { + TextProperty *p = (TextProperty*)props->data; + + proplen += p->length; + + g_message ("[%d,%p,", p->length, p); + if (p->flags & PROPERTY_FONT) + g_message ("%p,", p->font); + else + g_message ("-,"); + if (p->flags & PROPERTY_FOREGROUND) + g_message ("%ld, ", p->fore_color.pixel); + else + g_message ("-,"); + if (p->flags & PROPERTY_BACKGROUND) + g_message ("%ld] ", p->back_color.pixel); + else + g_message ("-] "); + } + + g_message ("\n"); + + if (proplen - 1 != TEXT_LENGTH(text)) + g_warning ("incorrect property list length in %s:%d -- bad!", msg, line); +} +#endif diff -ruN gtk+-1.2.10/gtk/gtktypeutils.h gtk-n/gtk/gtktypeutils.h --- gtk+-1.2.10/gtk/gtktypeutils.h Fri Aug 18 23:36:34 2000 +++ gtk-n/gtk/gtktypeutils.h Thu Jan 31 08:55:58 2002 @@ -191,6 +191,13 @@ GtkTypeClass *klass; }; +#ifdef __GNUC__ +struct _GtkTypeClassDummyAlign +{ + GtkType type; + guint *signals; +}; +#endif /* __GNUC__ */ /* A GtkTypeClass defines the minimum structure requirements for * a types class. Classes returned from gtk_type_class () and @@ -203,7 +210,11 @@ * one unique identifier per class. */ GtkType type; -}; +} +#ifdef __GNUC__ +__attribute__ ((aligned (__alignof (struct _GtkTypeClassDummyAlign)))) +#endif /* __GNUC__ */ +; struct _GtkArg diff -ruN gtk+-1.2.10/gtk/gtkwindow.c gtk-n/gtk/gtkwindow.c --- gtk+-1.2.10/gtk/gtkwindow.c Sat Mar 10 00:39:16 2001 +++ gtk-n/gtk/gtkwindow.c Thu Jan 31 08:57:10 2002 @@ -859,13 +859,60 @@ GTK_OBJECT_CLASS(parent_class)->finalize (object); } + +static void +reread_rc_files () +{ + if (gtk_rc_reparse_all ()) + { + /* If the above returned true, some of our RC files are out + * of date, so we need to reset all our widgets. Our other + * toplevel windows will also get the message, but by + * then, the RC file will up to date, so we have to tell + * them now. + */ + GList *toplevels; + + toplevels = gtk_container_get_toplevels(); + while (toplevels) + { + gtk_widget_reset_rc_styles (toplevels->data); + toplevels = toplevels->next; + } + } +} + static void gtk_window_show (GtkWidget *widget) { GtkWindow *window = GTK_WINDOW (widget); GtkContainer *container = GTK_CONTAINER (window); gboolean need_resize; + GList *toplevels; + gboolean had_visible = FALSE; + + /* If we have no windows shown at this point, then check for + * theme changes before showing the window. We really should + * be checking realized, not shown, but shown => realized, + * and checking in realize might cause reentrancy problems. + * + * Plus, this allows us to get the new size right before + * realizing. + */ + toplevels = gtk_container_get_toplevels (); + while (toplevels) + { + if (GTK_WIDGET_VISIBLE (toplevels->data)) + { + had_visible = TRUE; + break; + } + toplevels = toplevels->next; + } + if (!had_visible) + reread_rc_files (); + GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE); need_resize = container->need_resize || !GTK_WIDGET_REALIZED (widget); @@ -985,7 +1032,13 @@ break; case EnterNotify: case LeaveNotify: - if (xev->xcrossing.detail != NotifyInferior && + /* We only track the actual destination of keyboard events for real + * toplevels, not for embedded toplevels such as GtkPlug. The reason for + * this is that GtkPlug redirects events so the widget may effectively not + * have the focus even if it actually has the focus. + */ + if (gdk_window_get_parent (GTK_WIDGET (window)->window) == GDK_ROOT_PARENT () && + xev->xcrossing.detail != NotifyInferior && xev->xcrossing.focus && !window->window_has_focus) { window->window_has_pointer_focus = (xev->xany.type == EnterNotify) ? TRUE : FALSE; @@ -1474,23 +1527,7 @@ } } - if (gtk_rc_reparse_all ()) - { - /* If the above returned true, some of our RC files are out - * of date, so we need to reset all our widgets. Our other - * toplevel windows will also get the message, but by - * then, the RC file will up to date, so we have to tell - * them now. - */ - GList *toplevels; - - toplevels = gtk_container_get_toplevels(); - while (toplevels) - { - gtk_widget_reset_rc_styles (toplevels->data); - toplevels = toplevels->next; - } - } + reread_rc_files (); } static gint
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor