[comp.windows.x] Athena Text Widget with vi-like functionality

ebina@URBANA.MCD.MOT.COM (Eric Bina) (10/14/90)

I have been told that what I have done is similar to resurfacing a highway
to support horses.   But then, some of us like horses.


Anyways, what we have here is the Athena Text Widget with some modifications
(hacks, corruptions, etc.) so that it will support vi-like modal editing.

Please note that the vi-like changes in no way hinder the nasty ( <- my opinion)
emacs-like editing functions that are already there.

To switch between the editing modes use Ctrl-X.  This was the only Control Key
I could find that neither editor uses.

Upon startup the Text Widget looks for a resource startMode(Class: StartMode)
to decide which of the edit modes to start in.  The default is the old
emacs-like mode.  The other possible values are "vi" and "insert" which
represent the vi command and insert modes.  I really should have added a
converter for this resource, but I was being a minimalist (lazy) again.

These changes involve minor changes to Text.c, Text.h, TextP.h (mostly just to
add a resource which specifies what edit mode to start in), the addition of
several large translation tables to TextTr.c, and the addition of several new
actions to TextAction.c

Wherever possible I tried to use the emacsian actions that were already there.
As a result the vi-like functions are limited (crippled).  I could, and
probably will write a better version which will involve MAJOR changes to
TextAction.c.  But for all you minimalists out there, these are the minimal
changes I needed to make to get the minimal functionality that I required.

Functions implemented:
Ctrl-X		Switch to emacs-like mode.
Ctrl-T		Transpose characters.  This was a nice little function already
		built into the current text widget that I didn't want to
		waste.  Especially since 'xp' will no longer transpose
		characters because the cursor in the text widget is between
		characters, instead of on a character as in vi.
Ctrl-P		Form a paragraph.  Another built-in that was already there that
		I didn't want to waste.  I takes all the text between the
		previous and next blank lines and packs as many words as it can
		on a line, breaking at whole word boundries.
Ctrl-L		Redraw the text window.  This also centers the current line in
		the text window since that was what the currently existing
		function did.
Ctrl-F		Move foreward one screen of text.
Ctrl-B		Move backward one screen of text.
Ctrl-D		Scroll foreward one line of text.  Sorry, there was no builtin
		for half pages, and I didn't feel it was necessary enough to
		write one.
Ctrl-U		Scroll backward one line of text.  Sorry, there was no builtin
		for half pages, and I didn't feel it was necessary enough to
		write one.
Ctrl-H		Move backward one character.
BackSpace	Whatever your backspace character is will move backward
		one character.
Cursor-Left	Move backward one character.
h		Move backward one character.
l		Move foreward one character.
Space		Move foreward one character.
Cursor-Right	Move foreward one character.
Cursor-Up	Move up one line.
k		Move up one line.
j		Move down one line.
Cursor-Down	Move down one line.
LineFeed	Move down one line.
Return		Move to the beginning of the next line.
+		Move to the beginning of the next line.
-		Move to the beginning of the previous line.
0		Move to the beginning of the current line.
$		Move to the end of the current line.
G		Move to the end of the file.
g		Move to the beginning of the file.  This isn't in vi, but since
		implementing <count>G would be major changes, I needed a way
		to do a '1G', and this was it.
w or W		Move to the beginning of the next word.  Words are deliniated
		by white space.
e or E		Move to the end of the next word.  Words are deliniated
		by white space.
b or B		Move to the beginning of the previous word.  Words are
		deliniated by white space.
}		Move to the beginning of the next paragraph.  Paragraphs are
		delineated by blank lines.
{		Move to the beginning of the previous paragraph.  Paragraphs are
		delineated by blank lines.
D		Delete to end of current line.
dd		Delete all of current line.
dw		Delete next word.
db		Delete previous word.
d}		Delete next paragraph.  There was no builtin to delete previous
		paragraph, and I didn't think it was essential.
C		Change text to end of current line.
cc		Change text in all of current line.
cw		Change next word.
cb		Change previous word.
c}		Change next paragraph.
Y		Yank text in all of current line.  I know this is inconsistent,
		but it is what vi does.
yy		Yank text in all of current line.
yw		Yank next word.
yb		Yank previous word.
y}		Yank next paragraph.
p or P		insert last yanked or deleted text.  Since the cursor is always
		between two characters there is no put before and put after.
z.		Center current line in text window.  There was already a builtin
		for this one, but there were none for 'z-' and 'z<CR>' so I
		didn't implement those.
"[a-f]		Place text from next yank, delete, or change command into
		buffer '[a-f]', or get text for next put command from
		buffer '[a-f]'.  These are all the buffers you get, since
		I am using CUT_BUFFERS 2-7.  Just getting this required
		more hacking than I wanted to do, but I felt I needed a few
		buffers.  Note that you should be able to yank into a buffer
		in one text widget, and then put from that buffer in any text
		widget on the same display.
