Beispiel #1
0
class Downloads(QTreeView):
    def __init__(self, parent):
        #TODO: Create wrapper or subclass list to append and remove from items and rows_buffer.
        QTreeView.__init__(self)

        self.weak_parent = weakref.ref(parent)

        #listview look
        self.setWordWrap(True) #search textElideMode
        self.setRootIsDecorated(False)
        self.setIndentation(0)
        self.setAlternatingRowColors(True)
        
        self.icons_dict = self.get_icons()
        self.items = []
        self.rows_buffer = {} #{id_item: row_obj, }

        headers = ["hidden_id_item", "", _("File Name"), _("Host"), _("Size"), _("Complete"), _("Progress"), _("Time"), _("Remain"), _("Speed"), _("Status Message")]
        
        self.__model = SimpleListModel(headers, self.items)
        self.setModel(self.__model)
        self.setColumnHidden(0, True)
        self.setColumnWidth(1, 27)
        self.header().setResizeMode(1, QHeaderView.Fixed)
        #self.header().setResizeMode(3, QHeaderView.ResizeToContents)
        
        self.im_delegate = ImageDelegate(self)
        self.setItemDelegateForColumn(1, self.im_delegate)
        
        self.im2_delegate = ImageDelegate(self)
        self.setItemDelegateForColumn(3, self.im2_delegate)
        
        self.nf_delegate = NoFocusDelegate(self)
        self.setItemDelegate(self.nf_delegate)
        
        self.pb_delegate = ProgressBarDelegate(self)
        self.setItemDelegateForColumn(6, self.pb_delegate)
        
        #see http://stackoverflow.com/questions/1987546/qt4-stylesheets-and-focus-rect for removing the drawed border on focus
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        
        #self.setDragEnabled(True)
        #self.setAcceptDrops(True)
        #self.setDropIndicatorShown(False)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        #self.setDefaultDropAction(Qt.MoveAction)
        
        #drop indicator
        self.line = QWidget(self.viewport())
        self.line.setAutoFillBackground(True)
        pal = self.line.palette()
        pal.setColor(self.line.backgroundRole(), Qt.black)
        self.line.setPalette(pal)
        self.line.setGeometry(0, 0, 0, 0)
        self.line.hide()

        #custom signals
        signals.store_items.connect(self.store_items)
        signals.on_stop_all.connect(self.on_stop_all)

        #update list
        self.timer = parent.idle_timeout(1000, self.update_)

    @property
    def parent(self):
        return self.weak_parent()

    def remove_row(self, id_item):
        item = self.rows_buffer.pop(id_item)
        self.__model.remove(self.items.index(item))
    
    def get_selected_rows(self):
        """"""
        selected_rows = [index.row() for index in self.selectionModel().selectedRows()]
        selected_rows.sort()
        return selected_rows
    
    def dragEnterEvent(self, event):
        #FIXME: weird things may happen if draggin when an item is removed or added.
        #event.setDropAction(Qt.MoveAction)
        event.accept()
    
    def dragMoveEvent(self, event): #paint drop indicator at the current position.
        q_index = self.indexAt(event.pos())
        
        if q_index.isValid():
            rect = self.visualRect(q_index)
            self.line.setGeometry(0, rect.top(), self.viewport().width(), 1)
        else:
            q_index = self.__model.index(self.__model.rowCount() - 1)
            rect = self.visualRect(q_index)
            self.line.setGeometry(0, rect.bottom(), self.viewport().width(), 1)
        
        self.line.show()
    
    def dragLeaveEvent(self, event): #out of the drop area.
        self.line.hide()
    
    def dropEvent(self, event):
        self.line.hide()
        
        q_index = self.indexAt(event.pos())
        if q_index.isValid():
            index = q_index.row()
        else:
            index = -1
        
        items = [self.items[row] for row in self.get_selected_rows()]
        dest_item = self.items[index]
        [self.__model.remove(self.items.index(item)) for item in items if item != dest_item]
        if index >= 0:
            index = self.items.index(dest_item)
            [self.__model.insert(index, item) for item in reversed(items) if item != dest_item]
        else:
            [self.__model.append(item) for item in items if item != dest_item]
        api.reorder_queue([row[0] for row in self.items])
    
    #def keyboard_event(self, widget, event):
        #if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key
            #self.on_delete()
    
    def contextMenuEvent(self, event):
        rows = self.get_selected_rows()

        is_selection = True if rows else False

        options = [(_('Open destination folder'), self.on_open_folder, is_selection),
                    (_('Copy link'), self.on_copy_link, is_selection),
                    #(_('Add password'), self.on_password, is_selection),
                    None,
                    (_('Delete'), self.on_delete, is_selection),
                    None,
                    (_('Start all'), self.on_start_all, True),
                    (_('Stop all'), self.on_stop_all, True),
                    (_('Clear Completed'), self.on_clear_completed, True)]

        menu = Menu(options)
        
        menu.exec_(event.globalPos())

    def selectionChanged(self, selected, deselected):
        #overridden slot
        QTreeView.selectionChanged(self, selected, deselected)

        BTN = 0
        rows = self.get_selected_rows()

        self.parent.stop[BTN].setEnabled(False)
        self.parent.start[BTN].setEnabled(False)

        if len(rows) == 1: #single selection.
            row_index = rows[0]
            id_item = self.items[row_index][0]
            self.parent.stop[BTN].setEnabled(True)
            self.parent.start[BTN].setEnabled(False)
            stopped_downloads = api.get_stopped_downloads()
            try:
                item = stopped_downloads[id_item]
                self.parent.stop[BTN].setEnabled(False)
                self.parent.start[BTN].setEnabled(True)
            except KeyError:
                pass
        elif rows: #multi selection
            self.parent.stop[BTN].setEnabled(True)
            self.parent.start[BTN].setEnabled(True)
    
    def on_open_folder(self):
        rows = self.get_selected_rows()
        if rows:
            items_list = api.get_download_items([self.items[row_index][0] for row_index in rows])
            paths_list = {download_item.path for download_item in items_list}
            for folder_path in paths_list:
                utils.open_folder_window(folder_path)

    def on_copy_link(self):
        rows = self.get_selected_rows()
        if rows:
            items_list = api.get_download_items([self.items[row_index][0] for row_index in rows])
            links_list = [download_item.link for download_item in items_list if download_item.can_copy_link]
            clipboard = QApplication.clipboard()
            clipboard.setText('\n'.join(links_list))
    
    def on_password(self):
        rows = self.get_selected_rows()
        if rows:
            pass
            #entry = gtk.Entry()
            #entry.add_events(gtk.gdk.KEY_RELEASE_MASK)
            #entry.set_width_chars(25) #entry width
            
            #m = DlgGui(self.__parent, None, _("Password"), None, True, append_widget=entry)
            
            #pwd = entry.get_text().strip()
            
            #if m.accepted and pwd:
                #events.add_password.emit(pwd)
    
    def on_delete(self):
        rows = self.get_selected_rows()
        if rows:
            #message = _("Do you want to remove this download? (downloaded segments will be deleted)")
            #m = DlgGui(self.__parent, gtk.STOCK_DIALOG_WARNING, _("Remove Files"), message, True, True)
            m = True
            if m:
                id_items_list = []
                for row_index in rows:
                    id_item = self.items[row_index][0]
                    id_items_list.append(id_item)
                [self.remove_row(id_item) for id_item in id_items_list]
                api.delete_download(id_items_list)
    
    def on_clear_completed(self):
        finished_icon = self.icons_dict[cons.STATUS_FINISHED]
        for row in self.items[:]:
            if row[1] == finished_icon:
                self.remove_row(row[0])
        api.clear_complete()
    
    def on_start_all(self):
        """
        BUG: El boton start y stop no cambia.
        """
        id_item_list = [row[0] for row in self.items]
        api.start_all(id_item_list)
        stopped_icon = self.icons_dict[cons.STATUS_STOPPED]
        queue_icon = self.icons_dict[cons.STATUS_QUEUE]
        for row in self.items:
            if row[1] == stopped_icon:
                row[1] = queue_icon

    def on_stop_all(self):
        api.stop_all()
        stopped_icon = self.icons_dict[cons.STATUS_STOPPED]
        queue_icon = self.icons_dict[cons.STATUS_QUEUE]
        for row in self.items:
            if row[1] == queue_icon:
                row[1] = stopped_icon

    def store_items(self, item_list):
        for download_item in item_list:
            size_file = utils.size_format(download_item.size) if download_item.size else None
            size_complete = utils.size_format(download_item.size_complete) if download_item.size_complete else None
            time = utils.time_format(download_item.time) if download_item.time else None
            host_icon = self.get_host_icon(download_item.host)
            
            item = [download_item.id, self.icons_dict[download_item.status], download_item.name, [host_icon, None, None],
                    size_file, size_complete, download_item.progress, time, None, None, download_item.status_msg]
            
            self.__model.append(item)
            self.rows_buffer[item[0]] = item

    def update_(self):
        active_downloads = api.get_active_downloads()
        api.update_active_downloads()
        for download_item in active_downloads.itervalues():
            try:
                row = self.rows_buffer[download_item.id]
                #row[0] = download_item.id
                row[1] = self.icons_dict[download_item.status] #col 1
                row[2] = download_item.name #col 2
                #row[3][0] = download_item.host
                row[3][1] = self.icons_dict[cons.DL_RESUME] if download_item.can_resume else None
                row[3][2] = self.icons_dict[cons.DL_PREMIUM] if download_item.is_premium else None
                row[4] = utils.size_format(download_item.size) if download_item.size else None
                row[5] = utils.size_format(download_item.size_complete) if download_item.size_complete else None
                row[6] = download_item.progress
                row[7] = utils.time_format(download_item.time) if download_item.time else None
                row[8] = utils.time_format(download_item.time_remain) if download_item.time_remain else None
                row[9] = utils.speed_format(download_item.speed) if download_item.speed else None
                row[10] = self.get_status_msg(download_item)
            except KeyError as err:
                logger.debug(err)
        self.__model.refresh()

    def get_status_msg(self, download_item):
        if download_item.fail_count:
            return "{0} ({1} #{2})".format(download_item.status_msg, _("Retry"), download_item.fail_count)
        else:
            return download_item.status_msg
    
    def get_host_icon(self, host):
        try:
            return self.icons_dict[host]
        except KeyError:
            self.icons_dict[host] = QPixmap(os.path.join(cons.PLUGINS_PATH, host, "favicon.ico"))
            return self.icons_dict[host]
    
    def get_icons(self):
        running = media.get_pixmap(media.START, media.SMALL)
        stopped = media.get_pixmap(media.STOP, media.SMALL)
        queue = media.get_pixmap(media.QUEUE, media.SMALL)
        finished = media.get_pixmap(media.CHECK, media.SMALL)
        error = media.get_pixmap(media.X_MARK, media.SMALL)
        
        resume = media.get_pixmap(media.REFRESH, media.SMALL)
        premium = media.get_pixmap(media.ACCOUNTS, media.SMALL)
        
        return {cons.STATUS_RUNNING: running, cons.STATUS_STOPPED: stopped,
                cons.STATUS_QUEUE: queue, cons.STATUS_FINISHED: finished,
                cons.STATUS_ERROR: error, 
                cons.DL_RESUME: resume, cons.DL_PREMIUM: premium}
