#
# 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 <soul-less@blueyonder.co.uk>\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"), "<Control>n", None, self.new_window),
("Quit", "gtk-quit", _("_Quit"), "<Control>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, "<Control>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"), "<Control>a", _("Previous Message"), self.select_prev_msg),
("MsgDown", "icon-msgdown", _("Next Message"), "<Control>z", _("Next Message"), self.select_next_msg),
("ExportUnix", None, _("Export to Unix mailbox"), None, None, self.unixbox_export),
("ExpandAll", None, _("_Expand All"), "<Control>e", None, self.expand_all),
("CollapseAll", None, _("_Collapse All"), "<Control>w", None, self.collapse_all),
("SearchMessages", None, _("Search Messages"), None, None, self.search_messages),
])
_actgroup_new ("MsgAny", [
("MarkMsg", None, _("_Mark Message"), "<Control>m", None, self.mark_message),
("UnmarkMsg", None, _("_Unmark Message"), "<Control>u", None, self.unmark_message),
])
_actgroup_new ("MsgWithBody", [
("PrintMsg", "icon-printmsg", _("Print Message"), "<Control>p", _("Print the selected message"), self.print_message),
("AsciiSaveMsg", None, _("Save Message As"), "<Control>s", None, self.save_to_ascii),
("MsgViewNewWin", None, _("Open in New _Window"), None, None, self.view_new_window),
("MsgViewBrowser", None, _("Open in Browser"), "<Control>b", None, self.view_browser),
("MsgROT13", None, _("ROT13 Body"), None, None, self.rot13_message),
("FindInMsg", None, _("Fin_d in Message"), "<Control>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)
syntax highlighted by Code2HTML, v. 0.9.1