File indexing completed on 2024-04-28 04:37:27

0001 /*
0002     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "statusbar.h"
0008 #include "progresswidget/statusbarprogresswidget.h"
0009 #include "progresswidget/progressmanager.h"
0010 #include "progresswidget/progressdialog.h"
0011 
0012 #include <QTimer>
0013 
0014 #include <KColorScheme>
0015 #include <KSqueezedTextLabel>
0016 
0017 #include <interfaces/istatus.h>
0018 #include <interfaces/ilanguagecontroller.h>
0019 #include <language/backgroundparser/backgroundparser.h>
0020 
0021 #include <sublime/view.h>
0022 
0023 #include "plugincontroller.h"
0024 #include "core.h"
0025 
0026 namespace KDevelop
0027 {
0028 
0029 StatusBar::StatusBar(QWidget* parent)
0030     : QStatusBar(parent)
0031     , m_timer(new QTimer(this))
0032     , m_currentView(nullptr)
0033 {
0034 #ifdef Q_OS_MAC
0035     /* At time of writing this is only required for OSX and only has effect on OSX. 
0036        ifdef for robustness to future platform dependent theme/widget changes
0037        https://phabricator.kde.org/D656 
0038     */
0039     setStyleSheet(QStringLiteral("QStatusBar{background:transparent;}"));
0040 #endif
0041 
0042     m_timer->setSingleShot(true);
0043     connect(m_timer, &QTimer::timeout, this, &StatusBar::slotTimeout);
0044     connect(Core::self()->pluginController(), &IPluginController::pluginLoaded, this, &StatusBar::pluginLoaded);
0045     const QList<IPlugin*> plugins = Core::self()->pluginControllerInternal()->allPluginsForExtension(QStringLiteral("IStatus"));
0046 
0047     for (IPlugin* plugin : plugins) {
0048         registerStatus(plugin);
0049     }
0050 
0051     registerStatus(Core::self()->languageController()->backgroundParser());
0052 
0053     m_progressController = Core::self()->progressController();
0054     m_progressDialog = new ProgressDialog(this, parent); // construct this first, then progressWidget
0055     m_progressDialog->setVisible(false);
0056     m_progressWidget = new StatusbarProgressWidget(m_progressDialog, this);
0057 
0058     addPermanentWidget(m_progressWidget, 0);
0059 }
0060 
0061 StatusBar::~StatusBar() = default;
0062 
0063 void StatusBar::removeError(QWidget* w)
0064 {
0065     removeWidget(w);
0066     w->deleteLater();
0067 }
0068 
0069 void StatusBar::viewChanged(Sublime::View* view)
0070 {
0071     if (m_currentView)
0072         m_currentView->disconnect(this);
0073 
0074     m_currentView = view;
0075 
0076     if (view) {
0077         connect(view, &Sublime::View::statusChanged, this, &StatusBar::viewStatusChanged);
0078         QStatusBar::showMessage(view->viewStatus(), 0);
0079 
0080     }
0081 }
0082 
0083 void StatusBar::viewStatusChanged(Sublime::View* view)
0084 {
0085     QStatusBar::showMessage(view->viewStatus(), 0);
0086 }
0087 
0088 void StatusBar::pluginLoaded(IPlugin* plugin)
0089 {
0090     if (qobject_cast<IStatus*>(plugin))
0091         registerStatus(plugin);
0092 }
0093 
0094 void StatusBar::registerStatus(QObject* status)
0095 {
0096     Q_ASSERT(qobject_cast<IStatus*>(status));
0097     // can't convert this to new signal slot syntax, IStatus is not a QObject
0098     connect(status, SIGNAL(clearMessage(KDevelop::IStatus*)),
0099             SLOT(clearMessage(KDevelop::IStatus*)));
0100     connect(status, SIGNAL(showMessage(KDevelop::IStatus*,QString,int)),
0101             SLOT(showMessage(KDevelop::IStatus*,QString,int)));
0102     connect(status, SIGNAL(hideProgress(KDevelop::IStatus*)),
0103             SLOT(hideProgress(KDevelop::IStatus*)));
0104     connect(status, SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)),
0105             SLOT(showProgress(KDevelop::IStatus*,int,int,int)));
0106     connect(status, SIGNAL(showErrorMessage(QString,int)),
0107             SLOT(showErrorMessage(QString,int)));
0108 }
0109 
0110 QWidget* errorMessage(QWidget* parent, const QString& text)
0111 {
0112     auto* label = new KSqueezedTextLabel(parent);
0113     KStatefulBrush red(KColorScheme::Window, KColorScheme::NegativeText);
0114     QPalette pal = label->palette();
0115     pal.setBrush(QPalette::WindowText, red.brush(label->palette()));
0116     label->setPalette(pal);
0117     label->setAlignment(Qt::AlignRight);
0118     label->setText(text);
0119     label->setToolTip(text);
0120     return label;
0121 }
0122 
0123 QTimer* StatusBar::errorTimeout(QWidget* error, int timeout)
0124 {
0125     auto* timer = new QTimer(error);
0126     timer->setSingleShot(true);
0127     timer->setInterval(1000*timeout);
0128     connect(timer, &QTimer::timeout, this, [this, error](){ removeError(error); });
0129     return timer;
0130 }
0131 
0132 void StatusBar::showErrorMessage(const QString& message, int timeout)
0133 {
0134     QWidget* error = errorMessage(this, message);
0135     QTimer* timer = errorTimeout(error, timeout);
0136     addWidget(error);
0137     timer->start(); // triggers removeError()
0138 }
0139 
0140 void StatusBar::slotTimeout()
0141 {
0142     QMutableHashIterator<IStatus*, Message> it = m_messages;
0143 
0144     while (it.hasNext()) {
0145         it.next();
0146         if (it.value().timeout) {
0147             it.value().timeout -= m_timer->interval();
0148             if (it.value().timeout == 0)
0149                 it.remove();
0150         }
0151     }
0152 
0153     updateMessage();
0154 }
0155 
0156 void StatusBar::updateMessage()
0157 {
0158     if (m_timer->isActive()) {
0159         m_timer->stop();
0160         m_timer->setInterval(m_time.elapsed());
0161         slotTimeout();
0162     }
0163 
0164     int timeout = 0;
0165 
0166     QStringList messages;
0167     messages.reserve(m_messages.size());
0168     for (const Message& m : qAsConst(m_messages)) {
0169         messages.append(m.text);
0170 
0171         if (timeout)
0172             timeout = qMin(timeout, m.timeout);
0173         else
0174             timeout = m.timeout;
0175     }
0176 
0177     if (!messages.isEmpty())
0178         QStatusBar::showMessage(messages.join(QLatin1String("; ")));
0179     else
0180         QStatusBar::clearMessage();
0181 
0182     if (timeout) {
0183         m_time.start();
0184         m_timer->start(timeout);
0185     }
0186 }
0187 
0188 void StatusBar::clearMessage( IStatus* status )
0189 {
0190     QTimer::singleShot(0, this, [this, status]() {
0191         const auto messageIt = m_messages.find(status);
0192         if (messageIt != m_messages.end()) {
0193             m_messages.erase(messageIt);
0194             updateMessage();
0195         }
0196     });
0197 }
0198 
0199 void StatusBar::showMessage( IStatus* status, const QString & message, int timeout)
0200 {
0201     QPointer<QObject> context = dynamic_cast<QObject*>(status);
0202     QTimer::singleShot(0, this, [this, context, status, message, timeout]() {
0203         if (!context)
0204             return;
0205         const auto progressItemIt = m_progressItems.constFind(status);
0206         if (progressItemIt != m_progressItems.constEnd()) {
0207             ProgressItem* i = *progressItemIt;
0208             i->setStatus(message);
0209         } else {
0210             Message m;
0211             m.text = message;
0212             m.timeout = timeout;
0213             m_messages.insert(status, m);
0214             updateMessage();
0215         }
0216     });
0217 }
0218 
0219 void StatusBar::hideProgress( IStatus* status )
0220 {
0221     QTimer::singleShot(0, this, [this, status]() {
0222         const auto progressItemIt = m_progressItems.find(status);
0223         if (progressItemIt != m_progressItems.end()) {
0224             (*progressItemIt)->setComplete();
0225             m_progressItems.erase(progressItemIt);
0226         }
0227     });
0228 }
0229 
0230 void StatusBar::showProgress( IStatus* status, int minimum, int maximum, int value)
0231 {
0232     QPointer<QObject> context = dynamic_cast<QObject*>(status);
0233     QTimer::singleShot(0, this, [this, context, status, minimum, maximum, value]() {
0234         if (!context)
0235             return;
0236         auto progressItemIt = m_progressItems.find(status);
0237         if (progressItemIt == m_progressItems.end()) {
0238             bool canBeCanceled = false;
0239             progressItemIt = m_progressItems.insert(status, m_progressController->createProgressItem(
0240                 ProgressManager::createUniqueID(), status->statusName(), QString(), canBeCanceled));
0241         }
0242 
0243         ProgressItem* i = *progressItemIt;
0244         m_progressWidget->raise();
0245         m_progressDialog->raise();
0246         if( minimum == 0 && maximum == 0 ) {
0247             i->setUsesBusyIndicator( true );
0248         } else {
0249             i->setUsesBusyIndicator( false );
0250             i->setProgress( 100*value/maximum );
0251         }
0252     });
0253 }
0254 
0255 }
0256 
0257 #include "moc_statusbar.cpp"