Beispiel #2
0
class AddDownloads(QVBoxLayout):
    def __init__(self, downloads, parent=None):
        QVBoxLayout.__init__(self)
        self.setContentsMargins(0, 0, 0, 0)
        self.setSpacing(5)

        self.downloads = downloads
        self.parent = parent

        self.tree_view = QTreeView(parent)
        #
        self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree_view.customContextMenuRequested.connect(self.context_menu)
        #
        #listview look
        self.tree_view.setWordWrap(True)  #search textElideMode
        self.tree_view.setRootIsDecorated(False)
        self.tree_view.setIndentation(0)
        self.tree_view.setAlternatingRowColors(True)
        #
        self.items = []
        self.rows_buffer = {}  #{id_item: row_obj, }
        #self.items = [[True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"]]
        bool_cols = [
            1,
        ]
        headers = [
            "hidden_id_item", "",
            _("Status"),
            _("File Name"),
            _("Host"),
            _("Size"),
            _("Status Message")
        ]
        #
        self.__model = SimpleListModel(headers, self.items, bool_cols)
        self.tree_view.setModel(self.__model)
        self.addWidget(self.tree_view)
        #
        self.tree_view.setColumnHidden(0, True)
        self.tree_view.setColumnWidth(1, 27)
        self.tree_view.header().setResizeMode(1, QHeaderView.Fixed)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.setSpacing(10)

        self.cb = QComboBox()
        #self.cb.setFixedWidth(100)
        self.cb.setEditable(True)
        self.cb.setFixedHeight(35)
        self.cb.setMinimumWidth(1)
        cb_view = self.cb.view()
        cb_view.setAlternatingRowColors(True)
        #
        self.paths_list = conf.get_save_dl_paths()
        if not self.paths_list:
            self.cb.addItem(cons.DLFOLDER_PATH)
        else:
            [self.cb.addItem(path) for path in reversed(self.paths_list)]
        #
        hbox.addWidget(self.cb)

        btn_examine = QPushButton('...')
        btn_examine.clicked.connect(self.on_examine)
        btn_examine.setFixedHeight(35)
        btn_examine.setMaximumWidth(80)
        hbox.addWidget(btn_examine)

        hbox.addSpacing(40)  #index, size

        btn_download = QPushButton()
        btn_download.setIcon(media.get_icon(media.DOWN, media.MEDIUM))
        btn_download.setIconSize(QSize(24, 24))
        btn_download.clicked.connect(self.on_download_selected)
        btn_download.setFixedHeight(35)
        btn_download.setMaximumWidth(40)
        hbox.addWidget(btn_download)

        btn_add = QPushButton()
        btn_add.setIcon(media.get_icon(media.ADD, media.MEDIUM))
        btn_add.setIconSize(QSize(24, 24))
        btn_add.clicked.connect(self.on_add_links)
        btn_add.setFixedHeight(35)
        btn_add.setMaximumWidth(40)
        hbox.addWidget(btn_add)

        self.menu = QMenu(parent)
        import_action = self.menu.addAction(_("Import Container"),
                                            self.on_import_container)
        self.menu.addSeparator()
        recheck_action = self.menu.addAction(_("Re-check"), self.on_recheck)
        clear_action = self.menu.addAction(_("Clear list"), self.on_clear_list)
        #
        btn_menu = QPushButton()
        btn_menu.setMenu(self.menu)
        btn_menu.setFixedHeight(35)
        btn_menu.setMaximumWidth(22)
        btn_menu.setFlat(True)
        hbox.addWidget(btn_menu)

        self.addLayout(hbox)

        #update list
        parent.idle_timeout(1000, self.update)

    def context_menu(self, position):
        menu = QMenu()
        #indexes = self.selectedIndexes()

        #sensitive = True if indexes else False

        #individual_items = [('Open destination folder', self.on_open_folder),]

        #[menu.addAction(title, callback).setEnabled(sensitive) for title, callback in individual_items]

        #menu.addSeparator()

        generic_items = [(_('Download Selected'), self.on_download_selected),
                         (None, None), (_('Select all'), self.on_select_all),
                         (_('Select none'), self.on_select_none),
                         (_('Select inverse'), self.on_select_inverse),
                         (None, None), (_('Re-check'), self.on_recheck),
                         (_('Clear list'), self.on_clear_list)]

        [
            menu.addAction(title, callback)
            if title is not None else menu.addSeparator()
            for title, callback in generic_items
        ]

        menu.exec_(self.tree_view.viewport().mapToGlobal(position))

    def on_clear_list(self):
        self.__model.clear()
        self.rows_buffer.clear()
        api.clear_pending()

    def on_recheck(self):
        api.recheck_items()

    def on_select_all(self):
        for row in self.items:
            row[1] = True

    def on_select_none(self):
        for row in self.items:
            row[1] = False

    def on_select_inverse(self):
        for row in self.items:
            row[1] = False if row[1] else True

    def on_import_container(self):
        file_name, filter = QFileDialog.getOpenFileName(
            filter='OCH Files (*.och)')
        if file_name:
            container = Container(file_name)
            container.extract_links()
            links_list = container.get_linklist()

            if links_list:
                self.links_checking(links_list, copy_link=False)

    def on_add_links(self):
        add_links = AddLinks(self.parent)
        result_code = add_links.result()
        links_list = add_links.links_list
        if result_code == QDialog.Accepted and links_list:
            self.links_checking(links_list)

    def on_examine(self):
        folder = QFileDialog.getExistingDirectory()
        if folder:
            self.cb.setEditText(folder)

    def cb_remove(self, text):
        index = self.cb.findText(text)
        if index >= 0:
            self.cb.removeItem(index)

    def on_download_selected(self):
        current_path = self.cb.currentText()
        self.cb_remove(current_path)
        self.cb.insertItem(0, current_path)
        if current_path in self.paths_list:
            self.paths_list.remove(current_path)
        self.paths_list.append(current_path)
        if len(self.paths_list) > 5:
            self.paths_list.pop(0)
            self.cb.removeItem(5)
        self.cb.setCurrentIndex(0)
        conf.set_save_dl_paths(self.paths_list)

        id_items_list = []
        iters = []
        for row in self.items:
            if row[1]:  # and row[4] != cons.UNSUPPORTED: #tmp
                iters.append(row)
                id_item = row[0]
                id_items_list.append(id_item)
                del self.rows_buffer[id_item]
        [self.__model.remove(self.items.index(iter)) for iter in iters]

        item_list = api.get_added_items(id_items_list)

        api.downloader_init(item_list,
                            current_path)  #iniciar threads de descarga.
        #TODO: use a signal.
        self.downloads.store_items(item_list)

    def links_checking(self, links_list, copy_link=True):
        for link in links_list:
            download_item = api.create_download_item(
                cons.UNKNOWN, 0, link, copy_link)  #return download_item object
            item = [
                download_item.id, True, cons.LINK_CHECKING, cons.UNKNOWN, None,
                None, None
            ]
            #self.items.append(item)
            self.__model.append(item)
            self.rows_buffer[item[0]] = item
        api.start_checking()

    def update(self):  #this method steals cycles, its not a new thread
        items_list = api.get_checking_update()
        for download_item in items_list:
            try:
                row = self.rows_buffer[download_item.id]
                row[1] = True if download_item.link_status != cons.LINK_DEAD else False
                row[2] = download_item.link_status
                row[3] = download_item.name
                row[4] = download_item.host
                row[5] = misc.size_format(download_item.size)
                row[6] = download_item.link_status_msg
            except KeyError as err:
                logger.debug(err)
        self.__model.refresh()
