# -*- coding: utf-8 -*- """ OnionShare | https://onionshare.org/ Copyright (C) 2014 Micah Lee This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ import os from PyQt4 import QtCore, QtGui import common from onionshare import strings, helpers class FileList(QtGui.QListWidget): files_dropped = QtCore.pyqtSignal() files_updated = QtCore.pyqtSignal() def __init__(self, parent=None): super(FileList, self).__init__(parent) self.setAcceptDrops(True) self.setIconSize(QtCore.QSize(32, 32)) self.setSortingEnabled(True) class DropHereLabel(QtGui.QLabel): def __init__(self, parent, image=False): self.parent = parent super(DropHereLabel, self).__init__(parent=parent) self.setAcceptDrops(True) self.setAlignment(QtCore.Qt.AlignCenter) if image: self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(common.get_image_path('drop_files.png')))) else: self.setText(QtCore.QString(strings._('gui_drag_and_drop', True))) self.setStyleSheet('color: #999999;') self.hide() def dragEnterEvent(self, event): self.parent.drop_here_image.hide() self.parent.drop_here_text.hide() event.ignore() self.drop_here_image = DropHereLabel(self, True) self.drop_here_text = DropHereLabel(self, False) self.filenames = [] self.update() def update(self): # file list should have a background image if empty if len(self.filenames) == 0: self.drop_here_image.show() self.drop_here_text.show() else: self.drop_here_image.hide() self.drop_here_text.hide() def resizeEvent(self, event): self.drop_here_image.setGeometry(0, 0, self.width(), self.height()) self.drop_here_text.setGeometry(0, 0, self.width(), self.height()) def dragEnterEvent(self, event): if event.mimeData().hasUrls: event.accept() else: event.ignore() def dragLeaveEvent(self, event): event.accept() self.update() def dragMoveEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: event.ignore() def dropEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() for url in event.mimeData().urls(): filename = str(url.toLocalFile()) self.add_file(filename) else: event.ignore() self.files_dropped.emit() def add_file(self, filename): if filename not in self.filenames: # make filenames unicode-safe for Qt (#141) filename = filename.encode('utf-8').decode('utf-8', 'replace') self.filenames.append(filename) fileinfo = QtCore.QFileInfo(filename) basename = os.path.basename(filename.rstrip('/')) ip = QtGui.QFileIconProvider() icon = ip.icon(fileinfo) if os.path.isfile(filename): size = self.human_readable_filesize(fileinfo.size()) else: size = self.human_readable_filesize(helpers.dir_size(filename)) item_name = unicode('{0} ({1})'.format(basename, size)) item = QtGui.QListWidgetItem(item_name) item.setToolTip(QtCore.QString(size)) item.setIcon(icon) self.addItem(item) self.files_updated.emit() def human_readable_filesize(self, b): thresh = 1024.0 if b < thresh: return '{0} B'.format(b) units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'] u = 0 b /= thresh while b >= thresh: b /= thresh u += 1 return '{0} {1}'.format(round(b, 1), units[u]) class FileSelection(QtGui.QVBoxLayout): def __init__(self): super(FileSelection, self).__init__() self.server_on = False # file list self.file_list = FileList() self.file_list.currentItemChanged.connect(self.update) self.file_list.files_dropped.connect(self.update) # buttons self.add_files_button = QtGui.QPushButton(strings._('gui_add_files', True)) self.add_files_button.clicked.connect(self.add_files) self.add_dir_button = QtGui.QPushButton(strings._('gui_add_folder', True)) self.add_dir_button.clicked.connect(self.add_dir) self.delete_button = QtGui.QPushButton(strings._('gui_delete', True)) self.delete_button.clicked.connect(self.delete_file) button_layout = QtGui.QHBoxLayout() button_layout.addWidget(self.add_files_button) button_layout.addWidget(self.add_dir_button) button_layout.addWidget(self.delete_button) # add the widgets self.addWidget(self.file_list) self.addLayout(button_layout) self.update() def update(self): # all buttons should be disabled if the server is on if self.server_on: self.add_files_button.setEnabled(False) self.add_dir_button.setEnabled(False) self.delete_button.setEnabled(False) else: self.add_files_button.setEnabled(True) self.add_dir_button.setEnabled(True) # delete button should be disabled if item isn't selected current_item = self.file_list.currentItem() if not current_item: self.delete_button.setEnabled(False) else: self.delete_button.setEnabled(True) # update the file list self.file_list.update() def add_files(self): filenames = QtGui.QFileDialog.getOpenFileNames(caption=strings._('gui_choose_files', True), options=QtGui.QFileDialog.ReadOnly) if filenames: for filename in filenames: self.file_list.add_file(str(filename)) self.update() def add_dir(self): filename = QtGui.QFileDialog.getExistingDirectory(caption=strings._('gui_choose_folder', True), options=QtGui.QFileDialog.ReadOnly) if filename: self.file_list.add_file(str(filename)) self.update() def delete_file(self): current_row = self.file_list.currentRow() self.file_list.filenames.pop(current_row) self.file_list.takeItem(current_row) self.update() def server_started(self): self.server_on = True self.file_list.setAcceptDrops(False) self.update() def server_stopped(self): self.server_on = False self.file_list.setAcceptDrops(True) self.update() def get_num_files(self): return len(self.file_list.filenames)