diff options
Diffstat (limited to 'slime-0.11/ui.py')
-rw-r--r-- | slime-0.11/ui.py | 1058 |
1 files changed, 1058 insertions, 0 deletions
diff --git a/slime-0.11/ui.py b/slime-0.11/ui.py new file mode 100644 index 0000000..016f5ca --- /dev/null +++ b/slime-0.11/ui.py @@ -0,0 +1,1058 @@ +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("<Key-Down>", self.next_line) + self.top.bind("<Key-Up>", self.prev_line) + self.top.bind("<Key-Next>", self.next_page) + self.top.bind("<Key-Prior>", self.prev_page) + self.top.bind("<Key-space>", 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("<Button-1>", self.select) + self.bind("<Double-Button-1>", self.select_double) + self.bind("<Button-2>", self.select2) + self.bind("<Double-Button-2>", self.select2_double) + self.bind("<Button-3>", self.select3) + self.bind("<Double-Button-3>", 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 = "<none>" + 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() |