class AddDownloads(QVBoxLayout):
    def __init__(self, parent):
        QVBoxLayout.__init__(self)
        self.setContentsMargins(0, 0, 0, 0)
        self.setSpacing(5)

        self.weak_parent = weakref.ref(parent)
        
        self.tree_view = QTreeView()

        self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree_view.customContextMenuRequested.connect(self.context_menu)

        #listview look
        self.tree_view.setWordWrap(True) #search textElideMode
        self.tree_view.setRootIsDecorated(False)
        self.tree_view.setIndentation(0)
        self.tree_view.setAlternatingRowColors(True)

        self.icons_dict = self.get_icons()
        self.items = []
        self.rows_buffer = {} #{id_item: row_obj, }

        bool_cols = [1, ]
        image_cols = [2, ]
        headers = ["hidden_id_item", "", "", _("File Name"), _("Host"), _("Size"), _("Status Message")]

        self.__model = SimpleListModel(headers, self.items, bool_cols, image_cols)
        self.tree_view.setModel(self.__model)
        self.addWidget(self.tree_view)

        self.tree_view.setColumnHidden(0, True)
        self.tree_view.setColumnWidth(1, 27)
        self.tree_view.setColumnWidth(2, 27)
        self.tree_view.header().setResizeMode(1, QHeaderView.Fixed)
        
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.setSpacing(10)
        
        self.cb = QComboBox()
        #self.cb.setFixedWidth(100)
        self.cb.setEditable(True)
        self.cb.setFixedHeight(35)
        self.cb.setMinimumWidth(1)
        cb_view = self.cb.view()
        cb_view.setAlternatingRowColors(True)

        self.paths_list = conf.get_save_dl_paths()

        if not self.paths_list:
            self.cb.addItem(cons.DLFOLDER_PATH)
        else:
            [self.cb.addItem(path) for path in reversed(self.paths_list)]

        hbox.addWidget(self.cb)
        
        btn_examine = QPushButton('...')
        btn_examine.clicked.connect(self.on_examine)
        btn_examine.setFixedHeight(35)
        btn_examine.setMaximumWidth(80)
        hbox.addWidget(btn_examine)
        
        hbox.addSpacing(40) #index, size
        
        btn_download = QPushButton()
        btn_download.setIcon(media.get_icon(media.DOWN, media.MEDIUM))
        btn_download.setIconSize(QSize(24, 24))
        btn_download.clicked.connect(self.on_download_selected)
        btn_download.setFixedHeight(35)
        btn_download.setMaximumWidth(40)
        hbox.addWidget(btn_download)
        
        btn_add = QPushButton()
        btn_add.setIcon(media.get_icon(media.ADD, media.MEDIUM))
        btn_add.setIconSize(QSize(24, 24))
        btn_add.clicked.connect(self.on_add_links)
        btn_add.setFixedHeight(35)
        btn_add.setMaximumWidth(40)
        hbox.addWidget(btn_add)
        
        self.menu = QMenu()
        import_action = self.menu.addAction(_("Import Container"), self.on_import_container)
        self.menu.addSeparator()
        recheck_action = self.menu.addAction(_("Re-check"), self.on_recheck)
        clear_action = self.menu.addAction(_("Clear list"), self.on_clear_list)

        btn_menu = QPushButton()
        btn_menu.setMenu(self.menu)
        btn_menu.setFixedHeight(35)
        btn_menu.setMaximumWidth(22)
        btn_menu.setFlat(True)
        hbox.addWidget(btn_menu)
        
        self.addLayout(hbox)

        #custom signals
        signals.add_downloads_to_check.connect(self.add_downloads_to_check)
        
        #update list
        self.timer = parent.idle_timeout(1000, self.update_)

    @property
    def parent(self):
        return self.weak_parent()

    def get_selected_rows(self):
        """"""
        selected_rows = [index.row() for index in self.tree_view.selectionModel().selectedRows()]
        selected_rows.sort()
        return selected_rows

    def context_menu(self, position):
        rows = self.get_selected_rows()

        is_single_row = True if len(rows) == 1 else False

        options = [(_('Save as...'), self.on_save_as, is_single_row),
                    None,
                    (_('Download Selected'), self.on_download_selected, True),
                    None,
                    (_('Select all'), self.on_select_all, True),
                    (_('Select none'), self.on_select_none, True),
                    (_('Select inverse'), self.on_select_inverse, True),
                    None,
                    (_('Re-check'), self.on_recheck, True),
                    (_('Clear list'), self.on_clear_list, True)]

        menu = Menu(options)
        
        menu.exec_(self.tree_view.viewport().mapToGlobal(position))

    def on_save_as(self):
        rows = self.get_selected_rows()
        row = self.items[rows[0]]
        item_id = row[0]
        download_item = api.get_checking_download_item(item_id)
        widget = QLineEdit()

        # set the line entry file name if we have one.
        if download_item.save_as:
            widget.setText(download_item.save_as)
        elif download_item.name != cons.UNKNOWN:
            widget.setText(download_item.name)

        dialog = Dialog(self.parent, "Save as", widget)
        if dialog.result() == QDialog.Accepted:
            save_as = widget.text()
            # change the file name in treeview and DownloadItem
            if save_as:
                api.save_download_as(download_item, save_as)
                api.set_download_name(download_item, save_as)
                row[3] = save_as

    def on_clear_list(self):
        self.__model.clear()
        self.rows_buffer.clear()
        api.clear_pending()
    
    def on_recheck(self):
        api.recheck_items()

    def on_select_all(self):
        for row in self.items:
            row[1] = True

    def on_select_none(self):
        for row in self.items:
            row[1] = False

    def on_select_inverse(self):
        for row in self.items:
            row[1] = False if row[1] else True
    
    def on_import_container(self):
        file_name, filter = QFileDialog.getOpenFileName(filter='OCH Files (*.och)')
        if file_name:
            container = Container(file_name)
            container.extract_links()
            links_list = container.get_linklist()
            
            if links_list:
                self.add_downloads_to_check(links_list, copy_link=False)
    
    def on_add_links(self):
        dialog = AddLinks(self.parent)
        result_code = dialog.result()
        links_list = dialog.links_list
        if result_code == QDialog.Accepted and links_list:
            self.add_downloads_to_check(links_list)
    
    def on_examine(self):
        folder = QFileDialog.getExistingDirectory()
        if folder:
            self.cb.setEditText(folder)
    
    def cb_remove(self, text):
        index = self.cb.findText(text)
        if index >= 0:
            self.cb.removeItem(index)
    
    def on_download_selected(self):
        # save the selected path
        current_path = self.cb.currentText()
        self.cb_remove(current_path)
        self.cb.insertItem(0, current_path)

        if current_path in self.paths_list:
            self.paths_list.remove(current_path)

        self.paths_list.append(current_path)

        if len(self.paths_list) > 5:
            self.paths_list.pop(0)
            self.cb.removeItem(5)

        self.cb.setCurrentIndex(0)
        conf.set_save_dl_paths(self.paths_list)

        # move selected items to downloads tab
        id_items_list = []
        iters = []

        for row in self.items:
            if row[1]:
                iters.append(row)
                id_item = row[0]
                id_items_list.append(id_item)
                del self.rows_buffer[id_item]

        [self.__model.remove(self.items.index(iter)) for iter in iters]
        
        item_list = api.pop_checking_items(id_items_list)
        for download_item in item_list:
            download_item.path = current_path

        signals.add_to_downloader.emit(item_list)

    def add_downloads_to_check(self, links_list, copy_link=True):
        for link in links_list:
            download_item = api.create_download_item(cons.UNKNOWN, link, copy_link=copy_link)
            api.add_to_checker(download_item)
            item = [download_item.id, True, self.icons_dict[cons.LINK_CHECKING], cons.UNKNOWN, None, None, None]
            #self.items.append(item)
            self.__model.append(item)
            self.rows_buffer[item[0]] = item
        api.start_checking()
    
    def update_(self):
        checking_downloads = api.get_checking_downloads()
        api.update_checking_downloads()
        for download_item in checking_downloads.itervalues():
            row = self.rows_buffer[download_item.id]
            if download_item.link_status == cons.LINK_DEAD:
                row[1] = False
            row[2] = self.icons_dict[download_item.link_status]
            row[3] = download_item.name
            if not download_item.host == cons.UNSUPPORTED:
                row[4] = download_item.host
            if download_item.size:
                row[5] = utils.size_format(download_item.size)
            row[6] = download_item.link_status_msg
        self.__model.refresh()

    def get_icons(self):
        alive = media.get_pixmap(media.ALIVE, media.SMALL)
        dead = media.get_pixmap(media.DEAD, media.SMALL)
        error = media.get_pixmap(media.ERROR, media.SMALL)
        checking = media.get_pixmap(media.CHECKING, media.SMALL)
        #unavailable = media.get_pixmap(media.ERROR, media.SMALL)

        return {cons.LINK_ALIVE: alive, cons.LINK_DEAD: dead,
                cons.LINK_UNAVAILABLE: error, cons.LINK_ERROR: error,
                cons.LINK_CHECKING: checking}
