/*                   -*-Mode: c; c-file-style: "GNU"-*-              */
/**
 *
 * $Header: /cvsroot/hungry/lesstif/lib/Xm/TextIn.c,v 1.28 1998/04/16 21:46:45 u27113 Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static char rcsid[] = "$Header: /cvsroot/hungry/lesstif/lib/Xm/TextIn.c,v 1.28 1998/04/16 21:46:45 u27113 Exp $";

#include <LTconfig.h>
#include <Xm/XmP.h>
#include <XmI/XmI.h>
#include <Xm/TextP.h>
#include <X11/Xfuncs.h>
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#define ANSI_STRING
#else
#include <strings.h>
#endif
#include <stdlib.h>

#include <XmI/DebugUtil.h>

static void Activate();
static void MoveBackwardChar();
static void MoveBackwardParagraph();
static void MoveBackwardWord();
static void RingBell();
static void MoveBeginningOfFile();
static void MoveToLineStart();
static void ClearSelection();
static void CopyClipboard();
static void CopyPrimary();
static void ProcessCopy();
static void CutClipboard();
static void CutPrimary();
static void DeleteForwardChar();
static void DeleteBackwardChar();
static void DeleteForwardWord();
static void DeleteBackwardWord();
static void DeleteCurrentSelection();
static void DeleteToEndOfLine();
static void DeleteToStartOfLine();
static void DeselectAll();
static void DoSelection();
static void MoveEndOfFile();
static void MoveToLineEnd();
static void ExtendEnd();
static void StartExtendSelection();
static void FindWord();
static void MoveForwardChar();
static void MoveForwardParagraph();
static void MoveForwardWord();
static void DoGrabFocus();
static void InsertString();
static void KeySelection();
static void KillForwardChar();
static void KillForwardWord();
static void KillBackwardChar();
static void KillBackwardWord();
static void KillCurrentSelection();
static void KillToEndOfLine();
static void KillToStartOfLine();
static void MoveDestination();
static void ProcessMove();
static void InsertNewLine();
static void InsertNewLineAndBackup();
static void InsertNewLineAndIndent();
static void MoveNextLine();
static void MoveNextPage();
static void TraverseNextTabGroup();
static void MovePageLeft();
static void MovePageRight();
static void PasteClipboard();
static void TraversePrevTabGroup();
static void MovePreviousLine();
static void MovePreviousPage();
static void ProcessBDrag();
static void ProcessCancel();
static void ProcessDown();
static void ProcessUp();
static void ProcessHome();
static void ProcessReturn();
static void ProcessShiftDown();
static void ProcessShiftUp();
static void ProcessTab();
static void RedrawDisplay();
static void ScrollOneLineDown();
static void ScrollOneLineUp();
static void SecondaryAdjust();
static void SecondaryNotify();
static void ExtendSelection();
static void SelectAll();
static void StartExtendSelection();
static void SelfInsert();
static void SetAnchor();
static void SetCursorPosition();
static void SetSelectionHint();
static void ToggleAddMode();
static void TraverseHome();
static void TraverseDown();
static void TraverseUp();
static void TextFocusIn();
static void TextFocusOut();
static void TextLeave();
static void VoidAction();
static void UnKill();
extern void LineTable(XmTextWidget w);

#define Offset(field) XtOffsetOf(XmTextInnerRec, inner.in.field)

/* Resources for the TextIn class */
static XtResource input_resources[] =
{
    {
	XmNpendingDelete, XmCPendingDelete, XmRBoolean,
	sizeof(Boolean), Offset(pendingdelete),
	XmRImmediate, (XtPointer)True
    },
    {
	XmNselectionArray, XmCSelectionArray, XmRPointer,
	sizeof(XtPointer), Offset(sarray),
	XmRImmediate, (XtPointer)NULL
	/* FIX ME: Motif has XmRInt, wacky value here */
    },
    {
	XmNselectionArrayCount, XmCSelectionArrayCount, XmRInt,
	sizeof(int), Offset(sarraycount),
	XmRImmediate, (XtPointer)3
	/* FIX ME: Motif has XmRInt, (XtPointer)whacko value here */
    },
    {
	XmNselectThreshold, XmCSelectThreshold, XmRInt,
	sizeof(int), Offset(threshold),
	XmRImmediate, (XtPointer)5
    },
};

/* action table table */

static XtActionsRec ZdefaultTextActionsTable[] =
{
    {"activate", Activate},
    {"backward-character", MoveBackwardChar},
    {"backward-paragraph", MoveBackwardParagraph},
    {"backward-word", MoveBackwardWord},
    {"beep", RingBell},
    {"beginning-of-file", MoveBeginningOfFile},
    {"beginning-of-line", MoveToLineStart},
    {"clear-selection", ClearSelection},
    {"copy-clipboard", CopyClipboard},
    {"copy-primary", CopyPrimary},
    {"copy-to", ProcessCopy},
    {"cut-clipboard", CutClipboard},
    {"cut-primary", CutPrimary},
    {"delete-next-character", DeleteForwardChar},
    {"delete-previous-character", DeleteBackwardChar},
    {"delete-next-word", DeleteForwardWord},
    {"delete-previous-word", DeleteBackwardWord},
    {"delete-selection", DeleteCurrentSelection},
    {"delete-to-end-of-line", DeleteToEndOfLine},
    {"delete-to-start-of-line", DeleteToStartOfLine},
    {"deselect-all", DeselectAll},
    {"do-quick-action", VoidAction},
    {"end-of-file", MoveEndOfFile},
    {"end-of-line", MoveToLineEnd},
    {"enter", _XmPrimitiveEnter},
    {"extend-adjust", ExtendSelection},
    {"extend-end", ExtendEnd},
    {"extend-start", StartExtendSelection},
    {"find-word", FindWord},
    {"forward-character", MoveForwardChar},
    {"forward-paragraph", MoveForwardParagraph},
    {"forward-word", MoveForwardWord},
    {"grab-focus", DoGrabFocus},
    {"Help", _XmPrimitiveHelp},
    {"insert-string", InsertString},
    {"key-select", KeySelection},
    {"kill-next-character", KillForwardChar},
    {"kill-next-word", KillForwardWord},
    {"kill-previous-character", KillBackwardChar},
    {"kill-previous-word", KillBackwardWord},
    {"kill-selection", KillCurrentSelection},
    {"kill-to-end-of-line", KillToEndOfLine},
    {"kill-to-start-of-line", KillToStartOfLine},
    {"leave", TextLeave},
    {"move-destination", MoveDestination},
    {"move-to", ProcessMove},
    {"newline", InsertNewLine},
    {"newline-and-backup", InsertNewLineAndBackup},
    {"newline-and-indent", InsertNewLineAndIndent},
    {"next-line", MoveNextLine},
    {"next-page", MoveNextPage},
    {"next-tab-group", TraverseNextTabGroup},
    {"page-left", MovePageLeft},
    {"page-right", MovePageRight},
    {"paste-clipboard", PasteClipboard},
    {"prev-tab-group", TraversePrevTabGroup},
    {"previous-line", MovePreviousLine},
    {"previous-page", MovePreviousPage},
    {"process-bdrag", ProcessBDrag},
    {"process-cancel", ProcessCancel},
    {"process-down", ProcessDown},
    {"process-up", ProcessUp},
    {"process-home", ProcessHome},
    {"process-return", ProcessReturn},
    {"process-shift-down", ProcessShiftDown},
    {"process-shift-up", ProcessShiftUp},
    {"process-tab", ProcessTab},
    {"quick-copy-set", VoidAction},
    {"quick-cut-set", VoidAction},
    {"redraw-display", RedrawDisplay},
    {"scroll-one-line-down", ScrollOneLineDown},
    {"scroll-one-line-up", ScrollOneLineUp},
    {"secondary-adjust", SecondaryAdjust},
    {"secondary-notify", SecondaryNotify},
    {"select-adjust", DoSelection},
    {"select-all", SelectAll},
    {"select-end", DoSelection},
    {"select-start", StartExtendSelection},
    {"self-insert", SelfInsert},
    {"set-anchor", SetAnchor},
    {"set-insertion-point", SetCursorPosition},
    {"set-selection-hint", SetSelectionHint},
    {"toggle-add-mode", ToggleAddMode},
    {"traverse-home", TraverseHome},
    {"traverse-next", TraverseDown},
    {"traverse-prev", TraverseUp},
    {"focusIn", TextFocusIn},
    {"focusOut", TextFocusOut},
    {"unkill", UnKill},
    {"unmap", _XmPrimitiveUnmap},
};


