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"