]> code.delx.au - offlineimap/blobdiff - offlineimap/head/offlineimap/ui/Tk.py
/offlineimap/head: changeset 367
[offlineimap] / offlineimap / head / offlineimap / ui / Tk.py
index 68133a30b987c84fcd8b65926a1fe4f128d6be27..3e8201a8fc018c522cd69fa64b29c185eebbb20e 100644 (file)
@@ -1,5 +1,5 @@
 # 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
@@ -16,6 +16,8 @@
 #    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 *
@@ -25,6 +27,9 @@ from ScrolledText import ScrolledText
 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):
@@ -145,19 +150,24 @@ class ThreadFrame(Frame):
 
 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)
@@ -165,7 +175,6 @@ class VerboseUI(UIBase):
 
         self.created.wait()
         del self.created
-        time.sleep(1)
 
         if doidlevac:
             t = threadutil.ExitNotifyThread(target = self.idlevacuum,
@@ -176,9 +185,22 @@ class VerboseUI(UIBase):
     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)
@@ -260,6 +282,9 @@ class VerboseUI(UIBase):
 
 
     def init_banner(s):
+        s.threadframes = {}
+        s.availablethreadframes = []
+        s.tflock = Lock()
         s._createTopWindow()
         s._msg(version.productname + " " + version.versionstr + ", " +\
                version.copyright)
@@ -270,63 +295,108 @@ class VerboseUI(UIBase):
         
         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):
@@ -343,25 +413,8 @@ class LEDThreadFrame:
         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'
@@ -371,25 +424,19 @@ class Blinkenlights(VerboseUI):
         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,
@@ -405,10 +452,8 @@ class Blinkenlights(VerboseUI):
         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",
@@ -420,21 +465,12 @@ class Blinkenlights(VerboseUI):
         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:
@@ -449,7 +485,7 @@ class Blinkenlights(VerboseUI):
             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("")
@@ -457,70 +493,13 @@ class Blinkenlights(VerboseUI):
             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)
@@ -532,7 +511,7 @@ class Blinkenlights(VerboseUI):
             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()
@@ -556,17 +535,4 @@ class Blinkenlights(VerboseUI):
             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