FancyPlotter.cpp 38.1 KB
Newer Older
1
2
3
4
5
/*
    KSysGuard, the KDE System Guard

    Copyright (c) 1999 - 2002 Chris Schlaeger <cs@kde.org>

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as
 published by the Free Software Foundation; either version 2 of
 the License or (at your option) version 3 or any later version
 accepted by the membership of KDE e.V. (or its successor approved
 by the membership of KDE e.V.), which shall act as a proxy 
 defined in Section 14 of version 3 of the license.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22
*/

23
#include <QApplication>
24
#include <QDebug>
25
#include <QDomElement>
26
27
28
29
#include <QToolTip>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
30
#include <QLocale>
31
#include <QResizeEvent>
32
#include <QStandardPaths>
33

Martin Flöser's avatar
Martin Flöser committed
34
#include <KLocalizedString>
35
#include <kmessagebox.h>
36
#include <ksignalplotter.h>
37
38

#include <ksgrd/SensorManager.h>
39
#include "StyleEngine.h"
40
41
42
43
44

#include "FancyPlotterSettings.h"

#include "FancyPlotter.h"

45
46
47
48
49
50
51
52
53
54
// The unicode character 0x25CF is a big filled in circle.  We would prefer 
// to use this in the tooltip (or other messages). However it's possible 
// that the font used to draw the tooltip won't have it.  So we fall back to a 
// "#" instead.
static constexpr const int BLACK_CIRCLE = 0x25CF;
static inline QChar circleCharacter(const QFontMetrics& fm)
{
    return fm.inFont(QChar(BLACK_CIRCLE)) ? QChar(BLACK_CIRCLE) : QLatin1Char('#');
}

John Tapsell's avatar
John Tapsell committed
55
class SensorToAdd {
56
57
58
59
60
61
  public:
    QRegExp name;
    QString hostname;
    QString type;
    QList<QColor> colors;
    QString summationName;
John Tapsell's avatar
John Tapsell committed
62
};
63

64
class FancyPlotterLabel : public QLabel {
65
  public:
66
67
68
69
70
71
    FancyPlotterLabel(QWidget *parent) : QLabel(parent) {
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
        longHeadingWidth = 0;
        shortHeadingWidth = 0;
        textMargin = 0;
        setLayoutDirection(Qt::LeftToRight); //We do this because we organise the strings ourselves.. is this going to muck it up though for RTL languages?
72
    }
73
    ~FancyPlotterLabel() override {
74
    }
75
    void setLabel(const QString &name, const QColor &color) {
John Tapsell's avatar
John Tapsell committed
76
        labelName = name;
77
78

        if(indicatorSymbol.isNull()) {
79
            indicatorSymbol = circleCharacter(fontMetrics());
80
81
82
        }
        changeLabel(color);

83
    }
84
85
86
    void setValueText(const QString &value) {
        //value can have multiple strings, separated with the 0x9c character
        valueText = value.split(QChar(0x9c));
Laurent Montel's avatar
Laurent Montel committed
87
        resizeEvent(nullptr);
88
89
        update();
    }
90
    void resizeEvent( QResizeEvent * ) override {
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
        QFontMetrics fm = fontMetrics();

        if(valueText.isEmpty()) {
            if(longHeadingWidth < width())
                setText(longHeadingText);
            else
                setText(shortHeadingText);
            return;
        }
        QString value = valueText.first();

        int textWidth = fm.boundingRect(value).width();
        if(textWidth + longHeadingWidth < width())
            setBothText(longHeadingText, value);
        else if(textWidth + shortHeadingWidth < width())
            setBothText(shortHeadingText, value);
        else {
            int valueTextCount = valueText.count();
            int i;
            for(i = 1; i < valueTextCount; ++i) {
                textWidth = fm.boundingRect(valueText.at(i)).width();
                if(textWidth + shortHeadingWidth <= width()) {
                    break;
                }
            }
            if(i < valueTextCount)
                setBothText(shortHeadingText, valueText.at(i));
            else
                setText(noHeadingText + valueText.last()); //This just sets the color of the text
        }
    }
122
123
124
    void changeLabel(const QColor &_color) {
        color = _color;

125
        if ( QApplication::layoutDirection() == Qt::RightToLeft )
126
            longHeadingText = QLatin1String(": ") + labelName + QLatin1String(" <font color=\"") + color.name() + QLatin1String("\">") + indicatorSymbol + QLatin1String("</font>");
John Tapsell's avatar
John Tapsell committed
127
        else
128
129
130
            longHeadingText = QLatin1String("<qt><font color=\"") + color.name() + QLatin1String("\">") + indicatorSymbol + QLatin1String("</font> ") + labelName + QLatin1String(" :");
        shortHeadingText = QLatin1String("<qt><font color=\"") + color.name() + QLatin1String("\">") + indicatorSymbol + QLatin1String("</font>");
        noHeadingText = QLatin1String("<qt><font color=\"") + color.name() + QLatin1String("\">");
131

132
        textMargin = fontMetrics().boundingRect(QLatin1Char('x')).width() + margin()*2 + frameWidth()*2;
133
        longHeadingWidth = fontMetrics().boundingRect(labelName + QLatin1String(" :") + indicatorSymbol + QLatin1String(" x")).width() + textMargin;
134
135
136
        shortHeadingWidth = fontMetrics().boundingRect(indicatorSymbol).width() + textMargin;
        setMinimumWidth(shortHeadingWidth);
        update();
137
    }
138
139
140
  private:
    void setBothText(const QString &heading, const QString & value) {
        if(QApplication::layoutDirection() == Qt::LeftToRight)
141
            setText(heading + QLatin1Char(' ') + value);
142
        else
143
            setText(QStringLiteral("<qt>") + value + QLatin1Char(' ') + heading);
144
    }
145
146
147
148
149
150
151
    int textMargin;
    QString longHeadingText;
    QString shortHeadingText;
    QString noHeadingText;
    int longHeadingWidth;
    int shortHeadingWidth;
    QList<QString> valueText;
152
    QString labelName;
153
154
    QColor color;
    static QChar indicatorSymbol;
John Tapsell's avatar
John Tapsell committed
155
156
};