s		Substitute arbitrary amount of text for next character.
S		Substitute arbitrary amount of text for current line.
r		Replace next character.
R		Replace arbitrary amount of characters.
i or a		Insert mode.  There is no before and after, so these now have
		the same function.
I		Insert at beginning of current line.
A		Append at end of current line.
o		Open a new line after current line.
O		Open a new line before current line.
x		Delete next character.
X		Delete previous character.
/		Search forward.
?		Search backward.
J		Join next line to end of current line.
[2-9]		Multiply the next operation this many times (default 1).
		This is a big difference from vi that allows an arbitrary
		count.  If you need to do something more than 9 times you
		can stack these operations. '25dd' will delete the next
		2 * 5 = 10 lines.  Likewise '2222j' will move down 16 lines.
		I know this is awkward, but it was a builtin I could use
		instead of writing new code.


I'm sure you will all find many minor (and major) differences between the 
functions of this widget and vi.  I will try and point out a few major ones
here:

     vi treats the end of a line as 'special' somehow.  Cursor movements and
deletions stop at the endpoints of lines unless the are commands that work on
multiple lines.  In the text widget the end of a line is just a <CR> character.
And you can move over it, delete it, or replace it just like any other
character.

     When you delete a line you are just deleting a range of
characters and a <CR>, and that is what is in your cut buffer.  So doing a
put of a deleted or yanked line will not do what you expect unless you are
at the beginning of a line.

     As has already been pointed out, counts before operations are completely
different.

     Also, the change commands do not give the dollar sign to represent the
range being changed.  They immediately delete the range, and then leave you in
insert mode.

     There is no undo!

     There is no '.'!

     There are no 'f' and 't' functions.


Send comments, complaints, suggestions to
Eric Bina
ebina@urbana.mcd.mot.com


Context diffs for Text.c, Text.h, TextP.h, TextAction.c, and TextTr.c follow:

*** Text.c	Sat Oct 13 16:00:54 1990
--- lib/Xaw/Text.c	Sat Oct 13 16:00:13 1990
***************
*** 99,104 ****
--- 99,108 ----
  static caddr_t defaultSelectTypesPtr = (caddr_t)defaultSelectTypes;
  extern char *_XawDefaultTextTranslations1, *_XawDefaultTextTranslations2,
    *_XawDefaultTextTranslations3;
+ extern char *_XawViTextTranslations1, *_XawViTextTranslations2,
+   *_XawViTextTranslations3, *_XawViTextTranslations4;
+ extern char *_XawViInsertTranslations1;
+ char *DefaultTrans, *ViTrans, *InsertTrans;
  static Dimension defWidth = 100;
  static Dimension defHeight = DEFAULT_TEXT_HEIGHT;
  
***************
*** 138,143 ****
--- 142,149 ----
       offset(text.scroll_horiz), XtRImmediate, (caddr_t) XawtextScrollNever},
    {XtNwrap, XtCWrap, XtRWrapMode, sizeof(XawTextWrapMode),
       offset(text.wrap), XtRImmediate, (caddr_t) XawtextWrapNever},
