| #! /usr/bin/env python | |
| # Scan MH folder, display results in window | |
| import os | |
| import sys | |
| import re | |
| import getopt | |
| import string | |
| import mhlib | |
| from Tkinter import * | |
| from dialog import dialog | |
| mailbox = os.environ['HOME'] + '/Mail' | |
| def main(): | |
| global root, tk, top, mid, bot | |
| global folderbox, foldermenu, scanbox, scanmenu, viewer | |
| global folder, seq | |
| global mh, mhf | |
| # Parse command line options | |
| folder = 'inbox' | |
| seq = 'all' | |
| try: | |
| opts, args = getopt.getopt(sys.argv[1:], '') | |
| except getopt.error, msg: | |
| print msg | |
| sys.exit(2) | |
| for arg in args: | |
| if arg[:1] == '+': | |
| folder = arg[1:] | |
| else: | |
| seq = arg | |
| # Initialize MH | |
| mh = mhlib.MH() | |
| mhf = mh.openfolder(folder) | |
| # Build widget hierarchy | |
| root = Tk() | |
| tk = root.tk | |
| top = Frame(root) | |
| top.pack({'expand': 1, 'fill': 'both'}) | |
| # Build right part: folder list | |
| right = Frame(top) | |
| right.pack({'fill': 'y', 'side': 'right'}) | |
| folderbar = Scrollbar(right, {'relief': 'sunken', 'bd': 2}) | |
| folderbar.pack({'fill': 'y', 'side': 'right'}) | |
| folderbox = Listbox(right, {'exportselection': 0}) | |
| folderbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'}) | |
| foldermenu = Menu(root) | |
| foldermenu.add('command', | |
| {'label': 'Open Folder', | |
| 'command': open_folder}) | |
| foldermenu.add('separator') | |
| foldermenu.add('command', | |
| {'label': 'Quit', | |
| 'command': 'exit'}) | |
| foldermenu.bind('<ButtonRelease-3>', folder_unpost) | |
| folderbox['yscrollcommand'] = (folderbar, 'set') | |
| folderbar['command'] = (folderbox, 'yview') | |
| folderbox.bind('<Double-1>', open_folder, 1) | |
| folderbox.bind('<3>', folder_post) | |
| # Build left part: scan list | |
| left = Frame(top) | |
| left.pack({'expand': 1, 'fill': 'both', 'side': 'left'}) | |
| scanbar = Scrollbar(left, {'relief': 'sunken', 'bd': 2}) | |
| scanbar.pack({'fill': 'y', 'side': 'right'}) | |
| scanbox = Listbox(left, {'font': 'fixed'}) | |
| scanbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'}) | |
| scanmenu = Menu(root) | |
| scanmenu.add('command', | |
| {'label': 'Open Message', | |
| 'command': open_message}) | |
| scanmenu.add('command', | |
| {'label': 'Remove Message', | |
| 'command': remove_message}) | |
| scanmenu.add('command', | |
| {'label': 'Refile Message', | |
| 'command': refile_message}) | |
| scanmenu.add('separator') | |
| scanmenu.add('command', | |
| {'label': 'Quit', | |
| 'command': 'exit'}) | |
| scanmenu.bind('<ButtonRelease-3>', scan_unpost) | |
| scanbox['yscrollcommand'] = (scanbar, 'set') | |
| scanbar['command'] = (scanbox, 'yview') | |
| scanbox.bind('<Double-1>', open_message) | |
| scanbox.bind('<3>', scan_post) | |
| # Separator between middle and bottom part | |
| rule2 = Frame(root, {'bg': 'black'}) | |
| rule2.pack({'fill': 'x'}) | |
| # Build bottom part: current message | |
| bot = Frame(root) | |
| bot.pack({'expand': 1, 'fill': 'both'}) | |
| # | |
| viewer = None | |
| # Window manager commands | |
| root.minsize(800, 1) # Make window resizable | |
| # Fill folderbox with text | |
| setfolders() | |
| # Fill scanbox with text | |
| rescan() | |
| # Enter mainloop | |
| root.mainloop() | |
| def folder_post(e): | |
| x, y = e.x_root, e.y_root | |
| foldermenu.post(x - 10, y - 10) | |
| foldermenu.grab_set() | |
| def folder_unpost(e): | |
| tk.call('update', 'idletasks') | |
| foldermenu.grab_release() | |
| foldermenu.unpost() | |
| foldermenu.invoke('active') | |
| def scan_post(e): | |
| x, y = e.x_root, e.y_root | |
| scanmenu.post(x - 10, y - 10) | |
| scanmenu.grab_set() | |
| def scan_unpost(e): | |
| tk.call('update', 'idletasks') | |
| scanmenu.grab_release() | |
| scanmenu.unpost() | |
| scanmenu.invoke('active') | |
| scanparser = re.compile('^ *([0-9]+)') | |
| def open_folder(e=None): | |
| global folder, mhf | |
| sel = folderbox.curselection() | |
| if len(sel) != 1: | |
| if len(sel) > 1: | |
| msg = "Please open one folder at a time" | |
| else: | |
| msg = "Please select a folder to open" | |
| dialog(root, "Can't Open Folder", msg, "", 0, "OK") | |
| return | |
| i = sel[0] | |
| folder = folderbox.get(i) | |
| mhf = mh.openfolder(folder) | |
| rescan() | |
| def open_message(e=None): | |
| global viewer | |
| sel = scanbox.curselection() | |
| if len(sel) != 1: | |
| if len(sel) > 1: | |
| msg = "Please open one message at a time" | |
| else: | |
| msg = "Please select a message to open" | |
| dialog(root, "Can't Open Message", msg, "", 0, "OK") | |
| return | |
| cursor = scanbox['cursor'] | |
| scanbox['cursor'] = 'watch' | |
| tk.call('update', 'idletasks') | |
| i = sel[0] | |
| line = scanbox.get(i) | |
| if scanparser.match(line) >= 0: | |
| num = string.atoi(scanparser.group(1)) | |
| m = mhf.openmessage(num) | |
| if viewer: viewer.destroy() | |
| from MimeViewer import MimeViewer | |
| viewer = MimeViewer(bot, '+%s/%d' % (folder, num), m) | |
| viewer.pack() | |
| viewer.show() | |
| scanbox['cursor'] = cursor | |
| def interestingheader(header): | |
| return header != 'received' | |
| def remove_message(e=None): | |
| itop = scanbox.nearest(0) | |
| sel = scanbox.curselection() | |
| if not sel: | |
| dialog(root, "No Message To Remove", | |
| "Please select a message to remove", "", 0, "OK") | |
| return | |
| todo = [] | |
| for i in sel: | |
| line = scanbox.get(i) | |
| if scanparser.match(line) >= 0: | |
| todo.append(string.atoi(scanparser.group(1))) | |
| mhf.removemessages(todo) | |
| rescan() | |
| fixfocus(min(todo), itop) | |
| lastrefile = '' | |
| tofolder = None | |
| def refile_message(e=None): | |
| global lastrefile, tofolder | |
| itop = scanbox.nearest(0) | |
| sel = scanbox.curselection() | |
| if not sel: | |
| dialog(root, "No Message To Refile", | |
| "Please select a message to refile", "", 0, "OK") | |
| return | |
| foldersel = folderbox.curselection() | |
| if len(foldersel) != 1: | |
| if not foldersel: | |
| msg = "Please select a folder to refile to" | |
| else: | |
| msg = "Please select exactly one folder to refile to" | |
| dialog(root, "No Folder To Refile", msg, "", 0, "OK") | |
| return | |
| refileto = folderbox.get(foldersel[0]) | |
| todo = [] | |
| for i in sel: | |
| line = scanbox.get(i) | |
| if scanparser.match(line) >= 0: | |
| todo.append(string.atoi(scanparser.group(1))) | |
| if lastrefile != refileto or not tofolder: | |
| lastrefile = refileto | |
| tofolder = None | |
| tofolder = mh.openfolder(lastrefile) | |
| mhf.refilemessages(todo, tofolder) | |
| rescan() | |
| fixfocus(min(todo), itop) | |
| def fixfocus(near, itop): | |
| n = scanbox.size() | |
| for i in range(n): | |
| line = scanbox.get(repr(i)) | |
| if scanparser.match(line) >= 0: | |
| num = string.atoi(scanparser.group(1)) | |
| if num >= near: | |
| break | |
| else: | |
| i = 'end' | |
| scanbox.select_from(i) | |
| scanbox.yview(itop) | |
| def setfolders(): | |
| folderbox.delete(0, 'end') | |
| for fn in mh.listallfolders(): | |
| folderbox.insert('end', fn) | |
| def rescan(): | |
| global viewer | |
| if viewer: | |
| viewer.destroy() | |
| viewer = None | |
| scanbox.delete(0, 'end') | |
| for line in scanfolder(folder, seq): | |
| scanbox.insert('end', line) | |
| def scanfolder(folder = 'inbox', sequence = 'all'): | |
| return map( | |
| lambda line: line[:-1], | |
| os.popen('scan +%s %s' % (folder, sequence), 'r').readlines()) | |
| main() |