Beispiel #4
0
class Downloads(QTreeView):
    def __init__(self, parent):
        #TODO: Create wrapper or subclass list to append and remove from items and rows_buffer.
        QTreeView.__init__(self)

        self.weak_parent = weakref.ref(parent)

        #listview look
        self.setWordWrap(True)  #search textElideMode
        self.setRootIsDecorated(False)
        self.setIndentation(0)
        self.setAlternatingRowColors(True)

        self.icons_dict = self.get_icons()
        self.items = []
        self.rows_buffer = {}  #{id_item: row_obj, }

        headers = [
            "hidden_id_item", "",
            _("File Name"),
            _("Host"),
            _("Size"),
            _("Complete"),
            _("Progress"),
            _("Time"),
            _("Remain"),
            _("Speed"),
            _("Status Message")
        ]

        self.__model = SimpleListModel(headers, self.items)
        self.setModel(self.__model)
        self.setColumnHidden(0, True)
        self.setColumnWidth(1, 27)
        self.header().setResizeMode(1, QHeaderView.Fixed)
        #self.header().setResizeMode(3, QHeaderView.ResizeToContents)

        self.im_delegate = ImageDelegate(self)
        self.setItemDelegateForColumn(1, self.im_delegate)

        self.im2_delegate = ImageDelegate(self)
        self.setItemDelegateForColumn(3, self.im2_delegate)

        self.nf_delegate = NoFocusDelegate(self)
        self.setItemDelegate(self.nf_delegate)

        self.pb_delegate = ProgressBarDelegate(self)
        self.setItemDelegateForColumn(6, self.pb_delegate)

        #see http://stackoverflow.com/questions/1987546/qt4-stylesheets-and-focus-rect for removing the drawed border on focus
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)

        #self.setDragEnabled(True)
        #self.setAcceptDrops(True)
        #self.setDropIndicatorShown(False)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        #self.setDefaultDropAction(Qt.MoveAction)

        #drop indicator
        self.line = QWidget(self.viewport())
        self.line.setAutoFillBackground(True)
        pal = self.line.palette()
        pal.setColor(self.line.backgroundRole(), Qt.black)
        self.line.setPalette(pal)
        self.line.setGeometry(0, 0, 0, 0)
        self.line.hide()

        #custom signals
        signals.add_to_downloader.connect(self.add_to_downloader)
        signals.on_stop_all.connect(self.on_stop_all)

        #update list
        self.timer = parent.idle_timeout(1000, self.update_)

    @property
    def parent(self):
        return self.weak_parent()

    def remove_row(self, id_item):
        item = self.rows_buffer.pop(id_item)
        self.__model.remove(self.items.index(item))

    def get_selected_rows(self):
        """"""
        selected_rows = [
            index.row() for index in self.selectionModel().selectedRows()
        ]
        selected_rows.sort()
        return selected_rows

    def dragEnterEvent(self, event):
        #FIXME: weird things may happen if draggin when an item is removed or added.
        #event.setDropAction(Qt.MoveAction)
        event.accept()

    def dragMoveEvent(self,
                      event):  #paint drop indicator at the current position.
        q_index = self.indexAt(event.pos())

        if q_index.isValid():
            rect = self.visualRect(q_index)
            self.line.setGeometry(0, rect.top(), self.viewport().width(), 1)
        else:
            q_index = self.__model.index(self.__model.rowCount() - 1)
            rect = self.visualRect(q_index)
            self.line.setGeometry(0, rect.bottom(), self.viewport().width(), 1)

        self.line.show()

    def dragLeaveEvent(self, event):  #out of the drop area.
        self.line.hide()

    def dropEvent(self, event):
        self.line.hide()

        q_index = self.indexAt(event.pos())
        if q_index.isValid():
            index = q_index.row()
        else:
            index = -1

        items = [self.items[row] for row in self.get_selected_rows()]
        dest_item = self.items[index]
        [
            self.__model.remove(self.items.index(item)) for item in items
            if item != dest_item
        ]
        if index >= 0:
            index = self.items.index(dest_item)
            [
                self.__model.insert(index, item) for item in reversed(items)
                if item != dest_item
            ]
        else:
            [self.__model.append(item) for item in items if item != dest_item]
        api.reorder_queue([row[0] for row in self.items])

    #def keyboard_event(self, widget, event):
    #if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key
    #self.on_delete()

    def contextMenuEvent(self, event):
        rows = self.get_selected_rows()

        is_selection = True if rows else False

        options = [
            (_('Open destination folder'), self.on_open_folder, is_selection),
            (_('Copy link'), self.on_copy_link, is_selection),
            #(_('Add password'), self.on_password, is_selection),
            None,
            (_('Delete'), self.on_delete, is_selection),
            None,
            (_('Start all'), self.on_start_all, True),
            (_('Stop all'), self.on_stop_all, True),
            (_('Clear Completed'), self.on_clear_completed, True)
        ]

        menu = Menu(options)

        menu.exec_(event.globalPos())

    def selectionChanged(self, selected, deselected):
        #overridden slot
        QTreeView.selectionChanged(self, selected, deselected)

        BTN = 0
        rows = self.get_selected_rows()

        if len(rows) == 1:  # single selection.
            row_index = rows[0]
            id_item = self.items[row_index][0]
            stopped_downloads = api.get_stopped_downloads()

            if id_item in stopped_downloads:
                self.parent.stop[BTN].setEnabled(False)
                self.parent.start[BTN].setEnabled(True)
            else:
                self.parent.stop[BTN].setEnabled(True)
                self.parent.start[BTN].setEnabled(False)
        elif rows:  # multi selection
            self.parent.stop[BTN].setEnabled(True)
            self.parent.start[BTN].setEnabled(True)
        else:  # no selection
            self.parent.stop[BTN].setEnabled(False)
            self.parent.start[BTN].setEnabled(False)

    def on_open_folder(self):
        rows = self.get_selected_rows()
        if rows:
            items_list = api.get_download_items(
                [self.items[row_index][0] for row_index in rows])
            paths_list = {download_item.path for download_item in items_list}
            for folder_path in paths_list:
                utils.open_folder_window(folder_path)

    def on_copy_link(self):
        rows = self.get_selected_rows()
        if rows:
            items_list = api.get_download_items(
                [self.items[row_index][0] for row_index in rows])
            links_list = [
                download_item.link for download_item in items_list
                if download_item.can_copy_link
            ]
            clipboard = QApplication.clipboard()
            clipboard.setText('\n'.join(links_list))

    def on_password(self):
        rows = self.get_selected_rows()
        if rows:
            pass
            #entry = gtk.Entry()
            #entry.add_events(gtk.gdk.KEY_RELEASE_MASK)
            #entry.set_width_chars(25) #entry width

            #m = DlgGui(self.__parent, None, _("Password"), None, True, append_widget=entry)

            #pwd = entry.get_text().strip()

            #if m.accepted and pwd:
            #events.add_password.emit(pwd)

    def on_delete(self):
        rows = self.get_selected_rows()
        if rows:
            #message = _("Do you want to remove this download? (downloaded segments will be deleted)")
            #m = DlgGui(self.__parent, gtk.STOCK_DIALOG_WARNING, _("Remove Files"), message, True, True)
            m = True
            if m:
                id_items_list = []
                for row_index in rows:
                    id_item = self.items[row_index][0]
                    id_items_list.append(id_item)
                [self.remove_row(id_item) for id_item in id_items_list]
                api.delete_download(id_items_list)

    def on_clear_completed(self):
        finished_icon = self.icons_dict[cons.STATUS_FINISHED]
        for row in self.items[:]:
            if row[1] == finished_icon:
                self.remove_row(row[0])
        api.clear_complete()

    def on_start_all(self):
        """
        BUG: El boton start y stop no cambia.
        """
        id_item_list = [row[0] for row in self.items]
        api.start_all(id_item_list)
        stopped_icon = self.icons_dict[cons.STATUS_STOPPED]
        queue_icon = self.icons_dict[cons.STATUS_QUEUE]
        for row in self.items:
            if row[1] == stopped_icon:
                row[1] = queue_icon

    def on_stop_all(self):
        api.stop_all()
        stopped_icon = self.icons_dict[cons.STATUS_STOPPED]
        queue_icon = self.icons_dict[cons.STATUS_QUEUE]
        for row in self.items:
            if row[1] == queue_icon:
                row[1] = stopped_icon

    def add_to_downloader(self, download_items):
        for download_item in download_items:
            api.add_to_downloader(download_item)
            self.store_item(download_item)

        if conf.get_auto_switch_tab():
            signals.switch_tab.emit(0)

        api.next_download()

    def store_item(self, download_item):
        size_file = utils.size_format(
            download_item.size) if download_item.size else None
        size_complete = utils.size_format(
            download_item.size_complete
        ) if download_item.size_complete else None
        time = utils.time_format(
            download_item.time) if download_item.time else None
        host_icon = self.get_host_icon(download_item.host)

        item = [
            download_item.id,
            self.icons_dict[download_item.status], download_item.name,
            [host_icon, None, None], size_file, size_complete,
            download_item.progress, time, None, None, download_item.status_msg
        ]

        self.__model.append(item)
        self.rows_buffer[item[0]] = item

    def update_(self):
        active_downloads = api.get_active_downloads()
        api.update_active_downloads()
        for download_item in active_downloads.itervalues():
            row = self.rows_buffer[download_item.id]
            #row[0] = download_item.id
            row[1] = self.icons_dict[download_item.status]
            row[2] = download_item.name
            #row[3][0] = download_item.host
            row[3][1] = self.icons_dict[
                cons.DL_RESUME] if download_item.can_resume else None
            row[3][2] = self.icons_dict[
                cons.DL_PREMIUM] if download_item.is_premium else None
            row[4] = utils.size_format(
                download_item.size) if download_item.size else None
            row[5] = utils.size_format(
                download_item.size_complete
            ) if download_item.size_complete else None
            row[6] = download_item.progress
            row[7] = utils.time_format(
                download_item.time) if download_item.time else None
            row[8] = utils.time_format(download_item.time_remain
                                       ) if download_item.time_remain else None
            row[9] = utils.speed_format(
                download_item.speed) if download_item.speed else None
            row[10] = self.get_status_msg(download_item)
        self.__model.refresh()

    def get_status_msg(self, download_item):
        if download_item.fail_count:
            return "{0} ({1} #{2})".format(download_item.status_msg,
                                           _("Retry"),
                                           download_item.fail_count)
        else:
            return download_item.status_msg

    def get_host_icon(self, host):
        try:
            return self.icons_dict[host]
        except KeyError:
            self.icons_dict[host] = QPixmap(
                os.path.join(cons.PLUGINS_PATH, host.replace('.', '_'),
                             "favicon.ico"))
            return self.icons_dict[host]

    def get_icons(self):
        running = media.get_pixmap(media.START, media.SMALL)
        stopped = media.get_pixmap(media.STOP, media.SMALL)
        queue = media.get_pixmap(media.QUEUE, media.SMALL)
        finished = media.get_pixmap(media.CHECK, media.SMALL)
        error = media.get_pixmap(media.X_MARK, media.SMALL)

        resume = media.get_pixmap(media.REFRESH, media.SMALL)
        premium = media.get_pixmap(media.ACCOUNTS, media.SMALL)

        return {
            cons.STATUS_RUNNING: running,
            cons.STATUS_STOPPED: stopped,
            cons.STATUS_QUEUE: queue,
            cons.STATUS_FINISHED: finished,
            cons.STATUS_ERROR: error,
            cons.DL_RESUME: resume,
            cons.DL_PREMIUM: premium
        }
