| | 1386 | |
| | 1387 | class FileTransfersWindow: |
| | 1388 | def __init__(self, plugin): |
| | 1389 | self.files_props = {'r':{},'s':{}} |
| | 1390 | self.plugin = plugin |
| | 1391 | self.last_save_dir = None |
| | 1392 | self.xml = gtk.glade.XML(GTKGUI_GLADE, 'file_transfers_window', APP) |
| | 1393 | self.window = self.xml.get_widget('file_transfers_window') |
| | 1394 | self.tree = self.xml.get_widget('transfers_list') |
| | 1395 | self.stop_button = self.xml.get_widget('stop_button') |
| | 1396 | self.pause_button = self.xml.get_widget('pause_restore_button') |
| | 1397 | self.notify_ft_checkbox = \ |
| | 1398 | self.xml.get_widget('notify_ft_complete_checkbox') |
| | 1399 | notify = gajim.config.get('notify_on_file_complete') |
| | 1400 | if notify: |
| | 1401 | self.notify_ft_checkbox.set_active(1) |
| | 1402 | else: |
| | 1403 | self.notify_ft_checkbox.set_active(0) |
| | 1404 | self.model = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str, str, str) |
| | 1405 | self.tree.set_model(self.model) |
| | 1406 | col = gtk.TreeViewColumn() |
| | 1407 | |
| | 1408 | render_pixbuf = gtk.CellRendererPixbuf() |
| | 1409 | |
| | 1410 | col.pack_start(render_pixbuf, expand = True) |
| | 1411 | render_pixbuf.set_property('xpad', 3) |
| | 1412 | render_pixbuf.set_property('ypad', 3) |
| | 1413 | render_pixbuf.set_property('yalign', .0) |
| | 1414 | #~ render_pixbuf.set_property('stock-size', gtk.ICON_SIZE_MENU) |
| | 1415 | col.add_attribute(render_pixbuf, "pixbuf", 0) |
| | 1416 | self.tree.append_column(col) |
| | 1417 | |
| | 1418 | col = gtk.TreeViewColumn(_('File')) |
| | 1419 | renderer = gtk.CellRendererText() |
| | 1420 | col.pack_start(renderer, expand=False) |
| | 1421 | col.add_attribute(renderer, 'markup' , 1) |
| | 1422 | renderer.set_property('yalign', 0.) |
| | 1423 | renderer = gtk.CellRendererText() |
| | 1424 | col.pack_start(renderer, expand=True) |
| | 1425 | col.add_attribute(renderer, 'markup' , 2) |
| | 1426 | renderer.set_property('xalign', 0.) |
| | 1427 | renderer.set_property('yalign', 0.) |
| | 1428 | col.set_expand(True) |
| | 1429 | self.tree.append_column(col) |
| | 1430 | |
| | 1431 | col = gtk.TreeViewColumn(_('Progress')) |
| | 1432 | renderer = gtk.CellRendererText() |
| | 1433 | renderer.set_property('yalign', 0.) |
| | 1434 | renderer.set_property('xalign', 0.) |
| | 1435 | col.pack_start(renderer, expand = True) |
| | 1436 | col.set_expand(False) |
| | 1437 | col.add_attribute(renderer, 'text' , 3) |
| | 1438 | self.tree.append_column(col) |
| | 1439 | self.set_images() |
| | 1440 | self.tree.get_selection().set_select_function(self.select_func) |
| | 1441 | self.xml.signal_autoconnect(self) |
| | 1442 | |
| | 1443 | |
| | 1444 | |
| | 1445 | def show_file_request(self, account, contact, file_props): |
| | 1446 | if file_props is None or not file_props.has_key('name'): |
| | 1447 | return |
| | 1448 | sec_text = '\t' + _('File: %s') % file_props['name'] |
| | 1449 | if file_props.has_key('size'): |
| | 1450 | sec_text += '\n\t' + _('Size: %s') % \ |
| | 1451 | gtkgui_helpers.convert_bytes(file_props['size']) |
| | 1452 | if file_props.has_key('mime-type'): |
| | 1453 | sec_text += '\n\t' + _('Type: %s') % file_props['mime-type'] |
| | 1454 | if file_props.has_key('desc'): |
| | 1455 | sec_text += '\n\t' + _('Description: %s') % file_props['desc'] |
| | 1456 | prim_text = _(' %s wants to send you file') % contact.jid |
| | 1457 | dialog = ConfirmationDialog(prim_text, sec_text) |
| | 1458 | if dialog.get_response() == gtk.RESPONSE_OK: |
| | 1459 | dialog = gtk.FileChooserDialog(title=_('Save File As...'), |
| | 1460 | action=gtk.FILE_CHOOSER_ACTION_SAVE, |
| | 1461 | buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, |
| | 1462 | gtk.STOCK_SAVE, gtk.RESPONSE_OK)) |
| | 1463 | dialog.set_current_name(file_props['name']) |
| | 1464 | dialog.set_default_response(gtk.RESPONSE_OK) |
| | 1465 | if self.last_save_dir and os.path.exists(self.last_save_dir) \ |
| | 1466 | and os.path.isdir(self.last_save_dir): |
| | 1467 | dialog.set_current_folder(self.last_save_dir) |
| | 1468 | response = dialog.run() |
| | 1469 | if response == gtk.RESPONSE_OK: |
| | 1470 | file_path = dialog.get_filename() |
| | 1471 | (file_dir, file_name) = os.path.split(file_path) |
| | 1472 | if file_dir: |
| | 1473 | self.last_save_dir = file_dir |
| | 1474 | file_props['file-name'] = file_path |
| | 1475 | self.add_transfer(account, contact, file_props) |
| | 1476 | gajim.connections[account].send_file_approval(file_props) |
| | 1477 | else: |
| | 1478 | gajim.connections[account].send_file_rejection(file_props) |
| | 1479 | dialog.destroy() |
| | 1480 | else: |
| | 1481 | gajim.connections[account].send_file_rejection(file_props) |
| | 1482 | |
| | 1483 | def set_images(self): |
| | 1484 | self.images = {} |
| | 1485 | self.images['upload'] = self.window.render_icon(gtk.STOCK_GO_UP, |
| | 1486 | gtk.ICON_SIZE_MENU) |
| | 1487 | self.images['download'] = self.window.render_icon(gtk.STOCK_GO_DOWN, |
| | 1488 | gtk.ICON_SIZE_MENU) |
| | 1489 | self.images['stop'] = self.window.render_icon(gtk.STOCK_STOP, |
| | 1490 | gtk.ICON_SIZE_MENU) |
| | 1491 | self.images['waiting'] = self.window.render_icon(gtk.STOCK_REFRESH, |
| | 1492 | gtk.ICON_SIZE_MENU) |
| | 1493 | self.images['pause'] = self.window.render_icon(gtk.STOCK_MEDIA_PAUSE, |
| | 1494 | gtk.ICON_SIZE_MENU) |
| | 1495 | self.images['ok'] = self.window.render_icon(gtk.STOCK_APPLY, |
| | 1496 | gtk.ICON_SIZE_MENU) |
| | 1497 | |
| | 1498 | |
| | 1499 | |
| | 1500 | def set_status(self, typ, sid, status): |
| | 1501 | iter = self.get_iter_by_sid(typ, sid) |
| | 1502 | if iter is not None: |
| | 1503 | self.model.set(iter, 0, self.images[status]) |
| | 1504 | |
| | 1505 | def set_progress(self, typ, sid, transfered_size, iter = None): |
| | 1506 | if not self.files_props[typ].has_key(sid): |
| | 1507 | return |
| | 1508 | file_props = self.files_props[typ][sid] |
| | 1509 | full_size = int(file_props['size']) |
| | 1510 | if full_size == 0: |
| | 1511 | percent = 0 |
| | 1512 | else: |
| | 1513 | percent = round(float(transfered_size) / full_size * 100) |
| | 1514 | if iter is None: |
| | 1515 | iter = self.get_iter_by_sid(typ, sid) |
| | 1516 | if iter is not None: |
| | 1517 | text = str(percent) + '%\n' |
| | 1518 | if transfered_size == 0: |
| | 1519 | text += '0' |
| | 1520 | else: |
| | 1521 | text += gtkgui_helpers.convert_bytes(transfered_size) |
| | 1522 | text += '/' + gtkgui_helpers.convert_bytes(full_size) |
| | 1523 | self.model.set(iter, 3, text) |
| | 1524 | if percent == 100: |
| | 1525 | self.set_status(typ, sid, 'ok') |
| | 1526 | |
| | 1527 | def get_iter_by_sid(self, typ, sid): |
| | 1528 | iter = self.model.get_iter_root() |
| | 1529 | while iter: |
| | 1530 | if typ + sid == self.model.get_value(iter, 4): |
| | 1531 | return iter |
| | 1532 | iter = self.model.iter_next(iter) |
| | 1533 | return None |
| | 1534 | |
| | 1535 | def add_transfer(self, account, contact, file_props): |
| | 1536 | if file_props is None: |
| | 1537 | return |
| | 1538 | self.files_props[file_props['type']][file_props['sid']] = file_props |
| | 1539 | iter = self.model.append() |
| | 1540 | text_labels = '<b>' + _('Name: ') + '</b>\n' |
| | 1541 | text_labels += '<b>' + _('Sender: ') + '</b>' |
| | 1542 | text_props = file_props['name'] + '\n' |
| | 1543 | text_props += contact.name |
| | 1544 | self.model.set(iter, 1, text_labels, 2, text_props, 4, \ |
| | 1545 | file_props['type'] + file_props['sid']) |
| | 1546 | self.set_progress(file_props['type'], file_props['sid'], 0, iter) |
| | 1547 | self.set_status(file_props['type'], file_props['sid'], 'download') |
| | 1548 | self.window.show_all() |
| | 1549 | |
| | 1550 | |
| | 1551 | def on_transfers_list_enter_notify_event(self, widget, event): |
| | 1552 | pass |
| | 1553 | |
| | 1554 | def on_transfers_list_leave_notify_event(self, widget, event): |
| | 1555 | pass |
| | 1556 | |
| | 1557 | def on_transfers_list_row_activated(self, widget, path, col): |
| | 1558 | # try to open the file |
| | 1559 | pass |
| | 1560 | |
| | 1561 | def is_transfer_paused(self, file_props): |
| | 1562 | if file_props.has_key('error') and file_props['error'] != 0: |
| | 1563 | return False |
| | 1564 | if file_props['completed'] or file_props['disconnect_cb'] is None: |
| | 1565 | return False |
| | 1566 | return file_props['paused'] |
| | 1567 | |
| | 1568 | def is_transfer_active(self, file_props): |
| | 1569 | if file_props.has_key('error') and file_props['error'] != 0: |
| | 1570 | return False |
| | 1571 | if file_props['completed'] or file_props['disconnect_cb'] is None: |
| | 1572 | return False |
| | 1573 | return not file_props['paused'] |
| | 1574 | |
| | 1575 | def is_transfer_stoped(self, file_props): |
| | 1576 | if file_props.has_key('error') and file_props['error'] != 0: |
| | 1577 | return True |
| | 1578 | if file_props['completed']: |
| | 1579 | return True |
| | 1580 | if file_props.has_key('disconnect_cb') and \ |
| | 1581 | file_props['disconnect_cb'] is not None: |
| | 1582 | return False |
| | 1583 | return True |
| | 1584 | |
| | 1585 | def select_func(self, path): |
| | 1586 | is_selected = False |
| | 1587 | current_iter = self.model.get_iter(path) |
| | 1588 | selected = self.tree.get_selection().get_selected() |
| | 1589 | if selected[1] != None: |
| | 1590 | selected_path = self.model.get_path(selected[1]) |
| | 1591 | if selected_path == path: |
| | 1592 | is_selected = True |
| | 1593 | sid = self.model[current_iter][4] |
| | 1594 | file_props = self.files_props[sid[0]][sid[1:]] |
| | 1595 | if self.is_transfer_stoped(file_props): |
| | 1596 | is_selected = True |
| | 1597 | self.stop_button.set_property('sensitive', not is_selected) |
| | 1598 | if is_selected: |
| | 1599 | self.pause_button.set_property('sensitive', False) |
| | 1600 | else: |
| | 1601 | if self.is_transfer_active(file_props): |
| | 1602 | self.pause_button.set_property('sensitive', True) |
| | 1603 | self.pause_button.set_label(_('_Pause')) |
| | 1604 | elif self.is_transfer_paused(file_props): |
| | 1605 | self.pause_button.set_property('sensitive', True) |
| | 1606 | self.pause_button.set_label(_('_Continue')) |
| | 1607 | else: |
| | 1608 | self.pause_button.set_property('sensitive', False) |
| | 1609 | |
| | 1610 | return True |
| | 1611 | def on_clean_button_clicked(self, widget): |
| | 1612 | selected = self.tree.get_selection().get_selected() |
| | 1613 | if selected is None or selected[1] is None: |
| | 1614 | return |
| | 1615 | s_iter = selected[1] |
| | 1616 | sid = self.model[s_iter][4] |
| | 1617 | file_props = self.files_props[sid[0]][sid[1:]] |
| | 1618 | if not self.is_transfer_stoped(file_props): |
| | 1619 | file_props['disconnect_cb']() |
| | 1620 | self.model.remove(s_iter) |
| | 1621 | |
| | 1622 | def on_pause_restore_button_clicked(self, widget): |
| | 1623 | selected = self.tree.get_selection().get_selected() |
| | 1624 | if selected is None or selected[1] is None: |
| | 1625 | return |
| | 1626 | s_iter = selected[1] |
| | 1627 | sid = self.model[s_iter][4] |
| | 1628 | file_props = self.files_props[sid[0]][sid[1:]] |
| | 1629 | if self.is_transfer_paused(file_props): |
| | 1630 | file_props['paused'] = False |
| | 1631 | types = {'r':'download', 's':'upload'} |
| | 1632 | self.set_status(file_props['type'], file_props['sid'], types[sid[0]]) |
| | 1633 | widget.set_label(_('Pause')) |
| | 1634 | elif self.is_transfer_active(file_props): |
| | 1635 | file_props['paused'] = True |
| | 1636 | self.set_status(file_props['type'], file_props['sid'], 'pause') |
| | 1637 | widget.set_label(_('_Continue')) |
| | 1638 | |
| | 1639 | def on_stop_button_clicked(self, widget): |
| | 1640 | selected = self.tree.get_selection().get_selected() |
| | 1641 | if selected is None or selected[1] is None: |
| | 1642 | return |
| | 1643 | s_iter = selected[1] |
| | 1644 | sid = self.model[s_iter][4] |
| | 1645 | file_props = self.files_props[sid[0]][sid[1:]] |
| | 1646 | if not self.is_transfer_stoped(file_props): |
| | 1647 | file_props['disconnect_cb']() |
| | 1648 | self.set_status(file_props['type'], file_props['sid'], 'stop') |
| | 1649 | |
| | 1650 | def on_notify_ft_complete_checkbox_toggled(self, widget): |
| | 1651 | gajim.config.set('notify_on_file_complete', |
| | 1652 | widget.get_active()) |
| | 1653 | |
| | 1654 | def on_file_transfers_dialog_delete_event(self, widget, event): |
| | 1655 | self.window.hide() |
| | 1656 | return True |
| | 1657 | |