+   {XtNstartMode, XtCStartMode, XtRString, sizeof(String),
+      offset(text.start_mode), XtRString, "default"},
    {XtNresize, XtCResize, XtRResizeMode, sizeof(XawTextResizeMode),
       offset(text.resize), XtRImmediate, (caddr_t) XawtextResizeNever},
    {XtNautoFill, XtCAutoFill, XtRBoolean, sizeof(Boolean),
***************
*** 273,281 ****
    int len1 = strlen (_XawDefaultTextTranslations1);
    int len2 = strlen (_XawDefaultTextTranslations2);
    int len3 = strlen (_XawDefaultTextTranslations3);
!   char *buf = XtMalloc (len1 + len2 + len3 + 1);
!   char *cp = buf;
  
    XawInitializeWidgetSet();
  
  /* 
--- 279,295 ----
    int len1 = strlen (_XawDefaultTextTranslations1);
    int len2 = strlen (_XawDefaultTextTranslations2);
    int len3 = strlen (_XawDefaultTextTranslations3);
!   int vilen1 = strlen (_XawViTextTranslations1);
!   int vilen2 = strlen (_XawViTextTranslations2);
!   int vilen3 = strlen (_XawViTextTranslations3);
!   int vilen4 = strlen (_XawViTextTranslations4);
!   int inlen1 = strlen (_XawViInsertTranslations1);
!   char *cp;
  
+   DefaultTrans = (char *)XtMalloc (len1 + len2 + len3 + 1);
+   ViTrans = (char *)XtMalloc (vilen1 + vilen2 + vilen3 + vilen4 + 1);
+   InsertTrans = (char *)XtMalloc (inlen1 + 1);
+ 
    XawInitializeWidgetSet();
  
  /* 
***************
*** 284,293 ****
  
    textClassRec.core_class.num_actions = textActionsTableCount;
    
    strcpy (cp, _XawDefaultTextTranslations1); cp += len1;
    strcpy (cp, _XawDefaultTextTranslations2); cp += len2;
    strcpy (cp, _XawDefaultTextTranslations3);
!   textWidgetClass->core_class.tm_table = buf;
  
    XtAddConverter(XtRString, XtRScrollMode, CvtStringToScrollMode, NULL, 0);
    XtAddConverter(XtRString, XtRWrapMode,   CvtStringToWrapMode,   NULL, 0);
--- 298,316 ----
  
    textClassRec.core_class.num_actions = textActionsTableCount;
    
+   cp = DefaultTrans;
    strcpy (cp, _XawDefaultTextTranslations1); cp += len1;
    strcpy (cp, _XawDefaultTextTranslations2); cp += len2;
    strcpy (cp, _XawDefaultTextTranslations3);
!   cp = ViTrans;
!   strcpy (cp, _XawViTextTranslations1); cp += vilen1;
!   strcpy (cp, _XawViTextTranslations2); cp += vilen2;
!   strcpy (cp, _XawViTextTranslations3); cp += vilen3;
!   strcpy (cp, _XawViTextTranslations4);
!   cp = InsertTrans;
!   strcpy (cp, _XawViInsertTranslations1);
! 
!   textWidgetClass->core_class.tm_table = DefaultTrans;
  
    XtAddConverter(XtRString, XtRScrollMode, CvtStringToScrollMode, NULL, 0);
    XtAddConverter(XtRString, XtRWrapMode,   CvtStringToWrapMode,   NULL, 0);
***************
*** 515,520 ****
--- 538,562 ----
      }
      else if (ctx->text.scroll_horiz == XawtextScrollAlways)
        CreateHScrollBar(ctx);
+ 
+   if ((strcmp(ctx->text.start_mode, "default") == 0)||
+       (strcmp(ctx->text.start_mode, "Default") == 0))
+   {
+ 	XtOverrideTranslations(ctx, XtParseTranslationTable(DefaultTrans));
+   }
+   else if ((strcmp(ctx->text.start_mode, "vi") == 0)||
+       (strcmp(ctx->text.start_mode, "Vi") == 0)||
+       (strcmp(ctx->text.start_mode, "VI") == 0))
+   {
+ 	XtOverrideTranslations(ctx, XtParseTranslationTable(ViTrans));
+   }
+   else if ((strcmp(ctx->text.start_mode, "viinsert") == 0)||
+       (strcmp(ctx->text.start_mode, "ViInsert") == 0)||
+       (strcmp(ctx->text.start_mode, "Insert") == 0)||
+       (strcmp(ctx->text.start_mode, "insert") == 0))
+   {
+ 	XtOverrideTranslations(ctx, XtParseTranslationTable(InsertTrans));
+   }
  }
  
  static void 
***************
*** 2736,2741 ****
--- 2778,2805 ----
    if (oldtw->text.insertPos != newtw->text.insertPos) {
      newtw->text.showposition = TRUE;
      redisplay = TRUE;
+   }
+ 
+   if (oldtw->text.start_mode != newtw->text.start_mode)
+   {
+     if ((strcmp(newtw->text.start_mode, "default") == 0)||
+       (strcmp(newtw->text.start_mode, "Default") == 0))
+     {
+ 	XtOverrideTranslations(newtw, XtParseTranslationTable(DefaultTrans));
+     }
+     else if ((strcmp(newtw->text.start_mode, "vi") == 0)||
+       (strcmp(newtw->text.start_mode, "Vi") == 0)||
+       (strcmp(newtw->text.start_mode, "VI") == 0))
+     {
+ 	XtOverrideTranslations(newtw, XtParseTranslationTable(ViTrans));
+     }
+     else if ((strcmp(newtw->text.start_mode, "viinsert") == 0)||
+       (strcmp(newtw->text.start_mode, "ViInsert") == 0)||
+       (strcmp(newtw->text.start_mode, "Insert") == 0)||
+       (strcmp(newtw->text.start_mode, "insert") == 0))
+     {
+ 	XtOverrideTranslations(newtw, XtParseTranslationTable(InsertTrans));
+     }
    }
    
    _XawTextExecuteUpdate(newtw);
*** Text.h	Sat Oct 13 16:00:54 1990
--- lib/Xaw/Text.h	Sat Oct 13 16:00:13 1990
***************
*** 97,102 ****
--- 97,103 ----
  #define XtNselection "selection"
  #define XtNtopMargin "topMargin"
  #define XtNwrap "wrap"
+ #define XtNstartMode "startMode"
  
  #define XtCAutoFill "AutoFill"
  #define XtCResize "Resize"
***************
*** 103,108 ****
--- 104,110 ----
  #define XtCScroll "Scroll"
  #define XtCSelectTypes "SelectTypes"
  #define XtCWrap "Wrap"
+ #define XtCStartMode "StartMode"
  
  /* Return Error code for XawTextSearch */
  
*** TextP.h	Sat Oct 13 16:00:54 1990
--- lib/Xaw/TextP.h	Sat Oct 13 16:00:13 1990
***************
*** 160,165 ****
--- 160,166 ----
      Boolean             auto_fill;           /* Auto fill mode? */
      XawTextScrollMode   scroll_vert, scroll_horiz; /*what type of scrollbars.*/
      XawTextWrapMode     wrap;            /* The type of wrapping. */
+     char     		*start_mode;         /* What edit mode to start in. */
      XawTextResizeMode   resize;	             /* what to resize */
      XawTextMargin       r_margin;            /* The real margins. */
      
*** TextAction.c	Sat Oct 13 16:00:54 1990
--- lib/Xaw/TextAction.c	Sat Oct 13 16:00:13 1990
***************
*** 71,76 ****
--- 71,82 ----
  void _XawTextSetScrollBars(), _XawTextClearAndCenterDisplay();
  Atom * _XawTextSelectionList();
  
+ extern char *DefaultTrans, *ViTrans, *InsertTrans;
+ 
+ int ReplaceCnt;
+ int Infinite;
+ int DeleteBuf = 1;
+ 
  static void
  StartAction(ctx, event)
  TextWidget ctx;
***************
*** 112,117 ****
--- 118,124 ----
    _XawTextCheckResize(ctx);
    _XawTextExecuteUpdate(ctx);
    ctx->text.mult = 1;
+   DeleteBuf = 1;
  }
  
  