Beispiel #5
0
class AddDownloads(QVBoxLayout):
    def __init__(self, downloads, parent=None):
        QVBoxLayout.__init__(self)
        self.setContentsMargins(0, 0, 0, 0)
        self.setSpacing(5)
        
        self.downloads = downloads
        self.parent = parent
        
        self.tree_view = QTreeView(parent)
        #
        self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree_view.customContextMenuRequested.connect(self.context_menu)
        #
        #listview look
        self.tree_view.setWordWrap(True) #search textElideMode
        self.tree_view.setRootIsDecorated(False)
        self.tree_view.setIndentation(0)
        self.tree_view.setAlternatingRowColors(True)
        #
        self.items = []
        self.rows_buffer = {} #{id_item: row_obj, }
        #self.items = [[True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"], [True, "1", "11", "t5est", "t3est"]]
        bool_cols = [1, ]
        headers = ["hidden_id_item", "", _("Status"), _("File Name"), _("Host"), _("Size"), _("Status Message")]
        #
        self.__model = SimpleListModel(headers, self.items, bool_cols)
        self.tree_view.setModel(self.__model)
        self.addWidget(self.tree_view)
        #
        self.tree_view.setColumnHidden(0, True)
        self.tree_view.setColumnWidth(1, 27)
        self.tree_view.header().setResizeMode(1, QHeaderView.Fixed)
        
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.setSpacing(10)
        
        self.cb = QComboBox()
        #self.cb.setFixedWidth(100)
        self.cb.setEditable(True)
        self.cb.setFixedHeight(35)
        self.cb.setMinimumWidth(1)
        cb_view = self.cb.view()
        cb_view.setAlternatingRowColors(True)
        #
        self.paths_list = conf.get_save_dl_paths()
        if not self.paths_list:
            self.cb.addItem(cons.DLFOLDER_PATH)
        else:
            [self.cb.addItem(path) for path in reversed(self.paths_list)]
        #
        hbox.addWidget(self.cb)
        
        btn_examine = QPushButton('...')
        btn_examine.clicked.connect(self.on_examine)
        btn_examine.setFixedHeight(35)
        btn_examine.setMaximumWidth(80)
        hbox.addWidget(btn_examine)
        
        hbox.addSpacing(40) #index, size
        
        btn_download = QPushButton()
        btn_download.setIcon(media.get_icon(media.DOWN, media.MEDIUM))
        btn_download.setIconSize(QSize(24, 24))
        btn_download.clicked.connect(self.on_download_selected)
        btn_download.setFixedHeight(35)
        btn_download.setMaximumWidth(40)
        hbox.addWidget(btn_download)
        
        btn_add = QPushButton()
        btn_add.setIcon(media.get_icon(media.ADD, media.MEDIUM))
        btn_add.setIconSize(QSize(24, 24))
        btn_add.clicked.connect(self.on_add_links)
        btn_add.setFixedHeight(35)
        btn_add.setMaximumWidth(40)
        hbox.addWidget(btn_add)
        
        self.menu = QMenu(parent)
        import_action = self.menu.addAction(_("Import Container"), self.on_import_container)
        self.menu.addSeparator()
        recheck_action = self.menu.addAction(_("Re-check"), self.on_recheck)
        clear_action = self.menu.addAction(_("Clear list"), self.on_clear_list)
        #
        btn_menu = QPushButton()
        btn_menu.setMenu(self.menu)
        btn_menu.setFixedHeight(35)
        btn_menu.setMaximumWidth(22)
        btn_menu.setFlat(True)
        hbox.addWidget(btn_menu)
        
        
        self.addLayout(hbox)
        
        #update list
        parent.idle_timeout(1000, self.update)
    
    def context_menu(self, position):
        menu = QMenu()
        #indexes = self.selectedIndexes()
        
        #sensitive = True if indexes else False
        
        #individual_items = [('Open destination folder', self.on_open_folder),]
        
        #[menu.addAction(title, callback).setEnabled(sensitive) for title, callback in individual_items]

        #menu.addSeparator()
        
        generic_items = [(_('Download Selected'), self.on_download_selected),
                        (None, None),
                        (_('Select all'), self.on_select_all),
                        (_('Select none'), self.on_select_none),
                        (_('Select inverse'), self.on_select_inverse),
                        (None, None),
                        (_('Re-check'), self.on_recheck),
                        (_('Clear list'), self.on_clear_list)]
        
        [menu.addAction(title, callback) if title is not None else menu.addSeparator()
        for title, callback in generic_items]
        
        menu.exec_(self.tree_view.viewport().mapToGlobal(position))
    
    def on_clear_list(self):
        self.__model.clear()
        self.rows_buffer.clear()
        api.clear_pending()
    
    def on_recheck(self):
        api.recheck_items()

    def on_select_all(self):
        for row in self.items:
            row[1] = True

    def on_select_none(self):
        for row in self.items:
            row[1] = False

    def on_select_inverse(self):
        for row in self.items:
            row[1] = False if row[1] else True
    
    def on_import_container(self):
        file_name, filter = QFileDialog.getOpenFileName(filter='OCH Files (*.och)')
        if file_name:
            container = Container(file_name)
            container.extract_links()
            links_list = container.get_linklist()
            
            if links_list:
                self.links_checking(links_list, copy_link=False)
    
    def on_add_links(self):
        add_links = AddLinks(self.parent)
        result_code = add_links.result()
        links_list = add_links.links_list
        if result_code == QDialog.Accepted and links_list:
            self.links_checking(links_list)
    
    def on_examine(self):
        folder = QFileDialog.getExistingDirectory()
        if folder:
            self.cb.setEditText(folder)
    
    def cb_remove(self, text):
        index = self.cb.findText(text)
        if index >= 0:
            self.cb.removeItem(index)
    
    def on_download_selected(self):
        current_path = self.cb.currentText()
        self.cb_remove(current_path)
        self.cb.insertItem(0, current_path)
        if current_path in self.paths_list:
            self.paths_list.remove(current_path)
        self.paths_list.append(current_path)
        if len(self.paths_list) > 5:
            self.paths_list.pop(0)
            self.cb.removeItem(5)
        self.cb.setCurrentIndex(0)
        conf.set_save_dl_paths(self.paths_list)
        
        id_items_list = []
        iters = []
        for row in self.items:
            if row[1]: # and row[4] != cons.UNSUPPORTED: #tmp
                iters.append(row)
                id_item = row[0]
                id_items_list.append(id_item)
                del self.rows_buffer[id_item]
        [self.__model.remove(self.items.index(iter)) for iter in iters]
        
        item_list = api.get_added_items(id_items_list)
        
        api.downloader_init(item_list, current_path) #iniciar threads de descarga.
        #TODO: use a signal.
        self.downloads.store_items(item_list)

    def links_checking(self, links_list, copy_link=True):
        for link in links_list:
            download_item = api.create_download_item(cons.UNKNOWN, 0, link, copy_link) #return download_item object
            item = [download_item.id, True, cons.LINK_CHECKING, cons.UNKNOWN, None, None, None]
            #self.items.append(item)
            self.__model.append(item)
            self.rows_buffer[item[0]] = item
        api.start_checking()
    
    def update(self): #this method steals cycles, its not a new thread
        items_list = api.get_checking_update()
        for download_item in items_list:
            try:
                row = self.rows_buffer[download_item.id]
                row[1] = True if download_item.link_status != cons.LINK_DEAD else False
                row[2] = download_item.link_status
                row[3] = download_item.name
                row[4] = download_item.host
                row[5] = misc.size_format(download_item.size)
                row[6] = download_item.link_status_msg
            except KeyError as err:
                logger.debug(err)
        self.__model.refresh()
