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