***************
*** 273,278 ****
--- 280,318 ----
    EndAction((TextWidget)w);
  }
  
+ static char *BufNames[] = {
+ 	"CUT_BUFFER0",
+ 	"CUT_BUFFER1",
+ 	"CUT_BUFFER2",
+ 	"CUT_BUFFER3",
+ 	"CUT_BUFFER4",
+ 	"CUT_BUFFER5",
+ 	"CUT_BUFFER6",
+ 	"CUT_BUFFER7"
+ };
+ 
+ static void 
+ InsertChosenSelection(w, event, params, num_params)
+ Widget w;
+ XEvent *event;
+ String *params;		/* precedence list of selections to try */
+ Cardinal *num_params;
+ {
+   char *bptr;
+ 
+   StartAction((TextWidget)w, event); /* Get Time. */
+   if ((DeleteBuf >= 0)&&(DeleteBuf < 8))
+   {
+ 	bptr = BufNames[DeleteBuf];
+   }
+   else
+   {
+ 	bptr = BufNames[1];
+   }
+   GetSelection(w, ((TextWidget)w)->text.time, &bptr, 1);
+   EndAction((TextWidget)w);
+ }
+ 
  /************************************************************
   *
   * Routines for Moving Around.
***************
*** 553,559 ****
    
    if (kill && from < to) {
      ptr = _XawTextGetText(ctx, from, to);
!     XStoreBuffer(XtDisplay(ctx), ptr, strlen(ptr), 1);
      XtFree(ptr);
    }
    text.length = 0;
--- 593,599 ----
    
    if (kill && from < to) {
      ptr = _XawTextGetText(ctx, from, to);
!     XStoreBuffer(XtDisplay(ctx), ptr, strlen(ptr), DeleteBuf);
      XtFree(ptr);
    }
    text.length = 0;
***************
*** 602,607 ****
--- 642,697 ----
    EndAction(ctx);
  }
  
+ static void 
+ _Yank(ctx, from, to, kill)
+ TextWidget ctx;
+ XawTextPosition from, to;
+ Boolean	kill;
+ {
+   char *ptr;
+   
+   if (kill && from < to) {
+     ptr = _XawTextGetText(ctx, from, to);
+     XStoreBuffer(XtDisplay(ctx), ptr, strlen(ptr), DeleteBuf);
+     XtFree(ptr);
+   }
+ }
+ 
+ static void
+ Yank(ctx, event, dir, type, include, kill)
+ TextWidget	   ctx;
+ XEvent *event;
+ XawTextScanDirection dir;
+ XawTextScanType type;
+ Boolean	   include, kill;
+ {
+   XawTextPosition from, to;
+   
+   StartAction(ctx, event);
+   to = SrcScan(ctx->text.source, ctx->text.insertPos,
+ 	       type, dir, ctx->text.mult, include);
+ 
+ /*
+  * If no movement actually happened, then bump the count and try again. 
+  * This causes the character position at the very beginning and end of 
+  * a boundry to act correctly. 
+  */
+ 
+   if (to == ctx->text.insertPos)
+       to = SrcScan(ctx->text.source, ctx->text.insertPos,
+ 		   type, dir, ctx->text.mult + 1, include);
+ 
+   if (dir == XawsdLeft) {
+     from = to;
+     to = ctx->text.insertPos;
+   }
+   else 
+     from = ctx->text.insertPos;
+ 
+   _Yank(ctx, from, to, kill);
+   EndAction(ctx);
+ }
+ 
  /*ARGSUSED*/
  static void 
  DeleteForwardChar(w, event, p, n)