157
158
QChar FancyPlotterLabel::indicatorSymbol;

159
FancyPlotter::FancyPlotter( QWidget* parent,
160
161
162
                            const QString &title,
                            SharedSettings *workSheetSettings)
  : KSGRD::SensorDisplay( parent, title, workSheetSettings )
163
{
John Tapsell's avatar
John Tapsell committed
164
165
166
    mBeams = 0;
    mSettingsDialog = 0;
    mSensorReportedMax = mSensorReportedMin = 0;
167
168
    mSensorManualMax = mSensorManualMin = 0;
    mUseManualRange = false;
John Tapsell's avatar
John Tapsell committed
169
    mNumAnswers = 0;
Laurent Montel's avatar
Laurent Montel committed
170
    mLabelsWidget = nullptr;
John Tapsell's avatar
John Tapsell committed
171

172
    mIndicatorSymbol = circleCharacter(QFontMetrics(QToolTip::font()));
John Tapsell's avatar
John Tapsell committed
173
174
175
176

    QBoxLayout *layout = new QVBoxLayout(this);
    layout->setSpacing(0);
    mPlotter = new KSignalPlotter( this );
177
    int axisTextWidth = fontMetrics().boundingRect(i18nc("Largest axis title", "99999 XXXX")).width();
John Tapsell's avatar
John Tapsell committed
178
    mPlotter->setMaxAxisTextWidth( axisTextWidth );
179
    mPlotter->setUseAutoRange( true );
John Tapsell's avatar
John Tapsell committed
180
181
182
    mHeading = new QLabel(translatedTitle(), this);
    QFont headingFont;
    headingFont.setWeight(QFont::Bold);
183
    headingFont.setPointSizeF(headingFont.pointSizeF() * 1.19);
John Tapsell's avatar
John Tapsell committed
184
185
186
187
188
    mHeading->setFont(headingFont);
    layout->addWidget(mHeading);
    layout->addWidget(mPlotter);

    /* Create a set of labels underneath the graph. */
189
190
191
    mLabelsWidget = new QWidget;
    layout->addWidget(mLabelsWidget);
    QBoxLayout *outerLabelLayout = new QHBoxLayout(mLabelsWidget);
John Tapsell's avatar
John Tapsell committed
192
    outerLabelLayout->setSpacing(0);
193
    outerLabelLayout->setContentsMargins(0,0,0,0);
John Tapsell's avatar
John Tapsell committed
194
195

    /* create a spacer to fill up the space up to the start of the graph */
196
    outerLabelLayout->addItem(new QSpacerItem(axisTextWidth + 10, 0, QSizePolicy::Preferred));
John Tapsell's avatar
John Tapsell committed
197
198
199

    mLabelLayout = new QHBoxLayout;
    outerLabelLayout->addLayout(mLabelLayout);
200
    mLabelLayout->setContentsMargins(0,0,0,0);
John Tapsell's avatar
John Tapsell committed
201
202
    QFont font;
    font.setPointSize( KSGRD::Style->fontSize() );
203
    mPlotter->setFont( font );
John Tapsell's avatar
John Tapsell committed
204

205
    /* All RMB clicks to the mPlotter widget will be handled by
John Tapsell's avatar
John Tapsell committed
206
207
208
209
     * SensorDisplay::eventFilter. */
    mPlotter->installEventFilter( this );

    setPlotterWidget( mPlotter );
Laurent Montel's avatar
Laurent Montel committed
210
    connect(mPlotter, &KSignalPlotter::axisScaleChanged, this, &FancyPlotter::plotterAxisScaleChanged);
John Tapsell's avatar
John Tapsell committed
211
212
    QDomElement emptyElement;
    restoreSettings(emptyElement);
213
214
215
216
217
218
}

FancyPlotter::~FancyPlotter()
{
}

219
void FancyPlotter::setTitle( const QString &title ) { //virtual
John Tapsell's avatar
John Tapsell committed
220
221
222
    KSGRD::SensorDisplay::setTitle( title );
    if(mHeading)
        mHeading->setText(translatedTitle());
223
224
}

225
bool FancyPlotter::eventFilter( QObject* object, QEvent* event ) {	//virtual
John Tapsell's avatar
John Tapsell committed
226
227
228
    if(event->type() == QEvent::ToolTip)
        setTooltip();
    return SensorDisplay::eventFilter(object, event);
229
230
}

