view lib/OverConv.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 e55ccba56891 07a41a882b14
line wrap: on
line source

#ifndef lint
static char *rcsid = "$Id: OverConv.c,v 1.71 1999/05/06 09:07:58 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>
#if XtSpecificationRelease > 4
#include <X11/Xfuncs.h>
#endif
#include "CachedAtom.h"
#include "AsyncErr.h"
#include "OverConvP.h"
#include "InputConv.h"
#include "ConvDisp.h"
#include "CandPanel.h"
#include "AuxPanel.h"
#include "CanvasShel.h"
#include "ICLabel.h"

#define DEBUG_VAR debug_OverTheSpotConversion
#include "DebugPrint.h"

typedef enum { NeedNone, NeedRedraw, NeedReconfig } ResetStatus;

/*- resource table -*/
static XtResource resources[] = {
#define offset(field) XtOffset(OverTheSpotConversionWidget, overthespot.field)
    { XtNspotX, XtCPosition, XtRPosition, sizeof(Position),
	offset(spotx), XtRImmediate, (XtPointer)0 },
    { XtNspotY, XtCPosition, XtRPosition, sizeof(Position),
	offset(spoty), XtRImmediate, (XtPointer)0 },
    { XtNautoSpotForwarding, XtCAutoSpotForwarding, XtRBoolean, sizeof(Boolean),
	offset(spotforwarding), XtRImmediate, (XtPointer)False },
    { XtNlineSpacing, XtCLineSpacing, XtRDimension, sizeof(Dimension),
	offset(linespacing), XtRImmediate, (XtPointer)0 },
    { XtNmodeLocation, XtCModeLocation, XtRModeLocation, sizeof(ModeLocation),
	offset(modelocation), XtRString, "BottomLeft" },
    { XtNshrinkWindow, XtCShrinkWindow, XtRBoolean, sizeof(Boolean),
	offset(shrinkwindow), XtRImmediate, (XtPointer)False },
    { XtNignoreStatusAreaSpec, XtCIgnoreStatusAreaSpec,
	XtRBoolean, sizeof(Boolean),
	offset(ignorestatusarea), XtRImmediate, (XtPointer)False },
    { XtNmodeBorderForeground, XtCModeBorderForeground,
	XtRBoolean, sizeof(Boolean),
	offset(borderforeground), XtRImmediate, (XtPointer)False },
    { XtNuseOverrideShellForMode, XtCUseOverrideShellForMode,
	XtRBoolean, sizeof(Boolean),
	offset(useoverride), XtRImmediate, (XtPointer)False },
    /* changes superclass's default */
    { XtNmappedWhenManaged, XtCMappedWhenManaged, XtRBoolean, sizeof(Boolean),
	XtOffset(OverTheSpotConversionWidget, core.mapped_when_managed),
	XtRImmediate, (XtPointer)False },
#undef offset
};

/*- default translation table -*/
static char translations[] = "<Key>: to-inputobj()";	/* same as superclass's */

/*- declarations of static functions -*/
static void ClassInitialize();
static void Initialize();
static void Destroy();
static Boolean SetValues();

static void ConversionStartup();
static void ChangeAttributes();
static void ChangeFocus();
static void ConversionFinish();

static void CreateDisplayObject();
static void CreateSelectionWidget();
static void CreateModeWidget();
static TextCanvas * CreateTextCanvas();

static void setupTextCanvas();
static ResetStatus resetTextCanvas();
static void setupDisplayObject();
static ResetStatus resetDisplayObject();
static void setupModeWidget();
static ResetStatus resetModeWidget();
static void locateTextCanvasInitial();
static void locateModeWidget();
static void locateTrackingModeWidget();
static void redrawAndReconfigureTextCanvas();

static void UpdateText();
static void UpdateMode();
static void SelectionControl();

static void SelectionStart();
static void locateSelectionPopup();
static void SelectionEnd();
static void SelectionSet();
static void SelectionGet();
static void SelectionMove();
static void ForwardSpot();

static void CreateAuxWidget();
static void AuxControl();
static void AuxStart();
static void locateAuxPopup();
static void AuxEnd();
static void AuxChange();


static void TextRedisplay();

static void SelectionSelected();

static void computeDisplaySegments();
static void recomputeDisplaySegments();
static void computeLastPosition();
static DisplayFragment *computeDisplayFragments();
static int computeWidthAvailable();
static void nextLocation();
static DisplayLocation *findLocation();
static void reconfigureDisplay();
static void updateDisplay();
static void updateDisplaySegment();
static void redrawSegments();

static void adjustDisplay();
static Boolean getAttributeSegmentRange();
static Boolean getInsertingSegmentRange();
static void adjustOffset();

static void eraseCursor();
static void showCursor();
static Boolean exposeCursor();
static void computeCursor();

static void StringToModeLocation();

static void MoveShell();
static Window getToplevelWindow();
static void setTransientFor();
static void setMwmHints();
static void getFocusOffset();
static Boolean intersectRect();
static void unionRect();
static int enoughSpaceForStatus();
static DisplayFragment *allocDisplayFragment();
static void freeDisplayFragments();
static void destroyDisplayFragments();
static void allocDisplaySegments();
static void freeDisplaySegment();
static void clearAllDisplaySegments();
static void copyString();
static void freeString();

/*- composite-extension rec: for enabling non-widget children -*/
static CompositeClassExtensionRec CompositeExtension = {
    /* next_extension		*/	NULL,
    /* record_type		*/	NULLQUARK,
    /* version			*/	XtCompositeExtensionVersion,
    /* record_size		*/	sizeof(CompositeClassExtensionRec),
    /* accept_objects		*/	True,
};

/*- overTheSpotConversionClass record -*/
OverTheSpotConversionClassRec overTheSpotConversionClassRec = {
  { /* core fields */
    /* superclass		*/	(WidgetClass)&conversionControlClassRec,
    /* class_name		*/	"OverTheSpotConversion",
    /* widget_size		*/	sizeof(OverTheSpotConversionRec),
    /* class_initialize		*/	ClassInitialize,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	XtInheritRealize,
    /* actions			*/	NULL,
    /* num_actions		*/	0,
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	Destroy,
    /* resize			*/	XtInheritResize,
    /* 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		*/	UpdateText,
    /* Fix			*/	XtInheritFix,
    /* ModeChange		*/	UpdateMode,
    /* SelectionControl		*/	SelectionControl,
    /* SelectionControl		*/	AuxControl,
  },
  { /* overTheSpotConversion fields */
    /* empty			*/	0
  },
};

WidgetClass overTheSpotConversionWidgetClass = (WidgetClass)&overTheSpotConversionClassRec;

/*
 *+ Convenience macros
 */
#define SPOTX(w)	((w)->overthespot.spotx)
#define SPOTY(w)	((w)->overthespot.spoty)
#define CLAREA(w)	((w)->overthespot.clientarea)
#define FOCUSOFFX(w)	((w)->overthespot.focusoffsetx)
#define FOCUSOFFY(w)	((w)->overthespot.focusoffsety)

/*
 *+ Core class methods
 */

/*- ClassInitialize: add resource converter (string->modelocation) -*/
/* ARGSUSED */
static void
ClassInitialize()
{
    XtAddConverter(XtRString, XtRModeLocation, StringToModeLocation,
		   NULL, 0);
}

/*- Initialize: initialize method -*/
/* ARGSUSED */
static void
Initialize(req, new, args, num_args)
Widget req;
Widget new;
ArgList args;
Cardinal *num_args;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)new;

    ocw->overthespot.background = ocw->core.background_pixel;
    ocw->overthespot.canvaslist = NULL;
    ocw->overthespot.overflowcanvas = NULL;
    ocw->overthespot.dispsegments = NULL;
    ocw->overthespot.numsegments = 0;
    ocw->overthespot.dispsegmentsize = 0;
    ocw->overthespot.candlist = NULL;
    ocw->overthespot.numcands = 0;
    ocw->overthespot.selectionpoppedup = False;
    ocw->overthespot.cursorvisible = False;
    ocw->overthespot.canvascursor = None;
    ocw->overthespot.auxpoppedup = False;

    ocw->overthespot.wm_state =
      CachedInternAtom(XtDisplay(new), "WM_STATE", False);

    /* $B%F%-%9%HI=<($N(B widget $B$O:G=i$NJQ493+;O;~$K:n$k(B */
    CreateDisplayObject(ocw);
    CreateSelectionWidget(ocw);
    CreateAuxWidget(ocw);
    CreateModeWidget(ocw);
}

/*- Destroy: destroy method -*/
static void
Destroy(w)
Widget w;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;

    /* $B%G%#%9%W%l%$%;%0%a%s%H$NNN0h$r2rJ|(B */
    if (ocw->overthespot.dispsegments) {
	DisplaySegment *dsp = ocw->overthespot.dispsegments;
	int i;

	for (i = 0; i < ocw->overthespot.numsegments; i++) {
	    freeString(&dsp[i].seg);
	    destroyDisplayFragments(dsp->fragments);
	}
	XtFree((char *)dsp);
    }

    /* canvaslist $B$NNN0h$r2rJ|(B (canvas widget $B$O(B $B<+F0E*$K(B destroy $B$5$l$k(B */
    if (ocw->overthespot.canvaslist) {
	TextCanvas *p = ocw->overthespot.canvaslist;
	while (p) {
	    TextCanvas *q = p->next;
	    XtFree((char *)p);
	    p = q;
	}
    }
}

/*- SetValues: setvalues method -*/
/* ARGSUSED */
static Boolean
SetValues(cur, req, new, args, num_args)
Widget cur;
Widget req;
Widget new;
ArgList args;
Cardinal *num_args;
{
    /* OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)new; */
    return False;
}

/*
 *+ ConversionControl cass methods
 */

/*- ConversionStartup: class specific conversion startup -*/
static void
ConversionStartup(w, mask, value)
Widget w;
unsigned long mask;
ConversionAttributes *value;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;
    Widget inputobj = ocw->ccontrol.inputobj;
    Window toplevel;

    TRACE(("OverTheSpot:ConversionStartup()\n"));

    /* $BFbIt$N%P%C%U%!$r%/%j%"$9$k(B */
    clearAllDisplaySegments(ocw);

    /* $BJQ49%*%V%8%'%/%H$K%3!<%k%P%C%/$r@_Dj$9$k(B */
    XtAddCallback(inputobj, XtNfixNotify, ForwardSpot, (XtPointer)w);

    if (ocw->overthespot.ignorestatusarea) mask &= ~CAStatusArea;

    setupDisplayObject(ocw, mask, value);
    setupTextCanvas(ocw, mask, value);
    setupModeWidget(ocw, mask, value);

    /* WM_TRANSIENT_FOR $B%W%m%Q%F%#$r@5$7$/%;%C%H$9$k(B */
    toplevel = getToplevelWindow(XtDisplay(w),
				 ocw->ccontrol.clientwindow,
				 ocw->overthespot.wm_state);
    setTransientFor(ocw->overthespot.modeshell, toplevel);
    setTransientFor(ocw->overthespot.selectionshell, toplevel);
    setTransientFor(ocw->overthespot.auxshell, toplevel);

    /* $B%F%-%9%H%-%c%s%P%9$NI=<(0LCV$r7h$a$k(B */
    locateTextCanvasInitial(ocw);

    /* $B%b!<%I$NI=<(0LCV$r7h$a$k(B */
    if (!ocw->overthespot.modelocationspecified) locateModeWidget(ocw);

    /*
     * OverTheSpotConvesion $B$N>l9g!"<+J,<+?H$O%]%C%W%"%C%W$7$J$$$,!"(B
     * $B%P%C%/%(%s%I%?%$%W$N;~$K$O%/%i%$%"%s%H$,$3$N(B widget $B$N(B
     * $B%&%#%s%I%&$KBP$7$F%$%Y%s%H$rAw$k$N$G!"(BRealize $B$@$1$7$F$*$/(B
     * $B$=$N:]!"Bg$-$5$r;XDj$7$J$$$H%5%$%:$,(B 0 $B$K$J$C$F$7$^$&$N$GCm0U$9$k(B
     */
    if (!XtIsRealized(w)) {
	Arg args[2];

	XtSetArg(args[0], XtNwidth, 1);
	XtSetArg(args[1], XtNheight, 1);
	XtSetValues(w, args, 2);
	XtRealizeWidget(w);
    }

    /* $B%b!<%II=<(%-%c%s%P%9$r%]%C%W%"%C%W$9$k(B */
    if (ocw->overthespot.modeshell != NULL) {
	XtPopup(ocw->overthespot.modeshell, XtGrabNone);
    }
}

