from Tkinter import * from ScrolledText import ScrolledText import string, time, tempfile import Shells, Menu, ButtonBox import slime_abstract, slime_pgpmime, slime_send, ui_config config = ui_config.config from ui_helpers import * from ui_compose import * from slime_folderinfo import * INITIAL_INBOX_SCAN_DELAY = 5*1000 folderinfo_file = os.path.expanduser("~/.slime/folderinfo") root = None draft_folder = None fcc_folder = None pgp_password = None pgp_timestamp = 0 class DummyFeedback: def delete_msg(self, foo, msg, msgwin): pass def undelete_msg(self, foo, msg, msgwin): pass def close_msgwin(self, foo, msgwin): pass def next_msg(self, foo, msg, msgwin): pass def prev_msg(self, foo, msg, msgwin): pass def mark_unread_msg(self, foo, msg, msgwin): pass def send_msg(self, msg): pass class MessageWindow: _all_msgwin = [] def __init__(self): self._all_msgwin.append(self) self.top = Toplevel() self.top.title("Slime Message Window") self.top.iconname("Slime Message Window") self.feedback = DummyFeedback() self.shorten_headers = 1 self.top.bind("n", self.next_msg) self.top.bind("p", self.prev_msg) self.top.bind("d", self.delete_msg) self.top.bind("u", self.undelete_msg) self.top.bind("", self.next_line) self.top.bind("", self.prev_line) self.top.bind("", self.next_page) self.top.bind("", self.prev_page) self.top.bind("", self.next_page_or_msg) menubar = Menu.Menubar(self.top) Menu.Pulldown(menubar, "Menu", ["Reply", self.reply_to_msg], ["-"], ["Delete", self.delete_msg], ["Undelete", self.undelete_msg], ["Mark unread", self.undelete_msg], ["-"], ["Next", self.next_msg], ["Prev", self.prev_msg], ["-"], ["Show selected headers", self.show_important_headers], ["Show full headers", self.show_full_headers], ["-"], ["Close", self.close_msgwin]) buttonbar = Frame(self.top) buttonbar.pack(fill=X) CommandButton(buttonbar, "Delete", self.delete_msg) self.textwin = ScrolledText(self.top) self.textwin.pack(expand=YES, fill=BOTH) self.textwin.config(background="white") self.textwin.config(font=config["normal-font"]) self.disable() statusbar = Frame(self.top) statusbar.pack(fill=X) b = Label(statusbar, text="Status:") b.pack(side=LEFT) self.statuswin = Label(statusbar, text="") self.statuswin.config(relief=SUNKEN) self.statuswin.pack(side=LEFT) b = Label(statusbar, text="Size:") b.pack(side=LEFT) self.sizewin = Label(statusbar, text="", relief=SUNKEN) self.sizewin.pack(side=LEFT) self.feedback = None self.msg = None def show_full_headers(self): self.shorten_headers = 0 self.re_set() def show_important_headers(self): self.shorten_headers = 1 self.re_set() def place(self, x, y): self.top.geometry("+%d+%d" % (x,y)) def enable(self): self.textwin.config(state="normal") def disable(self): self.textwin.config(state="disabled") def reply_to_msg(self, event=None): self.feedback.reply_to_msg(self.msg) def delete_msg(self, event=None): self.feedback.delete_msg(event, self.msg, self) def undelete_msg(self, event=None): self.feedback.undelete_msg(event, self.msg, self) def close_msgwin(self, event=None): self.feedback.close_msgwin(self) self.top.destroy() self._all_msgwin.remove(self) def next_msg(self, event=None): self.feedback.next_msg(event, self.msg, self) def prev_msg(self, event=None): self.feedback.prev_msg(event, self.msg, self) def get_top_index(self): index = self.textwin.index("@0,0") pair = string.split(index, ".") return string.atoi(pair[0]), string.atoi(pair[1]) def height(self): return string.atoi(self.textwin.cget("height")) def next_line(self, event=None): line, col = self.get_top_index() self.textwin.yview(line) def prev_line(self, event=None): line, col = self.get_top_index() line = line - 1 if line < 0: line = 0 self.textwin.yview(line-1) def next_page(self, event=None): line, col = self.get_top_index() line = line + (self.height() - 2) self.textwin.yview(line-1) def next_page_or_msg(self, event=None): line, col = self.get_top_index() line2 = line + (self.height() - 2) if line2 >= self.lines() - 1: self.next_msg() else: self.textwin.yview(line2-1) def prev_page(self, event=None): line, col = self.get_top_index() line = line - (self.height() - 2) if line < 1: line = 1 self.textwin.yview(line-1) def mark_unread_msg(self, event=None): self.feedback.mark_unread_msg(event, self.msg, self) def set(self, msg, feedback): if feedback: self.feedback = feedback else: self.feedback = DummyFeedback() self.msg = msg self.enable() self.textwin.delete('0.0', 'end') for tag in self.textwin.tag_names(): self.textwin.tag_delete(tag) self.append_part(msg) self.append_separator() self.disable() x = "%.20s: %.30s" % (msg["from"], msg["subject"]) self.top.title(x) self.top.iconname(x) self.update_status() def re_set(self): self.set(self.msg, self.feedback) def update_status(self): for msgwin in self._all_msgwin: x = msgwin.msg.get_status() if x == "": x = " " msgwin.statuswin.config(text=x) msgwin.sizewin.config(text="%d" % self.lines()) def append_part(self, part): self.append_headers(part) type = part.getmaintype() subtype = part.getsubtype() if type == "multipart": if subtype == "signed": self.append_multipart_signed(part) else: self.append_multipart_any(part) elif type == "image" and subtype == "gif": self.append_separator() self.append_image_gif(part) else: self.append_separator() self.append_text_any(part) def append_multipart_signed(self, part): if ui_config.config["check-pgp-signatures"]: try: slime_pgpmime.check_signature(part) self.append_line("Signature: OK\n") except slime_pgpmime.SecretMissing, detail: self.append_error("Signature: Secret key is missing\n") except slime_pgpmime.PublicMissing, detail: self.append_error("Signature: Public key is missing\n") except slime_pgpmime.GoodUntrustedSignature, detail: self.append_error("Signature: Good, but untrusted signature\n") except slime_pgpmime.BadUntrustedSignature, detail: self.append_error("Signature: Bad signature\n") except slime_pgpmime.BadTrustedSignature, detail: self.append_error("Signature: Bad signature\n") except slime_pgpmime.OtherResult, detail: self.append_error("Signature: Unknown result\n") else: self.append_line("Signature: unchecked\n") self.append_multipart_any(part) def append_multipart_any(self, part): for p in part.getbodyparts(): self.append_part(p) def append_error(self, line): self.textwin.insert('end', line) tag = self.tag_last_line('error') self.textwin.tag_config(tag, background='red') def append_line(self, line): self.textwin.insert('end', line) def append_image_gif(self, part): data = part.getbodytext() tempname = tempfile.mktemp() helpers.write_text_to_named_file(tempname, data) photo = PhotoImage(file=tempname) label = Label(master=self.textwin, image=photo) label.pack() self.textwin.window_create('end', window=label) os.remove(tempname) def append_text_any(self, part): self.textwin.insert('end', part.getbodytext()) def append_separator(self): str = "" self.textwin.insert("end", str + "\n") tag = self.tag_last_line("sep") self.textwin.tag_config(tag, background='lightgrey', relief=RAISED, borderwidth=2, font='5x7') def lines(self): index = self.textwin.index("end") pair = string.split(index, ".") return string.atoi(pair[0]) - 2 def tag_last_line(self, tag_prefix): lines = self.lines() tag = "%s-%d" % (tag_prefix, lines) self.textwin.tag_add(tag, "%d.0" % lines, "%d.0" % (lines + 1)) return tag def append_headers(self, part): if self.shorten_headers: h = [] headers = string.split(config["important-headers"]) for header in headers: try: v = part[header] if v: h.append("%s: %s\n" % (header, v)) except KeyError: pass h = string.join(h, "") self.textwin.insert('end', h) else: self.textwin.insert('end', part.getheadertext()) class SelectionList(ScrolledText): def __init__(self, master): ScrolledText.__init__(self, master) self.pack(side=LEFT, expand=YES, fill=BOTH) self.config(wrap="none", spacing1=1, exportselection=NO) self.config(background="white") self.config(font=config["normal-font"]) self.bind("", self.select) self.bind("", self.select_double) self.bind("", self.select2) self.bind("", self.select2_double) self.bind("", self.select3) self.bind("", self.select3_double) self.disable() self.current = None def get_top_index(self): index = self.index("@0,0") pair = string.split(index, ".") return string.atoi(pair[0]), string.atoi(pair[1]) def enable(self): self.config(state="normal") def disable(self): self.config(state="disabled") def select_hook(self): pass def double_hook(self): pass def select2_hook(self, lineno): pass def double2_hook(self, lineno): pass def select3_hook(self, lineno): pass def double3_hook(self, lineno): pass def update_cursor(self): if not self.current is None: self.tag_delete("curline") self.tag_add("curline", "%d.0" % self.current, "%d.0 lineend + 1 chars" % (self.current)) self.tag_config("curline", background="darkgrey") def select_by_lineno(self, lineno): self.prev_current = self.current self.current = lineno self.select_hook() self.update_cursor() self.see("%d.0" % (lineno + 4)) self.see("%d.0" % (lineno)) def _get_lineno(self, event): index = self.index("@%d,%d" % (event.x, event.y)) pair = string.split(index, ".") return string.atoi(pair[0]) def select(self, event): self.select_by_lineno(self._get_lineno(event)) def select_double(self, event): self.double_hook() def select2(self, event): self.select2_hook(self._get_lineno(event)) def select2_double(self, event): self.double2_hook(self._get_lineno(event)) def select3(self, event): self.select3_hook(self._get_lineno(event)) def select3_double(self, event): self.double3_hook(self._get_lineno(event)) class FolderTocWindow(SelectionList): def __init__(self, master): SelectionList.__init__(self, master) self.config(width=60, height=15) self.folder = None self.msg = None self.msgwin = None self.msg_dict = {} self.msg_count = 0 self.focus_set() self.bind("n", self.next_msg) self.bind("p", self.prev_msg) self.bind("d", self.delete_msg) self.bind("u", self.undelete_msg) def set(self, folder, mcount_win, folder_win): self.enable() self.delete('0.0', 'end') n = 0 self.folder = folder self.msg = None self.msg_dict = {} self.msg_count = len(folder.list_all_threads()) for indent_level, msg in folder.list_all_threads(): if n > 0: self.insert('end', '\n') n = n + 1 self.msg_dict[msg] = n parts = self.make_toc_parts(indent_level, msg) line = self.make_toc_line(parts) self.insert('end', line) self.set_tags(parts, line, n) self.disable() self.mcount_win = mcount_win self.mcount_win.config(text="%d" % self.msg_count) self.folder_win = folder_win def re_set(self): if self.folder: old_msg = self.msg self.folder.rescan_messages() self.set(self.folder, self.mcount_win, self.folder_win) if self.msg_dict.has_key(old_msg): self.select_by_lineno(self.msg_dict[old_msg]) elif self.msgwin: self.msgwin.close_msgwin() def make_toc_line(self, parts): return "%s %s %-20.20s %s%s" % \ (parts[0], parts[4], parts[3], parts[1], parts[2]) def make_toc_parts(self, indent_level, msg): if msg.has_status("N"): status = "N" elif msg.has_status("D"): status = "D" else: status = " " if indent_level <= config["max-subject-indent"]: indent = "%*s" % (indent_level * 2, "") else: indent = "%*s(%d) " % \ (config["max-subject-indent"] * 2, "", indent_level) name, addr = msg.getaddr("from") if not name: name = addr subject = msg["subject"] if subject: subject = string.join(string.split(subject, "\n"), " ") else: subject = "" date_tuple = msg.getdate("date") if date_tuple is None: date = "%10s" % "" else: if date_tuple[0] < 1900: year = 1900 + date_tuple[0] else: year = date_tuple[0] date = "%04d-%02d-%02d" % \ (year, date_tuple[1], date_tuple[2]) return status, indent, subject, name, date def set_tags(self, parts, line, lineno): # sub1 = len(parts[0]) + 1 + len(parts[4]) + 22 + len(parts[1]) # sub2 = sub1 + len(parts[2]) sub1 = 0 sub2 = len(line) if parts[0] == "N": font = config["unread-font"] else: font = config["normal-font"] self.add_tag(".subject", lineno, sub1, sub2, font) def add_tag(self, suffix, lineno, fromcol, tocol, font): tag = "%d.%s" % (lineno, suffix) self.tag_add(tag, "%d.%d" % (lineno, fromcol), "%d.%d" % (lineno, tocol)) self.tag_configure(tag, font=font) def update_toc_line(self, msg): if not self.msg_dict.has_key(msg): return lineno = self.msg_dict[msg] parts = self.make_toc_parts(self.folder.get_msg_level(msg), msg) line = self.make_toc_line(parts) self.enable() self.delete("%d.0" % lineno, "%d.end" % lineno) self.insert("%d.0" % lineno, line) self.disable() self.set_tags(parts, line, lineno) self.folder_win.redraw_folder(self.folder) def select_hook(self): foo, self.msg = self.folder.list_all_threads()[self.current-1] if self.msg.has_status("N"): self.msg.remove_status("N") self.folder_win.decrement_unread() if not self.msgwin: self.msgwin = MessageWindow() self.put_at_good_position(self.msgwin) self.msgwin.set(self.msg, self) self.update_toc_line(self.msg) self.update_cursor() def put_at_good_position(self, msgwin): x = self.winfo_rootx() y = self.winfo_rooty() w = self.winfo_width() h = self.winfo_height() msgwin.place(x+w/10, y+h+h/10) def double_hook(self): msg = self.folder.list_all_messages()[self.current-1] msgwin = MessageWindow() msgwin.set(msg, self) def delete_msg(self, event=None, msg=None, msgwin=None): if msg is None: msg = self.msg msgwin = self.msgwin if msg: msg.delete_self() msgwin.update_status() if self.msg_dict.has_key(msg): self.update_toc_line(msg) self.update_cursor() self.next_msg(event, msg, msgwin) def undelete_msg(self, event=None, msg=None, msgwin=None): if msg is None: msg = self.msg msgwin = self.msgwin msg.undelete_self() if self.msg_dict.has_key(msg): self.update_toc_line(msg) self.update_cursor() def mark_unread_msg(self, event=None, msg=None, msgwin=None): if msg is None: msg = self.msg msgwin = self.msgwin msg.give_status("N") if self.msg_dict.has_key(msg): self.update_toc_line(msg) self.update_cursor() def next_msg(self, event=None, msg=None, msgwin=None): if msg is None: msg = self.msg msgwin = self.msgwin if msgwin == self.msgwin and self.current < self.msg_count: self.select_by_lineno(self.current+1) def prev_msg(self, event=None, msg=None, msgwin=None): if msg is None: msg = self.msg msgwin = self.msgwin if msgwin == self.msgwin and self.current > 1: self.select_by_lineno(self.current-1) def close_msgwin(self, msgwin): if msgwin == self.msgwin: self.msgwin = None def compose_msg(self): if self.folder == draft_folder and self.msg: c = ComposeWindow(self, self.msg, self) c.show() else: file = StringIO.StringIO(slime_send.make_new_message_text()) msg = draft_folder.add_message(file) self.refresh_draft_toc() c = ComposeWindow(self, msg, self) c.show() def reply_to_msg(self, origmsg): txt = slime_send.make_reply_message_text(origmsg) file = StringIO.StringIO(txt) msg = draft_folder.add_message(file) self.refresh_draft_toc() c = ComposeWindow(self, msg, self) c.show() def refresh_draft_toc(self): draft_folder.commit_changes() if self.folder == draft_folder: self.re_set() def get_pgp_authentication(self, msg): global pgp_password, pgp_timestamp if config["pgp-username"]: username = config["pgp-username"] else: d = PgpUsernameDialog(self, msg["from"]) username = d.show() if not username: return None, None now = time.time() diff = now - pgp_timestamp if diff < 0 or diff > 60*config["pgp-max-password-age"]: p = PasswordDialog(self) password = p.show() if not password: return None, None pgp_password = password pgp_timestamp = now else: password = pgp_password return username, password def send_msg(self, msg): if config["make-pgp-signatures"]: username, password = self.get_pgp_authentication(msg) if username is None: return 0 try: new_text = slime_pgpmime.make_signature(msg, username, password) except slime_pgpmime.OtherResult, detail: d = ErrorBox(self, "PGP failed", detail[2]) d.show() return 0 msg.change_text(StringIO.StringIO(new_text)) slime_send.prepare_for_send(msg) slime_send.send_msg(msg) if self.store_to_fcc_folder(msg) == 0: return 0 msg.delete_self() self.refresh_draft_toc() return 1 def store_to_fcc_folder(self, msg): global fcc_folder, tops if not config["fcc-folder"]: return 1 if not fcc_folder: fcc_folder = \ helpers.find_folder_by_name(config["fcc-folder"], root) if not fcc_folder: d = ErrorBox(self, "No FCC folder", ("There is no folder named\n%s\n" + "No copy of message has been saved") \ % (config["fcc-folder"])) d.show() return 0 try: was_open = fcc_folder.is_open() if not was_open: fcc_folder.open() fcc_folder.rescan_messages() fcc_folder.copy_message_here(msg) if not was_open: fcc_folder.close() except slime_abstract.Error, detail: d = ErrorBox(self, "FCC copy failed", "Saving copy of sent message failed:\n" + \ detail) return 0 return 1 class FolderWindow(SelectionList): def __init__(self, master): SelectionList.__init__(self, master) self.config(width=25, height=15) self.pack(expand=NO) self.folder_to_lineno = {} self.folder = None self.msgwin = None self.msg = None self.main_win = None def decrement_unread(self): if self.main_win: self.main_win.decrement_unread() def get_folders(self, root, indent=0, recurse=1): list = [] for sub in root.list_all_subfolders(): list.append(sub.get_nice_name(), sub) list.sort() result = [] for tuple in list: sub = tuple[1] result.append(indent, sub) if recurse: sub.open() sub.rescan_messages() sub.rescan_subfolders() result = result + self.get_folders(sub, indent+1, recurse-1) sub.close() return result def format_folder(self, level, folder, count_width, max_folder_indent): is_inbox = (get_folderinfo(repr(folder), "inbox") == "yes") if is_inbox: was_open = folder.is_open() if not was_open: folder.open() folder.rescan_messages() msgs = folder.list_all_messages() else: was_open = 1 msgs = [] if is_inbox: cnt = "%*s" % (count_width, len(msgs)) else: cnt = "%*s" % (count_width, "") if level <= max_folder_indent: indent = "%*s" % (level*2, "") else: indent = "%*s(%d) " % (max_folder_indent*2, "", level) str = "%s %s%s" % (cnt, indent, folder.get_nice_name()) font = config["normal-font"] for msg in msgs: if msg.has_status("N"): font = config["unread-font"] if is_inbox and not was_open: folder.close() return str, font def draw_folders(self): line, col = self.get_top_index() self.enable() self.delete('0.0', 'end') self.folder_to_lineno = {} lineno = 1 for level, f in self.folders: self.folder_to_lineno[f] = lineno s, font = self.format_folder(level, f, config["count-width"], config["max-folder-indent"]) if lineno > 1: self.insert('end', '\n') self.insert('end', s) self.change_font_on_line(lineno, font) lineno = lineno + 1 self.disable() try: self.current = self.folder_to_lineno[self.folder] except KeyError: self.current = None self.update_cursor() self.yview(line-1) self.fcount_win.config(text="%d" % len(self.folders)) def change_font_on_line(self, lineno, font): tag = "%d.font" % (lineno) self.tag_delete(tag) self.tag_add(tag, "%d.0" % (lineno), "%d.0 lineend" % (lineno)) self.tag_config(tag, font=font) def redraw_folder(self, folder): if self.folder_to_lineno.has_key(folder): lineno = self.folder_to_lineno[folder] for level, f in self.folders: if f == folder: break s, font = self.format_folder(level, folder, config["count-width"], config["max-folder-indent"]) self.enable() self.delete("%d.0" % lineno, "%d.0 lineend" % lineno) self.insert("%d.0" % lineno, s) self.change_font_on_line(lineno, font) self.disable() if self.folder == folder: self.update_cursor() def set(self, slime_root, toc_win, fcount_win, mcount_win, main_win): self.slime_root = slime_root self.toc_win = toc_win self.fcount_win = fcount_win self.mcount_win = mcount_win self.folders = self.get_folders(slime_root, recurse=0) self.draw_folders() self.main_win = main_win def show_subfolders(self, index): this = self.folders[index] was_open = this[1].is_open() if not was_open: this[1].open() this[1].rescan_subfolders() subs = self.get_folders(this[1], this[0]+1, recurse=0) if not was_open: this[1].close() self.folders = self.folders[:index+1] + subs + \ self.folders[index+1:] self.draw_folders() def hide_subfolders(self, index): this = self.folders[index] next = self.folders[index+1] i = index+1 while i < len(self.folders) and this[0] < self.folders[i][0]: i = i + 1 self.folders = self.folders[:index+1] + self.folders[i:] self.draw_folders() def show_or_hide_subfolders(self, index=None): if index is None: index = self.current-1 this = self.folders[index] if index+1 < len(self.folders): next = self.folders[index+1] if this[0] < next[0]: self.hide_subfolders(index) else: self.show_subfolders(index) else: self.show_subfolders(index) def select_hook(self): new_folder = self.folders[self.current-1][1] if not self.folder: self.folder = new_folder self.folder.open() self.folder.rescan_subfolders() self.toc_win.set(self.folder, self.mcount_win, self) self.show_or_hide_subfolders() elif self.folder == new_folder: self.folder.rescan_subfolders() self.toc_win.re_set() self.show_or_hide_subfolders() else: if self.commit_changes(re_set = 0): self.folder.close() self.folder = new_folder self.folder.open() self.folder.rescan_subfolders() self.folder.rescan_messages() self.toc_win.set(self.folder, self.mcount_win, self) self.show_or_hide_subfolders() else: self.current = self.prev_current def commit_changes(self, re_set = 1): if self.folder and not self.folder.is_clean(): d = YesNoDialog(self, "Commit folder %s?" % \ self.folder.get_nice_name()) doit = d.show() d.destroy() if doit: self.folder.commit_changes() self.redraw_folder(self.folder) if re_set: self.toc_win.re_set() return 1 return 0 return 1 def select2_hook(self, lineno): self.show_or_hide_subfolders(lineno-1) def select3_hook(self, lineno): if not self.folder: print "no open folder" elif not self.toc_win.msg: print "no selected message" else: target = self.folders[lineno-1][1] was_open = target.is_open() if not was_open: target.open() target.rescan_messages() newmsg = target.move_message_here(self.toc_win.msg) if not was_open: target.close() self.toc_win.delete_msg() class MainWindow(Frame): default_toplevel_used = 0 def __init__(self, slime_root): if self.default_toplevel_used: master = Toplevel() else: master = None self.default_toplevel_used = 1 Frame.__init__(self, master) self.pack(expand=YES, fill=BOTH) t = self.winfo_toplevel() t.title("Slime") t.iconname("Slime") menubar = Menu.Menubar(self) self.slime_root = slime_root f = Frame(self) f.pack(expand=YES, fill=BOTH) self.folder_win = FolderWindow(f) self.ftoc_win = FolderTocWindow(f) statusbar = Frame(self) statusbar.pack(fill=X) b = Label(statusbar, text="Folders:") b.pack(side=LEFT) self.fcount_win = Label(statusbar, text="", relief=SUNKEN) self.fcount_win.pack(side=LEFT) b = Label(statusbar, text="Unread:") b.pack(side=LEFT) self.total_unread_win = Label(statusbar, text="", relief=SUNKEN) self.total_unread_win.pack(side=LEFT) self.mcount_win = Label(statusbar, text="", relief=SUNKEN) self.mcount_win.pack(side=RIGHT) b = Label(statusbar, text="Messages:") b.pack(side=RIGHT) self.folder_win.set(self.slime_root, self.ftoc_win, self.fcount_win, self.mcount_win, self) Menu.Pulldown(menubar, "&Folder", ["Scan inboxes", self.scan_inboxes], ["Rescan folder", self.ftoc_win.re_set], ["Commit folder", self.folder_win.commit_changes], ["-"], ["Compose message", self.ftoc_win.compose_msg], ["-"], ["Edit folder options", self.edit_folderinfo], ["-"], ["Options", ui_config.edit_config], ["Save options", self.save_config], ["-"], ["Exit", self.quit_program]) self.unread = 0 self.after(INITIAL_INBOX_SCAN_DELAY, self.do_inbox_scan) def save_config(self): ui_config.save_config() d = InfoBox(self, "Config saved", "Configuration has been saved") d.show() def edit_folderinfo(self): if self.folder_win.folder is None: return name = repr(self.folder_win.folder) is_inbox = (get_folderinfo(name, "inbox") == "yes") d = FolderInfoEditor(self, name, is_inbox) is_inbox = d.show() if is_inbox is None: return if is_inbox: set_folderinfo(name, "inbox", "yes") else: set_folderinfo(name, "inbox", "no") self.folder_win.redraw_folder(self.folder_win.folder) def do_inbox_scan(self): self.scan_inboxes() self.after(config["inbox-scan-frequency"]*60*1000, self.do_inbox_scan) def scan_inboxes(self): global root names = get_folders_with_info("inbox", "yes") helpers.clear_name_to_folder_cache() unread = 0 for name in names: folder = helpers.find_folder_by_name(name, root) was_open = folder.is_open() if not was_open: folder.open() folder.rescan_messages() folder.rescan_subfolders() for msg in folder.list_all_messages(): if msg.has_status("N"): unread = unread + 1 self.folder_win.redraw_folder(folder) if not was_open: folder.close() helpers.clear_name_to_folder_cache() self.set_unread(unread) def set_unread(self, unread): self.unread = unread self.total_unread_win.config(text="%d" % unread) def decrement_unread(self): self.set_unread(self.unread - 1) def quit_program(self): d = YesNoDialog(self, "Really exit?") if d.show(): self.folder_win.commit_changes() self.quit() def main(): from slime_mh import * from slime_unix import * from slime_root import * from slime_draft import * import os global draft_folder, root ui_config.load_config() load_folderinfo(folderinfo_file) root = SlimeFolder_Root() root.open() draft_folder = make_draft_folder() draft_folder.open() draft_folder.rescan_messages() tops = [draft_folder] maybe_tops = [mh_top_folder(), unix_top_folder("~/Mail"), unix_top_folder("~/mail")] for f in maybe_tops: if f: tops.append(f) if len(tops) == 1: tops[0].open() tops[0].rescan_messages() tops[0].rescan_subfolders() if len(tops[0].list_all_messages()) == 0: for f in tops[0].list_all_subfolders(): root.add_subfolder(f) tops[0].close() tops = [] else: tops[0].close() for f in tops: root.add_subfolder(f) mainwin = MainWindow(root) mainwin.mainloop() save_folderinfo(folderinfo_file) draft_folder.close() root.close() if __name__ == "__main__": main()