231
232
void FancyPlotter::configureSettings()
{
John Tapsell's avatar
John Tapsell committed
233
234
235
    if(mSettingsDialog)
        return;
    mSettingsDialog = new FancyPlotterSettings( this, mSharedSettings->locked );
236

John Tapsell's avatar
John Tapsell committed
237
    mSettingsDialog->setTitle( title() );
238
239
240
241
242
243
244
245
    mSettingsDialog->setUseManualRange( mUseManualRange );
    if(mUseManualRange) {
        mSettingsDialog->setMinValue( mSensorManualMin );
        mSettingsDialog->setMaxValue( mSensorManualMax );
    } else {
        mSettingsDialog->setMinValue( mSensorReportedMin );
        mSettingsDialog->setMaxValue( mSensorReportedMax );
    }
246

John Tapsell's avatar
John Tapsell committed
247
    mSettingsDialog->setHorizontalScale( mPlotter->horizontalScale() );
248

John Tapsell's avatar
John Tapsell committed
249
250
251
    mSettingsDialog->setShowVerticalLines( mPlotter->showVerticalLines() );
    mSettingsDialog->setVerticalLinesDistance( mPlotter->verticalLinesDistance() );
    mSettingsDialog->setVerticalLinesScroll( mPlotter->verticalLinesScroll() );
252

John Tapsell's avatar
John Tapsell committed
253
    mSettingsDialog->setShowHorizontalLines( mPlotter->showHorizontalLines() );
254

John Tapsell's avatar
John Tapsell committed
255
    mSettingsDialog->setShowAxis( mPlotter->showAxis() );
256

257
    mSettingsDialog->setFontSize( mPlotter->font().pointSize()  );
258

John Tapsell's avatar
John Tapsell committed
259
260
    mSettingsDialog->setRangeUnits( mUnit );
    mSettingsDialog->setRangeUnits( mUnit );
261

262
263
    mSettingsDialog->setStackBeams( mPlotter->stackGraph() );

John Tapsell's avatar
John Tapsell committed
264
    bool hasIntegerRange = true;
John Tapsell's avatar
John Tapsell committed
265
    SensorModelEntry::List list;
John Tapsell's avatar
John Tapsell committed
266
    for ( int i = 0; i < (int)mBeams; ++i ) {
Laurent Montel's avatar
Laurent Montel committed
267
        FPSensorProperties *sensor = nullptr;
268
269
270
271
272
273
274
275
        //find the first sensor for this beam, since one beam can have many sensors
        for ( int j = 0; j < sensors().count(); ++j ) {
            FPSensorProperties *sensor2 = static_cast<FPSensorProperties *>(sensors().at(j));
            if(sensor2->beamId == i)
                sensor = sensor2;
        }
        if(!sensor)
            return;
John Tapsell's avatar
John Tapsell committed
276
277
        SensorModelEntry entry;
        entry.setId( i );
278
        entry.setHostName( sensor->hostName() );
John Tapsell's avatar
John Tapsell committed
279
        entry.setSensorName( sensor->regExpName().isEmpty()?sensor->name():sensor->regExpName() );
280
281
        entry.setUnit( sensor->unit() );
        entry.setStatus( sensor->isOk() ? i18n( "OK" ) : i18n( "Error" ) );
John Tapsell's avatar
John Tapsell committed
282
        entry.setColor( mPlotter->beamColor( i ) );
John Tapsell's avatar
John Tapsell committed
283
284
        if(!sensor->isInteger)
            hasIntegerRange = false;
John Tapsell's avatar
John Tapsell committed
285
286
287
        list.append( entry );
    }
    mSettingsDialog->setSensors( list );
John Tapsell's avatar
John Tapsell committed
288
    mSettingsDialog->setHasIntegerRange( hasIntegerRange );
289

Laurent Montel's avatar
Laurent Montel committed
290
291
292
    connect(mSettingsDialog, &FancyPlotterSettings::applyClicked, this, &FancyPlotter::applySettings);
    connect(mSettingsDialog, &FancyPlotterSettings::okClicked, this, &FancyPlotter::applySettings);
    connect(mSettingsDialog, &FancyPlotterSettings::finished, this, &FancyPlotter::settingsFinished);
293

294
    mSettingsDialog->open();	// open() opens the dialog modaly (ie. blocks the parent window)
295
}
296

297
298
void FancyPlotter::settingsFinished()
{
299
    applySettings();
300
301
    mSettingsDialog->hide();
    mSettingsDialog->deleteLater();
Laurent Montel's avatar
Laurent Montel committed
302
    mSettingsDialog = nullptr;
303
304
305
}

void FancyPlotter::applySettings() {
306
307
308
    if (!mSettingsDialog) {
        return;
    }
309
310
    setTitle( mSettingsDialog->title() );

311
312
313
314
    mUseManualRange = mSettingsDialog->useManualRange();
    if(mUseManualRange) {
        mSensorManualMin = mSettingsDialog->minValue();
        mSensorManualMax = mSettingsDialog->maxValue();
John Tapsell's avatar
John Tapsell committed
315
        mPlotter->changeRange( mSettingsDialog->minValue(), mSettingsDialog->maxValue() );
316
    }
317
318
319
    else {
        mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
    }
320

321
    if ( mPlotter->horizontalScale() != mSettingsDialog->horizontalScale() ) {
John Tapsell's avatar
John Tapsell committed
322
        mPlotter->setHorizontalScale( mSettingsDialog->horizontalScale() );
323
    }
324

325
326
327
    mPlotter->setShowVerticalLines( mSettingsDialog->showVerticalLines() );
    mPlotter->setVerticalLinesDistance( mSettingsDialog->verticalLinesDistance() );
    mPlotter->setVerticalLinesScroll( mSettingsDialog->verticalLinesScroll() );
328

329
    mPlotter->setShowHorizontalLines( mSettingsDialog->showHorizontalLines() );
330

John Tapsell's avatar
John Tapsell committed
331
    mPlotter->setShowAxis( mSettingsDialog->showAxis() );
332
    mPlotter->setStackGraph( mSettingsDialog->stackBeams() );
333
334

    QFont font;
335
    font.setPointSize( mSettingsDialog->fontSize() );
336
    mPlotter->setFont( font );
337

338
339
    QList<int> deletedBeams = mSettingsDialog->deleted();
    for ( int i =0; i < deletedBeams.count(); ++i) {
340
        removeBeam(deletedBeams[i]);
341
342
343
    }
    mSettingsDialog->clearDeleted(); //We have deleted them, so clear the deleted

344
    reorderBeams(mSettingsDialog->order());
345
    mSettingsDialog->resetOrder(); //We have now reordered the sensors, so reset the order
346

347
    SensorModelEntry::List list = mSettingsDialog->sensors();
348

349
350
    for( int i = 0; i < list.count(); i++)
        setBeamColor(i, list[i].color());
351
    mPlotter->update();
352
}
353
354
355

void FancyPlotter::resizeEvent( QResizeEvent* )
{
Laurent Montel's avatar
Laurent Montel committed
356
357
    bool showHeading = true;
    bool showLabels = true;
358
359
360
361
362
363
364
365
366
367

    if( height() < mLabelsWidget->sizeHint().height() + mHeading->sizeHint().height() + mPlotter->minimumHeight() )
        showHeading = false;
    if( height() < mLabelsWidget->sizeHint().height() + mPlotter->minimumHeight() )
        showLabels = false;
    mHeading->setVisible(showHeading);
    mLabelsWidget->setVisible(showLabels);

}

