File indexing completed on 2023-10-01 08:39:45

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2006 Dario Massarin <nekkar@libero.it>
0004    Copyright (C) 2009 Lukas Appelhans <l.appelhans@gmx.de>
0005 
0006    This program is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 */
0011 
0012 #include "transfersview.h"
0013 #include "core/kget.h"
0014 #include "core/transfertreemodel.h"
0015 #include "settings.h"
0016 #include "transferdetails.h"
0017 #include "transfersviewdelegate.h"
0018 
0019 #include "kget_debug.h"
0020 
0021 #include <KLocalizedString>
0022 #include <KRun>
0023 
0024 #include <QAction>
0025 #include <QDebug>
0026 #include <QDropEvent>
0027 #include <QGroupBox>
0028 #include <QHeaderView>
0029 #include <QMenu>
0030 #include <QSignalMapper>
0031 
0032 TransfersView::TransfersView(QWidget *parent)
0033     : QTreeView(parent)
0034 {
0035     //     setItemsExpandable(false);
0036     setRootIsDecorated(false);
0037     setAnimated(true);
0038     setAllColumnsShowFocus(true);
0039     header()->setDefaultAlignment(Qt::AlignCenter);
0040     header()->setMinimumSectionSize(80);
0041     header()->setContextMenuPolicy(Qt::CustomContextMenu);
0042     header()->setSectionsClickable(true);
0043     m_headerMenu = new QMenu(header());
0044 
0045     setSelectionMode(QAbstractItemView::ExtendedSelection);
0046     setDragEnabled(true);
0047     setAcceptDrops(true);
0048     setDropIndicatorShown(true);
0049     setEditTriggers(QAbstractItemView::NoEditTriggers);
0050     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
0051     setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
0052 
0053     connect(header(), &QWidget::customContextMenuRequested, this, &TransfersView::slotShowHeaderMenu);
0054     connect(header(), &QHeaderView::sectionCountChanged, this, &TransfersView::populateHeaderActions);
0055     connect(header(), &QHeaderView::sectionMoved, this, &TransfersView::slotSectionMoved);
0056     connect(header(), &QHeaderView::sectionResized, this, &TransfersView::slotSaveHeader);
0057     connect(this, &TransfersView::doubleClicked, this, &TransfersView::slotItemActivated);
0058     connect(this, &TransfersView::collapsed, this, &TransfersView::slotItemCollapsed);
0059     connect(KGet::model(), SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(closeExpandableDetails(QModelIndex, int, int)));
0060 }
0061 
0062 TransfersView::~TransfersView()
0063 {
0064 }
0065 
0066 void TransfersView::setModel(QAbstractItemModel *model)
0067 {
0068     QTreeView::setModel(model);
0069     int nGroups = model->rowCount(QModelIndex());
0070 
0071     for (int i = 0; i < nGroups; i++) {
0072         qCDebug(KGET_DEBUG) << "openEditor for row " << i;
0073         openPersistentEditor(model->index(i, TransferTreeModel::Status, QModelIndex()));
0074     }
0075 
0076     QByteArray loadedState = QByteArray::fromBase64(Settings::headerState().toLatin1());
0077     if (loadedState.isEmpty()) {
0078         setColumnWidth(0, 230);
0079     } else {
0080         header()->restoreState(loadedState);
0081     }
0082 
0083     // Workaround if the saved headerState is corrupted
0084     header()->setRootIndex(QModelIndex());
0085 
0086     populateHeaderActions();
0087     toggleMainGroup();
0088     connect(model, &QAbstractItemModel::rowsRemoved, this, &TransfersView::toggleMainGroup);
0089 }
0090 
0091 void TransfersView::dropEvent(QDropEvent *event)
0092 {
0093     QModelIndex dropIndex = indexAt(event->pos());
0094     QTreeView::dropEvent(event);
0095 
0096     setExpanded(dropIndex, true);
0097 }
0098 
0099 void TransfersView::rowsInserted(const QModelIndex &parent, int start, int end)
0100 {
0101     qCDebug(KGET_DEBUG) << "TransfersView::rowsInserted";
0102 
0103     if (!parent.isValid()) {
0104         qCDebug(KGET_DEBUG) << "parent is not valid " << start << "  " << end;
0105 
0106         for (int i = start; i <= end; i++) {
0107             qCDebug(KGET_DEBUG) << "openEditor for row " << i;
0108             openPersistentEditor(model()->index(i, TransferTreeModel::Status, parent));
0109         }
0110     }
0111 
0112     QTreeView::rowsInserted(parent, start, end);
0113 
0114     setExpanded(parent, true);
0115     toggleMainGroup();
0116 }
0117 
0118 void TransfersView::populateHeaderActions()
0119 {
0120     m_headerMenu->clear();
0121     m_headerMenu->addSection(i18n("Select columns"));
0122 
0123     auto *columnMapper = new QSignalMapper(this);
0124     connect(columnMapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped), this, &TransfersView::slotHideSection);
0125 
0126     // Create for each column an action with the column-header as name
0127     QVector<QAction *> orderedMenuItems(header()->count());
0128     for (int i = 0; i < header()->count(); ++i) {
0129         auto *action = new QAction(this);
0130         action->setText(model()->headerData(i, Qt::Horizontal).toString());
0131         action->setCheckable(true);
0132         action->setChecked(!header()->isSectionHidden(i));
0133         orderedMenuItems[header()->visualIndex(i)] = action;
0134 
0135         connect(action, SIGNAL(toggled(bool)), columnMapper, SLOT(map()));
0136         columnMapper->setMapping(action, i);
0137     }
0138 
0139     // append the sorted actions
0140     for (int i = 0; i < orderedMenuItems.count(); ++i) {
0141         m_headerMenu->addAction(orderedMenuItems[i]);
0142     }
0143 }
0144 
0145 void TransfersView::slotHideSection(int logicalIndex)
0146 {
0147     const bool hide = !header()->isSectionHidden(logicalIndex);
0148     header()->setSectionHidden(logicalIndex, hide);
0149     slotSaveHeader();
0150 }
0151 
0152 void TransfersView::slotSectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
0153 {
0154     Q_UNUSED(logicalIndex)
0155 
0156     // first item is the title, so increase the indexes by one
0157     ++oldVisualIndex;
0158     ++newVisualIndex;
0159     QList<QAction *> actions = m_headerMenu->actions();
0160 
0161     QAction *before = actions.last();
0162     if (newVisualIndex + 1 < actions.count()) {
0163         if (newVisualIndex > oldVisualIndex) {
0164             before = actions[newVisualIndex + 1];
0165         } else {
0166             before = actions[newVisualIndex];
0167         }
0168     }
0169 
0170     QAction *action = actions[oldVisualIndex];
0171     m_headerMenu->removeAction(action);
0172     m_headerMenu->insertAction(before, action);
0173     slotSaveHeader();
0174 }
0175 
0176 void TransfersView::slotSaveHeader()
0177 {
0178     Settings::setHeaderState(header()->saveState().toBase64());
0179     Settings::self()->save();
0180 }
0181 
0182 void TransfersView::dragMoveEvent(QDragMoveEvent *event)
0183 {
0184     Q_UNUSED(event)
0185 
0186     closeExpandableDetails();
0187     QTreeView::dragMoveEvent(event);
0188 }
0189 
0190 void TransfersView::slotItemActivated(const QModelIndex &index)
0191 {
0192     if (!index.isValid())
0193         return;
0194 
0195     TransferTreeModel *transferTreeModel = KGet::model();
0196     ModelItem *item = transferTreeModel->itemFromIndex(index);
0197     auto *view_delegate = static_cast<TransfersViewDelegate *>(itemDelegate());
0198 
0199     if (!item)
0200         return;
0201 
0202     if (!item->isGroup() && index.column() == 0) {
0203         if (!view_delegate->isExtended(index)) {
0204             TransferHandler *handler = item->asTransfer()->transferHandler();
0205             QWidget *widget = getDetailsWidgetForTransfer(handler);
0206 
0207             m_editingIndexes.append(index);
0208             view_delegate->extendItem(widget, index);
0209         } else {
0210             m_editingIndexes.removeAll(index);
0211             view_delegate->contractItem(index);
0212         }
0213         KGet::actionCollection()->action("transfer_show_details")->setChecked(view_delegate->isExtended(index));
0214     } else if (!item->isGroup() && static_cast<TransferModelItem *>(item)->transferHandler()->status() == Job::Finished) {
0215         new KRun(static_cast<TransferModelItem *>(item)->transferHandler()->dest(), this);
0216     }
0217 }
0218 
0219 void TransfersView::slotItemCollapsed(const QModelIndex &index)
0220 {
0221     if (!index.isValid())
0222         return;
0223 
0224     TransferTreeModel *transferTreeModel = KGet::model();
0225     ModelItem *item = transferTreeModel->itemFromIndex(index);
0226     auto *view_delegate = static_cast<TransfersViewDelegate *>(itemDelegate());
0227 
0228     if (!item)
0229         return;
0230 
0231     if (item->isGroup()) {
0232         TransferGroupHandler *groupHandler = item->asGroup()->groupHandler();
0233         QList<TransferHandler *> transfers = groupHandler->transfers();
0234 
0235         foreach (TransferHandler *transfer, transfers) {
0236             qCDebug(KGET_DEBUG) << "Transfer = " << transfer->source().toString();
0237             view_delegate->contractItem(KGet::model()->itemFromTransferHandler(transfer)->index());
0238         }
0239     }
0240 }
0241 
0242 void TransfersView::toggleMainGroup()
0243 {
0244     // show or hide the first group header if there's only one download group
0245     int nGroups = model()->rowCount(QModelIndex());
0246 
0247     if (nGroups <= 1) {
0248         setRootIndex(model()->index(0, 0, QModelIndex()));
0249     } else {
0250         setRootIndex(QModelIndex());
0251     }
0252     header()->setRootIndex(QModelIndex()); // HACK: else the header isn't visible with no visible items in the view
0253 }
0254 
0255 void TransfersView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
0256 {
0257     Q_UNUSED(parent)
0258     Q_UNUSED(start)
0259     Q_UNUSED(end)
0260 
0261     closeExpandableDetails(currentIndex());
0262 }
0263 
0264 void TransfersView::slotShowHeaderMenu(const QPoint &point)
0265 {
0266     m_headerMenu->popup(header()->mapToGlobal(point));
0267 }
0268 
0269 void TransfersView::closeExpandableDetails(const QModelIndex &transferIndex)
0270 {
0271     auto *view_delegate = static_cast<TransfersViewDelegate *>(itemDelegate());
0272 
0273     if (transferIndex.isValid()) {
0274         view_delegate->contractItem(transferIndex);
0275         m_editingIndexes.removeAll(transferIndex);
0276     } else {
0277         view_delegate->contractAll();
0278         m_editingIndexes.clear();
0279     }
0280 }
0281 
0282 void TransfersView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
0283 {
0284     Q_UNUSED(deselected)
0285     if (!selected.indexes().isEmpty()) {
0286         auto *view_delegate = static_cast<TransfersViewDelegate *>(itemDelegate());
0287         KGet::actionCollection()->action("transfer_show_details")->setChecked(view_delegate->isExtended(selected.indexes().first()));
0288     }
0289 
0290     QTreeView::selectionChanged(selected, deselected);
0291 }
0292 
0293 void TransfersView::closeExpandableDetails(const QModelIndex &parent, int rowStart, int rowEnd)
0294 {
0295     Q_UNUSED(parent)
0296     Q_UNUSED(rowStart)
0297     Q_UNUSED(rowEnd)
0298 
0299     auto *view_delegate = static_cast<TransfersViewDelegate *>(itemDelegate());
0300 
0301     view_delegate->contractAll();
0302     m_editingIndexes.clear();
0303 }
0304 
0305 QWidget *TransfersView::getDetailsWidgetForTransfer(TransferHandler *handler)
0306 {
0307     auto *groupBox = new QGroupBox(i18n("Transfer Details"));
0308 
0309     auto *layout = new QVBoxLayout(groupBox);
0310     QWidget *detailsWidget = TransferDetails::detailsWidget(handler);
0311     layout->addWidget(detailsWidget);
0312 
0313     return groupBox;
0314 }
0315 
0316 #include "moc_transfersview.cpp"