XtPointer _XmdefaultTextActionsTable = (XtPointer)ZdefaultTextActionsTable;

Cardinal _XmdefaultTextActionsTableSize = XtNumber(ZdefaultTextActionsTable);

static void
Invalidate(XmTextWidget w, XmTextPosition start, XmTextPosition end,
	   long delta)
{
}

static void
InputGetValues(Widget w, ArgList args, Cardinal num_args)
{
    XtGetSubvalues(Text_InnerWidget(w),
		   input_resources, XtNumber(input_resources),
		   args, num_args);

    DEBUGOUT(XdbDebug(__FILE__, w, "InputGetValues :\n"));

    XdbPrintArgList(__FILE__, w, args, num_args, True);
}

/*
 * Something analogous to OutputSetValues should probably happen here.
 * This means we'll have to create a temporary copy of some stuff, something
 * that Xt normally does for us. Unfortunately, somebody (me - hehe) got the
 * unfortunate idea to implement things this way.
 * Other ways are probably more trouble...
 */
static void
InputSetValues(Widget old, Widget request, Widget new,
	       ArgList args, Cardinal *num_args)
{
    DEBUGOUT(XdbDebug(__FILE__, new, "InputSetValues :\n"));

    DEBUGOUT(XdbPrintArgList(__FILE__, new, args, *num_args, False));

/* FIX ME : Need to treat the following resources :
 *    XmNpendingDelete
 *      XmNselectionArray + XmNselectionArrayCount
 *      XmNselectThreshold
 */
}

static void
InputDestroy(Widget aw)
{
    XmTextWidget w = (XmTextWidget)aw;

    DEBUGOUT(XdbDebug(__FILE__, aw, "InputDestroy"));

    (*Text_Source(w)->RemoveWidget) (Text_Source(w), w);

    XtFree((char *)Text_Input(w));
}

static void
GetSecResData(XmSecondaryResourceData *secres)
{
}

InputRec inputRec =
{
    /* _InputDataRec             */ NULL,
    /* InputInvalidateProc       */ Invalidate,
    /* InputGetValuesProc        */ InputGetValues,
    /* InputSetValuesProc        */ InputSetValues,
    /* XtWidgetProc              */ InputDestroy,
    /* InputGetSecResProc        */ GetSecResData
};

void
_XmTextInputCreate(Widget aw, ArgList args, Cardinal num_args)
{
    XmTextWidget w = (XmTextWidget)aw;
    XmTextInnerWidget iw = (XmTextInnerWidget)w->text.inner_widget;
    InputData i;

    Text_Input(w) = (Input)XtMalloc(sizeof(InputRec));
    bcopy(&inputRec, Text_Input(w), sizeof(InputRec));

    i = Text_Input(w)->data = &iw->inner.in;

    XtGetSubresources(aw, iw,
		      aw->core.name,
		      aw->core.widget_class->core_class.class_name,
		      input_resources,
		      XtNumber(input_resources),
		      args, num_args);


    if (!Text_Source(w))
    {
	Text_Source(w) = _XmStringSourceCreate(Text_Value(w), False);
    }

    (*Text_Source(w)->AddWidget) (Text_Source(w), w);

    In_SelArray(i) = NULL;
    In_SelArrayIndex(i) = 0;

    In_LastTime(i) = 0;
    In_HighlightStart(i) = 0;

    DEBUGOUT(XdbDebug(__FILE__, aw,
		      "_XmTextInputCreate (selectionArrayCount %d)\n",
		      In_SelArrayCount(i)));

    DEBUGOUT(XdbPrintArgList(__FILE__, aw, args, num_args, False));
}


/* Scan type utilities ---------------------------------------------------

 * FIX ME - XmNselectionArray should be used.
 */
static XmTextScanType
ScanType(XmTextWidget w)
{
    InputData i = Text_InputData(w);
    XmTextScanType type;

    if (In_SelArray(i))
    {
	if (In_SelArrayIndex(i) >= In_SelArrayCount(i))
	{
	    In_SelArrayIndex(i) = 0;
	}

	type = In_SelArray(i)[In_SelArrayIndex(i)];
    }
    else
    {
	switch (In_SelArrayIndex(i))
	{
	case 1:
	    type = XmSELECT_WORD;
	    break;

	case 2:
	    type = XmSELECT_LINE;
	    break;

	case 3:
	    type = XmSELECT_ALL;
	    break;

	default:
	    type = XmSELECT_POSITION;
	    In_SelArrayIndex(i) = 0;
	    break;
	}
    }

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "ScanType(%d) -> %s\n", In_SelArray(i),
		      (type == XmSELECT_WORD) ? "XmSELECT_WORD" :
		      (type == XmSELECT_LINE) ? "XmSELECT_LINE" :
		      (type == XmSELECT_ALL) ? "XmSELECT_ALL" :
		   (type == XmSELECT_POSITION) ? "XmSELECT_POSITION" : "???"));

    return type;
}