368
369
void FancyPlotter::reorderBeams(const QList<int> & orderOfBeams)
{
370
    //Q_ASSERT(orderOfBeams.size() == mLabelLayout.size());  Commented out because it cause compile problems in some cases??
371
372
    //Reorder the graph
    mPlotter->reorderBeams(orderOfBeams);
373
374
375
    //Reorder the labels underneath the graph
    QList<QLayoutItem *> labelsInOldOrder;
    while(!mLabelLayout->isEmpty())
John Tapsell's avatar
John Tapsell committed
376
        labelsInOldOrder.append(mLabelLayout->takeAt(0));
377
378

    for(int newIndex = 0; newIndex < orderOfBeams.count(); newIndex++) {
John Tapsell's avatar
John Tapsell committed
379
380
        int oldIndex = orderOfBeams.at(newIndex);
        mLabelLayout->addItem(labelsInOldOrder.at(oldIndex));
381
    }
382

383
    for ( int i = 0; i < sensors().count(); ++i ) {
John Tapsell's avatar
John Tapsell committed
384
385
386
387
388
389
390
        FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
        for(int newIndex = 0; newIndex < orderOfBeams.count(); newIndex++) {
            int oldIndex = orderOfBeams.at(newIndex);
            if(oldIndex == sensor->beamId) {
                sensor->beamId = newIndex;
                break;
            }
391
392
        }
    }
393
}
394
395
void FancyPlotter::applyStyle()
{
396
    QFont font = mPlotter->font();
John Tapsell's avatar
John Tapsell committed
397
    font.setPointSize(KSGRD::Style->fontSize() );
398
    mPlotter->setFont( font );
John Tapsell's avatar
John Tapsell committed
399
    for ( int i = 0; i < mPlotter->numBeams() &&
Laurent Montel's avatar
Laurent Montel committed
400
            i < KSGRD::Style->numSensorColors(); ++i ) {
401
        setBeamColor(i, KSGRD::Style->sensorColor(i));
John Tapsell's avatar
John Tapsell committed
402
403
404
    }

    mPlotter->update();
405
}
406
407
408
void FancyPlotter::setBeamColor(int i, const QColor &color)
{
        mPlotter->setBeamColor( i, color );
409
        static_cast<FancyPlotterLabel *>((static_cast<QWidgetItem *>(mLabelLayout->itemAt(i)))->widget())->changeLabel(color);
410
}
411
bool FancyPlotter::addSensor( const QString &hostName, const QString &name,
John Tapsell's avatar
John Tapsell committed
412
        const QString &type, const QString &title )
413
{
John Tapsell's avatar
John Tapsell committed
414
415
    return addSensor( hostName, name, type, title,
            KSGRD::Style->sensorColor( mBeams ), QString(), mBeams );
416
}
417

418
bool FancyPlotter::addSensor( const QString &hostName, const QString &name,
John Tapsell's avatar
John Tapsell committed
419
420
421
        const QString &type, const QString &title,
        const QColor &color, const QString &regexpName,
        int beamId, const QString & summationName)
422
{
423
    if ( type != QLatin1String("integer") && type != QLatin1String("float") )
John Tapsell's avatar
John Tapsell committed
424
        return false;
425
426


John Tapsell's avatar
John Tapsell committed
427
    registerSensor( new FPSensorProperties( hostName, name, type, title, color, regexpName, beamId, summationName ) );
John Tapsell's avatar
John Tapsell committed
428

John Tapsell's avatar
John Tapsell committed
429
430
    /* To differentiate between answers from value requests and info
     * requests we add 100 to the beam index for info requests. */
431
    sendRequest( hostName, name + QLatin1Char('?'), sensors().size() - 1 + 100 );
John Tapsell's avatar
John Tapsell committed
432

John Tapsell's avatar
John Tapsell committed
433
434
435
436
437
438
    if((int)mBeams == beamId) {
        mPlotter->addBeam( color );
        /* Add a label for this beam */
        FancyPlotterLabel *label = new FancyPlotterLabel(this);
        mLabelLayout->addWidget(label);
        if(!summationName.isEmpty()) {
439
            label->setLabel(summationName, mPlotter->beamColor(mBeams));
John Tapsell's avatar
John Tapsell committed
440
441
        }
        ++mBeams;
John Tapsell's avatar
John Tapsell committed
442
443
    }

John Tapsell's avatar
John Tapsell committed
444
    return true;
445
446
}

447
bool FancyPlotter::removeBeam( uint beamId )
448
{
John Tapsell's avatar
John Tapsell committed
449
    if ( beamId >= mBeams ) {
Ragnar Thomsen's avatar
Ragnar Thomsen committed
450
        qDebug() << "FancyPlotter::removeBeam: beamId out of range ("
451
            << beamId << ")";
John Tapsell's avatar
John Tapsell committed
452
        return false;
453
    }
454

John Tapsell's avatar
John Tapsell committed
455
456
457
458
459
460
    mPlotter->removeBeam( beamId );
    --mBeams;
    QWidget *label = (static_cast<QWidgetItem *>(mLabelLayout->takeAt( beamId )))->widget();
    mLabelLayout->removeWidget(label);
    delete label;

461
462
    mSensorReportedMax = 0;
    mSensorReportedMin = 0;
John Tapsell's avatar
John Tapsell committed
463
464
    for ( int i = sensors().count()-1; i >= 0; --i ) {
        FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
465
466

        if(sensor->beamId == (int)beamId)
John Tapsell's avatar
John Tapsell committed
467
            removeSensor( i );
468
469
470
471
472
473
474
475
476
477
478
479
        else {
            if(sensor->beamId > (int)beamId)
                sensor->beamId--;  //sensor pointer is no longer valid after removing the sensor
            mSensorReportedMax = qMax(mSensorReportedMax, sensor->maxValue);
            mSensorReportedMin = qMin(mSensorReportedMin, sensor->minValue);
        }
    }
    //change the plotter's range to the new maximum
    if ( !mUseManualRange )
        mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
    else
        mPlotter->changeRange( mSensorManualMin, mSensorManualMax );
John Tapsell's avatar
John Tapsell committed
480

481
482
483
484
485
486
    //loop through the new sensors to find the new unit
    for ( int i = 0; i < sensors().count(); i++ ) {
        FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
        if(i == 0)
            mUnit = sensor->unit();
        else if(mUnit != sensor->unit()) {
487
            mUnit = QLatin1String("");
488
489
            break;
        }
John Tapsell's avatar
John Tapsell committed
490
    }
John Tapsell's avatar
John Tapsell committed
491
492
493
494
    //adjust the scale to take into account the removed sensor
    plotterAxisScaleChanged();

    return true;
495
}
John Tapsell's avatar
John Tapsell committed
496

