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