blob: ccd658149b4f06f61b504e22e7ff4615a9b2739e [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
Guido van Rossum18468821994-06-20 07:49:28 +00002
3# Scan MH folder, display results in window
4
5import os
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00006import re
Georg Brandl856023a2010-10-25 17:50:20 +00007import sys
Guido van Rossum18468821994-06-20 07:49:28 +00008import getopt
Georg Brandl856023a2010-10-25 17:50:20 +00009import mailbox
Benjamin Petersond6d63f52009-01-04 18:53:28 +000010from tkinter import *
Guido van Rossum18468821994-06-20 07:49:28 +000011
12from dialog import dialog
13
Georg Brandl856023a2010-10-25 17:50:20 +000014MBOXPATH = os.environ['HOME'] + '/Mail'
Guido van Rossum18468821994-06-20 07:49:28 +000015
16def main():
Tim Peters182b5ac2004-07-18 06:16:08 +000017 global root, tk, top, mid, bot
18 global folderbox, foldermenu, scanbox, scanmenu, viewer
19 global folder, seq
20 global mh, mhf
Guido van Rossum18468821994-06-20 07:49:28 +000021
Tim Peters182b5ac2004-07-18 06:16:08 +000022 # Parse command line options
Guido van Rossum18468821994-06-20 07:49:28 +000023
Tim Peters182b5ac2004-07-18 06:16:08 +000024 folder = 'inbox'
25 seq = 'all'
26 try:
27 opts, args = getopt.getopt(sys.argv[1:], '')
Guido van Rossumb940e112007-01-10 16:19:56 +000028 except getopt.error as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +000029 print(msg)
Tim Peters182b5ac2004-07-18 06:16:08 +000030 sys.exit(2)
31 for arg in args:
32 if arg[:1] == '+':
33 folder = arg[1:]
34 else:
35 seq = arg
Guido van Rossum18468821994-06-20 07:49:28 +000036
Tim Peters182b5ac2004-07-18 06:16:08 +000037 # Initialize MH
Guido van Rossum18468821994-06-20 07:49:28 +000038
Georg Brandl856023a2010-10-25 17:50:20 +000039 mh = mailbox.MH(MBOXPATH)
40 mhf = mh.get_folder(folder)
Guido van Rossum18468821994-06-20 07:49:28 +000041
Tim Peters182b5ac2004-07-18 06:16:08 +000042 # Build widget hierarchy
Guido van Rossum18468821994-06-20 07:49:28 +000043
Tim Peters182b5ac2004-07-18 06:16:08 +000044 root = Tk()
45 tk = root.tk
Guido van Rossum18468821994-06-20 07:49:28 +000046
Tim Peters182b5ac2004-07-18 06:16:08 +000047 top = Frame(root)
48 top.pack({'expand': 1, 'fill': 'both'})
Guido van Rossum18468821994-06-20 07:49:28 +000049
Tim Peters182b5ac2004-07-18 06:16:08 +000050 # Build right part: folder list
Guido van Rossum18468821994-06-20 07:49:28 +000051
Tim Peters182b5ac2004-07-18 06:16:08 +000052 right = Frame(top)
53 right.pack({'fill': 'y', 'side': 'right'})
Guido van Rossum18468821994-06-20 07:49:28 +000054
Tim Peters182b5ac2004-07-18 06:16:08 +000055 folderbar = Scrollbar(right, {'relief': 'sunken', 'bd': 2})
56 folderbar.pack({'fill': 'y', 'side': 'right'})
Guido van Rossum18468821994-06-20 07:49:28 +000057
Tim Peters182b5ac2004-07-18 06:16:08 +000058 folderbox = Listbox(right, {'exportselection': 0})
59 folderbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
Guido van Rossum18468821994-06-20 07:49:28 +000060
Tim Peters182b5ac2004-07-18 06:16:08 +000061 foldermenu = Menu(root)
62 foldermenu.add('command',
63 {'label': 'Open Folder',
64 'command': open_folder})
65 foldermenu.add('separator')
66 foldermenu.add('command',
67 {'label': 'Quit',
68 'command': 'exit'})
69 foldermenu.bind('<ButtonRelease-3>', folder_unpost)
Guido van Rossum18468821994-06-20 07:49:28 +000070
Tim Peters182b5ac2004-07-18 06:16:08 +000071 folderbox['yscrollcommand'] = (folderbar, 'set')
72 folderbar['command'] = (folderbox, 'yview')
73 folderbox.bind('<Double-1>', open_folder, 1)
74 folderbox.bind('<3>', folder_post)
Guido van Rossum18468821994-06-20 07:49:28 +000075
Tim Peters182b5ac2004-07-18 06:16:08 +000076 # Build left part: scan list
Guido van Rossum18468821994-06-20 07:49:28 +000077
Tim Peters182b5ac2004-07-18 06:16:08 +000078 left = Frame(top)
79 left.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
Guido van Rossum18468821994-06-20 07:49:28 +000080
Tim Peters182b5ac2004-07-18 06:16:08 +000081 scanbar = Scrollbar(left, {'relief': 'sunken', 'bd': 2})
82 scanbar.pack({'fill': 'y', 'side': 'right'})
Guido van Rossum18468821994-06-20 07:49:28 +000083
Tim Peters182b5ac2004-07-18 06:16:08 +000084 scanbox = Listbox(left, {'font': 'fixed'})
85 scanbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
Guido van Rossum18468821994-06-20 07:49:28 +000086
Tim Peters182b5ac2004-07-18 06:16:08 +000087 scanmenu = Menu(root)
88 scanmenu.add('command',
89 {'label': 'Open Message',
90 'command': open_message})
91 scanmenu.add('command',
92 {'label': 'Remove Message',
93 'command': remove_message})
94 scanmenu.add('command',
95 {'label': 'Refile Message',
96 'command': refile_message})
97 scanmenu.add('separator')
98 scanmenu.add('command',
99 {'label': 'Quit',
100 'command': 'exit'})
101 scanmenu.bind('<ButtonRelease-3>', scan_unpost)
Guido van Rossum18468821994-06-20 07:49:28 +0000102
Tim Peters182b5ac2004-07-18 06:16:08 +0000103 scanbox['yscrollcommand'] = (scanbar, 'set')
104 scanbar['command'] = (scanbox, 'yview')
105 scanbox.bind('<Double-1>', open_message)
106 scanbox.bind('<3>', scan_post)
Guido van Rossum18468821994-06-20 07:49:28 +0000107
Tim Peters182b5ac2004-07-18 06:16:08 +0000108 # Separator between middle and bottom part
Guido van Rossum18468821994-06-20 07:49:28 +0000109
Tim Peters182b5ac2004-07-18 06:16:08 +0000110 rule2 = Frame(root, {'bg': 'black'})
111 rule2.pack({'fill': 'x'})
Guido van Rossum18468821994-06-20 07:49:28 +0000112
Tim Peters182b5ac2004-07-18 06:16:08 +0000113 # Build bottom part: current message
Guido van Rossum18468821994-06-20 07:49:28 +0000114
Tim Peters182b5ac2004-07-18 06:16:08 +0000115 bot = Frame(root)
116 bot.pack({'expand': 1, 'fill': 'both'})
117 #
118 viewer = None
Guido van Rossum18468821994-06-20 07:49:28 +0000119
Tim Peters182b5ac2004-07-18 06:16:08 +0000120 # Window manager commands
Guido van Rossum18468821994-06-20 07:49:28 +0000121
Tim Peters182b5ac2004-07-18 06:16:08 +0000122 root.minsize(800, 1) # Make window resizable
Guido van Rossum18468821994-06-20 07:49:28 +0000123
Tim Peters182b5ac2004-07-18 06:16:08 +0000124 # Fill folderbox with text
Guido van Rossum18468821994-06-20 07:49:28 +0000125
Tim Peters182b5ac2004-07-18 06:16:08 +0000126 setfolders()
Guido van Rossum18468821994-06-20 07:49:28 +0000127
Tim Peters182b5ac2004-07-18 06:16:08 +0000128 # Fill scanbox with text
Guido van Rossum18468821994-06-20 07:49:28 +0000129
Tim Peters182b5ac2004-07-18 06:16:08 +0000130 rescan()
Guido van Rossum18468821994-06-20 07:49:28 +0000131
Tim Peters182b5ac2004-07-18 06:16:08 +0000132 # Enter mainloop
Guido van Rossum18468821994-06-20 07:49:28 +0000133
Tim Peters182b5ac2004-07-18 06:16:08 +0000134 root.mainloop()
Guido van Rossum18468821994-06-20 07:49:28 +0000135
136def folder_post(e):
Tim Peters182b5ac2004-07-18 06:16:08 +0000137 x, y = e.x_root, e.y_root
138 foldermenu.post(x - 10, y - 10)
139 foldermenu.grab_set()
Guido van Rossum18468821994-06-20 07:49:28 +0000140
141def folder_unpost(e):
Tim Peters182b5ac2004-07-18 06:16:08 +0000142 tk.call('update', 'idletasks')
143 foldermenu.grab_release()
144 foldermenu.unpost()
145 foldermenu.invoke('active')
Guido van Rossum18468821994-06-20 07:49:28 +0000146
147def scan_post(e):
Tim Peters182b5ac2004-07-18 06:16:08 +0000148 x, y = e.x_root, e.y_root
149 scanmenu.post(x - 10, y - 10)
150 scanmenu.grab_set()
Guido van Rossum18468821994-06-20 07:49:28 +0000151
152def scan_unpost(e):
Tim Peters182b5ac2004-07-18 06:16:08 +0000153 tk.call('update', 'idletasks')
154 scanmenu.grab_release()
155 scanmenu.unpost()
156 scanmenu.invoke('active')
Guido van Rossum18468821994-06-20 07:49:28 +0000157
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000158scanparser = re.compile('^ *([0-9]+)')
Guido van Rossum18468821994-06-20 07:49:28 +0000159
Guido van Rossum78b0c821994-07-13 16:53:32 +0000160def open_folder(e=None):
Tim Peters182b5ac2004-07-18 06:16:08 +0000161 global folder, mhf
162 sel = folderbox.curselection()
163 if len(sel) != 1:
164 if len(sel) > 1:
165 msg = "Please open one folder at a time"
166 else:
167 msg = "Please select a folder to open"
168 dialog(root, "Can't Open Folder", msg, "", 0, "OK")
169 return
170 i = sel[0]
171 folder = folderbox.get(i)
Georg Brandl856023a2010-10-25 17:50:20 +0000172 mhf = mh.get_folder(folder)
Tim Peters182b5ac2004-07-18 06:16:08 +0000173 rescan()
Guido van Rossum18468821994-06-20 07:49:28 +0000174
Guido van Rossum78b0c821994-07-13 16:53:32 +0000175def open_message(e=None):
Tim Peters182b5ac2004-07-18 06:16:08 +0000176 global viewer
177 sel = scanbox.curselection()
178 if len(sel) != 1:
179 if len(sel) > 1:
180 msg = "Please open one message at a time"
181 else:
182 msg = "Please select a message to open"
183 dialog(root, "Can't Open Message", msg, "", 0, "OK")
184 return
185 cursor = scanbox['cursor']
186 scanbox['cursor'] = 'watch'
187 tk.call('update', 'idletasks')
188 i = sel[0]
189 line = scanbox.get(i)
Georg Brandl856023a2010-10-25 17:50:20 +0000190 m = scanparser.match(line)
191 if m:
192 num = int(m.group(1))
193 m = mhf.get_message(num)
Tim Peters182b5ac2004-07-18 06:16:08 +0000194 if viewer: viewer.destroy()
195 from MimeViewer import MimeViewer
196 viewer = MimeViewer(bot, '+%s/%d' % (folder, num), m)
197 viewer.pack()
198 viewer.show()
199 scanbox['cursor'] = cursor
Guido van Rossum18468821994-06-20 07:49:28 +0000200
201def interestingheader(header):
Tim Peters182b5ac2004-07-18 06:16:08 +0000202 return header != 'received'
Guido van Rossum18468821994-06-20 07:49:28 +0000203
Guido van Rossum78b0c821994-07-13 16:53:32 +0000204def remove_message(e=None):
Tim Peters182b5ac2004-07-18 06:16:08 +0000205 itop = scanbox.nearest(0)
206 sel = scanbox.curselection()
207 if not sel:
208 dialog(root, "No Message To Remove",
209 "Please select a message to remove", "", 0, "OK")
210 return
211 todo = []
212 for i in sel:
213 line = scanbox.get(i)
Georg Brandl856023a2010-10-25 17:50:20 +0000214 m = scanparser.match(line)
215 if m:
216 toremove = int(m.group(1))
217 todo.append(toremove)
218 mhf.remove(toremove)
Tim Peters182b5ac2004-07-18 06:16:08 +0000219 rescan()
220 fixfocus(min(todo), itop)
Guido van Rossum18468821994-06-20 07:49:28 +0000221
222lastrefile = ''
223tofolder = None
Guido van Rossum78b0c821994-07-13 16:53:32 +0000224def refile_message(e=None):
Tim Peters182b5ac2004-07-18 06:16:08 +0000225 global lastrefile, tofolder
226 itop = scanbox.nearest(0)
227 sel = scanbox.curselection()
228 if not sel:
229 dialog(root, "No Message To Refile",
230 "Please select a message to refile", "", 0, "OK")
231 return
232 foldersel = folderbox.curselection()
233 if len(foldersel) != 1:
234 if not foldersel:
235 msg = "Please select a folder to refile to"
236 else:
237 msg = "Please select exactly one folder to refile to"
238 dialog(root, "No Folder To Refile", msg, "", 0, "OK")
239 return
240 refileto = folderbox.get(foldersel[0])
241 todo = []
242 for i in sel:
243 line = scanbox.get(i)
Georg Brandl856023a2010-10-25 17:50:20 +0000244 m = scanparser.match(line)
245 if m:
246 todo.append(int(m.group(1)))
Tim Peters182b5ac2004-07-18 06:16:08 +0000247 if lastrefile != refileto or not tofolder:
248 lastrefile = refileto
249 tofolder = None
Georg Brandl856023a2010-10-25 17:50:20 +0000250 tofolder = mh.get_folder(lastrefile)
Tim Peters182b5ac2004-07-18 06:16:08 +0000251 mhf.refilemessages(todo, tofolder)
252 rescan()
253 fixfocus(min(todo), itop)
Guido van Rossum18468821994-06-20 07:49:28 +0000254
255def fixfocus(near, itop):
Tim Peters182b5ac2004-07-18 06:16:08 +0000256 n = scanbox.size()
257 for i in range(n):
258 line = scanbox.get(repr(i))
Georg Brandl856023a2010-10-25 17:50:20 +0000259 m = scanparser.match(line)
260 if m:
261 num = int(m.group(1))
Tim Peters182b5ac2004-07-18 06:16:08 +0000262 if num >= near:
263 break
264 else:
265 i = 'end'
Tim Peters182b5ac2004-07-18 06:16:08 +0000266 scanbox.yview(itop)
Guido van Rossum18468821994-06-20 07:49:28 +0000267
268def setfolders():
Tim Peters182b5ac2004-07-18 06:16:08 +0000269 folderbox.delete(0, 'end')
Georg Brandl856023a2010-10-25 17:50:20 +0000270 for fn in mh.list_folders():
Tim Peters182b5ac2004-07-18 06:16:08 +0000271 folderbox.insert('end', fn)
Guido van Rossum18468821994-06-20 07:49:28 +0000272
273def rescan():
Tim Peters182b5ac2004-07-18 06:16:08 +0000274 global viewer
275 if viewer:
276 viewer.destroy()
277 viewer = None
278 scanbox.delete(0, 'end')
279 for line in scanfolder(folder, seq):
280 scanbox.insert('end', line)
Guido van Rossum18468821994-06-20 07:49:28 +0000281
282def scanfolder(folder = 'inbox', sequence = 'all'):
Georg Brandl856023a2010-10-25 17:50:20 +0000283 return [line[:-1] for line in
284 os.popen('scan +%s %s' % (folder, sequence), 'r').readlines()]
Guido van Rossum18468821994-06-20 07:49:28 +0000285
286main()