|
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() |