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