# Tk UI
-# Copyright (C) 2002 John Goerzen
+# Copyright (C) 2002, 2003 John Goerzen
# <jgoerzen@complete.org>
#
# This program is free software; you can redistribute it and/or modify
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+from __future__ import nested_scopes
+
from Tkinter import *
import tkFont
from threading import *
from offlineimap import threadutil, version
from Queue import Queue
from UIBase import UIBase
+from offlineimap.ui.Blinkenlights import BlinkenBase
+
+usabletest = None
class PasswordDialog:
def __init__(self, accountname, config, master=None, errmsg = None):
class VerboseUI(UIBase):
def isusable(s):
+ global usabletest
+ if usabletest != None:
+ return usabletest
+
try:
Tk().destroy()
- return 1
+ usabletest = 1
except TclError:
- return 0
-
+ usabletest = 0
+ return usabletest
+
def _createTopWindow(self, doidlevac = 1):
- self.threadframes = {}
- self.availablethreadframes = []
- self.tflock = Lock()
self.notdeleted = 1
self.created = threading.Event()
+ self.af = {}
+ self.aflock = Lock()
+
t = threadutil.ExitNotifyThread(target = self._runmainloop,
name = "Tk Mainloop")
t.setDaemon(1)
self.created.wait()
del self.created
- time.sleep(1)
if doidlevac:
t = threadutil.ExitNotifyThread(target = self.idlevacuum,
def _runmainloop(s):
s.top = Tk()
s.top.title(version.productname + " " + version.versionstr)
- s.created.set()
+ s.top.after_idle(s.created.set)
s.top.mainloop()
s.notdeleted = 0
+
+ def getaccountframe(s):
+ accountname = s.getthreadaccount()
+ s.aflock.acquire()
+ try:
+ if accountname in s.af:
+ return s.af[accountname]
+
+ s.af[accountname] = LEDAccountFrame(s.top, accountname,
+ s.fontfamily, s.fontsize)
+ finally:
+ s.aflock.release()
+ return s.af[accountname]
def getpass(s, accountname, config, errmsg = None):
pd = PasswordDialog(accountname, config, errmsg = errmsg)
def init_banner(s):
+ s.threadframes = {}
+ s.availablethreadframes = []
+ s.tflock = Lock()
s._createTopWindow()
s._msg(version.productname + " " + version.versionstr + ", " +\
version.copyright)
b = Button(tf, text = "Exit", command = s.terminate)
b.pack(side = RIGHT)
+ s.sleeping_abort = {}
def deletingmessages(s, uidlist, destlist):
ds = s.folderlist(destlist)
s._msg("Deleting %d messages in %s" % (len(uidlist), ds))
def _sleep_cancel(s, args = None):
- s.sleeping_abort = 1
+ s.sleeping_abort[thread.get_ident()] = 1
def sleep(s, sleepsecs):
- s.sleeping_abort = 0
+ threadid = thread.get_ident()
+ s.sleeping_abort[threadid] = 0
tf = s.gettf().getthreadextraframe()
+ def sleep_cancel():
+ s.sleeping_abort[threadid] = 1
+
sleepbut = Button(tf, text = 'Sync immediately',
- command = s._sleep_cancel)
+ command = sleep_cancel)
sleepbut.pack()
UIBase.sleep(s, sleepsecs)
def sleeping(s, sleepsecs, remainingsecs):
+ retval = s.sleeping_abort[thread.get_ident()]
if remainingsecs:
s._msg("Next sync in %d:%02d" % (remainingsecs / 60,
remainingsecs % 60))
else:
s._msg("Wait done; synchronizing now.")
s.gettf().destroythreadextraframe()
+ del s.sleeping_abort[thread.get_ident()]
time.sleep(sleepsecs)
- return s.sleeping_abort
+ return retval
TkUI = VerboseUI
################################################## Blinkenlights
-class LEDCanvas(Canvas):
- def createLEDLock(self):
- self.ledlock = Lock()
- def acquireLEDLock(self):
- self.ledlock.acquire()
- def releaseLEDLock(self):
- self.ledlock.release()
- def setLEDCount(self, arg):
- self.ledcount = arg
- def getLEDCount(self):
- return self.ledcount
- def incLEDCount(self):
- self.ledcount += 1
+class LEDAccountFrame:
+ def __init__(self, top, accountname, fontfamily, fontsize):
+ self.top = top
+ self.accountname = accountname
+ self.fontfamily = fontfamily
+ self.fontsize = fontsize
+ self.frame = Frame(self.top, background = 'black')
+ self.frame.pack(side = BOTTOM, expand = 1, fill = X)
+ self._createcanvas(self.frame)
+
+ self.label = Label(self.frame, text = accountname,
+ background = "black", foreground = "blue",
+ font = (self.fontfamily, self.fontsize))
+ self.label.grid(sticky = E, row = 0, column = 1)
+
+ def getnewthreadframe(s):
+ return LEDThreadFrame(s.canvas)
+
+ def _createcanvas(self, parent):
+ c = LEDFrame(parent)
+ self.canvas = c
+ c.grid(sticky = E, row = 0, column = 0)
+ parent.grid_columnconfigure(1, weight = 1)
+ #c.pack(side = LEFT, expand = 0, fill = X)
+
+ def startsleep(s, sleepsecs):
+ s.sleeping_abort = 0
+ s.button = Button(s.frame, text = "Sync now", command = s.syncnow,
+ background = "black", activebackground = "black",
+ activeforeground = "white",
+ foreground = "blue", highlightthickness = 0,
+ padx = 0, pady = 0,
+ font = (s.fontfamily, s.fontsize), borderwidth = 0,
+ relief = 'solid')
+ s.button.grid(sticky = E, row = 0, column = 2)
+
+ def syncnow(s):
+ s.sleeping_abort = 1
+
+ def sleeping(s, sleepsecs, remainingsecs):
+ if remainingsecs:
+ s.button.config(text = 'Sync now (%d:%02d remain)' % \
+ (remainingsecs / 60, remainingsecs % 60))
+ time.sleep(sleepsecs)
+ else:
+ s.button.destroy()
+ del s.button
+ return s.sleeping_abort
+
+class LEDFrame(Frame):
+ """This holds the different lights."""
+ def getnewobj(self):
+ retval = Canvas(self, background = 'black', height = 20, bd = 0,
+ highlightthickness = 0, width = 10)
+ retval.pack(side = LEFT, padx = 0, pady = 0, ipadx = 0, ipady = 0)
+ return retval
class LEDThreadFrame:
+ """There is one of these for each little light."""
def __init__(self, master):
- self.canvas = master
+ self.canvas = master.getnewobj()
self.color = ''
- try:
- self.canvas.acquireLEDLock()
- startpos = 5 + self.canvas.getLEDCount() * 10
- self.canvas.incLEDCount()
- finally:
- self.canvas.releaseLEDLock()
- self.ovalid = self.canvas.create_oval(startpos, 5, startpos + 5,
- 10, fill = 'gray',
+ self.ovalid = self.canvas.create_oval(4, 4, 9,
+ 9, fill = 'gray',
outline = '#303030')
def setcolor(self, newcolor):
else:
self.setcolor('black')
- def destroythreadextraframe(self):
- pass
-
- def getthreadextraframe(self):
- raise NotImplementedError
-
- def setaccount(self, account):
- pass
- def setmailbox(self, mailbox):
- pass
- def updateloclabel(self):
- pass
- def appendmessage(self, newtext):
- pass
- def setmessage(self, newtext):
- pass
-
-class Blinkenlights(VerboseUI):
+class Blinkenlights(BlinkenBase, VerboseUI):
def __init__(s, config, verbose = 0):
VerboseUI.__init__(s, config, verbose)
s.fontfamily = 'Helvetica'
if config.has_option('ui.Tk.Blinkenlights', 'fontsize'):
s.fontsize = config.getint('ui.Tk.Blinkenlights', 'fontsize')
+ def isusable(s):
+ return VerboseUI.isusable(s)
+
def _createTopWindow(self):
VerboseUI._createTopWindow(self, 0)
#self.top.resizable(width = 0, height = 0)
self.top.configure(background = 'black', bd = 0)
- c = LEDCanvas(self.top, background = 'black', height = 20, bd = 0,
- highlightthickness = 0)
- c.setLEDCount(0)
- c.createLEDLock()
- self.canvas = c
- c.pack(side = BOTTOM, expand = 0, fill = X)
+
widthmetric = tkFont.Font(family = self.fontfamily, size = self.fontsize).measure("0")
- self.loglines = 5
- if self.config.has_option("ui.Tk.Blinkenlights", "loglines"):
- self.loglines = self.config.getint("ui.Tk.Blinkenlights",
- "loglines")
- self.bufferlines = 500
- if self.config.has_option("ui.Tk.Blinkenlights", "bufferlines"):
- self.bufferlines = self.config.getint("ui.Tk.Blinkenlights",
- "bufferlines")
+ self.loglines = self.config.getdefaultint("ui.Tk.Blinkenlights",
+ "loglines", 5)
+ self.bufferlines = self.config.getdefaultint("ui.Tk.Blinkenlights",
+ "bufferlines", 500)
self.text = ScrolledText(self.top, bg = 'black', #scrollbar = 'y',
font = (self.fontfamily, self.fontsize),
bd = 0, highlightthickness = 0, setgrid = 0,
self.tags = []
self.textlock = Lock()
- def gettf(s, newtype=LEDThreadFrame):
- return VerboseUI.gettf(s, newtype, s.canvas)
-
def init_banner(s):
+ BlinkenBase.init_banner(s)
s._createTopWindow()
menubar = Menu(s.top, activebackground = "black",
activeforeground = "white",
menubar.add_command(label = "Exit", command = s.terminate)
s.top.config(menu = menubar)
s.menubar = menubar
- s.gettf().setcolor('red')
- s._msg(version.banner)
s.text.see(END)
- s.top.resizable(width = 0, height = 0)
- if s.config.has_option("ui.Tk.Blinkenlights", "showlog") and \
- s.config.getboolean("ui.Tk.Blinkenlights", "showlog"):
+ if s.config.getdefaultboolean("ui.Tk.Blinkenlights", "showlog", 1):
s._togglelog()
- #s.tflock.acquire()
- #try:
- # for i in range(s.top.winfo_reqwidth() / 10 - 1):
- # newframe = LEDThreadFrame(s.canvas)
- # newframe.setthread(None)
- # s.availablethreadframes.append(newframe)
- #finally:
- # s.tflock.release()
+ s.gettf().setcolor('red')
+ s.top.resizable(width = 0, height = 0)
+ s._msg(version.banner)
def _togglelog(s):
if s.textenabled:
s.top.update()
else:
- s.text.pack(side = BOTTOM, expand = 1, fill = BOTH)
+ s.text.pack(side = TOP, expand = 1, fill = BOTH)
s.textenabled = 1
s.top.update()
s.top.geometry("")
s._rescroll()
s.top.resizable(width = 1, height = 1)
-
- def acct(s, accountname):
- s.gettf().setcolor('purple')
- VerboseUI.acct(s, accountname)
-
- def connecting(s, hostname, port):
- s.gettf().setcolor('gray')
- VerboseUI.connecting(s, hostname, port)
-
- def syncfolders(s, srcrepos, destrepos):
- s.gettf().setcolor('blue')
- VerboseUI.syncfolders(s, srcrepos, destrepos)
-
- def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder):
- s.gettf().setcolor('cyan')
- VerboseUI.syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder)
-
- 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')
- VerboseUI.syncingmessages(s, sr, sf, dr, df)
-
- def copyingmessage(s, uid, src, destlist):
- s.gettf().setcolor('orange')
- VerboseUI.copyingmessage(s, uid, src, destlist)
-
- def deletingmessages(s, uidlist, destlist):
- s.gettf().setcolor('red')
- VerboseUI.deletingmessages(s, uidlist, destlist)
-
- def deletingmessage(s, uid, destlist):
- s.gettf().setcolor('red')
- VerboseUI.deletingmessage(s, uid, destlist)
-
- def addingflags(s, uid, flags, destlist):
- s.gettf().setcolor('yellow')
- VerboseUI.addingflags(s, uid, flags, destlist)
-
- def deletingflags(s, uid, flags, destlist):
- s.gettf().setcolor('pink')
- VerboseUI.deletingflags(s, uid, flags, destlist)
-
- def threadExited(s, thread):
- threadid = thread.threadid
- s.tflock.acquire()
- try:
- if threadid in s.threadframes:
- tf = s.threadframes[threadid]
- del s.threadframes[threadid]
- s.availablethreadframes.append(tf)
- tf.setthread(None)
- finally:
- s.tflock.release()
-
def sleep(s, sleepsecs):
- s.sleeping_abort = 0
- s.menubar.add_command(label = "Sync now", command = s._sleep_cancel)
s.gettf().setcolor('red')
s._msg("Next sync in %d:%02d" % (sleepsecs / 60, sleepsecs % 60))
- UIBase.sleep(s, sleepsecs)
+ BlinkenBase.sleep(s, sleepsecs)
+
+ def sleeping(s, sleepsecs, remainingsecs):
+ return BlinkenBase.sleeping(s, sleepsecs, remainingsecs)
def _rescroll(s):
s.text.see(END)
for thisline in msg.split("\n"):
s._msg(thisline)
return
- VerboseUI._msg(s, msg)
+ #VerboseUI._msg(s, msg)
color = s.gettf().getcolor()
rescroll = 1
s.textlock.acquire()
s.text.config(state = DISABLED)
s.textlock.release()
- def sleeping(s, sleepsecs, remainingsecs):
- if remainingsecs:
- s.menubar.entryconfig('end', label = "Sync now (%d:%02d remain)" % \
- (remainingsecs / 60, remainingsecs % 60))
- if s.gettf().getcolor() == 'red':
- s.gettf().setcolor('black')
- else:
- s.gettf().setcolor('red')
- time.sleep(sleepsecs)
- else:
- s.menubar.delete('end')
- s.gettf().setcolor('black')
- return s.sleeping_abort