grid.c revision 31e5d586
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    /* extension                */      NULL
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
173	first = FcCharSetFirstPage (xft->charset, map, &next);
174	for (int 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
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 (int 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) \
250do { \
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} while (0)
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) \
268do { \
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} while (0)
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	do {							\
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	} while (0)
410
411static void
412XmuFreeXftColor (XtAppContext app, XrmValuePtr toVal,
413		 XtPointer closure _X_UNUSED,
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 _X_UNUSED)
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 _X_UNUSED,
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 _X_UNUSED)
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	    XtDisplayStringConversionWarning(dpy, (char *) fromVal->addr, XtRXftFont);
546    }
547    donestr (XftFont *, font, XtRXftFont);
548}
549
550static XtConvertArgRec xftFontConvertArgs[] = {
551    {XtWidgetBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.screen),
552     sizeof(Screen *)},
553};
554
555#endif
556
557static void
558ClassInitialize(void)
559{
560    XtAddConverter (XtRString, XtRLong, XmuCvtStringToLong, NULL, 0);
561#ifdef XRENDER
562    XtSetTypeConverter (XtRString, XtRXftColor,
563			XmuCvtStringToXftColor,
564			xftColorConvertArgs, XtNumber(xftColorConvertArgs),
565			XtCacheByDisplay, XmuFreeXftColor);
566    XtSetTypeConverter (XtRString, XtRXftFont,
567			XmuCvtStringToXftFont,
568			xftFontConvertArgs, XtNumber(xftFontConvertArgs),
569			XtCacheByDisplay, XmuFreeXftFont);
570#endif
571}
572
573
574static void
575Initialize(Widget request, Widget new,
576           ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
577{
578    FontGridWidget reqfg = (FontGridWidget) request;
579    FontGridWidget newfg = (FontGridWidget) new;
580    XFontStruct *fs = newfg->fontgrid.text_font;
581#ifdef XRENDER
582    XftFont *xft = newfg->fontgrid.text_face;
583#endif
584    unsigned maxn;
585
586    if (reqfg->fontgrid.cell_cols <= 0)
587      newfg->fontgrid.cell_cols = 16;
588
589    if (reqfg->fontgrid.cell_rows <= 0) {
590#ifdef XRENDER
591	if (xft)
592	    newfg->fontgrid.cell_rows = 16;
593	else
594#endif
595	if (fs && fs->max_byte1 == 0) {
596	    newfg->fontgrid.cell_rows = (fs->max_char_or_byte2 /
597					 newfg->fontgrid.cell_cols) + 1;
598	    if (newfg->fontgrid.cell_rows > 16)
599	      newfg->fontgrid.cell_rows = 16;
600	} else
601	  newfg->fontgrid.cell_rows = 16;
602    }
603
604    if (reqfg->fontgrid.cell_width <= 0)
605      newfg->fontgrid.cell_width = DefaultCellWidth (newfg);
606    if (reqfg->fontgrid.cell_height <= 0)
607      newfg->fontgrid.cell_height = DefaultCellHeight (newfg);
608
609    /* give a nice size that fits one screen full */
610    if (newfg->core.width == 0)
611      newfg->core.width = (newfg->fontgrid.cell_width *
612			   newfg->fontgrid.cell_cols +
613			   newfg->fontgrid.grid_width *
614			   (newfg->fontgrid.cell_cols + 1));
615
616    if (newfg->core.height == 0)
617      newfg->core.height = (newfg->fontgrid.cell_height *
618			    newfg->fontgrid.cell_rows +
619			    newfg->fontgrid.grid_width *
620			    (newfg->fontgrid.cell_rows + 1));
621
622    /*
623     * select the first character
624     */
625
626    if (newfg->fontgrid.start_char == 0xffffffff)
627	newfg->fontgrid.start_char = GridFirstChar(new) & ~0xff;
628    maxn = GridLastChar (new);
629    if (newfg->fontgrid.start_char > maxn)
630	newfg->fontgrid.start_char = (maxn + 1 -
631				      (newfg->fontgrid.cell_cols *
632				       newfg->fontgrid.cell_rows));
633}
634
635static void
636Realize(Widget gw, Mask *valueMask, XSetWindowAttributes *attributes)
637{
638    FontGridWidget fgw = (FontGridWidget) gw;
639    FontGridPart *p = &fgw->fontgrid;
640
641    p->text_gc = get_gc (fgw, GridForeground (fgw));
642    p->box_gc = get_gc (fgw, p->box_pixel);
643    Resize (gw);
644
645    (*(XtSuperclass(gw)->core_class.realize)) (gw, valueMask, attributes);
646#ifdef XRENDER
647    p->draw = XftDrawCreate (XtDisplay (gw), XtWindow (gw),
648			     DefaultVisual (XtDisplay (gw),
649					    DefaultScreen(XtDisplay (gw))),
650			     fgw->core.colormap);
651#endif
652    return;
653}
654
655
656
657static void
658Destroy(Widget gw)
659{
660    FontGridWidget fgw = (FontGridWidget) gw;
661
662    XtReleaseGC (gw, fgw->fontgrid.text_gc);
663    XtReleaseGC (gw, fgw->fontgrid.box_gc);
664}
665
666
667static void
668Resize(Widget gw)
669{
670    FontGridWidget fgw = (FontGridWidget) gw;
671
672    /* recompute in case we've changed size */
673    fgw->fontgrid.cell_width = CellWidth (fgw);
674    if (fgw->fontgrid.cell_width <= 0)
675	fgw->fontgrid.cell_width = 1;
676    fgw->fontgrid.cell_height = CellHeight (fgw);
677    if (fgw->fontgrid.cell_height <= 0)
678	fgw->fontgrid.cell_height = 1;
679    fgw->fontgrid.xoff = (fgw->fontgrid.cell_width -
680			    DefaultCellWidth (fgw)) / 2;
681    fgw->fontgrid.yoff = (fgw->fontgrid.cell_height -
682			    DefaultCellHeight (fgw)) / 2;
683
684}
685
686
687/* ARGSUSED */
688static void
689Redisplay(Widget gw, XEvent *event _X_UNUSED, Region region)
690{
691    FontGridWidget fgw = (FontGridWidget) gw;
692    XRectangle rect;			/* bounding rect for region */
693    int left, right, top, bottom;	/* which cells were damaged */
694    int cw, ch;				/* cell size */
695
696#ifdef XRENDER
697    if (!fgw->fontgrid.text_face)
698#endif
699    if (!fgw->fontgrid.text_font) {
700	Bell (gw, XkbBI_BadValue);
701	return;
702    }
703
704    /*
705     * compute the left, right, top, and bottom cells that were damaged
706     */
707    XClipBox (region, &rect);
708    cw = fgw->fontgrid.cell_width + fgw->fontgrid.grid_width;
709    ch = fgw->fontgrid.cell_height + fgw->fontgrid.grid_width;
710    if ((left = (((int) rect.x) / cw)) < 0) left = 0;
711    right = (((int) (rect.x + rect.width - 1)) / cw);
712    if ((top = (((int) rect.y) / ch)) < 0) top = 0;
713    bottom = (((int) (rect.y + rect.height - 1)) / ch);
714
715    paint_grid (fgw, left, top, right - left + 1, bottom - top + 1);
716}
717
718
719static void
720paint_grid(FontGridWidget fgw, 		/* widget in which to draw */
721	   int col, int row, 		/* where to start */
722	   int ncols, int nrows)	/* number of cells */
723{
724    FontGridPart *p = &fgw->fontgrid;
725    int i, j;
726    Display *dpy = XtDisplay(fgw);
727    Window wind = XtWindow(fgw);
728    int cw = p->cell_width + p->grid_width;
729    int ch = p->cell_height + p->grid_width;
730    int tcols = p->cell_cols;
731    int trows = p->cell_rows;
732    int x1, y1, x2, y2, x, y;
733    unsigned maxn = GridLastChar ((Widget) fgw);
734    unsigned n, prevn;
735    int startx;
736
737    if (col + ncols >= tcols) {
738	ncols = tcols - col;
739	if (ncols < 1) return;
740    }
741
742    if (row + nrows >= trows) {
743	nrows = trows - row;
744	if (nrows < 1) return;
745    }
746
747    /*
748     * paint the grid lines for the indicated rows
749     */
750    if (p->grid_width > 0) {
751	int half_grid_width = p->grid_width >> 1;
752	x1 = col * cw + half_grid_width;
753	y1 = row * ch + half_grid_width;
754	x2 = x1 + ncols * cw;
755	y2 = y1 + nrows * ch;
756	for (i = 0, x = x1; i <= ncols; i++, x += cw) {
757	    XDrawLine (dpy, wind, p->box_gc, x, y1, x, y2);
758	}
759	for (i = 0, y = y1; i <= nrows; i++, y += ch) {
760	    XDrawLine (dpy, wind, p->box_gc, x1, y, x2, y);
761	}
762    }
763    /*
764     * Draw a character in every box; treat all fonts as if they were 16bit
765     * fonts.  Store the high eight bits in byte1 and the low eight bits in
766     * byte2.
767     */
768    prevn = p->start_char + col + row * tcols;
769    startx = col * cw + p->internal_pad + p->grid_width;
770    for (j = 0,
771	 y = row * ch + p->internal_pad + p->grid_width + GridFontAscent (fgw);
772	 j < nrows; j++, y += ch) {
773	n = prevn;
774	for (i = 0, x = startx; i < ncols; i++, x += cw) {
775	    int xoff = p->xoff, yoff = p->yoff;
776	    if (n > maxn) goto done;	/* no break out of nested */
777
778#ifdef XRENDER
779	    if (fgw->fontgrid.text_face)
780	    {
781		XftFont *xft = p->text_face;
782		FcChar32    c = n;
783		XGlyphInfo  extents;
784		XftTextExtents32 (dpy, xft, &c, 1, &extents);
785		if (p->center_chars)
786		{
787		    xoff = (p->cell_width - extents.width) / 2 - extents.x;
788		    yoff = (p->cell_height - extents.height) / 2 - extents.y;
789		}
790		if (extents.width && extents.height)
791		{
792		    XClearArea (dpy, wind, x + xoff - extents.x,
793				y + yoff - extents.y,
794				extents.width, extents.height, False);
795		    if (p->box_chars)
796			XDrawRectangle (dpy, wind, p->box_gc,
797					x + xoff - extents.x,
798					y + yoff - extents.y,
799					extents.width - 1,
800					extents.height - 1);
801		}
802		XftDrawString32 (p->draw, &p->fg_color, xft,
803				 x + xoff, y + yoff, &c, 1);
804	    }
805	    else
806#endif
807	    {
808	    XChar2b thechar;
809
810	    thechar.byte1 = (n >> 8);	/* high eight bits */
811	    thechar.byte2 = (n & 255);	/* low eight bits */
812	    if (p->box_chars || p->center_chars) {
813		XCharStruct metrics;
814		int direction, fontascent, fontdescent;
815
816		XTextExtents16 (p->text_font, &thechar, 1, &direction,
817				&fontascent, &fontdescent, &metrics);
818
819		if (p->center_chars) {
820		    /*
821		     * We want to move the origin by enough to center the ink
822		     * within the cell.  The left edge will then be at
823		     * (cell_width - (rbearing - lbearing)) / 2; so we subtract
824		     * the lbearing to find the origin.  Ditto for vertical.
825		     */
826		    xoff = (((p->cell_width -
827			      (metrics.rbearing - metrics.lbearing)) / 2) -
828			    p->internal_pad - metrics.lbearing);
829		    yoff = (((p->cell_height -
830			      (metrics.descent + metrics.ascent)) / 2) -
831			    p->internal_pad -
832			    p->text_font->ascent + metrics.ascent);
833		}
834		if (p->box_chars) {
835		    XDrawRectangle (dpy, wind, p->box_gc,
836				    x + xoff, y + yoff - p->text_font->ascent,
837				    metrics.width - 1,
838				    fontascent + fontdescent - 1);
839		}
840	    }
841	    XDrawString16 (dpy, wind, p->text_gc, x + xoff, y + yoff,
842			   &thechar, 1);
843	    }
844	    n++;
845	}
846	prevn += tcols;
847    }
848
849  done:
850    /*
851     * paint the grid lines for the indicated rows
852     */
853    if (p->grid_width > 0) {
854	int half_grid_width = p->grid_width >> 1;
855	x1 = col * cw + half_grid_width;
856	y1 = row * ch + half_grid_width;
857	x2 = x1 + ncols * cw;
858	y2 = y1 + nrows * ch;
859	for (i = 0, x = x1; i <= ncols; i++, x += cw) {
860	    XDrawLine (dpy, wind, p->box_gc, x, y1, x, y2);
861	}
862	for (i = 0, y = y1; i <= nrows; i++, y += ch) {
863	    XDrawLine (dpy, wind, p->box_gc, x1, y, x2, y);
864	}
865    }
866
867
868    return;
869}
870
871static Boolean
872PageBlank (Widget w, long first, long last)
873{
874    while (first <= last)
875    {
876	if (GridHasChar (w, first))
877	    return False;
878	first++;
879    }
880    return True;
881}
882
883/*ARGSUSED*/
884static Boolean
885SetValues(Widget current, Widget request _X_UNUSED, Widget new,
886	  ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
887{
888    FontGridWidget curfg = (FontGridWidget) current;
889    FontGridWidget newfg = (FontGridWidget) new;
890    Boolean redisplay = FALSE;
891
892    if (curfg->fontgrid.text_font != newfg->fontgrid.text_font ||
893	curfg->fontgrid.internal_pad != newfg->fontgrid.internal_pad) {
894	newfg->fontgrid.cell_width = DefaultCellWidth (newfg);
895	newfg->fontgrid.cell_height = DefaultCellHeight (newfg);
896	redisplay = TRUE;
897    }
898
899    if (GridForeground(curfg) != GridForeground (newfg)) {
900	XtReleaseGC (new, curfg->fontgrid.text_gc);
901	newfg->fontgrid.text_gc = get_gc (newfg, GridForeground (newfg));
902	redisplay = TRUE;
903    }
904
905    if (curfg->fontgrid.box_pixel != newfg->fontgrid.box_pixel) {
906	XtReleaseGC (new, curfg->fontgrid.text_gc);
907	newfg->fontgrid.box_gc = get_gc (newfg, newfg->fontgrid.box_pixel);
908	redisplay = TRUE;
909    }
910
911    if (curfg->fontgrid.center_chars != newfg->fontgrid.center_chars ||
912	curfg->fontgrid.box_chars != newfg->fontgrid.box_chars)
913      redisplay = TRUE;
914
915    if (curfg->fontgrid.start_char != newfg->fontgrid.start_char) {
916	long maxn = GridLastChar (new);
917	long page = newfg->fontgrid.cell_cols * newfg->fontgrid.cell_rows;
918	long dir = page;
919	long start = newfg->fontgrid.start_char;
920
921	if (start < curfg->fontgrid.start_char)
922	    dir = -page;
923
924	if (start < 0)
925	    start = 0;
926	if (start > maxn)
927	  start = (maxn / page) * page;
928
929	while (PageBlank (new, start, start + page - 1))
930	{
931	    long    next = start + dir;
932
933	    if (next < 0 || maxn < next)
934		break;
935	    start = next;
936	}
937
938	newfg->fontgrid.start_char = start;
939	redisplay = (curfg->fontgrid.start_char != newfg->fontgrid.start_char);
940    }
941
942    return redisplay;
943}
944
945
946/* ARGSUSED */
947static void
948Notify(Widget gw, XEvent *event,
949       String *params _X_UNUSED, Cardinal *nparams _X_UNUSED)
950{
951    FontGridWidget fgw = (FontGridWidget) gw;
952    int x, y;				/* where the event happened */
953    FontGridCharRec rec;		/* callback data */
954
955    /*
956     * only allow events with (x,y)
957     */
958    switch (event->type) {
959      case KeyPress:
960      case KeyRelease:
961	x = event->xkey.x;
962	y = event->xkey.y;
963	break;
964      case ButtonPress:
965      case ButtonRelease:
966	x = event->xbutton.x;
967	y = event->xbutton.y;
968	break;
969      case MotionNotify:
970	x = event->xmotion.x;
971	y = event->xmotion.y;
972	break;
973      default:
974	Bell (gw, XkbBI_Ignore);
975	return;
976    }
977
978    /*
979     * compute the callback data
980     */
981    {
982	int cw = fgw->fontgrid.cell_width + fgw->fontgrid.grid_width;
983	int ch = fgw->fontgrid.cell_height + fgw->fontgrid.grid_width;
984	unsigned n;
985
986	if (x > (fgw->fontgrid.cell_cols * cw)) {
987	    Bell (gw, XkbBI_InvalidLocation);
988	    return;
989	}
990
991	n= (fgw->fontgrid.start_char +
992	    ((y / ch) * fgw->fontgrid.cell_cols) + (x / cw));
993
994	rec.thefont = fgw->fontgrid.text_font;
995#ifdef XRENDER
996	rec.theface = fgw->fontgrid.text_face;
997#endif
998	rec.thechar = n;
999    }
1000
1001    XtCallCallbacks (gw, XtNcallback, (XtPointer) &rec);
1002}
1003
1004