/* High level text insert and delete routines ------------------------------ */

static void
VerifyBell(XmTextWidget w)
{
    if (Text_VerifyBell(w))
    {
	XBell(XtDisplay((Widget)w), 50);
    }
}

#if 0
static Boolean
DoCursorMove(XmTextWidget w, XEvent *ev, XmTextPosition pos)
{
    Widget aw = (Widget)w;

    if (pos > Text_LastPos(w))
	pos = Text_LastPos(w);

    cbs.doit = True;
    cbs.newInsert = pos;
    if (Text_MotionVerifyCallback(w))
    {
	cbs.reason = XmCR_MOVING_INSERT_CURSOR;
	cbs.event = ev;
	cbs.currInsert = Text_CursorPos(w);
	cbs.startPos = cbs.endPos = 0;
	cbs.text = NULL;

	XtCallCallbacks(aw, XmNmotionVerifyCallback, &cbs);

	if (cbs.doit)
	{
	    pos = cbs.newInsert;
	}
    }

    if (cbs.doit)
    {
	_XmTextSetCursorPosition(aw, pos);
    }
    else
    {
	VerifyBell(w);
    }
    return cbs.doit;
}
#endif

/* Need this in XmTextRemove, hence no longer static */
void
_XmTextDelete(XmTextWidget w, XEvent *ev, XmTextPosition start, XmTextPosition end)
{
    Widget aw = (Widget)w;
    XmTextVerifyCallbackStruct cbs;
    XmTextBlockRec blockrec;

    if (end <= 0)
    {
	end = 0;
    }

    blockrec.ptr = NULL;
    blockrec.length = 0;
    blockrec.format = XmFMT_8_BIT;

    cbs.reason = XmCR_MODIFYING_TEXT_VALUE;
    cbs.event = ev;
    cbs.startPos = cbs.newInsert = start;
    cbs.endPos = end;
    cbs.currInsert = Text_CursorPos(w);
    cbs.text = &blockrec;
    cbs.doit = True;

    if (Text_ModifyVerifyCallback(w))
    {
	XtCallCallbacks((Widget)w, XmNmodifyVerifyCallback, &cbs);

	if (!cbs.doit)
	{
	    VerifyBell(w);
	}
    }

    if (cbs.doit)
    {
	XmTextStatus status;

	start = cbs.startPos;
	end = cbs.endPos;
	status = (*Text_Source(w)->Replace) (w, ev, &start, &end,
					     &blockrec, True);

	if (status == EditDone)
	{
	    /*
	     * FIX ME - This code probably belongs in TextStrSo.c.
	     * Think about what should happen if multiple widgets use one
	     * source.
	     * Danny 8/5/1997
	     */
	    _XmTextUpdateLineTable((Widget)w, cbs.startPos,
				   cbs.endPos, &blockrec, True);
	    _XmTextSetCursorPosition(aw, cbs.newInsert);
	}
    }

    /* FIX ME:  Are we required to free this if it exists?  It can only exist
     * if the user made it, but does Motif think that the user malloced it?
     */
/*   if (blockrec.ptr) XtFree(blockrec.ptr); */
}

static void
DoInsert(XmTextWidget w, XEvent *ev, char *buf, int len)
{
    Widget aw = (Widget)w;
    XmTextVerifyCallbackStruct cbs;
    XmTextBlockRec blockrec;

    if (len <= 0)
    {
	return;
    }

    if (!Text_Editable(w))
    {
	VerifyBell(w);
	return;
    }

    (*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w), off);

    blockrec.ptr = XtMalloc(len + 1);
    blockrec.length = len;
    blockrec.format = XmFMT_8_BIT;
    strncpy(blockrec.ptr, buf, len);
    blockrec.ptr[len] = '\0';

    /* Danny 1/7/97 */
    if (Text_CursorPos(w) == PASTENDPOS)
    {
	Text_CursorPos(w) = Text_LastPos(w);	/* End of text */
    }

    cbs.reason = XmCR_MODIFYING_TEXT_VALUE;
    cbs.event = ev;
    cbs.currInsert = cbs.startPos = cbs.endPos = Text_CursorPos(w);

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "DoInsert before add: CursorPos %08x\n",
		      Text_CursorPos(w)));

    cbs.newInsert = (Text_CursorPos(w) += blockrec.length);

    DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		      "DoInsert before CB: CursorPos %08x len %08x\n",
		      Text_CursorPos(w), len));

    cbs.text = &blockrec;
    cbs.doit = True;

    if (Text_ModifyVerifyCallback(w))
    {
	XtCallCallbacks((Widget)w, XmNmodifyVerifyCallback, &cbs);

	if (!cbs.doit)
	{
	    VerifyBell(w);
	}
    }

    if (cbs.doit)
    {
	XmTextStatus status;
	XmTextPosition start, end;

	start = cbs.startPos;
	end = cbs.endPos;
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "DoInsert: start %08x:%08x end %08x:%08x\n",
			  start, cbs.startPos, end, cbs.endPos));
	status = (*Text_Source(w)->Replace) (w, ev, &start,
					     &end, &blockrec, True);

	if (status == EditDone)
	{
	    _XmTextSetCursorPosition(aw, cbs.newInsert);
	}
    }

    XtFree(blockrec.ptr);
}

/*
 * Figure out what is supposed to get selected after this many clicks,
 *      also select it.
 * Number of clicks was recorded elsewhere, and stored into
 *      In_SelArrayIndex(Text_InputData(w)). The function ScanType()
 *      translates that into a type of selection (e.g. XmSELECT_WORD),
 *      based on which we really select text here.
 */
static void
DoScanType(XmTextWidget w, XEvent *ev, XmTextPosition pos)
{
    InputData i = Text_InputData(w);
    XmTextScanType st = ScanType(w);
    XmTextPosition left, right;

    switch (st)
    {
    case XmSELECT_POSITION:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "DoScanType: XmSELECT_POSITION\n"));

	_XmTextSetCursorPosition((Widget)w, pos);

	/* Undo selection */
	XmTextSetSelection((Widget)w, pos, pos, ev->xbutton.time);

	In_HighlightPivot(i) = Text_CursorPos(w);
	break;

    case XmSELECT_WORD:
	right = (*Text_Source(w)->Scan) (Text_Source(w), pos, st,
					 XmsdRight, -1, False);
	left = (*Text_Source(w)->Scan) (Text_Source(w), right, st,
					XmsdLeft, -1, False);

	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		       "DoScanType: XmSELECT_WORD (left %d pos %d right %d)\n",
			  left, pos, right));

	XmTextSetSelection((Widget)w, left, right, ev->xbutton.time);

	break;

    case XmSELECT_LINE:
	right = (*Text_Source(w)->Scan) (Text_Source(w), pos, st,
					 XmsdRight, -1, False);
	left = (*Text_Source(w)->Scan) (Text_Source(w), right, st,
					XmsdLeft, -1, False);

	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
		       "DoScanType: XmSELECT_LINE (left %d pos %d right %d)\n",
			  left, pos, right));

	XmTextSetSelection((Widget)w, left, right, ev->xbutton.time);
	break;

