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