***************
*** 650,655 ****
--- 740,767 ----
  
  /*ARGSUSED*/
  static void 
+ KillForwardChar(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   DeleteOrKill((TextWidget) w, event, XawsdRight, XawstPositions, TRUE, TRUE);
+ }
+ 
+ /*ARGSUSED*/
+ static void
+ KillBackwardChar(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   DeleteOrKill((TextWidget) w, event, XawsdLeft, XawstPositions, TRUE, TRUE);
+ }
+ 
+ /*ARGSUSED*/
+ static void 
  KillForwardWord(w, event, p, n)
  Widget w;
  XEvent *event;
***************
*** 697,702 ****
--- 809,836 ----
  
  /*ARGSUSED*/
  static void
+ KillLine(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   TextWidget ctx = (TextWidget) w;
+   XawTextPosition end_of_line;
+ 
+   StartAction(ctx, event);
+   ctx->text.insertPos = SrcScan(ctx->text.source, ctx->text.insertPos,
+ 				XawstEOL, XawsdLeft, 1, FALSE);
+   end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL, 
+ 			XawsdRight, ctx->text.mult, TRUE);
+ 
+   _DeleteOrKill(ctx, ctx->text.insertPos, end_of_line, TRUE);
+   EndAction(ctx);
+   _XawTextSetScrollBars(ctx);
+ }
+ 
+ /*ARGSUSED*/
+ static void 
  KillToEndOfParagraph(w, event, p, n)
  Widget w;
  XEvent *event;
***************
*** 706,711 ****
--- 840,924 ----
    DeleteOrKill((TextWidget) w, event, XawsdRight, XawstParagraph, FALSE, TRUE);
  }
  
+ /*ARGSUSED*/
+ static void 
+ YankForwardWord(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   Yank((TextWidget) w, event, 
+ 	       XawsdRight, XawstWhiteSpace, FALSE, TRUE);
+ }
+ 
+ /*ARGSUSED*/
+ static void 
+ YankBackwardWord(w, event, p, n)
+ TextWidget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   Yank((TextWidget) w, event,
+ 	       XawsdLeft, XawstWhiteSpace, FALSE, TRUE);
+ }
+ 
+ /*ARGSUSED*/
+ static void
+ YankToEndOfLine(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   TextWidget ctx = (TextWidget) w;
+   XawTextPosition end_of_line;
+ 
+   StartAction(ctx, event);
+   end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL, 
+ 			XawsdRight, ctx->text.mult, FALSE);
+   if (end_of_line == ctx->text.insertPos)
+     end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL, 
+ 			  XawsdRight, ctx->text.mult, TRUE);
+ 
+   _Yank(ctx, ctx->text.insertPos, end_of_line, TRUE);
+   EndAction(ctx);
+ }
+ 
+ /*ARGSUSED*/
+ static void
+ YankLine(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   TextWidget ctx = (TextWidget) w;
+   XawTextPosition start_of_line;
+   XawTextPosition end_of_line;
+ 
+   StartAction(ctx, event);
+   start_of_line = SrcScan(ctx->text.source, ctx->text.insertPos,
+ 				XawstEOL, XawsdLeft, 1, FALSE);
+   end_of_line = SrcScan(ctx->text.source, ctx->text.insertPos, XawstEOL, 
+ 			XawsdRight, ctx->text.mult, TRUE);
+ 
+   _Yank(ctx, start_of_line, end_of_line, TRUE);
+   EndAction(ctx);
+ }
+ 
+ /*ARGSUSED*/
+ static void 
+ YankToEndOfParagraph(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   Yank((TextWidget) w, event, XawsdRight, XawstParagraph, FALSE, TRUE);
+ }
+ 
  void 
  _XawTextZapSelection(ctx, event, kill)
  TextWidget ctx;
***************
*** 1077,1082 ****
--- 1290,1412 ----
  
  /*ARGSUSED*/
  static void