/*- ChangeAttributes: class specific conversion attribute change routine -*/
/* ARGSUSED */
static void
ChangeAttributes(w, mask, value)
Widget w;
unsigned long mask;
ConversionAttributes *value;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;
    ResetStatus dispres, tcres;

    TRACE(("OverTheSpot:ChangeAttributes()\n"));

    if (ocw->overthespot.ignorestatusarea) mask &= ~CAStatusArea;

    dispres = resetDisplayObject(ocw, mask, value);
    tcres = resetTextCanvas(ocw, mask, value);
    if (dispres == NeedReconfig || tcres == NeedReconfig) {
	redrawAndReconfigureTextCanvas(ocw);
    } else if (dispres == NeedRedraw || tcres == NeedRedraw) {
	TextCanvas *tcp = ocw->overthespot.canvaslist;

	while (tcp != NULL) {
	    if (XtIsRealized(tcp->canvas)) {
		XClearArea(XtDisplay(tcp->canvas), XtWindow(tcp->canvas),
			   0, 0, 0, 0, True);
	    }
	    tcp = tcp->next;
	}
    }

    if (resetModeWidget(ocw, mask, value) != NeedNone &&
	XtIsRealized(ocw->overthespot.modewidget)) {
	XClearArea(XtDisplay(w), XtWindow((Widget)ocw->overthespot.modewidget),
		   0, 0, 0, 0, True);
    }
}

/*- ChangeFocus: class specific conversion attribute change routine -*/
static void
ChangeFocus(w, set)
Widget w;
int set;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;

    TRACE(("OverTheSpot:ChangeFocus()\n"));

    if (ocw->overthespot.modeshell == NULL) return;
    if (set) {
	XtPopup(ocw->overthespot.modeshell, XtGrabNone);
    } else {
	XtPopdown(ocw->overthespot.modeshell);
    }
}

/*- ConversionFinish: class specific conversion finish -*/
/* ARGSUSED */
static void
ConversionFinish(w)
Widget w;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;
    Widget inputobj = ocw->ccontrol.inputobj;
    TextCanvas *tcp = ocw->overthespot.canvaslist;
    XAEHandle h;

    /* $BJQ49%*%V%8%'%/%H$N%3!<%k%P%C%/$r>C$9(B */
    XtRemoveCallback(inputobj, XtNfixNotify, ForwardSpot, (XtPointer)w);

    /* Popdown and unrealize textcanvases
     *	we must be careful here. if clientwindow are destroyed,
     *	the text canvases are also destroyed.
     *	we have to popdown and unrealize canvas widgets, but if
     *	they are destroyed, BadWindow error will be generated.
     *	so we must set own error handler that ignores errors.
     */
    h = XAESetIgnoreErrors(XtDisplay(w));
    while (tcp != NULL) {
	if (tcp->poppedup) XtPopdown(tcp->canvas);
	XtUnrealizeWidget(tcp->canvas);
	/* XtVaSetValues(tcp->canvas, XtNcursor, None, NULL); */
	tcp->poppedup = False;
	tcp = tcp->next;
    }
    /* Popdown mode widget */
    if (ocw->overthespot.modeshell != NULL) {
	XtPopdown(ocw->overthespot.modeshell);
    }
    if (ocw->overthespot.modeshell == ocw->overthespot.modeshell_fix) {
	XtUnrealizeWidget(ocw->overthespot.modeshell);
    }
    XAEUnset(h);

    /* Popdown selection popup (if popped-up) */
    if (ocw->overthespot.selectionpoppedup) {
	XtPopdown(ocw->overthespot.selectionshell);
	ocw->overthespot.selectionpoppedup = False;
    }
    if (ocw->overthespot.auxpoppedup) {
	XtPopdown(ocw->overthespot.auxshell);
	ocw->overthespot.auxpoppedup = False;
    }
}

/*
 *+ sub-widget creation
 */

/*- CreateDisplayObject: create display object for text drawing -*/
static void
CreateDisplayObject(ocw)
OverTheSpotConversionWidget ocw;
{
    Widget dispobj;

    dispobj = XtCreateWidget("displayObj", ocw->ccontrol.displayobjclass,
			     (Widget)ocw, NULL, 0);

    ocw->overthespot.displayobj = dispobj;
    ocw->overthespot.lineheight = CDLineHeight(dispobj,
					       &ocw->overthespot.ascent);

}

/*- CreateSelectionWidget: create selection widget for selecting candidates -*/
static void
CreateSelectionWidget(ocw)
OverTheSpotConversionWidget ocw;
{
    Widget shell, sel, obj;

    shell = XtVaCreatePopupShell("selectionShell",
				 transientShellWidgetClass,
				 (Widget)ocw,
				 XtNwidth, 1, XtNheight, 1,
				 NULL);
    ocw->overthespot.selectionshell = shell;

    sel = XtCreateManagedWidget("selection", candidatePanelWidgetClass,
				shell, NULL, 0);
    obj = XtCreateWidget("display", ocw->ccontrol.displayobjclass,
			 sel, NULL, 0);
    XtAddCallback(sel, XtNcallback, SelectionSelected, (Widget)ocw);
    XtInstallAccelerators(sel, (Widget)ocw);

    ocw->overthespot.selectionwidget = sel;
    ocw->overthespot.selectiondisplayobj = obj;
}

/*- CreateAuxWidget: create auxiliary widget for displaying auxiliary data -*/
static void
CreateAuxWidget(ocw)
OverTheSpotConversionWidget ocw;
{
    Widget shell, sel, obj;

    shell = XtVaCreatePopupShell("auxShell",
				 transientShellWidgetClass,
				 (Widget)ocw,
				 XtNwidth, 1, XtNheight, 1,
				 XtNallowShellResize, True,
				 NULL);
    ocw->overthespot.auxshell = shell;

    sel = XtCreateManagedWidget("aux", auxPanelWidgetClass,
				shell, NULL, 0);
    obj = XtCreateWidget("display", ocw->ccontrol.displayobjclass,
			 sel, NULL, 0);
    XtAddCallback(sel, XtNcallback, SelectionSelected, (Widget)ocw);
    XtInstallAccelerators(sel, (Widget)ocw);

    ocw->overthespot.auxwidget = sel;
    ocw->overthespot.auxdisplayobj = obj;
}

/*- CreateModeWidget: create mode displaying widget -*/
static void
CreateModeWidget(ocw)
OverTheSpotConversionWidget ocw;
{
    Widget shell, w, obj;

    TRACE(("CreateModeWidget()\n"));

    /* create fixed widget */
    shell = XtCreatePopupShell("modeShell", adoptedShellWidgetClass,
			       (Widget)ocw, NULL, 0);
    XtVaGetValues(shell, XtNborderWidth, &ocw->overthespot.saved_bw, NULL);
    w = XtCreateManagedWidget("mode", icLabelWidgetClass, shell, NULL, 0);
    obj = XtCreateWidget("display", ocw->ccontrol.displayobjclass, w,
			 NULL, 0);
    XtInstallAccelerators(shell, (Widget)ocw);
    XtInstallAccelerators(w, (Widget)ocw);
    ocw->overthespot.modeshell_fix = shell;
    ocw->overthespot.modewidget_fix = w;
    ocw->overthespot.modedisplayobj_fix = obj;

    /* create floating widget */
    if (ocw->overthespot.useoverride) {
	shell = XtCreatePopupShell("modeShell", overrideShellWidgetClass,
				   (Widget)ocw, NULL, 0);
    } else {
	shell = XtCreatePopupShell("modeShell", transientShellWidgetClass,
				   (Widget)ocw, NULL, 0);
    }
    w = XtCreateManagedWidget("mode", icLabelWidgetClass, shell, NULL, 0);
    obj = XtCreateWidget("display", ocw->ccontrol.displayobjclass, w,
			 NULL, 0);
    XtInstallAccelerators(shell, (Widget)ocw);
    XtInstallAccelerators(w, (Widget)ocw);
    ocw->overthespot.modeshell_float = shell;
    ocw->overthespot.modewidget_float = w;
    ocw->overthespot.modedisplayobj_float = obj;

    /* set mwm hints for the shell */
    setMwmHints(shell);
}

/*- CreateTextCanvas: create a text canvas -*/
static TextCanvas *
CreateTextCanvas(ocw)
OverTheSpotConversionWidget ocw;
{
    TextCanvas *tcp;

    tcp = XtNew(TextCanvas);
    tcp->x = 0;
    tcp->y = 0;
    tcp->poppedup = False;
    tcp->next = NULL;
    tcp->canvas = XtVaCreatePopupShell("text",
				       canvasShellWidgetClass,
				       (Widget)ocw,
				       XtNcolormap,
				           ocw->overthespot.colormap,
				       XtNbackground,
				           ocw->overthespot.background,
				       XtNparentWindow,
				           ocw->ccontrol.clientwindow,
				       XtNoverrideRedirect, True,
				       NULL);	/* XXX for now XXX */
    XtAddCallback(tcp->canvas, XtNexposeCallback, TextRedisplay, (XtPointer)ocw);
    XtInstallAccelerators(tcp->canvas, (Widget)ocw);

    return tcp;
}


/*
 *+ subwidget configuration
 */

