1 /*
2 KSysGuard, the KDE System Guard
3
4 Copyright (c) 1999, 2000, 2001 Chris Schlaeger <cs@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <QCheckBox>
24 #include <QDebug>
25 #include <QDomElement>
26 #include <QPushButton>
27 #include <QHBoxLayout>
28
29 #include <KLocalizedString>
30 #include <ksgrd/SensorManager.h>
31 #include "StyleEngine.h"
32
33 #include "BarGraph.h"
34 #include "DancingBarsSettings.h"
35
36 #include "DancingBars.h"
37
DancingBars(QWidget * parent,const QString & title,SharedSettings * workSheetSettings)38 DancingBars::DancingBars( QWidget *parent, const QString &title, SharedSettings *workSheetSettings)
39 : KSGRD::SensorDisplay( parent, title, workSheetSettings)
40 {
41 mBars = 0;
42 mFlags = QBitArray( 100 );
43 mFlags.fill( false );
44
45 QLayout *layout = new QHBoxLayout(this);
46 mPlotter = new BarGraph( this );
47 layout->addWidget(mPlotter);
48
49 setMinimumSize( sizeHint() );
50
51 /* All RMB clicks to the mPlotter widget will be handled by
52 * SensorDisplay::eventFilter. */
53 mPlotter->installEventFilter( this );
54
55 setPlotterWidget( mPlotter );
56
57 }
58
~DancingBars()59 DancingBars::~DancingBars()
60 {
61 }
62
configureSettings()63 void DancingBars::configureSettings()
64 {
65 DancingBarsSettings dlg( this );
66
67 dlg.setTitle( title() );
68 dlg.setMinValue( mPlotter->getMin() );
69 dlg.setMaxValue( mPlotter->getMax() );
70
71 double l, u;
72 bool la, ua;
73 mPlotter->getLimits( l, la, u, ua );
74
75 dlg.setUseUpperLimit( ua );
76 dlg.setUpperLimit( u );
77
78 dlg.setUseLowerLimit( la );
79 dlg.setLowerLimit( l );
80
81 dlg.setForegroundColor( mPlotter->normalColor );
82 dlg.setAlarmColor( mPlotter->alarmColor );
83 dlg.setBackgroundColor( mPlotter->mBackgroundColor );
84 dlg.setFontSize( mPlotter->fontSize );
85
86 SensorModelEntry::List list;
87 for(int i = 0; i < mBars; i++){
88 SensorModelEntry entry;
89 auto sensor = sensors().at( i );
90 entry.setId( i );
91 entry.setHostName( sensor->hostName() );
92 entry.setSensorName( KSGRD::SensorMgr->translateSensor( sensor->name() ) );
93 entry.setLabel( mPlotter->footers[ i ] );
94 entry.setUnit( KSGRD::SensorMgr->translateUnit( sensor->unit() ) );
95 entry.setStatus( sensor->isOk() ? i18n( "OK" ) : i18n( "Error" ) );
96
97 list.append( entry );
98 }
99 dlg.setSensors( list );
100
101 if ( !dlg.exec() )
102 return;
103
104 setTitle( dlg.title() );
105 mPlotter->changeRange( dlg.minValue(), dlg.maxValue() );
106 mPlotter->setLimits( dlg.useLowerLimit() ?
107 dlg.lowerLimit() : 0,
108 dlg.useLowerLimit(),
109 dlg.useUpperLimit() ?
110 dlg.upperLimit() : 0,
111 dlg.useUpperLimit() );
112
113 mPlotter->normalColor = dlg.foregroundColor();
114 mPlotter->alarmColor = dlg.alarmColor();
115 mPlotter->mBackgroundColor = dlg.backgroundColor();
116 mPlotter->fontSize = dlg.fontSize();
117
118 // Each deleted Id is relative to the length of the list
119 // at the time of deletion.
120 QList<uint> deletedIds = dlg.getDeletedIds();
121 for(int i = 0; i<deletedIds.count(); i++){
122 removeSensor(deletedIds[i]);
123 }
124
125 // If the range has reset to "auto-range" then we need to ask for
126 // sensor info to re-calibrate. In answerReceived() there's a special-
127 // case recalibrating on sensor 0 (with id 100), so ask for that one.
128 if ( mPlotter->getMin() == 0.0 && mPlotter->getMax() == 0.0 && mBars > 0 ) {
129 const auto& sensor = sensors().at( 0 );
130 // The 100 is magic in answerReceived()
131 sendRequest( sensor->hostName(), sensor->name() + QLatin1Char('?'), 100 );
132 }
133
134 // The remaining entries in the dialog are the ones that haven't been
135 // deleted, so that should match the remaining sensors. The dialog does
136 // not edit units or real sensor information, but can change the label.
137 // Reset the footer labels as needed.
138 const auto remainingSensors = dlg.sensors();
139 if (remainingSensors.count() == mPlotter->footers.count())
140 {
141 for(int i = 0; i < remainingSensors.count(); ++i) {
142 const auto newLabel = remainingSensors.at(i).label();
143 if (newLabel != mPlotter->footers[ i ]) {
144 mPlotter->footers[ i ] = newLabel;
145 }
146 }
147 }
148
149 repaint();
150 }
151
applyStyle()152 void DancingBars::applyStyle()
153 {
154 mPlotter->normalColor = KSGRD::Style->firstForegroundColor();
155 mPlotter->alarmColor = KSGRD::Style->alarmColor();
156 mPlotter->mBackgroundColor = KSGRD::Style->backgroundColor();
157 mPlotter->fontSize = KSGRD::Style->fontSize();
158
159 repaint();
160 }
161
addSensor(const QString & hostName,const QString & name,const QString & type,const QString & title)162 bool DancingBars::addSensor( const QString &hostName, const QString &name,
163 const QString &type, const QString &title )
164 {
165 if ( type != QLatin1String("integer") && type != QLatin1String("float") )
166 return false;
167
168 if ( mBars >= 32 )
169 return false;
170
171 if ( !mPlotter->addBar( title ) )
172 return false;
173
174 registerSensor( new KSGRD::SensorProperties( hostName, name, type, title ) );
175
176 /* To differentiate between answers from value requests and info
177 * requests we add 100 to the beam index for info requests. */
178 sendRequest( hostName, name + QLatin1Char('?'), mBars + 100 );
179 ++mBars;
180 mSampleBuffer.resize( mBars );
181
182 QString tooltip;
183 for ( int i = 0; i < mBars; ++i ) {
184 tooltip += QStringLiteral( "%1%2:%3" ).arg( i != 0 ? QStringLiteral("\n") : QString() )
185 .arg( sensors().at( i )->hostName() )
186 .arg( sensors().at( i )->name() );
187 }
188 mPlotter->setToolTip( tooltip );
189
190 return true;
191 }
192
removeSensor(uint pos)193 bool DancingBars::removeSensor( uint pos )
194 {
195 if ( pos >= mBars ) {
196 qDebug() << "DancingBars::removeSensor: idx out of range ("
197 << pos << ")";
198 return false;
199 }
200
201 mPlotter->removeBar( pos );
202 mBars--;
203 KSGRD::SensorDisplay::removeSensor( pos );
204
205 QString tooltip;
206 for ( int i = 0; i < mBars; ++i ) {
207 tooltip += QStringLiteral( "%1%2:%3" ).arg( i != 0 ? QStringLiteral("\n") : QString() )
208 .arg( sensors().at( i )->hostName() )
209 .arg( sensors().at( i )->name() );
210 }
211 mPlotter->setToolTip( tooltip );
212
213 return true;
214 }
215
updateSamples(const QVector<double> & samples)216 void DancingBars::updateSamples( const QVector<double> &samples )
217 {
218 mPlotter->updateSamples( samples );
219 }
220
answerReceived(int id,const QList<QByteArray> & answerlist)221 void DancingBars::answerReceived( int id, const QList<QByteArray> &answerlist )
222 {
223 /* We received something, so the sensor is probably ok. */
224 sensorError( id, false );
225 QByteArray answer;
226 if(!answerlist.isEmpty()) answer = answerlist[0];
227 if ( id < 100 ) {
228 if(id >= mSampleBuffer.count()) {
229 qDebug() << "ERROR: DancingBars received invalid data";
230 sensorError(id, true);
231 return;
232 }
233 mSampleBuffer[ id ] = answer.toDouble();
234 if ( mFlags.testBit( id ) == true ) {
235 qDebug() << "ERROR: DancingBars lost sample (" << mFlags
236 << ", " << mBars << ")";
237 sensorError( id, true );
238 return;
239 }
240 mFlags.setBit( id, true );
241
242 bool allBitsAvailable = true;
243 for ( int i = 0; i < mBars; ++i )
244 allBitsAvailable &= mFlags.testBit( i );
245
246 if ( allBitsAvailable ) {
247 mPlotter->updateSamples( mSampleBuffer );
248 mFlags.fill( false );
249 }
250 } else if ( id >= 100 ) {
251 KSGRD::SensorIntegerInfo info( answer );
252 if ( id == 100 )
253 if ( mPlotter->getMin() == 0.0 && mPlotter->getMax() == 0.0 ) {
254 /* We only use this information from the sensor when the
255 * display is still using the default values. If the
256 * sensor has been restored we don't touch the already set
257 * values. */
258 mPlotter->changeRange( info.min(), info.max() );
259 }
260
261 sensors().at( id - 100 )->setUnit( info.unit() );
262 }
263 }
264
restoreSettings(QDomElement & element)265 bool DancingBars::restoreSettings( QDomElement &element )
266 {
267 SensorDisplay::restoreSettings( element );
268
269 mPlotter->changeRange( element.attribute( QStringLiteral("min"), QStringLiteral("0") ).toDouble(),
270 element.attribute( QStringLiteral("max"), QStringLiteral("0") ).toDouble() );
271
272 mPlotter->setLimits( element.attribute( QStringLiteral("lowlimit"), QStringLiteral("0") ).toDouble(),
273 element.attribute( QStringLiteral("lowlimitactive"), QStringLiteral("0") ).toInt(),
274 element.attribute( QStringLiteral("uplimit"), QStringLiteral("0") ).toDouble(),
275 element.attribute( QStringLiteral("uplimitactive"), QStringLiteral("0") ).toInt() );
276
277 mPlotter->normalColor = restoreColor( element, QStringLiteral("normalColor"),
278 KSGRD::Style->firstForegroundColor() );
279 mPlotter->alarmColor = restoreColor( element, QStringLiteral("alarmColor"),
280 KSGRD::Style->alarmColor() );
281 mPlotter->mBackgroundColor = restoreColor( element, QStringLiteral("backgroundColor"),
282 KSGRD::Style->backgroundColor() );
283 mPlotter->fontSize = element.attribute( QStringLiteral("fontSize"), QStringLiteral( "%1" ).arg(
284 KSGRD::Style->fontSize() ) ).toInt();
285
286 QDomNodeList dnList = element.elementsByTagName( QStringLiteral("beam") );
287 for ( int i = 0; i < dnList.count(); ++i ) {
288 QDomElement el = dnList.item( i ).toElement();
289 addSensor( el.attribute( QStringLiteral("hostName") ), el.attribute( QStringLiteral("sensorName") ),
290 ( el.attribute( QStringLiteral("sensorType") ).isEmpty() ? QStringLiteral("integer") :
291 el.attribute( QStringLiteral("sensorType") ) ), el.attribute( QStringLiteral("sensorDescr") ) );
292 }
293
294
295 return true;
296 }
297
saveSettings(QDomDocument & doc,QDomElement & element)298 bool DancingBars::saveSettings( QDomDocument &doc, QDomElement &element)
299 {
300 element.setAttribute( QStringLiteral("min"), mPlotter->getMin() );
301 element.setAttribute( QStringLiteral("max"), mPlotter->getMax() );
302 double l, u;
303 bool la, ua;
304 mPlotter->getLimits( l, la, u, ua );
305 element.setAttribute( QStringLiteral("lowlimit"), l );
306 element.setAttribute( QStringLiteral("lowlimitactive"), la );
307 element.setAttribute( QStringLiteral("uplimit"), u );
308 element.setAttribute( QStringLiteral("uplimitactive"), ua );
309
310 saveColor( element, QStringLiteral("normalColor"), mPlotter->normalColor );
311 saveColor( element, QStringLiteral("alarmColor"), mPlotter->alarmColor );
312 saveColor( element, QStringLiteral("backgroundColor"), mPlotter->mBackgroundColor );
313 element.setAttribute( QStringLiteral("fontSize"), mPlotter->fontSize );
314
315 for ( int i = 0; i < mBars; ++i ) {
316 QDomElement beam = doc.createElement( QStringLiteral("beam") );
317 element.appendChild( beam );
318 beam.setAttribute( QStringLiteral("hostName"), sensors().at( i )->hostName() );
319 beam.setAttribute( QStringLiteral("sensorName"), sensors().at( i )->name() );
320 beam.setAttribute( QStringLiteral("sensorType"), sensors().at( i )->type() );
321 beam.setAttribute( QStringLiteral("sensorDescr"), mPlotter->footers[ i ] );
322 }
323
324 SensorDisplay::saveSettings( doc, element );
325
326 return true;
327 }
328
hasSettingsDialog() const329 bool DancingBars::hasSettingsDialog() const
330 {
331 return true;
332 }
333
334
335