grid.c revision 1afad795
1/*
2 *
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 *
25 * Author:  Jim Fulton, MIT X Consortium
26 */
27
28#include <X11/IntrinsicP.h>
29#include <X11/StringDefs.h>
30#include <X11/Xaw/SimpleP.h>
31#include <X11/Xmu/Converters.h>
32#include <X11/Xos.h>
33#include "gridP.h"
34
35#ifdef XKB
36#include <X11/extensions/XKBbells.h>
37#else
38#define	XkbBI_MinorError		2
39#define	XkbBI_Ignore			11
40#endif
41
42#ifdef XKB
43#define Bell(w,n) XkbStdBell(XtDisplay(w), XtWindow(w), 50, n)
44#else
45#define Bell(w,n) XBell(XtDisplay(w), 0)
46#endif
47
48static GC get_gc(FontGridWidget fgw, Pixel fore);
49static void ClassInitialize(void);
50static void Initialize(Widget request, Widget new, ArgList args,
51		       Cardinal *num_args);
52static void Realize(Widget gw, Mask *valueMask,
53		    XSetWindowAttributes *attributes);
54static void Destroy(Widget gw);
55static void Resize(Widget gw);
56static void Redisplay(Widget gw, XEvent *event, Region region);
57static void paint_grid(FontGridWidget fgw, int col, int row,
58		       int ncols, int nrows);
59static Boolean SetValues(Widget current, Widget request, Widget new,
60			 ArgList args, Cardinal *num_args);
61static void Notify(Widget gw, XEvent *event, String *params,
62		   Cardinal *nparams);
63
64#define Offset(field) XtOffsetOf(FontGridRec, fontgrid.field)
65
66static XtResource resources[] = {
67    { XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
68	Offset(text_font), XtRString, (XtPointer) NULL },
69    { XtNcellColumns, XtCCellColumns, XtRInt, sizeof(int),
70	Offset(cell_cols), XtRImmediate, (XtPointer) 0 },
71    { XtNcellRows, XtCCellRows, XtRInt, sizeof(int),
72	Offset(cell_rows), XtRImmediate, (XtPointer) 0 },
73    { XtNcellWidth, XtCCellWidth, XtRInt, sizeof(int),
74	Offset(cell_width), XtRImmediate, (XtPointer) 0 },
75    { XtNcellHeight, XtCCellHeight, XtRInt, sizeof(int),
76	Offset(cell_height), XtRImmediate, (XtPointer) 0 },
77    { XtNstartChar, XtCStartChar, XtRLong, sizeof(long),
78	Offset(start_char), XtRImmediate, (XtPointer) 0xffffffff },
79#ifndef XRENDER
80    { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
81	Offset(foreground_pixel), XtRString, (XtPointer) XtDefaultForeground },
82#endif
83    { XtNcenterChars, XtCCenterChars, XtRBoolean, sizeof(Boolean),
84	Offset(center_chars), XtRImmediate, (XtPointer) FALSE },
85    { XtNboxChars, XtCBoxChars, XtRBoolean, sizeof(Boolean),
86	Offset(box_chars), XtRImmediate, (XtPointer) FALSE },
87    { XtNboxColor, XtCForeground, XtRPixel, sizeof(Pixel),
88	Offset(box_pixel), XtRString, (XtPointer) XtDefaultForeground },
89    { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
90	Offset(callbacks), XtRCallback, (XtPointer) NULL },
91    { XtNinternalPad, XtCInternalPad, XtRInt, sizeof(int),
92	Offset(internal_pad), XtRImmediate, (XtPointer) 4 },
93    { XtNgridWidth, XtCGridWidth, XtRInt, sizeof(int),
94	Offset(grid_width), XtRImmediate, (XtPointer) 1 },
95#ifdef XRENDER
96    {XtNforeground, XtCForeground, XtRXftColor, sizeof(XftColor),
97        Offset(fg_color), XtRString, XtDefaultForeground},
98    {XtNface, XtCFace, XtRXftFont, sizeof (XftFont *),
99	Offset (text_face), XtRString, NULL},
100#endif
101};
102
103#undef Offset
104
105static char defaultTranslations[] =
106  "<ButtonPress>:  notify()";
107
108static XtActionsRec actions_list[] = {
109    { "notify",		Notify },
110};
111
112FontGridClassRec fontgridClassRec = {
113  { /* core fields */
114    /* superclass               */      (WidgetClass) &simpleClassRec,
115    /* class_name               */      "FontGrid",
116    /* widget_size              */      sizeof(FontGridRec),
117    /* class_initialize         */      ClassInitialize,
118    /* class_part_initialize    */      NULL,
119    /* class_inited             */      FALSE,
120    /* initialize               */      Initialize,
121    /* initialize_hook          */      NULL,
122    /* realize                  */      Realize,
123    /* actions                  */      actions_list,
124    /* num_actions              */      XtNumber(actions_list),
125    /* resources                */      resources,
126    /* num_resources            */      XtNumber(resources),
127    /* xrm_class                */      NULLQUARK,
128    /* compress_motion          */      TRUE,
129    /* compress_exposure        */      TRUE,
130    /* compress_enterleave      */      TRUE,
131    /* visible_interest         */      FALSE,
132    /* destroy                  */      Destroy,
133    /* resize                   */      Resize,
134    /* expose                   */      Redisplay,
135    /* set_values               */      SetValues,
136    /* set_values_hook          */      NULL,
137    /* set_values_almost        */      XtInheritSetValuesAlmost,
138    /* get_values_hook          */      NULL,
139    /* accept_focus             */      NULL,
140    /* version                  */      XtVersion,
141    /* callback_private         */      NULL,
142    /* tm_table                 */      defaultTranslations,
143    /* query_geometry           */      XtInheritQueryGeometry,
144    /* display_accelerator      */      XtInheritDisplayAccelerator,
145    /* extension                */      NULL
146  },
147  { /* simple fields */
148    /* change_sensitive		*/	XtInheritChangeSensitive
149  }
150};
151
152WidgetClass fontgridWidgetClass = (WidgetClass) &fontgridClassRec;
153
154
155long
156GridFirstChar (Widget w)
157{
158    FontGridWidget	fgw = (FontGridWidget) w;
159    XFontStruct		*fs = fgw->fontgrid.text_font;
160#ifdef XRENDER
161    XftFont		*xft = fgw->fontgrid.text_face;
162    if (xft)
163    {
164	FcChar32    map[FC_CHARSET_MAP_SIZE];
165	FcChar32    next;
166	FcChar32    first;
167	int	    i;
168
169	first = FcCharSetFirstPage (xft->charset, map, &next);
170	for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
171	    if (map[i])
172	    {
173		FcChar32    bits = map[i];
174		first += i * 32;
175		while (!(bits & 0x1))
176		{
177		    bits >>= 1;
178		    first++;
179		}
180		break;
181	    }
182	return first;
183    }
184    else
185#endif
186    if (fs)
187    {
188	return (fs->min_byte1 << 8) | (fs->min_char_or_byte2);
189    }
190    else
191	return 0;
192}
193
194long
195GridLastChar (Widget w)
196{
197    FontGridWidget	fgw = (FontGridWidget) w;
198    XFontStruct		*fs = fgw->fontgrid.text_font;
199#ifdef XRENDER
200    XftFont		*xft = fgw->fontgrid.text_face;
201    if (xft)
202    {
203	FcChar32    this, last, next;
204	FcChar32    map[FC_CHARSET_MAP_SIZE];
205	int	    i;
206	last = FcCharSetFirstPage (xft->charset, map, &next);
207	while ((this = FcCharSetNextPage (xft->charset, map, &next)) != FC_CHARSET_DONE)
208	    last = this;
209	last &= ~0xff;
210	for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
211	    if (map[i])
212	    {
213		FcChar32    bits = map[i];
214		last += i * 32 + 31;
215		while (!(bits & 0x80000000))
216		{
217		    last--;
218		    bits <<= 1;
219		}
220		break;
221	    }
222	return (long) last;
223    }
224    else
225#endif
226    if (fs)
227    {
228	return (fs->max_byte1 << 8) | (fs->max_char_or_byte2);
229    }
230    else
231	return 0;
232}
233
234/*
235 * CI_GET_CHAR_INFO_1D - return the charinfo struct for the indicated 8bit
236 * character.  If the character is in the column and exists, then return the
237 * appropriate metrics (note that fonts with common per-character metrics will
238 * return min_bounds).
239 */
240
241#define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
242			     (((cs)->rbearing|(cs)->lbearing| \
243			       (cs)->ascent|(cs)->descent) == 0))
244
245#define CI_GET_CHAR_INFO_1D(fs,col,cs) \
246{ \
247    cs = NULL; \
248    if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
249	if (fs->per_char == NULL) { \
250	    cs = &fs->min_bounds; \
251	} else { \
252	    cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
253	} \
254	if (CI_NONEXISTCHAR(cs)) \
255	    cs = NULL; \
256    } \
257}
258
259/*
260 * CI_GET_CHAR_INFO_2D - return the charinfo struct for the indicated row and
261 * column.  This is used for fonts that have more than row zero.
262 */
263#define CI_GET_CHAR_INFO_2D(fs,row,col,cs) \
264{ \
265    cs = NULL; \
266    if (row >= fs->min_byte1 && row <= fs->max_byte1 && \
267	col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
268	if (fs->per_char == NULL) { \
269	    cs = &fs->min_bounds; \
270	} else { \
271	    cs = &fs->per_char[((row - fs->min_byte1) * \
272			        (fs->max_char_or_byte2 - \
273				 fs->min_char_or_byte2 + 1)) + \
274			       (col - fs->min_char_or_byte2)]; \
275        } \
276	if (CI_NONEXISTCHAR(cs)) \
277	    cs = NULL; \
278    } \
279}
280
281static Boolean
282GridHasChar (Widget w, long ch)
283{
284    FontGridWidget	fgw = (FontGridWidget) w;
285#ifdef XRENDER
286    XftFont		*xft = fgw->fontgrid.text_face;
287    if (xft)
288    {
289	return FcCharSetHasChar (xft->charset, (FcChar32) ch);
290    }
291    else
292#endif
293    {
294	XFontStruct	*fs = fgw->fontgrid.text_font;
295	XCharStruct	*cs;
296
297	if (!fs)
298	    return False;
299	if (fs->max_byte1 == 0)
300	{
301	    CI_GET_CHAR_INFO_1D (fs, ch, cs);
302	}
303	else
304	{
305	    unsigned int	r = (ch >> 8);
306	    unsigned int	c = (ch & 0xff);
307	    CI_GET_CHAR_INFO_2D (fs, r, c, cs);
308	}
309	return cs != NULL;
310    }
311}
312
313/*
314 * public routines
315 */
316
317void
318GetFontGridCellDimensions(Widget w, long *startp,
319			  int *ncolsp, int *nrowsp)
320{
321    FontGridWidget fgw = (FontGridWidget) w;
322    *startp = fgw->fontgrid.start_char;
323    *ncolsp = fgw->fontgrid.cell_cols;
324    *nrowsp = fgw->fontgrid.cell_rows;
325}
326
327
328void
329GetPrevNextStates(Widget w, Bool *prevvalidp, Bool *nextvalidp,
330		  Bool *prev16validp, Bool *next16validp)
331{
332    FontGridWidget fgw = (FontGridWidget) w;
333    long minn = (long) GridFirstChar (w);
334    long maxn = (long) GridLastChar (w);
335
336    *prev16validp = (fgw->fontgrid.start_char - 0xf00 > minn);
337    *prevvalidp = (fgw->fontgrid.start_char > minn);
338    *nextvalidp = (fgw->fontgrid.start_char +
339		   (fgw->fontgrid.cell_cols * fgw->fontgrid.cell_rows)
340		   < maxn);
341    *next16validp =((fgw->fontgrid.start_char + 0xf00 +
342		    (fgw->fontgrid.cell_cols * fgw->fontgrid.cell_rows))
343		   < maxn);
344}
345
346
347
348/*
349 * private routines and methods
350 */
351
352
353static GC
354get_gc(FontGridWidget fgw, Pixel fore)
355{
356    XtGCMask mask;
357    XGCValues gcv;
358
359    mask = (GCForeground | GCBackground | GCFunction);
360    gcv.foreground = fore;
361    gcv.background = fgw->core.background_pixel;
362    gcv.function = GXcopy;
363    if (fgw->fontgrid.text_font)
364    {
365	mask |= GCFont;
366	gcv.font = fgw->fontgrid.text_font->fid;
367    }
368    gcv.cap_style = CapProjecting;
369    mask |= GCCapStyle;
370    if (fgw->fontgrid.grid_width > 0) {
371	mask |= GCLineWidth;
372	gcv.line_width = ((fgw->fontgrid.grid_width < 2) ? 0 :
373			  fgw->fontgrid.grid_width);
374    }
375    return (XtGetGC ((Widget) fgw, mask, &gcv));
376}
377
378
379#ifdef XRENDER
380static XtConvertArgRec xftColorConvertArgs[] = {
381    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen),
382     sizeof(Screen *)},
383    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.colormap),
384     sizeof(Colormap)}
385};
386
387#define	donestr(type, value, tstr) \
388	{							\
389	    if (toVal->addr != NULL) {				\
390		if (toVal->size < sizeof(type)) {		\
391		    toVal->size = sizeof(type);			\
392		    XtDisplayStringConversionWarning(dpy, 	\
393			(char*) fromVal->addr, tstr);		\
394		    return False;				\
395		}						\
396		*(type*)(toVal->addr) = (value);		\
397	    }							\
398	    else {						\
399		static type static_val;				\
400		static_val = (value);				\
401		toVal->addr = (XPointer)&static_val;		\
402	    }							\
403	    toVal->size = sizeof(type);				\
404	    return True;					\
405	}
406
407static void
408XmuFreeXftColor (XtAppContext app, XrmValuePtr toVal, XtPointer closure,
409		 XrmValuePtr args, Cardinal *num_args)
410{
411    Screen	*screen;
412    Colormap	colormap;
413    XftColor	*color;
414
415    if (*num_args != 2)
416    {
417	XtAppErrorMsg (app,
418		       "freeXftColor", "wrongParameters",
419		       "XtToolkitError",
420		       "Freeing an XftColor requires screen and colormap arguments",
421		       (String *) NULL, (Cardinal *)NULL);
422	return;
423    }
424
425    screen = *((Screen **) args[0].addr);
426    colormap = *((Colormap *) args[1].addr);
427    color = (XftColor *) toVal->addr;
428    XftColorFree (DisplayOfScreen (screen),
429		  DefaultVisual (DisplayOfScreen (screen),
430				 XScreenNumberOfScreen (screen)),
431		  colormap, color);
432}
433
434static Boolean
435XmuCvtStringToXftColor(Display *dpy,
436		       XrmValue *args, Cardinal *num_args,
437		       XrmValue *fromVal, XrmValue *toVal,
438		       XtPointer *converter_data)
439{
440    char	    *spec;
441    XRenderColor    renderColor;
442    XftColor	    xftColor;
443    Screen	    *screen;
444    Colormap	    colormap;
445
446    if (*num_args != 2)
447    {
448	XtAppErrorMsg (XtDisplayToApplicationContext (dpy),
449		       "cvtStringToXftColor", "wrongParameters",
450		       "XtToolkitError",
451		       "String to render color conversion needs screen and colormap arguments",
452		       (String *) NULL, (Cardinal *)NULL);
453	return False;
454    }
455
456    screen = *((Screen **) args[0].addr);
457    colormap = *((Colormap *) args[1].addr);
458
459    spec = (char *) fromVal->addr;
460    if (strcasecmp (spec, XtDefaultForeground) == 0)
461    {
462	renderColor.red = 0;
463	renderColor.green = 0;
464	renderColor.blue = 0;
465	renderColor.alpha = 0xffff;
466    }
467    else if (strcasecmp (spec, XtDefaultBackground) == 0)
468    {
469	renderColor.red = 0xffff;
470	renderColor.green = 0xffff;
471	renderColor.blue = 0xffff;
472	renderColor.alpha = 0xffff;
473    }
474    else if (!XRenderParseColor (dpy, spec, &renderColor))
475	return False;
476    if (!XftColorAllocValue (dpy,
477			     DefaultVisual (dpy,
478					    XScreenNumberOfScreen (screen)),
479			     colormap,
480			     &renderColor,
481			     &xftColor))
482	return False;
483
484    donestr (XftColor, xftColor, XtRXftColor);
485}
486
487static void
488XmuFreeXftFont (XtAppContext app, XrmValuePtr toVal, XtPointer closure,
489		XrmValuePtr args, Cardinal *num_args)
490{
491    Screen  *screen;
492    XftFont *font;
493
494    if (*num_args != 1)
495    {
496	XtAppErrorMsg (app,
497		       "freeXftFont", "wrongParameters",
498		       "XtToolkitError",
499		       "Freeing an XftFont requires screen argument",
500		       (String *) NULL, (Cardinal *)NULL);
501	return;
502    }
503
504    screen = *((Screen **) args[0].addr);
505    font = *((XftFont **) toVal->addr);
506    if (font)
507	XftFontClose (DisplayOfScreen (screen), font);
508}
509
510static Boolean
511XmuCvtStringToXftFont(Display *dpy,
512		      XrmValue *args, Cardinal *num_args,
513		      XrmValue *fromVal, XrmValue *toVal,
514		      XtPointer *converter_data)
515{
516    char    *name;
517    XftFont *font;
518    Screen  *screen;
519
520    if (*num_args != 1)
521    {
522	XtAppErrorMsg (XtDisplayToApplicationContext (dpy),
523		       "cvtStringToXftFont", "wrongParameters",
524		       "XtToolkitError",
525		       "String to XftFont conversion needs screen argument",
526		       (String *) NULL, (Cardinal *)NULL);
527	return False;
528    }
529
530    screen = *((Screen **) args[0].addr);
531    name = (char *) fromVal->addr;
532
533    font = NULL;
534    if (name)
535    {
536	font = XftFontOpenName (dpy,
537				XScreenNumberOfScreen (screen),
538				name);
539	if (!font)
540	    XtDisplayStringConversionWarning(dpy, (char *) fromVal->addr, XtRXftFont);
541    }
542    donestr (XftFont *, font, XtRXftFont);
543}
544
545static XtConvertArgRec xftFontConvertArgs[] = {
546    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen),
547     sizeof(Screen *)},
548};
549
550#endif
551
552static void
553ClassInitialize(void)
554{
555    XtAddConverter (XtRString, XtRLong, XmuCvtStringToLong, NULL, 0);
556#ifdef XRENDER
557    XtSetTypeConverter (XtRString, XtRXftColor,
558			XmuCvtStringToXftColor,
559			xftColorConvertArgs, XtNumber(xftColorConvertArgs),
560			XtCacheByDisplay, XmuFreeXftColor);
561    XtSetTypeConverter (XtRString, XtRXftFont,
562			XmuCvtStringToXftFont,
563			xftFontConvertArgs, XtNumber(xftFontConvertArgs),
564			XtCacheByDisplay, XmuFreeXftFont);
565#endif
566}
567
568
569static void
570Initialize(Widget request, Widget new, ArgList args, Cardinal *num_args)
571{
572    FontGridWidget reqfg = (FontGridWidget) request;
573    FontGridWidget newfg = (FontGridWidget) new;
574    XFontStruct *fs = newfg->fontgrid.text_font;
575#ifdef XRENDER
576    XftFont *xft = newfg->fontgrid.text_face;
577#endif
578    unsigned maxn;
579
580    if (reqfg->fontgrid.cell_cols <= 0)
581      newfg->fontgrid.cell_cols = 16;
582
583    if (reqfg->fontgrid.cell_rows <= 0) {
584#ifdef XRENDER
585	if (xft)
586	    newfg->fontgrid.cell_rows = 16;
587	else
588#endif
589	if (fs && fs->max_byte1 == 0) {
590	    newfg->fontgrid.cell_rows = (fs->max_char_or_byte2 /
591					 newfg->fontgrid.cell_cols) + 1;
592	    if (newfg->fontgrid.cell_rows > 16)
593	      newfg->fontgrid.cell_rows = 16;
594	} else
595	  newfg->fontgrid.cell_rows = 16;
596    }
597
598    if (reqfg->fontgrid.cell_width <= 0)
599      newfg->fontgrid.cell_width = DefaultCellWidth (newfg);
600    if (reqfg->fontgrid.cell_height <= 0)
601      newfg->fontgrid.cell_height = DefaultCellHeight (newfg);
602
603    /* give a nice size that fits one screen full */
604    if (newfg->core.width == 0)
605      newfg->core.width = (newfg->fontgrid.cell_width *
606			   newfg->fontgrid.cell_cols +
607			   newfg->fontgrid.grid_width *
608			   (newfg->fontgrid.cell_cols + 1));
609
610    if (newfg->core.height == 0)
611      newfg->core.height = (newfg->fontgrid.cell_height *
612			    newfg->fontgrid.cell_rows +
613			    newfg->fontgrid.grid_width *
614			    (newfg->fontgrid.cell_rows + 1));
615
616    /*
617     * select the first character
618     */
619
620    if (newfg->fontgrid.start_char == 0xffffffff)
621	newfg->fontgrid.start_char = GridFirstChar(new) & ~0xff;
622    maxn = GridLastChar (new);
623    if (newfg->fontgrid.start_char > maxn)
624	newfg->fontgrid.start_char = (maxn + 1 -
625				      (newfg->fontgrid.cell_cols *
626				       newfg->fontgrid.cell_rows));
627}
628
629static void
630Realize(Widget gw, Mask *valueMask, XSetWindowAttributes *attributes)
631{
632    FontGridWidget fgw = (FontGridWidget) gw;
633    FontGridPart *p = &fgw->fontgrid;
634
635    p->text_gc = get_gc (fgw, GridForeground (fgw));
636    p->box_gc = get_gc (fgw, p->box_pixel);
637    Resize (gw);
638
639    (*(XtSuperclass(gw)->core_class.realize)) (gw, valueMask, attributes);
640#ifdef XRENDER
641    p->draw = XftDrawCreate (XtDisplay (gw), XtWindow (gw),
642			     DefaultVisual (XtDisplay (gw),
643					    DefaultScreen(XtDisplay (gw))),
644			     fgw->core.colormap);
645#endif
646    return;
647}
648
649
650
651static void
652Destroy(Widget gw)
653{
654    FontGridWidget fgw = (FontGridWidget) gw;
655
656    XtReleaseGC (gw, fgw->fontgrid.text_gc);
657    XtReleaseGC (gw, fgw->fontgrid.box_gc);
658}
659
660
661static void
662Resize(Widget gw)
663{
664    FontGridWidget fgw = (FontGridWidget) gw;
665
666    /* recompute in case we've changed size */
667    fgw->fontgrid.cell_width = CellWidth (fgw);
668    if (fgw->fontgrid.cell_width <= 0)
669	fgw->fontgrid.cell_width = 1;
670    fgw->fontgrid.cell_height = CellHeight (fgw);
671    if (fgw->fontgrid.cell_height <= 0)
672	fgw->fontgrid.cell_height = 1;
673    fgw->fontgrid.xoff = (fgw->fontgrid.cell_width -
674			    DefaultCellWidth (fgw)) / 2;
675    fgw->fontgrid.yoff = (fgw->fontgrid.cell_height -
676			    DefaultCellHeight (fgw)) / 2;
677
678}
679
680
681/* ARGSUSED */
682static void
683Redisplay(Widget gw, XEvent *event, Region region)
684{
685    FontGridWidget fgw = (FontGridWidget) gw;
686    XRectangle rect;			/* bounding rect for region */
687    int left, right, top, bottom;	/* which cells were damaged */
688    int cw, ch;				/* cell size */
689
690#ifdef XRENDER
691    if (!fgw->fontgrid.text_face)
692#endif
693    if (!fgw->fontgrid.text_font) {
694	Bell (gw, XkbBI_BadValue);
695	return;
696    }
697
698    /*
699     * compute the left, right, top, and bottom cells that were damaged
700     */
701    XClipBox (region, &rect);
702    cw = fgw->fontgrid.cell_width + fgw->fontgrid.grid_width;
703    ch = fgw->fontgrid.cell_height + fgw->fontgrid.grid_width;
704    if ((left = (((int) rect.x) / cw)) < 0) left = 0;
705    right = (((int) (rect.x + rect.width - 1)) / cw);
706    if ((top = (((int) rect.y) / ch)) < 0) top = 0;
707    bottom = (((int) (rect.y + rect.height - 1)) / ch);
708
709    paint_grid (fgw, left, top, right - left + 1, bottom - top + 1);
710}
711
712
713static void
714paint_grid(FontGridWidget fgw, 		/* widget in which to draw */
715	   int col, int row, 		/* where to start */
716	   int ncols, int nrows)	/* number of cells */
717{
718    FontGridPart *p = &fgw->fontgrid;
719    int i, j;
720    Display *dpy = XtDisplay(fgw);
721    Window wind = XtWindow(fgw);
722    int cw = p->cell_width + p->grid_width;
723    int ch = p->cell_height + p->grid_width;
724    int tcols = p->cell_cols;
725    int trows = p->cell_rows;
726    int x1, y1, x2, y2, x, y;
727    unsigned maxn = GridLastChar ((Widget) fgw);
728    unsigned n, prevn;
729    int startx;
730
731    if (col + ncols >= tcols) {
732	ncols = tcols - col;
733	if (ncols < 1) return;
734    }
735
736    if (row + nrows >= trows) {
737	nrows = trows - row;
738	if (nrows < 1) return;
739    }
740
741    /*
742     * paint the grid lines for the indicated rows
743     */
744    if (p->grid_width > 0) {
745	int half_grid_width = p->grid_width >> 1;
746	x1 = col * cw + half_grid_width;
747	y1 = row * ch + half_grid_width;
748	x2 = x1 + ncols * cw;
749	y2 = y1 + nrows * ch;
750	for (i = 0, x = x1; i <= ncols; i++, x += cw) {
751	    XDrawLine (dpy, wind, p->box_gc, x, y1, x, y2);
752	}
753	for (i = 0, y = y1; i <= nrows; i++, y += ch) {
754	    XDrawLine (dpy, wind, p->box_gc, x1, y, x2, y);
755	}
756    }
757    /*
758     * Draw a character in every box; treat all fonts as if they were 16bit
759     * fonts.  Store the high eight bits in byte1 and the low eight bits in
760     * byte2.
761     */
762    prevn = p->start_char + col + row * tcols;
763    startx = col * cw + p->internal_pad + p->grid_width;
764    for (j = 0,
765	 y = row * ch + p->internal_pad + p->grid_width + GridFontAscent (fgw);
766	 j < nrows; j++, y += ch) {
767	n = prevn;
768	for (i = 0, x = startx; i < ncols; i++, x += cw) {
769	    int xoff = p->xoff, yoff = p->yoff;
770	    if (n > maxn) goto done;	/* no break out of nested */
771
772#ifdef XRENDER
773	    if (fgw->fontgrid.text_face)
774	    {
775		XftFont *xft = p->text_face;
776		FcChar32    c = n;
777		XGlyphInfo  extents;
778		XftTextExtents32 (dpy, xft, &c, 1, &extents);
779		if (p->center_chars)
780		{
781		    xoff = (p->cell_width - extents.width) / 2 - extents.x;
782		    yoff = (p->cell_height - extents.height) / 2 - extents.y;
783		}
784		if (extents.width && extents.height)
785		{
786		    XClearArea (dpy, wind, x + xoff - extents.x,
787				y + yoff - extents.y,
788				extents.width, extents.height, False);
789		    if (p->box_chars)
790			XDrawRectangle (dpy, wind, p->box_gc,
791					x + xoff - extents.x,
792					y + yoff - extents.y,
793					extents.width - 1,
794					extents.height - 1);
795		}
796		XftDrawString32 (p->draw, &p->fg_color, xft,
797				 x + xoff, y + yoff, &c, 1);
798	    }
799	    else
800#endif
801	    {
802	    XChar2b thechar;
803
804	    thechar.byte1 = (n >> 8);	/* high eight bits */
805	    thechar.byte2 = (n & 255);	/* low eight bits */
806	    if (p->box_chars || p->center_chars) {
807		XCharStruct metrics;
808		int direction, fontascent, fontdescent;
809
810		XTextExtents16 (p->text_font, &thechar, 1, &direction,
811				&fontascent, &fontdescent, &metrics);
812
813		if (p->center_chars) {
814		    /*
815		     * We want to move the origin by enough to center the ink
816		     * within the cell.  The left edge will then be at
817		     * (cell_width - (rbearing - lbearing)) / 2; so we subtract
818		     * the lbearing to find the origin.  Ditto for vertical.
819		     */
820		    xoff = (((p->cell_width -
821			      (metrics.rbearing - metrics.lbearing)) / 2) -
822			    p->internal_pad - metrics.lbearing);
823		    yoff = (((p->cell_height -
824			      (metrics.descent + metrics.ascent)) / 2) -
825			    p->internal_pad -
826			    p->text_font->ascent + metrics.ascent);
827		}
828		if (p->box_chars) {
829		    XDrawRectangle (dpy, wind, p->box_gc,
830				    x + xoff, y + yoff - p->text_font->ascent,
831				    metrics.width - 1,
832				    fontascent + fontdescent - 1);
833		}
834	    }
835	    XDrawString16 (dpy, wind, p->text_gc, x + xoff, y + yoff,
836			   &thechar, 1);
837	    }
838	    n++;
839	}
840	prevn += tcols;
841    }
842
843  done:
844    /*
845     * paint the grid lines for the indicated rows
846     */
847    if (p->grid_width > 0) {
848	int half_grid_width = p->grid_width >> 1;
849	x1 = col * cw + half_grid_width;
850	y1 = row * ch + half_grid_width;
851	x2 = x1 + ncols * cw;
852	y2 = y1 + nrows * ch;
853	for (i = 0, x = x1; i <= ncols; i++, x += cw) {
854	    XDrawLine (dpy, wind, p->box_gc, x, y1, x, y2);
855	}
856	for (i = 0, y = y1; i <= nrows; i++, y += ch) {
857	    XDrawLine (dpy, wind, p->box_gc, x1, y, x2, y);
858	}
859    }
860
861
862    return;
863}
864
865static Boolean
866PageBlank (Widget w, long first, long last)
867{
868    while (first <= last)
869    {
870	if (GridHasChar (w, first))
871	    return False;
872	first++;
873    }
874    return True;
875}
876
877/*ARGSUSED*/
878static Boolean
879SetValues(Widget current, Widget request, Widget new,
880	  ArgList args, Cardinal *num_args)
881{
882    FontGridWidget curfg = (FontGridWidget) current;
883    FontGridWidget newfg = (FontGridWidget) new;
884    Boolean redisplay = FALSE;
885
886    if (curfg->fontgrid.text_font != newfg->fontgrid.text_font ||
887	curfg->fontgrid.internal_pad != newfg->fontgrid.internal_pad) {
888	newfg->fontgrid.cell_width = DefaultCellWidth (newfg);
889	newfg->fontgrid.cell_height = DefaultCellHeight (newfg);
890	redisplay = TRUE;
891    }
892
893    if (GridForeground(curfg) != GridForeground (newfg)) {
894	XtReleaseGC (new, curfg->fontgrid.text_gc);
895	newfg->fontgrid.text_gc = get_gc (newfg, GridForeground (newfg));
896	redisplay = TRUE;
897    }
898
899    if (curfg->fontgrid.box_pixel != newfg->fontgrid.box_pixel) {
900	XtReleaseGC (new, curfg->fontgrid.text_gc);
901	newfg->fontgrid.box_gc = get_gc (newfg, newfg->fontgrid.box_pixel);
902	redisplay = TRUE;
903    }
904
905    if (curfg->fontgrid.center_chars != newfg->fontgrid.center_chars ||
906	curfg->fontgrid.box_chars != newfg->fontgrid.box_chars)
907      redisplay = TRUE;
908
909    if (curfg->fontgrid.start_char != newfg->fontgrid.start_char) {
910	long maxn = GridLastChar (new);
911	long page = newfg->fontgrid.cell_cols * newfg->fontgrid.cell_rows;
912	long dir = page;
913	long start = newfg->fontgrid.start_char;
914
915	if (start < curfg->fontgrid.start_char)
916	    dir = -page;
917
918	if (start < 0)
919	    start = 0;
920	if (start > maxn)
921	  start = (maxn / page) * page;
922
923	while (PageBlank (new, start, start + page - 1))
924	{
925	    long    next = start + dir;
926
927	    if (next < 0 || maxn < next)
928		break;
929	    start = next;
930	}
931
932	newfg->fontgrid.start_char = start;
933	redisplay = (curfg->fontgrid.start_char != newfg->fontgrid.start_char);
934    }
935
936    return redisplay;
937}
938
939
940/* ARGSUSED */
941static void
942Notify(Widget gw, XEvent *event, String *params, Cardinal *nparams)
943{
944    FontGridWidget fgw = (FontGridWidget) gw;
945    int x, y;				/* where the event happened */
946    FontGridCharRec rec;		/* callback data */
947
948    /*
949     * only allow events with (x,y)
950     */
951    switch (event->type) {
952      case KeyPress:
953      case KeyRelease:
954	x = event->xkey.x;
955	y = event->xkey.y;
956	break;
957      case ButtonPress:
958      case ButtonRelease:
959	x = event->xbutton.x;
960	y = event->xbutton.y;
961	break;
962      case MotionNotify:
963	x = event->xmotion.x;
964	y = event->xmotion.y;
965	break;
966      default:
967	Bell (gw, XkbBI_Ignore);
968	return;
969    }
970
971    /*
972     * compute the callback data
973     */
974    {
975	int cw = fgw->fontgrid.cell_width + fgw->fontgrid.grid_width;
976	int ch = fgw->fontgrid.cell_height + fgw->fontgrid.grid_width;
977	unsigned n;
978
979	if (x > (fgw->fontgrid.cell_cols * cw)) {
980	    Bell (gw, XkbBI_InvalidLocation);
981	    return;
982	}
983
984	n= (fgw->fontgrid.start_char +
985	    ((y / ch) * fgw->fontgrid.cell_cols) + (x / cw));
986
987	rec.thefont = fgw->fontgrid.text_font;
988#ifdef XRENDER
989	rec.theface = fgw->fontgrid.text_face;
990#endif
991	rec.thechar = n;
992    }
993
994    XtCallCallbacks (gw, XtNcallback, (XtPointer) &rec);
995}
996
997