[PATCH] allow dynamic addition of ui plugins
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: |
[PATCH] allow dynamic addition of ui plugins |
From: |
=3D?utf-8?q?Christoph=3D20H=3DC3=3DB6ger?=3D <choeger@xxxxx-berlin.= |
Date: |
Fri, 24 Jul 2009 14:21:01 +0200 |
Hi,
this is more a RFC to my last mail. I wanted to be able to add UI plugins=
dynamically mostly because they can have dependencies that offlineimap i=
tself should not have (e.g. GTK or Curses). So I moved all ui plugins to =
the offlineimap.ui.plugins package. The invocation with this patch is off=
lineimap -u <MODULENAME> and not offlineimap -u <MODULENAME>.<CLASSNAME> =
as the concret class is defined by the plugins getUIClass() method.
Although this patch is rather invasive it allows to easily create and dis=
tribute new UI modules (think of the Gnome branch) without changing any f=
ile in the rest of the distribution.
Signed-off-by: Christoph H=F6ger <choeger@xxxxxxxxxxxxxxx>
---
offlineimap/ui/Blinkenlights.py | 147 ----------
offlineimap/ui/Curses.py | 593 --------------------------------=
------
offlineimap/ui/Machine.py | 179 ------------
offlineimap/ui/Noninteractive.py | 51 ----
offlineimap/ui/TTY.py | 60 ----
offlineimap/ui/__init__.py | 19 +--
offlineimap/ui/detector.py | 23 +-
setup.py | 3 +-
8 files changed, 12 insertions(+), 1063 deletions(-)
delete mode 100644 offlineimap/ui/Blinkenlights.py
delete mode 100644 offlineimap/ui/Curses.py
delete mode 100644 offlineimap/ui/Machine.py
delete mode 100644 offlineimap/ui/Noninteractive.py
delete mode 100644 offlineimap/ui/TTY.py
diff --git a/offlineimap/ui/Blinkenlights.py b/offlineimap/ui/Blinkenligh=
ts.py
deleted file mode 100644
index dcc4e01..0000000
--- a/offlineimap/ui/Blinkenlights.py
+++ /dev/null
@@ -1,147 +0,0 @@
-# Blinkenlights base classes
-# Copyright (C) 2003 John Goerzen
-# <jgoerzen@xxxxxxxxxxxx>
-#
-# This program is free software; you can redistribute it and/or modif=
y
-# it under the terms of the GNU General Public License as published b=
y
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-13=
01 USA
-
-from threading import *
-from offlineimap.ui.UIBase import UIBase
-import thread
-from offlineimap.threadutil import MultiLock
-
-class BlinkenBase:
- """This is a mix-in class that should be mixed in with either UIBase
- or another appropriate base class. The Tk interface, for instance,
- will probably mix it in with VerboseUI."""
-
- def acct(s, accountname):
- s.gettf().setcolor('purple')
- s.__class__.__bases__[-1].acct(s, accountname)
-
- def connecting(s, hostname, port):
- s.gettf().setcolor('gray')
- s.__class__.__bases__[-1].connecting(s, hostname, port)
-
- def syncfolders(s, srcrepos, destrepos):
- s.gettf().setcolor('blue')
- s.__class__.__bases__[-1].syncfolders(s, srcrepos, destrepos)
-
- def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder):
- s.gettf().setcolor('cyan')
- s.__class__.__bases__[-1].syncingfolder(s, srcrepos, srcfolder, =
destrepos, destfolder)
-
- def skippingfolder(s, folder):
- s.gettf().setcolor('cyan')
- s.__class__.__bases__[-1].skippingfolder(s, folder)
-
- def loadmessagelist(s, repos, folder):
- s.gettf().setcolor('green')
- s._msg("Scanning folder [%s/%s]" % (s.getnicename(repos),
- folder.getvisiblename()))
-
- def syncingmessages(s, sr, sf, dr, df):
- s.gettf().setcolor('blue')
- s.__class__.__bases__[-1].syncingmessages(s, sr, sf, dr, df)
-
- def copyingmessage(s, uid, src, destlist):
- s.gettf().setcolor('orange')
- s.__class__.__bases__[-1].copyingmessage(s, uid, src, destlist)
-
- def deletingmessages(s, uidlist, destlist):
- s.gettf().setcolor('red')
- s.__class__.__bases__[-1].deletingmessages(s, uidlist, destlist)
-
- def deletingmessage(s, uid, destlist):
- s.gettf().setcolor('red')
- s.__class__.__bases__[-1].deletingmessage(s, uid, destlist)
-
- def addingflags(s, uidlist, flags, destlist):
- s.gettf().setcolor('yellow')
- s.__class__.__bases__[-1].addingflags(s, uidlist, flags, destlis=
t)
-
- def deletingflags(s, uidlist, flags, destlist):
- s.gettf().setcolor('pink')
- s.__class__.__bases__[-1].deletingflags(s, uidlist, flags, destl=
ist)
-
- def warn(s, msg, minor =3D 0):
- if minor:
- s.gettf().setcolor('pink')
- else:
- s.gettf().setcolor('red')
- s.__class__.__bases__[-1].warn(s, msg, minor)
-
- def init_banner(s):
- s.availablethreadframes =3D {}
- s.threadframes =3D {}
- s.tflock =3D MultiLock()
-
- def threadExited(s, thread):
- threadid =3D thread.threadid
- accountname =3D s.getthreadaccount(thread)
- s.tflock.acquire()
- try:
- if threadid in s.threadframes[accountname]:
- tf =3D s.threadframes[accountname][threadid]
- del s.threadframes[accountname][threadid]
- s.availablethreadframes[accountname].append(tf)
- tf.setthread(None)
- finally:
- s.tflock.release()
-
- UIBase.threadExited(s, thread)
-
- def gettf(s):
- threadid =3D thread.get_ident()
- accountname =3D s.getthreadaccount()
-
- s.tflock.acquire()
-
- try:
- if not accountname in s.threadframes:
- s.threadframes[accountname] =3D {}
- =20
- if threadid in s.threadframes[accountname]:
- return s.threadframes[accountname][threadid]
-
- if not accountname in s.availablethreadframes:
- s.availablethreadframes[accountname] =3D []
-
- if len(s.availablethreadframes[accountname]):
- tf =3D s.availablethreadframes[accountname].pop(0)
- tf.setthread(currentThread())
- else:
- tf =3D s.getaccountframe().getnewthreadframe()
- s.threadframes[accountname][threadid] =3D tf
- return tf
- finally:
- s.tflock.release()
-
- def callhook(s, msg):
- s.gettf().setcolor('white')
- s.__class__.__bases__[-1].callhook(s, msg)
- =20
- def sleep(s, sleepsecs, siglistener):
- s.gettf().setcolor('red')
- s.getaccountframe().startsleep(sleepsecs)
- return UIBase.sleep(s, sleepsecs, siglistener)
-
- def sleeping(s, sleepsecs, remainingsecs):
- if remainingsecs and s.gettf().getcolor() =3D=3D 'black':
- s.gettf().setcolor('red')
- else:
- s.gettf().setcolor('black')
- return s.getaccountframe().sleeping(sleepsecs, remainingsecs)
-
- =20
diff --git a/offlineimap/ui/Curses.py b/offlineimap/ui/Curses.py
deleted file mode 100644
index 8eb709f..0000000
--- a/offlineimap/ui/Curses.py
+++ /dev/null
@@ -1,593 +0,0 @@
-# Curses-based interfaces
-# Copyright (C) 2003 John Goerzen
-# <jgoerzen@xxxxxxxxxxxx>
-#
-# This program is free software; you can redistribute it and/or modif=
y
-# it under the terms of the GNU General Public License as published b=
y
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-13=
01 USA
-
-from Blinkenlights import BlinkenBase
-from UIBase import UIBase
-from threading import *
-import thread, time, sys, os, signal, time
-from offlineimap import version, threadutil
-from offlineimap.threadutil import MultiLock
-
-import curses, curses.panel, curses.textpad, curses.wrapper
-
-acctkeys =3D '1234567890abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVW=
XYZ-=3D;/.,'
-
-class CursesUtil:
- def __init__(self):
- self.pairlock =3D Lock()
- self.iolock =3D MultiLock()
- self.start()
-
- def initpairs(self):
- self.pairlock.acquire()
- try:
- self.pairs =3D {self._getpairindex(curses.COLOR_WHITE,
- curses.COLOR_BLACK): 0}
- self.nextpair =3D 1
- finally:
- self.pairlock.release()
-
- def lock(self):
- self.iolock.acquire()
-
- def unlock(self):
- self.iolock.release()
- =20
- def locked(self, target, *args, **kwargs):
- """Perform an operation with full locking."""
- self.lock()
- try:
- apply(target, args, kwargs)
- finally:
- self.unlock()
-
- def refresh(self):
- def lockedstuff():
- curses.panel.update_panels()
- curses.doupdate()
- self.locked(lockedstuff)
-
- def isactive(self):
- return hasattr(self, 'stdscr')
-
- def _getpairindex(self, fg, bg):
- return '%d/%d' % (fg,bg)
-
- def getpair(self, fg, bg):
- if not self.has_color:
- return 0
- pindex =3D self._getpairindex(fg, bg)
- self.pairlock.acquire()
- try:
- if self.pairs.has_key(pindex):
- return curses.color_pair(self.pairs[pindex])
- else:
- self.pairs[pindex] =3D self.nextpair
- curses.init_pair(self.nextpair, fg, bg)
- self.nextpair +=3D 1
- return curses.color_pair(self.nextpair - 1)
- finally:
- self.pairlock.release()
- =20
- def start(self):
- self.stdscr =3D curses.initscr()
- curses.noecho()
- curses.cbreak()
- self.stdscr.keypad(1)
- try:
- curses.start_color()
- self.has_color =3D curses.has_colors()
- except:
- self.has_color =3D 0
-
- self.oldcursor =3D None
- try:
- self.oldcursor =3D curses.curs_set(0)
- except:
- pass
- =20
- self.stdscr.clear()
- self.stdscr.refresh()
- (self.height, self.width) =3D self.stdscr.getmaxyx()
- self.initpairs()
-
- 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))
- if self.oldcursor !=3D None:
- curses.curs_set(self.oldcursor)
- self.stdscr.refresh()
- self.stdscr.keypad(0)
- curses.nocbreak()
- curses.echo()
- curses.endwin()
- del self.stdscr
-
- def reset(self):
- self.stop()
- self.start()
-
-class CursesAccountFrame:
- def __init__(s, master, accountname, ui):
- s.c =3D master
- s.children =3D []
- s.accountname =3D accountname
- s.ui =3D ui
-
- def drawleadstr(s, secs =3D None):
- if secs =3D=3D None:
- acctstr =3D '%s: [active] %13.13s: ' % (s.key, s.accountname=
)
- else:
- acctstr =3D '%s: [%3d:%02d] %13.13s: ' % (s.key,
- secs / 60, secs % 60=
,
- s.accountname)
- s.c.locked(s.window.addstr, 0, 0, acctstr)
- s.location =3D len(acctstr)
-
- def setwindow(s, window, key):
- s.window =3D window
- s.key =3D key
- s.drawleadstr()
- for child in s.children:
- child.update(window, 0, s.location)
- s.location +=3D 1
-
- def getnewthreadframe(s):
- tf =3D CursesThreadFrame(s.c, s.ui, s.window, 0, s.location)
- s.location +=3D 1
- s.children.append(tf)
- return tf
-
- def startsleep(s, sleepsecs):
- s.sleeping_abort =3D 0
-
- def sleeping(s, sleepsecs, remainingsecs):
- if remainingsecs:
- s.c.lock()
- try:
- s.drawleadstr(remainingsecs)
- s.window.refresh()
- finally:
- s.c.unlock()
- time.sleep(sleepsecs)
- else:
- s.c.lock()
- try:
- s.drawleadstr()
- s.window.refresh()
- finally:
- s.c.unlock()
- return s.sleeping_abort
-
- def syncnow(s):
- s.sleeping_abort =3D 1
-
-class CursesThreadFrame:
- def __init__(s, master, ui, window, y, x):
- """master should be a CursesUtil object."""
- s.c =3D master
- s.ui =3D ui
- s.window =3D window
- s.x =3D x
- s.y =3D y
- s.colors =3D []
- bg =3D curses.COLOR_BLACK
- s.colormap =3D {'black': s.c.getpair(curses.COLOR_BLACK, bg),
- 'gray': s.c.getpair(curses.COLOR_WHITE, bg),
- 'white': curses.A_BOLD | s.c.getpair(curses.COL=
OR_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.CO=
LOR_YELLOW, bg),
- 'pink': curses.A_BOLD | s.c.getpair(curses.COLO=
R_RED, bg)}
- #s.setcolor('gray')
- s.setcolor('black')
-
- def setcolor(self, color):
- self.color =3D self.colormap[color]
- self.colorname =3D color
- self.display()
-
- def display(self):
- def lockedstuff():
- if self.getcolor() =3D=3D 'black':
- self.window.addstr(self.y, self.x, ' ', self.color)
- else:
- self.window.addstr(self.y, self.x, self.ui.config.getdef=
ault("ui.Curses.Blinkenlights", "statuschar", '.'), self.color)
- self.c.stdscr.move(self.c.height - 1, self.c.width - 1)
- self.window.refresh()
- self.c.locked(lockedstuff)
-
- def getcolor(self):
- return self.colorname
-
- def getcolorpair(self):
- return self.color
-
- def update(self, window, y, x):
- self.window =3D window
- self.y =3D y
- self.x =3D x
- self.display()
-
- def setthread(self, newthread):
- self.setcolor('black')
- #if newthread:
- # self.setcolor('gray')
- #else:
- # self.setcolor('black')
-
-class InputHandler:
- def __init__(s, util):
- s.c =3D util
- s.bgchar =3D None
- s.inputlock =3D Lock()
- s.lockheld =3D 0
- s.statuslock =3D Lock()
- s.startup =3D Event()
- s.startthread()
-
- def startthread(s):
- s.thread =3D threadutil.ExitNotifyThread(target =3D s.bgreaderlo=
op,
- name =3D "InputHandler lo=
op")
- s.thread.setDaemon(1)
- s.thread.start()
-
- def bgreaderloop(s):
- while 1:
- s.statuslock.acquire()
- if s.lockheld or s.bgchar =3D=3D None:
- s.statuslock.release()
- s.startup.wait()
- else:
- s.statuslock.release()
- ch =3D s.c.stdscr.getch()
- s.statuslock.acquire()
- try:
- if s.lockheld or s.bgchar =3D=3D 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 =3D s.bgchar
- newhandler =3D callback
- s.bgchar =3D callback
-
- if oldhandler and not newhandler:
- pass
- if newhandler and not oldhandler:
- s.startup.set()
- =20
- 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 =3D 1
- s.statuslock.release()
-
- def input_release(s):
- """Call this method when you are done getting input."""
- s.statuslock.acquire()
- s.lockheld =3D 0
- s.statuslock.release()
- s.inputlock.release()
- s.startup.set()
- =20
-class Blinkenlights(BlinkenBase, UIBase):
- def init_banner(s):
- s.af =3D {}
- s.aflock =3D Lock()
- s.c =3D CursesUtil()
- s.text =3D []
- BlinkenBase.init_banner(s)
- s.setupwindows()
- s.inputhandler =3D InputHandler(s.c)
- s.gettf().setcolor('red')
- s._msg(version.banner)
- s.inputhandler.set_bgchar(s.keypress)
- signal.signal(signal.SIGWINCH, s.resizehandler)
- s.resizelock =3D Lock()
- s.resizecount =3D 0
-
- def resizehandler(s, signum, frame):
- s.resizeterm()
-
- def resizeterm(s, dosleep =3D 1):
- if not s.resizelock.acquire(0):
- s.resizecount +=3D 1
- return
- signal.signal(signal.SIGWINCH, signal.SIG_IGN)
- s.aflock.acquire()
- s.c.lock()
- s.resizecount +=3D 1
- while s.resizecount:
- s.c.reset()
- s.setupwindows()
- s.resizecount -=3D 1
- s.c.unlock()
- s.aflock.release()
- s.resizelock.release()
- signal.signal(signal.SIGWINCH, s.resizehandler)
- if dosleep:
- time.sleep(1)
- s.resizeterm(0)
-
- def isusable(s):
- # Not a terminal? Can't use curses.
- if not sys.stdout.isatty() and sys.stdin.isatty():
- return 0
-
- # No TERM specified? Can't use curses.
- try:
- if not len(os.environ['TERM']):
- return 0
- except: return 0
-
- # ncurses doesn't want to start? Can't use curses.
- # This test is nasty because initscr() actually EXITS on error.
- # grr.
-
- pid =3D os.fork()
- if pid:
- # parent
- return not os.WEXITSTATUS(os.waitpid(pid, 0)[1])
- else:
- # child
- curses.initscr()
- curses.endwin()
- # If we didn't die by here, indicate success.
- sys.exit(0)
-
- def keypress(s, key):
- if key < 1 or key > 255:
- return
- =20
- if chr(key) =3D=3D 'q':
- # Request to quit.
- s.terminate()
- =20
- try:
- index =3D acctkeys.index(chr(key))
- except ValueError:
- # Key not a valid one: exit.
- return
-
- if index >=3D len(s.hotkeys):
- # Not in our list of valid hotkeys.
- return
-
- # Trying to end sleep somewhere.
-
- s.getaccountframe(s.hotkeys[index]).syncnow()
-
- def getpass(s, accountname, config, errmsg =3D None):
- s.inputhandler.input_acquire()
-
- # See comment on _msg for info on why both locks are obtained.
- =20
- s.tflock.acquire()
- s.c.lock()
- try:
- s.gettf().setcolor('white')
- s._addline(" *** Input Required", s.gettf().getcolorpair())
- s._addline(" *** Please enter password for account %s: " % a=
ccountname,
- s.gettf().getcolorpair())
- s.logwindow.refresh()
- password =3D s.logwindow.getstr()
- finally:
- s.tflock.release()
- s.c.unlock()
- s.inputhandler.input_release()
- return password
-
- def setupwindows(s):
- s.c.lock()
- try:
- s.bannerwindow =3D curses.newwin(1, s.c.width, 0, 0)
- s.setupwindow_drawbanner()
- s.logheight =3D s.c.height - 1 - len(s.af.keys())
- s.logwindow =3D curses.newwin(s.logheight, s.c.width, 1, 0)
- s.logwindow.idlok(1)
- s.logwindow.scrollok(1)
- s.logwindow.move(s.logheight - 1, 0)
- s.setupwindow_drawlog()
- accounts =3D s.af.keys()
- accounts.sort()
- accounts.reverse()
-
- pos =3D s.c.height - 1
- index =3D 0
- s.hotkeys =3D []
- for account in accounts:
- accountwindow =3D curses.newwin(1, s.c.width, pos, 0)
- s.af[account].setwindow(accountwindow, acctkeys[index])
- s.hotkeys.append(account)
- index +=3D 1
- pos -=3D 1
-
- curses.doupdate()
- finally:
- s.c.unlock()
-
- def setupwindow_drawbanner(s):
- if s.c.has_color:
- color =3D s.c.getpair(curses.COLOR_WHITE, curses.COLOR_BLUE)=
| \
- curses.A_BOLD
- else:
- color =3D curses.A_REVERSE
- s.bannerwindow.bkgd(' ', color) # Fill background with that colo=
r
- s.bannerwindow.addstr("%s %s" % (version.productname,
- version.versionstr))
- s.bannerwindow.addstr(0, s.bannerwindow.getmaxyx()[1] - len(vers=
ion.copyright) - 1,
- version.copyright)
- =20
- s.bannerwindow.noutrefresh()
-
- def setupwindow_drawlog(s):
- if s.c.has_color:
- color =3D s.c.getpair(curses.COLOR_WHITE, curses.COLOR_BLACK=
)
- else:
- color =3D curses.A_NORMAL
- s.logwindow.bkgd(' ', color)
- for line, color in s.text:
- s.logwindow.addstr("\n" + line, color)
- s.logwindow.noutrefresh()
-
- def getaccountframe(s, accountname =3D None):
- if accountname =3D=3D None:
- accountname =3D s.getthreadaccount()
- s.aflock.acquire()
- try:
- if accountname in s.af:
- return s.af[accountname]
-
- # New one.
- s.af[accountname] =3D CursesAccountFrame(s.c, accountname, s=
)
- s.c.lock()
- try:
- s.c.reset()
- s.setupwindows()
- finally:
- s.c.unlock()
- finally:
- s.aflock.release()
- return s.af[accountname]
-
-
- def _display(s, msg, color =3D None):
- if "\n" in msg:
- for thisline in msg.split("\n"):
- s._msg(thisline)
- return
-
- # We must acquire both locks. Otherwise, deadlock can result.
- # This can happen if one thread calls _msg (locking curses, then
- # tf) and another tries to set the color (locking tf, then curse=
s)
- #
- # By locking both up-front here, in this order, we prevent deadl=
ock.
- =20
- s.tflock.acquire()
- s.c.lock()
- try:
- if not s.c.isactive():
- # For dumping out exceptions and stuff.
- print msg
- return
- if color:
- s.gettf().setcolor(color)
- elif s.gettf().getcolor() =3D=3D 'black':
- s.gettf().setcolor('gray')
- s._addline(msg, s.gettf().getcolorpair())
- s.logwindow.refresh()
- finally:
- s.c.unlock()
- s.tflock.release()
-
- def _addline(s, msg, color):
- s.c.lock()
- try:
- s.logwindow.addstr("\n" + msg, color)
- s.text.append((msg, color))
- while len(s.text) > s.logheight:
- s.text =3D s.text[1:]
- finally:
- s.c.unlock()
-
- def terminate(s, exitstatus =3D 0, errortitle =3D None, errormsg =3D=
None):
- s.c.stop()
- UIBase.terminate(s, exitstatus =3D exitstatus, errortitle =3D er=
rortitle, errormsg =3D errormsg)
-
- def threadException(s, thread):
- s.c.stop()
- UIBase.threadException(s, thread)
-
- def mainException(s):
- s.c.stop()
- UIBase.mainException(s)
-
- def sleep(s, sleepsecs, siglistener):
- s.gettf().setcolor('red')
- s._msg("Next sync in %d:%02d" % (sleepsecs / 60, sleepsecs % 60)=
)
- return BlinkenBase.sleep(s, sleepsecs, siglistener)
- =20
-if __name__ =3D=3D '__main__':
- x =3D Blinkenlights(None)
- x.init_banner()
- import time
- time.sleep(5)
- x.c.stop()
- fgs =3D {'black': curses.COLOR_BLACK, 'red': curses.COLOR_RED,
- 'green': curses.COLOR_GREEN, 'yellow': curses.COLOR_YELLOW,
- 'blue': curses.COLOR_BLUE, 'magenta': curses.COLOR_MAGENTA,
- 'cyan': curses.COLOR_CYAN, 'white': curses.COLOR_WHITE}
- =20
- x =3D CursesUtil()
- win1 =3D curses.newwin(x.height, x.width / 4 - 1, 0, 0)
- win1.addstr("Black/normal\n")
- for name, fg in fgs.items():
- win1.addstr("%s\n" % name, x.getpair(fg, curses.COLOR_BLACK))
- win2 =3D curses.newwin(x.height, x.width / 4 - 1, 0, win1.getmaxyx()=
[1])
- win2.addstr("Blue/normal\n")
- for name, fg in fgs.items():
- win2.addstr("%s\n" % name, x.getpair(fg, curses.COLOR_BLUE))
- win3 =3D curses.newwin(x.height, x.width / 4 - 1, 0, win1.getmaxyx()=
[1] +
- win2.getmaxyx()[1])
- win3.addstr("Black/bright\n")
- for name, fg in fgs.items():
- win3.addstr("%s\n" % name, x.getpair(fg, curses.COLOR_BLACK) | \
- curses.A_BOLD)
- win4 =3D curses.newwin(x.height, x.width / 4 - 1, 0, win1.getmaxyx()=
[1] * 3)
- win4.addstr("Blue/bright\n")
- for name, fg in fgs.items():
- win4.addstr("%s\n" % name, x.getpair(fg, curses.COLOR_BLUE) | \
- curses.A_BOLD)
- =20
- =20
- win1.refresh()
- win2.refresh()
- win3.refresh()
- win4.refresh()
- x.stdscr.refresh()
- import time
- time.sleep(5)
- x.stop()
- print x.has_color
- print x.height
- print x.width
-
diff --git a/offlineimap/ui/Machine.py b/offlineimap/ui/Machine.py
deleted file mode 100644
index 0a07e3e..0000000
--- a/offlineimap/ui/Machine.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# Copyright (C) 2007 John Goerzen
-# <jgoerzen@xxxxxxxxxxxx>
-#
-# This program is free software; you can redistribute it and/or modif=
y
-# it under the terms of the GNU General Public License as published b=
y
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-13=
01 USA
-
-import offlineimap.version
-import urllib, sys, re, time, traceback, threading, thread
-from UIBase import UIBase
-from threading import *
-
-protocol =3D '6.0.0'
-
-class MachineUI(UIBase):
- def __init__(s, config, verbose =3D 0):
- UIBase.__init__(s, config, verbose)
- s.safechars=3D" ;,./-_=3D+()[]"
- s.iswaiting =3D 0
- s.outputlock =3D Lock()
- s._printData('__init__', protocol)
-
- def isusable(s):
- return True
-
- def _printData(s, command, data, dolock =3D True):
- s._printDataOut('msg', command, data, dolock)
-
- def _printWarn(s, command, data, dolock =3D True):
- s._printDataOut('warn', command, data, dolock)
-
- def _printDataOut(s, datatype, command, data, dolock =3D True):
- if dolock:
- s.outputlock.acquire()
- try:
- print "%s:%s:%s:%s" % \
- (datatype,
- urllib.quote(command, s.safechars),=20
- urllib.quote(currentThread().getName(), s.safechars=
),
- urllib.quote(data, s.safechars))
- sys.stdout.flush()
- finally:
- if dolock:
- s.outputlock.release()
-
- def _display(s, msg):
- s._printData('_display', msg)
-
- def warn(s, msg, minor):
- s._printData('warn', '%s\n%d' % (msg, int(minor)))
-
- def registerthread(s, account):
- UIBase.registerthread(s, account)
- s._printData('registerthread', account)
-
- def unregisterthread(s, thread):
- UIBase.unregisterthread(s, thread)
- s._printData('unregisterthread', thread.getName())
-
- def debugging(s, debugtype):
- s._printData('debugging', debugtype)
-
- def acct(s, accountname):
- s._printData('acct', accountname)
-
- def acctdone(s, accountname):
- s._printData('acctdone', accountname)
-
- def validityproblem(s, folder):
- s._printData('validityproblem', "%s\n%s\n%s\n%s" % \
- (folder.getname(), folder.getrepository().getname(),
- folder.getsaveduidvalidity(), folder.getuidvalidity()))
-
- def connecting(s, hostname, port):
- s._printData('connecting', "%s\n%s" % (hostname, str(port)))
-
- def syncfolders(s, srcrepos, destrepos):
- s._printData('syncfolders', "%s\n%s" % (s.getnicename(srcrepos),=
=20
- s.getnicename(destrepos)=
))
-
- def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder):
- s._printData('syncingfolder', "%s\n%s\n%s\n%s\n" % \
- (s.getnicename(srcrepos), srcfolder.getname(),
- s.getnicename(destrepos), destfolder.getname()))
-
- def loadmessagelist(s, repos, folder):
- s._printData('loadmessagelist', "%s\n%s" % (s.getnicename(repos)=
,
- folder.getvisiblenam=
e()))
-
- def messagelistloaded(s, repos, folder, count):
- s._printData('messagelistloaded', "%s\n%s\n%d" % \
- (s.getnicename(repos), folder.getname(), count))
-
- def syncingmessages(s, sr, sf, dr, df):
- s._printData('syncingmessages', "%s\n%s\n%s\n%s\n" % \
- (s.getnicename(sr), sf.getname(), s.getnicename(dr),
- df.getname()))
-
- def copyingmessage(s, uid, src, destlist):
- ds =3D s.folderlist(destlist)
- s._printData('copyingmessage', "%d\n%s\n%s\n%s" % \
- (uid, s.getnicename(src), src.getname(), ds))
- =20
- def folderlist(s, list):
- return ("\f".join(["%s\t%s" % (s.getnicename(x), x.getname()) fo=
r x in list]))
-
- def deletingmessage(s, uid, destlist):
- s.deletingmessages(s, [uid], destlist)
-
- def uidlist(s, list):
- return ("\f".join([str(u) for u in list]))
-
- def deletingmessages(s, uidlist, destlist):
- ds =3D s.folderlist(destlist)
- s._printData('deletingmessages', "%s\n%s" % (s.uidlist(uidlist),=
ds))
-
- def addingflags(s, uidlist, flags, destlist):
- ds =3D s.folderlist(destlist)
- s._printData("addingflags", "%s\n%s\n%s" % (s.uidlist(uidlist),
- "\f".join(flags),
- ds))
-
- def deletingflags(s, uidlist, flags, destlist):
- ds =3D s.folderlist(destlist)
- s._printData('deletingflags', "%s\n%s\n%s" % (s.uidlist(uidlist)=
,
- "\f".join(flags),
- ds))
-
- def threadException(s, thread):
- print s.getThreadExceptionString(thread)
- s._printData('threadException', "%s\n%s" % \
- (thread.getName(), s.getThreadExceptionString(threa=
d)))
- s.delThreadDebugLog(thread)
- s.terminate(100)
-
- def terminate(s, exitstatus =3D 0, errortitle =3D '', errormsg =3D '=
'):
- s._printData('terminate', "%d\n%s\n%s" % (exitstatus, errortitle=
, errormsg))
- sys.exit(exitstatus)
-
- def mainException(s):
- s._printData('mainException', s.getMainExceptionString())
-
- def threadExited(s, thread):
- s._printData('threadExited', thread.getName())
- UIBase.threadExited(s, thread)
-
- def sleeping(s, sleepsecs, remainingsecs):
- s._printData('sleeping', "%d\n%d" % (sleepsecs, remainingsecs))
- if sleepsecs > 0:
- time.sleep(sleepsecs)
- return 0
-
-
- def getpass(s, accountname, config, errmsg =3D None):
- s.outputlock.acquire()
- try:
- if errmsg:
- s._printData('getpasserror', "%s\n%s" % (accountname, er=
rmsg),
- False)
- s._printData('getpass', accountname, False)
- return (sys.stdin.readline()[:-1])
- finally:
- s.outputlock.release()
-
- def init_banner(s):
- s._printData('initbanner', offlineimap.version.banner)
-
- def callhook(s, msg):
- s._printData('callhook', msg)
diff --git a/offlineimap/ui/Noninteractive.py b/offlineimap/ui/Noninterac=
tive.py
deleted file mode 100644
index 9cd5eca..0000000
--- a/offlineimap/ui/Noninteractive.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Noninteractive UI
-# Copyright (C) 2002 John Goerzen
-# <jgoerzen@xxxxxxxxxxxx>
-#
-# This program is free software; you can redistribute it and/or modif=
y
-# it under the terms of the GNU General Public License as published b=
y
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-13=
01 USA
-
-import sys, time
-from UIBase import UIBase
-
-class Basic(UIBase):
- def getpass(s, accountname, config, errmsg =3D None):
- raise NotImplementedError, "Prompting for a password is not supp=
orted in noninteractive mode."
-
- def _display(s, msg):
- print msg
- sys.stdout.flush()
-
- def warn(s, msg, minor =3D 0):
- warntxt =3D 'WARNING'
- if minor:
- warntxt =3D 'warning'
- sys.stderr.write(warntxt + ": " + str(msg) + "\n")
-
- def sleep(s, sleepsecs, siglistener):
- if s.verbose >=3D 0:
- s._msg("Sleeping for %d:%02d" % (sleepsecs / 60, sleepsecs %=
60))
- return UIBase.sleep(s, sleepsecs, siglistener)
-
- def sleeping(s, sleepsecs, remainingsecs):
- if sleepsecs > 0:
- time.sleep(sleepsecs)
- return 0
-
- def locked(s):
- s.warn("Another OfflineIMAP is running with the same metadatadir=
; exiting.")
-
-class Quiet(Basic):
- def __init__(s, config, verbose =3D -1):
- Basic.__init__(s, config, verbose)
diff --git a/offlineimap/ui/TTY.py b/offlineimap/ui/TTY.py
deleted file mode 100644
index 99c46d4..0000000
--- a/offlineimap/ui/TTY.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# TTY UI
-# Copyright (C) 2002 John Goerzen
-# <jgoerzen@xxxxxxxxxxxx>
-#
-# This program is free software; you can redistribute it and/or modif=
y
-# it under the terms of the GNU General Public License as published b=
y
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-13=
01 USA
-
-from UIBase import UIBase
-from getpass import getpass
-import select, sys
-from threading import *
-
-class TTYUI(UIBase):
- def __init__(s, config, verbose =3D 0):
- UIBase.__init__(s, config, verbose)
- s.iswaiting =3D 0
- s.outputlock =3D Lock()
-
- def isusable(s):
- return sys.stdout.isatty() and sys.stdin.isatty()
- =20
- def _display(s, msg):
- s.outputlock.acquire()
- try:
- if (currentThread().getName() =3D=3D 'MainThread'):
- print msg
- else:
- print "%s:\n %s" % (currentThread().getName(), msg)
- sys.stdout.flush()
- finally:
- s.outputlock.release()
-
- def getpass(s, accountname, config, errmsg =3D None):
- if errmsg:
- s._msg("%s: %s" % (accountname, errmsg))
- s.outputlock.acquire()
- try:
- return getpass("%s: Enter password: " % accountname)
- finally:
- s.outputlock.release()
-
- def mainException(s):
- if isinstance(sys.exc_info()[1], KeyboardInterrupt) and \
- s.iswaiting:
- sys.stdout.write("Timer interrupted at user request; program=
terminating. \n")
- s.terminate()
- else:
- UIBase.mainException(s)
-
diff --git a/offlineimap/ui/__init__.py b/offlineimap/ui/__init__.py
index 0206ab4..fa08df3 100644
--- a/offlineimap/ui/__init__.py
+++ b/offlineimap/ui/__init__.py
@@ -16,23 +16,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-13=
01 USA
=20
-
-import UIBase, Blinkenlights
-try:
- import TTY
-except ImportError:
- pass
-
-try:
- import curses
-except ImportError:
- pass
-else:
- import Curses
-
-import Noninteractive
-import Machine
-
+import plugins
+from plugins import *
# Must be last
import detector
=20
diff --git a/offlineimap/ui/detector.py b/offlineimap/ui/detector.py
index 4ec7503..571bb01 100644
--- a/offlineimap/ui/detector.py
+++ b/offlineimap/ui/detector.py
@@ -19,17 +19,8 @@
import offlineimap.ui
import sys
=20
-DEFAULT_UI_LIST =3D ('Curses.Blinkenlights', 'TTY.TTYUI',
- 'Noninteractive.Basic', 'Noninteractive.Quiet',
- 'Machine.MachineUI')
-
def findUI(config, chosenUI=3DNone):
- uistrlist =3D list(DEFAULT_UI_LIST)
- namespace=3D{}
- for ui in dir(offlineimap.ui):
- if ui.startswith('_') or ui in ('detector', 'UIBase'):
- continue
- namespace[ui]=3Dgetattr(offlineimap.ui, ui)
+ uistrlist =3D []
=20
if chosenUI is not None:
uistrlist =3D [chosenUI]
@@ -37,7 +28,7 @@ def findUI(config, chosenUI=3DNone):
uistrlist =3D config.get("general", "ui").replace(" ", "").split=
(",")
=20
for uistr in uistrlist:
- uimod =3D getUImod(uistr, config.getlocaleval(), namespace)
+ uimod =3D getUImod(uistr)
if uimod:
uiinstance =3D uimod(config)
if uiinstance.isusable():
@@ -45,10 +36,12 @@ def findUI(config, chosenUI=3DNone):
sys.stderr.write("ERROR: No UIs were found usable!\n")
sys.exit(200)
=20
-def getUImod(uistr, localeval, namespace):
+def getUImod(uistr):
try:
- uimod =3D localeval.eval(uistr, namespace)
+ uimod =3D __import__("offlineimap.ui.plugins." + uistr,[],[],[ui=
str])
+ uiClass =3D uimod.getUIClass()
except (AttributeError, NameError), e:
- #raise
+ print e
+ #raise
return None
- return uimod
+ return uiClass
diff --git a/setup.py b/setup.py
index 92fb1fd..fe3c53d 100644
--- a/setup.py
+++ b/setup.py
@@ -33,7 +33,8 @@ setup(name =3D "offlineimap",
author_email =3D offlineimap.version.author_email,
url =3D offlineimap.version.homepage,
packages =3D ['offlineimap', 'offlineimap.folder',
- 'offlineimap.repository', 'offlineimap.ui'],
+ 'offlineimap.repository',
+ 'offlineimap.ui','offlineimap.ui.plugins'],
scripts =3D ['bin/offlineimap'],
license =3D offlineimap.version.copyright + \
", Licensed under the GPL version 2"
--=20
1.6.2.5
- [PATCH] allow dynamic addition of ui plugins,
=3D?utf-8?q?Christoph=3D20H=3DC3=3DB6ger?=3D <=
|
|