John Tapsell's avatar
John Tapsell committed
497
void FancyPlotter::setTooltip()
498
{
499
    QString tooltip = QStringLiteral("<qt><p style='white-space:pre'>");
John Tapsell's avatar
John Tapsell committed
500
501
502
503

    QString description;
    QString lastValue;
    bool neednewline = false;
504
    bool showingSummationGroup = false;
John Tapsell's avatar
John Tapsell committed
505
506
507
508
509
    int beamId = -1;
    //Note that the number of beams can be less than the number of sensors, since some sensors
    //get added together for a beam.
    //For the tooltip, we show all the sensors
    for ( int i = 0; i < sensors().count(); ++i ) {
510
        FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
John Tapsell's avatar
John Tapsell committed
511
512
513
514
515
        description = sensor->description();
        if(description.isEmpty())
            description = sensor->name();

        if(sensor->isOk()) {
516
            lastValue = QLocale().toString( sensor->lastValue, (sensor->isInteger)?0:-1 );
517
            if (sensor->unit() == QLatin1String("%"))
John Tapsell's avatar
John Tapsell committed
518
                lastValue = i18nc("units", "%1%", lastValue);
John Tapsell's avatar
John Tapsell committed
519
            else if( !sensor->unit().isEmpty() )
520
                lastValue = i18nc("units", QString(QLatin1String("%1 ") + sensor->unit()).toUtf8().constData(), lastValue);
John Tapsell's avatar
John Tapsell committed
521
522
523
        } else {
            lastValue = i18n("Error");
        }
524
525
        if (beamId != sensor->beamId) {
            if (!sensor->summationName.isEmpty()) {
526
                tooltip += i18nc("%1 is what is being shown statistics for, like 'Memory', 'Swap', etc.", "<p><b>%1:</b><br>", i18n(sensor->summationName.toUtf8().constData()));
527
528
529
                showingSummationGroup = true;
                neednewline = false;
            } else if (showingSummationGroup) {
Yuri Chornoivan's avatar
Yuri Chornoivan committed
530
                //When a summation group has finished, separate the next sensor with a newline
531
                showingSummationGroup = false;
532
                tooltip += QLatin1String("<br>");
533
534
            }

John Tapsell's avatar
John Tapsell committed
535
536
537
538
        }
        beamId = sensor->beamId;

        if(sensor->isLocalhost()) {
539
            tooltip += QStringLiteral( "%1%2 %3 (%4)" ).arg( neednewline  ? QStringLiteral("<br>") : QString())
540
                .arg(QLatin1String("<font color=\"") + mPlotter->beamColor( beamId ).name() + QLatin1String("\">") + mIndicatorSymbol+ QLatin1String("</font>"))
Martin Flöser's avatar
Martin Flöser committed
541
                .arg( i18n(description.toUtf8().constData()) )
John Tapsell's avatar
John Tapsell committed
542
                .arg( lastValue );
John Tapsell's avatar
John Tapsell committed
543

John Tapsell's avatar
John Tapsell committed
544
        } else {
545
            tooltip += QStringLiteral( "%1%2 %3:%4 (%5)" ).arg( neednewline ? QStringLiteral("<br>") : QString() )
546
                .arg(QLatin1String("<font color=\"") + mPlotter->beamColor( beamId ).name() + QLatin1String("\">") + mIndicatorSymbol+ QLatin1String("</font>"))
John Tapsell's avatar
John Tapsell committed
547
                .arg( sensor->hostName() )
Martin Flöser's avatar
Martin Flöser committed
548
                .arg( i18n(description.toUtf8().constData()) )
John Tapsell's avatar
John Tapsell committed
549
                .arg( lastValue );
John Tapsell's avatar
John Tapsell committed
550
        }
John Tapsell's avatar
John Tapsell committed
551
        neednewline = true;
John Tapsell's avatar
John Tapsell committed
552
    }
John Tapsell's avatar
John Tapsell committed
553
554
555
    //  tooltip += "</td></tr></table>";
    mPlotter->setToolTip( tooltip );
}
556