Beispiel #6
0
class Downloads(QTreeView):
    def __init__(self, parent=None):
        #TODO: Create wrapper or subclass list to append and remove from items and rows_buffer.
        QTreeView.__init__(self, parent)

        #listview look
        self.setWordWrap(True)  #search textElideMode
        self.setRootIsDecorated(False)
        self.setIndentation(0)
        self.setAlternatingRowColors(True)

        self.icons_dict = self.get_icons()
        self.items = []
        self.rows_buffer = {}  #{id_item: row_obj, }

        #self.ico = QPixmap('stop.png')

        #self.items = [[[], "1", "11", "t5est", "t3est"], [[self.ico, self.ico], "22", "22", "t5ests", "as3est"],
        #[[self.ico, self.ico], "33", "24", "t5edd", "t3edd"], [[self.ico, self.ico], "44", "24", "t5edd", "t3edd"]]

        #for item in range(10000):
        #self.items.append([[QIcon('stop.png'), QIcon('stop.png')], "22", "24", "t5edd", "t3edd"])

        headers = [
            "hidden_id_item", "",
            _("File Name"),
            _("Host"),
            _("Size"),
            _("Complete"),
            _("Progress"),
            _("Time"),
            _("Remain"),
            _("Speed"),
            _("Status Message")
        ]

        self.__model = SimpleListModel(headers, self.items)
        self.setModel(self.__model)
        self.setColumnHidden(0, True)
        self.setColumnWidth(1, 27)
        header_view = self.header()
        header_view.setResizeMode(1, QHeaderView.Fixed)
        header_view.setResizeMode(3, QHeaderView.ResizeToContents)

        self.im_delegate = ImageDelegate(self)
        self.setItemDelegateForColumn(1, self.im_delegate)

        self.im2_delegate = ImageDelegate(self)
        self.setItemDelegateForColumn(3, self.im2_delegate)

        self.nf_delegate = NoFocusDelegate(self)
        self.setItemDelegate(self.nf_delegate)

        self.pb_delegate = ProgressBarDelegate(self)
        self.setItemDelegateForColumn(6, self.pb_delegate)

        #see http://stackoverflow.com/questions/1987546/qt4-stylesheets-and-focus-rect for removing the drawed border on focus
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)

        #self.setDragEnabled(True)
        #self.setAcceptDrops(True)
        #self.setDropIndicatorShown(False)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        #self.setDefaultDropAction(Qt.MoveAction)

        #drop indicator
        self.line = QWidget(self.viewport())
        self.line.setAutoFillBackground(True)
        pal = self.line.palette()
        pal.setColor(self.line.backgroundRole(), Qt.black)
        self.line.setPalette(pal)
        self.line.setGeometry(0, 0, 0, 0)
        self.line.hide()

        #update list
        parent.idle_timeout(1000, self.update)

    def remove_row(self, id_item):
        item = self.rows_buffer.pop(id_item)
        self.__model.remove(self.items.index(item))

    def get_selected_rows(self):
        """"""
        selected_rows = [
            index.row() for index in self.selectionModel().selectedRows()
        ]
        selected_rows.sort()
        return selected_rows

    def dragEnterEvent(self, event):
        #FIXME: weird things may happen if draggin when an item is removed or added.
        #event.setDropAction(Qt.MoveAction)
        event.accept()

    def dragMoveEvent(self,
                      event):  #paint drop indicator at the current position.
        q_index = self.indexAt(event.pos())

        if q_index.isValid():
            rect = self.visualRect(q_index)
            self.line.setGeometry(0, rect.top(), self.viewport().width(), 1)
        else:
            q_index = self.__model.index(self.__model.rowCount() - 1)
            rect = self.visualRect(q_index)
            self.line.setGeometry(0, rect.bottom(), self.viewport().width(), 1)

        self.line.show()

    def dragLeaveEvent(self, event):  #out of the drop area.
        self.line.hide()

    def dropEvent(self, event):
        self.line.hide()

        q_index = self.indexAt(event.pos())
        if q_index.isValid():
            index = q_index.row()
        else:
            index = -1

        items = [self.items[row] for row in self.get_selected_rows()]
        dest_item = self.items[index]
        [
            self.__model.remove(self.items.index(item)) for item in items
            if item != dest_item
        ]
        if index >= 0:
            index = self.items.index(dest_item)
            [
                self.__model.insert(index, item) for item in reversed(items)
                if item != dest_item
            ]
        else:
            [self.__model.append(item) for item in items if item != dest_item]
        api.reorder_queue([row[0] for row in self.items])

    #def keyboard_event(self, widget, event):
    #if gtk.gdk.keyval_name(event.keyval) == "Delete": #supr. key
    #self.on_delete()

    def contextMenuEvent(self, event):
        menu = QMenu()
        indexes = self.selectedIndexes()

        sensitive = True if indexes else False

        individual_items = [(_('Open destination folder'),
                             self.on_open_folder),
                            (_('Copy link'), self.on_copy_link),
                            (_('Add password'), self.on_password),
                            (_('Delete'), self.on_delete)]

        [
            menu.addAction(title, callback).setEnabled(sensitive)
            for title, callback in individual_items
        ]

        menu.addSeparator()

        generic_items = [(_('Clear Completed'), self.on_clear_completed),
                         (_('Start all'), self.on_start_all),
                         (_('Stop all'), self.on_stop_all)]

        [menu.addAction(title, callback) for title, callback in generic_items]

        menu.exec_(event.globalPos())

    def on_open_folder(self):
        rows = self.get_selected_rows()
        if rows:
            items_list = api.get_download_items(
                [self.items[row_index][0] for row_index in rows])
            paths_list = set(
                [download_item.path for download_item in items_list])
            for folder_path in paths_list:
                #misc.open_folder_window(folder_path)
                threading.Thread(group=None,
                                 target=misc.open_folder_window,
                                 name=None,
                                 args=(folder_path, )).start()

    def on_copy_link(self):
        rows = self.get_selected_rows()
        if rows:
            items_list = api.get_download_items(
                [self.items[row_index][0] for row_index in rows])
            links_list = [
                download_item.link for download_item in items_list
                if download_item.can_copy_link
            ]
            clipboard = QApplication.clipboard()
            clipboard.setText('\n'.join(links_list))

    def on_password(self):
        rows = self.get_selected_rows()
        if rows:
            pass
            #entry = gtk.Entry()
            #entry.add_events(gtk.gdk.KEY_RELEASE_MASK)
            #entry.set_width_chars(25) #entry width

            #m = DlgGui(self.__parent, None, _("Password"), None, True, append_widget=entry)

            #pwd = entry.get_text().strip()

            #if m.accepted and pwd:
            #events.trigger_pwd(pwd)

    def on_delete(self):
        rows = self.get_selected_rows()
        if rows:
            #message = _("Do you want to remove this download? (downloaded segments will be deleted)")
            #m = DlgGui(self.__parent, gtk.STOCK_DIALOG_WARNING, _("Remove Files"), message, True, True)
            m = True
            if m:
                id_items_list = []
                for row_index in rows:
                    id_item = self.items[row_index][0]
                    id_items_list.append(id_item)
                [self.remove_row(id_item) for id_item in id_items_list]
                api.delete_download(id_items_list)

    def on_clear_completed(self):
        finished_icon = self.icons_dict[cons.STATUS_FINISHED]
        for row in self.items[:]:
            if row[1] == finished_icon:
                self.remove_row(row[0])
                #todo: remove from complete_downloads

    def on_start_all(self):
        """
        BUG: El boton start y stop no cambia.
        """
        id_item_list = [row[0] for row in self.items]
        api.start_all(id_item_list)
        stopped_icon = self.icons_dict[cons.STATUS_STOPPED]
        queue_icon = self.icons_dict[cons.STATUS_QUEUE]
        for row in self.items:
            if row[1] == stopped_icon:
                row[1] = queue_icon

    def on_stop_all(self):
        api.stop_all()
        stopped_icon = self.icons_dict[cons.STATUS_STOPPED]
        queue_icon = self.icons_dict[cons.STATUS_QUEUE]
        for row in self.items:
            if row[1] == queue_icon:
                row[1] = stopped_icon

    def store_items(self, item_list):
        for download_item in item_list:
            size_file = misc.size_format(
                download_item.size) if download_item.size else None
            size_complete = misc.size_format(
                download_item.size_complete
            ) if download_item.size_complete else None
            time = misc.time_format(
                download_item.time) if download_item.time else None
            host_icon = self.get_host_icon(download_item.host)

            item = [
                download_item.id,
                self.icons_dict[download_item.status], download_item.name,
                [host_icon, None,
                 None], size_file, size_complete, download_item.progress, time,
                None, None, download_item.status_msg
            ]

            self.__model.append(item)
            self.rows_buffer[item[0]] = item

    def update(self):
        downloads_list = api.get_status()
        for download_item in downloads_list:
            try:
                row = self.rows_buffer[download_item.id]
                #row[0] = download_item.id #this column is hidden and wont be modificated.
                row[1] = self.icons_dict[download_item.status]  #col 1
                row[2] = download_item.name  #col 2
                #row[3][0] = download_item.host #download_item.host #col 3
                row[3][1] = self.icons_dict[
                    cons.
                    DL_RESUME] if download_item.can_resume else None  #download_item.host #col 3
                row[3][2] = self.icons_dict[
                    cons.
                    DL_PREMIUM] if download_item.is_premium else None  #download_item.host #col 3
                row[4] = misc.size_format(
                    download_item.size) if download_item.size else None
                row[5] = misc.size_format(
                    download_item.size_complete
                ) if download_item.size_complete else None
                row[6] = download_item.progress
                row[7] = misc.time_format(
                    download_item.time) if download_item.time else None
                row[8] = misc.time_format(
                    download_item.time_remain
                ) if download_item.time_remain else None
                row[9] = misc.speed_format(
                    download_item.speed) if download_item.speed else None
                row[10] = self.get_status_msg(download_item)
            except KeyError as err:
                logger.debug(err)
        #uncomment if model doesnt get upated.
        self.__model.refresh()

    def get_status_msg(self, download_item):
        if download_item.fail_count:
            return "{0} ({1} #{2})".format(download_item.status_msg,
                                           _("Retry"),
                                           download_item.fail_count)
        else:
            return download_item.status_msg

    def get_host_icon(self, host):
        try:
            return self.icons_dict[host]
        except KeyError:
            self.icons_dict[host] = QPixmap(
                os.path.join(cons.PLUGINS_PATH, host, "favicon.ico"))
            return self.icons_dict[host]

    def get_icons(self):
        running = media.get_pixmap(media.START, media.SMALL)
        stopped = media.get_pixmap(media.STOP, media.SMALL)
        queue = media.get_pixmap(media.QUEUE, media.SMALL)
        finished = media.get_pixmap(media.CHECK, media.SMALL)
        error = media.get_pixmap(media.X_MARK, media.SMALL)

        resume = media.get_pixmap(media.REFRESH, media.SMALL)
        premium = media.get_pixmap(media.ACCOUNTS, media.SMALL)

        return {
            cons.STATUS_RUNNING: running,
            cons.STATUS_STOPPED: stopped,
            cons.STATUS_QUEUE: queue,
            cons.STATUS_FINISHED: finished,
            cons.STATUS_ERROR: error,
            cons.DL_RESUME: resume,
            cons.DL_PREMIUM: premium
        }