diff lib/CandPanel.c @ 0:92745d501b9a

initial import from kinput2-v3.1
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 08 Mar 2010 04:44:30 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CandPanel.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,737 @@
+#ifndef lint
+static char *rcsid = "$Id: CandPanel.c,v 1.11 1994/04/22 04:26:22 ishisone Rel $";
+#endif
+/*
+ * Copyright (c) 1990  Software Research Associates, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Software Research Associates not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.  Software Research
+ * Associates makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * Author:  Makoto Ishisone, Software Research Associates, Inc., Japan
+ */
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include "CandPanelP.h"
+#include "ConvDisp.h"
+
+static XtResource resources[] = {
+#define offset(field) XtOffset(CandidatePanelWidget, cpanel.field)
+    { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
+	offset(foreground), XtRString, XtDefaultForeground },
+    { XtNhorizontalSpacing, XtCSpacing, XtRDimension, sizeof(Dimension),
+	offset(hspace), XtRString, "6" },
+    { XtNverticalSpacing, XtCSpacing, XtRDimension, sizeof(Dimension),
+	offset(vspace), XtRString, "4" },
+    { XtNlist, XtCList, XtRPointer, sizeof(ICString*),
+	offset(list), XtRImmediate, NULL },
+    { XtNnumStrings, XtCNumStrings, XtRInt, sizeof(int),
+	offset(nstrings), XtRImmediate, 0 },
+    { XtNdefaultWidth, XtCDefaultWidth, XtRDimension, sizeof(Dimension),
+	offset(defaultwidth), XtRString, "400" },
+    { XtNcurrentItem, XtCCurrentItem, XtRInt, sizeof(int),
+	offset(current), XtRImmediate, 0 },
+    { XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
+	offset(cursor), XtRImmediate, (XtPointer)None },
+    { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
+	offset(callback), XtRCallback, NULL },
+#undef offset
+};
+
+static void Move(/* Widget, XEvent*, String*, Cardinal* */);
+static void Set(/* Widget, XEvent*, String*, Cardinal* */);
+static void Notify(/* Widget, XEvent*, String*, Cardinal* */);
+
+static XtActionsRec actions[] = {
+    { "move",	Move },
+    { "set",	Set },
+    { "select",	Notify },
+};
+
+static char translations[] =
+    "<Btn1Down>: set() select()\n\
+     <Key>Up:    move(up)\n\
+     <Key>Down:  move(down)\n\
+     <Key>Left:  move(left)\n\
+     <Key>Right: move(right)";
+
+static void Initialize(), Destroy();
+static void Realize();
+static void Redisplay();
+static void Resize();
+static Boolean SetValues();
+static XtGeometryResult QueryGeometry();
+static void InsertChild();
+
+static void GetInvGC();
+static int MaxWidth();
+static void ComputeSize();
+static void Layout();
+static void ToggleHighlight();
+
+static CompositeClassExtensionRec CompositeExtension = {
+    /* next_extension		*/	NULL,
+    /* record_type		*/	NULLQUARK,
+    /* version			*/	XtCompositeExtensionVersion,
+    /* record_size		*/	sizeof(CompositeClassExtensionRec),
+    /* accept_objects		*/	True,
+};
+
+CandidatePanelClassRec candidatePanelClassRec = {
+  { /* core fields */
+    /* superclass		*/	(WidgetClass) &compositeClassRec,
+    /* class_name		*/	"CandidatePanel",
+    /* widget_size		*/	sizeof(CandidatePanelRec),
+    /* class_initialize		*/	NULL,
+    /* class_part_initialize	*/	NULL,
+    /* class_inited		*/	FALSE,
+    /* initialize		*/	Initialize,
+    /* initialize_hook		*/	NULL,
+    /* realize			*/	Realize,
+    /* actions			*/	actions,
+    /* num_actions		*/	XtNumber(actions),
+    /* resources		*/	resources,
+    /* num_resources		*/	XtNumber(resources),
+    /* xrm_class		*/	NULLQUARK,
+    /* compress_motion		*/	TRUE,
+    /* compress_exposure	*/	TRUE,
+    /* compress_enterleave	*/	TRUE,
+    /* visible_interest		*/	FALSE,
+    /* destroy			*/	Destroy,
+    /* resize			*/	Resize,
+    /* expose			*/	Redisplay,
+    /* set_values		*/	SetValues,
+    /* set_values_hook		*/	NULL,
+    /* set_values_almost	*/	XtInheritSetValuesAlmost,
+    /* get_values_hook		*/	NULL,
+    /* accept_focus		*/	NULL,
+    /* version			*/	XtVersion,
+    /* callback_private		*/	NULL,
+    /* tm_table			*/	translations,
+    /* query_geometry		*/	QueryGeometry,
+    /* display_accelerator	*/	XtInheritDisplayAccelerator,
+    /* extension		*/	NULL
+  },
+  { /* composite fields */
+    /* geometry_manager		*/	NULL,
+    /* change_managed		*/	NULL,
+    /* insert_child		*/	InsertChild,
+    /* delete_child		*/	XtInheritDeleteChild,
+    /* extension		*/	(XtPointer)&CompositeExtension,
+  },
+  { /* candidatepanel fields */
+    /* empty			*/	0
+  }
+};
+
+WidgetClass candidatePanelWidgetClass = (WidgetClass)&candidatePanelClassRec;
+
+/* ARGSUSED */
+static void
+Initialize(req, new, args, num_args)
+Widget req;
+Widget new;
+ArgList args;
+Cardinal *num_args;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)new;
+
+    cpw->cpanel.displayobj = NULL;
+    GetInvGC(cpw);
+}
+
+static void
+Destroy(w)
+Widget w;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+
+    if (cpw->cpanel.invgc != NULL) XtReleaseGC(w, cpw->cpanel.invgc);
+}
+
+static void
+Realize(w, mask, value)
+Widget w;
+XtValueMask *mask;
+XSetWindowAttributes *value;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+    CompositeWidgetClass super = (CompositeWidgetClass)XtClass(w)->core_class.superclass;
+    String params[1];
+    Cardinal num_params;
+
+    if (cpw->cpanel.displayobj == NULL) {
+	params[0] = XtClass(w)->core_class.class_name;
+	num_params = 1;
+	XtAppErrorMsg(XtWidgetToApplicationContext(w),
+		      "childError", "number", "WidgetError",
+		      "%s: has no child (ConvDisplayObject)",
+		      params, &num_params);
+    }
+
+    if (cpw->cpanel.cursor != None) {
+	*mask |= CWCursor;
+	value->cursor = cpw->cpanel.cursor;
+    }
+
+    (*super->core_class.realize)(w, mask, value);
+}
+
+/* ARGSUSED */
+static void
+Redisplay(w, ev, region)
+Widget w;
+XEvent *ev;
+Region region;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+    Widget dispobj = cpw->cpanel.displayobj;
+    XExposeEvent *event = (XExposeEvent *)ev;
+    ICString *list = cpw->cpanel.list;
+    int cwidth, cheight, hspace, vspace;
+    int c0, c1, r0, r1;
+    int row, col;
+    int idx;
+    int x, y;
+
+    if (list == NULL || dispobj == NULL) return;
+
+    cwidth = cpw->cpanel.maxwidth;
+    cheight = cpw->cpanel.fontheight;
+    hspace = cpw->cpanel.hspace;
+    vspace = cpw->cpanel.vspace;
+
+    c0 = (event->x + hspace - hspace / 2) / (cwidth + hspace);
+    c1 = (event->x + event->width - 1 - hspace / 2) / (cwidth + hspace) + 1;
+    if (c1 > cpw->cpanel.ncolumns) c1 = cpw->cpanel.ncolumns;
+
+    r0 = (event->y + vspace - vspace / 2) / (cheight + vspace);
+    r1 = (event->y + event->height - 1 - vspace / 2) / (cheight + vspace) + 1;
+    if (r1 > cpw->cpanel.nrows) r1 = cpw->cpanel.nrows;
+
+    for (row = r0; row < r1; row++) {
+	y = (cheight + vspace) * row + vspace / 2;
+	for (col = c0; col < c1; col++) {
+	    x = (cwidth + hspace) * col + hspace / 2;
+	    idx = row * cpw->cpanel.ncolumns + col;
+	    if (idx >= cpw->cpanel.nstrings) return;
+	    if (idx == cpw->cpanel.current) {
+		XClearArea(XtDisplay(w), XtWindow(w), x, y,
+			   (unsigned int)cwidth, (unsigned int)cheight,
+			   False);
+	    }
+	    CDDrawString(dispobj, w, list + idx, 0, -1, x, y);
+	    if (idx == cpw->cpanel.current) {
+		ToggleHighlight(cpw, idx);
+	    }
+	}
+    }
+}
+
+static void
+Resize(w)
+Widget w;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+
+    Layout(cpw, False, False);
+}
+
+/* ARGSUSED */
+static Boolean
+SetValues(cur, req, wid, args, num_args)
+Widget cur;
+Widget req;
+Widget wid;
+ArgList args;
+Cardinal *num_args;
+{
+    CandidatePanelWidget old = (CandidatePanelWidget)cur;
+    CandidatePanelWidget new = (CandidatePanelWidget)wid;
+    Boolean redisplay = False;
+    Boolean listspecified = False;
+    int i;
+
+    if (new->cpanel.displayobj == NULL) return False;
+
+    for (i = 0; i < *num_args; i++) {
+	if (!strcmp(args[i].name, XtNlist)) {
+	    listspecified = True;
+	    break;
+	}
+    }
+
+    if (new->cpanel.foreground != old->cpanel.foreground ||
+	new->core.background_pixel != old->core.background_pixel) {
+	XtVaSetValues(new->cpanel.displayobj,
+		      XtNforeground, new->cpanel.foreground,
+		      XtNbackground, new->core.background_pixel,
+		      NULL);
+	redisplay = True;
+    }
+		      
+    if (listspecified ||
+	new->cpanel.list != old->cpanel.list ||
+	new->cpanel.nstrings != old->cpanel.nstrings) {
+	/* compute maximum pixel width of the list items */
+	new->cpanel.maxwidth = MaxWidth(new);
+    }
+
+    if (listspecified ||
+	new->cpanel.list != old->cpanel.list ||
+	new->cpanel.nstrings != old->cpanel.nstrings ||
+	new->cpanel.hspace != old->cpanel.hspace ||
+	new->cpanel.vspace != old->cpanel.vspace ||
+	new->cpanel.defaultwidth != old->cpanel.defaultwidth) {
+	Layout(new, True, True);
+	redisplay = True;
+    }
+
+    if (new->cpanel.current != old->cpanel.current &&
+	!redisplay && XtIsRealized(wid)) {
+	ToggleHighlight(new, old->cpanel.current);
+	ToggleHighlight(new, new->cpanel.current);
+    }
+
+    if (new->cpanel.cursor != old->cpanel.cursor && XtIsRealized(wid)) {
+	XDefineCursor(XtDisplay(wid), XtWindow(wid), new->cpanel.cursor);
+    }
+
+    return redisplay;
+}
+
+static XtGeometryResult
+QueryGeometry(w, req, ret)
+Widget w;
+XtWidgetGeometry *req;
+XtWidgetGeometry *ret;
+{
+    Dimension width, height;
+    Dimension owidth, oheight;
+
+    ret->request_mode = 0;
+
+    if ((req->request_mode & (CWWidth | CWHeight)) == 0) return XtGeometryYes;
+
+    width = (req->request_mode & CWWidth) ? req->width : w->core.width;
+    height = (req->request_mode & CWHeight) ? req->height : w->core.height;
+
+    owidth = width;
+    oheight = height;
+    ComputeSize((CandidatePanelWidget)w,
+		(req->request_mode & CWWidth) != 0,
+		(req->request_mode & CWHeight) != 0,
+		&width, &height);
+    ret->request_mode = CWWidth | CWHeight;
+    ret->width = width;
+    ret->height = height;
+
+    if (width != owidth || height != oheight) return XtGeometryAlmost;
+
+    return XtGeometryYes;
+}
+
+static void
+InsertChild(w)
+Widget w;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)XtParent(w);
+    CompositeWidgetClass super = (CompositeWidgetClass)XtClass(cpw)->core_class.superclass;
+    String params[1];
+    Cardinal num_params;
+
+    if (!XtIsSubclass(w, convDisplayObjectClass)) {
+	params[0] = XtClass(cpw)->core_class.class_name;
+	num_params = 1;
+	XtAppErrorMsg(XtWidgetToApplicationContext(w),
+		      "childError", "class", "WidgetError",
+		      "%s: child must be subclass of ConvDisplayObject",
+		      params, &num_params);
+    }
+    if (cpw->composite.num_children != 0) {
+	params[0] = XtClass(cpw)->core_class.class_name;
+	num_params = 1;
+	XtAppErrorMsg(XtWidgetToApplicationContext(w),
+		      "childError", "number", "WidgetError",
+		      "%s: can only take one child",
+		      params, &num_params);
+    }
+
+    (*super->composite_class.insert_child)(w);
+
+    cpw->cpanel.displayobj = w;
+    cpw->cpanel.fontheight = CDLineHeight(w, (Position *)NULL);
+
+#ifdef notdef
+    {
+    Pixel fg, bg;
+    XtVaGetValues(w, XtNforeground, &fg, XtNbackground, &bg, NULL);
+    GetInvGC(cpw, fg, bg);
+    }
+#endif
+
+    if (cpw->cpanel.list != NULL) {
+	cpw->cpanel.maxwidth = MaxWidth(cpw);
+	Layout(cpw, cpw->core.width == 0, cpw->core.height == 0);
+    }
+}
+
+static void
+GetInvGC(cpw)
+CandidatePanelWidget cpw;
+{
+    XGCValues values;
+
+    values.function = GXinvert;
+    values.plane_mask = cpw->cpanel.foreground ^ cpw->core.background_pixel;
+    cpw->cpanel.invgc = XtGetGC((Widget)cpw, GCFunction|GCPlaneMask, &values);
+}
+
+/* ARGSUSED */
+static void
+Move(w, ev, args, num_args)
+Widget w;
+XEvent *ev;
+String *args;
+Cardinal *num_args;
+{
+    int dir;
+
+    if (*num_args < 1) return;
+
+    if (!strcmp(*args, "left") || !strcmp(*args, "Left")) {
+	dir = ICMoveLeft;
+    } else if (!strcmp(*args, "right") || !strcmp(*args, "Right")) {
+	dir = ICMoveRight;
+    } else if (!strcmp(*args, "up") || !strcmp(*args, "Up")) {
+	dir = ICMoveUp;
+    } else if (!strcmp(*args, "down") || !strcmp(*args, "Down")) {
+	dir = ICMoveDown;
+    } else {
+	XtAppWarning(XtWidgetToApplicationContext(w),
+		     "CandidatePanel: unknown direction");
+	return;
+    }
+
+    CPanelMoveCurrent(w, dir);
+}
+
+/* ARGSUSED */
+static void
+Set(w, ev, args, num_args)
+Widget w;
+XEvent *ev;
+String *args;
+Cardinal *num_args;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+    XButtonEvent *event = (XButtonEvent *)ev;
+    int cwidth, cheight, hspace, vspace;
+    int x, y;
+    int row, col;
+    int newidx;
+
+    cwidth = cpw->cpanel.maxwidth;
+    cheight = cpw->cpanel.fontheight;
+    hspace = cpw->cpanel.hspace;
+    vspace = cpw->cpanel.vspace;
+
+    x = event->x - hspace / 2;
+    col = x / (cwidth + hspace);
+    if (col >= cpw->cpanel.ncolumns || (x % (cwidth + hspace)) > cwidth) return;
+
+    y = event->y - vspace / 2;
+    row = y / (cheight + vspace);
+    if (row >= cpw->cpanel.nrows || (y % (cheight + vspace)) > cheight) return;
+
+    newidx = col + cpw->cpanel.ncolumns * row;
+    if (newidx >= cpw->cpanel.nstrings) return;
+
+    CPanelSetCurrent(w, newidx);
+}
+
+/* ARGSUSED */
+static void
+Notify(w, ev, args, num_args)
+Widget w;
+XEvent *ev;
+String *args;
+Cardinal *num_args;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+
+    XtCallCallbackList(w, cpw->cpanel.callback,
+		       (XtPointer)cpw->cpanel.current);
+}
+
+static int
+MaxWidth(cpw)
+CandidatePanelWidget cpw;
+{
+    Widget dispobj = cpw->cpanel.displayobj;
+    ICString *list = cpw->cpanel.list;
+    int maxwidth;
+    int i;
+
+    maxwidth = 0;
+    for (i = 0; i < cpw->cpanel.nstrings; i++) {
+	int w = CDStringWidth(dispobj, list + i, 0, -1);
+	if (w > maxwidth) maxwidth = w;
+    }
+
+    return maxwidth;
+}
+
+static void
+ComputeSize(cpw, resizex, resizey, width_inout, height_inout)
+CandidatePanelWidget cpw;
+int resizex;
+int resizey;
+Dimension *width_inout;
+Dimension *height_inout;
+{
+    int nrows, ncolumns;
+    int width, height;
+
+    if (cpw->cpanel.displayobj == NULL || cpw->cpanel.nstrings == 0) return;
+
+    width = *width_inout;
+    height = *height_inout;
+
+    if (resizex) {
+	if (resizey) {
+	    int maxheight = HeightOfScreen(XtScreen((Widget)cpw));
+
+	    /* use defaultwidth */
+	    ncolumns = cpw->cpanel.defaultwidth /
+			(cpw->cpanel.maxwidth + cpw->cpanel.hspace);
+	    if (ncolumns > cpw->cpanel.nstrings) ncolumns = cpw->cpanel.nstrings;
+	    if (ncolumns <= 0) ncolumns = 1;
+	    nrows = (cpw->cpanel.nstrings + ncolumns - 1) / ncolumns;
+	    width = (cpw->cpanel.maxwidth + cpw->cpanel.hspace) * ncolumns;
+	    height = (cpw->cpanel.fontheight + cpw->cpanel.vspace) * nrows;
+	    /*
+	     * If the computed height exceeds display height,
+	     * expand width so that the entire window fits into the display.
+	     */
+	    if (height > maxheight) {
+		/* compute maximum number of rows */
+		nrows = maxheight / (cpw->cpanel.fontheight + cpw->cpanel.vspace);
+		if (nrows <= 0) nrows = 1;
+		ncolumns = (cpw->cpanel.nstrings + nrows - 1) / nrows;
+		if (ncolumns > cpw->cpanel.nstrings) {
+		    ncolumns = cpw->cpanel.nstrings;
+		}
+		/* re-compute correct number of rows */
+		nrows = (cpw->cpanel.nstrings + ncolumns - 1) / ncolumns;
+		width = (cpw->cpanel.maxwidth + cpw->cpanel.hspace) * ncolumns;
+		height = (cpw->cpanel.fontheight + cpw->cpanel.vspace) * nrows;
+	    }
+	} else {
+	    /* use specified height */
+	    nrows = height / (cpw->cpanel.fontheight + cpw->cpanel.vspace);
+	    if (nrows <= 0) nrows = 1;
+	    ncolumns = (cpw->cpanel.nstrings + nrows - 1) / nrows;
+	    if (ncolumns > cpw->cpanel.nstrings) ncolumns = cpw->cpanel.nstrings;
+	    width = (cpw->cpanel.maxwidth + cpw->cpanel.hspace) * ncolumns;
+	}
+    } else {
+	ncolumns = width / (cpw->cpanel.maxwidth + cpw->cpanel.hspace);
+	if (ncolumns <= 0) ncolumns = 1;
+	nrows = (cpw->cpanel.nstrings + ncolumns - 1) / ncolumns;
+	if (resizey) {
+	    height = (cpw->cpanel.fontheight + cpw->cpanel.vspace) * nrows;
+	}
+    }
+    cpw->cpanel.ncolumns = ncolumns;
+    cpw->cpanel.nrows = nrows;
+    *width_inout = width;
+    *height_inout = height;
+}
+
+static void
+Layout(cpw, resizex, resizey)
+CandidatePanelWidget cpw;
+int resizex;
+int resizey;
+{
+    Dimension width, height;
+    Dimension owidth, oheight;
+    XtGeometryResult re;
+
+    if (cpw->cpanel.displayobj == NULL) return;
+
+    width = cpw->core.width;
+    height = cpw->core.height;
+    ComputeSize(cpw, resizex, resizey, &width, &height);
+
+    if (width != cpw->core.width || height != cpw->core.height) {
+	owidth = width;
+	oheight = height;
+	re = XtMakeResizeRequest((Widget)cpw, owidth, oheight, &width, &height);
+	switch (re) {
+	case XtGeometryYes:
+	    /* no problem */
+	    break;
+	case XtGeometryNo:
+	    width = cpw->core.width;
+	    height = cpw->core.height;
+	    ComputeSize(cpw, False, False, &width, &height);
+	    break;
+	case XtGeometryAlmost:
+	    ComputeSize(cpw,
+			width != owidth,
+			height != oheight,
+			&width, &height);
+	    re = XtMakeResizeRequest((Widget)cpw, width, height, &width, &height);
+	    switch (re) {
+	    case XtGeometryYes:
+		break;
+	    case XtGeometryNo:
+		width = cpw->core.width;
+		height = cpw->core.height;
+		ComputeSize(cpw, False, False, &width, &height);
+		break;
+	    case XtGeometryAlmost:
+		ComputeSize(cpw, False, False, &width, &height);
+		(void)XtMakeResizeRequest((Widget)cpw, width, height, &width, &height);
+	    }
+	}
+    }
+}
+
+static void
+ToggleHighlight(cpw, idx)
+CandidatePanelWidget cpw;
+int idx;
+{
+    int row, col;
+    int x, y;
+    int width;
+
+    if (idx < 0 || cpw->cpanel.nstrings <= idx) return;
+
+    col = idx % cpw->cpanel.ncolumns;
+    row = idx / cpw->cpanel.ncolumns;
+    x = (cpw->cpanel.maxwidth + cpw->cpanel.hspace) * col +
+      cpw->cpanel.hspace / 2;
+    y = (cpw->cpanel.fontheight + cpw->cpanel.vspace) * row +
+      cpw->cpanel.vspace / 2;
+    width = CDStringWidth(cpw->cpanel.displayobj, cpw->cpanel.list + idx,
+			  0, -1);
+
+    XFillRectangle(XtDisplay(cpw), XtWindow(cpw), cpw->cpanel.invgc, x, y,
+		   (unsigned int)width, (unsigned int)cpw->cpanel.fontheight);
+}
+
+
+/*
+ * Public Functions
+ */
+
+void
+CPanelSetList(w, list, nstrings, current, resize)
+Widget w;
+ICString *list;
+int nstrings;
+int current;
+int resize;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+
+    if (list != NULL) {
+	cpw->cpanel.list = list;
+	cpw->cpanel.nstrings = nstrings;
+	if (current < 0) current = 0;
+	if (current >= nstrings) current = nstrings - 1;
+	cpw->cpanel.current = current;
+    }
+
+    if (cpw->cpanel.displayobj == NULL) return;
+
+    cpw->cpanel.fontheight = CDLineHeight(cpw->cpanel.displayobj,
+					  (Position *)NULL);
+    /* compute maximum pixel width of the list items */
+    cpw->cpanel.maxwidth = MaxWidth(cpw);
+    Layout(cpw, resize, resize);
+
+    if (XtIsRealized(w)) XClearWindow(XtDisplay(w), XtWindow(w));
+}
+
+void
+CPanelSetCurrent(w, idx)
+Widget w;
+int idx;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+
+    if (idx < 0 || cpw->cpanel.nstrings <= idx) return;
+
+    if (idx == cpw->cpanel.current) return;
+
+    if (cpw->cpanel.displayobj != NULL && XtIsRealized(w)) {
+	ToggleHighlight(cpw, cpw->cpanel.current);
+	ToggleHighlight(cpw, idx);
+    }
+    cpw->cpanel.current = idx;
+}
+
+void
+CPanelMoveCurrent(w, dir)
+Widget w;
+int dir;
+{
+    CandidatePanelWidget cpw = (CandidatePanelWidget)w;
+    int newidx;
+    int row, col;
+    int nstrings = cpw->cpanel.nstrings;
+
+    if (nstrings <= 0) return;
+
+    col = cpw->cpanel.current % cpw->cpanel.ncolumns;
+    row = cpw->cpanel.current / cpw->cpanel.ncolumns;
+
+    switch (dir) {
+    case ICMoveLeft:
+	if ((newidx = cpw->cpanel.current - 1) < 0) newidx = nstrings - 1;
+	break;
+    case ICMoveRight:
+	if ((newidx = cpw->cpanel.current + 1) >= nstrings) newidx = 0;
+	break;
+    case ICMoveUp:
+    case ICMovePrevPage:
+	if (--row < 0) row = cpw->cpanel.nrows - 1;
+	newidx = row * cpw->cpanel.ncolumns + col;
+	if (newidx >= nstrings) newidx -= cpw->cpanel.ncolumns;
+	break;
+    case ICMoveDown:
+    case ICMoveNextPage:
+	if (++row >= cpw->cpanel.nrows) row = 0;
+	newidx = row * cpw->cpanel.ncolumns + col;
+	if (newidx >= nstrings) newidx = col;
+	break;
+    case ICMoveLeftMost:
+	newidx = row * cpw->cpanel.ncolumns;
+	break;
+    case ICMoveRightMost:
+	newidx = (row + 1) * cpw->cpanel.ncolumns - 1;
+	if (newidx >= cpw->cpanel.nstrings) newidx = cpw->cpanel.nstrings - 1;
+	break;
+    case ICMoveFirst:
+	newidx = 0;
+	break;
+    case ICMoveLast:
+	newidx = cpw->cpanel.nstrings - 1;
+	break;
+    }
+
+    CPanelSetCurrent(w, newidx);
+}