#if	XmVERSION > 1
    case XmSELECT_OUT_LINE:	/* Motif 2.* feature - unimplemented here */
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			  "DoScanType: XmSELECT_OUT_LINE\n"));
	break;
#endif

    case XmSELECT_ALL:
	left = 0;
	right = XmTextGetLastPosition((Widget)w);

	DEBUGOUT(XdbDebug(__FILE__, (Widget)w,
			"DoScanType: XmSELECT_ALL (left %d pos %d right %d)\n",
			  left, pos, right));

	XmTextSetSelection((Widget)w, left, right, ev->xbutton.time);

	break;

    default:
	DEBUGOUT(XdbDebug(__FILE__, (Widget)w, "DoScanType: ???\n"));

	break;
    }
}



/* Action Routines -------------------------------------------------------- */

static void
Activate(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextWidget tw = (XmTextWidget)w;
    XmAnyCallbackStruct cb;

    if (Text_EditMode(tw) != XmSINGLE_LINE_EDIT)
    {
	return;
    }

    DEBUGOUT(XdbDebug(__FILE__, w, "Activate\n"));

    cb.reason = XmCR_ACTIVATE;
    cb.event = ev;

    XtCallCallbackList(w, tw->text.activate_callback, (XtPointer)&cb);
    if (XmIsManager(XtParent(w)))
    {
        XmParentProcessDataRec data;

	data.input_action.process_type = XmINPUT_ACTION;
	data.input_action.event = ev;
	data.input_action.action = XmPARENT_ACTIVATE;
	data.input_action.params = params;
	data.input_action.num_params = num_params;

	XtCallActionProc(XtParent(w), "ManagerParentActivate",
			 ev, params, *num_params);
    }
}

static void
MoveBackwardChar(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MoveBackwardChar"));

    if (Text_CursorPos(w) > 0)
    {
	(*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					       Text_CursorPos(w), off);
	_XmTextSetCursorPosition((Widget)w, Text_CursorPos(w) - 1);
    }
}

static void
MoveBackwardParagraph(Widget w, XEvent *ev,
		      String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MoveBackwardParagraph"));

    if (Text_CursorPos(w) > 0)
    {
	XmTextPosition pos;

	pos = (*Text_Source(w)->Scan) (Text_Source(w), Text_CursorPos(w),
				     XmSELECT_WHITESPACE, XmsdLeft, -1, False);

	pos = (*Text_Source(w)->Scan) (Text_Source(w), pos,
				       XmSELECT_PARAGRAPH, XmsdLeft, -1, False);

	(*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					       Text_CursorPos(w), off);

	_XmTextSetCursorPosition((Widget)w, pos);
    }
}

static void
MoveBackwardWord(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MoweBackwardWord"));

    if (Text_CursorPos(w) > 0)
    {
	XmTextPosition pos;

	pos = (*Text_Source(w)->Scan) (Text_Source(w), Text_CursorPos(w),
				     XmSELECT_WHITESPACE, XmsdLeft, -1, False);

	pos = (*Text_Source(w)->Scan) (Text_Source(w), pos,
				       XmSELECT_WORD, XmsdLeft, -1, False);

	(*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					       Text_CursorPos(w), off);

	_XmTextSetCursorPosition((Widget)w, pos);
    }
}

static void
RingBell(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
}

static void
MoveBeginningOfFile(Widget w, XEvent *ev, String *params,
		    Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MoveBeginningOfFile"));

    if (!Text_Editable(w))
    {
	return;
    }

    if (Text_CursorPos(w) > 0)
    {
	(*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					       Text_CursorPos(w), off);
	_XmTextSetCursorPosition(w, 0);
    }
}

static void
MoveToLineStart(Widget w, XEvent *ev, String *params,
		Cardinal *num_params)
{
    XmTextPosition pos;

    DEBUGOUT(XdbDebug(__FILE__, w, "MoveToLineStart"));

    pos = (*Text_Source(w)->Scan) (Text_Source(w), Text_CursorPos(w),
				   XmSELECT_LINE, XmsdLeft, -1, False);

    (*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					   Text_CursorPos(w), off);

    _XmTextSetCursorPosition(w, pos);
}

static void
ClearSelection(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ClearSelection\n"));
}

static void
CopyClipboard(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "CopyClipboard\n"));
}

static void
CopyPrimary(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "CopyPrimary\n"));
}

/*
 * _XmTextGetSelection is the function that's called when we receive the
 * string from a copy/paste operation.
 * Now we need to insert this text into our widget.
 */
static void
_XmTextGetSelection(Widget w, XtPointer client, Atom *selection, Atom *type,
		    XtPointer value, unsigned long *len, int *format)
{
    XEvent *ev = (XEvent *)client;

    DEBUGOUT(XdbDebug(__FILE__, w, "_XmTextGetSelection\n"));

    if (*len == 0)
    {
	return;
    }

    if (value == NULL)
    {
	return;
    }

    if (*type == XA_STRING)
    {
	char *s = (char *)value;

	if (s)
	{
	    DoInsert((XmTextWidget)w, ev, s, strlen(s));
	}
#if 0
	XtFree(s);
#endif
    }
}

static void
ProcessCopy(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessCopy\n"));

    if (! Text_Editable(w))
	return;

    XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, _XmTextGetSelection,
			(XtPointer)ev, ev->xbutton.time);
}

static void
ProcessCancel(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessCancel\n"));
}

static void
CutClipboard(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "CutClipboard\n"));
}

static void
CutPrimary(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "CutPrimary\n"));
}

/*
 * The specs say :
 *    In normal (not add-mode) mode :
 *      - if there is a selection, delete it
 *      - else delete the next character.
 *    In add mode :
 *      - if there's a selection, and the cursor's in it,
 *        and PendingDelete is True, then delete the selection
 *      - otherwise delete the next character.
 */
