# # Pyne main window module. # # The main window is 0wned by a 'user' object, # which contains all the smelly data. # import utils import os.path from time import * from copy import copy import threading import tempfile import gtk from pyne import * import pynei18n from addressbook import * from pyneheaders import * import pynemsg from boxtypes import * from boxtypes.superbox import * import ptk.message_tree import ptk.folder_tree import ptk.msg_view_box import ptk.progresswin # specific update flavours UPDATE_CHANGED = 0 #default UPDATE_ALL = 1 UPDATE_FOLDERONLY = 2 class pyne_window(gtk.Window): """ Create Pyne window """ actgroups = None ui = None def about_box(self, _a=None): win = gtk.Window() win.set_title("About "+ver_string) win.set_transient_for(self) box1 = gtk.VBox(spacing=5) box1.set_border_width(5) win.add(box1) box1.show() def _close(_button, win=win): win.destroy() logo = gtk.Image() logo.set_from_file(os.path.join (pyne_path,"icons","logo.xpm")) close_button = gtk.Button() close_button.connect("clicked", _close) close_button.add(logo) box1.pack_start(close_button, expand=False) logo.show() close_button.show() swin = gtk.ScrolledWindow() swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.set_shadow_type(gtk.SHADOW_IN) swin.set_size_request(-1, 150) textview = gtk.TextView() textview.set_editable(False) textview.set_wrap_mode (gtk.WRAP_WORD) textview.show() box1.pack_start(swin) swin.add(textview) swin.show() textbuffer = textview.get_buffer() textbuffer.insert(textbuffer.get_end_iter(), ver_string+"\n") textbuffer.insert(textbuffer.get_end_iter(), "\n") textbuffer.insert(textbuffer.get_end_iter(), "Written by Tom Morton \n") textbuffer.insert(textbuffer.get_end_iter(), "\n") textbuffer.insert(textbuffer.get_end_iter(), "Visit the Pyne Homepage: www.soul-less.pwp.blueyonder.co.uk/pyne\n") textbuffer.insert(textbuffer.get_end_iter(), "\n") textbuffer.insert(textbuffer.get_end_iter(), "This program is distributed under the terms of the GNU Public License version 2.\n") textbuffer.insert(textbuffer.get_end_iter(), "\n") win.show() def new_window(self, _a=None, _b=None): """ Spawn a new pyne_window object. """ self.user.new_window() def close(self, _a=None, _b=None): """ Close this window. """ # Close subwindows for i in xrange(len(self.subwindows)-1, -1, -1): self.subwindows[i].close() self.user.kill_window(self.number) def _box_init (self, newbox): user = self.user # save changes newbox.save(user) user.save() # Open settings box newbox.setup(self.user, self) # Update windows newbox.changed = 1 user.update(UPDATE_ALL) def new_folder (self, _a=None): newbox = storebox.storebox(self.user, self.user.get_uid("store")) self.user.contents.append(newbox) self._box_init (newbox) def new_mailbox (self, _a=None): newbox = mailbox.mailbox(self.user, self.user.get_uid("mail")) self.user.contents.append(newbox) self._box_init (newbox) def new_nntpbox (self, _a=None): newbox = nntpbox.nntpbox(self.user, self.user.get_uid("news")) self.user.contents.append(newbox) self._box_init (newbox) def get_folder_list_selected(self): """ Return first selected item in folder_list. ** This may not be the folder currently showing messages ** ** use self.opened_folder for that ** """ uids = self.folder_list.get_selected_folder_uids() if len(uids) == 0: return None else: return self.user.get_folder_by_uid(uids[0]) def get_message_list_selected(self): """ Return ids of selected messages in message_list as a list. """ if self.display_msg != None: return [ self.display_msg[1] ] return self.message_list.get_selected_msg_ids() def update_ui(self, folder, msg): """ Decide what widgets should be enabled/disabled according to what folder and message is open. """ def _set (group, state): self.actgroups[group].set_sensitive (state) # Start with them all disabled _set ("FolderUpdate", False) _set ("IsMsgFolder", False) _set ("MsgWithBody", False) _set ("MsgAny", False) _set ("MsgNNTP", False) _set ("NewMsg", False) _set ("EditMsg", False) _set ("ReplySingle", False) _set ("ReplyAll", False) _set ("FwdMsg", False) _set ("DelMsg", False) _set ("ImportMsg", False) _set ("FolderSelected", False) _set ("MsgWithoutBody", False) _set ("Sender2AddrBook", False) # No folder selected. run away if folder == None: return _set ("FolderSelected", True) flags = folder.get_flags() _set ("IsMsgFolder", folder.__dict__.has_key ("messages")) _set ("NewMsg", flags & BOX_MAKE_NEW_MSG) _set ("FolderUpdate", flags & BOX_UPDATE_METHOD) _set ("ImportMsg", flags & BOX_IMPORT_TO) # No message. run away if msg == None: return _set ("MsgAny", True) if msg.opts & MSG_NO_BODY: _set ("MsgWithoutBody", True) return _set ("MsgNNTP", isinstance(folder, nntpbox.newsgroup)) _set ("MsgWithBody", True) _set ("ReplySingle", flags & BOX_REPLY_SINGLE) _set ("ReplyAll", flags & BOX_REPLY_GROUP) _set ("FwdMsg", flags & BOX_REPLY_FORWARD) _set ("DelMsg", flags & BOX_DEL_MESSAGE) # Message from some other guy. may collect address _set ("Sender2AddrBook", msg.senduid == None) # Outgoing message. may edit it _set ("EditMsg", msg.senduid != None) def message_list_popup_box(self, event): """ Popup menu for right click in message_list. """ # get selected message id msg_id = self.get_message_list_selected() if msg_id == []: return menu = self.ui.get_widget ("/PopupMsgMenu") menu.popup(None, None, None, event.button, event.time) def folder_list_popup_box(self, event): """ Popup menu for right click in folder_list. """ selected = self.get_folder_list_selected() if selected == None: return menu = self.ui.get_widget ("/PopupFolderMenu") menu.popup(None, None, None, event.button, event.time) def folder_settings(self, _a=None, _b=None): """ Change a folder's settings. """ user = self.user selected = self.get_folder_list_selected() if selected == None: return selected.setup(user, self) def move_folder(self, offset, _a=None): """ Move the selected folder up or down by 'offset' in the folder list. """ user = self.user # Check a folder is selected folder = self.get_folder_list_selected() if folder == None: return # Find parent object parent_object = user.parent_of(folder) # Get item number in parent's contents for x in xrange(0, len(parent_object.contents)): if parent_object.contents[x].uid == folder.uid: item = x break # delete it del parent_object.contents[item] # dump in new location parent_object.contents.insert(item+offset, folder) # save changes user.save() # update folder list user.update(UPDATE_ALL) def delete_folder(self, _a=None, _b=None): """ You clicked on delete folder. """ user = self.user # Check a folder is selected folder = self.get_folder_list_selected() if folder == None: return # Can't delete 'special' folders. if not (folder.get_flags() & BOX_ISDELETEABLE): return do_it = ptk.misc_widgets.ReturnDialog (_("Delete %s") % folder.name, \ _("Do you really want to delete\nfolder \"%s\"?") % folder.name, \ ((gtk.STOCK_OK, 1), (gtk.STOCK_CANCEL, 0)), parent_win=self) if do_it: # Find parent object to delete from parent_object = user.parent_of(folder) # Get item number in parent's contents for x in xrange(0, len(parent_object.contents)): if parent_object.contents[x].uid == folder.uid: item = x break def _recursive_nuke_folder(folder, _r): if folder.__dict__.has_key("contents"): for child in folder.contents: _r(child, _r) folder.nuke_storage() # delete whole damn lot below it _recursive_nuke_folder(parent_object.contents[item], _recursive_nuke_folder) # delete folder del parent_object.contents[item] # Save changes user.save() # update folder list user.update(UPDATE_ALL) def sender2addrbook(self, _a=None, _b=None): """ Add sender of message to address book. """ # get selected message id msg_ids = self.get_message_list_selected() folder = self.opened_folder if msg_ids == []: return msg = folder.load_article(msg_ids[0]) addr_box = AddressEditBox(self.user.addressbook, self) def test_thing(_button, addr_box=addr_box): addr_box.destroy() addr_box.ok_button.connect("clicked", test_thing) addr_box.add_senders_address(msg, addr_box) def mark_message(self, _a=None, _b=None): user = self.user # get selected message id msg_ids = self.get_message_list_selected() folder = self.opened_folder # apply to all selected messages for msg_id in msg_ids: # mark it msg = folder.load_article(msg_id) if msg.opts & MSG_ISMARKED: continue msg.opts = msg.opts | MSG_ISMARKED folder.save_article(msg) # update message list self.message_list.update_node(user, msg.opts, msg_id=msg_id) def unmark_message(self, _a=None, _b=None): user = self.user # get selected message id msg_ids = self.get_message_list_selected() folder = self.opened_folder # apply to all selected messages for msg_id in msg_ids: # unmark it msg = folder.load_article(msg_id) if not (msg.opts & MSG_ISMARKED): return msg.opts = msg.opts & (~MSG_ISMARKED) folder.save_article(msg) # update message list self.message_list.update_node(user, msg.opts, msg_id=msg_id) def reply_to_message(self, type=REPLY_AUTO, _b=None): """ Reply to sender of selected message by email. """ user = self.user # get selected message id msg_id = self.get_message_list_selected() if msg_id == []: return # find outbox the_outbox = user.get_folder_by_uid("outbox") from_folder = self.opened_folder msg_id = msg_id[0] msg = from_folder.load_article(msg_id) # we can't reply to messages we don't have the body text of if msg.opts & MSG_NO_BODY: return the_outbox.do_reply(user, msg, from_folder, type, parent_win=self) def delete_thread(self, _a=None, _b=None): """ Delete an entire thread from a newsgroup. """ folder = self.opened_folder for msg_id in self.get_message_list_selected(): if msg_id in folder.messages: folder.kill_thread(msg_id) folder.changed = 1 self.user.update() def download_body(self, _a=None, _b=None): """ Download the body of a partially downloaded (headers only) message. """ user = self.user folder = self.opened_folder msg_ids = self.get_message_list_selected() pager = ptk.progresswin.ProgressWin(_("Collecting messages..."), self.user, hidetext=_("Show Logs")) progressbar = pager.get_progress_bar() threading.Thread(target=folder.get_rest_of_msg, args=(user, msg_ids, progressbar)).start() def delete_message(self, _a=None, _b=None): """ Delete selected message(s), archiving into deleted folder unless already there. """ user = self.user # get selected message id msg_ids = self.get_message_list_selected() if msg_ids == []: return # find deleted box deleted = user.get_folder_by_uid("deleted") oldfolder = self.opened_folder for x in msg_ids: # if we are deleting from the deleted folder then # permanently delete the message if oldfolder.uid != "deleted": msg = oldfolder.load_article(x) deleted.save_new_article(msg) oldfolder.delete_article(x) else: oldfolder.delete_article(x) deleted.changed = 1 oldfolder.changed = 1 user.update() def shred_message(self, _a=None, _b=None): """ Permenently delete a message or group of messages. """ user = self.user #### Allow multiple selections [not yet] msg_ids = self.get_message_list_selected() folder = self.opened_folder if folder == None: return for x in msg_ids: # permanently delete folder.delete_article(x) folder.changed = 1 user.update() def edit_message(self, _a=None, _b=None): """ Edit the currently selected message. """ user = self.user # get selected message id msg_id = self.get_message_list_selected() if msg_id == []: return folder = self.opened_folder msg_id = msg_id[0] msg = folder.load_article(msg_id) # You can only edit outgoing messages. stands to reason. if msg.senduid == None: return msg.edit(folder, user) def print_message(self, _a=None, _b=None): """ Print the message. """ from printmodule import print_box user = self.user # get selected message id msg_id = self.get_message_list_selected() if msg_id == []: return # You can only edit outgoing messages. stands to reason. folder = self.opened_folder if folder == None: return msg_id = msg_id[0] message = folder.load_article(msg_id) print_dialog = print_box(user, message) print_dialog.set_transient_for(self) print_dialog.grab_add() print_dialog.show() def new_message(self, _a=None, _b=None): """ Compose a new message. """ user = self.user # find folder to send from folder = self.opened_folder if folder == None: return # find outbox the_outbox = user.get_folder_by_uid("outbox") # use REPLY types... if isinstance(folder, nntpbox.newsgroup): # need to find the nntpbox parent box parent_folder = user.parent_of(folder) # preset to field to NG name the_outbox.new_message(user, parent_folder, REPLY_GROUP, to=folder.name) elif isinstance(folder, mailbox.mailbox): the_outbox.new_message(user, folder, REPLY_EMAIL, to="") def unixbox_export(self, _a=None, _b=None): """ Export messages to a unix mailbox format file. """ folder = self.opened_folder def _do_it(filename): if filename == None: return try: f = open(filename, "a") except IOError: ptk.misc_widgets.InfoBox (_("Error"), _("Could not open %s") % filename, gtk.STOCK_OK, parent_win=self) return for i in folder.messages: msg = folder.load_article(i) lines = string.split(msg.body, "\n") from_addr = utils.split_address(msg.headers["from"])[1] if from_addr == "": from_addr = "nobody@localdomain" f.write("From "+from_addr+time.strftime(" %a %b %d %H:%M:%S %Y\n", time.localtime(msg.date))) # dump lines up to start of body while lines[0] != "": f.write(lines[0]+"\n") del lines[0] # dump body. escape 'From' lines this time for x in lines: if x[:5] == "From ": f.write(">"+x+"\n") else: f.write(x+"\n") # extra \n at end f.write("\n") f.close() # open file selection box fsel = ptk.misc_widgets.FileSelectorBox(self.user, _("Save/Append messages to Unix mailbox"), "", _do_it) fsel.set_transient_for(self) fsel.show() def unixbox_import(self, _a=None, _b=None): """ Load all the messages in a unix (~/Mail) mailbox into the current folder. """ folder = self.opened_folder def _do_it(filename): if filename == None: return try: import mailbox f = open(filename, "r") except IOError: ptk.misc_widgets.InfoBox (_("Error"), _("Could not open %s") % filename, gtk.STOCK_OK, parent_win=self) return except ImportError: ptk.misc_widgets.InfoBox (_("Error"), _("Missing Python \"mailbox\" module."), gtk.STOCK_OK, parent_win=self) return msgs = mailbox.UnixMailbox(f) while 1: rfc822msg = msgs.next() if rfc822msg == None: break body = str(rfc822msg) + "\n" + rfc822msg.fp.read() # Make a pyne message from this body msg = pynemsg.pynemsg() msg.body = body msg.parseheaders(self.user) msg.date_received = int(time.time()) if msg.headers.has_key("message-id"): if msg.headers["message-id"] in folder.messages: # don't let duplicates be added continue folder.save_new_article(msg) f.close() folder.changed = 1 self.user.update() # Open file selection box fsel = ptk.misc_widgets.FileSelectorBox(self.user, _("Load messages from a Unix mailbox"), "", _do_it) fsel.set_transient_for(self) fsel.show() def load_from_ascii(self, _a=None, _b=None): """ Load an ascii format message with headers and all that junk and put in the current folder. """ folder = self.opened_folder def _do_it(filename): if filename == None: return try: file = open(filename, "r") body = file.read() file.close() except IOError: ptk.misc_widgets.InfoBox (_("Error"), _("Could not open %s") % filename, gtk.STOCK_OK, parent_win=self) return msg = pynemsg.pynemsg() msg.body = body msg.parseheaders(self.user) msg.date_received = int(time.time()) if msg.headers.has_key("message-id"): if msg.headers["message-id"] in folder.messages: # don't let duplicates be added return folder.save_new_article(msg) folder.changed = 1 self.user.update() # Open file selection box fsel = ptk.misc_widgets.FileSelectorBox(self.user, _("Load ascii message"), "", _do_it) fsel.set_transient_for(self) fsel.show() def save_to_ascii(self, _a=None, _b=None): """ Save currently selected message's body to a file. """ msg_id = self.get_message_list_selected() folder = self.opened_folder if folder==None: pass if msg_id == []: return msg_id = msg_id[0] def _do_it(filename): if filename == None: return msg = folder.load_article(msg_id) try: file = open(filename, "w") except IOError: ptk.misc_widgets.ReturnDialog (_("Error"), _("Could not create %s") % filename, ((gtk.STOCK_OK, 0),), parent_win=self) else: file.write(msg.body) file.close() # Open file selection box fsel = ptk.misc_widgets.FileSelectorBox(self.user, _("Save message as"), "", _do_it) fsel.set_transient_for(self) fsel.show() def view_new_window(self, _a=None, _b=None): """ Open selected message in a new window. """ self.user.new_window(display_msg=(self.opened_folder, self.get_message_list_selected()[0])) def view_browser(self, _a=None, _b=None): """ Open selected message in browser (for crappy html emails) """ user = self.user msg_id = self.get_message_list_selected() folder = self.opened_folder if folder==None: pass if msg_id == []: return msg_id = msg_id[0] msg = folder.load_article(msg_id) # body not downloaded if msg.opts & MSG_NO_BODY: return filename = tempfile.mktemp(".html") file = open(filename, "w") file.write(msg.parts_text[0]) file.close() # remember temporary files for cleanup time :-) # /me kisses pyne.py user.tempfiles.append(filename) user.open_url ("file://"+filename) def view_docs(self, _a=None, _b=None): """ Open documentation in browser. """ self.user.open_url ("file://"+os.path.join(pyne_path, "docs","index.html")) def updateall_mail(self, _a=None, _b=None): """ You clicked 'update' all mailboxes. """ user = self.user pager = ptk.progresswin.ProgressWin(_("Collecting messages..."), self.user, hidetext=_("Show Logs"), parent_win=self) # Update every mailbox def _recurse_update(folder, user=user, pager=pager): # nntpboxes recurse into the newsgroups for us.. if isinstance(folder, nntpbox.newsgroup): return if (not folder.__dict__.has_key("locked")) and (folder.get_flags() & BOX_UPDATE_METHOD): threading.Thread(target=folder.update, args=(user,pager.get_progress_bar())).start() utils.recurse_apply(user.contents, _recurse_update) def update_mail(self, _a=None, _b=None): """ You clicked 'update' mailbox. """ user = self.user folder = self.get_folder_list_selected() if folder == None: return if not folder.__dict__.has_key("locked"): pager = ptk.progresswin.ProgressWin(_("Collecting messages..."), self.user, hidetext=_("Show Logs"), parent_win=self) func = folder.update threading.Thread(target=func, args=(user,pager.get_progress_bar())).start() def addr_book(self, _a=None, _b=None): """ Open the address book. """ user=self.user addr_box = AddressEditBox(user.addressbook, self) def test_thing(_button, addr_box=addr_box): addr_box.destroy() addr_box.ok_button.connect("clicked", test_thing) return addr_box def rot13_message(self, _a=None, _b=None): """ Rot13 the body of the currently displayed message. """ self.msg_view.rot13_redisplay(self.user) def search_message(self, _a=None, _b=None): """ Find instance of a phrase in the currently displayed message. """ self.subwindows.append( self.msg_view.search_message(self.user, self) ) def search_messages(self, _a=None, _b=None): """ Find a message in the current mailbox containing some phrase. """ ptk.message_tree.search_box(self.user, self) def expand_all(self, _a=None, _b=None): self.message_list.expand_all() def collapse_all(self, _a=None, _b=None): self.message_list.collapse_all() def select_prev_msg(self, _a=None, _b=None): self.message_list.select_next(prev=1) def select_next_msg(self, _a=None, _b=None): self.message_list.select_next() def reply_email(self, _button): self.reply_to_message(REPLY_EMAIL) def reply_group(self, _button): self.reply_to_message(REPLY_GROUP) def reply_forward(self, _button): self.reply_to_message(REPLY_FORWARD) def preferences_box (self, _a=None): self.user.set_preferences(self) def setup_uimanager (self): # some junk def _folder_move_up (_a=None): self.move_folder (-1) def _folder_move_down (_a=None): self.move_folder (+1) # UIManager stuff... def _iconset (filename): return gtk.IconSet (gtk.gdk.pixbuf_new_from_file (os.path.join (pyne_path, "icons", filename))) self.iconfuck = gtk.IconFactory () self.iconfuck.add ("icon-updateall", _iconset ("tb_updateall_mail.xpm")) self.iconfuck.add ("icon-updatebox", _iconset ("tb_update_mail.xpm")) self.iconfuck.add ("icon-msgup", _iconset ("tb_msg_up.xpm")) self.iconfuck.add ("icon-msgdown", _iconset ("tb_msg_down.xpm")) self.iconfuck.add ("icon-newmsg", _iconset ("tb_new_message.xpm")) self.iconfuck.add ("icon-editmsg", _iconset ("tb_edit_message.xpm")) self.iconfuck.add ("icon-replymsg", _iconset ("tb_reply_message.xpm")) self.iconfuck.add ("icon-replyall", _iconset ("tb_replyall_message.xpm")) self.iconfuck.add ("icon-fwdmsg", _iconset ("tb_forward_message.xpm")) self.iconfuck.add ("icon-delmsg", _iconset ("tb_delete_message.xpm")) self.iconfuck.add ("icon-shredmsg", _iconset ("tb_shred_message.xpm")) self.iconfuck.add ("icon-printmsg", _iconset ("tb_print_message.xpm")) self.iconfuck.add ("icon-addressbook", _iconset ("tb_address_book.xpm")) self.iconfuck.add_default () self.actgroups = {} self.ui = gtk.UIManager () def _actgroup_new (name, actions): self.actgroups[name] = gtk.ActionGroup (name) self.actgroups[name].add_actions (actions) self.ui.insert_action_group (self.actgroups[name], 0) _actgroup_new ("AlwaysOn", [ ("UpdateAll", "icon-updateall", _("Update All"), None, _("Send and recieve mail for all folders"), self.updateall_mail), ("AddressBook", "icon-addressbook", _("Address Book"), None, _("Open address book."), self.addr_book), ("Preferences", "gtk-preferences", _("Preferences"), None, None, self.preferences_box), ("FileMenu", None, _("_File"), None, None), ("FolderMenu", None, _("_Folder"), None, None), ("FolderNewBranch", None, _("New"), None, None), ("MessageMenu", None, _("_Message"), None, None), ("ViewMenu", None, _("_View"), None, None), ("OptionsMenu", None, _("_Options"), None, None), ("HelpMenu", None, _("_Help"), None, None), ("NewWindow", None, _("_New Window"), "n", None, self.new_window), ("Quit", "gtk-quit", _("_Quit"), "q", None, self.close), ("NewFolder", None, _("New Folder"), None, None, self.new_folder), ("NewMailbox", None, _("New Mailbox"), None, None, self.new_mailbox), ("NewNNTPBox", None, _("New NNTP Box"), None, None, self.new_nntpbox), ("UserGuide", None, _("_User Guide"), None, None, self.view_docs), ("AboutPyne", "gtk-about", _("_About Pyne"), None, None, self.about_box), ]) _actgroup_new ("FolderSelected", [ ("FolderMoveUp", None, _("Move Up"), None, None, _folder_move_up), ("FolderMoveDown", None, _("Move Down"), None, None, _folder_move_down), ("FolderSettings", None, _("Settings"), None, None, self.folder_settings), ("FolderDelete", None, _("Delete"), None, None, self.delete_folder), ]) _actgroup_new ("ImportMsg", [ ("AsciiLoadMsg", None, _("Load Message"), None, "o", self.load_from_ascii), ("ImportUnix", None, _("Import Unix mailbox"), None, None, self.unixbox_import), ]) _actgroup_new ("FolderUpdate", [ ("UpdateBox", "icon-updatebox", _("Update"), None, _("Send or receive mail for current folder only"), self.update_mail) ]) _actgroup_new ("IsMsgFolder", [ ("MsgUp", "icon-msgup", _("Previous Message"), "a", _("Previous Message"), self.select_prev_msg), ("MsgDown", "icon-msgdown", _("Next Message"), "z", _("Next Message"), self.select_next_msg), ("ExportUnix", None, _("Export to Unix mailbox"), None, None, self.unixbox_export), ("ExpandAll", None, _("_Expand All"), "e", None, self.expand_all), ("CollapseAll", None, _("_Collapse All"), "w", None, self.collapse_all), ("SearchMessages", None, _("Search Messages"), None, None, self.search_messages), ]) _actgroup_new ("MsgAny", [ ("MarkMsg", None, _("_Mark Message"), "m", None, self.mark_message), ("UnmarkMsg", None, _("_Unmark Message"), "u", None, self.unmark_message), ]) _actgroup_new ("MsgWithBody", [ ("PrintMsg", "icon-printmsg", _("Print Message"), "p", _("Print the selected message"), self.print_message), ("AsciiSaveMsg", None, _("Save Message As"), "s", None, self.save_to_ascii), ("MsgViewNewWin", None, _("Open in New _Window"), None, None, self.view_new_window), ("MsgViewBrowser", None, _("Open in Browser"), "b", None, self.view_browser), ("MsgROT13", None, _("ROT13 Body"), None, None, self.rot13_message), ("FindInMsg", None, _("Fin_d in Message"), "f", None, self.search_message), ]) _actgroup_new ("MsgWithoutBody", [ ("DownloadBody", None, _("Download _Body"), None, None, self.download_body), ]) _actgroup_new ("MsgNNTP", [ ("DeleteThread", None, _("Delete Thread"), None, None, self.delete_thread), ]) _actgroup_new ("NewMsg", [ ("NewMsg", "icon-newmsg", _("New Message"), None, _("Compose a new message"), self.new_message), ]) _actgroup_new ("EditMsg", [ ("EditMsg", "icon-editmsg", _("Edit Message"), None, _("Edit an outgoint message"), self.edit_message), ]) _actgroup_new ("ReplySingle", [ ("ReplySingle", "icon-replymsg", _("Reply to sender"), None, _("Reply to sender"), self.reply_email), ]) _actgroup_new ("Sender2AddrBook", [ ("Sender2AddrBook", None, _("Add sender to Address Book"), None, None, self.sender2addrbook), ]) _actgroup_new ("ReplyAll", [ ("ReplyAll", "icon-replyall", _("Reply to group"), None, _("Reply to group"), self.reply_group) ]) _actgroup_new ("FwdMsg", [ ("FwdMsg", "icon-fwdmsg", _("Forward message"), None, _("Forward the message"), self.reply_forward) ]) _actgroup_new ("DelMsg", [ ("DelMsg", "icon-delmsg", _("Delete message"), None, _("Move message to deleted folder"), self.delete_message), ("ShredMsg", "icon-shredmsg", _("Shred message"), None, _("Permanently delete the message"), self.shred_message), ]) self.ui.add_ui_from_file (os.path.join (pyne_path, "ui_mainwin.xml")) def make_toolbar(self): """ Make toolbar """ # Toolbar self.toolbar = self.ui.get_widget ("/ToolBar") #self.toolbar.set_style(gtk.TOOLBAR_BOTH) #self.toolbar.set_icon_size (gtk.ICON_SIZE_SMALL_TOOLBAR) #optmenu = gtk.OptionMenu() #menu = gtk.Menu() #menu.show() #for i in ["tom", "dick", "harry"]: # menuitem = gtk.MenuItem(i) # menuitem.set_data("0", i) # #menuitem.connect("activate", _something) # menu.add(menuitem) # menuitem.show() #optmenu.set_menu(menu) #self.toolbar.append_widget(optmenu, "Default personality", "something private") #optmenu.show() self.box1.pack_start(self.toolbar, expand=False) self.toolbar.show() def make_menubar(self): """ Construct the menu bar. """ m = self.ui.get_widget ("/MenuBar") self.add_accel_group (self.ui.get_accel_group ()) self.box1.pack_start (m, expand=False) def _toggle_filter(_w, num): if not self.filter_msgview & (0x1 << num): self.filter_msgview |= 0x1 << num else: self.filter_msgview &= ~ (0x1 << num) self.message_list.update(self.opened_folder, self.filter_msgview) # view menu menuitem = self.ui.get_widget ("/MenuBar/View") menuitem.remove_submenu () menu = gtk.Menu () menuitem.set_submenu (menu) view_filters = ["Hide read", "Hide unread", "Hide partial", "Hide full", "Hide unmarked", "Hide marked" ] for i in xrange(0, len(view_filters)): menuitem = gtk.CheckMenuItem(view_filters[i]) menuitem.connect("toggled", _toggle_filter, i) menu.append(menuitem) menuitem.show() menu.show() m.show () def open_message(self, iter, msg_id, mark_as_read=1, new_window=0): """ View this message. """ folder = self.message_list.cur_object # Mark as read msg = folder.load_article(msg_id) if mark_as_read and (not msg.opts & MSG_ISREAD) and (not msg.opts & MSG_NO_BODY): msg.opts = msg.opts | MSG_ISREAD if folder.__dict__.has_key ("num_unread"): folder.num_unread -= 1 self.user.update (UPDATE_FOLDERONLY) folder.save_article(msg) # Update UI state self.update_ui(folder, msg) # change icon as appropriate self.message_list.update_node(self.user, msg.opts, iter=iter) # if the object contains messages show message if new_window == 0: # dump message to view pane self.msg_view.dump_msg(self.user, msg, folder) else: if msg.senduid != None: # outgoing message: open edit box msg.edit(folder, self.user) else: # else: open new window displaying message self.user.new_window(display_msg=(folder,msg_id)) def update_settings (self): """ User settings have changed. Must update some ui thingies. """ self.msg_view.set_tab_pos(self.user.tab_pos) self.msg_view.update_settings () self.update_quickview_visible () if self.ui_style != self.user.ui_style: self.ui_style = self.user.ui_style self.remove (self.get_child ()) self.build_ui () self.update(self.user, UPDATE_ALL) def update_quickview_visible (self): if self.display_msg != None: self.msg_view_show () if isinstance (self.opened_folder, boxtypes.nntpbox.newsgroup): if self.user.opts & OPT_NO_QUICKVIEW_NNTP: self.msg_view.hide () else: self.msg_view.show () else: if self.user.opts & OPT_NO_QUICKVIEW_MAIL: self.msg_view.hide () else: self.msg_view.show () def make_panes(self): """ Make panes like so: ################################# # # # Message List # # # ################################# # # # # Mailboxes # Preview Pane # # # # ################################# """ def click_folder_list(w, event, ctree, self=self): user = self.user # Update message list mousebutton = event.button # Right mouse button brings popup menu if mousebutton == 3: self.folder_list_popup_box(event) return # if the object contains messages show them in # message list pane object = self.user.get_folder_by_uid(self.folder_list.user_selected) if object == None: # probably clicked on the root node return if object.__dict__.has_key("messages"): # mark this as the open folder. whoohoo! :-) # note that subfolders need the uid of # their parents. self.opened_folder = object self.update_quickview_visible () self.message_list.update(object, self.filter_msgview) msg_ids = self.get_message_list_selected() if not msg_ids: msg = None else: msg = object.load_article(msg_ids[0]) self.update_ui(object, msg) else: self.update_ui(object, None) # Dump clicked on message to the preview window def click_message_list(msg_tree, event, msg_id, iter, self=self): """ iter is the GtkTreeIter object for the message clicked on. """ mousebutton = event.button # Right mouse button brings popup menu if mousebutton == 3: self.message_list_popup_box(event) return folder = msg_tree.cur_object # mark as read if needed if msg_id != None: # Mark as read mark_as_read = 0 if not ((self.user.opts & OPT_2CLICK_MARK) and (event.type == gtk.gdk.BUTTON_PRESS)): mark_as_read = 1 if event.type == gtk.gdk._2BUTTON_PRESS: new_window = 1 else: new_window = 0 self.open_message(iter, msg_id, mark_as_read, new_window) # 'None' article else: self.msg_view.clear() self.update_ui(folder, None) def keypress_message_list(msg_tree, event, msg_id, self=self): user = self.user folder = msg_tree.cur_object # Open on hitting return. mark as read if needed if msg_id != None and event.string == '\r': # Mark as read msg = folder.load_article(msg_id) if not ((user.opts & OPT_2CLICK_MARK) and (event.type == gtk.gdk.BUTTON_PRESS)): if (not msg.opts & MSG_ISREAD) and (not msg.opts & MSG_NO_BODY): msg.opts = msg.opts | MSG_ISREAD folder.save_article(msg) # Update UI state self.update_ui(folder, msg) # change icon as appropriate msg_tree.update_node(user, msg.opts, msg_id=msg_id) # if the object contains messages show message self.msg_view.dump_msg(user, msg, folder) self.message_list.connect_key_press_event(keypress_message_list) self.message_list.connect_button_press_event(click_message_list) self.folder_list.connect_button_press_event(click_folder_list) self.msg_view = ptk.msg_view_box.msg_view_box(self.user, self) if self.display_msg != None: # Simply mainwin showing only quick view pane self.box1.pack_start(self.msg_view) self.msg_view.show() # Dump requested message to msg_view msg = self.display_msg[0].load_article(self.display_msg[1]) self.msg_view.dump_msg(self.user, msg, self.display_msg[0]) else: # default multi-pane setup mpwin = gtk.ScrolledWindow() mpwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) mpwin.add(self.message_list) mpwin.show() self.message_list.show() fpwin = gtk.ScrolledWindow() fpwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) fpwin.add(self.folder_list) fpwin.show() self.folder_list.show() if not (self.user.opts & OPT_NO_QUICKVIEW_MAIL): self.msg_view.show() # tabbed UI style if self.user.ui_style == UI_TABBED: # tabbed UI style self.notebook = gtk.Notebook() self.notebook.set_tab_pos(gtk.POS_TOP) self.box1.pack_start(self.notebook) self.notebook.show() label = gtk.Label(_("Folders")) self.notebook.append_page(fpwin, label) label = gtk.Label(_("Messages")) self.notebook.append_page(mpwin, label) label = gtk.Label(_("View")) self.notebook.append_page(self.msg_view, label) else: # Vertical pane self.vpaned = gtk.VPaned() self.box1.pack_start(self.vpaned) self.vpaned.set_border_width(2) self.vpaned.show() # Horizontal pane self.hpaned = gtk.HPaned() # pane layouts if self.user.ui_style == UI_CLASSIC: self.vpaned.add1(self.hpaned) self.vpaned.add2(self.msg_view) self.hpaned.add1(fpwin) self.hpaned.add2(mpwin) else: self.vpaned.add1(mpwin) self.vpaned.add2(self.hpaned) self.hpaned.add1(fpwin) self.hpaned.add2(self.msg_view) self.hpaned.show() # size panes self.vpaned.set_position(self.user.window_setup[self.number][VPANE_POS]) self.hpaned.set_position(self.user.window_setup[self.number][HPANE_POS]) def keypress (self, win, event): # Alt-1,2,3 change tabs in tabbed view. if (event.state & gtk.gdk.MOD1_MASK): if self.user.ui_style == UI_TABBED: if event.keyval == ord('1'): self.notebook.set_current_page(0) elif event.keyval == ord('2'): self.notebook.set_current_page(1) elif event.keyval == ord('3'): self.notebook.set_current_page(2) def __init__(self, user, title, size, number, display_msg=None): """ Main window initialisation. size: (width, height, hpane pos) """ # Parent user and window number self.user = user self.number = number self.display_msg = display_msg self.subwindows = [] def resize(win, event, self=self, user=user): # Store mainwindow size size = (event.width, event.height, -1, -1) user.window_setup[self.number] = size #create main window gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) self.set_default_size(size[0], size[1]) self.connect("key-press-event", self.keypress) self.connect("delete_event", self.close) self.connect("configure_event", resize) self.set_title(title) # set icon i = gtk.Image() i.set_from_file(os.path.join (pyne_path,"icons","pyne.xpm")) self.set_icon(i.get_pixbuf()) self.pane_sizes = size self.ui_style = user.ui_style self.build_ui () def build_ui (self): # Box to contain the shit in the main window self.box1 = gtk.VBox() self.add(self.box1) self.box1.show() # Filter out of message view self.filter_msgview = 0 # Defined early 'cause menubar/toolbar needs them self.folder_list = ptk.folder_tree.folder_tree(self.user) # Setup DND def dnd_drag_data_received(w, context, x, y, data, info, time, self=self): # get target ('pyne_msgid' or 'pyne_boxid') content_type = str(context.targets[0]) user = self.user to_folder = self.get_folder_list_selected() self.update_ui(to_folder, None) if content_type == "pyne_msgid": if to_folder == None: return #if data and data.format == 8 and to_folder != None: from_folder = user.get_folder_by_uid(string.split(data.data, "/")[0]) if from_folder.uid == to_folder.uid: return # evaluate to list of message ids msg_ids = eval( string.join( string.split(data.data, "/")[1:], "/" ) ) for msg_id in msg_ids: # don't bother if it's there already if msg_id in to_folder.messages: continue msg = from_folder.load_article(msg_id) # check we are moving OUTGOING messages to the outbox if isinstance(to_folder, outbox.outbox): if msg.senduid == None: # moving non-outgoing message to outbox. # make it a reply. to_folder.do_reply(user, msg, from_folder, type=REPLY_AUTO, parent_win=self) continue to_folder.save_new_article(msg) # copy not move if from newsgroup if not isinstance(from_folder, nntpbox.newsgroup): from_folder.delete_article(msg_id) from_folder.changed = 1 to_folder.changed = 1 user.update() elif content_type == "pyne_boxid": from_folder = user.get_folder_by_uid(data.data) # illegal from folders. we only want to move # mailboxes at the moment. if not isinstance(from_folder, mailbox.mailbox) and \ not isinstance(from_folder, storebox.storebox): ptk.misc_widgets.ReturnDialog (_("Error"), _("You can only move mailboxes into\nother mailboxes."), ((gtk.STOCK_CANCEL, 0),), parent_win=self) return # make sure we are not moving into ourself or a child box def _is_child(folder, to_folder=to_folder): if folder is to_folder: return 1 if len(utils.recurse_apply([from_folder], _is_child)) > 0: ptk.misc_widgets.ReturnDialog (_("Error"), _("You cannot move folders into\nthemselves or their sub-folders."), ((gtk.STOCK_CANCEL, 0),), parent_win=self) return if from_folder == to_folder: return if to_folder == None: # copying to root node (user.contents) to_folder = user # find parent of from_folder and remove it from # there parent_object = user.parent_of(from_folder) parent_object.contents.remove(from_folder) # append to to_folder contents to_folder.contents.append(from_folder) # save changes user.save() # big update of folderview, since its structure has changed user.update(UPDATE_ALL) def dnd_drag_motion(w, context, x, y, time, self=self): # get target ('pyne_msgid' or 'pyne_boxid') content_type = str(context.targets[0]) # For some reason the last entry in the TreeView # cannot be selected when dragged over :-\ XXX XXX XXX folder_list = self.folder_list y = y-20 # hack to make TreeView DND selection not look # too sloppy... see XXX thingy path = folder_list.get_path_at_pos(x, y) if path == None: return # Get folder iter = folder_list.get_model().get_iter(path[0]) uid = folder_list.get_model().get_value(iter, 2) folder = self.user.get_folder_by_uid(uid) if content_type == "pyne_msgid": # only allow sensible folders :-] if isinstance(folder, mailbox.mailbox) or \ isinstance(folder, storebox.storebox) or \ isinstance(folder, outbox.outbox): # gtk selectalise it :-. folder_list.get_selection().select_iter(iter) elif content_type == "pyne_boxid": # only allow moving to the root node # and into mailboxes. if folder==None or \ isinstance(folder, mailbox.mailbox) or \ isinstance(folder, storebox.storebox): # folder==None indicates root node folder_list.get_selection().select_iter(iter) targets = [ ("pyne_msgid", 0, 0), ("pyne_boxid", 0, -1) ] self.folder_list.connect("drag_motion", dnd_drag_motion) self.folder_list.connect("drag_data_received", dnd_drag_data_received) self.folder_list.drag_dest_set(gtk.DEST_DEFAULT_ALL, targets, gtk.gdk.ACTION_MOVE) self.message_list = ptk.message_tree.message_tree(self, self.pane_sizes[0]) # the uid of the current open folder (not necessarily the # selected one. just the one showing messages) if self.display_msg == None: self.opened_folder = None else: self.opened_folder = self.display_msg[0] self.setup_uimanager () self.make_menubar() self.make_toolbar() self.make_panes() # Set default method states if self.display_msg != None: folder, msg_id = self.display_msg self.update_ui(folder, folder.load_article(msg_id)) else: self.update_ui(None, None) self.show() def update(self, user, update_type): """ Fill folder_list with items from user's contents. """ if self.display_msg != None: # no visible folder or message lists return # update folder and message list if update_type == UPDATE_ALL: self.message_list.update(self.opened_folder, self.filter_msgview, clear=1) self.folder_list.update(user, update_all=1) elif update_type == UPDATE_FOLDERONLY: self.folder_list.update(user, update_all=1) else: self.message_list.update(self.opened_folder, self.filter_msgview) self.folder_list.update(user)