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"