/Demo/tkinter/guido/mbox.py

http://unladen-swallow.googlecode.com/ · Python · 285 lines · 219 code · 52 blank · 14 comment · 31 complexity · 048b443aa03cd70a0d0f4fdd4bd3cc6f MD5 · raw file

  1. #! /usr/bin/env python
  2. # Scan MH folder, display results in window
  3. import os
  4. import sys
  5. import re
  6. import getopt
  7. import string
  8. import mhlib
  9. from Tkinter import *
  10. from dialog import dialog
  11. mailbox = os.environ['HOME'] + '/Mail'
  12. def main():
  13. global root, tk, top, mid, bot
  14. global folderbox, foldermenu, scanbox, scanmenu, viewer
  15. global folder, seq
  16. global mh, mhf
  17. # Parse command line options
  18. folder = 'inbox'
  19. seq = 'all'
  20. try:
  21. opts, args = getopt.getopt(sys.argv[1:], '')
  22. except getopt.error, msg:
  23. print msg
  24. sys.exit(2)
  25. for arg in args:
  26. if arg[:1] == '+':
  27. folder = arg[1:]
  28. else:
  29. seq = arg
  30. # Initialize MH
  31. mh = mhlib.MH()
  32. mhf = mh.openfolder(folder)
  33. # Build widget hierarchy
  34. root = Tk()
  35. tk = root.tk
  36. top = Frame(root)
  37. top.pack({'expand': 1, 'fill': 'both'})
  38. # Build right part: folder list
  39. right = Frame(top)
  40. right.pack({'fill': 'y', 'side': 'right'})
  41. folderbar = Scrollbar(right, {'relief': 'sunken', 'bd': 2})
  42. folderbar.pack({'fill': 'y', 'side': 'right'})
  43. folderbox = Listbox(right, {'exportselection': 0})
  44. folderbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
  45. foldermenu = Menu(root)
  46. foldermenu.add('command',
  47. {'label': 'Open Folder',
  48. 'command': open_folder})
  49. foldermenu.add('separator')
  50. foldermenu.add('command',
  51. {'label': 'Quit',
  52. 'command': 'exit'})
  53. foldermenu.bind('<ButtonRelease-3>', folder_unpost)
  54. folderbox['yscrollcommand'] = (folderbar, 'set')
  55. folderbar['command'] = (folderbox, 'yview')
  56. folderbox.bind('<Double-1>', open_folder, 1)
  57. folderbox.bind('<3>', folder_post)
  58. # Build left part: scan list
  59. left = Frame(top)
  60. left.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
  61. scanbar = Scrollbar(left, {'relief': 'sunken', 'bd': 2})
  62. scanbar.pack({'fill': 'y', 'side': 'right'})
  63. scanbox = Listbox(left, {'font': 'fixed'})
  64. scanbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
  65. scanmenu = Menu(root)
  66. scanmenu.add('command',
  67. {'label': 'Open Message',
  68. 'command': open_message})
  69. scanmenu.add('command',
  70. {'label': 'Remove Message',
  71. 'command': remove_message})
  72. scanmenu.add('command',
  73. {'label': 'Refile Message',
  74. 'command': refile_message})
  75. scanmenu.add('separator')
  76. scanmenu.add('command',
  77. {'label': 'Quit',
  78. 'command': 'exit'})
  79. scanmenu.bind('<ButtonRelease-3>', scan_unpost)
  80. scanbox['yscrollcommand'] = (scanbar, 'set')
  81. scanbar['command'] = (scanbox, 'yview')
  82. scanbox.bind('<Double-1>', open_message)
  83. scanbox.bind('<3>', scan_post)
  84. # Separator between middle and bottom part
  85. rule2 = Frame(root, {'bg': 'black'})
  86. rule2.pack({'fill': 'x'})
  87. # Build bottom part: current message
  88. bot = Frame(root)
  89. bot.pack({'expand': 1, 'fill': 'both'})
  90. #
  91. viewer = None
  92. # Window manager commands
  93. root.minsize(800, 1) # Make window resizable
  94. # Fill folderbox with text
  95. setfolders()
  96. # Fill scanbox with text
  97. rescan()
  98. # Enter mainloop
  99. root.mainloop()
  100. def folder_post(e):
  101. x, y = e.x_root, e.y_root
  102. foldermenu.post(x - 10, y - 10)
  103. foldermenu.grab_set()
  104. def folder_unpost(e):
  105. tk.call('update', 'idletasks')
  106. foldermenu.grab_release()
  107. foldermenu.unpost()
  108. foldermenu.invoke('active')
  109. def scan_post(e):
  110. x, y = e.x_root, e.y_root
  111. scanmenu.post(x - 10, y - 10)
  112. scanmenu.grab_set()
  113. def scan_unpost(e):
  114. tk.call('update', 'idletasks')
  115. scanmenu.grab_release()
  116. scanmenu.unpost()
  117. scanmenu.invoke('active')
  118. scanparser = re.compile('^ *([0-9]+)')
  119. def open_folder(e=None):
  120. global folder, mhf
  121. sel = folderbox.curselection()
  122. if len(sel) != 1:
  123. if len(sel) > 1:
  124. msg = "Please open one folder at a time"
  125. else:
  126. msg = "Please select a folder to open"
  127. dialog(root, "Can't Open Folder", msg, "", 0, "OK")
  128. return
  129. i = sel[0]
  130. folder = folderbox.get(i)
  131. mhf = mh.openfolder(folder)
  132. rescan()
  133. def open_message(e=None):
  134. global viewer
  135. sel = scanbox.curselection()
  136. if len(sel) != 1:
  137. if len(sel) > 1:
  138. msg = "Please open one message at a time"
  139. else:
  140. msg = "Please select a message to open"
  141. dialog(root, "Can't Open Message", msg, "", 0, "OK")
  142. return
  143. cursor = scanbox['cursor']
  144. scanbox['cursor'] = 'watch'
  145. tk.call('update', 'idletasks')
  146. i = sel[0]
  147. line = scanbox.get(i)
  148. if scanparser.match(line) >= 0:
  149. num = string.atoi(scanparser.group(1))
  150. m = mhf.openmessage(num)
  151. if viewer: viewer.destroy()
  152. from MimeViewer import MimeViewer
  153. viewer = MimeViewer(bot, '+%s/%d' % (folder, num), m)
  154. viewer.pack()
  155. viewer.show()
  156. scanbox['cursor'] = cursor
  157. def interestingheader(header):
  158. return header != 'received'
  159. def remove_message(e=None):
  160. itop = scanbox.nearest(0)
  161. sel = scanbox.curselection()
  162. if not sel:
  163. dialog(root, "No Message To Remove",
  164. "Please select a message to remove", "", 0, "OK")
  165. return
  166. todo = []
  167. for i in sel:
  168. line = scanbox.get(i)
  169. if scanparser.match(line) >= 0:
  170. todo.append(string.atoi(scanparser.group(1)))
  171. mhf.removemessages(todo)
  172. rescan()
  173. fixfocus(min(todo), itop)
  174. lastrefile = ''
  175. tofolder = None
  176. def refile_message(e=None):
  177. global lastrefile, tofolder
  178. itop = scanbox.nearest(0)
  179. sel = scanbox.curselection()
  180. if not sel:
  181. dialog(root, "No Message To Refile",
  182. "Please select a message to refile", "", 0, "OK")
  183. return
  184. foldersel = folderbox.curselection()
  185. if len(foldersel) != 1:
  186. if not foldersel:
  187. msg = "Please select a folder to refile to"
  188. else:
  189. msg = "Please select exactly one folder to refile to"
  190. dialog(root, "No Folder To Refile", msg, "", 0, "OK")
  191. return
  192. refileto = folderbox.get(foldersel[0])
  193. todo = []
  194. for i in sel:
  195. line = scanbox.get(i)
  196. if scanparser.match(line) >= 0:
  197. todo.append(string.atoi(scanparser.group(1)))
  198. if lastrefile != refileto or not tofolder:
  199. lastrefile = refileto
  200. tofolder = None
  201. tofolder = mh.openfolder(lastrefile)
  202. mhf.refilemessages(todo, tofolder)
  203. rescan()
  204. fixfocus(min(todo), itop)
  205. def fixfocus(near, itop):
  206. n = scanbox.size()
  207. for i in range(n):
  208. line = scanbox.get(repr(i))
  209. if scanparser.match(line) >= 0:
  210. num = string.atoi(scanparser.group(1))
  211. if num >= near:
  212. break
  213. else:
  214. i = 'end'
  215. scanbox.select_from(i)
  216. scanbox.yview(itop)
  217. def setfolders():
  218. folderbox.delete(0, 'end')
  219. for fn in mh.listallfolders():
  220. folderbox.insert('end', fn)
  221. def rescan():
  222. global viewer
  223. if viewer:
  224. viewer.destroy()
  225. viewer = None
  226. scanbox.delete(0, 'end')
  227. for line in scanfolder(folder, seq):
  228. scanbox.insert('end', line)
  229. def scanfolder(folder = 'inbox', sequence = 'all'):
  230. return map(
  231. lambda line: line[:-1],
  232. os.popen('scan +%s %s' % (folder, sequence), 'r').readlines())
  233. main()