+ DefaultMode(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   TextWidget ctx = (TextWidget) w;
+   XtOverrideTranslations(w, XtParseTranslationTable(DefaultTrans));
+ }
+ 
+ /*ARGSUSED*/
+ static void
+ ViMode(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   TextWidget ctx = (TextWidget) w;
+   XtOverrideTranslations(w, XtParseTranslationTable(ViTrans));
+ }
+ 
+ /*ARGSUSED*/
+ static void
+ ReplaceTick(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   char strbuf[BUFSIZ];
+   KeySym keysym;
+   int keylen;
+ 
+   keylen = XLookupString (&event->xkey, strbuf, BUFSIZ,
+ 			       &keysym, &compose_status);
+   if ((ReplaceCnt > 0)&&(keylen != 0))
+   {
+     ReplaceCnt--;
+     if (ReplaceCnt == 0)
+     {
+ 	if (Infinite)
+ 	{
+ 	  ReplaceCnt = 1;
+ 	}
+ 	else
+ 	{
+ 	  XtOverrideTranslations(w, XtParseTranslationTable(ViTrans));
+ 	}
+     }
+   }
+ }
+ 
+ /*ARGSUSED*/
+ static void
+ MayDelete(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   char strbuf[BUFSIZ];
+   KeySym keysym;
+   int keylen;
+ 
+   keylen = XLookupString (&event->xkey, strbuf, BUFSIZ,
+ 			       &keysym, &compose_status);
+   if ((ReplaceCnt > 0)&&(keylen != 0))
+   {
+     DeleteOrKill((TextWidget)w, event, XawsdRight, XawstPositions, TRUE, FALSE);
+   }
+ }
+ 
+ /*ARGSUSED*/
+ static void
+ InsertMode(w, event, p, n)
+ Widget w;
+ XEvent *event;
+ String *p;
+ Cardinal *n;
+ {
+   TextWidget ctx = (TextWidget) w;
+   int cnt;
+ 
+   if (*n != 1) {
+     XtAppError(XtWidgetToApplicationContext(w), 
+ 	       "The insert action takes exactly one argument.");
+     XBell(XtDisplay(w), 0);
+     return;
+   }
+ 
+   if ((p[0][0] == 'I')||(p[0][0] == 'i'))
+   {
+     cnt = 1;
+     Infinite = 1;
+   }
+   else
+   {
+     cnt = atoi(p[0]);
+     cnt = cnt * ctx->text.mult;
+     ctx->text.mult = 1;
+     Infinite = 0;
+   }
+   if ( cnt < 0 ) {
+     char buf[BUFSIZ];
+     sprintf(buf, "%s %s", "Text Widget: The insert action's argument",
+ 	    "must be a number greater than or equal to zero, or 'INF'.");
+     XtAppError(XtWidgetToApplicationContext(w), buf);
+     XBell(XtDisplay(w), 0);
+     return;
+   }
+   XtOverrideTranslations(w, XtParseTranslationTable(InsertTrans));
+   ReplaceCnt = cnt;
+ }
+ 
+ /*ARGSUSED*/
+ static void
  InsertChar(w, event, p, n)
  Widget w;
  XEvent *event;
***************
*** 1258,1263 ****
--- 1588,1625 ----
    ctx->text.mult *= mult;
  }
  
+ /* ARGSUSED */
+ static void 
+ SelectBuffer(w, event, params, num_params)
+ Widget w;
+ XEvent *event;
+ String * params;
+ Cardinal * num_params;
+ {
+   TextWidget ctx = (TextWidget) w;
+   int buf;
+ 
+   if (*num_params != 1) {
+     XtAppError(XtWidgetToApplicationContext(w), 
+ 	       "The buffer-select action takes exactly one argument.");
+     XBell(XtDisplay(w), 0);
+     return;
+   }
+ 
+   buf = atoi(params[0]);
+ 
+   if ((buf < 0)||(buf > 7)) {
+     char buf[BUFSIZ];
+     sprintf(buf, "%s %s", "Text Widget: The buffer-select action's argument",
+ 	    "must be a number greater than or equal to zero and less than eight.");
+     XtAppError(XtWidgetToApplicationContext(w), buf);
+     XBell(XtDisplay(w), 0);
+     return;
+   }
+ 
+   DeleteBuf = buf;
+ }
+ 
  /*	Function Name: StripOutOldCRs
   *	Description: strips out the old carrige returns.
   *	Arguments: ctx - the text widget.
***************
*** 1571,1581 ****
--- 1933,1952 ----
    {"delete-previous-word", 	DeleteBackwardWord},
    {"delete-selection", 		DeleteCurrentSelection},
  /* kill bindings */
+   {"kill-next-character", 	KillForwardChar},
+   {"kill-previous-character",	KillBackwardChar},
    {"kill-word", 		KillForwardWord},
    {"backward-kill-word", 	KillBackwardWord},
    {"kill-selection", 		KillCurrentSelection},
    {"kill-to-end-of-line", 	KillToEndOfLine},
+   {"kill-line", 		KillLine},
    {"kill-to-end-of-paragraph", 	KillToEndOfParagraph},
+ /* yank bindings */
+   {"yank-word", 		YankForwardWord},
+   {"backward-yank-word", 	YankBackwardWord},
+   {"yank-to-end-of-line", 	YankToEndOfLine},
+   {"yank-line", 		YankLine},
+   {"yank-to-end-of-paragraph", 	YankToEndOfParagraph},
  #ifdef XAW_BC
  /* unkill bindings */
    {"unkill", 			UnKill},