557
void FancyPlotter::sendDataToPlotter( )
John Tapsell's avatar
John Tapsell committed
558
559
560
561
562
563
564
565
566
567
{
    if(!mSampleBuf.isEmpty() && mBeams != 0) {  
        if((uint)mSampleBuf.count() > mBeams) {
            mSampleBuf.clear();
            return; //ignore invalid results - can happen if a sensor is deleted
        }
        while((uint)mSampleBuf.count() < mBeams)
            mSampleBuf.append(mPlotter->lastValue(mSampleBuf.count())); //we might have sensors missing so set their values to the previously known value
        mPlotter->addSample( mSampleBuf );
        if(isVisible()) {
568
            if(QToolTip::isVisible() && (qApp->topLevelAt(QCursor::pos()) == window()) && mPlotter->geometry().contains(mPlotter->mapFromGlobal( QCursor::pos() ))) {
John Tapsell's avatar
John Tapsell committed
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
                setTooltip();
                QToolTip::showText(QCursor::pos(), mPlotter->toolTip(), mPlotter);
            }
            QString lastValue;
            int beamId = -1;
            for ( int i = 0; i < sensors().size(); ++i ) {
                FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
                if(sensor->beamId == beamId)
                    continue;
                beamId = sensor->beamId;
                if(sensor->isOk() && mPlotter->numBeams() > beamId) {

                    int precision;
                    if(sensor->unit() == mUnit) {
                        precision = (sensor->isInteger && mPlotter->scaleDownBy() == 1)?0:-1;
                        lastValue = mPlotter->lastValueAsString(beamId, precision);
                    } else {
                        precision = (sensor->isInteger)?0:-1;
587
                        lastValue = QLocale().toString( mPlotter->lastValue(beamId), precision );
588
                        if (sensor->unit() == QLatin1String("%"))
John Tapsell's avatar
John Tapsell committed
589
                            lastValue = i18nc("units", "%1%", lastValue);
John Tapsell's avatar
John Tapsell committed
590
                        else if( !sensor->unit().isEmpty() )  {
591
                            lastValue = i18nc("units", QString(QLatin1String("%1 ") + sensor->unit()).toUtf8().constData(), lastValue);
John Tapsell's avatar
John Tapsell committed
592
593
594
                        }
                    }

595
                    if(sensor->maxValue != 0 && sensor->unit() != QLatin1String("%")) {
596
                        //Use a multi length string incase we do not have enough room
597
                        lastValue = i18n("%1 of %2", lastValue, mPlotter->valueAsString(sensor->maxValue, precision) ) + "\xc2\x9c" + lastValue;
598
                    }
John Tapsell's avatar
John Tapsell committed
599
600
601
                } else {
                    lastValue = i18n("Error");
                }
602
                static_cast<FancyPlotterLabel *>((static_cast<QWidgetItem *>(mLabelLayout->itemAt(beamId)))->widget())->setValueText(lastValue);
John Tapsell's avatar
John Tapsell committed
603
604
            }
        }
605

John Tapsell's avatar
John Tapsell committed
606
607
    }
    mSampleBuf.clear();
608
609
610
}
void FancyPlotter::timerTick() //virtual
{
611
612
613
    if(mNumAnswers < sensors().count())
        sendDataToPlotter(); //we haven't received enough answers yet, but plot what we do have
    mNumAnswers = 0;
John Tapsell's avatar
John Tapsell committed
614
    SensorDisplay::timerTick();
John Tapsell's avatar
John Tapsell committed
615
}
John Tapsell's avatar
John Tapsell committed
616
617
void FancyPlotter::plotterAxisScaleChanged()
{
618
    //Prevent this being called recursively
Laurent Montel's avatar
Laurent Montel committed
619
    disconnect(mPlotter, &KSignalPlotter::axisScaleChanged, this, &FancyPlotter::plotterAxisScaleChanged);
620
    KLocalizedString unit;
621
    double value = mPlotter->currentMaximumRangeValue();
622
    if(mUnit  == QLatin1String("KiB")) {
John Tapsell's avatar
John Tapsell committed
623
624
        if(value >= 1024*1024*1024*0.7) {  //If it's over 0.7TiB, then set the scale to terabytes
            mPlotter->setScaleDownBy(1024*1024*1024);
625
            unit = ki18nc("units", "%1 TiB"); // the unit - terabytes
John Tapsell's avatar
John Tapsell committed
626
627
        } else if(value >= 1024*1024*0.7) {  //If it's over 0.7GiB, then set the scale to gigabytes
            mPlotter->setScaleDownBy(1024*1024);
628
            unit = ki18nc("units", "%1 GiB"); // the unit - gigabytes
John Tapsell's avatar
John Tapsell committed
629
630
        } else if(value > 1024) {
            mPlotter->setScaleDownBy(1024);
631
            unit = ki18nc("units", "%1 MiB"); // the unit - megabytes
632
633
        } else {
            mPlotter->setScaleDownBy(1);
634
            unit = ki18nc("units", "%1 KiB"); // the unit - kilobytes
John Tapsell's avatar
John Tapsell committed
635
        }
636
    } else if(mUnit == QLatin1String("KiB/s")) {
John Tapsell's avatar
John Tapsell committed
637
638
        if(value >= 1024*1024*1024*0.7) {  //If it's over 0.7TiB, then set the scale to terabytes
            mPlotter->setScaleDownBy(1024*1024*1024);
639
            unit = ki18nc("units", "%1 TiB/s"); // the unit - terabytes per second
John Tapsell's avatar
John Tapsell committed
640
641
        } else if(value >= 1024*1024*0.7) {  //If it's over 0.7GiB, then set the scale to gigabytes
            mPlotter->setScaleDownBy(1024*1024);
642
            unit = ki18nc("units", "%1 GiB/s"); // the unit - gigabytes per second
John Tapsell's avatar
John Tapsell committed
643
644
        } else if(value > 1024) {
            mPlotter->setScaleDownBy(1024);
645
            unit = ki18nc("units", "%1 MiB/s"); // the unit - megabytes per second
646
647
        } else {
            mPlotter->setScaleDownBy(1);
648
            unit = ki18nc("units", "%1 KiB/s"); // the unit - kilobytes per second
John Tapsell's avatar
John Tapsell committed
649
        }
650
    } else if(mUnit == QLatin1String("%")) {
651
652
        mPlotter->setScaleDownBy(1);
        unit = ki18nc("units", "%1%"); //the unit - percentage
653
    } else if(mUnit.isEmpty()) {
654
        unit = ki18nc("unitless - just a number", "%1");
655
    } else {
656
657
658
659
660
#if 0  // the strings are here purely for translation
        NOOP_I18NC("units", "%1 1/s"); // the unit - 1 per second
        NOOP_I18NC("units", "%1 s");  // the unit - seconds
        NOOP_I18NC("units", "%1 MHz");  // the unit - frequency megahertz
#endif
661
        mPlotter->setScaleDownBy(1);
662
        //translate any others
663
        unit = ki18nc("units", QString(QLatin1String("%1 ") + mUnit).toUtf8().constData());
John Tapsell's avatar
John Tapsell committed
664
    }
665
666
    mPlotter->setUnit(unit);
    //reconnect
Laurent Montel's avatar
Laurent Montel committed
667
    connect(mPlotter, &KSignalPlotter::axisScaleChanged, this, &FancyPlotter::plotterAxisScaleChanged);
668
}
669
void FancyPlotter::answerReceived( int id, const QList<QByteArray> &answerlist )
670
{
John Tapsell's avatar
John Tapsell committed
671
672
673
674
    QByteArray answer;

    if(!answerlist.isEmpty()) answer = answerlist[0];
    if ( (uint)id < 100 ) {
675
        //Make sure that we put the answer in the correct place.  Its index in the list should be equal to the sensor index.  This in turn will contain the beamId
676
677
        if(id >= sensors().count())
            return;  //just ignore if we get a result for an invalid sensor
John Tapsell's avatar
John Tapsell committed
678
679
680
681
682
683
684
685
686
687
688
689
690
691
        FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(id));
        int beamId = sensor->beamId;
        double value = answer.toDouble();
        while(beamId > mSampleBuf.count())
            mSampleBuf.append(0); //we might have sensors missing so set their values to zero

        if(beamId == mSampleBuf.count()) {
            mSampleBuf.append( value );
        } else {
            mSampleBuf[beamId] += value; //If we get two answers for the same beamid, we should add them together.  That's how the summation works
        }
        sensor->lastValue = value;
        /* We received something, so the sensor is probably ok. */
        sensorError( id, false );
692
693
694

        if(++mNumAnswers == sensors().count())
            sendDataToPlotter(); //we have received all the answers so start plotting
John Tapsell's avatar
John Tapsell committed
695
    } else if ( id >= 100 && id < 200 ) {
696
697
        if( (id - 100) >= sensors().count())
            return;  //just ignore if we get a result for an invalid sensor
John Tapsell's avatar
John Tapsell committed
698
        KSGRD::SensorFloatInfo info( answer );
699
        QString unit = info.unit();
700
701
702
703
        if(unit.toUpper() == QLatin1String("KB") || unit.toUpper() == QLatin1String("KIB"))
            unit = QStringLiteral("KiB");
        if(unit.toUpper() == QLatin1String("KB/S") || unit.toUpper() == QLatin1String("KIB/S"))
            unit = QStringLiteral("KiB/s");
704
705
706
707

        if(id == 100) //if we are the first sensor, just use that sensors units as the global unit
            mUnit = unit;
        else if(unit != mUnit)
708
            mUnit = QLatin1String(""); //if the units don't match, then set the units on the scale to empty, to avoid any confusion
709

John Tapsell's avatar
John Tapsell committed
710
711
712
        mSensorReportedMax = qMax(mSensorReportedMax, info.max());
        mSensorReportedMin = qMin(mSensorReportedMin, info.min());

713
        if ( !mUseManualRange )
John Tapsell's avatar
John Tapsell committed
714
            mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
715
        plotterAxisScaleChanged();
John Tapsell's avatar
John Tapsell committed
716
717
718

        FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(id - 100));
        sensor->maxValue = info.max();