static void
DeleteForwardChar(Widget w, XEvent *ev, String *params,
		  Cardinal *num_params)
{
    Boolean sel;
    XmTextPosition left, right;

    DEBUGOUT(XdbDebug(__FILE__, w, "DeleteForwardChar"));

    if (!Text_Editable(w))
    {
	VerifyBell((XmTextWidget)w);

	return;
    }

    sel = (*Text_Source(w)->GetSelection) (Text_Source(w), &left, &right);

    if (Text_AddMode(w))
    {
	/* FIX ME */
    }
    else
    {
	if (sel)
	{
	    _XmTextDelete((XmTextWidget)w, ev, left, right);
	}
	else if (Text_CursorPos(w) < Text_LastPos(w))
	{
	    _XmTextDelete((XmTextWidget)w, ev, Text_CursorPos(w),
		     Text_CursorPos(w) + 1);
	}
    }
}

static void
DeleteBackwardChar(Widget w, XEvent *ev, String *params,
		   Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DeleteBackardChar"));

    if (!Text_Editable(w))
    {
	VerifyBell((XmTextWidget)w);
    }

    if (Text_CursorPos(w) > 0)
    {
	_XmTextDelete((XmTextWidget)w, ev, Text_CursorPos(w) - 1, Text_CursorPos(w));
    }
}

static void
DeleteForwardWord(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DeleteForwardWord\n"));
}

static void
DeleteBackwardWord(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DeleteBackwardWord\n"));
}

static void
DeleteCurrentSelection(Widget w, XEvent *ev,
		       String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DeleteCurrentSelection\n"));
}

static void
DeleteToEndOfLine(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DeleteToEndOfLine\n"));
}

static void
DeleteToStartOfLine(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DeleteToStartOfLine\n"));
}

static void
DeselectAll(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "DeselectAll\n"));
}


static void
MoveToLineEnd(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextPosition pos;

    DEBUGOUT(XdbDebug(__FILE__, w, "MoveToLineEnd"));

    pos = (*Text_Source(w)->Scan) (Text_Source(w), Text_CursorPos(w),
				   XmSELECT_LINE, XmsdRight, -1, False);
    (*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					   Text_CursorPos(w), off);
    _XmTextSetCursorPosition(w, pos);
}

static void
MoveEndOfFile(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextPosition pos;

    DEBUGOUT(XdbDebug(__FILE__, w, "MoveToEndOfFile"));

    pos = Text_LastPos(w);

    (*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					   Text_CursorPos(w), off);

    _XmTextSetCursorPosition(w, pos);
}


static void
DoSelection(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    if (ev->type != MotionNotify)
    {
	return;
    }

    DEBUGOUT(XdbDebug(__FILE__, w, "DoSelection"));
}

/*
 * ExtendEnd gets called at the end of a selection, e.g. after you've moved
 * the pointer while holding down MB1 (i.e. the left mouse button).
 * If, after that, MB1 is released, then this method is called.
 */
static void
ExtendEnd(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    InputData i = Text_InputData(w);
    XmTextPosition pos, left, right;
    Boolean sel;

    if (!i->extending)
    {
	return;
    }

    pos = (*Text_Output(w)->XYToPos) ((XmTextWidget)w,
				      ev->xbutton.x, ev->xbutton.y);

    sel = (*Text_Source(w)->GetSelection) (Text_Source(w), &left, &right);

    if (sel)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
		 "ExtendEnd (pos %d, origleft %d), existing selection %d-%d\n",
			  pos, In_HighlightStart(i), left, right));
    }
    else
    {
	DEBUGOUT(XdbDebug(__FILE__, w, "ExtendEnd (pos %d, origleft %d)\n",
			  pos, In_HighlightStart(i)));
    }

    if (sel && In_HighlightStart(i) >= 0 && pos > In_HighlightStart(i))
    {
	XmTextSetSelection(w, In_HighlightStart(i), pos,
			   ev->xbutton.time);
    }

    /*
     * Set a flag for ExtendSelection() to know that when it happens,
     * it must be a new selection.
     */
    i->extending = False;
}

/*
 * To continue the example above (comments above ExtendEnd), ExtendSelection
 * is called while moving the pointer while holding down MB1.
 */
static void
ExtendSelection(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextPosition pos, left, right;
    InputData i = Text_InputData(w);
    Boolean sel;

    pos = (*Text_Output(w)->XYToPos) ((XmTextWidget)w,
				      ev->xbutton.x, ev->xbutton.y);

    if (!i->extending)
    {
	sel = False;
	In_HighlightStart(i) = pos;
    }

    sel = (*Text_Source(w)->GetSelection) (Text_Source(w), &left, &right);

    if (sel)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
		 "ExtendSelection (pos %d, origleft %d) existing sel. %d-%d\n",
			  pos, In_HighlightStart(i), left, right));
    }
    else
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
	    "ExtendSelection (pos %d, origleft %d) (x %d y %d)\n",
	    pos, In_HighlightStart(i), ev->xbutton.x, ev->xbutton.y));
    }

    if (In_HighlightStart(i) < 0)
    {
	In_HighlightStart(i) = pos;
    }

    XmTextSetSelection(w, In_HighlightStart(i), pos, ev->xbutton.time);

    i->extending = True;
}

static void
FindWord(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
}

static void
MoveForwardChar(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MoveForwardChar"));

    if (!Text_Editable(w))
    {
	return;
    }

    if (Text_CursorPos(w) < Text_LastPos(w))
    {
	(*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					       Text_CursorPos(w), off);
    }

    _XmTextSetCursorPosition(w, Text_CursorPos(w) + 1);
}

static void
MoveForwardParagraph(Widget w, XEvent *ev, String *params,
		     Cardinal *num_params)
{
    XmTextPosition pos;

    DEBUGOUT(XdbDebug(__FILE__, w, "MoveForwardParagraph"));

    pos = (*Text_Source(w)->Scan) (Text_Source(w), Text_CursorPos(w),
				   XmSELECT_PARAGRAPH, XmsdRight, -1, False);

    pos = (*Text_Source(w)->Scan) (Text_Source(w), pos,
				   XmSELECT_WHITESPACE, XmsdRight, -1, False);

    (*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					   Text_CursorPos(w), off);

    _XmTextSetCursorPosition(w, pos);
}


static void
MoveForwardWord(Widget w, XEvent *ev, String *params,
		Cardinal *num_params)
{
    XmTextPosition pos;

    DEBUGOUT(XdbDebug(__FILE__, w, "MoveForwardWord"));

    pos = (*Text_Source(w)->Scan) (Text_Source(w), Text_CursorPos(w),
				   XmSELECT_WORD, XmsdRight, -1, False);

    pos = (*Text_Source(w)->Scan) (Text_Source(w), pos,
				   XmSELECT_WHITESPACE, XmsdRight, -1, False);

    (*Text_Output(w)->DrawInsertionPoint) ((XmTextWidget)w,
					   Text_CursorPos(w), off);

    _XmTextSetCursorPosition(w, pos);
}