***************
*** 1596,1605 ****
--- 1967,1983 ----
    {"extend-adjust", 		ExtendAdjust},
    {"extend-end", 		ExtendEnd},
    {"insert-selection",		InsertSelection},
+   {"insert-chosen-selection",	InsertChosenSelection},
  /* Miscellaneous */
    {"redraw-display", 		RedrawDisplay},
    {"insert-file", 		_XawTextInsertFile},
    {"search",		        _XawTextSearch},
+   {"select-buffer", 		SelectBuffer},
+   {"default-mode", 		DefaultMode},
+   {"vi-mode", 			ViMode},
+   {"may-delete", 		MayDelete},
+   {"replace-tick", 		ReplaceTick},
+   {"insert-mode", 		InsertMode},
    {"insert-char", 		InsertChar},
    {"insert-string",		InsertString},
    {"focus-in", 	 	        TextFocusIn},
*** TextTr.c	Sat Oct 13 16:00:55 1990
--- lib/Xaw/TextTr.c	Sat Oct 13 16:00:13 1990
***************
*** 22,27 ****
--- 22,28 ----
  Ctrl<Key>U:	multiply(4) \n\
  Ctrl<Key>V:	next-page() \n\
  Ctrl<Key>W:	kill-selection() \n\
+ Ctrl<Key>X:	vi-mode() \n\
  Ctrl<Key>Y:	insert-selection(CUT_BUFFER1) \n\
  Ctrl<Key>Z:	scroll-one-line-up() \n\
  ", *_XawDefaultTextTranslations2 = "\
***************
*** 65,67 ****
--- 66,195 ----
  <Btn3Motion>:	extend-adjust() \n\
  <Btn3Up>:	extend-end(PRIMARY, CUT_BUFFER0) \
  ";
