Files
gajim-plugins/file_sharing/fileshare_window.py
2012-10-29 15:04:48 +01:00

400 lines
17 KiB
Python

import gtk
import gobject
from common import gajim
from common import helpers
from common.file_props import FilesProp
import fshare
import os
import fshare_protocol
class FileShareWindow(gtk.Window):
def __init__(self, plugin):
self.plugin = plugin
gtk.Window.__init__(self)
self.set_title('File Share')
self.connect('delete_event', self.delete_event)
self.set_position(gtk.WIN_POS_CENTER)
self.set_default_size(400, 400)
# Children
self.notebook = gtk.Notebook()
# Browse page
self.bt_search = gtk.Button('Search')
self.bt_search.connect('clicked', self.on_bt_search_clicked)
self.entry_search = gtk.Entry(max=0)
self.entry_search.set_size_request(300, -1)
self.exp_advance = gtk.Expander('Advance')
self.ts_search = gtk.TreeStore(gobject.TYPE_STRING)
self.tv_search = gtk.TreeView(self.ts_search)
self.tv_search.connect('row-expanded', self.row_expanded)
self.tv_search.connect('button-press-event',
self.on_treeview_button_press_event)
self.browse_popup = gtk.Menu()
mi_download = gtk.MenuItem('Download')
mi_property = gtk.MenuItem('Property')
mi_download.show()
mi_property.show()
mi_download.connect('activate', self.on_download_clicked)
self.browse_popup.append(mi_download)
self.browse_popup.append(mi_property)
self.browse_sw = gtk.ScrolledWindow()
self.browse_sw.add(self.tv_search)
self.tvcolumn_browse = gtk.TreeViewColumn('')
self.tv_search.append_column(self.tvcolumn_browse)
self.cell_browse = gtk.CellRendererText()
self.tvcolumn_browse.pack_start(self.cell_browse, True)
self.tvcolumn_browse.add_attribute(self.cell_browse, 'text', 0)
self.lbl_browse = gtk.Label('Browse')
self.browse_hbox = gtk.HBox()
self.browse_hbox2 = gtk.HBox()
self.browse_vbox = gtk.VBox()
self.browse_hbox.pack_start(self.entry_search, True, True, 10)
self.browse_hbox.pack_start(self.bt_search, False, False, 10)
self.browse_vbox.pack_start(self.browse_hbox, False, False, 10)
self.browse_vbox.pack_start(self.exp_advance, False, False, 10)
self.browse_vbox.pack_start(self.browse_sw, True, True, 10)
self.notebook.append_page(self.browse_vbox, self.lbl_browse)
# file references for tv_search
self.browse_fref = {}
# contact references for tv_search
self.browse_jid = {}
# Information of the files inserted in the treeview: name, size, etc
self.brw_file_info = {}
# dummy row children so that we can get expanders
self.empty_row_child = {}
# Manage page
self.bt_add_file = gtk.Button('Add file')
self.bt_add_file.connect('clicked', self.add_file)
self.bt_add_dir = gtk.Button('Add directory')
self.bt_add_dir.connect('clicked', self.add_directory)
self.bt_remove = gtk.Button('Remove')
self.bt_remove.connect('clicked', self.remove_file_clicked)
self.bt_remove_all = gtk.Button('Remove all')
self.bt_remove_all.connect('clicked', self.remove_all_clicked)
self.ts_contacts = gtk.TreeStore(gobject.TYPE_STRING)
self.cbb_contacts = gtk.ComboBoxEntry(self.ts_contacts)
self.cbb_contacts.connect('changed', self.__check_combo_edit)
cbb_entry = self.cbb_contacts.child
self.cbb_completion = gtk.EntryCompletion()
cbb_entry.set_completion(self.cbb_completion)
self.cbb_completion.set_model(self.ts_contacts)
self.cbb_completion.set_text_column(0)
self.bt_stophash = gtk.Button('Calculating hash')
self.lbl_manage = gtk.Label('Manage Shared Files')
self.ts_files = gtk.TreeStore(gobject.TYPE_STRING)
self.tv_files = gtk.TreeView(self.ts_files)
self.treeSelection_files = self.tv_files.get_selection()
self.treeSelection_files.connect('changed', self.row_selected)
self.manage_sw = gtk.ScrolledWindow()
self.manage_sw.add(self.tv_files)
self.tvcolumn = gtk.TreeViewColumn('')
self.tv_files.append_column(self.tvcolumn)
self.cell = gtk.CellRendererText()
self.tvcolumn.pack_start(self.cell, True)
self.tvcolumn.add_attribute(self.cell, 'text', 0)
self.pb_filehash = gtk.ProgressBar()
self.manage_hbox = gtk.HBox()
self.manage_hbox2 = gtk.HBox()
self.manage_vbox = gtk.VBox()
self.manage_vbox2 = gtk.VBox()
self.manage_hbox.pack_start(self.bt_stophash , False, False, 10)
self.manage_hbox.pack_start(self.pb_filehash, True, True, 10)
self.manage_hbox.set_sensitive(False)
self.manage_vbox.pack_start(self.cbb_contacts, False, False, 10)
self.manage_vbox.pack_start(self.manage_hbox, False, False, 10)
self.manage_hbox2.pack_start(self.manage_sw, True, True, 10)
self.manage_hbox2.pack_start(self.manage_vbox2, False, False, 10)
self.manage_vbox2.pack_start(self.bt_add_file, False, False, 10)
self.manage_vbox2.pack_start(self.bt_add_dir, False, False, 10)
self.manage_vbox2.pack_start(self.bt_remove, False, False, 10)
self.manage_vbox2.pack_start(self.bt_remove_all, False, False, 10)
self.manage_vbox2.set_sensitive(False)
self.manage_vbox.pack_start(self.manage_hbox2, True, True, 10)
self.notebook.append_page(self.manage_vbox, self.lbl_manage)
# Preferences page
self.lbl_pref = gtk.Label('Preferences')
self.entry_dir_pref = gtk.Entry(max=0)
self.entry_dir_pref.set_text(self.plugin.config['incoming_dir'])
self.bt_sel_dir_pref = gtk.Button('Select dir', gtk.STOCK_OPEN)
self.bt_sel_dir_pref.connect('clicked', self.on_bt_sel_dir_pref_clicked)
self.frm_dir_pref = gtk.Frame('Incoming files directory')
self.frm_dir_pref.set_shadow_type(gtk.SHADOW_IN)
self.pref_hbox = gtk.HBox()
self.pref_vbox = gtk.VBox()
self.pref_hbox.pack_start(self.entry_dir_pref, True, True, 10)
self.pref_hbox.pack_start(self.bt_sel_dir_pref, False, False, 10)
self.frm_dir_pref.add(self.pref_hbox)
self.pref_vbox.pack_start(self.frm_dir_pref, False, False, 10)
self.notebook.append_page(self.pref_vbox , self.lbl_pref)
self.add(self.notebook)
self.show_all()
def set_account(self, account):
self.account = account
# connect window to protocol
pro = fshare.FileSharePlugin.prohandler[self.account]
pro.set_window(self)
def add_file(self, widget):
dialog = gtk.FileChooserDialog('Add file to be shared', self,
gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
gtk.RESPONSE_OK)
)
dialog.set_select_multiple(True)
response = dialog.run()
if response == gtk.RESPONSE_OK:
file_list = dialog.get_filenames()
self.add_items_tvcontacts(file_list)
dialog.destroy()
def add_directory(self, widget):
dialog = gtk.FileChooserDialog('Add directory to be shared', self,
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
gtk.RESPONSE_ACCEPT)
)
response = dialog.run()
if response == gtk.RESPONSE_ACCEPT:
file_list = []
file_list.append(dialog.get_filename())
self.add_items_tvcontacts(file_list)
dialog.destroy()
def add_file_list(self, flist, treestore, fref = {}, root = None):
# file references to their in the treeStore
for f in flist:
# keeps track of the relative dir path
tail = ''
# keeps track of the parent row
parent = None
dirpath = f.split('/')
if len(dirpath) == 1:
# Top level file, it doesnt have parent, add it right away
fref[dirpath[0]] = treestore.insert(root, 0, (dirpath[0],))
else:
for dir_ in dirpath:
if tail + dir_ not in fref:
fref[tail + dir_] = treestore.append(parent, (dir_,))
parent = fref[tail + dir_]
tail = tail + dir_ + '/'
return fref
def __convert_date(self, epoch):
# Converts date-time from seconds from epoch to iso 8601
import time, datetime
ts = time.gmtime(epoch)
dt = datetime.datetime(ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour,
ts.tm_min, ts.tm_sec)
return dt.isoformat()
def add_items_tvcontacts(self, file_list, parentdir = None, parent = None):
# TODO: execute this method inside of a thread
for f in file_list:
# Relative name to be used internally in the shared folders
relative_name = f.split('/')[-1]
if parentdir:
relative_name = parentdir + '/' + relative_name
short_name = relative_name.split('/')[-1]
if short_name[0] == '.':
# Return if it is a hidden file
return
if parent:
row = self.ts_files.append(parent, (short_name,))
else:
row = self.ts_files.insert(None, 0, (short_name,))
# File info
size = os.path.getsize(f)
is_dir = os.path.isdir(f)
mod_date = os.path.getmtime(f)
mod_date = self.__convert_date(mod_date)
# TODO: add hash
file_ = (f, relative_name, '', size, '', mod_date, is_dir)
requester = self.cbb_contacts.get_active_text()
try:
fid = self.plugin.database.add_file(self.account, requester,
file_)
except Exception, e:
if e == 'Duplicated entry':
print 'Error: ' + e
continue
else:
raise Exception(e)
if is_dir:
tmpfl = os.listdir(f)
fl = []
for item in tmpfl:
fl.append(f + '/' + item)
self.add_items_tvcontacts(fl, relative_name, row)
def add_contact_browse(self, contact):
fjid = contact.get_full_jid()
jid = gajim.get_jid_without_resource(fjid)
contacts = gajim.contacts.get_contacts(self.account, jid)
for con in contacts:
if con.show in ('offline', 'error') and not \
con.supports(fshare_protocol.NS_FILE_SHARING):
break
cjid = con.get_full_jid()
r = self.ts_search.insert(None, 0, (cjid, ))
self.browse_jid[cjid] = r
pro = fshare.FileSharePlugin.prohandler[self.account]
# Request list of files from peer
stanza = pro.request(cjid)
if pro.conn.connection:
pro.conn.connection.send(stanza)
def add_contact_manage(self, contact):
self.contacts_rows = []
self.cbb_contacts.grab_focus()
for c in gajim.contacts.iter_contacts(self.account):
jid = gajim.get_jid_without_resource(c.get_full_jid())
r = self.ts_contacts.insert(None, len(self.ts_contacts), (jid, ))
if c.get_full_jid() == contact.get_full_jid():
self.cbb_contacts.set_active_iter(r)
self.contacts_rows.append(r)
self.manage_vbox2.set_sensitive(True)
self.bt_remove.set_sensitive(False)
self.add_file_list(self.plugin.database.get_files_name(self.account,
gajim.get_jid_without_resource(contact.get_full_jid())),
self.ts_files)
def delete_event(self, widget, data=None):
fshare.FileSharePlugin.filesharewindow = {}
return False
def __check_combo_edit(self, widget, data=None):
self.ts_files.clear()
entry = self.cbb_contacts.child
contact = entry.get_text()
self.manage_vbox2.set_sensitive(False)
for i in self.contacts_rows:
# If the contact in the comboboxentry is include inside of the
# combobox
if contact == self.ts_contacts.get_value(i, 0):
self.add_file_list(self.plugin.database.get_files_name(
self.account, contact), self.ts_files)
self.manage_vbox2.set_sensitive(True)
self.bt_remove.set_sensitive(False)
break
def remove_file_clicked(self, widget, data=None):
entry = self.cbb_contacts.child
contact = entry.get_text()
sel = self.treeSelection_files.get_selected()
relative_name = self.ts_files.get_value(sel[1], 0)
self.ts_files.remove(sel[1])
self.plugin.database.delete(self.account, contact, relative_name)
widget.set_sensitive(False)
def remove_all_clicked(self, widget, data=None):
entry = self.cbb_contacts.child
contact = entry.get_text()
self.plugin.database.delete_all(self.account, contact)
self.ts_files.clear()
def row_selected(self, widget, data=None):
# When row is selected in tv_files
sel = self.treeSelection_files.get_selected()
if not sel[1]:
return
depth = self.ts_files.iter_depth(sel[1])
# Don't remove file and dirs that aren't at the root level
if depth == 0:
self.bt_remove.set_sensitive(True)
else:
self.bt_remove.set_sensitive(False)
def row_expanded(self, widget, iter_, path, data=None):
name = None
for key in self.empty_row_child:
parent = self.ts_search.iter_parent(self.empty_row_child[key])
p = self.ts_search.get_path(parent)
if p == path:
name = key
break
if name:
# if we found that the expanded row is the parent of the empty row
# remove it from the treestore and empty_row dictionary. Then ask
# peer for list of files of that directory
i = self.empty_row_child[name]
pro = fshare.FileSharePlugin.prohandler[self.account]
contact = self.get_contact_from_iter(self.ts_search, i)
contact = gajim.contacts.get_contact_with_highest_priority(
self.account, contact)
stanza = pro.request(contact.get_full_jid(), name, isFile=False)
if pro.conn.connection:
pro.conn.connection.send(stanza)
self.ts_search.remove(i)
del self.empty_row_child[name]
def get_contact_from_iter(self, treestore, iter_):
toplevel = treestore.get_iter_root()
while toplevel:
if treestore.is_ancestor(toplevel, iter_):
return gajim.get_jid_without_resource(treestore.get_value(
toplevel, 0))
toplevel = treestore.iter_next(toplevel)
def on_treeview_button_press_event(self, treeview, event):
if event.button == 3:
x = int(event.x)
y = int(event.y)
time = event.time
pthinfo = treeview.get_path_at_pos(x, y)
if pthinfo is not None:
path, col, cellx, celly = pthinfo
treeview.grab_focus()
treeview.set_cursor( path, col, 0)
treestore = treeview.get_model()
it = treestore.get_iter(path)
if treestore.iter_depth(it) != 0:
self.browse_popup.popup(None, None, None, event.button, time)
return True
def on_bt_search_clicked(self, widget, data=None):
pass
def on_download_clicked(self, widget, data=None):
tree, row = self.tv_search.get_selection().get_selected()
path = tree.get_path(row)
file_info = self.brw_file_info[path]
fjid = self.get_contact_from_iter(tree, row)
# Request the file
file_path = os.path.join(self.plugin.config['incoming_dir'],
file_info[0])
sid = helpers.get_random_string_16()
new_file_props = FilesProp.getNewFileProp(self.account, sid)
new_file_props.file_name = file_path
print file_path
new_file_props.name = file_info[0]
new_file_props.desc = file_info[4]
new_file_props.size = file_info[2]
new_file_props.date = file_info[1]
new_file_props.hash_ = None if file_info[3] == '' else file_info[3]
new_file_props.type_ = 'r'
tsid = gajim.connections[self.account].start_file_transfer(fjid,
new_file_props, True)
new_file_props.transport_sid = tsid
ft_window = gajim.interface.instances['file_transfers']
contact = gajim.contacts.get_contact_from_full_jid(self.account, fjid)
ft_window .add_transfer(self.account, contact, new_file_props)
def on_bt_sel_dir_pref_clicked(self, widget, data=None):
chooser = gtk.FileChooserDialog(title='Incoming files directory',
action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
gtk.RESPONSE_OK))
response = chooser.run()
if response == gtk.RESPONSE_OK:
file_name = chooser.get_filename()
self.entry_dir_pref.set_text(file_name)
self.plugin.config['incoming_dir'] = file_name
chooser.destroy()
if __name__ == "__main__":
f = FileShareWindow(None)
f.show()
gtk.main()