/*- setupTextCanvas: do text canvas configuration on conversion startup -*/
static void
setupTextCanvas(ocw, mask, value)
OverTheSpotConversionWidget ocw;
unsigned long mask;
ConversionAttributes *value;
{
    TRACE(("setupTextCanvas(mask=0x%lx)\n", mask));

    getFocusOffset(ocw);

    if (mask & CAClientArea) {
	CLAREA(ocw) = value->clientarea;
    } else {
	/* default */
	CLAREA(ocw).x = 0;
	CLAREA(ocw).y = 0;
	CLAREA(ocw).width = ocw->ccontrol.focus_attr.width;
	CLAREA(ocw).height = ocw->ccontrol.focus_attr.height;
    }
    CLAREA(ocw).x += FOCUSOFFX(ocw);
    CLAREA(ocw).y += FOCUSOFFY(ocw);

    TRACE(("\tclientarea: (%d,%d)-(%d,%d)\n",CLAREA(ocw).x,CLAREA(ocw).y,CLAREA(ocw).width,CLAREA(ocw).height));
    if (mask & CASpotLocation) {
	SPOTX(ocw) = value->spotx + FOCUSOFFX(ocw);
	SPOTY(ocw) = value->spoty + FOCUSOFFY(ocw);
    } else {
	/* default */
	SPOTX(ocw) = CLAREA(ocw).x;
	SPOTY(ocw) = CLAREA(ocw).y + ocw->overthespot.ascent;
    }
    TRACE(("\tspotlocation: (%d,%d)\n",SPOTX(ocw),SPOTY(ocw)));

    if (mask & CALineSpacing) {
	ocw->overthespot.linespacing = value->linespacing;
	if (ocw->overthespot.linespacing == 0) {
	    DPRINT(("\tspecified linespacing is 0. reset to default\n"));
	    ocw->overthespot.linespacing = ocw->overthespot.lineheight;
	}
    } else {
	ocw->overthespot.linespacing = ocw->overthespot.lineheight;
    }
    if (mask & CAColormap) {
	ocw->overthespot.colormap = value->colormap;
    } else {
	ocw->overthespot.colormap = DefaultColormapOfScreen(XtScreen((Widget)ocw));
    }
    if (mask & CAColor) {
	ocw->overthespot.background = value->background;
    } else {
	/* default */
	ocw->overthespot.background = ocw->core.background_pixel;
    }
    if (mask & CACursor) {
	ocw->overthespot.canvascursor = value->cursor;
    } else {
	ocw->overthespot.canvascursor = None;
    }

    if (ocw->overthespot.canvaslist == NULL) {
	ocw->overthespot.canvaslist = CreateTextCanvas(ocw);
    } else {
	TextCanvas *tcp = ocw->overthespot.canvaslist;
	while (tcp != NULL) {
	    XtVaSetValues(tcp->canvas,
			  XtNcolormap, ocw->overthespot.colormap,
			  XtNbackground, ocw->overthespot.background,
			  XtNparentWindow, ocw->ccontrol.clientwindow,
			  XtNcursor, ocw->overthespot.canvascursor,
			  NULL);
	    tcp = tcp->next;
	}
    }
}

/*- resetTextCanvas: do text canvas reconfiguration on attribute change -*/
static ResetStatus
resetTextCanvas(ocw, mask, value)
OverTheSpotConversionWidget ocw;
unsigned long mask;
ConversionAttributes *value;
{
    ResetStatus redraw = NeedNone;

    if (mask & (CAColormap|CAColor|CACursor)) {
	Arg args[3];
	Cardinal i = 0;
	if (mask & CAColormap &&
	    value->colormap != ocw->overthespot.colormap) {
	    ocw->overthespot.colormap = value->colormap;
	    XtSetArg(args[i], XtNcolormap, value->colormap); i++;
	}
	if (mask & CAColor &&
	    value->background != ocw->overthespot.background) {
	    ocw->overthespot.background = value->background;
	    XtSetArg(args[i], XtNbackground, value->background); i++;
	}
	if (mask & CACursor &&
	    value->cursor != ocw->overthespot.canvascursor) {
	    ocw->overthespot.canvascursor = value->cursor;
	    XtSetArg(args[i], XtNcursor, value->cursor); i++;
	}
	if (i > 0) {
	    TextCanvas *tcp = ocw->overthespot.canvaslist;

	    while (tcp != NULL) {
		XtSetValues(tcp->canvas, args, i);
		tcp = tcp->next;
	    }
	    redraw = NeedRedraw;
	}
    }
    if (mask & CAFocusWindow) {
	getFocusOffset(ocw);
	redraw = NeedReconfig;
    }
    if (mask & CAClientArea) {
	if (value->clientarea.x + FOCUSOFFX(ocw) != CLAREA(ocw).x ||
	    value->clientarea.y + FOCUSOFFY(ocw) != CLAREA(ocw).y ||
	    value->clientarea.width != CLAREA(ocw).width ||
	    value->clientarea.height != CLAREA(ocw).height) {
	    CLAREA(ocw) = value->clientarea;
	    CLAREA(ocw).x += FOCUSOFFX(ocw);
	    CLAREA(ocw).y += FOCUSOFFY(ocw);
	    redraw = NeedReconfig;
	}
    } else if (mask & CAFocusWindow) {
	CLAREA(ocw).x = FOCUSOFFX(ocw);
	CLAREA(ocw).y = FOCUSOFFY(ocw);
	CLAREA(ocw).width = ocw->ccontrol.focus_attr.width;
	CLAREA(ocw).height = ocw->ccontrol.focus_attr.height;
    }
    if (mask & CASpotLocation) {
	if (value->spotx + FOCUSOFFX(ocw) != SPOTX(ocw) ||
	    value->spoty + FOCUSOFFY(ocw) != SPOTY(ocw)) {
	    SPOTX(ocw) = value->spotx + FOCUSOFFX(ocw);
	    SPOTY(ocw) = value->spoty + FOCUSOFFY(ocw);
	    redraw = NeedReconfig;
	}
    } else if (mask & CAFocusWindow) {
	SPOTX(ocw) = CLAREA(ocw).x;
	SPOTY(ocw) = CLAREA(ocw).y + ocw->overthespot.ascent;
    }
    if (mask & CALineSpacing) {
	if (value->linespacing != ocw->overthespot.linespacing &&
	    value->linespacing != 0) {
	    ocw->overthespot.linespacing = value->linespacing;
	    redraw = NeedReconfig;
	}
    } else if (mask & CAFonts) {
	ocw->overthespot.linespacing = ocw->overthespot.lineheight;
	redraw = NeedReconfig;
    }

    return redraw;
}

/*- setupDisplayObject: do displayobj configuration on conversion startup -*/
static void
setupDisplayObject(ocw, mask, value)
OverTheSpotConversionWidget ocw;
unsigned long mask;
ConversionAttributes *value;
{
    Widget dispobj = ocw->overthespot.displayobj;

    TRACE(("setupDisplayObject()\n"));

    /*
     * order is important. we must set fonts BEFORE anything else,
     * because it is possible that the fonts previously set in the
     * display object no longer exist, and if so, that causes BadFont
     * error when changing GCs.
     */

    if (mask & CAFonts) {
	TRACE(("\tchanging fonts...\n"));
	CDSetFonts(dispobj, value->fonts, value->num_fonts);
    } else {
	/* reset to default */
	CDSetFonts(dispobj, (XFontStruct **)NULL, 0);
    }
    if (mask & CAColor) {
	TRACE(("\tchanging colors...\n"));
	XtVaSetValues(dispobj,
		      XtNforeground, value->foreground,
		      XtNbackground, value->background,
		      NULL);
    }
    ocw->overthespot.lineheight = CDLineHeight(dispobj,
					       &ocw->overthespot.ascent);
}

/*- resetDisplayObject: do displayobj reconfiguration on attribute change -*/
static ResetStatus
resetDisplayObject(ocw, mask, value)
OverTheSpotConversionWidget ocw;
unsigned long mask;
ConversionAttributes *value;
{
    Widget dispobj = ocw->overthespot.displayobj;
    ResetStatus redraw = NeedNone;

    TRACE(("resetDisplayObject()\n"));

    if (mask & CAColor) {
	TRACE(("\tchanging colors...\n"));
	XtVaSetValues(dispobj,
		      XtNforeground, value->foreground,
		      XtNbackground, value->background,
		      NULL);
	redraw = NeedRedraw;
    }
    if (mask & CAFonts) {
	TRACE(("\tchanging fonts...\n"));
	CDSetFonts(dispobj, value->fonts, value->num_fonts);
	ocw->overthespot.lineheight = CDLineHeight(dispobj,
						   &ocw->overthespot.ascent);
	redraw = NeedReconfig;
    }

    return redraw;
}

/*- setupModeWidget: do mode widget configuration on conversion startup -*/
static void
setupModeWidget(ocw, mask, value)
OverTheSpotConversionWidget ocw;
unsigned long mask;
ConversionAttributes *value;
{
    Widget inputobj = ocw->ccontrol.inputobj;
    Widget dispobj;
    Widget mode;
    Arg modeargs[10];
    Arg shellargs[15];
    Cardinal i = 0, j = 0;

    TRACE(("setupModeWidget()\n"));

    /* choose appropriate widgets */
    if (mask & CAStatusArea) {
	/* use fixed modedisplay */
	ocw->overthespot.modeshell = ocw->overthespot.modeshell_fix;
	ocw->overthespot.modewidget = ocw->overthespot.modewidget_fix;
	ocw->overthespot.modedisplayobj = ocw->overthespot.modedisplayobj_fix;
	XtSetArg(shellargs[j], XtNparentWindow, ocw->ccontrol.clientwindow); j++;
	XtSetArg(shellargs[j], XtNborderWidth, 0); j++;
	XtSetArg(shellargs[j], XtNallowShellResize, False); j++;
	XtSetArg(shellargs[j], XtNx, value->statusarea.x); j++;
	XtSetArg(shellargs[j], XtNy, value->statusarea.y); j++;
	XtSetArg(shellargs[j], XtNwidth, value->statusarea.width); j++;
	XtSetArg(shellargs[j], XtNheight, value->statusarea.height); j++;
	ocw->overthespot.modelocationspecified = True;
    } else if (ocw->overthespot.modelocation == ModeTrackText &&
	       enoughSpaceForStatus(ocw)) {
	ocw->overthespot.modeshell = ocw->overthespot.modeshell_fix;
	ocw->overthespot.modewidget = ocw->overthespot.modewidget_fix;
	ocw->overthespot.modedisplayobj = ocw->overthespot.modedisplayobj_fix;
	ocw->overthespot.modelocationspecified = False;
	XtSetArg(shellargs[j], XtNparentWindow, ocw->ccontrol.clientwindow); j++;
	XtSetArg(shellargs[j], XtNallowShellResize, True); j++;
	XtSetArg(shellargs[j], XtNborderWidth, ocw->overthespot.saved_bw); j++;
    } else if (ocw->overthespot.modelocation == ModeNone) {
	ocw->overthespot.modeshell = NULL;
	ocw->overthespot.modewidget = NULL;
	ocw->overthespot.modedisplayobj = NULL;
	ocw->overthespot.modelocationspecified = False;
	return;
    } else {
	/* use floating modedisplay */
	ocw->overthespot.modeshell = ocw->overthespot.modeshell_float;
	ocw->overthespot.modewidget = ocw->overthespot.modewidget_float;
	ocw->overthespot.modedisplayobj = ocw->overthespot.modedisplayobj_float;
	ocw->overthespot.modelocationspecified = False;
    }

    mode = ocw->overthespot.modewidget;
    dispobj = ocw->overthespot.modedisplayobj;

    XtSetArg(modeargs[i], XtNlabel, ICGetMode(inputobj)); i++;
    if (mask & CAColormap) {
	XtSetArg(modeargs[i], XtNcolormap, value->colormap); i++;
    }
    /* ignore background_pixmap... */

    /*
     * order of changing display object resources is important.
     * see comment in setupDisplayObject() for details.
     */
    if (mask & CAStatusFonts) {
	TRACE(("\tchanging fonts...\n"));
	CDSetFonts(dispobj, value->status_fonts, value->num_status_fonts);
    } else {
	/* reset to default */
	CDSetFonts(dispobj, (XFontStruct **)NULL, 0);
    }
    if (mask & CAColor) {
	TRACE(("\tchanging colors...\n"));
	XtVaSetValues(dispobj,
		      XtNforeground, value->foreground,
		      XtNbackground, value->background,
		      NULL);
	XtSetArg(modeargs[i], XtNbackground, value->background); i++;
	if (ocw->overthespot.borderforeground) {
	    XtSetArg(shellargs[j], XtNborderColor, value->foreground); j++;
	}
    } else {
	XtSetArg(modeargs[i], XtNbackground, ocw->overthespot.background); i++;
    }

    XtSetValues(mode, modeargs, i);
    ICLRecomputeSize(mode);

    if (!(mask & CAStatusArea)) {
	/*
	 * force shell to resize.
	 * it is because Shell doesn't honor its child's dimension
	 * at second (or later)  realization.
	 */
	XtSetArg(shellargs[j], XtNwidth, mode->core.width); j++;
	XtSetArg(shellargs[j], XtNheight, mode->core.height); j++;
    }
    XtSetValues(ocw->overthespot.modeshell, shellargs, j);
}

