Edit

IABSD.fr/xenocara/app/xfd/grid.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2026-03-08 15:29:17
    Hash : 5c424f06
    Message : Update to xfd 1.1.5

  • app/xfd/grid.c
  • /*
     *
    Copyright 1989, 1998  The Open Group
    
    Permission to use, copy, modify, distribute, and sell this software and its
    documentation for any purpose is hereby granted without fee, provided that
    the above copyright notice appear in all copies and that both that
    copyright notice and this permission notice appear in supporting
    documentation.
    
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
    AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    
    Except as contained in this notice, the name of The Open Group shall not be
    used in advertising or otherwise to promote the sale, use or other dealings
    in this Software without prior written authorization from The Open Group.
     *
     * Author:  Jim Fulton, MIT X Consortium
     */
    
    #ifdef HAVE_CONFIG_H
    # include "config.h"
    #endif
    
    #include <X11/IntrinsicP.h>
    #include <X11/StringDefs.h>
    #include <X11/Xaw/SimpleP.h>
    #include <X11/Xmu/Converters.h>
    #include <X11/Xos.h>
    #include "gridP.h"
    
    #ifdef XKB
    #include <X11/extensions/XKBbells.h>
    #else
    #define	XkbBI_MinorError		2
    #define	XkbBI_Ignore			11
    #endif
    
    #ifdef XKB
    #define Bell(w,n) XkbStdBell(XtDisplay(w), XtWindow(w), 50, n)
    #else
    #define Bell(w,n) XBell(XtDisplay(w), 0)
    #endif
    
    static GC get_gc(FontGridWidget fgw, Pixel fore);
    static void ClassInitialize(void);
    static void Initialize(Widget request, Widget new, ArgList args,
    		       Cardinal *num_args);
    static void Realize(Widget gw, Mask *valueMask,
    		    XSetWindowAttributes *attributes);
    static void Destroy(Widget gw);
    static void Resize(Widget gw);
    static void Redisplay(Widget gw, XEvent *event, Region region);
    static void paint_grid(FontGridWidget fgw, int col, int row,
    		       int ncols, int nrows);
    static Boolean SetValues(Widget current, Widget request, Widget new,
    			 ArgList args, Cardinal *num_args);
    static void Notify(Widget gw, XEvent *event, String *params,
    		   Cardinal *nparams);
    
    #define Offset(field) XtOffsetOf(FontGridRec, fontgrid.field)
    
    static XtResource resources[] = {
        { XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
    	Offset(text_font), XtRString, (XtPointer) NULL },
        { XtNcellColumns, XtCCellColumns, XtRInt, sizeof(int),
    	Offset(cell_cols), XtRImmediate, (XtPointer) 0 },
        { XtNcellRows, XtCCellRows, XtRInt, sizeof(int),
    	Offset(cell_rows), XtRImmediate, (XtPointer) 0 },
        { XtNcellWidth, XtCCellWidth, XtRInt, sizeof(int),
    	Offset(cell_width), XtRImmediate, (XtPointer) 0 },
        { XtNcellHeight, XtCCellHeight, XtRInt, sizeof(int),
    	Offset(cell_height), XtRImmediate, (XtPointer) 0 },
        { XtNstartChar, XtCStartChar, XtRLong, sizeof(long),
    	Offset(start_char), XtRImmediate, (XtPointer) 0xffffffff },
    #ifndef XRENDER
        { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
    	Offset(foreground_pixel), XtRString, (XtPointer) XtDefaultForeground },
    #endif
        { XtNcenterChars, XtCCenterChars, XtRBoolean, sizeof(Boolean),
    	Offset(center_chars), XtRImmediate, (XtPointer) FALSE },
        { XtNboxChars, XtCBoxChars, XtRBoolean, sizeof(Boolean),
    	Offset(box_chars), XtRImmediate, (XtPointer) FALSE },
        { XtNboxColor, XtCForeground, XtRPixel, sizeof(Pixel),
    	Offset(box_pixel), XtRString, (XtPointer) XtDefaultForeground },
        { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
    	Offset(callbacks), XtRCallback, (XtPointer) NULL },
        { XtNinternalPad, XtCInternalPad, XtRInt, sizeof(int),
    	Offset(internal_pad), XtRImmediate, (XtPointer) 4 },
        { XtNgridWidth, XtCGridWidth, XtRInt, sizeof(int),
    	Offset(grid_width), XtRImmediate, (XtPointer) 1 },
    #ifdef XRENDER
        {XtNforeground, XtCForeground, XtRXftColor, sizeof(XftColor),
            Offset(fg_color), XtRString, XtDefaultForeground},
        {XtNface, XtCFace, XtRXftFont, sizeof (XftFont *),
    	Offset (text_face), XtRString, NULL},
    #endif
    };
    
    #undef Offset
    
    static char defaultTranslations[] =
      "<ButtonPress>:  notify()";
    
    static XtActionsRec actions_list[] = {
        { "notify",		Notify },
    };
    
    FontGridClassRec fontgridClassRec = {
      { /* core fields */
        /* superclass               */      (WidgetClass) &simpleClassRec,
        /* class_name               */      "FontGrid",
        /* widget_size              */      sizeof(FontGridRec),
        /* class_initialize         */      ClassInitialize,
        /* class_part_initialize    */      NULL,
        /* class_inited             */      FALSE,
        /* initialize               */      Initialize,
        /* initialize_hook          */      NULL,
        /* realize                  */      Realize,
        /* actions                  */      actions_list,
        /* num_actions              */      XtNumber(actions_list),
        /* resources                */      resources,
        /* num_resources            */      XtNumber(resources),
        /* xrm_class                */      NULLQUARK,
        /* compress_motion          */      TRUE,
        /* compress_exposure        */      TRUE,
        /* compress_enterleave      */      TRUE,
        /* visible_interest         */      FALSE,
        /* destroy                  */      Destroy,
        /* resize                   */      Resize,
        /* expose                   */      Redisplay,
        /* set_values               */      SetValues,
        /* set_values_hook          */      NULL,
        /* set_values_almost        */      XtInheritSetValuesAlmost,
        /* get_values_hook          */      NULL,
        /* accept_focus             */      NULL,
        /* version                  */      XtVersion,
        /* callback_private         */      NULL,
        /* tm_table                 */      defaultTranslations,
        /* query_geometry           */      XtInheritQueryGeometry,
        /* display_accelerator      */      XtInheritDisplayAccelerator,
        /* extension                */      NULL
      },
      { /* simple fields */
        /* change_sensitive		*/	XtInheritChangeSensitive,
        /* extension                */      NULL
      }
    };
    
    WidgetClass fontgridWidgetClass = (WidgetClass) &fontgridClassRec;
    
    
    long
    GridFirstChar (Widget w)
    {
        FontGridWidget	fgw = (FontGridWidget) w;
        XFontStruct		*fs = fgw->fontgrid.text_font;
    #ifdef XRENDER
        XftFont		*xft = fgw->fontgrid.text_face;
        if (xft)
        {
    	FcChar32    map[FC_CHARSET_MAP_SIZE];
    	FcChar32    next;
    	FcChar32    first;
    
    	first = FcCharSetFirstPage (xft->charset, map, &next);
    	for (int i = 0; i < FC_CHARSET_MAP_SIZE; i++)
    	    if (map[i])
    	    {
    		FcChar32    bits = map[i];
    		first += i * 32;
    		while (!(bits & 0x1))
    		{
    		    bits >>= 1;
    		    first++;
    		}
    		break;
    	    }
    	return first;
        }
        else
    #endif
        if (fs)
        {
    	return (fs->min_byte1 << 8) | (fs->min_char_or_byte2);
        }
        else
    	return 0;
    }
    
    long
    GridLastChar (Widget w)
    {
        FontGridWidget	fgw = (FontGridWidget) w;
        XFontStruct		*fs = fgw->fontgrid.text_font;
    #ifdef XRENDER
        XftFont		*xft = fgw->fontgrid.text_face;
        if (xft)
        {
    	FcChar32    this, last, next;
    	FcChar32    map[FC_CHARSET_MAP_SIZE];
    
    	last = FcCharSetFirstPage (xft->charset, map, &next);
    	while ((this = FcCharSetNextPage (xft->charset, map, &next)) != FC_CHARSET_DONE)
    	    last = this;
    	last &= ~0xff;
    	for (int i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
    	    if (map[i])
    	    {
    		FcChar32    bits = map[i];
    		last += i * 32 + 31;
    		while (!(bits & 0x80000000))
    		{
    		    last--;
    		    bits <<= 1;
    		}
    		break;
    	    }
    	return (long) last;
        }
        else
    #endif
        if (fs)
        {
    	return (fs->max_byte1 << 8) | (fs->max_char_or_byte2);
        }
        else
    	return 0;
    }
    
    /*
     * CI_GET_CHAR_INFO_1D - return the charinfo struct for the indicated 8bit
     * character.  If the character is in the column and exists, then return the
     * appropriate metrics (note that fonts with common per-character metrics will
     * return min_bounds).
     */
    
    #define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
    			     (((cs)->rbearing|(cs)->lbearing| \
    			       (cs)->ascent|(cs)->descent) == 0))
    
    #define CI_GET_CHAR_INFO_1D(fs,col,cs) \
    do { \
        cs = NULL; \
        if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
    	if (fs->per_char == NULL) { \
    	    cs = &fs->min_bounds; \
    	} else { \
    	    cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
    	} \
    	if (CI_NONEXISTCHAR(cs)) \
    	    cs = NULL; \
        } \
    } while (0)
    
    /*
     * CI_GET_CHAR_INFO_2D - return the charinfo struct for the indicated row and
     * column.  This is used for fonts that have more than row zero.
     */
    #define CI_GET_CHAR_INFO_2D(fs,row,col,cs) \
    do { \
        cs = NULL; \
        if (row >= fs->min_byte1 && row <= fs->max_byte1 && \
    	col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
    	if (fs->per_char == NULL) { \
    	    cs = &fs->min_bounds; \
    	} else { \
    	    cs = &fs->per_char[((row - fs->min_byte1) * \
    			        (fs->max_char_or_byte2 - \
    				 fs->min_char_or_byte2 + 1)) + \
    			       (col - fs->min_char_or_byte2)]; \
            } \
    	if (CI_NONEXISTCHAR(cs)) \
    	    cs = NULL; \
        } \
    } while (0)
    
    static Boolean
    GridHasChar (Widget w, long ch)
    {
        FontGridWidget	fgw = (FontGridWidget) w;
    #ifdef XRENDER
        XftFont		*xft = fgw->fontgrid.text_face;
        if (xft)
        {
    	return FcCharSetHasChar (xft->charset, (FcChar32) ch);
        }
        else
    #endif
        {
    	XFontStruct	*fs = fgw->fontgrid.text_font;
    	XCharStruct	*cs;
    
    	if (!fs)
    	    return False;
    	if (fs->max_byte1 == 0)
    	{
    	    CI_GET_CHAR_INFO_1D (fs, ch, cs);
    	}
    	else
    	{
    	    unsigned int	r = (ch >> 8);
    	    unsigned int	c = (ch & 0xff);
    	    CI_GET_CHAR_INFO_2D (fs, r, c, cs);
    	}
    	return cs != NULL;
        }
    }
    
    /*
     * public routines
     */
    
    void
    GetFontGridCellDimensions(Widget w, long *startp,
    			  int *ncolsp, int *nrowsp)
    {
        FontGridWidget fgw = (FontGridWidget) w;
        *startp = fgw->fontgrid.start_char;
        *ncolsp = fgw->fontgrid.cell_cols;
        *nrowsp = fgw->fontgrid.cell_rows;
    }
    
    
    void
    GetPrevNextStates(Widget w, Bool *prevvalidp, Bool *nextvalidp,
    		  Bool *prev16validp, Bool *next16validp)
    {
        FontGridWidget fgw = (FontGridWidget) w;
        long minn = GridFirstChar (w);
        long maxn = GridLastChar (w);
    
        *prev16validp = (fgw->fontgrid.start_char - 0xf00 > minn);
        *prevvalidp = (fgw->fontgrid.start_char > minn);
        *nextvalidp = (fgw->fontgrid.start_char +
    		   (fgw->fontgrid.cell_cols * fgw->fontgrid.cell_rows)
    		   < maxn);
        *next16validp =((fgw->fontgrid.start_char + 0xf00 +
    		    (fgw->fontgrid.cell_cols * fgw->fontgrid.cell_rows))
    		   < maxn);
    }
    
    
    
    /*
     * private routines and methods
     */
    
    
    static GC
    get_gc(FontGridWidget fgw, Pixel fore)
    {
        XtGCMask mask;
        XGCValues gcv;
    
        mask = (GCForeground | GCBackground | GCFunction);
        gcv.foreground = fore;
        gcv.background = fgw->core.background_pixel;
        gcv.function = GXcopy;
        if (fgw->fontgrid.text_font)
        {
    	mask |= GCFont;
    	gcv.font = fgw->fontgrid.text_font->fid;
        }
        gcv.cap_style = CapProjecting;
        mask |= GCCapStyle;
        if (fgw->fontgrid.grid_width > 0) {
    	mask |= GCLineWidth;
    	gcv.line_width = ((fgw->fontgrid.grid_width < 2) ? 0 :
    			  fgw->fontgrid.grid_width);
        }
        return (XtGetGC ((Widget) fgw, mask, &gcv));
    }
    
    
    #ifdef XRENDER
    static XtConvertArgRec xftColorConvertArgs[] = {
        {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen),
         sizeof(Screen *)},
        {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.colormap),
         sizeof(Colormap)}
    };
    
    #define	donestr(type, value, tstr) \
    	do {							\
    	    if (toVal->addr != NULL) {				\
    		if (toVal->size < sizeof(type)) {		\
    		    toVal->size = sizeof(type);			\
    		    XtDisplayStringConversionWarning(dpy, 	\
    			fromVal->addr, tstr);			\
    		    return False;				\
    		}						\
    		*(type*)(toVal->addr) = (value);		\
    	    }							\
    	    else {						\
    		static type static_val;				\
    		static_val = (value);				\
    		toVal->addr = (XPointer)&static_val;		\
    	    }							\
    	    toVal->size = sizeof(type);				\
    	    return True;					\
    	} while (0)
    
    static void
    XmuFreeXftColor (XtAppContext app, XrmValuePtr toVal,
    		 XtPointer closure _X_UNUSED,
    		 XrmValuePtr args, Cardinal *num_args)
    {
        Screen	*screen;
        Colormap	colormap;
        XftColor	*color;
    
        if (*num_args != 2)
        {
    	XtAppErrorMsg (app,
    		       "freeXftColor", "wrongParameters",
    		       "XtToolkitError",
    		       "Freeing an XftColor requires screen and colormap arguments",
    		       (String *) NULL, (Cardinal *)NULL);
    	return;
        }
    
        screen = *((Screen **) args[0].addr);
        colormap = *((Colormap *) args[1].addr);
        color = (XftColor *) toVal->addr;
        XftColorFree (DisplayOfScreen (screen),
    		  DefaultVisual (DisplayOfScreen (screen),
    				 XScreenNumberOfScreen (screen)),
    		  colormap, color);
    }
    
    static Boolean
    XmuCvtStringToXftColor(Display *dpy,
    		       XrmValue *args, Cardinal *num_args,
    		       XrmValue *fromVal, XrmValue *toVal,
    		       XtPointer *converter_data _X_UNUSED)
    {
        char	    *spec;
        XRenderColor    renderColor;
        XftColor	    xftColor;
        Screen	    *screen;
        Colormap	    colormap;
    
        if (*num_args != 2)
        {
    	XtAppErrorMsg (XtDisplayToApplicationContext (dpy),
    		       "cvtStringToXftColor", "wrongParameters",
    		       "XtToolkitError",
    		       "String to render color conversion needs screen and colormap arguments",
    		       (String *) NULL, (Cardinal *)NULL);
    	return False;
        }
    
        screen = *((Screen **) args[0].addr);
        colormap = *((Colormap *) args[1].addr);
    
        spec = fromVal->addr;
        if (strcasecmp (spec, XtDefaultForeground) == 0)
        {
    	renderColor.red = 0;
    	renderColor.green = 0;
    	renderColor.blue = 0;
    	renderColor.alpha = 0xffff;
        }
        else if (strcasecmp (spec, XtDefaultBackground) == 0)
        {
    	renderColor.red = 0xffff;
    	renderColor.green = 0xffff;
    	renderColor.blue = 0xffff;
    	renderColor.alpha = 0xffff;
        }
        else if (!XRenderParseColor (dpy, spec, &renderColor))
    	return False;
        if (!XftColorAllocValue (dpy,
    			     DefaultVisual (dpy,
    					    XScreenNumberOfScreen (screen)),
    			     colormap,
    			     &renderColor,
    			     &xftColor))
    	return False;
    
        donestr (XftColor, xftColor, XtRXftColor);
    }
    
    static void
    XmuFreeXftFont (XtAppContext app, XrmValuePtr toVal, XtPointer closure _X_UNUSED,
    		XrmValuePtr args, Cardinal *num_args)
    {
        Screen  *screen;
        XftFont *font;
    
        if (*num_args != 1)
        {
    	XtAppErrorMsg (app,
    		       "freeXftFont", "wrongParameters",
    		       "XtToolkitError",
    		       "Freeing an XftFont requires screen argument",
    		       (String *) NULL, (Cardinal *)NULL);
    	return;
        }
    
        screen = *((Screen **) args[0].addr);
        font = *((XftFont **) toVal->addr);
        if (font)
    	XftFontClose (DisplayOfScreen (screen), font);
    }
    
    static Boolean
    XmuCvtStringToXftFont(Display *dpy,
    		      XrmValue *args, Cardinal *num_args,
    		      XrmValue *fromVal, XrmValue *toVal,
    		      XtPointer *converter_data _X_UNUSED)
    {
        char    *name;
        XftFont *font;
        Screen  *screen;
    
        if (*num_args != 1)
        {
    	XtAppErrorMsg (XtDisplayToApplicationContext (dpy),
    		       "cvtStringToXftFont", "wrongParameters",
    		       "XtToolkitError",
    		       "String to XftFont conversion needs screen argument",
    		       (String *) NULL, (Cardinal *)NULL);
    	return False;
        }
    
        screen = *((Screen **) args[0].addr);
        name = fromVal->addr;
    
        font = NULL;
        if (name)
        {
    	font = XftFontOpenName (dpy,
    				XScreenNumberOfScreen (screen),
    				name);
    	if (!font)
    	    XtDisplayStringConversionWarning(dpy, fromVal->addr, XtRXftFont);
        }
        donestr (XftFont *, font, XtRXftFont);
    }
    
    static XtConvertArgRec xftFontConvertArgs[] = {
        {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen),
         sizeof(Screen *)},
    };
    
    #endif
    
    static void
    ClassInitialize(void)
    {
        XtAddConverter (XtRString, XtRLong, XmuCvtStringToLong, NULL, 0);
    #ifdef XRENDER
        XtSetTypeConverter (XtRString, XtRXftColor,
    			XmuCvtStringToXftColor,
    			xftColorConvertArgs, XtNumber(xftColorConvertArgs),
    			XtCacheByDisplay, XmuFreeXftColor);
        XtSetTypeConverter (XtRString, XtRXftFont,
    			XmuCvtStringToXftFont,
    			xftFontConvertArgs, XtNumber(xftFontConvertArgs),
    			XtCacheByDisplay, XmuFreeXftFont);
    #endif
    }
    
    
    static void
    Initialize(Widget request, Widget new,
               ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    {
        FontGridWidget reqfg = (FontGridWidget) request;
        FontGridWidget newfg = (FontGridWidget) new;
        XFontStruct *fs = newfg->fontgrid.text_font;
    #ifdef XRENDER
        XftFont *xft = newfg->fontgrid.text_face;
    #endif
        unsigned maxn;
    
        if (reqfg->fontgrid.cell_cols <= 0)
          newfg->fontgrid.cell_cols = 16;
    
        if (reqfg->fontgrid.cell_rows <= 0) {
    #ifdef XRENDER
    	if (xft)
    	    newfg->fontgrid.cell_rows = 16;
    	else
    #endif
    	if (fs && fs->max_byte1 == 0) {
    	    newfg->fontgrid.cell_rows = (fs->max_char_or_byte2 /
    					 newfg->fontgrid.cell_cols) + 1;
    	    if (newfg->fontgrid.cell_rows > 16)
    	      newfg->fontgrid.cell_rows = 16;
    	} else
    	  newfg->fontgrid.cell_rows = 16;
        }
    
        if (reqfg->fontgrid.cell_width <= 0)
          newfg->fontgrid.cell_width = DefaultCellWidth (newfg);
        if (reqfg->fontgrid.cell_height <= 0)
          newfg->fontgrid.cell_height = DefaultCellHeight (newfg);
    
        /* give a nice size that fits one screen full */
        if (newfg->core.width == 0)
          newfg->core.width = (newfg->fontgrid.cell_width *
    			   newfg->fontgrid.cell_cols +
    			   newfg->fontgrid.grid_width *
    			   (newfg->fontgrid.cell_cols + 1));
    
        if (newfg->core.height == 0)
          newfg->core.height = (newfg->fontgrid.cell_height *
    			    newfg->fontgrid.cell_rows +
    			    newfg->fontgrid.grid_width *
    			    (newfg->fontgrid.cell_rows + 1));
    
        /*
         * select the first character
         */
    
        if (newfg->fontgrid.start_char == 0xffffffff)
    	newfg->fontgrid.start_char = GridFirstChar(new) & ~0xff;
        maxn = GridLastChar (new);
        if (newfg->fontgrid.start_char > maxn)
    	newfg->fontgrid.start_char = (maxn + 1 -
    				      (newfg->fontgrid.cell_cols *
    				       newfg->fontgrid.cell_rows));
    }
    
    static void
    Realize(Widget gw, Mask *valueMask, XSetWindowAttributes *attributes)
    {
        FontGridWidget fgw = (FontGridWidget) gw;
        FontGridPart *p = &fgw->fontgrid;
    
        p->text_gc = get_gc (fgw, GridForeground (fgw));
        p->box_gc = get_gc (fgw, p->box_pixel);
        Resize (gw);
    
        (*(XtSuperclass(gw)->core_class.realize)) (gw, valueMask, attributes);
    #ifdef XRENDER
        p->draw = XftDrawCreate (XtDisplay (gw), XtWindow (gw),
    			     DefaultVisual (XtDisplay (gw),
    					    DefaultScreen(XtDisplay (gw))),
    			     fgw->core.colormap);
    #endif
        return;
    }
    
    
    
    static void
    Destroy(Widget gw)
    {
        FontGridWidget fgw = (FontGridWidget) gw;
    
        XtReleaseGC (gw, fgw->fontgrid.text_gc);
        XtReleaseGC (gw, fgw->fontgrid.box_gc);
    }
    
    
    static void
    Resize(Widget gw)
    {
        FontGridWidget fgw = (FontGridWidget) gw;
    
        /* recompute in case we've changed size */
        fgw->fontgrid.cell_width = CellWidth (fgw);
        if (fgw->fontgrid.cell_width <= 0)
    	fgw->fontgrid.cell_width = 1;
        fgw->fontgrid.cell_height = CellHeight (fgw);
        if (fgw->fontgrid.cell_height <= 0)
    	fgw->fontgrid.cell_height = 1;
        fgw->fontgrid.xoff = (fgw->fontgrid.cell_width -
    			    DefaultCellWidth (fgw)) / 2;
        fgw->fontgrid.yoff = (fgw->fontgrid.cell_height -
    			    DefaultCellHeight (fgw)) / 2;
    
    }
    
    
    /* ARGSUSED */
    static void
    Redisplay(Widget gw, XEvent *event _X_UNUSED, Region region)
    {
        FontGridWidget fgw = (FontGridWidget) gw;
        XRectangle rect;			/* bounding rect for region */
        int left, right, top, bottom;	/* which cells were damaged */
        int cw, ch;				/* cell size */
    
    #ifdef XRENDER
        if (!fgw->fontgrid.text_face)
    #endif
        if (!fgw->fontgrid.text_font) {
    	Bell (gw, XkbBI_BadValue);
    	return;
        }
    
        /*
         * compute the left, right, top, and bottom cells that were damaged
         */
        XClipBox (region, &rect);
        cw = fgw->fontgrid.cell_width + fgw->fontgrid.grid_width;
        ch = fgw->fontgrid.cell_height + fgw->fontgrid.grid_width;
        if ((left = (((int) rect.x) / cw)) < 0) left = 0;
        right = (((int) (rect.x + rect.width - 1)) / cw);
        if ((top = (((int) rect.y) / ch)) < 0) top = 0;
        bottom = (((int) (rect.y + rect.height - 1)) / ch);
    
        paint_grid (fgw, left, top, right - left + 1, bottom - top + 1);
    }
    
    
    static void
    paint_grid(FontGridWidget fgw, 		/* widget in which to draw */
    	   int col, int row, 		/* where to start */
    	   int ncols, int nrows)	/* number of cells */
    {
        FontGridPart *p = &fgw->fontgrid;
        int i, j;
        Display *dpy = XtDisplay(fgw);
        Window wind = XtWindow(fgw);
        int cw = p->cell_width + p->grid_width;
        int ch = p->cell_height + p->grid_width;
        int tcols = p->cell_cols;
        int trows = p->cell_rows;
        int x1, y1, x2, y2, x, y;
        unsigned maxn = GridLastChar ((Widget) fgw);
        unsigned n, prevn;
        int startx;
    
        if (col + ncols >= tcols) {
    	ncols = tcols - col;
    	if (ncols < 1) return;
        }
    
        if (row + nrows >= trows) {
    	nrows = trows - row;
    	if (nrows < 1) return;
        }
    
        /*
         * paint the grid lines for the indicated rows
         */
        if (p->grid_width > 0) {
    	int half_grid_width = p->grid_width >> 1;
    	x1 = col * cw + half_grid_width;
    	y1 = row * ch + half_grid_width;
    	x2 = x1 + ncols * cw;
    	y2 = y1 + nrows * ch;
    	for (i = 0, x = x1; i <= ncols; i++, x += cw) {
    	    XDrawLine (dpy, wind, p->box_gc, x, y1, x, y2);
    	}
    	for (i = 0, y = y1; i <= nrows; i++, y += ch) {
    	    XDrawLine (dpy, wind, p->box_gc, x1, y, x2, y);
    	}
        }
        /*
         * Draw a character in every box; treat all fonts as if they were 16bit
         * fonts.  Store the high eight bits in byte1 and the low eight bits in
         * byte2.
         */
        prevn = p->start_char + col + row * tcols;
        startx = col * cw + p->internal_pad + p->grid_width;
        for (j = 0,
    	 y = row * ch + p->internal_pad + p->grid_width + GridFontAscent (fgw);
    	 j < nrows; j++, y += ch) {
    	n = prevn;
    	for (i = 0, x = startx; i < ncols; i++, x += cw) {
    	    int xoff = p->xoff, yoff = p->yoff;
    	    if (n > maxn) goto done;	/* no break out of nested */
    
    #ifdef XRENDER
    	    if (fgw->fontgrid.text_face)
    	    {
    		XftFont *xft = p->text_face;
    		FcChar32    c = n;
    		XGlyphInfo  extents;
    		XftTextExtents32 (dpy, xft, &c, 1, &extents);
    		if (p->center_chars)
    		{
    		    xoff = (p->cell_width - extents.width) / 2 - extents.x;
    		    yoff = (p->cell_height - extents.height) / 2 - extents.y;
    		}
    		if (extents.width && extents.height)
    		{
    		    XClearArea (dpy, wind, x + xoff - extents.x,
    				y + yoff - extents.y,
    				extents.width, extents.height, False);
    		    if (p->box_chars)
    			XDrawRectangle (dpy, wind, p->box_gc,
    					x + xoff - extents.x,
    					y + yoff - extents.y,
    					extents.width - 1,
    					extents.height - 1);
    		}
    		XftDrawString32 (p->draw, &p->fg_color, xft,
    				 x + xoff, y + yoff, &c, 1);
    	    }
    	    else
    #endif
    	    {
    	    XChar2b thechar;
    
    	    thechar.byte1 = (n >> 8);	/* high eight bits */
    	    thechar.byte2 = (n & 255);	/* low eight bits */
    	    if (p->box_chars || p->center_chars) {
    		XCharStruct metrics;
    		int direction, fontascent, fontdescent;
    
    		XTextExtents16 (p->text_font, &thechar, 1, &direction,
    				&fontascent, &fontdescent, &metrics);
    
    		if (p->center_chars) {
    		    /*
    		     * We want to move the origin by enough to center the ink
    		     * within the cell.  The left edge will then be at
    		     * (cell_width - (rbearing - lbearing)) / 2; so we subtract
    		     * the lbearing to find the origin.  Ditto for vertical.
    		     */
    		    xoff = (((p->cell_width -
    			      (metrics.rbearing - metrics.lbearing)) / 2) -
    			    p->internal_pad - metrics.lbearing);
    		    yoff = (((p->cell_height -
    			      (metrics.descent + metrics.ascent)) / 2) -
    			    p->internal_pad -
    			    p->text_font->ascent + metrics.ascent);
    		}
    		if (p->box_chars) {
    		    XDrawRectangle (dpy, wind, p->box_gc,
    				    x + xoff, y + yoff - p->text_font->ascent,
    				    metrics.width - 1,
    				    fontascent + fontdescent - 1);
    		}
    	    }
    	    XDrawString16 (dpy, wind, p->text_gc, x + xoff, y + yoff,
    			   &thechar, 1);
    	    }
    	    n++;
    	}
    	prevn += tcols;
        }
    
      done:
        /*
         * paint the grid lines for the indicated rows
         */
        if (p->grid_width > 0) {
    	int half_grid_width = p->grid_width >> 1;
    	x1 = col * cw + half_grid_width;
    	y1 = row * ch + half_grid_width;
    	x2 = x1 + ncols * cw;
    	y2 = y1 + nrows * ch;
    	for (i = 0, x = x1; i <= ncols; i++, x += cw) {
    	    XDrawLine (dpy, wind, p->box_gc, x, y1, x, y2);
    	}
    	for (i = 0, y = y1; i <= nrows; i++, y += ch) {
    	    XDrawLine (dpy, wind, p->box_gc, x1, y, x2, y);
    	}
        }
    
    
        return;
    }
    
    static Boolean
    PageBlank (Widget w, long first, long last)
    {
        while (first <= last)
        {
    	if (GridHasChar (w, first))
    	    return False;
    	first++;
        }
        return True;
    }
    
    /*ARGSUSED*/
    static Boolean
    SetValues(Widget current, Widget request _X_UNUSED, Widget new,
    	  ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
    {
        FontGridWidget curfg = (FontGridWidget) current;
        FontGridWidget newfg = (FontGridWidget) new;
        Boolean redisplay = FALSE;
    
        if (curfg->fontgrid.text_font != newfg->fontgrid.text_font ||
    	curfg->fontgrid.internal_pad != newfg->fontgrid.internal_pad) {
    	newfg->fontgrid.cell_width = DefaultCellWidth (newfg);
    	newfg->fontgrid.cell_height = DefaultCellHeight (newfg);
    	redisplay = TRUE;
        }
    
        if (GridForeground(curfg) != GridForeground (newfg)) {
    	XtReleaseGC (new, curfg->fontgrid.text_gc);
    	newfg->fontgrid.text_gc = get_gc (newfg, GridForeground (newfg));
    	redisplay = TRUE;
        }
    
        if (curfg->fontgrid.box_pixel != newfg->fontgrid.box_pixel) {
    	XtReleaseGC (new, curfg->fontgrid.text_gc);
    	newfg->fontgrid.box_gc = get_gc (newfg, newfg->fontgrid.box_pixel);
    	redisplay = TRUE;
        }
    
        if (curfg->fontgrid.center_chars != newfg->fontgrid.center_chars ||
    	curfg->fontgrid.box_chars != newfg->fontgrid.box_chars)
          redisplay = TRUE;
    
        if (curfg->fontgrid.start_char != newfg->fontgrid.start_char) {
    	long maxn = GridLastChar (new);
    	long page = newfg->fontgrid.cell_cols * newfg->fontgrid.cell_rows;
    	long dir = page;
    	long start = newfg->fontgrid.start_char;
    
    	if (start < curfg->fontgrid.start_char)
    	    dir = -page;
    
    	if (start < 0)
    	    start = 0;
    	if (start > maxn)
    	  start = (maxn / page) * page;
    
    	while (PageBlank (new, start, start + page - 1))
    	{
    	    long    next = start + dir;
    
    	    if (next < 0 || maxn < next)
    		break;
    	    start = next;
    	}
    
    	newfg->fontgrid.start_char = start;
    	redisplay = (curfg->fontgrid.start_char != newfg->fontgrid.start_char);
        }
    
        return redisplay;
    }
    
    
    /* ARGSUSED */
    static void
    Notify(Widget gw, XEvent *event,
           String *params _X_UNUSED, Cardinal *nparams _X_UNUSED)
    {
        FontGridWidget fgw = (FontGridWidget) gw;
        int x, y;				/* where the event happened */
        FontGridCharRec rec;		/* callback data */
    
        /*
         * only allow events with (x,y)
         */
        switch (event->type) {
          case KeyPress:
          case KeyRelease:
    	x = event->xkey.x;
    	y = event->xkey.y;
    	break;
          case ButtonPress:
          case ButtonRelease:
    	x = event->xbutton.x;
    	y = event->xbutton.y;
    	break;
          case MotionNotify:
    	x = event->xmotion.x;
    	y = event->xmotion.y;
    	break;
          default:
    	Bell (gw, XkbBI_Ignore);
    	return;
        }
    
        /*
         * compute the callback data
         */
        {
    	int cw = fgw->fontgrid.cell_width + fgw->fontgrid.grid_width;
    	int ch = fgw->fontgrid.cell_height + fgw->fontgrid.grid_width;
    	unsigned n;
    
    	if (x > (fgw->fontgrid.cell_cols * cw)) {
    	    Bell (gw, XkbBI_InvalidLocation);
    	    return;
    	}
    
    	n= (fgw->fontgrid.start_char +
    	    ((y / ch) * fgw->fontgrid.cell_cols) + (x / cw));
    
    	rec.thefont = fgw->fontgrid.text_font;
    #ifdef XRENDER
    	rec.theface = fgw->fontgrid.text_face;
    #endif
    	rec.thechar = n;
        }
    
        XtCallCallbacks (gw, XtNcallback, (XtPointer) &rec);
    }