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) 2007 by Javier Goday <jgoday@gmail.com> 0005 Copyright (C) 2008 Lukas Appelhans <l.appelhans@gmx.de> 0006 Copyright (C) 2010 Matthias Fuchs <mat69@gmx.net> 0007 0008 This program is free software; you can redistribute it and/or 0009 modify it under the terms of the GNU General Public 0010 License as published by the Free Software Foundation; either 0011 version 2 of the License, or (at your option) any later version. 0012 */ 0013 0014 #include "ui/transfersviewdelegate.h" 0015 0016 #include "core/kget.h" 0017 #include "core/transfergrouphandler.h" 0018 #include "core/transferhandler.h" 0019 #include "core/transfertreemodel.h" 0020 #include "core/transfertreeselectionmodel.h" 0021 #include "settings.h" 0022 #include "transferdetails.h" 0023 #include "ui/contextmenu.h" 0024 0025 #include "kget_debug.h" 0026 #include <KLocalizedString> 0027 0028 #include <QAbstractItemView> 0029 #include <QApplication> 0030 #include <QButtonGroup> 0031 #include <QDebug> 0032 #include <QHBoxLayout> 0033 #include <QIcon> 0034 #include <QMenu> 0035 #include <QMouseEvent> 0036 #include <QPainter> 0037 0038 GroupStatusButton::GroupStatusButton(const QModelIndex &index, QWidget *parent) 0039 : QToolButton(parent) 0040 , m_status(None) 0041 , m_index(index) 0042 , m_timerId(-1) 0043 , m_iconSize(22) 0044 , m_gradientId(0) 0045 { 0046 setAttribute(Qt::WA_NoSystemBackground); 0047 } 0048 0049 void GroupStatusButton::checkStateSet() 0050 { 0051 // qCDebug(KGET_DEBUG) << "GroupStatusButton::checkStateSet"; 0052 0053 QToolButton::checkStateSet(); 0054 0055 if (isChecked()) { 0056 if (m_status == None) 0057 m_gradientId = 0.9; 0058 m_status = Selecting; 0059 } else { 0060 if (m_status == None) 0061 m_gradientId = 1; 0062 m_status = Deselecting; 0063 } 0064 0065 setMouseTracking(!isChecked()); 0066 0067 if (m_timerId == -1) 0068 m_timerId = startTimer(100); 0069 } 0070 0071 void GroupStatusButton::enterEvent(QEnterEvent *event) 0072 { 0073 Q_UNUSED(event) 0074 if (!isChecked()) { 0075 m_status = Blinking; 0076 0077 if (m_timerId == -1) { 0078 m_timerId = startTimer(100); 0079 0080 if (m_status == !BlinkingExiting) 0081 m_gradientId = 1; 0082 } 0083 } 0084 } 0085 0086 void GroupStatusButton::leaveEvent(QEvent *event) 0087 { 0088 Q_UNUSED(event) 0089 if (m_status == Blinking) 0090 m_status = BlinkingExiting; 0091 } 0092 0093 void GroupStatusButton::paintEvent(QPaintEvent *event) 0094 { 0095 Q_UNUSED(event) 0096 0097 QPainter p(this); 0098 0099 const int offset = (rect().width() - m_iconSize) / 2; 0100 0101 if (m_gradientId == 0) 0102 m_gradientId = isChecked() ? 1 : 0.7; 0103 0104 QRadialGradient gradient(height() / 2.0, height() / 2.0, height() / 2); 0105 0106 QPen pen; 0107 0108 if (KGet::selectionModel()->isSelected(m_index)) { 0109 gradient.setColorAt(0, palette().color(QPalette::AlternateBase)); 0110 gradient.setColorAt(m_gradientId, Qt::transparent); 0111 gradient.setColorAt(1, Qt::transparent); 0112 pen.setColor(palette().color(QPalette::AlternateBase)); 0113 } else { 0114 gradient.setColorAt(0, palette().color(QPalette::Highlight)); 0115 gradient.setColorAt(m_gradientId, Qt::transparent); 0116 gradient.setColorAt(1, Qt::transparent); 0117 pen.setColor(palette().color(QPalette::Highlight)); 0118 } 0119 0120 QRect r = rect().adjusted(0, 0, 0, 1); 0121 0122 p.fillRect(r, gradient); 0123 0124 p.setRenderHint(QPainter::Antialiasing); 0125 0126 if (isChecked()) { 0127 pen.setWidth(1); 0128 p.setPen(pen); 0129 p.drawEllipse(rect().x() + 5, rect().y() + 4, rect().width() - 10, rect().width() - 10); 0130 } 0131 0132 p.drawPixmap(rect().topLeft() + QPoint(offset, offset - 1), 0133 icon().pixmap(m_iconSize, isChecked() || m_status == Blinking ? QIcon::Normal : QIcon::Disabled)); 0134 } 0135 0136 void GroupStatusButton::timerEvent(QTimerEvent *event) 0137 { 0138 Q_UNUSED(event) 0139 0140 if (m_status == Selecting) { 0141 m_gradientId += 0.04; 0142 0143 if (m_gradientId >= 1) { 0144 m_status = None; 0145 m_gradientId = 1; 0146 killTimer(m_timerId); 0147 m_timerId = -1; 0148 } 0149 } else if (m_status == Deselecting) { 0150 m_gradientId -= 0.04; 0151 0152 if (m_gradientId <= 0.7) { 0153 m_status = None; 0154 m_gradientId = 0.7; 0155 killTimer(m_timerId); 0156 m_timerId = -1; 0157 } 0158 } else if (m_status == Blinking) { 0159 if (isChecked()) { 0160 m_status = Selecting; 0161 m_gradientId = 0.9; 0162 return; 0163 } 0164 0165 m_gradientId -= 0.04; 0166 0167 if (m_gradientId <= 0.7) { 0168 m_gradientId = 1; 0169 } 0170 } else if (m_status == BlinkingExiting) { 0171 m_gradientId -= 0.04; 0172 0173 if (m_gradientId <= 0.7) { 0174 m_status = None; 0175 m_gradientId = 0.7; 0176 killTimer(m_timerId); 0177 m_timerId = -1; 0178 } 0179 } 0180 0181 update(); 0182 } 0183 0184 GroupStatusEditor::GroupStatusEditor(const QModelIndex &index, QWidget *parent) 0185 : QWidget(parent) 0186 , m_index(index) 0187 { 0188 setMinimumWidth(80); 0189 0190 m_layout = new QHBoxLayout(); 0191 m_layout->addStretch(); 0192 setLayout(m_layout); 0193 0194 m_btGroup = new QButtonGroup(this); 0195 m_btGroup->setExclusive(true); 0196 0197 m_startBt = new GroupStatusButton(m_index, this); 0198 m_startBt->setCheckable(true); 0199 m_startBt->setAutoRaise(true); 0200 m_startBt->setIcon(QIcon::fromTheme("media-playback-start")); 0201 m_startBt->setFixedSize(36, 36); 0202 m_startBt->setIconSize(QSize(22, 22)); 0203 m_layout->addWidget(m_startBt); 0204 m_btGroup->addButton(m_startBt); 0205 0206 m_stopBt = new GroupStatusButton(m_index, this); 0207 m_stopBt->setCheckable(true); 0208 m_stopBt->setAutoRaise(true); 0209 m_stopBt->setIcon(QIcon::fromTheme("media-playback-pause")); 0210 m_stopBt->setFixedSize(36, 36); 0211 m_stopBt->setIconSize(QSize(22, 22)); 0212 m_layout->addWidget(m_stopBt); 0213 m_btGroup->addButton(m_stopBt); 0214 0215 m_stopBt->setChecked(true); 0216 0217 m_layout->addStretch(); 0218 m_layout->setContentsMargins(1, 1, 1, 1); 0219 0220 connect(m_startBt, &GroupStatusButton::toggled, this, &GroupStatusEditor::slotStatusChanged); 0221 } 0222 0223 void GroupStatusEditor::setRunning(bool running) 0224 { 0225 if (running == m_startBt->isChecked()) 0226 return; 0227 0228 if (running) 0229 m_startBt->setChecked(true); 0230 else 0231 m_stopBt->setChecked(true); 0232 } 0233 0234 bool GroupStatusEditor::isRunning() 0235 { 0236 return m_startBt->isChecked(); 0237 } 0238 0239 void GroupStatusEditor::slotStatusChanged() 0240 { 0241 Q_EMIT changedStatus(this); 0242 } 0243 0244 BasicTransfersViewDelegate::BasicTransfersViewDelegate(QAbstractItemView *parent) 0245 : KExtendableItemDelegate(parent) 0246 { 0247 } 0248 0249 QWidget *BasicTransfersViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const 0250 { 0251 if (index.column() == TransferTreeModel::Status) { 0252 auto *qroupStatusEditor = new GroupStatusEditor(index, parent); 0253 connect(qroupStatusEditor, &GroupStatusEditor::changedStatus, this, &BasicTransfersViewDelegate::slotGroupStatusChanged); 0254 return qroupStatusEditor; 0255 } else { 0256 return KExtendableItemDelegate::createEditor(parent, option, index); 0257 } 0258 } 0259 0260 void BasicTransfersViewDelegate::slotGroupStatusChanged(GroupStatusEditor *editor) 0261 { 0262 commitData(editor); 0263 } 0264 0265 void BasicTransfersViewDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const 0266 { 0267 if (index.column() == TransferTreeModel::Status) { 0268 auto *groupEditor = static_cast<GroupStatusEditor *>(editor); 0269 groupEditor->setRunning(KGet::model()->itemFromIndex(index)->asGroup()->groupHandler()->status() == JobQueue::Running); 0270 } else { 0271 KExtendableItemDelegate::setEditorData(editor, index); 0272 } 0273 } 0274 0275 void BasicTransfersViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const 0276 { 0277 if (index.column() == TransferTreeModel::Status) { 0278 auto *groupEditor = static_cast<GroupStatusEditor *>(editor); 0279 TransferGroupHandler *groupHandler = KGet::model()->itemFromIndex(index)->asGroup()->groupHandler(); 0280 0281 if (groupEditor->isRunning()) { 0282 groupHandler->start(); 0283 } else { 0284 groupHandler->stop(); 0285 } 0286 } else { 0287 KExtendableItemDelegate::setModelData(editor, model, index); 0288 } 0289 } 0290 0291 TransfersViewDelegate::TransfersViewDelegate(QAbstractItemView *parent) 0292 : BasicTransfersViewDelegate(parent) 0293 { 0294 Q_ASSERT(qobject_cast<QAbstractItemView *>(parent)); 0295 setExtendPixmap(QIcon::fromTheme("arrow-right").pixmap(16)); 0296 setContractPixmap(QIcon::fromTheme("arrow-down").pixmap(16)); 0297 } 0298 0299 void TransfersViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0300 { 0301 TransferTreeModel *transferTreeModel = KGet::model(); 0302 0303 ModelItem *item = transferTreeModel->itemFromIndex(index); 0304 0305 if (item->isGroup()) { 0306 painter->save(); 0307 0308 if (option.state & QStyle::State_Selected) { 0309 } else { 0310 static bool backgroundInitialized = false; 0311 static QPixmap groupBackground(64, 35); 0312 static QPalette palette(QApplication::palette()); 0313 0314 if (!backgroundInitialized || palette != QApplication::palette()) { 0315 const QRect rect = groupBackground.rect(); 0316 QPainter p(&groupBackground); 0317 0318 QLinearGradient gradient(rect.x(), rect.y(), rect.x(), (rect.y() + rect.height())); 0319 0320 gradient.setColorAt(0, QApplication::palette().color(QPalette::Base)); 0321 gradient.setColorAt(0.5, QApplication::palette().color(QPalette::AlternateBase).darker(110)); 0322 gradient.setColorAt(1, QApplication::palette().color(QPalette::Base)); 0323 0324 p.fillRect(rect, gradient); 0325 backgroundInitialized = true; 0326 } 0327 0328 painter->drawTiledPixmap(option.rect, groupBackground); 0329 } 0330 0331 KExtendableItemDelegate::paint(painter, option, index); 0332 0333 painter->restore(); 0334 } else { 0335 if (KGet::selectionModel()->isSelected(index)) 0336 painter->fillRect(option.rect, 0337 QApplication::palette().color(option.state & QStyle::State_Active ? QPalette::Active : QPalette::Inactive, QPalette::Highlight)); 0338 0339 KExtendableItemDelegate::paint(painter, option, index); 0340 0341 if (index.column() == 3 && !isExtended(transferTreeModel->index(index.row(), 0, index.parent()))) { // the percent column 0342 TransferHandler *transferHandler = item->asTransfer()->transferHandler(); 0343 0344 // following progressbar code has mostly been taken from Qt4 examples/network/torrent/mainview.cpp 0345 // Set up a QStyleOptionProgressBar to precisely mimic the 0346 // environment of a progress bar. 0347 QStyleOptionProgressBar progressBarOption; 0348 progressBarOption.state = QStyle::State_Enabled | QStyle::State_Horizontal; 0349 progressBarOption.direction = QApplication::layoutDirection(); 0350 progressBarOption.rect = option.rect; 0351 progressBarOption.fontMetrics = QApplication::fontMetrics(); 0352 progressBarOption.minimum = 0; 0353 progressBarOption.maximum = 100; 0354 progressBarOption.textAlignment = Qt::AlignCenter; 0355 progressBarOption.textVisible = true; 0356 0357 // Set the progress and text values of the style option. 0358 int progress = transferHandler->percent(); 0359 if (progress >= 0 && progress <= 100) { 0360 progressBarOption.progress = progress; 0361 progressBarOption.text = QStringLiteral("%1%").arg(progressBarOption.progress); 0362 } else { 0363 progressBarOption.text = i18nc("not available", "n/a"); 0364 } 0365 0366 progressBarOption.rect.setY(progressBarOption.rect.y() + (option.rect.height() - QApplication::fontMetrics().height()) / 2); 0367 progressBarOption.rect.setHeight(QApplication::fontMetrics().height()); 0368 0369 // Draw the progress bar onto the view. 0370 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); 0371 } 0372 } 0373 0374 /// These lines are just for testing purposes. Uncomment them to show on the view the repaint events. 0375 // static int i=0; 0376 // qCDebug(KGET_DEBUG) << "paint!!! " << i++ << " " << index.internalPointer() << " " << index.column(); 0377 // 0378 // painter->drawRect(option.rect); 0379 // painter->drawText(option.rect.topLeft(), QString::number(i)); 0380 } 0381 0382 void TransfersViewDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const 0383 { 0384 Q_UNUSED(painter) 0385 Q_UNUSED(option) 0386 Q_UNUSED(rect) 0387 } 0388 0389 QSize TransfersViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0390 { 0391 Q_UNUSED(option) 0392 0393 TransferTreeModel *transferTreeModel = KGet::model(); 0394 ModelItem *item = transferTreeModel->itemFromIndex(index); 0395 0396 if (!item) { 0397 qCWarning(KGET_DEBUG) << "Sizehint for non-existing item."; 0398 return QSize(); 0399 } 0400 0401 if (transferTreeModel->itemFromIndex(index)->isGroup()) { 0402 return QSize(0, 35); 0403 } else { 0404 QSize ret(KExtendableItemDelegate::sizeHint(option, index)); 0405 ret.rheight() += 8; 0406 return ret; 0407 } 0408 } 0409 0410 bool TransfersViewDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) 0411 { 0412 Q_UNUSED(model) 0413 Q_UNUSED(option) 0414 0415 if (event->type() == QEvent::MouseButtonRelease) { 0416 auto *mouseEvent = static_cast<QMouseEvent *>(event); 0417 if (mouseEvent->button() == Qt::RightButton) { 0418 // qCDebug(KGET_DEBUG) << "TransfersViewDelegate::editorEvent() -> rightClick"; 0419 0420 QMenu *popup = nullptr; 0421 0422 TransferTreeModel *transferTreeModel = KGet::model(); 0423 0424 ModelItem *item = transferTreeModel->itemFromIndex(index); 0425 if (item->isGroup()) { 0426 // qCDebug(KGET_DEBUG) << "isTransferGroup = true"; 0427 TransferGroupHandler *transferGroupHandler = item->asGroup()->groupHandler(); 0428 0429 popup = ContextMenu::createTransferGroupContextMenu(transferGroupHandler, qobject_cast<QWidget *>(this)); 0430 } else { 0431 // qCDebug(KGET_DEBUG) << "isTransferGroup = false"; 0432 0433 TransferHandler *transferHandler = item->asTransfer()->transferHandler(); 0434 0435 popup = ContextMenu::createTransferContextMenu(transferHandler, qobject_cast<QWidget *>(this)); 0436 } 0437 0438 if (popup) { 0439 popup->exec(QCursor::pos()); 0440 popup->deleteLater(); 0441 } 0442 } 0443 } 0444 0445 return false; 0446 } 0447 0448 #include "moc_transfersviewdelegate.cpp"