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