/*- resetModeWidget: do mode widget reconfiguration on attribute change -*/
static ResetStatus
resetModeWidget(ocw, mask, value)
OverTheSpotConversionWidget ocw;
unsigned long mask;
ConversionAttributes *value;
{
    Widget mode = ocw->overthespot.modewidget;
    Widget dispobj = ocw->overthespot.modedisplayobj;
    ResetStatus redraw = NeedNone;

    TRACE(("resetModeWidget()\n"));

    if (ocw->overthespot.modeshell == NULL) return NeedNone;

    if (mask & CAStatusArea) {
	if (ocw->overthespot.modelocationspecified &&
	    ocw->overthespot.modeshell == ocw->overthespot.modeshell_fix) {
	    XtVaSetValues(ocw->overthespot.modeshell,
			  XtNx, value->statusarea.x,
			  XtNy, value->statusarea.y,
			  XtNwidth, value->statusarea.width,
			  XtNheight, value->statusarea.height,
			  NULL);
	} /* else ignore... */
    }

    if (mask & CAColormap) {
	XtVaSetValues(mode, XtNcolormap, value->colormap, NULL);
    }

    if (mask & CAColor) {
	TRACE(("\tchanging colors...\n"));
	XtVaSetValues(dispobj,
		      XtNforeground, value->foreground,
		      XtNbackground, value->background,
		      NULL);
	XtVaSetValues(mode, XtNbackground, value->background, NULL);
	if (ocw->overthespot.borderforeground) {
	    XtVaSetValues(ocw->overthespot.modeshell,
			  XtNborderColor, value->foreground,
			  NULL);
	}
	redraw = NeedRedraw;
    }
    if (mask & CAStatusFonts) {
	TRACE(("\tchanging fonts...\n"));
	CDSetFonts(dispobj, value->status_fonts, value->num_status_fonts);
	ICLRecomputeSize(mode);
	redraw = NeedRedraw;
    }

    return redraw;
}

/*- locateTextCanvasInitial: put the text canvas at the initial position -*/
static void
locateTextCanvasInitial(ocw)
OverTheSpotConversionWidget ocw;
{
    TextCanvas *tcp = ocw->overthespot.canvaslist;

    tcp->x = SPOTX(ocw);
    tcp->y = SPOTY(ocw) - ocw->overthespot.ascent;
}

/*- locateModeWidget: put the mode widget at the initial position -*/
static void
locateModeWidget(ocw)
OverTheSpotConversionWidget ocw;
{
    Position x, y;
    Widget modewidget = ocw->overthespot.modewidget;
    Widget modeshell = ocw->overthespot.modeshell;
    int rootx, rooty;
    Window child;

    if (modeshell == ocw->overthespot.modeshell_fix) {
	/* must be tracking text type */
	locateTrackingModeWidget(ocw, True, 0, 0);
	return;
    }

    switch (ocw->overthespot.modelocation) {
    case ModeTopLeft:
	x = 0;
	y = -(modewidget->core.height + modeshell->core.border_width * 2);
	y -= ocw->ccontrol.titlebarheight;
	break;
    case ModeTopRight:
	x = ocw->ccontrol.client_attr.width - modewidget->core.width + modeshell->core.border_width * 2;
	y = -(modewidget->core.height + modeshell->core.border_width * 2);
	y -= ocw->ccontrol.titlebarheight;
	break;
    case ModeBottomRight:
	x = ocw->ccontrol.client_attr.width - modewidget->core.width + modeshell->core.border_width * 2;
	y = ocw->ccontrol.client_attr.height;
	break;
    case ModeTrackText:	/* in case of insufficient space in the client area */
	x = CLAREA(ocw).x;
	y = CLAREA(ocw).y + CLAREA(ocw).height + 2;
	break;
    case ModeBottomLeft:
	x = 0;
	y = ocw->ccontrol.client_attr.height;
	break;
    default:
	/* ModeNone */
	return;
    }

    (void)XTranslateCoordinates(XtDisplay(ocw), ocw->ccontrol.clientwindow,
				RootWindowOfScreen(XtScreen(ocw)),
				x, y, &rootx, &rooty, &child);
    MoveShell(ocw->overthespot.modeshell, rootx, rooty);
}

/*- locateTrackingModeWidget: put the tracking text type mode widget at appropriate position */
static void
locateTrackingModeWidget(ocw, initial, x, y)
OverTheSpotConversionWidget ocw;
Boolean initial;
Position x;
Position y;
{
    Widget modewidget = ocw->overthespot.modewidget;
    Widget modeshell = ocw->overthespot.modeshell;
    Dimension width, height;
    XRectangle *clarea = &CLAREA(ocw);
    static Position lastx, lasty;

    if (initial) {
	x = SPOTX(ocw);
	y = SPOTY(ocw) - ocw->overthespot.ascent
		+ ocw->overthespot.lineheight;
    } else if (x == lastx && y == lasty) {
	return;
    }

    lastx = x;
    lasty = y;

    width = modewidget->core.width + modeshell->core.border_width * 2;
    height = modewidget->core.height + modeshell->core.border_width * 2;

    /* adjust x */
    if (x + width > clarea->x + clarea->width) {
	x = clarea->x + clarea->width - width;
    }
    if (x < clarea->x) x = clarea->x;

    /* adjust y */
    if (y + height + 2 <= clarea->y + clarea->height) {
	y += 2;	/* make some (2pixels high) space between text and mode */
    } else if (y + height > clarea->y + clarea->height) {
	Position initx, inity;

	if (initial) {
	    initx = SPOTX(ocw);
	    inity = SPOTY(ocw) - ocw->overthespot.ascent;
	} else {
	    TextCanvas *tcp = ocw->overthespot.canvaslist;
	    initx = tcp->x;
	    inity = tcp->y;
	}
	if (inity - height > clarea->y) {
	    y = inity - height;
	} else if (x + width < initx) {
	    y = inity - modeshell->core.border_width * 2;
	} else if (clarea->x + width < initx) {
	    x = initx - width;
	    y = inity - modeshell->core.border_width * 2;
	} else {
	    x = initx - width;
	    y = inity - height;
	}
	if (y < clarea->y) y = clarea->y;
    }
    XtMoveWidget(modeshell, x, y);
}

/*- redrawAndReconfigureTextCanvas: redraw & reconfigure text canvas -*/
static void
redrawAndReconfigureTextCanvas(ocw)
OverTheSpotConversionWidget ocw;
{
    TextCanvas *tcp = ocw->overthespot.canvaslist;

    TRACE(("OverTheSpotConversion:redrawAndReconfigureTextCanvas()\n"));

    /* popdown and clear all canvases */
    while (tcp != NULL) {
	if (tcp->poppedup) XtPopdown(tcp->canvas);
	tcp->poppedup = False;
	if (XtIsRealized(tcp->canvas)) {
	    XClearArea(XtDisplay(tcp->canvas), XtWindow(tcp->canvas),
		       0, 0, 0, 0, True);
	}
	tcp = tcp->next;
    }
    locateTextCanvasInitial(ocw);
    recomputeDisplaySegments(ocw);
    computeCursor(ocw);
    reconfigureDisplay(ocw);
}

/*
 *+ inputobject callback
 */

/*- UpdateText: update text -*/
static void
UpdateText(w)
Widget w;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;

    TRACE(("OverTheSpotConversion:UpdateText()\n"));
    eraseCursor(ocw);
    computeDisplaySegments(ocw);
    computeCursor(ocw);
    reconfigureDisplay(ocw);
    updateDisplay(ocw);
    showCursor(ocw);
}

/*- UpdateMode: update mode -*/
static void
UpdateMode(w)
Widget w;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;

    if (ocw->overthespot.modewidget == NULL) return;

    XtVaSetValues(ocw->overthespot.modewidget,
		  XtNlabel, ICGetMode(ocw->ccontrol.inputobj),
		  NULL);
#ifdef notdef
    /* a hack... */
    if (ocw->overthespot.modeshell == ocw->overthespot.modeshell_float &&
	XtIsRealized(ocw->overthespot.modeshell)) {
	XRaiseWindow(XtDisplay(ocw->overthespot.modeshell),
		     XtWindow(ocw->overthespot.modeshell));
    }
#endif
}

/*- SelectionControl: selection control -*/
static void
SelectionControl(w, arg)
Widget w;
ICSelectionControlArg *arg;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;

    switch (arg->command) {
    case ICSelectionStart:
	SelectionStart(ocw, arg->u.selection_kind);
	break;
    case ICSelectionEnd:
	SelectionEnd(ocw, &arg->u.current_item);
	break;
    case ICSelectionSet:
	SelectionSet(ocw, arg->u.current_item);
	break;
    case ICSelectionMove:
	SelectionMove(ocw, arg->u.dir);
	break;
    case ICSelectionGet:
	SelectionGet(ocw, &arg->u.current_item);
	break;
    default:
	XtAppWarning(XtWidgetToApplicationContext(w),
		     "OverTheSpotConversion: unknown selection control command");
	break;
    }
}

/*- SelectionStart: selection startup -*/
/* ARGSUSED */
static void
SelectionStart(ocw, kind)
OverTheSpotConversionWidget ocw;
int kind;
{
    Cardinal ncand;

    TRACE(("OverTheSpotConversion:SelectionStart()\n"));
    if (ocw->overthespot.selectionpoppedup) {
	DPRINT(("\tselection already started -- ignored\n"));
	return;
    }

    ocw->overthespot.candlist = ICGetItemList(ocw->ccontrol.inputobj, &ncand);
    ocw->overthespot.numcands = ncand;

    TRACE(("\tnumcands=%d\n", ocw->overthespot.numcands));
    CPanelSetList(ocw->overthespot.selectionwidget,
		  ocw->overthespot.candlist,
		  ocw->overthespot.numcands, 0, True);

    locateSelectionPopup(ocw);
    XtPopup(ocw->overthespot.selectionshell, XtGrabNone);
    ocw->overthespot.selectionpoppedup = True;
}

