1 /*
2 SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "statusbar.h"
8 #include "progresswidget/statusbarprogresswidget.h"
9 #include "progresswidget/progressmanager.h"
10 #include "progresswidget/progressdialog.h"
11
12 #include <QTimer>
13
14 #include <KColorScheme>
15 #include <KSqueezedTextLabel>
16
17 #include <interfaces/istatus.h>
18 #include <interfaces/ilanguagecontroller.h>
19 #include <language/backgroundparser/backgroundparser.h>
20
21 #include <sublime/view.h>
22
23 #include "plugincontroller.h"
24 #include "core.h"
25
26 namespace KDevelop
27 {
28
StatusBar(QWidget * parent)29 StatusBar::StatusBar(QWidget* parent)
30 : QStatusBar(parent)
31 , m_timer(new QTimer(this))
32 , m_currentView(nullptr)
33 {
34 #ifdef Q_OS_MAC
35 /* At time of writing this is only required for OSX and only has effect on OSX.
36 ifdef for robustness to future platform dependent theme/widget changes
37 https://phabricator.kde.org/D656
38 */
39 setStyleSheet(QStringLiteral("QStatusBar{background:transparent;}"));
40 #endif
41
42 m_timer->setSingleShot(true);
43 connect(m_timer, &QTimer::timeout, this, &StatusBar::slotTimeout);
44 connect(Core::self()->pluginController(), &IPluginController::pluginLoaded, this, &StatusBar::pluginLoaded);
45 const QList<IPlugin*> plugins = Core::self()->pluginControllerInternal()->allPluginsForExtension(QStringLiteral("IStatus"));
46
47 for (IPlugin* plugin : plugins) {
48 registerStatus(plugin);
49 }
50
51 registerStatus(Core::self()->languageController()->backgroundParser());
52
53 m_progressController = Core::self()->progressController();
54 m_progressDialog = new ProgressDialog(this, parent); // construct this first, then progressWidget
55 m_progressDialog->setVisible(false);
56 m_progressWidget = new StatusbarProgressWidget(m_progressDialog, this);
57
58 addPermanentWidget(m_progressWidget, 0);
59 }
60
61 StatusBar::~StatusBar() = default;
62
removeError(QWidget * w)63 void StatusBar::removeError(QWidget* w)
64 {
65 removeWidget(w);
66 w->deleteLater();
67 }
68
viewChanged(Sublime::View * view)69 void StatusBar::viewChanged(Sublime::View* view)
70 {
71 if (m_currentView)
72 m_currentView->disconnect(this);
73
74 m_currentView = view;
75
76 if (view) {
77 connect(view, &Sublime::View::statusChanged, this, &StatusBar::viewStatusChanged);
78 QStatusBar::showMessage(view->viewStatus(), 0);
79
80 }
81 }
82
viewStatusChanged(Sublime::View * view)83 void StatusBar::viewStatusChanged(Sublime::View* view)
84 {
85 QStatusBar::showMessage(view->viewStatus(), 0);
86 }
87
pluginLoaded(IPlugin * plugin)88 void StatusBar::pluginLoaded(IPlugin* plugin)
89 {
90 if (qobject_cast<IStatus*>(plugin))
91 registerStatus(plugin);
92 }
93
registerStatus(QObject * status)94 void StatusBar::registerStatus(QObject* status)
95 {
96 Q_ASSERT(qobject_cast<IStatus*>(status));
97 // can't convert this to new signal slot syntax, IStatus is not a QObject
98 connect(status, SIGNAL(clearMessage(KDevelop::IStatus*)),
99 SLOT(clearMessage(KDevelop::IStatus*)));
100 connect(status, SIGNAL(showMessage(KDevelop::IStatus*,QString,int)),
101 SLOT(showMessage(KDevelop::IStatus*,QString,int)));
102 connect(status, SIGNAL(hideProgress(KDevelop::IStatus*)),
103 SLOT(hideProgress(KDevelop::IStatus*)));
104 connect(status, SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)),
105 SLOT(showProgress(KDevelop::IStatus*,int,int,int)));
106 connect(status, SIGNAL(showErrorMessage(QString,int)),
107 SLOT(showErrorMessage(QString,int)));
108 }
109
errorMessage(QWidget * parent,const QString & text)110 QWidget* errorMessage(QWidget* parent, const QString& text)
111 {
112 auto* label = new KSqueezedTextLabel(parent);
113 KStatefulBrush red(KColorScheme::Window, KColorScheme::NegativeText);
114 QPalette pal = label->palette();
115 pal.setBrush(QPalette::WindowText, red.brush(label->palette()));
116 label->setPalette(pal);
117 label->setAlignment(Qt::AlignRight);
118 label->setText(text);
119 label->setToolTip(text);
120 return label;
121 }
122
errorTimeout(QWidget * error,int timeout)123 QTimer* StatusBar::errorTimeout(QWidget* error, int timeout)
124 {
125 auto* timer = new QTimer(error);
126 timer->setSingleShot(true);
127 timer->setInterval(1000*timeout);
128 connect(timer, &QTimer::timeout, this, [this, error](){ removeError(error); });
129 return timer;
130 }
131
showErrorMessage(const QString & message,int timeout)132 void StatusBar::showErrorMessage(const QString& message, int timeout)
133 {
134 QWidget* error = errorMessage(this, message);
135 QTimer* timer = errorTimeout(error, timeout);
136 addWidget(error);
137 timer->start(); // triggers removeError()
138 }
139
slotTimeout()140 void StatusBar::slotTimeout()
141 {
142 QMutableHashIterator<IStatus*, Message> it = m_messages;
143
144 while (it.hasNext()) {
145 it.next();
146 if (it.value().timeout) {
147 it.value().timeout -= m_timer->interval();
148 if (it.value().timeout == 0)
149 it.remove();
150 }
151 }
152
153 updateMessage();
154 }
155
updateMessage()156 void StatusBar::updateMessage()
157 {
158 if (m_timer->isActive()) {
159 m_timer->stop();
160 m_timer->setInterval(m_time.elapsed());
161 slotTimeout();
162 }
163
164 int timeout = 0;
165
166 QStringList messages;
167 messages.reserve(m_messages.size());
168 for (const Message& m : qAsConst(m_messages)) {
169 messages.append(m.text);
170
171 if (timeout)
172 timeout = qMin(timeout, m.timeout);
173 else
174 timeout = m.timeout;
175 }
176
177 if (!messages.isEmpty())
178 QStatusBar::showMessage(messages.join(QLatin1String("; ")));
179 else
180 QStatusBar::clearMessage();
181
182 if (timeout) {
183 m_time.start();
184 m_timer->start(timeout);
185 }
186 }
187
clearMessage(IStatus * status)188 void StatusBar::clearMessage( IStatus* status )
189 {
190 QTimer::singleShot(0, this, [this, status]() {
191 const auto messageIt = m_messages.find(status);
192 if (messageIt != m_messages.end()) {
193 m_messages.erase(messageIt);
194 updateMessage();
195 }
196 });
197 }
198
showMessage(IStatus * status,const QString & message,int timeout)199 void StatusBar::showMessage( IStatus* status, const QString & message, int timeout)
200 {
201 QPointer<QObject> context = dynamic_cast<QObject*>(status);
202 QTimer::singleShot(0, this, [this, context, status, message, timeout]() {
203 if (!context)
204 return;
205 const auto progressItemIt = m_progressItems.constFind(status);
206 if (progressItemIt != m_progressItems.constEnd()) {
207 ProgressItem* i = *progressItemIt;
208 i->setStatus(message);
209 } else {
210 Message m;
211 m.text = message;
212 m.timeout = timeout;
213 m_messages.insert(status, m);
214 updateMessage();
215 }
216 });
217 }
218
hideProgress(IStatus * status)219 void StatusBar::hideProgress( IStatus* status )
220 {
221 QTimer::singleShot(0, this, [this, status]() {
222 const auto progressItemIt = m_progressItems.find(status);
223 if (progressItemIt != m_progressItems.end()) {
224 (*progressItemIt)->setComplete();
225 m_progressItems.erase(progressItemIt);
226 }
227 });
228 }
229
showProgress(IStatus * status,int minimum,int maximum,int value)230 void StatusBar::showProgress( IStatus* status, int minimum, int maximum, int value)
231 {
232 QPointer<QObject> context = dynamic_cast<QObject*>(status);
233 QTimer::singleShot(0, this, [this, context, status, minimum, maximum, value]() {
234 if (!context)
235 return;
236 auto progressItemIt = m_progressItems.find(status);
237 if (progressItemIt == m_progressItems.end()) {
238 bool canBeCanceled = false;
239 progressItemIt = m_progressItems.insert(status, m_progressController->createProgressItem(
240 ProgressManager::createUniqueID(), status->statusName(), QString(), canBeCanceled));
241 }
242
243 ProgressItem* i = *progressItemIt;
244 m_progressWidget->raise();
245 m_progressDialog->raise();
246 if( minimum == 0 && maximum == 0 ) {
247 i->setUsesBusyIndicator( true );
248 } else {
249 i->setUsesBusyIndicator( false );
250 i->setProgress( 100*value/maximum );
251 }
252 });
253 }
254
255 }
256
257