static void
DoGrabFocus(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    InputData i = Text_InputData(w);
    XmTextPosition pos;

    DEBUGOUT(XdbDebug(__FILE__, w, "DoGrabFocus"));

    XmProcessTraversal((Widget)w, XmTRAVERSE_CURRENT);

    pos = (*Text_Output(w)->XYToPos) ((XmTextWidget)w,
				      ev->xbutton.x, ev->xbutton.y);

    if ((In_LastTime(i) + XtGetMultiClickTime(XtDisplay((Widget)w))) >
	ev->xbutton.time)
    {
	In_SelArrayIndex(i)++;
    }
    else
    {
	In_SelArrayIndex(i) = 0;
    }

    DoScanType((XmTextWidget)w, ev, pos);

    In_LastTime(i) = ev->xbutton.time;
}


static void
InsertString(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "InsertString\n"));
}

static void
_KeySelection(Widget w, XEvent *ev, int flag, String *params, Cardinal *np)
{
    Boolean sel;
    XmTextPosition left, right, old;

    sel = (*Text_Source(w)->GetSelection) (Text_Source(w), &left, &right);

    switch (flag)
    {
    case 1:			/* left */
	old = Text_CursorPos(w);
	MoveBackwardChar(w, ev, params, np);

	DEBUGOUT(XdbDebug(__FILE__, w,
			  "KeySelection old %d new %d left %d right %d\n",
			  old, Text_CursorPos(w), left, right));

	if (old == Text_CursorPos(w))
	{
	    break;
	}

	if (sel)
	{			/* Already have a selection, extend it */
	    if (old == left)
	    {
		left = Text_CursorPos(w);
	    }

	    if (old == right)
	    {
		right = Text_CursorPos(w);
	    }

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	else
	{			/* New selection */
	    left = Text_CursorPos(w);
	    right = old;

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	break;

    case 2:			/* right */
	old = Text_CursorPos(w);
	MoveForwardChar(w, ev, params, np);

	DEBUGOUT(XdbDebug(__FILE__, w,
			  "KeySelection old %d new %d left %d right %d\n",
			  old, Text_CursorPos(w), left, right));

	if (old == Text_CursorPos(w))
	{
	    break;
	}

	if (sel)
	{			/* Already have a selection, extend it */
	    if (old == left)
	    {
		left = Text_CursorPos(w);
	    }

	    if (old == right)
	    {
		right = Text_CursorPos(w);
	    }

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	else
	{			/* New selection */
	    left = old;
	    right = Text_CursorPos(w);

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	break;

    case 3:			/* up */
	old = Text_CursorPos(w);
	MovePreviousLine(w, ev, params, np);

	DEBUGOUT(XdbDebug(__FILE__, w,
			  "KeySelection old %d new %d left %d right %d\n",
			  old, Text_CursorPos(w), left, right));

	if (old == Text_CursorPos(w))
	{
	    break;
	}

	if (sel)
	{			/* Already have a selection, extend it */
	    if (old == left)
	    {
		left = Text_CursorPos(w);
	    }

	    if (old == right)
	    {
		right = Text_CursorPos(w);
	    }

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	else
	{			/* New selection */
	    left = Text_CursorPos(w);
	    right = old;

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	break;

    case 4:			/* down */
	old = Text_CursorPos(w);
	MoveNextLine(w, ev, params, np);

	DEBUGOUT(XdbDebug(__FILE__, w,
			  "KeySelection old %d new %d left %d right %d\n",
			  old, Text_CursorPos(w), left, right));

	if (old == Text_CursorPos(w))
	{
	    break;
	}

	if (sel)
	{			/* Already have a selection, extend it */
	    if (old == left)
	    {
		left = Text_CursorPos(w);
	    }
	    if (old == right)
	    {
		right = Text_CursorPos(w);
	    }

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	else
	{			/* New selection */
	    left = old;
	    right = Text_CursorPos(w);

	    (*Text_Source(w)->SetSelection) (Text_Source(w),
					     left, right, ev->xkey.time);
	}
	break;
    }
}

/*
 * KeySelection is the action routine for key-select.
 * Can either get called with no parameters,
 * or with "left", "right", "up", or "down". Any more ?
 */
static void
KeySelection(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    int flag = 0;

    if (*num_params == 0)
    {
	DEBUGOUT(XdbDebug(__FILE__, w, "KeySelection()\n"));
    }
    else
    {
	DEBUGOUT(XdbDebug(__FILE__, w, "KeySelection(%s)\n", params[0]));
    }

    if (*num_params == 1)
    {
	if (strcmp(params[0], "left") == 0)
	{
	    flag = 1;
	}
	else if (strcmp(params[0], "right") == 0)
	{
	    flag = 2;
	}
#if 0
	else if (strcmp(params[0], "up") == 0)
	{
	    flag = 3;
	}
	else if (strcmp(params[0], "down") == 0)
	{
	    flag = 4;
	}
#endif
    }

    _KeySelection(w, ev, flag, params, num_params);
}

/*
 * ProcessShiftDown : extend the selection by another line
 */
static void
ProcessShiftDown(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessShiftDown\n"));

    _KeySelection(w, ev, 4, params, num_params);
}

static void
ProcessShiftUp(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessShiftUp\n"));

    _KeySelection(w, ev, 3, params, num_params);
}

static void
KillForwardChar(Widget w, XEvent *ev, String *params,
		Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "KillForwardChar\n"));

    DeleteForwardChar(w, ev, params, num_params);
}

static void
KillForwardWord(Widget w, XEvent *ev, String *params,
		Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "KillForwardWord\n"));

    DeleteForwardWord(w, ev, params, num_params);
}

static void
KillBackwardChar(Widget w, XEvent *ev, String *params,
		 Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "KillBackwardChar\n"));

    DeleteBackwardChar(w, ev, params, num_params);
}

static void
KillBackwardWord(Widget w, XEvent *ev, String *params,
		 Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "KillBackwardWord\n"));

    DeleteBackwardWord(w, ev, params, num_params);
}

static void
KillCurrentSelection(Widget w, XEvent *ev, String *params,
		     Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "KillCurrentSelection\n"));
}

static void
KillToEndOfLine(Widget w, XEvent *ev, String *params,
		Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "KillToEndOfLine\n"));

    DeleteToEndOfLine(w, ev, params, num_params);
}

static void
KillToStartOfLine(Widget w, XEvent *ev, String *params,
		  Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "KillToStartOfLine\n"));
}

static void
MoveDestination(Widget w, XEvent *ev, String *params,
		Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MoveDestination\n"));
}

static void
ProcessMove(Widget w, XEvent *ev, String *params,
	    Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessMove\n"));
}