719
720
        sensor->minValue = info.min();
        sensor->setUnit( unit );
John Tapsell's avatar
John Tapsell committed
721
722
723
724
725
        sensor->setDescription( info.name() );

        QString summationName = sensor->summationName;
        int beamId = sensor->beamId;

726
727
728
        Q_ASSERT(beamId < mPlotter->numBeams());
        Q_ASSERT(beamId < mLabelLayout->count());

John Tapsell's avatar
John Tapsell committed
729
        if(summationName.isEmpty())
730
            static_cast<FancyPlotterLabel *>((static_cast<QWidgetItem *>(mLabelLayout->itemAt(beamId)))->widget())->setLabel(info.name(), mPlotter->beamColor(beamId));
John Tapsell's avatar
John Tapsell committed
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749

    } else if( id == 200) {
        /* FIXME This doesn't check the host!  */
        if(!mSensorsToAdd.isEmpty())  {
            foreach(SensorToAdd *sensor, mSensorsToAdd) {
                int beamId = mBeams;  //Assign the next sensor to the next available beamId
                for ( int i = 0; i < answerlist.count(); ++i ) {
                    if ( answerlist[ i ].isEmpty() )
                        continue;
                    QString sensorName = QString::fromUtf8(answerlist[ i ].split('\t')[0]);
                    if(sensor->name.exactMatch(sensorName)) {
                        if(sensor->summationName.isEmpty())
                            beamId = mBeams; //If summationName is not empty then reuse the previous beamId.  In this way we can have multiple sensors with the same beamId, which can then be summed together
                        QColor color;
                        if(!sensor->colors.isEmpty() )
                            color = sensor->colors.takeFirst();
                        else if(KSGRD::Style->numSensorColors() != 0)
                            color = KSGRD::Style->sensorColor( beamId % KSGRD::Style->numSensorColors());
                        addSensor( sensor->hostname, sensorName,
750
751
                                (sensor->type.isEmpty()) ? QStringLiteral("float") : sensor->type
                                , QLatin1String(""), color, sensor->name.pattern(), beamId, sensor->summationName);
John Tapsell's avatar
John Tapsell committed
752
753
754
                    }
                }
            }
755
            qDeleteAll(mSensorsToAdd);
John Tapsell's avatar
John Tapsell committed
756
            mSensorsToAdd.clear();
John Tapsell's avatar
John Tapsell committed
757
        }
758
759
760
761
762
    }
}

bool FancyPlotter::restoreSettings( QDomElement &element )
{
763
    mUseManualRange = element.attribute( QStringLiteral("manualRange"), QStringLiteral("0") ).toInt();
764
765

    if(mUseManualRange) {
766
767
        mSensorManualMax = element.attribute( QStringLiteral("max") ).toDouble();
        mSensorManualMin = element.attribute( QStringLiteral("min") ).toDouble();
768
769
770
        mPlotter->changeRange( mSensorManualMin, mSensorManualMax );
    } else {
        mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
John Tapsell's avatar
John Tapsell committed
771
    }
772

773
    mPlotter->setUseAutoRange(element.attribute( QStringLiteral("autoRange"), QStringLiteral("1") ).toInt());
774

John Tapsell's avatar
John Tapsell committed
775
    // Do not restore the color settings from a previous version
776
    int version = element.attribute(QStringLiteral("version"), QStringLiteral("0")).toInt();
John Tapsell's avatar
John Tapsell committed
777

778
779
780
781
    mPlotter->setShowVerticalLines( element.attribute( QStringLiteral("vLines"), QStringLiteral("0") ).toUInt() );
    mPlotter->setVerticalLinesDistance( element.attribute( QStringLiteral("vDistance"), QStringLiteral("30") ).toUInt() );
    mPlotter->setVerticalLinesScroll( element.attribute( QStringLiteral("vScroll"), QStringLiteral("0") ).toUInt() );
    mPlotter->setHorizontalScale( element.attribute( QStringLiteral("hScale"), QStringLiteral("6") ).toUInt() );
John Tapsell's avatar
John Tapsell committed
782

783
784
    mPlotter->setShowHorizontalLines( element.attribute( QStringLiteral("hLines"), QStringLiteral("1") ).toUInt() );
    mPlotter->setStackGraph( element.attribute(QStringLiteral("stacked"), QStringLiteral("0")).toInt());
John Tapsell's avatar
John Tapsell committed
785

786
    QString filename = element.attribute( QStringLiteral("svgBackground"));
787
    if (!filename.isEmpty() && filename[0] == QLatin1Char('/')) {
788
        filename = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("ksysguard/") + filename);
John Tapsell's avatar
John Tapsell committed
789
790
791
    }
    mPlotter->setSvgBackground( filename );
    if(version >= 1) {
792
793
        mPlotter->setShowAxis( element.attribute( QStringLiteral("labels"), QStringLiteral("1") ).toUInt() );
        uint fontsize = element.attribute( QStringLiteral("fontSize"), QStringLiteral("0")).toUInt();
John Tapsell's avatar
John Tapsell committed
794
795
796
        if(fontsize == 0) fontsize =  KSGRD::Style->fontSize();
        QFont font;
        font.setPointSize( fontsize );
797
        mPlotter->setFont( font );
John Tapsell's avatar
John Tapsell committed
798
    }
