Mercurial > kinput2.yaz
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); +}