/*- locateSelectionPopup: put selection popup at an appropriate position -*/
static void
locateSelectionPopup(ocw)
OverTheSpotConversionWidget ocw;
{
    Position x, y;
    int clx, cly;
    Dimension dpyWidth, dpyHeight;
    Widget panel = ocw->overthespot.selectionwidget;
    Widget shell = ocw->overthespot.selectionshell;
    Window junk;
    int barheight = ocw->ccontrol.titlebarheight;

    (void)XTranslateCoordinates(XtDisplay(ocw),
				ocw->ccontrol.clientwindow,
				RootWindowOfScreen(XtScreen(ocw)),
				0, 0, &clx, &cly, &junk);

    if (ocw->overthespot.numsegments > 0) {
	DisplayLocation lastp;
	DisplaySegment *dsp = ocw->overthespot.dispsegments;
	int i;
	int offset = 0;

	/* find current segment. if not found, use last segment */
	for (i = 0; i < ocw->overthespot.numsegments - 1; i++) {
	    if (dsp[i].seg.attr & ICAttrCurrentSegment) break;
	}

	computeLastPosition(dsp[i].fragments, &lastp);
	if (lastp.canvas == ocw->overthespot.overflowcanvas) {
	    offset = ocw->overthespot.overflowoffset;
	}
	x = clx + lastp.canvas->x + lastp.x
	  - panel->core.width / 2 + offset;
	y = cly + lastp.canvas->y + lastp.y + ocw->overthespot.lineheight;
    } else {
	x = clx + SPOTX(ocw) - panel->core.width / 2;
	y = cly + SPOTY(ocw);
    }

    dpyWidth = WidthOfScreen(XtScreen(shell));
    dpyHeight = HeightOfScreen(XtScreen(shell));

    if (x + panel->core.width > (int)dpyWidth) x = dpyWidth - panel->core.width;
    if (x < 0) x = 0;
    if (y + panel->core.height + barheight > (int)dpyHeight) {
	y = cly + SPOTY(ocw) - panel->core.height - barheight;
	if (y < 0) y = dpyHeight - panel->core.height - barheight;
    }
    MoveShell(shell, x, y);
}

/*- SelectionEnd: selection finish -*/
static void
SelectionEnd(ocw, current)
OverTheSpotConversionWidget ocw;
int *current;
{
    TRACE(("OverTheSpotConversion:SelectionEnd()\n"));

    if (!ocw->overthespot.selectionpoppedup) {	/* for safe */
	TRACE(("\tnot in selection mode -- ignored\n"));
	return;
    }

    XtVaGetValues(ocw->overthespot.selectionwidget,
		  XtNcurrentItem, current,
		  NULL);

    XtPopdown(ocw->overthespot.selectionshell);

    ocw->overthespot.selectionpoppedup = False;
}

/*- SelectionSet: set current selection item -*/
static void
SelectionSet(ocw, current)
OverTheSpotConversionWidget ocw;
int current;
{
    TRACE(("OverTheSpotConversion:SelectionSet()\n"));

    if (!ocw->overthespot.selectionpoppedup) {	/* for safe */
	TRACE(("\tnot in selection mode -- ignored\n"));
	return;
    }

    XtVaSetValues(ocw->overthespot.selectionwidget,
		  XtNcurrentItem, current,
		  NULL);
}

/*- SelectionGet: get current selection item -*/
static void
SelectionGet(ocw, current)
OverTheSpotConversionWidget ocw;
int *current;
{
    TRACE(("OverTheSpotConversion:SelectionGet()\n"));

    if (!ocw->overthespot.selectionpoppedup) {	/* for safe */
	TRACE(("\tnot in selection mode -- ignored\n"));
	return;
    }

    XtVaGetValues(ocw->overthespot.selectionwidget,
		  XtNcurrentItem, current,
		  NULL);
}

/*- SelectionMove: move crrent selection to specified direction -*/
static void
SelectionMove(ocw, dir)
OverTheSpotConversionWidget ocw;
int dir;
{
    TRACE(("OverTheSpotConversion:SelectionMove()\n"));

    if (!ocw->overthespot.selectionpoppedup) {	/* for safe */
	TRACE(("\tnot in selection mode -- ignored\n"));
	return;
    }

    CPanelMoveCurrent(ocw->overthespot.selectionwidget, dir);
}

/*- ForwardSpot: forward spot location when text is fixed -*/
/* ARGSUSED */
static void
ForwardSpot(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)client_data;
    DisplaySegment *dsp = ocw->overthespot.dispsegments;
    Cardinal nsegs = ocw->overthespot.numsegments;
    DisplayLocation disploc;

    if (!ocw->overthespot.spotforwarding || nsegs == 0) return;

    /* get next spot location */
    computeLastPosition(dsp[nsegs - 1].fragments, &disploc);

    SPOTX(ocw) = disploc.canvas->x + disploc.x;
    SPOTY(ocw) = disploc.canvas->y + disploc.y + ocw->overthespot.ascent;
    locateTextCanvasInitial(ocw);
}

/*
 * Aux Callback
 */

static void
AuxControl(w, arg)
Widget w;
ICAuxControlArg *arg;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)w;
    String params[1];
    Cardinal num_params;

    switch (arg->command) {
    case ICAuxStart:
	AuxStart(ocw);
	break;
    case ICAuxEnd:
	AuxEnd(ocw);
	break;
    case ICAuxChange:
	AuxChange(ocw);
	break;
    default:
	params[0] = XtClass(w)->core_class.class_name;
	num_params = 1;
	XtAppWarningMsg(XtWidgetToApplicationContext(w),
			"parameterError", "AuxControl", "WidgetError",
			"%s: unknown aux control command",
			params, &num_params);
	break;
    }
}

/* ARGSUSED */
static void
AuxStart(ocw)
OverTheSpotConversionWidget ocw;
{
  ICString *auxstr;
  Cardinal ncand, curseg, cursorpos;
  
  if (ocw->overthespot.auxpoppedup) return;
  
  /* $B%F%-%9%H%3!<%k%P%C%/$N;~$N$h$&$J=hM}$r$9$k(B
     $B$N$O(B AuxPanel.c $B$K$^$+$;$h$&(B */

  auxstr = ICGetAuxSegments(ocw->ccontrol.inputobj,
			    &ncand, &curseg, &cursorpos);

  APanelStart(ocw->overthespot.auxwidget, auxstr, ncand, curseg, cursorpos);

  /* $B%]%C%W%"%C%W$9$k>l=j$r7h$a$k(B */
  locateAuxPopup(ocw, False);
  
  XtPopup(ocw->overthespot.auxshell, XtGrabNone);
  ocw->overthespot.auxpoppedup = True;
}

/* ARGSUSED */
static void
AuxEnd(ocw)
OverTheSpotConversionWidget ocw;
{
  if (!ocw->overthespot.auxpoppedup) return;	/* for safe */

/*  APanelEnd(ocw->overthespot.auxwidget); */

  XtPopdown(ocw->overthespot.auxshell);

  ocw->overthespot.auxpoppedup = False;
}

/* ARGSUSED */
static void
AuxChange(ocw)
OverTheSpotConversionWidget ocw;
{
  Cardinal ncand, curseg, cursorpos;
  ICString *auxstr;

  if (!ocw->overthespot.auxpoppedup) return;	/* for safe */

  auxstr = ICGetAuxSegments(ocw->ccontrol.inputobj,
			    &ncand, &curseg, &cursorpos);

  APanelChange(ocw->overthespot.auxwidget, auxstr, ncand, curseg, cursorpos);

  /* reposition popup shell */
  locateAuxPopup(ocw, True);
}

/*- locateAuxPopup: put aux popup at an appropriate position -*/
static void
locateAuxPopup(ocw, usecurloc)
OverTheSpotConversionWidget ocw;
Boolean usecurloc;	/* use the current location as the default */
{
    int x, y;
    int clx, cly;
    int dpyWidth, dpyHeight;
    Widget panel = ocw->overthespot.auxwidget;
    Widget shell = ocw->overthespot.auxshell;
    Window junk;
    int barheight = ocw->ccontrol.titlebarheight;

    (void)XTranslateCoordinates(XtDisplay(ocw),
				ocw->ccontrol.clientwindow,
				RootWindowOfScreen(XtScreen(ocw)),
				0, 0, &clx, &cly, &junk);

    if (usecurloc) {
	x = shell->core.x;
	y = shell->core.y;
    } else {
	if (ocw->overthespot.numsegments > 0) {
	    DisplayLocation lastp;
	    DisplaySegment *dsp = ocw->overthespot.dispsegments;
	    int i;
	    int offset;
	    
	    /* find current segment. if not found, use last segment */
	    for (i = 0; i < ocw->overthespot.numsegments - 1; i++) {
		if (dsp[i].seg.attr & ICAttrCurrentSegment) break;
	    }
	    
	    computeLastPosition(dsp[i].fragments, &lastp);
	    if (lastp.canvas == ocw->overthespot.overflowcanvas)
		offset = ocw->overthespot.overflowoffset;
	    else
		offset = 0;
	    x = clx + lastp.canvas->x + lastp.x
		- panel->core.width / 2 + offset;
	    y = cly + lastp.canvas->y + lastp.y + ocw->overthespot.lineheight;
	} else {
	    x = clx + ocw->overthespot.spotx - panel->core.width / 2;
	    y = cly + ocw->overthespot.spoty;
	}
    }

    dpyWidth = (int)WidthOfScreen(XtScreen(shell));
    dpyHeight = (int)HeightOfScreen(XtScreen(shell));

    if ((int)(x + panel->core.width) > dpyWidth) x = dpyWidth - panel->core.width;
    if (x < 0) x = 0;
    if ((int)(y + panel->core.height + barheight) > dpyHeight) {
	y = cly + ocw->overthespot.spoty - panel->core.height - barheight;
	if (y < 0) y = dpyHeight - panel->core.height - barheight;
    }
    MoveShell(shell, x, y);
}


/*
 *+ TextCanvas callback
 */

/*- TextRedisplay: redraw text canvas -*/
static void
TextRedisplay(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)client_data;
    XExposeEvent *event = (XExposeEvent *)call_data;
    XRectangle region;
    Boolean cursorredraw;

    TRACE(("OverTheSpotConversion:TextRedisplay()\n"));
    region.x = event->x;
    region.y = event->y;
    region.width = event->width;
    region.height = event->height;

    cursorredraw = exposeCursor(ocw, w, &region);
    redrawSegments(ocw, w, &region);
    if (cursorredraw) showCursor(ocw);
}


/*
 *+ Selection Widget callback
 */

/*- SelectionSelected: selection selected callback -*/
/* ARGSUSED */
static void
SelectionSelected(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
    OverTheSpotConversionWidget ocw = (OverTheSpotConversionWidget)client_data;
    int current = (int)call_data;

    TRACE(("OverTheSpotConversion:SelectionSelected()\n"));
    XtPopdown(ocw->overthespot.selectionshell);
    ocw->overthespot.selectionpoppedup = False;
    ICSelectItem(ocw->ccontrol.inputobj, current);
}


/*
 *+ text drawing functions
 */

