]> code.delx.au - offlineimap/commitdiff
/offlineimap/head: changeset 301
authorjgoerzen <jgoerzen>
Sun, 5 Jan 2003 07:51:35 +0000 (08:51 +0100)
committerjgoerzen <jgoerzen>
Sun, 5 Jan 2003 07:51:35 +0000 (08:51 +0100)
Nominally-working Blinkenlights interface for Curses!

offlineimap/head/offlineimap/ui/Blinkenlights.py
offlineimap/head/offlineimap/ui/Curses.py
offlineimap/head/offlineimap/ui/__init__.py
offlineimap/head/offlineimap/ui/detector.py
offlineimap/head/offlineimap/version.py

index c499f644b523ae881705997d83931d5755ec7742..bb8e066f422a225a056fc4d9b7c15ba4b5cdf0a9 100644 (file)
@@ -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)
index 0f122d1a519a8fb5b84c2fed9a2ff1808605b9c9..8591169bc4e064d51a8b6173e7df736147b2d33d 100644 (file)
@@ -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):
-        
index 255efa14adb35b31cd38b5368ca55b5f6f7d992a..d470819949f2f0885a782549c8052cc44733df5c 100644 (file)
 #    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
index c561ada3511d062d1d6c9b296737deff149c9595..2cda9b8b99b3354f3d2587691fc5abe42728ae9b 100644 (file)
@@ -46,5 +46,6 @@ def getUImod(uistr, localeval, namespace):
     try:
         uimod = localeval.eval(uistr, namespace)
     except (AttributeError, NameError), e:
+        #raise
         return None
     return uimod
index 6ae6be27cecd4946545694c76f51664650f7da51..a116d2837e5e5c262f2f62caeb9ee5c6c1d13678 100644 (file)
@@ -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"