+ 
+ char *_XawViTextTranslations1 =
+ "\
+ Ctrl<Key>X:	default-mode() \n\
+ Ctrl<Key>T:     transpose-characters() \n\
+ Ctrl<Key>P:     form-paragraph() \n\
+ Ctrl<Key>L:	redraw-display() \n\
+ Ctrl<Key>F:	next-page() \n\
+ Ctrl<Key>B:	previous-page() \n\
+ Ctrl<Key>U:	scroll-one-line-down() \n\
+ Ctrl<Key>D:	scroll-one-line-up() \n\
+ Ctrl<Key>H:	backward-character() \n\
+ :<Key>\",:<Key>0:	select-buffer(0) \n\
+ :<Key>\",:<Key>a:	select-buffer(2) \n\
+ :<Key>\",:<Key>b:	select-buffer(3) \n\
+ :<Key>\",:<Key>c:	select-buffer(4) \n\
+ :<Key>\",:<Key>d:	select-buffer(5) \n\
+ :<Key>\",:<Key>e:	select-buffer(6) \n\
+ :<Key>\",:<Key>f:	select-buffer(7) \n\
+ :<Key>\\:,:<Key>r:	insert-file() \n\
+ :<Key>z,:<Key>\.:	redraw-display() \n\
+ :<Key>y,:<Key>y:	yank-line() \n\
+ :<Key>y,:<Key>w:	yank-word() \n\
+ :<Key>y,:<Key>b:	backward-yank-word() \n\
+ :<Key>y,:<Key>\}:	yank-to-end-of-paragraph() \n\
+ :<Key>y,:<Key>\{:	yank-to-end-of-paragraph() \n\
+ :<Key>d,:<Key>d:	kill-line() \n\
+ :<Key>d,:<Key>w:	kill-word() \n\
+ :<Key>d,:<Key>b:	backward-kill-word() \n\
+ :<Key>d,:<Key>\}:	kill-to-end-of-paragraph() \n\
+ :<Key>d,:<Key>\{:	kill-to-end-of-paragraph() \n\
+ :<Key>c,:<Key>c:	kill-line() newline-and-backup() insert-mode(0) \n\
+ :<Key>c,:<Key>w:	kill-word() insert-mode(0) \n\
+ :<Key>c,:<Key>b:	backward-kill-word() insert-mode(0) \n\
+ :<Key>c,:<Key>\}:	kill-to-end-of-paragraph() insert-mode(0) \n\
+ :<Key>c,:<Key>\{:	kill-to-end-of-paragraph() insert-mode(0) \n\
+ ", *_XawViTextTranslations2 = "\
+ :<Key>Y:	yank-line() \n\
+ :<Key>D:	kill-to-end-of-line() \n\
+ :<Key>C:	kill-to-end-of-line() insert-mode(0) \n\
+ :<Key>e:	forward-word() \n\
+ :<Key>E:	forward-word() \n\
+ :<Key>w:	forward-word() forward-word() backward-word() \n\
+ :<Key>W:	forward-word() forward-word() backward-word() \n\
+ :<Key>b:	backward-word() \n\
+ :<Key>B:	backward-word() \n\
+ :<Key>\}:	forward-paragraph() \n\
+ :<Key>\{:	backward-paragraph() \n\
+ <Key>BackSpace:	backward-character() \n\
+ :<Key>h:	backward-character() \n\
+ :<Key>l:	forward-character() \n\
+ <Key>space:	forward-character() \n\
+ :<Key>j:	next-line() \n\
+ :<Key>k:	previous-line() \n\
+ :<Key>0:	beginning-of-line() \n\
+ :<Key>\$:	end-of-line() \n\
+ :<Key>g:	beginning-of-file() \n\
+ :<Key>G:	end-of-file() \n\
+ ", *_XawViTextTranslations3 = "\
+ :<Key>s:	kill-next-character() insert-mode(0) \n\
+ :<Key>S:	kill-line() newline-and-backup() insert-mode(0) \n\
+ :<Key>r:	insert-mode(1) \n\
+ :<Key>R:	insert-mode(INF) \n\
+ :<Key>i:	insert-mode(0) \n\
+ :<Key>I:	beginning-of-line() insert-mode(0) \n\
+ :<Key>a:	insert-mode(0) \n\
+ :<Key>A:	end-of-line() insert-mode(0) \n\
+ :<Key>o:	end-of-line() newline() insert-mode(0) \n\
+ :<Key>O:	beginning-of-line() newline-and-backup() insert-mode(0) \n\
+ :<Key>x:	kill-next-character() \n\
+ :<Key>X:	kill-previous-character() \n\
+ :<Key>\?:	search(backward) \n\
+ :<Key>\/:	search(forward) \n\
+ :<Key>p:	insert-chosen-selection() \n\
+ :<Key>P:	insert-chosen-selection() \n\
+ :<Key>J:	end-of-line() delete-next-character() \n\
+ :<Key>2:	multiply(2) \n\
+ :<Key>3:	multiply(3) \n\
+ :<Key>4:	multiply(4) \n\
+ :<Key>5:	multiply(5) \n\
+ :<Key>6:	multiply(6) \n\
+ :<Key>7:	multiply(7) \n\
+ :<Key>8:	multiply(8) \n\
+ :<Key>9:	multiply(9) \n\
+ ", *_XawViTextTranslations4 = "\
+ <Key>Escape:	multiply(Reset) \n\
+ <Key>Right:	forward-character() \n\
+ <Key>Left:	backward-character() \n\
+ <Key>Down:	next-line() \n\
+ <Key>Up:	previous-line() \n\
+ <Key>Delete:	kill-selection() \n\
+ <Key>Linefeed:	next-line() \n\
+ <Key>Return:	next-line() beginning-of-line() \n\
+ <Key>\+:	next-line() beginning-of-line() \n\
+ <Key>\-:	previous-line() beginning-of-line() \n\
+ <Key>:		no-op() \n\
+ <FocusIn>:	focus-in() \n\
+ <FocusOut>:	focus-out() \n\
+ <Btn1Down>:	select-start() \n\
+ <Btn1Motion>:	extend-adjust() \n\
+ <Btn1Up>:	extend-end(PRIMARY, CUT_BUFFER0) \n\
+ <Btn2Down>:	insert-selection(PRIMARY, CUT_BUFFER0) \n\
+ <Btn3Down>:	extend-start() \n\
+ <Btn3Motion>:	extend-adjust() \n\
+ <Btn3Up>:	extend-end(PRIMARY, CUT_BUFFER0) \
+ ";
+ 
+ char *_XawViInsertTranslations1 =
+ "\
+ Ctrl<Key>X:	default-mode() \n\
+ <Key>Escape:	vi-mode() \n\
+ <Key>BackSpace:	delete-previous-character() replace-tick() \n\
+ <Key>Delete:	delete-previous-character() replace-tick() \n\
+ <Key>Linefeed:	newline() replace-tick() \n\
+ <Key>Return:	newline() replace-tick() \n\
+ <Key>:		may-delete() insert-char() replace-tick() \n\
+ <FocusIn>:	focus-in() \n\
+ <FocusOut>:	focus-out() \n\
+ <Btn1Down>:	select-start() \n\
+ <Btn1Motion>:	extend-adjust() \n\
+ <Btn1Up>:	extend-end(PRIMARY, CUT_BUFFER0) \n\
+ <Btn2Down>:	insert-selection(PRIMARY, CUT_BUFFER0) \n\
+ <Btn3Down>:	extend-start() \n\
+ <Btn3Motion>:	extend-adjust() \n\
+ <Btn3Up>:	extend-end(PRIMARY, CUT_BUFFER0) \
+ ";
+