/offlineimap/head: changeset 301
Nominally-working Blinkenlights interface for Curses!
This commit is contained in:
parent
380f654df5
commit
ba0c95f6bc
@ -21,10 +21,6 @@ class BlinkenBase:
|
||||
or another appropriate base class. The Tk interface, for instance,
|
||||
will probably mix it in with VerboseUI."""
|
||||
|
||||
def isusable(s):
|
||||
"This class is not usable by itself; please override this."
|
||||
return 0
|
||||
|
||||
def acct(s, accountname):
|
||||
s.gettf().setcolor('purple')
|
||||
s.__class__.__bases__[-1].acct(s, accountname)
|
||||
|
@ -19,6 +19,7 @@
|
||||
from Blinkenlights import BlinkenBase
|
||||
from UIBase import UIBase
|
||||
from threading import *
|
||||
from offlineimap import version, threadutil
|
||||
|
||||
import curses, curses.panel, curses.textpad, curses.wrapper
|
||||
|
||||
@ -30,6 +31,9 @@ class CursesUtil:
|
||||
self.nextpair = 1
|
||||
self.pairlock = Lock()
|
||||
|
||||
def isactive(self):
|
||||
return hasattr(self, 'stdscr')
|
||||
|
||||
def _getpairindex(self, fg, bg):
|
||||
return '%d/%d' % (fg,bg)
|
||||
|
||||
@ -63,15 +67,201 @@ class CursesUtil:
|
||||
(self.height, self.width) = self.stdscr.getmaxyx()
|
||||
|
||||
def stop(self):
|
||||
if not hasattr(self, 'stdscr'):
|
||||
return
|
||||
#self.stdscr.addstr(self.height - 1, 0, "\n",
|
||||
# self.getpair(curses.COLOR_WHITE,
|
||||
# curses.COLOR_BLACK))
|
||||
self.stdscr.refresh()
|
||||
self.stdscr.keypad(0)
|
||||
curses.nocbreak()
|
||||
curses.echo()
|
||||
curses.endwin()
|
||||
del self.stdscr
|
||||
|
||||
#def resize(self):
|
||||
def resize(self):
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
class CursesThreadFrame:
|
||||
def __init__(s, master):
|
||||
"""master should be a CursesUtil object."""
|
||||
s.c = master
|
||||
bg = curses.COLOR_BLACK
|
||||
s.colormap = {'black': s.c.getpair(curses.COLOR_BLACK, bg),
|
||||
'gray': s.c.getpair(curses.COLOR_WHITE, bg),
|
||||
'white': curses.A_BOLD | s.c.getpair(curses.COLOR_WHITE, bg),
|
||||
'blue': s.c.getpair(curses.COLOR_BLUE, bg),
|
||||
'red': s.c.getpair(curses.COLOR_RED, bg),
|
||||
'purple': s.c.getpair(curses.COLOR_MAGENTA, bg),
|
||||
'cyan': s.c.getpair(curses.COLOR_CYAN, bg),
|
||||
'green': s.c.getpair(curses.COLOR_GREEN, bg),
|
||||
'orange': s.c.getpair(curses.COLOR_YELLOW, bg),
|
||||
'yellow': curses.A_BOLD | s.c.getpair(curses.COLOR_YELLOW, bg),
|
||||
'pink': curses.A_BOLD | s.c.getpair(curses.COLOR_RED, bg)}
|
||||
s.setcolor('gray')
|
||||
|
||||
def setcolor(self, color):
|
||||
self.color = self.colormap[color]
|
||||
|
||||
def getcolor(self):
|
||||
return self.color
|
||||
|
||||
class InputHandler:
|
||||
def __init__(s, util):
|
||||
s.c = util
|
||||
s.bgchar = None
|
||||
s.inputlock = Lock()
|
||||
s.lockheld = 0
|
||||
s.statuslock = Lock()
|
||||
s.startup = Event()
|
||||
s.startthread()
|
||||
|
||||
def startthread(s):
|
||||
s.thread = threadutil.ExitNotifyThread(target = s.bgreaderloop,
|
||||
name = "InputHandler loop")
|
||||
s.thread.setDaemon(1)
|
||||
s.thread.start()
|
||||
|
||||
def bgreaderloop(s):
|
||||
while 1:
|
||||
s.statuslock.acquire()
|
||||
if s.lockheld or s.bgchar == None:
|
||||
s.statuslock.release()
|
||||
s.startup.wait()
|
||||
else:
|
||||
s.statuslock.release()
|
||||
ch = s.c.stdscr.getch()
|
||||
s.statuslock.acquire()
|
||||
try:
|
||||
if s.lockheld or s.bgchar == None:
|
||||
curses.ungetch(ch)
|
||||
else:
|
||||
s.bgchar(ch)
|
||||
finally:
|
||||
s.statuslock.release()
|
||||
|
||||
def set_bgchar(s, callback):
|
||||
"""Sets a "background" character handler. If a key is pressed
|
||||
while not doing anything else, it will be passed to this handler.
|
||||
|
||||
callback is a function taking a single arg -- the char pressed.
|
||||
|
||||
If callback is None, clears the request."""
|
||||
s.statuslock.acquire()
|
||||
oldhandler = s.bgchar
|
||||
newhandler = callback
|
||||
s.bgchar = callback
|
||||
|
||||
if oldhandler and not newhandler:
|
||||
pass
|
||||
if newhandler and not oldhandler:
|
||||
s.startup.set()
|
||||
|
||||
s.statuslock.release()
|
||||
|
||||
def input_acquire(s):
|
||||
"""Call this method when you want exclusive input control.
|
||||
Make sure to call input_release afterwards!
|
||||
"""
|
||||
|
||||
s.inputlock.acquire()
|
||||
s.statuslock.acquire()
|
||||
s.lockheld = 1
|
||||
s.statuslock.release()
|
||||
|
||||
def input_release(s):
|
||||
"""Call this method when you are done getting input."""
|
||||
s.statuslock.acquire()
|
||||
s.lockheld = 0
|
||||
s.statuslock.release()
|
||||
s.inputlock.release()
|
||||
s.startup.set()
|
||||
|
||||
class Blinkenlights(BlinkenBase, UIBase):
|
||||
def init_banner(s):
|
||||
s.iolock = Lock()
|
||||
s.c = CursesUtil()
|
||||
s.accounts = []
|
||||
s.text = []
|
||||
s.tf = CursesThreadFrame(s.c)
|
||||
s.setupwindows()
|
||||
s.inputhandler = InputHandler(s.c)
|
||||
s._msg(version.banner)
|
||||
s._msg(str(dir(s.c.stdscr)))
|
||||
s.inputhandler.set_bgchar(s.keypress)
|
||||
|
||||
def keypress(s, key):
|
||||
s._msg("Key pressed: " + str(key))
|
||||
|
||||
def setupwindows(s):
|
||||
s.bannerwindow = curses.newwin(1, s.c.width, 0, 0)
|
||||
s.drawbanner()
|
||||
s.logheight = s.c.height - 1 - len(s.accounts) * 2
|
||||
s.logwindow = curses.newwin(s.logheight, s.c.width, 1, 0)
|
||||
s.logwindow.idlok(1)
|
||||
s.logwindow.scrollok(1)
|
||||
s.drawlog()
|
||||
curses.doupdate()
|
||||
|
||||
def drawbanner(s):
|
||||
s.bannerwindow.bkgd(' ', curses.A_BOLD | \
|
||||
s.c.getpair(curses.COLOR_WHITE,
|
||||
curses.COLOR_BLUE))
|
||||
s.bannerwindow.addstr("%s %s" % (version.productname,
|
||||
version.versionstr))
|
||||
s.bannerwindow.addstr(0, s.bannerwindow.getmaxyx()[1] - len(version.copyright) - 1,
|
||||
version.copyright)
|
||||
|
||||
s.bannerwindow.noutrefresh()
|
||||
|
||||
def drawlog(s):
|
||||
s.logwindow.bkgd(' ', s.c.getpair(curses.COLOR_WHITE, curses.COLOR_BLACK))
|
||||
for line, color in s.text:
|
||||
s.logwindow.addstr(line + "\n", color)
|
||||
s.logwindow.noutrefresh()
|
||||
|
||||
def gettf(s):
|
||||
return s.tf
|
||||
|
||||
def _msg(s, msg):
|
||||
if "\n" in msg:
|
||||
for thisline in msg.split("\n"):
|
||||
s._msg(thisline)
|
||||
return
|
||||
s.iolock.acquire()
|
||||
try:
|
||||
if not s.c.isactive():
|
||||
# For dumping out exceptions and stuff.
|
||||
print msg
|
||||
return
|
||||
color = s.gettf().getcolor()
|
||||
s.logwindow.addstr(msg + "\n", color)
|
||||
s.text.append((msg, color))
|
||||
while len(s.text) > s.logheight:
|
||||
s.text = s.text[1:]
|
||||
s.logwindow.refresh()
|
||||
finally:
|
||||
s.iolock.release()
|
||||
|
||||
def terminate(s, exitstatus = 0):
|
||||
s.c.stop()
|
||||
UIBase.terminate(s, exitstatus)
|
||||
|
||||
def threadException(s, thread):
|
||||
s.c.stop()
|
||||
UIBase.threadException(s, thread)
|
||||
|
||||
def mainException(s):
|
||||
s.c.stop()
|
||||
UIBase.mainException()
|
||||
|
||||
if __name__ == '__main__':
|
||||
x = Blinkenlights(None)
|
||||
x.init_banner()
|
||||
import time
|
||||
time.sleep(10)
|
||||
x.c.stop()
|
||||
fgs = {'black': curses.COLOR_BLACK, 'red': curses.COLOR_RED,
|
||||
'green': curses.COLOR_GREEN, 'yellow': curses.COLOR_YELLOW,
|
||||
'blue': curses.COLOR_BLUE, 'magenta': curses.COLOR_MAGENTA,
|
||||
@ -111,6 +301,3 @@ if __name__ == '__main__':
|
||||
print x.height
|
||||
print x.width
|
||||
|
||||
#class Blinkenlights(BlinkenBase, UIBase):
|
||||
# def init_banner(s):
|
||||
|
||||
|
@ -17,11 +17,12 @@
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
import UIBase, BlinkenBase
|
||||
import UIBase, Blinkenlights
|
||||
try:
|
||||
import TTY
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
import Tkinter
|
||||
except ImportError:
|
||||
@ -29,6 +30,13 @@ except ImportError:
|
||||
else:
|
||||
import Tk
|
||||
|
||||
try:
|
||||
import curses
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
import Curses
|
||||
|
||||
import Noninteractive
|
||||
|
||||
# Must be last
|
||||
|
@ -46,5 +46,6 @@ def getUImod(uistr, localeval, namespace):
|
||||
try:
|
||||
uimod = localeval.eval(uistr, namespace)
|
||||
except (AttributeError, NameError), e:
|
||||
#raise
|
||||
return None
|
||||
return uimod
|
||||
|
@ -1,14 +1,14 @@
|
||||
productname = 'OfflineIMAP'
|
||||
versionstr = "3.99.5"
|
||||
revno = long('$Rev: 288 $'[6:-2])
|
||||
revno = long('$Rev: 301 $'[6:-2])
|
||||
revstr = "Rev %d" % revno
|
||||
datestr = '$Date: 2002-11-19 16:34:09 -0600 (Tue, 19 Nov 2002) $'
|
||||
datestr = '$Date: 2003-01-04 19:51:35 -0600 (Sat, 04 Jan 2003) $'
|
||||
|
||||
versionlist = versionstr.split(".")
|
||||
major = versionlist[0]
|
||||
minor = versionlist[1]
|
||||
patch = versionlist[2]
|
||||
copyright = "Copyright (C) 2002 John Goerzen"
|
||||
copyright = "Copyright (C) 2002, 2003 John Goerzen"
|
||||
author = "John Goerzen"
|
||||
author_email = "jgoerzen@complete.org"
|
||||
description = "Disconnected Universal IMAP Mail Synchronization/Reader Support"
|
||||
|
Loading…
Reference in New Issue
Block a user