diff lib/ConvCtrl.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 7a454839a6de
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/ConvCtrl.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,1274 @@
+#ifndef lint
+static char *rcsid = "$Id: ConvCtrl.c,v 1.54 2001/01/10 08:51:28 ishisone Exp $";
+#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 <X11/Xmu/CharSet.h>
+#include "ConvCtrlP.h"
+#include "InputConv.h"
+#include "ConvDisp.h"
+#include "MyDispatch.h"
+#include "AsyncErr.h"
+
+#define DEBUG_VAR debug_ConversionControl
+#include "DebugPrint.h"
+
+static XtResource resources[] = {
+#define offset(field) XtOffset(ConversionControlWidget, ccontrol.field)
+    { XtNinputObject, XtCInputObject, XtRWidget, sizeof(Widget),
+	offset(inputobj), XtRImmediate, (XtPointer)NULL },
+    { XtNinputObjectClass, XtCInputObjectClass,
+	XtRPointer, sizeof(WidgetClass),
+	offset(inputobjclass), XtRImmediate, (XtPointer)NULL },
+    { XtNdisplayObjectClass, XtCDisplayObjectClass,
+	XtRPointer, sizeof(WidgetClass),
+	offset(displayobjclass), XtRImmediate, (XtPointer)NULL },
+    { XtNclientWindow, XtCWindow, XtRWindow, sizeof(Window),
+	offset(clientwindow), XtRImmediate, (XtPointer)None },
+    { XtNfocusWindow, XtCWindow, XtRWindow, sizeof(Window),
+	offset(focuswindow), XtRImmediate, (XtPointer)None },
+    { XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
+	offset(cursor), XtRImmediate, (XtPointer)None },
+    { XtNeventSelectMethod, XtCEventSelectMethod,
+	XtREventSelectMethod, sizeof(EventSelectMethod),
+	offset(eventselectmethod), XtRString, (XtPointer)"none" },
+    { XtNtextEncoding, XtCTextEncoding, XtRAtom, sizeof(Atom),
+	offset(textencoding), XtRString, "COMPOUND_TEXT" },
+    { XtNtextCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
+	offset(textcallback), XtRCallback, (XtPointer)NULL },
+    { XtNnewTextCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
+	offset(newtextcallback), XtRCallback, (XtPointer)NULL },
+    { XtNendCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
+	offset(endcallback), XtRCallback, (XtPointer)NULL },
+    { XtNunusedEventCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
+	offset(unusedeventcallback), XtRCallback, (XtPointer)NULL },
+    { XtNsendbackKeyPress, XtCSendbackEvent, XtRBoolean, sizeof(Boolean),
+	offset(sendbackKeyPress), XtRString, (XtPointer)"False" },
+    { XtNtitlebarHeight, XtCTitlebarHeight, XtRDimension, sizeof(Dimension),
+	offset(titlebarheight), XtRImmediate, (XtPointer)0 },
+#undef offset
+};
+
+static void EventToInputObject();
+
+static XtActionsRec actions[] = {
+    {"to-inputobj",	EventToInputObject },
+};
+
+static char translations[] = "<Key>: to-inputobj()";
+
+static void ClassInitialize();
+static void StringToESM();
+static void ClassPartInitialize();
+static void Initialize(), Destroy();
+static void Realize();
+static void Resize();
+static Boolean SetValues();
+
+static void ConversionStartup();
+static void ConversionFinish();
+static void ChangeAttributes();
+static void ChangeFocus();
+static void TextChange();
+static void Fix();
+static void ModeChange();
+static void SelectionControl();
+static void AuxControl();
+
+static void GetClientCoordinates();
+
+static Widget CreateInputObject();
+
+static Boolean ClassIsSubClassOf();
+
+static void CaptureClientDead();
+static void InterceptClientKeyEvent();
+static void SelectFocusKeyEvent();
+static void UnselectFocusKeyEvent();
+static void ClientKey();
+static void ClientDead();
+
+static Boolean SafeGetWindowAttributes();
+static void CheckAttributes();
+static void CheckCoordinates();
+static Boolean clipRectangle();
+
+static void FixCallback();
+static void ConversionEndCallback();
+static void TextChangeCallback();
+static void ModeChangeCallback();
+static void SelectionControlCallback();
+static void AuxControlCallback();
+
+static void WidgetError(), WidgetWarning();
+
+static CompositeClassExtensionRec CompositeExtension = {
+    /* next_extension		*/	NULL,
+    /* record_type		*/	NULLQUARK,
+    /* version			*/	XtCompositeExtensionVersion,
+    /* record_size		*/	sizeof(CompositeClassExtensionRec),
+    /* accept_objects		*/	True,
+};
+
+ConversionControlClassRec conversionControlClassRec = {
+  { /* core fields */
+    /* superclass		*/	(WidgetClass) &transientShellClassRec,
+    /* class_name		*/	"ConversionControl",
+    /* widget_size		*/	sizeof(ConversionControlRec),
+    /* class_initialize		*/	ClassInitialize,
+    /* class_part_initialize	*/	ClassPartInitialize,
+    /* 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			*/	NULL,
+    /* 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		*/	XtInheritQueryGeometry,
+    /* display_accelerator	*/	XtInheritDisplayAccelerator,
+    /* extension		*/	NULL
+  },
+  { /* composite fields */
+    /* geometry_manager		*/	XtInheritGeometryManager,
+    /* change_managed		*/	XtInheritChangeManaged,
+    /* insert_child		*/	XtInheritInsertChild,
+    /* delete_child		*/	XtInheritDeleteChild,
+    /* extension		*/	(XtPointer)&CompositeExtension,
+  },
+  { /* shell fields */
+    /* extension		*/	NULL
+  },
+  { /* wm_shell fields */
+    /* extension		*/	NULL
+  },
+  { /* vendor_shell fields */
+    /* extension		*/	NULL
+  },
+  { /* transient_shell fields */
+    /* extension		*/	NULL
+  },
+  { /* conversionControl fields */
+    /* Startup			*/	ConversionStartup,
+    /* Finish			*/	ConversionFinish,
+    /* ChangeAttributes		*/	ChangeAttributes,
+    /* ChangeFocus		*/	ChangeFocus,
+    /* TextChange		*/	TextChange,
+    /* Fix			*/	Fix,
+    /* ModeChange		*/	ModeChange,
+    /* SelectionControl		*/	SelectionControl,
+    /* AuxControl		*/	AuxControl,
+  }
+};
+
+WidgetClass conversionControlWidgetClass = (WidgetClass)&conversionControlClassRec;
+
+/* ARGSUSED */
+static void
+ClassInitialize()
+{
+    /* add String -> EventSelectionMethod converter */
+    XtAddConverter(XtRString, XtREventSelectMethod, StringToESM,
+		   (XtConvertArgList)NULL, (Cardinal)0);
+}
+
+/* ARGSUSED */
+static void
+StringToESM(args, num_args, from, to)
+XrmValue *args;
+Cardinal *num_args;
+XrmValue *from;
+XrmValue *to;
+{
+    char *s = (char *)from->addr;
+    static EventSelectMethod esm = ESMethodNone;
+
+    if (!XmuCompareISOLatin1(s, "inputonly")) {
+	esm = ESMethodInputOnly;
+    } else if (!XmuCompareISOLatin1(s, "selectfocus")) {
+	esm = ESMethodSelectFocus;
+    } else if (!XmuCompareISOLatin1(s, "none")) {
+	esm = ESMethodNone;
+    } else {
+	XtStringConversionWarning(s, XtREventSelectMethod);
+    }
+
+    to->size = sizeof(EventSelectMethod);
+    to->addr = (caddr_t)&esm;
+}
+
+static void
+ClassPartInitialize(cl)
+WidgetClass cl;
+{
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)cl;
+    ConversionControlWidgetClass super = (ConversionControlWidgetClass)class->core_class.superclass;
+
+#define ccclass conversionControl_class
+    if (class->ccclass.Startup == XtInheritStartup) {
+	class->ccclass.Startup = super->ccclass.Startup;
+    }
+    if (class->ccclass.Finish == XtInheritFinish) {
+	class->ccclass.Finish = super->ccclass.Finish;
+    }
+    if (class->ccclass.ChangeAttributes == XtInheritChangeAttributes) {
+	class->ccclass.ChangeAttributes = super->ccclass.ChangeAttributes;
+    }
+    if (class->ccclass.ChangeFocus == XtInheritChangeFocus) {
+	class->ccclass.ChangeFocus = super->ccclass.ChangeFocus;
+    }
+    if (class->ccclass.TextChange == XtInheritTextChange) {
+	class->ccclass.TextChange = super->ccclass.TextChange;
+    }
+    if (class->ccclass.Fix == XtInheritFix) {
+	class->ccclass.Fix = super->ccclass.Fix;
+    }
+    if (class->ccclass.ModeChange == XtInheritModeChange) {
+	class->ccclass.ModeChange = super->ccclass.ModeChange;
+    }
+    if (class->ccclass.SelectionControl == XtInheritSelectionControl) {
+	class->ccclass.SelectionControl = super->ccclass.SelectionControl;
+    }
+    if (class->ccclass.AuxControl == XtInheritAuxControl) {
+	class->ccclass.AuxControl = super->ccclass.AuxControl;
+    }
+#undef ccclass
+}
+
+/* ARGSUSED */
+static void
+Initialize(req, new, args, num_args)
+Widget req;
+Widget new;
+ArgList args;
+Cardinal *num_args;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)new;
+
+    /*
+     * check inputobj/inputobjclass resource
+     */
+
+    if (ccw->ccontrol.inputobj == NULL) {
+	/* if inputobj not specified, inputobjclass must be specified */
+	if (ccw->ccontrol.inputobjclass == NULL) {
+	    WidgetError(new, "noResourceError", "inputObjectClass",
+			"either inputObject or inputObjectClass must be specified at creation time");
+	} else if (!ClassIsSubClassOf(ccw->ccontrol.inputobjclass,
+				      inputConvObjectClass)) {
+	    WidgetError(new, "classError", "inputObjectClass",
+			"inputObjectClass must be subclass of inputConvObjectClass");
+	}
+	(void)CreateInputObject(ccw);
+	ccw->ccontrol.createinputobj = True;
+
+    } else if (!XtIsSubclass(ccw->ccontrol.inputobj, inputConvObjectClass)) {
+	WidgetError(new, "classError", "inputObject",
+		    "inputObject must be subclass of inputConvObjectClass");
+    }
+
+    if (ccw->ccontrol.displayobjclass == NULL) {
+	WidgetError(new, "noResourceError", "displayObjectClass",
+		    "displayObjectClass must be specified");
+    } else if (!ClassIsSubClassOf(ccw->ccontrol.displayobjclass,
+				  convDisplayObjectClass)) {
+	WidgetError(new, "classError", "displayObjectClass",
+		    "displayObjectClass must be subclass of convDisplayObjectClass");
+    }
+
+    ccw->ccontrol.active = False;
+    ccw->ccontrol.oldclientwindow = None;
+    ccw->ccontrol.probewindow = None;
+}
+
+static void
+Destroy(w)
+Widget w;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+    Display *dpy = XtDisplay(w);
+
+    if (ccw->ccontrol.active == False) return;
+
+    if (ccw->ccontrol.clientwindow != None) {
+	MyRemoveAllEventHandler(dpy, ccw->ccontrol.clientwindow);
+    }
+
+    if (ccw->ccontrol.probewindow != None) {
+	MyRemoveAllEventHandler(dpy, ccw->ccontrol.probewindow);
+	XDestroyWindow(dpy, ccw->ccontrol.probewindow);
+    }
+}
+
+static void
+Realize(w, maskp, attr)
+Widget w;
+XtValueMask *maskp;
+XSetWindowAttributes *attr;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+
+    if (ccw->ccontrol.cursor != None) {
+	attr->cursor = ccw->ccontrol.cursor;
+	*maskp |= CWCursor;
+    }
+
+    /* call super class's realize function */
+    (*conversionControlWidgetClass->core_class.superclass->core_class.realize)(w, maskp, attr);
+}
+
+static void
+Resize(w)
+Widget w;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+    Widget child;
+    int i;
+
+    TRACE(("ConversionControl:Resize()\n"));
+
+    /* ignore non-widgets */
+    for (i = 0; i < ccw->composite.num_children; i++) {
+	child = ccw->composite.children[i];
+	if (XtIsWidget(child) && child->core.managed) {
+	    XtResizeWidget(child, ccw->core.width, ccw->core.height,
+			   child->core.border_width);
+	}
+    }
+}
+
+/* ARGSUSED */
+static Boolean
+SetValues(cur, req, wid, args, num_args)
+Widget cur;
+Widget req;
+Widget wid;
+ArgList args;
+Cardinal *num_args;
+{
+    ConversionControlWidget old = (ConversionControlWidget)cur;
+    ConversionControlWidget new = (ConversionControlWidget)wid;
+
+    if (new->ccontrol.active) {
+	if (old->ccontrol.inputobj != new->ccontrol.inputobj) {
+	    WidgetWarning(wid, "setValuesError", "inputObject",
+			  "inputObject resource can't be changed during conversion");
+	    new->ccontrol.inputobj = old->ccontrol.inputobj;	/* restore */
+	}
+	if (old->ccontrol.eventselectmethod != new->ccontrol.eventselectmethod) {
+	    WidgetWarning(wid, "setValuesError", "eventSelectionMethod",
+			  "eventSelectionMethod resource can't be changed during conversion");
+	    new->ccontrol.eventselectmethod = old->ccontrol.eventselectmethod; /* restore */
+	}
+    }
+
+    if (new->ccontrol.clientwindow != old->ccontrol.clientwindow ||
+	new->ccontrol.focuswindow != old->ccontrol.focuswindow) {
+	WidgetWarning(wid, "setValuesError", "clientWindow",
+		      "clientWindow and focusWindow resources are read-only");
+	new->ccontrol.clientwindow = old->ccontrol.clientwindow; /* restore */
+	new->ccontrol.focuswindow = old->ccontrol.focuswindow; /* restore */
+    }
+
+    if (new->ccontrol.cursor != old->ccontrol.cursor && XtIsRealized(wid)) {
+	XDefineCursor(XtDisplay(wid), XtWindow(wid), new->ccontrol.cursor);
+    }
+
+    return False;
+}
+
+/* ARGSUSED */
+static void
+ConversionStartup(w, mask, value)
+Widget w;
+unsigned long mask;
+ConversionAttributes *value;
+{
+    /* do nothing */
+}
+
+/* ARGSUSED */
+static void
+ConversionFinish(w)
+Widget w;
+{
+    /* do nothing */
+}
+
+/* ARGSUSED */
+static void
+ChangeAttributes(w, mask, value)
+Widget w;
+unsigned long mask;
+ConversionAttributes *value;
+{
+    /* do nothing */
+}
+
+/* ARGSUSED */
+static void
+ChangeFocus(w, set)
+Widget w;
+int set;
+{
+    /* do nothing */
+}
+
+/* ARGSUSED */
+static void
+TextChange(w)
+Widget w;
+{
+    /* do nothing */
+}
+
+/* ARGSUSED */
+static void
+Fix(w, arg)
+Widget w;
+CCTextCallbackArg *arg;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+
+    XtCallCallbackList((Widget)ccw, ccw->ccontrol.textcallback,
+		       (XtPointer)arg);
+}
+
+/* ARGSUSED */
+static void
+ModeChange(w)
+Widget w;
+{
+    /* do nothing */
+}
+
+/* ARGSUSED */
+static void
+SelectionControl(w, controlarg)
+Widget w;
+ICSelectionControlArg *controlarg;
+{
+    /* do nothing */
+}
+
+/* ARGSUSED */
+static void
+AuxControl(w, controlarg)
+Widget w;
+ICAuxControlArg *controlarg;
+{
+    /* do nothing */
+}
+
+/*
+ * public functions
+ */
+
+void
+CControlStartConversion(w, clientwindow, valuemask, value)
+Widget w;
+Window clientwindow;
+unsigned long valuemask;
+ConversionAttributes *value;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)w->core.widget_class;
+
+    TRACE(("CControlStartConversion(clientwindow=%lx)\n", clientwindow));
+    if (ccw->ccontrol.active) {
+	WidgetWarning(w, "busyError", "CControlStartConversion",
+		      "is busy. can't start conversion");
+	return;
+    }
+    if (clientwindow == None) {
+	/* ouch */
+	WidgetWarning(w, "dataError", "cControlStartConversion",
+		     "clientWindow not specified. can't start conversion.");
+	return;
+    }
+
+    /* check clientWindow's existance */
+    if (!SafeGetWindowAttributes(XtDisplay(w), clientwindow,
+				 &(ccw->ccontrol.client_attr))) {
+	WidgetWarning(w, "badWindowError", "clientWindow",
+		      "clientWindow does not exist. can't start conversion.");
+	return;
+    }
+
+    ICClearConversion(ccw->ccontrol.inputobj);
+    ccw->ccontrol.notext = ICNumSegments(ccw->ccontrol.inputobj) == 0;
+
+    ccw->ccontrol.active = True;
+    ccw->ccontrol.clientwindow = clientwindow;
+
+    /* check given attributes */
+    CheckAttributes(ccw, &valuemask, value);
+
+    if (valuemask & CAFocusWindow) {
+	ccw->ccontrol.focuswindow = value->focuswindow;
+    } else {
+	ccw->ccontrol.focuswindow = clientwindow;
+	ccw->ccontrol.focus_attr = ccw->ccontrol.client_attr;
+    }
+
+    if (ccw->ccontrol.eventselectmethod == ESMethodInputOnly) {
+	InterceptClientKeyEvent(ccw);
+    } else if (ccw->ccontrol.eventselectmethod == ESMethodSelectFocus) {
+	SelectFocusKeyEvent(ccw);
+    }
+
+    CheckCoordinates(ccw, &valuemask, value, 1);
+
+    GetClientCoordinates(ccw);
+
+    CaptureClientDead(ccw);
+
+    XtAddCallback(ccw->ccontrol.inputobj,
+		  XtNfixNotify, FixCallback, (XtPointer)ccw);
+    XtAddCallback(ccw->ccontrol.inputobj,
+		  XtNendNotify, ConversionEndCallback, (XtPointer)ccw);
+    XtAddCallback(ccw->ccontrol.inputobj,
+		  XtNtextChangeNotify, TextChangeCallback, (XtPointer)ccw);
+    XtAddCallback(ccw->ccontrol.inputobj,
+		  XtNmodeChangeNotify, ModeChangeCallback, (XtPointer)ccw);
+    XtAddCallback(ccw->ccontrol.inputobj,
+		  XtNselectionControl, SelectionControlCallback,
+		  (XtPointer)ccw);
+    XtAddCallback(ccw->ccontrol.inputobj,
+		  XtNauxControl, AuxControlCallback,
+		  (XtPointer)ccw);
+
+    /* call input style dependent startup */
+    (*class->conversionControl_class.Startup)(w, valuemask, value);
+}
+
+void
+CControlEndConversion(w)
+Widget w;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)w->core.widget_class;
+    Display *dpy = XtDisplay(w);
+
+    if (!ccw->ccontrol.active) {
+	WidgetWarning(w, "busyError", "cControlEndConversion",
+		      "is not active. can't stop conversion");
+	return;
+    }
+
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNfixNotify, FixCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNendNotify, ConversionEndCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNtextChangeNotify, TextChangeCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNmodeChangeNotify, ModeChangeCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNselectionControl, SelectionControlCallback,
+		     (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNauxControl, AuxControlCallback,
+		     (XtPointer)ccw);
+
+    ICClearConversion(ccw->ccontrol.inputobj);
+
+    MyRemoveEventHandler(dpy, ccw->ccontrol.clientwindow, DestroyNotify,
+			 ClientDead, (XtPointer)ccw);
+
+    if (ccw->ccontrol.probewindow != None) {
+	MyRemoveAllEventHandler(dpy, ccw->ccontrol.probewindow);
+	XDestroyWindow(dpy, ccw->ccontrol.probewindow);
+	ccw->ccontrol.probewindow = None;
+    }
+
+    /* unselect focuswindow events */
+    if (ccw->ccontrol.eventselectmethod == ESMethodSelectFocus) {
+	UnselectFocusKeyEvent(ccw);
+    }
+
+    ccw->ccontrol.active = False;
+
+    /* call input style dependent finish */
+    (*class->conversionControl_class.Finish)(w);
+
+    ccw->ccontrol.oldclientwindow = ccw->ccontrol.clientwindow;
+    ccw->ccontrol.clientwindow = None;
+}
+
+void
+CControlChangeAttributes(w, valuemask, value)
+Widget w;
+unsigned long valuemask;
+ConversionAttributes *value;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)w->core.widget_class;
+
+    if (!ccw->ccontrol.active) {
+	WidgetWarning(w, "busyError", "cControlChangeAttributes",
+		      "is not active. can't change attributes");
+	return;
+    }
+
+    CheckAttributes(ccw, &valuemask, value);
+    CheckCoordinates(ccw, &valuemask, value, 0);
+
+    if (valuemask == 0L) return;
+
+    if (valuemask & CAFocusWindow) {
+	if (ccw->ccontrol.eventselectmethod == ESMethodSelectFocus) {
+	    UnselectFocusKeyEvent(ccw);
+	}
+	ccw->ccontrol.focuswindow = value->focuswindow;
+	if (ccw->ccontrol.eventselectmethod == ESMethodSelectFocus) {
+	    SelectFocusKeyEvent(ccw);
+	}
+    }
+
+    (*class->conversionControl_class.ChangeAttributes)(w, valuemask, value);
+}
+
+void
+CControlChangeFocus(w, set)
+Widget w;
+int set;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)w->core.widget_class;
+
+    if (!ccw->ccontrol.active) {
+	WidgetWarning(w, "busyError", "cControlChangeFocus",
+		      "is not active. can't change focus");
+	return;
+    }
+
+    (*class->conversionControl_class.ChangeFocus)(w, set);
+}
+
+static Boolean
+SafeGetWindowAttributes(dpy, w, attr)
+Display *dpy;
+Window w;
+XWindowAttributes *attr;
+{
+    XAEHandle h;
+    unsigned long errbits = 0;
+
+    h = XAESetRecordErrors(dpy, &errbits);
+    (void)XGetWindowAttributes(dpy, w, attr);
+    XAEUnset(h);
+
+    return (errbits == 0);
+}
+
+static void
+CheckAttributes(ccw, valuemaskp, value)
+ConversionControlWidget ccw;
+unsigned long *valuemaskp;
+ConversionAttributes *value;
+{
+    Display *dpy = XtDisplay((Widget)ccw);
+    XAEHandle h;
+    unsigned long ebits = 0;
+
+#define CHECKATTRS (CAFocusWindow|CAColormap|CACursor|CABackgroundPixmap)
+    if ((*valuemaskp & CHECKATTRS) == 0) return;
+#undef CHECKATTRS
+
+#define XERROR(e)	(ebits & (1 << (e)))
+
+    h = XAESetRecordErrors(dpy, &ebits);
+
+    if (*valuemaskp & (CAColormap|CACursor|CABackgroundPixmap)) {
+	XSetWindowAttributes attr;
+	Window w;
+
+	w = XCreateSimpleWindow(dpy, ccw->ccontrol.clientwindow,
+				0, 0, 1, 1, 0, 0L, 0L);
+
+	if (*valuemaskp & CACursor) {
+	    /* BadCursor */
+	    attr.cursor = value->cursor;
+	    XChangeWindowAttributes(dpy, w, CWCursor, &attr);
+	}
+	if (*valuemaskp & CAColormap) {
+	    /* BadMatch or BadColormap */
+	    attr.colormap = value->colormap;
+	    XChangeWindowAttributes(dpy, w, CWColormap, &attr);
+	}
+	if (*valuemaskp & CABackgroundPixmap) {
+	    /* BadMatch or BadPixmap */
+	    attr.background_pixmap = value->background_pixmap;
+	    XChangeWindowAttributes(dpy, w, CWBackPixmap, &attr);
+	}
+
+	XDestroyWindow(dpy, w);
+    }
+    if (*valuemaskp & CAFocusWindow) {
+	(void)XGetWindowAttributes(dpy, value->focuswindow,
+				   &(ccw->ccontrol.focus_attr));
+    } else {
+	XSync(dpy, False);
+    }
+    XAEUnset(h);
+
+    if ((*valuemaskp & CAFocusWindow) && XERROR(BadWindow)) {
+	WidgetWarning((Widget)ccw, "badWindowError", "focusWindow",
+		      "focusWindow does not exist.");
+	*valuemaskp &= ~CAFocusWindow;
+    }
+    if ((*valuemaskp & CAColormap) && XERROR(BadColor)) {
+	WidgetWarning((Widget)ccw, "badColorError", "colormap",
+		      "invalid colormap ID.");
+	*valuemaskp &= ~CAColormap;
+    }
+    if ((*valuemaskp & CAColormap) && XERROR(BadMatch)) {
+	WidgetWarning((Widget)ccw, "badMatchError", "colormap",
+		      "invalid colormap.");
+	*valuemaskp &= ~CAColormap;
+    }
+    if ((*valuemaskp & CABackgroundPixmap) && XERROR(BadPixmap)) {
+	WidgetWarning((Widget)ccw, "badPixmapError", "backgroundPixmap",
+		      "invalid pixmap ID.");
+	*valuemaskp &= ~CABackgroundPixmap;
+    }
+    if ((*valuemaskp & CABackgroundPixmap) && XERROR(BadMatch)) {
+	WidgetWarning((Widget)ccw, "badMatchError", "backgroundPixmap",
+		      "invalid pixmap for background.");
+	*valuemaskp &= ~CABackgroundPixmap;
+    }
+    if ((*valuemaskp & CACursor) && XERROR(BadCursor)) {
+	WidgetWarning((Widget)ccw, "badCursorError", "cursor",
+		      "invalid cursor ID.");
+	*valuemaskp &= ~CACursor;
+    }
+#undef XERROR
+}
+
+static void
+CheckCoordinates(ccw, valuemaskp, value, clip)
+ConversionControlWidget ccw;
+unsigned long *valuemaskp;
+ConversionAttributes *value;
+int clip;
+{
+#define INVALIDRECT(r) (r.width == 0 || r.height == 0)
+    if ((*valuemaskp & CAClientArea) &&
+	(INVALIDRECT(value->clientarea) ||
+	 (clip && !clipRectangle(&value->clientarea, &ccw->ccontrol.client_attr)))) {
+	DPRINT(("CheckCoordinates: invalid ClientArea\n"));
+	*valuemaskp &= ~CAClientArea;
+    }
+    if ((*valuemaskp & CAStatusArea) &&
+	(INVALIDRECT(value->statusarea) ||
+	 (clip && !clipRectangle(&value->statusarea, &ccw->ccontrol.client_attr)))) {
+	DPRINT(("CheckCoordinates: invalid StatusArea\n"));
+	*valuemaskp &= ~CAStatusArea;
+    }
+#undef INVALIDRECT
+}
+
+static Boolean
+clipRectangle(rectp, attrp)
+register XRectangle *rectp;
+register XWindowAttributes *attrp;
+{
+    register int z0, z1, e;
+
+    z0 = rectp->x; z1 = z0 + rectp->width; e = attrp->width;
+    if (z0 == z1) z1 = e;		/* if (rectp->width == 0)... */
+    if (z0 >= e || z1 <= 0) return False;
+    if (z0 < 0) z0 = 0;
+    if (z1 > e) z1 = e;
+    rectp->x = z0; rectp->width = z1 - z0;
+
+    z0 = rectp->y; z1 = z0 + rectp->height; e = attrp->height;
+    if (z0 == z1) z1 = e;		/* if (rectp->height == 0)... */
+    if (z0 >= e || z1 <= 0) return False;
+    if (z0 < 0) z0 = 0;
+    if (z1 > e) z1 = e;
+    rectp->y = z0; rectp->height = z1 - z0;
+
+    return True;
+}
+
+static void
+GetClientCoordinates(ccw)
+ConversionControlWidget ccw;
+{
+    Display *dpy = XtDisplay(ccw);
+    Window root = RootWindowOfScreen(XtScreen(ccw));
+    Window win = ccw->ccontrol.clientwindow;
+    Window junk;
+    int rootx, rooty;
+
+    if (!XTranslateCoordinates(dpy, win, root, 0, 0, &rootx, &rooty, &junk)) {
+	WidgetWarning((Widget)ccw, "windowError", "differentRoot",
+		      "clientWindow and conversion widget have different root. this should not happen!");
+	rootx = rooty = 0;
+    }
+
+    ccw->ccontrol.client_rootx = rootx;
+    ccw->ccontrol.client_rooty = rooty;
+}
+
+/* ARGSUSED */
+static void
+EventToInputObject(w, event, args, num_args)
+Widget w;
+XEvent *event;
+String *args;
+Cardinal *num_args;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)w;
+    Boolean hascallback = False;
+    int r;
+#ifdef OBSOLETE_FEATURE
+    Boolean sendback = False;
+#endif
+
+    if (ccw->ccontrol.inputobj == NULL) return;
+
+    ccw->ccontrol.endnotify = False;
+
+    if (ccw->ccontrol.unusedeventcallback != NULL &&
+	XtHasCallbacks(w, XtNunusedEventCallback) == XtCallbackHasSome) {
+	hascallback = True;
+    }
+
+#ifdef OBSOLETE_FEATURE
+    /*
+     * a cheap little hack -- sending back unused events
+     *
+     * if user set some callback on XtNunusedEventCallback, we call
+     * the callback functions on 'unused' KeyPress events.
+     * 
+     * otherwise, if the resource XtNsendbackKeyPress is true, we
+     * attempt to send 'unused' KeyPress events back to the client.
+     *
+     * we call callbacks  or send an event back to the client when
+     * following conditions are satisfied:
+     *    + it is a KeyPress event AND
+     *    + the number of segments is 0 before the conversion object
+     *      processes it AND
+     *    + the number of segments remains 0 after processing AND
+     *    + none of the modeChangeNotify, selectionControl, endNotify and
+     *      fixNofity callbacks are called during processing
+     * it is intentional to exclude textChangeNotify callback from
+     * above condition, because this callback is often called even if
+     * the text doesn't change actually. so we use the condition that
+     * the number of segments remains 0 instead.
+     */
+
+    if ((hascallback ||ccw->ccontrol.sendbackKeyPress) &&
+	event->type == KeyPress &&
+	ICNumSegments(ccw->ccontrol.inputobj) == 0) {
+	sendback = True;
+    }
+
+    ccw->ccontrol.eventused = False;
+
+    if ((r = ICInputEvent(ccw->ccontrol.inputobj, event)) == 1 ||
+	(sendback && !ccw->ccontrol.eventused &&
+	 ICNumSegments(ccw->ccontrol.inputobj) == 0)) {
+#else
+    /*
+     * Above feature is obsolete.  Now it is the input object's
+     * responsibility to decide whether the event should be
+     * sent back to the client or not.
+     */
+    if ((r = ICInputEvent(ccw->ccontrol.inputobj, event)) == 1) {
+#endif
+
+	/* event isn't used */
+	if (hascallback) {
+	    TRACE(("call XtNunusedEventCallback\n"));
+	    XtCallCallbackList(w, ccw->ccontrol.unusedeventcallback,
+			       (XtPointer)event);
+	} else if (ccw->ccontrol.sendbackKeyPress) {
+	    Window savewin;
+	    Window savesubwin;
+	    
+	    TRACE(("sendback event to window 0x%lx\n", ccw->ccontrol.focuswindow));
+	    savewin = event->xkey.window;
+	    savesubwin = event->xkey.subwindow;
+	    event->xkey.window = ccw->ccontrol.focuswindow;
+	    event->xkey.subwindow = None;
+
+	    /*
+	     * here we use NoEventMask as the eventmask, not
+	     * KeyPressMask.  that means the event will be sent only
+	     * to the client who created the destination window.
+	     */
+	    XSendEvent(XtDisplay(w), event->xkey.window,
+		       False, NoEventMask, event);
+
+	    event->xkey.window = savewin;	/* restore */
+	    event->xkey.subwindow = savesubwin;	/* restore */
+	}
+    }
+
+    if (r < 0 || ccw->ccontrol.endnotify) {
+	CControlEndConversion(w);
+	XtCallCallbackList(w, ccw->ccontrol.endcallback, (XtPointer)False);
+    }
+}
+
+
+/*
+ * sub-widget creation
+ */
+
+static Widget
+CreateInputObject(ccw)
+ConversionControlWidget ccw;
+{
+    Widget inputobj;
+    Arg args[1];
+
+    XtSetArg(args[0], XtNdisplayObjectClass, ccw->ccontrol.displayobjclass);
+    inputobj = XtCreateWidget("inputObj", ccw->ccontrol.inputobjclass,
+			      (Widget)ccw, args, 1);
+    ccw->ccontrol.inputobj = inputobj;
+
+    return inputobj;
+}
+
+static Boolean
+ClassIsSubClassOf(class, reference)
+WidgetClass class;
+WidgetClass reference;
+{
+    while (class != NULL) {
+	if (class == reference) return True;
+	class = class->core_class.superclass;
+    }
+    return False;
+}
+
+static void
+CaptureClientDead(ccw)
+ConversionControlWidget ccw;
+{
+    Display *dpy = XtDisplay(ccw);
+    Window win = ccw->ccontrol.clientwindow;
+
+    MyAddEventHandler(dpy, win, DestroyNotify, StructureNotifyMask,
+		      ClientDead, (XtPointer)ccw);
+}
+
+static void
+InterceptClientKeyEvent(ccw)
+ConversionControlWidget ccw;
+{
+    Display *dpy = XtDisplay(ccw);
+    Window win = ccw->ccontrol.clientwindow;
+    Window probe;
+
+    TRACE(("InterceptClientKeyEvent()\n"));
+    probe = XCreateWindow(dpy, win, 0, 0, 9999, 9999, 0, 0,
+			  InputOnly, (Visual *)CopyFromParent,
+			  0L, (XSetWindowAttributes *)NULL);
+    TRACE(("\tprobewindow = %lx\n", probe));
+
+    MyAddEventHandler(dpy, probe, KeyPress, KeyPressMask,
+		      ClientKey, (XtPointer)ccw);
+    MyAddEventHandler(dpy, probe, KeyRelease, KeyReleaseMask,
+		      ClientKey, (XtPointer)ccw);
+
+    ccw->ccontrol.probewindow = probe;
+
+    XMapWindow(dpy, probe);
+}
+
+static void
+SelectFocusKeyEvent(ccw)
+ConversionControlWidget ccw;
+{
+    Display *dpy = XtDisplay(ccw);
+    Window win = ccw->ccontrol.focuswindow;
+
+    MyAddEventHandler(dpy, win, KeyPress, KeyPressMask,
+		      ClientKey, (XtPointer)ccw);
+    MyAddEventHandler(dpy, win, KeyRelease, KeyReleaseMask,
+		      ClientKey, (XtPointer)ccw);
+}
+
+static void
+UnselectFocusKeyEvent(ccw)
+ConversionControlWidget ccw;
+{
+    Display *dpy = XtDisplay(ccw);
+    Window win = ccw->ccontrol.focuswindow;
+
+    MyRemoveEventHandler(dpy, win, KeyPress, ClientKey, (XtPointer)ccw);
+    MyRemoveEventHandler(dpy, win, KeyRelease, ClientKey, (XtPointer)ccw);
+}
+
+static void
+ClientKey(ev, data)
+XEvent *ev;
+XtPointer data;
+{
+    Widget w = (Widget)data;
+    Cardinal num_params = 0;
+
+    EventToInputObject(w, ev, (String *)NULL, &num_params);
+}
+
+static void
+ClientDead(ev, data)
+XEvent *ev;
+XtPointer data;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)data;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)ccw->core.widget_class;
+
+    if (ev->type != DestroyNotify ||
+	ev->xdestroywindow.window != ccw->ccontrol.clientwindow) return;
+
+    /*
+     * Client window is destroyed.
+     */
+
+    /* remove all event handlers */
+    MyRemoveAllEventHandler(XtDisplay(ccw), ccw->ccontrol.clientwindow);
+    if (ccw->ccontrol.probewindow != None) {
+	/* no need to destroy probewindow. it's already destroyed. */
+	MyRemoveAllEventHandler(XtDisplay(ccw), ccw->ccontrol.probewindow);
+    }
+    if (ccw->ccontrol.eventselectmethod == ESMethodSelectFocus &&
+	ccw->ccontrol.focuswindow != ccw->ccontrol.clientwindow) {
+	MyRemoveAllEventHandler(XtDisplay(ccw), ccw->ccontrol.focuswindow);
+    }
+
+    ccw->ccontrol.oldclientwindow = ccw->ccontrol.clientwindow;
+    ccw->ccontrol.clientwindow = None;
+    ccw->ccontrol.focuswindow = None;
+    ccw->ccontrol.probewindow = None;
+    ccw->ccontrol.active = False;
+
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNfixNotify, FixCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNendNotify, ConversionEndCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNtextChangeNotify, TextChangeCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNmodeChangeNotify, ModeChangeCallback, (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNselectionControl, SelectionControlCallback,
+		     (XtPointer)ccw);
+    XtRemoveCallback(ccw->ccontrol.inputobj,
+		     XtNauxControl, AuxControlCallback,
+		     (XtPointer)ccw);
+
+    /* call input style dependent finish */
+    (*class->conversionControl_class.Finish)((Widget)ccw);
+
+    /* clear conversion object */
+    ICClearConversion(ccw->ccontrol.inputobj);
+
+    ccw->ccontrol.oldclientwindow = None;
+    XtCallCallbackList((Widget)ccw, ccw->ccontrol.endcallback, (XtPointer)True);
+}
+
+/* ARGSUSED */
+static void
+FixCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    Widget widget = (Widget)client_data;
+    ConversionControlWidget ccw = (ConversionControlWidget)widget;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)widget->core.widget_class;
+    Atom encoding = ccw->ccontrol.textencoding;
+    int format;
+    int length;
+    XtPointer text;
+    CCTextCallbackArg arg;
+
+    ccw->ccontrol.eventused = True;
+
+    if (ICGetConvertedString(w, &encoding, &format, &length, &text) < 0) {
+	return;
+    }
+    arg.encoding = encoding;
+    arg.format = format;
+    arg.length = length;
+    arg.text = text;
+
+    (*class->conversionControl_class.Fix)(widget, &arg);
+
+    XtFree(text);
+}
+
+/* ARGSUSED */
+static void
+ConversionEndCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    ConversionControlWidget ccw = (ConversionControlWidget)client_data;
+
+    ccw->ccontrol.eventused = True;
+    ccw->ccontrol.endnotify = True;
+}
+
+/* ARGSUSED */
+static void
+TextChangeCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    Widget widget = (Widget)client_data;
+    ConversionControlWidget ccw = (ConversionControlWidget)widget;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)widget->core.widget_class;
+    int nsegs = ICNumSegments(ccw->ccontrol.inputobj);
+
+    /*
+     * don't do:
+     *	ccw->ccontrol.eventused = True;
+     * because this callback is often called even if
+     * the text didn't change actually.
+     */
+
+    /*
+     * when num-segments changed from 0 to 1 (or more),
+     * call new-text calllback
+     */
+    if (ccw->ccontrol.notext && nsegs > 0) {
+	XtCallCallbackList((Widget)ccw, ccw->ccontrol.newtextcallback,
+			   (XtPointer)w);
+    }
+    ccw->ccontrol.notext = nsegs == 0;
+
+    (*class->conversionControl_class.TextChange)(widget);
+}
+
+/* ARGSUSED */
+static void
+ModeChangeCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    Widget widget = (Widget)client_data;
+    ConversionControlWidget ccw = (ConversionControlWidget)widget;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)widget->core.widget_class;
+
+    ccw->ccontrol.eventused = True;
+
+    (*class->conversionControl_class.ModeChange)(widget);
+}
+
+/* ARGSUSED */
+static void
+SelectionControlCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    Widget widget = (Widget)client_data;
+    ConversionControlWidget ccw = (ConversionControlWidget)widget;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)widget->core.widget_class;
+    ICSelectionControlArg *arg = (ICSelectionControlArg *)call_data;
+
+    ccw->ccontrol.eventused = True;
+
+    (*class->conversionControl_class.SelectionControl)(widget, arg);
+}
+
+/* ARGSUSED */
+static void
+AuxControlCallback(w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+    Widget widget = (Widget)client_data;
+    ConversionControlWidget ccw = (ConversionControlWidget)widget;
+    ConversionControlWidgetClass class = (ConversionControlWidgetClass)widget->core.widget_class;
+    ICAuxControlArg *arg = (ICAuxControlArg *)call_data;
+
+    ccw->ccontrol.eventused = True;
+
+    (*class->conversionControl_class.AuxControl)(widget, arg);
+}
+
+static void
+WidgetError(w, name, type, msg)
+Widget w;
+String name;
+String type;
+String msg;
+{
+    char buf[512];
+    String params[1];
+    Cardinal num_params;
+
+    params[0] = XtClass(w)->core_class.class_name;
+    num_params = 1;
+
+    (void)sprintf(buf, "%%s: %s", msg);
+
+    XtAppErrorMsg(XtWidgetToApplicationContext(w),
+		  name, type, "WidgetError", buf, params, &num_params);
+}
+
+static void
+WidgetWarning(w, name, type, msg)
+Widget w;
+String name;
+String type;
+String msg;
+{
+    char buf[512];
+    String params[1];
+    Cardinal num_params;
+
+    params[0] = XtClass(w)->core_class.class_name;
+    num_params = 1;
+
+    (void)sprintf(buf, "%%s: %s", msg);
+
+    XtAppWarningMsg(XtWidgetToApplicationContext(w),
+		    name, type, "WidgetError", buf, params, &num_params);
+}