/*- computeDisplaySegments: compare old&new text and update segments/fragments -*/
static void
computeDisplaySegments(ocw)
OverTheSpotConversionWidget ocw;
{
    Widget inputobj = ocw->ccontrol.inputobj;
    int nnew = ICNumSegments(inputobj);
    int nold = ocw->overthespot.numsegments;
    ICString *newseg;
    DisplaySegment *dseg;
    DisplayLocation disploc;
    Cardinal nsame;
    int diff;
    int i;

    TRACE(("OverTheSpotConversion:computeDisplaySegments() nnew=%d\n", nnew));
    allocDisplaySegments(ocw, nnew);

    ocw->overthespot.overflowcanvas = NULL;

    disploc.x = disploc.y = 0;
    if (ocw->overthespot.canvaslist == NULL) {
	ocw->overthespot.canvaslist = CreateTextCanvas(ocw);
    }
    disploc.canvas = ocw->overthespot.canvaslist;

    for (i = 0, dseg = ocw->overthespot.dispsegments; i < nnew; i++, dseg++) {
	newseg = ICGetSegment(ocw->ccontrol.inputobj, i);
	if (i >= nold) {
	    copyString(newseg, &dseg->seg);
	    dseg->redrawpos = 0;
	    dseg->fragments = computeDisplayFragments(ocw, newseg, &disploc);
	} else {
	    DisplayFragment *oldfragments, *newfragments;

	    dseg->redrawpos = -1;
	    diff = ICCompareSegment(inputobj, newseg, &dseg->seg, &nsame);
	    if (diff != ICSame ||
		disploc.canvas != dseg->fragments->canvas ||
		disploc.x != dseg->fragments->region.x ||
		disploc.y != dseg->fragments->region.y) {
		oldfragments = dseg->fragments;
		newfragments = computeDisplayFragments(ocw, newseg, &disploc);
		dseg->fragments = newfragments;
	    } else {
		oldfragments = NULL;
		newfragments = dseg->fragments;
		computeLastPosition(newfragments, &disploc);
	    }

	    switch (diff) {
	    case ICSame:
		if (oldfragments == NULL ||
		    oldfragments->canvas == newfragments->canvas &&
		    oldfragments->region.x == newfragments->region.x &&
		    oldfragments->region.y == newfragments->region.y) {
		    dseg->redrawpos = -1;
		} else {
		    dseg->redrawpos = 0;
		}
		break;
	    case ICAttrChanged:
		dseg->redrawpos = 0;
		dseg->seg.attr = newseg->attr;
		break;
	    case ICStringChanged:
		if (oldfragments == NULL ||
		    oldfragments->canvas == newfragments->canvas &&
		    oldfragments->region.x == newfragments->region.x &&
		    oldfragments->region.y == newfragments->region.y) {
		    dseg->redrawpos = nsame;
		} else {
		    dseg->redrawpos = 0;
		}
		freeString(&dseg->seg);
		copyString(newseg, &dseg->seg);
		break;
	    default:
		dseg->redrawpos = 0;
		freeString(&dseg->seg);
		copyString(newseg, &dseg->seg);
		break;
	    }
	    if (oldfragments) freeDisplayFragments(oldfragments);
	}
    }

    for (; i < nold; i++, dseg++) freeDisplaySegment(dseg);

    ocw->overthespot.numsegments = nnew;
}

/*- recomputeDisplaySegments: recompute segments/fragments -*/
static void
recomputeDisplaySegments(ocw)
OverTheSpotConversionWidget ocw;
{
    int nseg = ocw->overthespot.numsegments;
    DisplaySegment *dseg;
    DisplayLocation disploc;
    int i;

    ocw->overthespot.overflowcanvas = NULL;

    disploc.x = disploc.y = 0;
    if (ocw->overthespot.canvaslist == NULL) {
	ocw->overthespot.canvaslist = CreateTextCanvas(ocw);
    }
    disploc.canvas = ocw->overthespot.canvaslist;

    for (i = 0, dseg = ocw->overthespot.dispsegments; i < nseg; i++, dseg++) {
	freeDisplayFragments(dseg->fragments);
	dseg->redrawpos = 0;
	dseg->fragments = computeDisplayFragments(ocw, &dseg->seg, &disploc);
    }
}

/*- computeLastPosition: get last position of the specified fragment -*/
static void
computeLastPosition(fragments, disploc)
DisplayFragment *fragments;
DisplayLocation *disploc;
{
    while (fragments->next != NULL) fragments = fragments->next;
    disploc->canvas = fragments->canvas;
    disploc->x = fragments->region.x + fragments->region.width;
    disploc->y = fragments->region.y;
}

/*- computeDisplayFragments: compute fragment(s) of the specified segment -*/
static DisplayFragment *
computeDisplayFragments(ocw, newseg, disploc)
OverTheSpotConversionWidget ocw;
ICString *newseg;
DisplayLocation *disploc;
{
    int start;
    int nchars;
    Widget dispobj = ocw->overthespot.displayobj;
    DisplayFragment *fragments, *dfp;
    int widthavailable;

    TRACE(("computeDisplayFragments()\n"));
    start = 0;
    fragments = NULL;
    while (start < newseg->nchars) {
	widthavailable = computeWidthAvailable(ocw, disploc);
	nchars = CDMaxChar(dispobj, newseg, start, widthavailable);
	if (nchars == 0) {
	    if (disploc->canvas->x <= CLAREA(ocw).x &&
		disploc->x == 0) {
		/*
		 * specified width is too narrow to display a character.
		 * we force to display at least one character per line.
		 */
		nchars = 1;
	    }
	}
	TRACE(("\twidthavailable=%d, start=%d, maxchar=%d\n", widthavailable, start, nchars));
	if (nchars > 0) {
	    if (fragments == NULL) {
		fragments = dfp = allocDisplayFragment();
	    } else {
		dfp->next = allocDisplayFragment();
		dfp = dfp->next;
	    }
	    dfp->from = start;
	    dfp->nchars = nchars;
	    dfp->canvas = disploc->canvas;
	    dfp->region.x = disploc->x;
	    dfp->region.y = disploc->y;
	    dfp->region.width = CDStringWidth(dispobj, newseg, start,
						    start + nchars);
	    dfp->region.height = ocw->overthespot.lineheight;
	    dfp->next = NULL;

	    disploc->x += dfp->region.width;
	}
	start += nchars;
	if (start < newseg->nchars) nextLocation(ocw, disploc);
    }

    return fragments;
}

/*- computeWidthAvailable: return the width of the current line left for drawing -*/
/* ARGSUSED */
static int
computeWidthAvailable(ocw, disploc)
OverTheSpotConversionWidget ocw;
DisplayLocation *disploc;
{
    XRectangle *cregion = &CLAREA(ocw);

    if (disploc->canvas == ocw->overthespot.overflowcanvas) {
	 /* we pretend this canvas is veeeeeeery wide */
	return 9999;
    }
    return (cregion->x + cregion->width) - (disploc->canvas->x + disploc->x);
}

/*- nextLocation: return the position of the next line -*/
/* ARGSUSED */
static void
nextLocation(ocw, disploc)
OverTheSpotConversionWidget ocw;
DisplayLocation *disploc;
{
    XRectangle *cregion = &CLAREA(ocw);
    Position x, y;

    if (disploc->canvas->y + ocw->overthespot.linespacing * 2 >
	cregion->y + cregion->height) {
	/* no new canvas can create underneath this canvas */
	ocw->overthespot.overflowcanvas = disploc->canvas;
	return;
    }

    if (disploc->canvas->next == NULL) {
	disploc->canvas->next = CreateTextCanvas(ocw);
    }
    x = CLAREA(ocw).x;
    y = disploc->canvas->y + ocw->overthespot.linespacing;
    disploc->canvas = disploc->canvas->next;
    disploc->x = disploc->y = 0;

    disploc->canvas->x = x;
    disploc->canvas->y = y;
}

/*- findLocation: compute the display position of specified char -*/
static DisplayLocation *
findLocation(ocw, dsp, offset, disploc)
OverTheSpotConversionWidget ocw;
DisplaySegment *dsp;
Cardinal offset;
DisplayLocation *disploc;
{
    DisplayFragment *dfp = dsp->fragments;

    while (dfp != NULL) {
	if (dfp->nchars > offset ||
	    dfp->next == NULL && dfp->nchars == offset) {
	    break;
	}
	offset -= dfp->nchars;
	dfp = dfp->next;
    }
    if (dfp == NULL) return NULL;

    disploc->canvas = dfp->canvas;
    disploc->x = dfp->region.x + CDStringWidth(ocw->overthespot.displayobj,
					       &dsp->seg, dfp->from,
					       dfp->from + offset);
    disploc->y = dfp->region.y;

    return disploc;
}

/*- reconfigureDisplay: do reconfiguration of text canvas (resize/popup/popdown) -*/
static void
reconfigureDisplay(ocw)
OverTheSpotConversionWidget ocw;
{
    DisplaySegment *dsp;
    DisplayFragment *dfp;
    TextCanvas *tcp, *lasttcp;
    Boolean shrink = ocw->overthespot.shrinkwindow;
    XRectangle *areap = &CLAREA(ocw);
    int i;

    for (tcp = ocw->overthespot.canvaslist; tcp != NULL; tcp = tcp->next) {
	tcp->maxx = tcp->maxy = 0;
	tcp->shouldpopup = False;
    }

    for (i = 0, dsp = ocw->overthespot.dispsegments; i < ocw->overthespot.numsegments; i++, dsp++) {
	for (dfp = dsp->fragments; dfp != NULL; dfp = dfp->next) {
	    tcp = dfp->canvas;
	    tcp->maxx = dfp->region.x + dfp->region.width;
	    tcp->maxy = dfp->region.y + dfp->region.height;
	    tcp->shouldpopup = True;
	}
    }

    lasttcp = NULL;
    for (tcp = ocw->overthespot.canvaslist; tcp != NULL; tcp = tcp->next) {
	if (tcp->maxx < tcp->canvas->core.width && XtIsRealized(tcp->canvas)) {
	    XClearArea(XtDisplay(tcp->canvas), XtWindow(tcp->canvas),
		       tcp->maxx, 0, 0, 0, False);
	}
	if (tcp->shouldpopup) lasttcp = tcp;
    }

    if (!ocw->overthespot.modelocationspecified &&
	ocw->overthespot.modeshell == ocw->overthespot.modeshell_fix) {
	/* ModeTrackText */
	if (lasttcp == NULL) {
	    locateTrackingModeWidget(ocw, True, 0, 0);
	} else {
	    locateTrackingModeWidget(ocw, False, lasttcp->x,
				     lasttcp->y + lasttcp->maxy);
	}
    }

    if (ocw->overthespot.cursorvisible) {
	DisplayLocation *dlp = &ocw->overthespot.cursorlocation;
	XRectangle cbounds;
	int x;

	tcp = dlp->canvas;
	CDGetCursorBounds(ocw->overthespot.displayobj, &cbounds);
	x = dlp->x +  cbounds.x + cbounds.width;
	if (x > tcp->maxx) tcp->maxx = x;
    }

    if (lasttcp != NULL &&
	lasttcp->x + lasttcp->maxx > areap->x + areap->width) {
	ocw->overthespot.overflowcanvas = lasttcp;
	adjustDisplay(ocw);
    }

    for (tcp = ocw->overthespot.canvaslist; tcp != NULL; tcp = tcp->next) {
	Arg args[2];
	int nargs = 0;
	if (tcp->shouldpopup && tcp->maxx > 0 && tcp->maxy > 0) {
	    if (tcp == ocw->overthespot.overflowcanvas) {
		XtMoveWidget(tcp->canvas,
			     tcp->x + ocw->overthespot.overflowoffset, tcp->y);
	    } else if (tcp->x != tcp->canvas->core.x ||
		       tcp->y != tcp->canvas->core.y) {
		XtMoveWidget(tcp->canvas, tcp->x, tcp->y);
	    }
	    if (shrink || !tcp->poppedup ||
		tcp->maxx > tcp->canvas->core.width) {
		XtSetArg(args[nargs], XtNwidth, tcp->maxx); nargs++;
	    }
	    if (!tcp->poppedup || tcp->maxy > tcp->canvas->core.height) {
		XtSetArg(args[nargs], XtNheight, tcp->maxy); nargs++;
	    }
	    if (nargs > 0) XtSetValues(tcp->canvas, args, nargs);

	    if (!tcp->poppedup) {
		TRACE(("reconfigureDisplay(): canvas popup\n"));
		XtPopup(tcp->canvas, XtGrabNone);
		tcp->poppedup = True;
	    }
	} else {
	    if (tcp->poppedup) {
		TRACE(("reconfigureDisplay(): canvas popdown\n"));
		XtPopdown(tcp->canvas);
		tcp->poppedup = False;
	    }
	}
    }
}