static void
TraverseNextTabGroup(Widget w, XEvent *ev, String *params,
		     Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "TraverseNextTabGroup\n"));

    XtCallActionProc(w, "PrimitiveNextTabGroup", ev, params, *num_params);
}

static void
MoveNextLine(Widget aw, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextWidget w = (XmTextWidget)aw;
    unsigned int index, maxindex;
    XmTextPosition pos = Text_CursorPos(w);

    DEBUGOUT(XdbDebug(__FILE__, aw, "MoveNextLine"));

    if (!Text_Editable(w))
    {
	return;
    }

    index = _XmTextGetTableIndex(w, pos);
    if (index == (maxindex = Text_TotalLines(w) - 1))
    {
	pos = Text_LastPos(w);
    }
    else
    {
	pos += Text_LineTable(w)[index + 1].start_pos -
	    Text_LineTable(w)[index].start_pos;

	if (index <= maxindex - 2
	    && pos >= Text_LineTable(w)[index + 2].start_pos)
	{
	    pos = Text_LineTable(w)[index + 2].start_pos - 1;
	}
	else if (pos > Text_LastPos(w))
	{
	    pos = Text_LastPos(w);
	}
    }

    if (pos != Text_CursorPos(w))
    {
	(*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w), off);
    }

    _XmTextSetCursorPosition((Widget)w, pos);
}


static void
MoveNextPage(Widget aw, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextWidget w = (XmTextWidget)aw;
    int index, maxindex;
    XmTextPosition pos = Text_CursorPos(w);
    int delta = Text_LineCount(w) - 1;

    DEBUGOUT(XdbDebug(__FILE__, aw, "MoveNextPage"));

    if (!Text_Editable(w))
    {
	return;
    }

    index = _XmTextGetTableIndex(w, pos);
    maxindex = Text_TotalLines(w) - 1;

    if (index > maxindex - delta)
    {
	pos = Text_LastPos(w);
    }
    else
    {
	pos += Text_LineTable(w)[index + delta].start_pos -
	    Text_LineTable(w)[index].start_pos;

	if (index <= maxindex - delta - 1
	    && pos >= Text_LineTable(w)[index + delta + 1].start_pos)
	{
	    pos = Text_LineTable(w)[index + delta + 1].start_pos - 1;
	}
	else if (pos > Text_LastPos(w))
	{
	    pos = Text_LastPos(w);
	}
    }

    if (pos != Text_CursorPos(w))
    {
	(*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w), off);
	_XmTextSetCursorPosition((Widget)w, pos);
    }
}

static void
InsertNewLine(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "InsertNewLine\n"));

    if (!Text_Editable(w))
    {
	return;			/* if we can't edit */
    }
    else if (Text_EditMode(w) == XmSINGLE_LINE_EDIT)
    {
	Activate(w, ev, params, num_params);
	return;
    }

    DoInsert((XmTextWidget)w, ev, "\n", 1);
}

static void
InsertNewLineAndBackup(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "InsertNewLineAndBackup\n"));

    if ((!Text_Editable(w)) || (Text_EditMode(w) == XmSINGLE_LINE_EDIT))
    {
	/* if we can't edit, or we are in single line only mode. */
	return;
    }

    DoInsert((XmTextWidget)w, ev, "\n", 1);
}

static void
InsertNewLineAndIndent(Widget w, XEvent *ev, String *params,
		       Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "InsertNewLineAndIndent\n"));

    if ((!Text_Editable(w)) || (Text_EditMode(w) == XmSINGLE_LINE_EDIT))
    {
	/* if we can't edit, or we are in single line only mode. */
	return;
    }

    DoInsert((XmTextWidget)w, ev, "\n", 1);
}

static void
MovePageLeft(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MovePageLeft\n"));
}

static void
MovePageRight(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "MovePageRight\n"));
}

static void
PasteClipboard(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "PasteClipboard\n"));
}

static void
TraversePrevTabGroup(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "TraversePrevTabGroup\n"));

    XtCallActionProc(w, "PrimitivePrevTabGroup", ev, params, *num_params);
}


static void
MovePreviousLine(Widget aw, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextWidget w = (XmTextWidget)aw;
    unsigned int index;
    XmTextPosition pos = Text_CursorPos(w);

    DEBUGOUT(XdbDebug(__FILE__, aw, "MovePreviousLine"));

    if (!Text_Editable(w))
    {
	return;
    }

    index = _XmTextGetTableIndex(w, pos);
    if (index == 0)
    {
	pos = Text_FirstPos(w);
    }
    else
    {
	pos += Text_LineTable(w)[index - 1].start_pos -
	    Text_LineTable(w)[index].start_pos;

	if (pos >= Text_LineTable(w)[index].start_pos)
	{
	    pos = Text_LineTable(w)[index].start_pos - 1;
	}
    }
    if (pos != Text_CursorPos(w))
    {
	(*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w), off);

	_XmTextSetCursorPosition((Widget)w, pos);
    }
}


static void
MovePreviousPage(Widget aw, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextWidget w = (XmTextWidget)aw;
    unsigned int index;
    XmTextPosition pos = Text_CursorPos(w);
    int delta = Text_LineCount(w) - 1;

    DEBUGOUT(XdbDebug(__FILE__, aw, "MovePreviousPage"));

    if (!Text_Editable(w))
    {
	return;
    }

    index = _XmTextGetTableIndex(w, pos);
    if (index < delta)
    {
	pos = Text_FirstPos(w);
    }
    else
    {
	pos += Text_LineTable(w)[index - delta].start_pos -
	    Text_LineTable(w)[index].start_pos;

	if (pos >= Text_LineTable(w)[index - delta + 1].start_pos)
	{
	    pos = Text_LineTable(w)[index - delta + 1].start_pos - 1;
	}
    }
    if (pos != Text_CursorPos(w))
    {
	(*Text_Output(w)->DrawInsertionPoint) (w, Text_CursorPos(w), off);

	_XmTextSetCursorPosition((Widget)w, pos);
    }
}

static void
ProcessBDrag(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessBDrag\n"));
}

static void
ProcessDown(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessDown\n"));

    MoveNextLine(w, ev, params, num_params);
}

static void
ProcessHome(Widget w, XEvent *ev, String *params,
	    Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessHome\n"));

    MoveToLineStart(w, ev, params, num_params);
}

static void
ProcessReturn(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessReturn\n"));

    InsertNewLine(w, ev, params, num_params);
}

static void
ProcessTab(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessTab\n"));
    if (Text_EditMode(w) == XmSINGLE_LINE_EDIT)
    {
    	_XmMgrTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
    }
    else
    {
    	DoInsert((XmTextWidget)w, ev, "\t", 1);
    }
}

