Tk Entry widget with emacs/readline style key-bindings

tnb2d@henson.cs.virginia.edu
Sun, 5 Jun 94 22:36:20 EDT

I'm sure I'm re-inventing a wheel somebody has written fifty
times over in tcl, but here is a Tk Entry widget (written using
Tkinter.py) that provides many of the emacs keybindings.
Specifically:

<Control-d> = delete next char
<Control-a> = beginning of line
<Control-e> = end of line
<Control-f> = forward one char
<Right> = " " "
<Control-b> = backward one char
<Left> = " " "
<Control-k> = kill to end of line
<Control-y> = yank last killed line
<Button-2> = insert primary selection

It keeps the <Backspace> and Button-1 dragging and such from the
regular Entry widget. Using <Button-2> you can swipe in any X
application or Tk widget and insert it into your EmacsEntry widget.
This is all pretty primitive, but I'm glad I have it and I thought
somebody else might be gald to have it too. (I would also like to
encourage as much widget sharing as possible!)
To use it just "from EmacsEntry import EmacsEntry". the
methods provided are really a first pass and python-izing these
Tkinter classes a little more. Please fix it if it's broke, and post
any and all suggestions about making it better.

---------%< snip %<------------------%< snip %<---------

from strop import atoi
from Tkinter import *

class EmacsEntry(Entry):
def __init__(self, master=None, cmd={}):
Entry.__init__(self, master, cmd)
self.killed = ''
self.viewAt = 0

self.bind('<Control-a>', lambda self=self: (self.icursor('0'),
self.view('0')))
self.bind('<Control-e>', lambda self=self: (self.icursor('end'), self.stayVisible()))
self.bind('<Control-d>', lambda self=self: (self.delete('insert')))
self.bind('<Control-f>', lambda self=self: (self.icursor(`atoi(self.index('insert'))+1`),
self.stayVisible()))
self.bind('<Right>', lambda self=self: (self.icursor(`atoi(self.index('insert'))+1`),
self.stayVisible()))
self.bind('<Control-b>', lambda self=self: (self.icursor(`atoi(self.index('insert'))-1`),
self.stayVisible()))
self.bind('<Left>', lambda self=self: (self.icursor(`atoi(self.index('insert'))-1`),
self.stayVisible()))
self.bind('<Control-k>', lambda self=self: (self.setKilled(),
self.delete('insert', 'end')))
self.bind('<Control-y>', lambda self=self: (self.insert('insert', self.killed),
self.stayVisible()))
self.bind('<Button-2>', self._putSelection, ('%x',))


def _putSelection(self, x):
sel = self.selection('get')
self.insert('@'+x, sel)
newSpot = atoi(self.index('@'+x))+len(sel)
self.icursor(`newSpot`)
width = atoi(self['width'])
if newSpot-self.viewAt > width:
self.view(`newSpot-width`)

def stayVisible(self):
insert = atoi(self.index('insert'))
width = atoi(self['width'])
if (insert - self.viewAt) >= width:
self.view(`insert - width`)
elif insert < self.viewAt:
self.view(`insert`)

def delete(self, *args):
return apply(self.cmd, ('delete',) + args)

def get(self):
return self.cmd('get')

def icursor(self, *args):
return apply(self.cmd, ('icursor',) + args)

def index(self, *args):
return apply(self.cmd, ('index',) + args)

def insert(self, *args):
return apply(self.cmd, ('insert',) + args)

def select(self, *args):
return apply(self.cmd, ('select',) + args)

def view(self, goto):
self.viewAt = atoi(goto)
return self.cmd('view', goto)

def setKilled(self):
self.killed = self.get()[atoi(self.index('insert')):]

def goAway(self):
Tk.__del__(self)

-------> Tommy.

"Deserve victory!" - Winston Churchill