File indexing completed on 2024-05-12 05:47:52

0001 /*
0002  * SPDX-FileCopyrightText: 2008 Konstantin Heil <konst.heil@stud.uni-heidelberg.de>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "tooltipmanager.h"
0008 
0009 #include "dolphinfilemetadatawidget.h"
0010 
0011 #include <KConfigGroup>
0012 #include <KIO/PreviewJob>
0013 #include <KIconLoader>
0014 #include <KJobWidgets>
0015 #include <KSharedConfig>
0016 #include <KToolTipWidget>
0017 
0018 #include <QApplication>
0019 #include <QStyle>
0020 #include <QTimer>
0021 #include <QWindow>
0022 
0023 class IconLoaderSingleton
0024 {
0025 public:
0026     IconLoaderSingleton() = default;
0027 
0028     KIconLoader self;
0029 };
0030 
0031 Q_GLOBAL_STATIC(IconLoaderSingleton, iconLoader)
0032 
0033 ToolTipManager::ToolTipManager(QWidget *parent)
0034     : QObject(parent)
0035     , m_showToolTipTimer(nullptr)
0036     , m_contentRetrievalTimer(nullptr)
0037     , m_transientParent(nullptr)
0038     , m_toolTipRequested(false)
0039     , m_metaDataRequested(false)
0040     , m_appliedWaitCursor(false)
0041     , m_margin(4)
0042     , m_item()
0043     , m_itemRect()
0044 {
0045     if (parent) {
0046         m_margin = qMax(m_margin, parent->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth));
0047     }
0048 
0049     m_showToolTipTimer = new QTimer(this);
0050     m_showToolTipTimer->setSingleShot(true);
0051     m_showToolTipTimer->setInterval(500);
0052     connect(m_showToolTipTimer, &QTimer::timeout, this, QOverload<>::of(&ToolTipManager::showToolTip));
0053 
0054     m_contentRetrievalTimer = new QTimer(this);
0055     m_contentRetrievalTimer->setSingleShot(true);
0056     m_contentRetrievalTimer->setInterval(200);
0057     connect(m_contentRetrievalTimer, &QTimer::timeout, this, &ToolTipManager::startContentRetrieval);
0058 
0059     Q_ASSERT(m_contentRetrievalTimer->interval() < m_showToolTipTimer->interval());
0060 }
0061 
0062 ToolTipManager::~ToolTipManager()
0063 {
0064     if (!m_fileMetaDatWidgetOwnershipTransferred) {
0065         delete m_fileMetaDataWidget;
0066     }
0067 }
0068 
0069 void ToolTipManager::showToolTip(const KFileItem &item, const QRectF &itemRect, QWindow *transientParent)
0070 {
0071     hideToolTip(HideBehavior::Instantly);
0072 
0073     m_itemRect = itemRect.toRect();
0074 
0075     m_itemRect.adjust(-m_margin, -m_margin, m_margin, m_margin);
0076     m_item = item;
0077 
0078     m_transientParent = transientParent;
0079 
0080     // Only start the retrieving of the content, when the mouse has been over this
0081     // item for 200 milliseconds. This prevents a lot of useless preview jobs and
0082     // meta data retrieval, when passing rapidly over a lot of items.
0083     if (!m_fileMetaDataWidget) {
0084         m_fileMetaDataWidget = new DolphinFileMetaDataWidget();
0085         connect(m_fileMetaDataWidget, &DolphinFileMetaDataWidget::metaDataRequestFinished, this, &ToolTipManager::slotMetaDataRequestFinished);
0086         connect(m_fileMetaDataWidget, &DolphinFileMetaDataWidget::urlActivated, this, &ToolTipManager::urlActivated);
0087     }
0088 
0089     m_contentRetrievalTimer->start();
0090     m_showToolTipTimer->start();
0091     m_toolTipRequested = true;
0092     Q_ASSERT(!m_metaDataRequested);
0093 }
0094 
0095 void ToolTipManager::hideToolTip(const HideBehavior behavior)
0096 {
0097     if (m_appliedWaitCursor) {
0098         QApplication::restoreOverrideCursor();
0099         m_appliedWaitCursor = false;
0100     }
0101 
0102     m_toolTipRequested = false;
0103     m_metaDataRequested = false;
0104     m_showToolTipTimer->stop();
0105     m_contentRetrievalTimer->stop();
0106     if (m_tooltipWidget) {
0107         switch (behavior) {
0108         case HideBehavior::Instantly:
0109             m_tooltipWidget->hide();
0110             break;
0111         case HideBehavior::Later:
0112             m_tooltipWidget->hideLater();
0113             break;
0114         }
0115     }
0116 }
0117 
0118 void ToolTipManager::startContentRetrieval()
0119 {
0120     if (!m_toolTipRequested) {
0121         return;
0122     }
0123 
0124     m_fileMetaDataWidget->setName(m_item.text());
0125 
0126     // Request the retrieval of meta-data. The slot
0127     // slotMetaDataRequestFinished() is invoked after the
0128     // meta-data have been received.
0129     m_metaDataRequested = true;
0130     m_fileMetaDataWidget->setItems(KFileItemList() << m_item);
0131     m_fileMetaDataWidget->adjustSize();
0132 
0133     // Request a preview of the item
0134     m_fileMetaDataWidget->setPreview(QPixmap());
0135 
0136     const KConfigGroup globalConfig(KSharedConfig::openConfig(), QLatin1String("PreviewSettings"));
0137     const QStringList plugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins());
0138     KIO::PreviewJob *job = new KIO::PreviewJob(KFileItemList() << m_item, QSize(256, 256), &plugins);
0139     job->setIgnoreMaximumSize(m_item.isLocalFile() && !m_item.isSlow());
0140     if (job->uiDelegate()) {
0141         KJobWidgets::setWindow(job, qApp->activeWindow());
0142     }
0143 
0144     connect(job, &KIO::PreviewJob::gotPreview, this, &ToolTipManager::setPreviewPix);
0145     connect(job, &KIO::PreviewJob::failed, this, &ToolTipManager::previewFailed);
0146 }
0147 
0148 void ToolTipManager::setPreviewPix(const KFileItem &item, const QPixmap &pixmap)
0149 {
0150     if (!m_toolTipRequested || (m_item.url() != item.url())) {
0151         // No tooltip is requested anymore or an old preview has been received
0152         return;
0153     }
0154 
0155     if (pixmap.isNull()) {
0156         previewFailed();
0157     } else {
0158         m_fileMetaDataWidget->setPreview(pixmap);
0159         if (!m_showToolTipTimer->isActive()) {
0160             showToolTip();
0161         }
0162     }
0163 }
0164 
0165 void ToolTipManager::previewFailed()
0166 {
0167     if (!m_toolTipRequested) {
0168         return;
0169     }
0170     QPalette pal;
0171     for (auto state : {QPalette::Active, QPalette::Inactive, QPalette::Disabled}) {
0172         pal.setBrush(state, QPalette::WindowText, pal.toolTipText());
0173         pal.setBrush(state, QPalette::Window, pal.toolTipBase());
0174     }
0175     iconLoader->self.setCustomPalette(pal);
0176     const QPixmap pixmap = KDE::icon(m_item.iconName(), &iconLoader->self).pixmap(128, 128);
0177     m_fileMetaDataWidget->setPreview(pixmap);
0178     if (!m_showToolTipTimer->isActive()) {
0179         showToolTip();
0180     }
0181 }
0182 
0183 void ToolTipManager::slotMetaDataRequestFinished()
0184 {
0185     if (!m_toolTipRequested) {
0186         return;
0187     }
0188 
0189     m_metaDataRequested = false;
0190 
0191     if (!m_showToolTipTimer->isActive()) {
0192         showToolTip();
0193     }
0194 }
0195 
0196 void ToolTipManager::showToolTip()
0197 {
0198     Q_ASSERT(m_toolTipRequested);
0199     if (m_appliedWaitCursor) {
0200         QApplication::restoreOverrideCursor();
0201         m_appliedWaitCursor = false;
0202     }
0203 
0204     if (m_fileMetaDataWidget->preview().isNull() || m_metaDataRequested) {
0205         Q_ASSERT(!m_appliedWaitCursor);
0206         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0207         m_appliedWaitCursor = true;
0208         return;
0209     }
0210 
0211     // Adjust the size to get a proper sizeHint()
0212     m_fileMetaDataWidget->adjustSize();
0213     if (!m_tooltipWidget) {
0214         m_tooltipWidget.reset(new KToolTipWidget());
0215     }
0216     m_tooltipWidget->showBelow(m_itemRect, m_fileMetaDataWidget, m_transientParent);
0217     // At this point KToolTipWidget adopted our parent-less metadata widget.
0218     m_fileMetaDatWidgetOwnershipTransferred = true;
0219 
0220     m_toolTipRequested = false;
0221 }
0222 
0223 #include "moc_tooltipmanager.cpp"