/*- updateDisplay: redraw text (if needed) -*/
static void
updateDisplay(ocw)
OverTheSpotConversionWidget ocw;
{
    Widget dispobj = ocw->overthespot.displayobj;
    DisplaySegment *dsp = ocw->overthespot.dispsegments;
    int i;

    for (i = 0; i < ocw->overthespot.numsegments; i++, dsp++) {
	if (dsp->redrawpos >= 0) {
	    TRACE(("updateDisplaySegment(seg#=%d)\n", i));
	    updateDisplaySegment(dispobj, dsp);
	}
    }
}

/*- updateDisplaySegment: redraw specified segment (if needed) -*/
static void
updateDisplaySegment(dispobj, dsp)
Widget dispobj;
DisplaySegment *dsp;
{
    DisplayFragment *dfp = dsp->fragments;
    int from;
    int x;

    while (dfp != NULL) {
	if (dsp->redrawpos < dfp->from + dfp->nchars) {
	    from = (dsp->redrawpos > dfp->from) ? dsp->redrawpos : dfp->from;
	    x = dfp->region.x;
	    if (from > dfp->from) {
		x += CDStringWidth(dispobj, &dsp->seg, dfp->from, from);
	    } 
	    CDDrawString(dispobj, dfp->canvas->canvas, &dsp->seg,
			 from, dfp->from + dfp->nchars,
			 x, dfp->region.y);
	}
	dfp = dfp->next;
    }
}

/*- redrawSegments: redraw segments in specified area -*/
static void
redrawSegments(ocw, canvas, region)
OverTheSpotConversionWidget ocw;
Widget canvas;
XRectangle *region;
{
    DisplaySegment *dsp = ocw->overthespot.dispsegments;
    DisplayFragment *dfp;
    Widget dispobj = ocw->overthespot.displayobj;
    int i;

    for (i = 0; i < ocw->overthespot.numsegments; i++, dsp++) {
	for (dfp = dsp->fragments; dfp != NULL; dfp = dfp->next) {
	    if (dfp->canvas->canvas == canvas &&
		intersectRect(&dfp->region, region)) {
		CDDrawString(dispobj, canvas, &dsp->seg,
			      dfp->from, dfp->from + dfp->nchars,
			      dfp->region.x, dfp->region.y);
	    }
	}
    }
}

/*
 *+ handle overflow canvas functions
 */

/*- adjustDisplay: compute appropriate offset for the overflow canvas -*/
static void
adjustDisplay(ocw)
OverTheSpotConversionWidget ocw;
{
    Position outerleft, outerright, innerleft, innerright;
    TextCanvas *overflowcanvas = ocw->overthespot.overflowcanvas;
    Cardinal curseg;
    Cardinal curoffset;
    XRectangle *areap;
    Position offset;

    TRACE(("adjustDisplay()\n"));
    ocw->overthespot.overflowoffset = 0;

    /*
     * $B%9%H%i%F%8$H$7$F$O(B
     * $B%+%l%s%H%;%0%a%s%H!&%+%l%s%H%5%V%;%0%a%s%H!&%$%s%5!<%H%+!<%=%k$N$"$k(B
     * $B%;%0%a%s%H$N$I$l$b$J$1$l$P5$$K$7$J$$(B
     * $B%$%s%5!<%H%+!<%=%k$,$"$l$P$=$l$r:GM%@h$9$k!#$D$^$j%$%s%5!<%H%+!<%=%k(B
     * $B$O2?$,$"$C$F$bI=<($9$k$h$&$K$9$k!#(B
     * $B$G$-$l$P%$%s%5!<%H%+!<%=%k$N$"$k%;%0%a%s%H$O$9$Y$FI=<($9$k!#(B
     */

    outerleft = innerleft = 9999;
    outerright = innerright = 0;

    if (ICCursorPos(ocw->ccontrol.inputobj, &curseg, &curoffset) == 1) {
	(void)getInsertingSegmentRange(ocw, overflowcanvas,
				       curseg, curoffset,
				       &outerleft, &outerright, &innerleft);
	if (outerleft <= outerright) innerright = innerleft + 2; /* XXX */
    } else {
	(void)getAttributeSegmentRange(ocw, overflowcanvas,
				       ICAttrCurrentSegment,
				       &innerleft, &innerright);
	(void)getAttributeSegmentRange(ocw, overflowcanvas,
				       ICAttrCurrentSubSegment,
				       &outerleft, &outerright);
    }

    if (outerleft > outerright && innerleft > innerright) {
	/* no important segments is on the overflow canvas */
	TRACE(("\tno important segments on the canvas\n"));
	return;
    }

    if (outerleft > innerleft) outerleft = innerleft;
    if (outerright < innerright) outerright = innerright;

    areap = &CLAREA(ocw);

    if (areap->x <= outerleft && outerright <= areap->x + areap->width) {
	/* important part fits in the visible area */
	TRACE(("\timportant segments are visible\n"));
	return;
    }

    offset = 0;
    adjustOffset(areap, outerleft, outerright, &offset, False);
    adjustOffset(areap, innerleft, innerright, &offset, True);
    TRACE(("\toffset = %d\n", offset));
    ocw->overthespot.overflowoffset = offset;
}

/*- getAttributeSegmentRange: compute span of segments which has the specified attributes -*/
static Boolean
getAttributeSegmentRange(ocw, canvas, attr, leftp, rightp)
OverTheSpotConversionWidget ocw;
TextCanvas *canvas;
int attr;
Position *leftp;
Position *rightp;
{
    int nsegs = ocw->overthespot.numsegments;
    DisplaySegment *dseg = ocw->overthespot.dispsegments;
    DisplayFragment *dfp;
    Position left, right;

    left = 32767;
    right = 0;

    while (nsegs-- > 0) {
	if (dseg->seg.attr & attr) {
	    dfp = dseg->fragments;

	    while (dfp != NULL) {
		if (dfp->canvas == canvas) {
		    if (dfp->region.x < left) left = dfp->region.x;
		    if (right < dfp->region.x + dfp->region.width) {
			right = dfp->region.x + dfp->region.width;
		    }
		}
		dfp = dfp->next;
	    }
	}
	dseg++;
    }

    if (left > right) return False;

    *leftp = left + canvas->x;
    *rightp = right + canvas->x;
    return True;
}

/*- getInsertingSegmentRange: compute span of segments which has insert cursor -*/
static Boolean
getInsertingSegmentRange(ocw, canvas, curseg, offset, leftp, rightp, posp)
OverTheSpotConversionWidget ocw;
TextCanvas *canvas;
Cardinal curseg;
Cardinal offset;
Position *leftp;
Position *rightp;
Position *posp;
{
    DisplaySegment *dseg = ocw->overthespot.dispsegments + curseg;
    DisplayFragment *dfp;
    Position left, right, insert;

    left = 32767;
    right = 0;

    dfp = dseg->fragments;

    while (dfp != NULL) {
	if (dfp->canvas == canvas &&
	    dfp->from <= offset && offset <= dfp->from + dfp->nchars) {
	    if (dfp->region.x < left) left = dfp->region.x;
	    if (right < dfp->region.x + dfp->region.width) {
		right = dfp->region.x + dfp->region.width;
	    }

	    if (offset == dfp->from) {
		insert = dfp->region.x;
	    } else if (offset == dfp->from + dfp->nchars) {
		insert = dfp->region.x + dfp->region.width;
	    } else {
		insert = dfp->region.x +
		         CDStringWidth(ocw->overthespot.displayobj,
				       &dseg->seg, dfp->from,
				       offset);
	    }
	    break;
	}
	dfp = dfp->next;
    }

    if (left > right) return False;

    *leftp = left + canvas->x;
    *rightp = right + canvas->x;
    *posp = insert + canvas->x;
    return True;
}

/*- adjustOffset: make the span fit within the specified area -*/
static void
adjustOffset(rectp, left, right, offsetp, force)
XRectangle *rectp;
Position left;
Position right;
Position *offsetp;
Boolean force;
{
    Position offset = *offsetp;

    if (rectp->x <= left + offset &&
	right + offset <= rectp->x + rectp->width) return;

    if (right - left > rectp->width) {
	if (!force) return;
	/* centering */
	offset = (rectp->x + rectp->width / 2) - (right + left) / 2;
    } else {
	if (left + offset < rectp->x) {
	    offset = rectp->x - left;
	} else if (rectp->x + rectp->width < right + offset) {
	    offset = rectp->x + rectp->width - right;
	}
    }
    *offsetp = offset;
    return;
}


/*
 *+ insert cursor handling functions
 */

/*- eraseCursor: erase insert cursor -*/
static void
eraseCursor(ocw)
OverTheSpotConversionWidget ocw;
{
    if (!ocw->overthespot.cursorvisible) return;

    TRACE(("eraseCursor() at (%d,%d)\n",
	    ocw->overthespot.cursorlocation.x,
	    ocw->overthespot.cursorlocation.y));
    CDDrawCursor(ocw->overthespot.displayobj,
		 ocw->overthespot.cursorlocation.canvas->canvas,
		 ocw->overthespot.cursorlocation.x,
		 ocw->overthespot.cursorlocation.y,
		 False);
    ocw->overthespot.cursorvisible = False;
}

/*- showCursor: draw insert cursor -*/
static void
showCursor(ocw)
OverTheSpotConversionWidget ocw;
{
    if (!ocw->overthespot.cursorvisible) return;

    TRACE(("showCursor at (%d,%d)\n",
	    ocw->overthespot.cursorlocation.x,
	    ocw->overthespot.cursorlocation.y));
    CDDrawCursor(ocw->overthespot.displayobj,
		 ocw->overthespot.cursorlocation.canvas->canvas,
		 ocw->overthespot.cursorlocation.x,
		 ocw->overthespot.cursorlocation.y,
		 True);
}

/*- exposeCursor: make the insert cursor redraw correctly when exposing -*/
static Boolean
exposeCursor(ocw, w, region)
OverTheSpotConversionWidget ocw;
Widget w;
XRectangle *region;
{
    XRectangle bounds;

    if (!ocw->overthespot.cursorvisible ||
	w != ocw->overthespot.cursorlocation.canvas->canvas) return False;

    TRACE(("exposeCursor(region=%d,%d-%d,%d)\n",
	    region->x, region->y, region->width, region->height));
    /*
     * if a part of the insert cursor is in the exposing region,
     * clear the entire cursor before redraw, since the cursor is
     * drawn with xor mode.
     */
    CDGetCursorBounds(ocw->overthespot.displayobj, &bounds);
    bounds.x += ocw->overthespot.cursorlocation.x;
    bounds.y += ocw->overthespot.cursorlocation.y;
    if (intersectRect(region, &bounds)) {
	eraseCursor(ocw);
	XClearArea(XtDisplay(w), XtWindow(w),
		   bounds.x, bounds.y, bounds.width, bounds.height, False);
	unionRect(region, &bounds, region);
    }
    ocw->overthespot.cursorvisible = True;
    return True;
}

/*- computeCursor: compute insert cursor position if visible -*/
static void
computeCursor(ocw)
OverTheSpotConversionWidget ocw;
{
    DisplaySegment *dsp;
    DisplayLocation disploc;
    Cardinal seg, offset;

    if (!ICCursorPos(ocw->ccontrol.inputobj, &seg, &offset)) {
	ocw->overthespot.cursorvisible = False;
	return;
    }

    /* sanity check */
    if (seg >= ocw->overthespot.numsegments) return;
    dsp = ocw->overthespot.dispsegments + seg;
    if (offset > dsp->seg.nchars) return;

    if (findLocation(ocw, dsp, offset, &disploc) == NULL) return;

    disploc.y += ocw->overthespot.ascent;

    ocw->overthespot.cursorvisible = True;
    ocw->overthespot.cursorlocation = disploc;
}

