Home | History | Annotate | Download | only in guido
      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()
    286