File indexing completed on 2024-04-21 04:57:23

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"