/*
 *+ resource converter
 */

/*- StringToModeLocation: string->mode-location resource converter -*/
/* ARGSUSED */
static void
StringToModeLocation(args, num_args, from, to)
XrmValue *args;
Cardinal *num_args;
XrmValue *from;
XrmValue *to;
{
    char *s = (char *)from->addr;
    static ModeLocation ml = ModeBottomLeft;

    if (!XmuCompareISOLatin1(s, "topleft")) ml = ModeTopLeft;
    else if (!XmuCompareISOLatin1(s, "topright")) ml = ModeTopRight;
    else if (!XmuCompareISOLatin1(s, "bottomleft")) ml = ModeBottomLeft;
    else if (!XmuCompareISOLatin1(s, "bottomright")) ml = ModeBottomRight;
    else if (!XmuCompareISOLatin1(s, "tracktext")) ml = ModeTrackText;
    else if (!XmuCompareISOLatin1(s, "none")) ml = ModeNone;
    else {
	XtStringConversionWarning(s, XtRModeLocation);
    }

    to->size = sizeof(ModeLocation);
    to->addr = (caddr_t)&ml;
}

/*
 *+ miscellaneous functions
 */

/*- MoveShell: move shell widget -*/
static void
MoveShell(w, x, y)
Widget w;
Position x;
Position y;
{
    ShellWidget shell = (ShellWidget)w;

    TRACE(("MoveShell(%s,x=%d,y=%d,core.x=%d,core.y=%d)\n",XtName(w),x,y,w->core.x,w->core.y));
    XtCheckSubclass(w, shellWidgetClass,
		    "MoveShell: specified widget is not a shell");
    /*
     * calling XtMoveWidget() is NOT enough to move shell widgets when
     * they are not mapped. we must use XtMakeGeometryRequest() or
     * XtSetValues() to invoke root-geometry-manager which modifies
     * the size hint appropriately.
     */
    if (shell->shell.popped_up) {
	XtMoveWidget(w, x, y);
    } else {
	XtWidgetGeometry req;

	req.request_mode = CWX | CWY;
	req.x = x;
	req.y = y;
	(void)XtMakeGeometryRequest(w, &req, (XtWidgetGeometry *)NULL);
    }
}

/*- getToplevelWindow: get top-level window of a given window -*/
static Window
getToplevelWindow(dpy, win, wm_state)
Display *dpy;
Window win;
Atom wm_state;
{
    Atom type;
    int format;
    unsigned long nitems, bytesafter;
    unsigned char *data;
    Window root, parent;
    Window *children;
    unsigned int nchildren;

    /*
     * find toplevel window which has WM_STATE property or if no exists,
     * direct subwindow of the root window. (ie I assume that if a
     * window manager is running, that is a ICCCM compliant one)
     */
    for (;;) {
	type = None;
	if (wm_state != None) {
	    data = NULL;
	    XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False,
			       AnyPropertyType, &type, &format,
			       &nitems, &bytesafter, &data);
	    if (data != NULL) XtFree((char *)data);
	    if (type != None) break;
	}
	if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) break;
	if (nchildren > 0) XtFree((char *)children);
	if (root == parent) break;
	win = parent;
    }
    return win;
}

/*- setTransientFor: set WM_TRANSIENT_FOR property to specified widget -*/
static void
setTransientFor(w, win)
Widget w;
Window win;
{
    if (w == NULL) return;
    if (!XtIsRealized(w)) XtRealizeWidget(w);
    XSetTransientForHint(XtDisplay(w), XtWindow(w), win);
}

/*-setMwmHints: set _MOTIF_WM_HINTS for mode shell -*/
static void
setMwmHints(w)
Widget w;
{
#define MWM_HINTS_ATOM		"_MOTIF_WM_HINTS"
#define MWM_HINTS_DECOR		(1 << 1)
#define MWM_DECOR_BORDER	(1 << 1)
    Atom mwm_hints;
    unsigned long hints[5];

    if (w == NULL) return;
    if (!XtIsRealized(w)) XtRealizeWidget(w);

    mwm_hints = CachedInternAtom(XtDisplay(w), MWM_HINTS_ATOM, False);
    if (mwm_hints == None) return;	/* just in case.. */

    hints[0] = MWM_HINTS_DECOR;		/* flags */
    hints[2] = MWM_DECOR_BORDER;	/* decorations */
    hints[1] = hints[3] = hints[4] = 0;	/* functions, input mode and status */

    XChangeProperty(XtDisplay(w), XtWindow(w),
		    mwm_hints, mwm_hints, 32, PropModeReplace,
		    (unsigned char *)hints, 5);
}

/*- getFocusOffset: get the focus window's position relative to the client window -*/
static void
getFocusOffset(ocw)
OverTheSpotConversionWidget ocw;
{
    int offx, offy;
    Window junkw;

    if (ocw->ccontrol.focuswindow == ocw->ccontrol.clientwindow) {
	FOCUSOFFX(ocw) = 0;
	FOCUSOFFY(ocw) = 0;
	return;
    }
    XTranslateCoordinates(XtDisplay((Widget)ocw),
			  ocw->ccontrol.focuswindow,
			  ocw->ccontrol.clientwindow,
			  0, 0, &offx, &offy, &junkw);
    FOCUSOFFX(ocw) = offx;
    FOCUSOFFY(ocw) = offy;
}
   
/*- intersectRect: returns given rectangles have a intersection -*/
static Boolean
intersectRect(rect1, rect2)
register XRectangle *rect1;
register XRectangle *rect2;
{
    return (rect1->x + rect1->width <= rect2->x ||
	    rect1->x >= rect2->x + rect2->width ||
	    rect1->y + rect1->height <= rect2->y ||
	    rect1->y >= rect2->y + rect2->height) ? False : True;
}

/*- unionRect: returns minimum rectangle that covers given rectangles -*/
static void
unionRect(rect1, rect2, rect_ret)
register XRectangle *rect1;
register XRectangle *rect2;
XRectangle *rect_ret;
{
    int x0, x1, y0, y1;

    x0 = (rect1->x > rect2->x) ? rect2->x : rect1->x;
    y0 = (rect1->y > rect2->y) ? rect2->y : rect1->y;
    x1 = (rect1->x + rect1->width > rect2->x + rect2->width) ?
		rect1->x + rect1->width : rect2->x + rect2->width;
    y1 = (rect1->y + rect1->height > rect2->y + rect2->height) ?
		rect1->y + rect1->height : rect2->y + rect2->height;

    rect_ret->x = x0;
    rect_ret->y = y0;
    rect_ret->width = x1 - x0;
    rect_ret->height = y1 - y0;
}

/*- enoughSpaceForStatus: checks if there's enough space for the status display -*/
static int
enoughSpaceForStatus(ocw)
OverTheSpotConversionWidget ocw;
{
    Widget modew = ocw->overthespot.modewidget_fix;
    int modespace;
    int ascent = ocw->overthespot.ascent;
    int descent = ocw->overthespot.lineheight - ascent;
    int lspace = ocw->overthespot.linespacing;
    int areatop = CLAREA(ocw).y;
    int areabottom = CLAREA(ocw).y + CLAREA(ocw).height;
    int top, bottom, y;

    if (lspace == 0) lspace = 1;	/* avoid "divide by zero" error */

    /*
     * tracking $B%9%F!<%?%9$,I=<($G$-$k$+$I$&$+%A%'%C%/$9$k$K$O!"%/%i%$(B
     * $B%"%s%H%(%j%"$KI=<($G$-$k:G=i$H:G8e$N9T$K$D$$$F$=$N>e$+2<$K%9%F!<(B
     * $B%?%9$,I=<($G$-$k$3$H$rD4$Y$l$P$h$$!#(B
     */

    modespace = modew->core.height + modew->core.border_width * 2 + 2;

    /* $B:G=i$N9T$N>e2<$N(B Y $B:BI8$r7W;;$7$F%9%F!<%?%9$,I=<($G$-$k$+D4$Y$k(B */
    y = SPOTY(ocw) - ascent;
    top = y - ((y - areatop) / lspace) * lspace;
    bottom = top + ascent + descent;
    if (top - areatop < modespace && areabottom - bottom < modespace) {
	return 0;
    }

    /* $B:G8e$N9T$N>e2<$N(B Y $B:BI8$r7W;;$7$F%9%F!<%?%9$,I=<($G$-$k$+D4$Y$k(B */
    y = SPOTY(ocw) + descent;
    bottom = y + ((areabottom - y) / lspace) * lspace;
    top = bottom - (ascent + descent);
    if (top - areatop < modespace && areabottom - bottom < modespace) {
	return 0;
    }

    return 1;
}

static DisplayFragment *free_fragments = NULL;

/*- allocDisplayFragment: get new fragment -*/
static DisplayFragment *
allocDisplayFragment()
{
    if (free_fragments == NULL) {
	return XtNew(DisplayFragment);
    } else {
	DisplayFragment *dfp = free_fragments;
	free_fragments = dfp->next;
	return dfp;
    }
}

/*- freeDisplayFragments: add specified fragment list to free-list -*/
static void
freeDisplayFragments(fragments)
DisplayFragment *fragments;
{
    DisplayFragment *dfp = fragments;

    if (dfp == NULL) return;
    while (dfp->next != NULL) dfp = dfp->next;
    dfp->next = free_fragments;
    free_fragments = fragments;
}

/*- destroyDisplayFragments: free specified fragment list -*/
static void
destroyDisplayFragments(fragments)
DisplayFragment *fragments;
{
    DisplayFragment *dfp;

    while (fragments != NULL) {
	dfp = fragments->next;
	XtFree((char *)fragments);
	fragments = dfp;
    }
}

/*- allocDisplaySegments: prepare specified number of display segments -*/
static void
allocDisplaySegments(ocw, n)
OverTheSpotConversionWidget ocw;
int n;
{
    if (ocw->overthespot.dispsegmentsize > n) return;
    n = ((n + 3) / 4) * 4 ;
    if (ocw->overthespot.dispsegments == NULL) {
	ocw->overthespot.dispsegments = (DisplaySegment *)XtMalloc(n * sizeof(DisplaySegment));
    } else {
	ocw->overthespot.dispsegments = (DisplaySegment *)XtRealloc((char *)ocw->overthespot.dispsegments, n * sizeof(DisplaySegment));
    }
    ocw->overthespot.dispsegmentsize = n;
}

/*- freeDisplaySegment: free display segment's contents -*/
static void
freeDisplaySegment(dsp)
DisplaySegment *dsp;
{
    freeString(&dsp->seg);
    freeDisplayFragments(dsp->fragments);
    dsp->fragments = NULL;
}

/*- clearAllDisplaySegments: clear all display segment's -*/
static void
clearAllDisplaySegments(ocw)
OverTheSpotConversionWidget ocw;
{
    DisplaySegment *dsp = ocw->overthespot.dispsegments;
    int i;

    for (i = 0; i < ocw->overthespot.numsegments; i++) {
	freeDisplaySegment(dsp++);
    }
    ocw->overthespot.numsegments = 0;
}

/*- copyString: copy ICString -*/
static void
copyString(from, to)
ICString *from;
ICString *to;
{
    *to = *from;
    to->data = XtMalloc(to->nbytes);
    (void)bcopy(from->data, to->data, to->nbytes);
}

/*- freeString: free ICString -*/
static void
freeString(seg)
ICString *seg;
{
    XtFree(seg->data);
    seg->data = NULL;
    seg->nbytes = 0;
}