799
    QDomNodeList dnList = element.elementsByTagName( QStringLiteral("beam") );
John Tapsell's avatar
John Tapsell committed
800
801
    for ( int i = 0; i < dnList.count(); ++i ) {
        QDomElement el = dnList.item( i ).toElement();
802
        if(el.hasAttribute(QStringLiteral("regexpSensorName"))) {
John Tapsell's avatar
John Tapsell committed
803
            SensorToAdd *sensor = new SensorToAdd();
804
805
806
807
            sensor->name = QRegExp(el.attribute(QStringLiteral("regexpSensorName")));
            sensor->hostname = el.attribute( QStringLiteral("hostName") );
            sensor->type = el.attribute( QStringLiteral("sensorType") );
            sensor->summationName = el.attribute(QStringLiteral("summationName"));
808
            QStringList colors = el.attribute(QStringLiteral("color")).split(QLatin1Char(','));
John Tapsell's avatar
John Tapsell committed
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
            bool ok;
            foreach(const QString &color, colors) {
                int c = color.toUInt( &ok, 0 );
                if(ok) {
                    QColor col( (c & 0xff0000) >> 16, (c & 0xff00) >> 8, (c & 0xff), (c & 0xff000000) >> 24);
                    if(col.isValid()) {
                        if(col.alpha() == 0) col.setAlpha(255);
                        sensor->colors << col;
                    }
                    else
                        sensor->colors << KSGRD::Style->sensorColor( i );
                }
                else
                    sensor->colors << KSGRD::Style->sensorColor( i );
            }
            mSensorsToAdd.append(sensor);
825
            sendRequest( sensor->hostname, QStringLiteral("monitors"), 200 );
John Tapsell's avatar
John Tapsell committed
826
        } else
827
828
829
830
            addSensor( el.attribute( QStringLiteral("hostName") ), el.attribute( QStringLiteral("sensorName") ),
                    ( el.attribute( QStringLiteral("sensorType") ).isEmpty() ? QStringLiteral("float") :
                      el.attribute( QStringLiteral("sensorType") ) ), QLatin1String(""), restoreColor( el, QStringLiteral("color"),
                      KSGRD::Style->sensorColor( i ) ), QString(), mBeams, el.attribute(QStringLiteral("summationName")) );
John Tapsell's avatar
John Tapsell committed
831
832
833
834
835
    }

    SensorDisplay::restoreSettings( element );

    return true;
836
837
}

838
bool FancyPlotter::saveSettings( QDomDocument &doc, QDomElement &element)
839
{
840
    element.setAttribute( QStringLiteral("autoRange"), mPlotter->useAutoRange() );
841

842
    element.setAttribute( QStringLiteral("manualRange"), mUseManualRange );
843
    if(mUseManualRange) {
844
845
        element.setAttribute( QStringLiteral("min"), mSensorManualMin );
        element.setAttribute( QStringLiteral("max"), mSensorManualMax );
846
847
848
    }


849
850
851
    element.setAttribute( QStringLiteral("vLines"), mPlotter->showVerticalLines() );
    element.setAttribute( QStringLiteral("vDistance"), mPlotter->verticalLinesDistance() );
    element.setAttribute( QStringLiteral("vScroll"), mPlotter->verticalLinesScroll() );
John Tapsell's avatar
John Tapsell committed
852

853
    element.setAttribute( QStringLiteral("hScale"), mPlotter->horizontalScale() );
John Tapsell's avatar
John Tapsell committed
854

855
    element.setAttribute( QStringLiteral("hLines"), mPlotter->showHorizontalLines() );
John Tapsell's avatar
John Tapsell committed
856

857
858
    element.setAttribute( QStringLiteral("svgBackground"), mPlotter->svgBackground() );
    element.setAttribute( QStringLiteral("stacked"), mPlotter->stackGraph() );
John Tapsell's avatar
John Tapsell committed
859

860
861
862
    element.setAttribute( QStringLiteral("version"), 1 );
    element.setAttribute( QStringLiteral("labels"), mPlotter->showAxis() );
    element.setAttribute( QStringLiteral("fontSize"), mPlotter->font().pointSize() );
John Tapsell's avatar
John Tapsell committed
863
864
865
866
867
868
869
870
871
872
873
874

    QHash<QString,QDomElement> hash;
    int beamId = -1;
    for ( int i = 0; i < sensors().size(); ++i ) {
        FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
        if(sensor->beamId == beamId)
            continue;
        beamId = sensor->beamId;

        QString regExpName = sensor->regExpName();
        if(!regExpName.isEmpty() && hash.contains( regExpName )) {
            QDomElement oldBeam = hash.value(regExpName);
875
            saveColorAppend( oldBeam, QStringLiteral("color"), mPlotter->beamColor( beamId ) );
John Tapsell's avatar
John Tapsell committed
876
        } else {
877
            QDomElement beam = doc.createElement( QStringLiteral("beam") );
John Tapsell's avatar
John Tapsell committed
878
            element.appendChild( beam );
879
            beam.setAttribute( QStringLiteral("hostName"), sensor->hostName() );
John Tapsell's avatar
John Tapsell committed
880
            if(regExpName.isEmpty())
881
                beam.setAttribute( QStringLiteral("sensorName"), sensor->name() );