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