1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 **************************************************************************/
19
20 #include "TransfersWidget.h"
21 #include "../../../core/Application.h"
22 #include "../../../core/ThemesManager.h"
23 #include "../../../core/TransfersManager.h"
24 #include "../../../core/Utils.h"
25 #include "../../../ui/Action.h"
26 #include "../../../ui/ProgressBarWidget.h"
27
28 #include <QtCore/QDir>
29 #include <QtCore/QFileInfo>
30 #include <QtCore/QtMath>
31 #include <QtGui/QMouseEvent>
32 #include <QtWidgets/QBoxLayout>
33 #include <QtWidgets/QFileIconProvider>
34 #include <QtWidgets/QFrame>
35 #include <QtWidgets/QMenu>
36 #include <QtWidgets/QToolTip>
37 #include <QtWidgets/QWidgetAction>
38
39 namespace Otter
40 {
41
TransfersWidget(const ToolBarsManager::ToolBarDefinition::Entry & definition,QWidget * parent)42 TransfersWidget::TransfersWidget(const ToolBarsManager::ToolBarDefinition::Entry &definition, QWidget *parent) : ToolButtonWidget(definition, parent),
43 m_icon(ThemesManager::createIcon(QLatin1String("transfers")))
44 {
45 setMenu(new QMenu(this));
46 setPopupMode(QToolButton::InstantPopup);
47 setToolTip(tr("Downloads"));
48 updateState();
49
50 connect(TransfersManager::getInstance(), &TransfersManager::transferChanged, this, &TransfersWidget::updateState);
51 connect(TransfersManager::getInstance(), &TransfersManager::transferStarted, this, [&](Transfer *transfer)
52 {
53 if ((!transfer->isArchived() || transfer->getState() == Transfer::RunningState) && menu()->isVisible())
54 {
55 QAction *firstAction(menu()->actions().value(0));
56 QWidgetAction *widgetAction(new QWidgetAction(menu()));
57 widgetAction->setDefaultWidget(new TransferActionWidget(transfer, menu()));
58
59 menu()->insertAction(firstAction, widgetAction);
60 menu()->insertSeparator(firstAction);
61 }
62
63 updateState();
64 });
65 connect(TransfersManager::getInstance(), &TransfersManager::transferFinished, this, [&](Transfer *transfer)
66 {
67 const QList<QAction*> actions(menu()->actions());
68
69 for (int i = 0; i < actions.count(); ++i)
70 {
71 const QWidgetAction *widgetAction(qobject_cast<QWidgetAction*>(actions.at(i)));
72
73 if (widgetAction && widgetAction->defaultWidget())
74 {
75 const TransferActionWidget *transferActionWidget(qobject_cast<TransferActionWidget*>(widgetAction->defaultWidget()));
76
77 if (transferActionWidget && transferActionWidget->getTransfer() == transfer)
78 {
79 menu()->removeAction(actions.at(i));
80 menu()->removeAction(actions.value(i + 1));
81
82 break;
83 }
84 }
85 }
86
87 updateState();
88 });
89 connect(TransfersManager::getInstance(), &TransfersManager::transferRemoved, this, &TransfersWidget::updateState);
90 connect(TransfersManager::getInstance(), &TransfersManager::transferStopped, this, &TransfersWidget::updateState);
91 connect(menu(), &QMenu::aboutToShow, this, &TransfersWidget::populateMenu);
92 connect(menu(), &QMenu::aboutToHide, menu(), &QMenu::clear);
93 }
94
changeEvent(QEvent * event)95 void TransfersWidget::changeEvent(QEvent *event)
96 {
97 ToolButtonWidget::changeEvent(event);
98
99 if (event->type() == QEvent::LanguageChange)
100 {
101 setToolTip(tr("Downloads"));
102 }
103 }
104
populateMenu()105 void TransfersWidget::populateMenu()
106 {
107 const QVector<Transfer*> transfers(TransfersManager::getInstance()->getTransfers());
108
109 for (int i = 0; i < transfers.count(); ++i)
110 {
111 Transfer *transfer(transfers.at(i));
112
113 if (!transfer->isArchived() || transfer->getState() == Transfer::RunningState)
114 {
115 QWidgetAction *widgetAction(new QWidgetAction(menu()));
116 widgetAction->setDefaultWidget(new TransferActionWidget(transfer, menu()));
117
118 menu()->addAction(widgetAction);
119 menu()->addSeparator();
120 }
121 }
122
123 menu()->addAction(new Action(ActionsManager::TransfersAction, {}, {{QLatin1String("text"), tr("Show all Downloads")}}, ActionExecutor::Object(Application::getInstance(), Application::getInstance()), this));
124 }
125
updateState()126 void TransfersWidget::updateState()
127 {
128 const QVector<Transfer*> transfers(TransfersManager::getInstance()->getTransfers());
129 qint64 bytesTotal(0);
130 qint64 bytesReceived(0);
131 qint64 transferAmount(0);
132
133 for (int i = 0; i < transfers.count(); ++i)
134 {
135 Transfer *transfer(transfers.at(i));
136
137 if (transfer->getState() == Transfer::RunningState && transfer->getBytesTotal() > 0)
138 {
139 ++transferAmount;
140
141 bytesTotal += transfer->getBytesTotal();
142 bytesReceived += transfer->getBytesReceived();
143 }
144 }
145
146 setIcon(getIcon());
147 }
148
getIcon() const149 QIcon TransfersWidget::getIcon() const
150 {
151 return m_icon;
152 }
153
TransferActionWidget(Transfer * transfer,QWidget * parent)154 TransferActionWidget::TransferActionWidget(Transfer *transfer, QWidget *parent) : QWidget(parent),
155 m_transfer(transfer),
156 m_detailsLabel(new QLabel(this)),
157 m_fileNameLabel(new QLabel(this)),
158 m_iconLabel(new QLabel(this)),
159 m_progressBar(new ProgressBarWidget(this)),
160 m_toolButton(new QToolButton(this)),
161 m_centralWidget(new QWidget(this))
162 {
163 QVBoxLayout *centralLayout(new QVBoxLayout(m_centralWidget));
164 centralLayout->setContentsMargins(0, 0, 0, 0);
165 centralLayout->addWidget(m_fileNameLabel);
166 centralLayout->addWidget(m_progressBar);
167 centralLayout->addWidget(m_detailsLabel);
168
169 QFrame *leftSeparatorFrame(new QFrame(this));
170 leftSeparatorFrame->setFrameShape(QFrame::VLine);
171
172 QFrame *rightSeparatorFrame(new QFrame(this));
173 rightSeparatorFrame->setFrameShape(QFrame::VLine);
174
175 QHBoxLayout *mainLayout(new QHBoxLayout(this));
176 mainLayout->addWidget(m_iconLabel);
177 mainLayout->addWidget(leftSeparatorFrame);
178 mainLayout->addWidget(m_centralWidget);
179 mainLayout->addWidget(rightSeparatorFrame);
180 mainLayout->addWidget(m_toolButton);
181
182 setLayout(mainLayout);
183 updateState();
184
185 m_iconLabel->setFixedSize(32, 32);
186 m_progressBar->setMode(ProgressBarWidget::ThinMode);
187 m_toolButton->setIconSize({16, 16});
188 m_toolButton->setAutoRaise(true);
189
190 connect(transfer, &Transfer::changed, this, &TransferActionWidget::updateState);
191 connect(transfer, &Transfer::finished, this, &TransferActionWidget::updateState);
192 connect(transfer, &Transfer::stopped, this, &TransferActionWidget::updateState);
193 connect(transfer, &Transfer::progressChanged, this, &TransferActionWidget::updateState);
194 connect(m_toolButton, &QToolButton::clicked, [&]()
195 {
196 switch (m_transfer->getState())
197 {
198 case Transfer::CancelledState:
199 case Transfer::ErrorState:
200 m_transfer->restart();
201
202 break;
203 case Transfer::FinishedState:
204 Utils::runApplication({}, QUrl::fromLocalFile(QFileInfo(m_transfer->getTarget()).dir().canonicalPath()));
205
206 break;
207 default:
208 m_transfer->stop();
209
210 break;
211 }
212 });
213 }
214
mousePressEvent(QMouseEvent * event)215 void TransferActionWidget::mousePressEvent(QMouseEvent *event)
216 {
217 event->accept();
218 }
219
mouseReleaseEvent(QMouseEvent * event)220 void TransferActionWidget::mouseReleaseEvent(QMouseEvent *event)
221 {
222 event->accept();
223
224 if (event->button() == Qt::LeftButton)
225 {
226 m_transfer->openTarget();
227 }
228 }
229
updateState()230 void TransferActionWidget::updateState()
231 {
232 const QString iconName(m_transfer->getMimeType().iconName());
233 QString details;
234 QVector<QPair<QString, QString> > detailsValues({{tr("From:"), Utils::extractHost(m_transfer->getSource())}});
235 const bool isIndeterminate(m_transfer->getBytesTotal() <= 0);
236 const bool hasError(m_transfer->getState() == Transfer::UnknownState || m_transfer->getState() == Transfer::ErrorState);
237
238 if (m_transfer->getState() == Transfer::FinishedState)
239 {
240 detailsValues.append({tr("Size:"), tr("%1 (download completed)").arg(Utils::formatUnit(m_transfer->getBytesTotal()))});
241 }
242 else
243 {
244 detailsValues.append({tr("Size:"), tr("%1 (%2% downloaded)").arg(Utils::formatUnit(m_transfer->getBytesTotal())).arg(Utils::calculatePercent(m_transfer->getBytesReceived(), m_transfer->getBytesTotal()), 0, 'f', 1)});
245 }
246
247 for (int i = 0; i < detailsValues.count(); ++i)
248 {
249 details.append(detailsValues.at(i).first + QLatin1Char(' ') + detailsValues.at(i).second);
250
251 if (i < (detailsValues.count() - 1))
252 {
253 details.append(QLatin1String("<br>"));
254 }
255 }
256
257 m_fileNameLabel->setText(Utils::elideText(QFileInfo(m_transfer->getTarget()).fileName(), m_fileNameLabel->fontMetrics(), nullptr, 300));
258 m_detailsLabel->setText(QLatin1String("<small>") + details + QLatin1String("</small>"));
259 m_iconLabel->setPixmap(QIcon::fromTheme(iconName, QFileIconProvider().icon(iconName)).pixmap(32, 32));
260 m_progressBar->setHasError(hasError);
261 m_progressBar->setRange(0, ((isIndeterminate && !hasError) ? 0 : 100));
262 m_progressBar->setValue(isIndeterminate ? (hasError ? 0 : -1) : ((m_transfer->getBytesTotal() > 0) ? qFloor(Utils::calculatePercent(m_transfer->getBytesReceived(), m_transfer->getBytesTotal())) : -1));
263 m_progressBar->setFormat(isIndeterminate ? tr("Unknown") : QLatin1String("%p%"));
264
265 switch (m_transfer->getState())
266 {
267 case Transfer::CancelledState:
268 case Transfer::ErrorState:
269 m_toolButton->setIcon(ThemesManager::createIcon(QLatin1String("view-refresh")));
270 m_toolButton->setToolTip(tr("Redownload"));
271
272 break;
273 case Transfer::FinishedState:
274 m_toolButton->setIcon(ThemesManager::createIcon(QLatin1String("document-open-folder")));
275 m_toolButton->setToolTip(tr("Open Folder"));
276
277 break;
278 default:
279 m_toolButton->setIcon(ThemesManager::createIcon(QLatin1String("task-reject")));
280 m_toolButton->setToolTip(tr("Stop"));
281
282 break;
283 }
284 }
285
getTransfer() const286 Transfer* TransferActionWidget::getTransfer() const
287 {
288 return m_transfer;
289 }
290
event(QEvent * event)291 bool TransferActionWidget::event(QEvent *event)
292 {
293 if (event->type() == QEvent::ToolTip)
294 {
295 const bool isIndeterminate(m_transfer->getBytesTotal() <= 0);
296
297 QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tr("<div style=\"white-space:pre;\">Source: %1\nTarget: %2\nSize: %3\nDownloaded: %4\nProgress: %5</div>").arg(m_transfer->getSource().toDisplayString().toHtmlEscaped()).arg(m_transfer->getTarget().toHtmlEscaped()).arg(isIndeterminate ? tr("Unknown") : Utils::formatUnit(m_transfer->getBytesTotal(), false, 1, true)).arg(Utils::formatUnit(m_transfer->getBytesReceived(), false, 1, true)).arg(isIndeterminate ? tr("Unknown") : QStringLiteral("%1%").arg(Utils::calculatePercent(m_transfer->getBytesReceived(), m_transfer->getBytesTotal()), 0, 'f', 1)));
298
299 return true;
300 }
301
302 return QWidget::event(event);
303 }
304
305 }
306