File indexing completed on 2023-10-01 08:39:44

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2002 Patrick Charbonnier <pch@freeshell.org>
0004    Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
0005    Copyright (C) 2008 Urs Wolfer <uwolfer @ kde.org>
0006 
0007    This program is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 */
0012 
0013 #include "ui/droptarget.h"
0014 
0015 #include "core/kget.h"
0016 #include "core/transfergrouphandler.h"
0017 #include "core/transferhandler.h"
0018 #include "core/transfertreemodel.h"
0019 #include "mainwindow.h"
0020 #include "settings.h"
0021 #include "ui/newtransferdialog.h"
0022 
0023 #include <KMessageBox>
0024 #include <KPassivePopup>
0025 #include <KWindowSystem>
0026 #include <kwidgetsaddons_version.h>
0027 
0028 #include <QBitmap>
0029 #include <QClipboard>
0030 #include <QDesktopWidget>
0031 #include <QGuiApplication>
0032 #include <QMenu>
0033 #include <QPainter>
0034 #include <QStringList>
0035 #include <QTimer>
0036 #include <QToolTip>
0037 
0038 #include <cmath>
0039 
0040 #define TARGET_SIZE 64
0041 #define TARGET_ANI_MS 20
0042 #define TARGET_TOOLTIP_MS 1000
0043 
0044 DropTarget::DropTarget(MainWindow *mw)
0045     : QWidget(nullptr, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint)
0046     , parentWidget(mw)
0047     , animTimer(nullptr)
0048     , showInformation(false)
0049 {
0050     KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::KeepAbove);
0051 
0052     QRect screenGeo = qApp->desktop()->screenGeometry(Settings::dropPosition());
0053     if ((screenGeo.x() + screenGeo.width() >= Settings::dropPosition().x() && screenGeo.y() + screenGeo.height() >= Settings::dropPosition().y())
0054         && Settings::dropPosition().y() >= 0 && Settings::dropPosition().x() >= 0)
0055         position = QPoint(Settings::dropPosition());
0056     else
0057         position = QPoint(screenGeo.x() + screenGeo.width() / 2, screenGeo.y() + screenGeo.height() / 2);
0058     setFixedSize(TARGET_SIZE, TARGET_SIZE);
0059 
0060     cachedPixmap = QIcon::fromTheme("kget").pixmap(TARGET_SIZE);
0061     if (!cachedPixmap.mask().isNull()) {
0062         QBitmap mask(size());
0063         mask.fill(Qt::color0);
0064         QBitmap pixMask = cachedPixmap.mask();
0065         QPainter p(&mask);
0066         p.drawPixmap((mask.width() - pixMask.width()) / 2, (mask.height() - pixMask.height()) / 2, pixMask);
0067         setMask(mask);
0068     } else
0069         setMask(QBitmap());
0070 
0071     // popup menu for right mouse button
0072     popupMenu = new QMenu(this);
0073     popupMenu->addSection(mw->windowTitle());
0074 
0075     QAction *downloadAction = mw->actionCollection()->action("start_all_download");
0076     popupMenu->addAction(downloadAction);
0077     connect(downloadAction, &QAction::toggled, this, &DropTarget::slotStartStopToggled);
0078     popupMenu->addSeparator();
0079     pop_show = popupMenu->addAction(QString(), this, &DropTarget::toggleMinimizeRestore);
0080     popupMenu->addAction(parentWidget->actionCollection()->action("show_drop_target"));
0081     pop_sticky = popupMenu->addAction(i18nc("fix position for droptarget", "Sticky"), this, &DropTarget::toggleSticky);
0082     pop_sticky->setCheckable(true);
0083     pop_sticky->setChecked(Settings::dropSticky());
0084     popupMenu->addSeparator();
0085     popupMenu->addAction(mw->actionCollection()->action("preferences"));
0086 
0087     auto *quitAction = new QAction(this);
0088     quitAction->setText(i18n("Quit KGet"));
0089     quitAction->setIcon(QIcon::fromTheme("system-shutdown"));
0090     connect(quitAction, SIGNAL(triggered()), mw, SLOT(slotQuit()));
0091     popupMenu->addAction(quitAction);
0092 
0093     isdragging = false;
0094 
0095     // Enable dropping
0096     setAcceptDrops(true);
0097 
0098     if (Settings::showDropTarget() && Settings::firstRun()) {
0099         showInformation = true;
0100     }
0101 
0102     animTimer = new QTimer(this);
0103     popupTimer = new QTimer(this);
0104 
0105     setMouseTracking(true);
0106 
0107     connect(KGet::model(), &TransferTreeModel::transfersChangedEvent, this, &DropTarget::slotToolTipUpdate);
0108 
0109     connect(popupTimer, &QTimer::timeout, this, &DropTarget::slotToolTipTimer);
0110 }
0111 
0112 DropTarget::~DropTarget()
0113 {
0114     Settings::setDropPosition(pos());
0115     Settings::setShowDropTarget(!isHidden());
0116     Settings::self()->save();
0117     //    unsigned long state = KWindowSystem::windowInfo(kdrop->winId()).state();
0118     //    // state will be 0L if droptarget is hidden. Sigh.
0119     //    config->writeEntry("State", state ? state : DEFAULT_DOCK_STATE );
0120 }
0121 
0122 void DropTarget::setDropTargetVisible(bool shown, bool internal)
0123 {
0124     if (shown == !isHidden())
0125         return;
0126 
0127     if (internal)
0128         Settings::setShowDropTarget(shown);
0129 
0130     if (!shown) {
0131         Settings::setDropPosition(pos());
0132         position = pos();
0133         if (Settings::animateDropTarget())
0134             playAnimationHide();
0135         else
0136             hide();
0137     } else {
0138         if (Settings::animateDropTarget()) {
0139             playAnimationShow();
0140         } else {
0141             move(position);
0142             show();
0143         }
0144         slotToolTipUpdate();
0145     }
0146 }
0147 
0148 void DropTarget::playAnimationShow()
0149 {
0150     if (animTimer->isActive())
0151         animTimer->stop();
0152     animTimer->disconnect();
0153     connect(animTimer, &QTimer::timeout, this, &DropTarget::slotAnimateShow);
0154 
0155     move(position.x(), -TARGET_SIZE);
0156 
0157     ani_y = -1;
0158     ani_vy = 0;
0159     show();
0160     animTimer->start(TARGET_ANI_MS);
0161 }
0162 
0163 void DropTarget::playAnimationHide()
0164 {
0165     if (animTimer->isActive())
0166         animTimer->stop();
0167 
0168     animTimer->disconnect();
0169     connect(animTimer, &QTimer::timeout, this, &DropTarget::slotAnimateHide);
0170     ani_y = (float)y();
0171     ani_vy = 0;
0172     animTimer->start(TARGET_ANI_MS);
0173 }
0174 
0175 void DropTarget::playAnimationSync()
0176 {
0177     if (animTimer->isActive())
0178         animTimer->stop();
0179 
0180     animTimer->disconnect();
0181     connect(animTimer, &QTimer::timeout, this, &DropTarget::slotAnimateSync);
0182     ani_y = (float)y();
0183     ani_vy = -1;
0184     animTimer->start(TARGET_ANI_MS);
0185 }
0186 
0187 void DropTarget::slotStartStopToggled(bool started)
0188 {
0189     if (started && Settings::animateDropTarget())
0190         playAnimationSync();
0191 }
0192 
0193 /** widget events */
0194 
0195 void DropTarget::dragEnterEvent(QDragEnterEvent *event)
0196 {
0197     event->setAccepted(event->mimeData()->hasUrls() || event->mimeData()->hasText());
0198 }
0199 
0200 void DropTarget::dropEvent(QDropEvent *event)
0201 {
0202     QList<QUrl> list = event->mimeData()->urls();
0203     QString str;
0204 
0205     if (!list.isEmpty()) {
0206         if (list.count() == 1 && list.first().url().endsWith(QLatin1String(".kgt"))) {
0207 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0208             const int msgBoxResult = KMessageBox::questionTwoActionsCancel(this,
0209 #else
0210             const int msgBoxResult = KMessageBox::questionYesNoCancel(this,
0211 #endif
0212                                                                            i18n("The dropped file is a KGet Transfer List"),
0213                                                                            "KGet",
0214                                                                            KGuiItem(i18n("&Download"), QIcon::fromTheme("document-save")),
0215                                                                            KGuiItem(i18n("&Load transfer list"), QIcon::fromTheme("list-add")),
0216                                                                            KStandardGuiItem::cancel());
0217 
0218 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0219             if (msgBoxResult == KMessageBox::PrimaryAction) // Download
0220 #else
0221             if (msgBoxResult == KMessageBox::Yes) // Download
0222 #endif
0223                 NewTransferDialogHandler::showNewTransferDialog(list.first());
0224 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0225             else if (msgBoxResult == KMessageBox::SecondaryAction) // Load
0226 #else
0227             else if (msgBoxResult == KMessageBox::No) // Load
0228 #endif
0229                 KGet::load(list.first().url());
0230         } else {
0231             if (list.count() == 1) {
0232                 str = event->mimeData()->text();
0233                 NewTransferDialogHandler::showNewTransferDialog(QUrl(str));
0234             } else
0235                 NewTransferDialogHandler::showNewTransferDialog(list);
0236         }
0237     } else {
0238         NewTransferDialogHandler::showNewTransferDialog();
0239     }
0240 
0241     if (Settings::animateDropTarget())
0242         playAnimationSync();
0243 }
0244 
0245 void DropTarget::closeEvent(QCloseEvent *e)
0246 {
0247     if (qApp->isSavingSession())
0248         e->ignore();
0249     else {
0250         setVisible(false);
0251         e->accept();
0252     }
0253 }
0254 
0255 void DropTarget::mousePressEvent(QMouseEvent *e)
0256 {
0257     // If the user click on the droptarget, stop any animation that is going on
0258     if (animTimer) {
0259         animTimer->stop();
0260     }
0261 
0262     if (e->button() == Qt::LeftButton) {
0263         isdragging = true;
0264         dx = e->globalPos().x() - pos().x();
0265         dy = e->globalPos().y() - pos().y();
0266     } else if (e->button() == Qt::RightButton) {
0267         pop_show->setText(parentWidget->isHidden() ? i18n("Show Main Window") : i18n("Hide Main Window"));
0268         popupMenu->popup(e->globalPos());
0269     } else if (e->button() == Qt::MidButton) {
0270         // Here we paste the transfer
0271         QString newtransfer = QApplication::clipboard()->text();
0272         newtransfer = newtransfer.trimmed();
0273 
0274         if (!newtransfer.isEmpty())
0275             KGet::addTransfer(QUrl(newtransfer), QString(), QString(), QString(), true);
0276     }
0277 }
0278 
0279 void DropTarget::mouseReleaseEvent(QMouseEvent *)
0280 {
0281     isdragging = false;
0282 }
0283 
0284 void DropTarget::mouseDoubleClickEvent(QMouseEvent *e)
0285 {
0286     if (e->button() == Qt::LeftButton)
0287         toggleMinimizeRestore();
0288 }
0289 
0290 void DropTarget::mouseMoveEvent(QMouseEvent *e)
0291 {
0292     Q_UNUSED(e)
0293     if (isdragging && !Settings::dropSticky()) {
0294         move(QCursor::pos().x() - dx, QCursor::pos().y() - dy);
0295         e->accept();
0296     }
0297 }
0298 
0299 void DropTarget::enterEvent(QEvent *event)
0300 {
0301     Q_UNUSED(event)
0302     popupTimer->start(2000);
0303 }
0304 
0305 void DropTarget::leaveEvent(QEvent *event)
0306 {
0307     Q_UNUSED(event)
0308     popupTimer->stop();
0309 }
0310 
0311 void DropTarget::paintEvent(QPaintEvent *)
0312 {
0313     QPainter p(this);
0314     p.drawPixmap(0, 0, cachedPixmap);
0315 }
0316 
0317 void DropTarget::toggleSticky()
0318 {
0319     Settings::setDropSticky(!Settings::dropSticky());
0320     pop_sticky->setChecked(Settings::dropSticky());
0321 }
0322 
0323 void DropTarget::toggleMinimizeRestore()
0324 {
0325     bool nextState = parentWidget->isHidden();
0326     Settings::setShowMain(nextState);
0327     parentWidget->setVisible(nextState);
0328     if (nextState) {
0329         KWindowSystem::activateWindow(static_cast<KXmlGuiWindow *>(parentWidget)->winId());
0330     }
0331 }
0332 
0333 /** widget animations */
0334 void DropTarget::slotAnimateShow()
0335 {
0336     static float dT = TARGET_ANI_MS / 1000.0;
0337 
0338     ani_vy -= ani_y * 30 * dT;
0339     ani_vy *= 0.95;
0340     ani_y += ani_vy * dT;
0341 
0342     move(x(), qRound(position.y() * (1 + ani_y)));
0343 
0344     if (fabs(ani_y) < 0.01 && fabs(ani_vy) < 0.01 && animTimer->isActive()) {
0345         animTimer->stop();
0346 
0347         if (showInformation)
0348             KPassivePopup::message(i18n("Drop Target"), i18n("You can drag download links into the drop target."), this);
0349     }
0350 }
0351 
0352 void DropTarget::slotAnimateHide()
0353 {
0354     static float dT = TARGET_ANI_MS / 1000.0;
0355 
0356     ani_vy += -2000 * dT;
0357     float new_y = y() + ani_vy * dT;
0358 
0359     if (new_y < -height()) {
0360         animTimer->stop();
0361         hide();
0362         move(x(), qRound(ani_y));
0363     } else
0364         move(x(), qRound(new_y));
0365 }
0366 
0367 void DropTarget::slotAnimateSync()
0368 {
0369     static float dT = TARGET_ANI_MS / 1000.0;
0370 
0371     ani_vy += 4 * dT; // from -1 to 1 in 0.5 seconds
0372     float i = 2 * M_PI * ani_vy; // from -2PI to 2PI
0373     float j = (i == 0.0) ? 1 : (sin(i) / i) * (1 + fabs(ani_vy));
0374 
0375     if (ani_vy >= 1) {
0376         animTimer->stop();
0377         move(x(), qRound(ani_y));
0378     } else
0379         move(x(), qRound(ani_y + 6 * j));
0380 }
0381 
0382 void DropTarget::slotToolTipUpdate()
0383 {
0384     QStringList dataList;
0385     QString data;
0386 
0387     foreach (TransferHandler *transfer, KGet::allTransfers()) {
0388         data.clear();
0389         switch (transfer->status()) {
0390         case Job::Finished:
0391             data = i18nc("%1 filename, %2 total size, %3 status",
0392                          "%1(%2) %3",
0393                          transfer->source().fileName(),
0394                          KIO::convertSize(transfer->totalSize()),
0395                          transfer->statusText());
0396             break;
0397         case Job::Running:
0398             data = i18nc("%1 filename, %2 percent complete, %3 downloaded out of %4 total size",
0399                          "%1(%2% %3/%4) Speed:%5/s",
0400                          transfer->source().fileName(),
0401                          transfer->percent(),
0402                          KIO::convertSize(transfer->downloadedSize()),
0403                          KIO::convertSize(transfer->totalSize()),
0404                          KIO::convertSize(transfer->downloadSpeed()));
0405             break;
0406         default:
0407             data = i18nc("%1 filename, %2 percent complete, %3 downloaded out of %4 total size, %5 status",
0408                          "%1(%2% %3/%4) %5",
0409                          transfer->source().fileName(),
0410                          transfer->percent(),
0411                          KIO::convertSize(transfer->downloadedSize()),
0412                          KIO::convertSize(transfer->totalSize()),
0413                          transfer->statusText());
0414             break;
0415         }
0416         dataList << data;
0417     }
0418 
0419     if (!dataList.empty())
0420         tooltipText = dataList.join("\n");
0421     else
0422         tooltipText = i18n("Ready");
0423 }
0424 
0425 void DropTarget::slotToolTipTimer()
0426 {
0427     if (!popupMenu->isVisible() && isVisible() && mask().contains(mapFromGlobal(QCursor::pos())))
0428         QToolTip::showText(QCursor::pos(), tooltipText, this, rect());
0429 }
0430 
0431 void DropTarget::slotClose()
0432 {
0433     setVisible(false);
0434 }
0435 
0436 #include "moc_droptarget.cpp"