static void
ProcessUp(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ProcessUp\n"));

    MovePreviousLine(w, ev, params, num_params);
}

static void
RedrawDisplay(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    xmTextClassRec.core_class.expose(w, NULL, (Region)NULL);
}

static void
ScrollOneLineUp(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ScrollOneLineUp\n"));
}

static void
SecondaryAdjust(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "SecondaryAdjust\n"));
}

static void
SecondaryNotify(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "SecondaryNotify\n"));
}


static void
SelectAll(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "SelectAll\n"));
}

static void
StartExtendSelection(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "StartExtendSelection\n"));

    DoGrabFocus(w, ev, params, num_params);
}



static void
SelfInsert(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
#define INSERTCHARBUFSIZ 32
    char buf[INSERTCHARBUFSIZ];
    KeySym keysym;
    int len, status;

    DEBUGOUT(XdbDebug(__FILE__, w, "SelfInsert"));

    len = XmImMbLookupString((Widget)w, (XKeyPressedEvent *)ev, buf, INSERTCHARBUFSIZ, &keysym, &status);

    if (XdbInDebug(__FILE__, w))
    {
	DEBUGOUT(XdbDebug(__FILE__, w, "XmImMbLookupString => %d\n", len));
	DEBUGOUT(XdbDebug0(__FILE__, w, "\tStatus %s\n",
			   (status == XLookupNone) ? "none" :
			   (status == XLookupChars) ? "chars" :
			   (status == XLookupBoth) ? "both" :
			   (status == XLookupKeySym) ? "keysym" :
			   (status == XBufferOverflow) ? "overflow" : "????"));

	if (status == XLookupBoth || status == XLookupKeySym)
	{
	    DEBUGOUT(XdbDebug0(__FILE__, w, "\tKeySym 0x%X\n", keysym));
	}

	if (len > 0)
	{
	    int i;

	    DEBUGOUT(XdbDebug0(__FILE__, w, "\tBuffer "));
	    for (i = 0; i < len; i++)
	    {
		DEBUGOUT(XdbDebug(__FILE__, w, " %X", 0xFF & buf[i]));
	    }
	    DEBUGOUT(XdbDebug0(__FILE__, w, "\n"));
	}
    }

    if (len > 0)
    {				/* FIX ME */
	if (status == XLookupBoth || status == XLookupChars)
	{
	    DoInsert((XmTextWidget)w, ev, buf, len);
	}
    }
}

/* This is the insertion point for secondary selections */
static void
SetAnchor(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "SetAnchor\n"));

    Text_DestPosition(w) = (*Text_Output(w)->XYToPos)
	((XmTextWidget)w, ev->xbutton.x, ev->xbutton.y);
}

static void
SetCursorPosition(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "SetCursorPosition\n"));
}

static void
SetSelectionHint(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "SetSelectionHint\n"));
}

static void
ScrollOneLineDown(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ScrollOneLineDown\n"));
}

static void
ToggleAddMode(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "ToggleAddMode\n"));
}

static void
TextLeave(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "TextLeave\n"));
}

static void
TraverseHome(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "TraverseHome\n"));

    XtCallActionProc(w, "PrimitiveTraverseHome", ev, params, *num_params);
}

static void
TraverseDown(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "TraverseDown\n"));

    XtCallActionProc(w, "PrimitiveTraverseDown", ev, params, *num_params);
}

static void
TraverseUp(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "TraverseUp\n"));

    XtCallActionProc(w, "PrimitiveTraverseUp", ev, params, *num_params);
}

static void
TextFocusIn(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextVerifyCallbackStruct cbs;
    OutputData o = Text_OutputData(w);

    DEBUGOUT(XdbDebug(__FILE__, w, "TextFocusIn"));

    if (Text_Editable(w))
    {
	XmImSetFocusValues(w, NULL, 0);
    }

    if (!Out_HasFocus(o))
    {
	if (Text_FocusCallback(w))
	{
	    cbs.reason = XmCR_FOCUS;
	    cbs.event = ev;
	    cbs.currInsert = cbs.newInsert = Text_CursorPos(w);
	    cbs.startPos = cbs.endPos = 0;
	    cbs.text = NULL;

	    XtCallCallbackList((Widget)w, Text_FocusCallback(w), &cbs);
	}

	XtCallActionProc((Widget)w, "PrimitiveFocusIn", ev,
			 params, *num_params);

	Out_HasFocus(o) = True;
    }
}

static void
TextFocusOut(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    XmTextVerifyCallbackStruct cbs;
    OutputData o = Text_OutputData(w);

    DEBUGOUT(XdbDebug(__FILE__, w, "TextFocusOut"));

    if (Text_Editable(w))
    {
	XmImUnsetFocus(w);
    }

    if (Out_HasFocus(o))
    {
	if (Text_LosingFocusCallback(w))
	{
	    cbs.reason = XmCR_LOSING_FOCUS;
	    cbs.event = ev;
	    cbs.currInsert = cbs.newInsert = Text_CursorPos(w);
	    cbs.startPos = cbs.endPos = 0;
	    cbs.text = NULL;

	    XtCallCallbackList((Widget)w, Text_LosingFocusCallback(w), &cbs);
	}

	XtCallActionProc((Widget)w, "PrimitiveFocusOut", ev,
			 params, *num_params);

	Out_HasFocus(o) = False;
    }
}

static void
UnKill(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "UnKill\n"));
}

static void
VoidAction(Widget w, XEvent *ev, String *params, Cardinal *num_params)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "VoidAction\n"));
}


/*
 * Quasi-Public functions -----------------------------------------------------
 */


Widget
_XmTextGetDropReciever(Widget w)
{
    /* FIX ME */
    return w;
}

Boolean
_XmTextHasDestination(Widget w)
{
    /* FIX ME */
    return False;
}

void
_XmTextInputGetSecResData(XmSecondaryResourceData *secResDataRtn)
{
}

XmTextPosition
_XmTextGetAnchor(XmTextWidget tw)
{
    InputData i = Text_InputData(tw);
    return In_HighlightPivot(i);
}

Boolean
_XmTextSetDestinationSelection(Widget w,
			       XmTextPosition position,
			       Boolean disown,
			       Time set_time)
{
    /* FIX ME */
    return False;
}

Boolean
_XmTextSetSel2(XmTextWidget tw,
	       XmTextPosition left,
	       XmTextPosition right,
	       Time set_time)
{
    /* FIX ME */
    return False;
}

Boolean
_XmTextGetSel2(XmTextWidget tw,
	       XmTextPosition *left,
	       XmTextPosition *right)
{
    /* FIX ME */
    return False;
}
