2
KSysGuard, the KDE System Guard
4
Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org>
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of the GNU General Public
8
License version 2 or at your option version 3 as published by
9
the Free Software Foundation.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
#include <QtGui/QDrag>
23
#include <QtGui/QLabel>
24
#include <QtGui/QMouseEvent>
25
#include <QtGui/QPainter>
26
#include <QtGui/QPixmap>
27
#include <QtGui/QVBoxLayout>
33
#include <kmessagebox.h>
34
#include <ksgrd/SensorManager.h>
35
#include <kfilterproxysearchline.h>
37
#include "SensorBrowser.h"
38
//#define SENSOR_MODEL_DO_TEST
39
//uncomment the above to test the model
40
#ifdef SENSOR_MODEL_DO_TEST
41
#include "modeltest.h"
44
SensorBrowserModel::SensorBrowserModel()
46
#ifdef SENSOR_MODEL_DO_TEST
51
SensorBrowserModel::~SensorBrowserModel()
53
qDeleteAll( mHostInfoMap );
55
qDeleteAll( mSensorInfoMap );
56
mSensorInfoMap.clear();
59
int SensorBrowserModel::columnCount( const QModelIndex &) const { //virtual
63
QVariant SensorBrowserModel::data( const QModelIndex & index, int role) const { //virtual
64
if(!index.isValid()) return QVariant();
66
case Qt::DisplayRole: {
67
if(index.column()==0) {
68
uint id = index.internalId();
69
if(mSensorInfoMap.contains(id)) {
70
Q_ASSERT(mSensorInfoMap.value(id));
71
SensorInfo *sensorInfo = mSensorInfoMap.value(id);
72
return QString(sensorInfo->description() + " (" +sensorInfo->type() +')' );
74
if(mTreeNodeNames.contains(id)) return mTreeNodeNames.value(id);
75
if(mHostInfoMap.contains(id)) {
76
Q_ASSERT(mHostInfoMap.value(id));
77
return mHostInfoMap.value(id)->hostName();
82
case Qt::DecorationRole: {
83
if(index.column() == 0) {
84
HostInfo *host = getHostInfo(index.internalId());
85
KSGRD::SensorAgent *agent;
86
if(host != NULL && (agent = host->sensorAgent())) {
87
if(agent->daemonOnLine())
88
return KIcon("computer");
90
return KIcon("dialog-warning");
97
case Qt::ToolTipRole: {
98
if(index.column() == 0) {
99
HostInfo *host = getHostInfo(index.internalId());
100
KSGRD::SensorAgent *agent;
101
if(host != NULL && (agent = host->sensorAgent())) {
102
if(agent->daemonOnLine())
103
return agent->hostName();
105
return agent->reasonForOffline();
115
QVariant SensorBrowserModel::headerData ( int section, Qt::Orientation , int role) const { //virtual
116
if(role != Qt::DisplayRole) return QVariant();
117
if(section==0) return i18n("Sensor Browser");
121
void SensorBrowserModel::retranslate() {
122
emit headerDataChanged(Qt::Horizontal, 0,0);
125
QModelIndex SensorBrowserModel::index ( int row, int column, const QModelIndex & parent) const { //virtual
126
if(column != 0) return QModelIndex();
128
if(!parent.isValid()) {
129
ids = mHostInfoMap.keys();
132
ids = mTreeMap.value(parent.internalId());
134
if( row >= ids.size() || row< 0) {
135
return QModelIndex();
137
QModelIndex index = createIndex(row, column, ids[row]);
138
Q_ASSERT(index.isValid());
142
QStringList SensorBrowserModel::listHosts() const
144
QStringList hostList;
146
QMapIterator<int, HostInfo*> it( mHostInfoMap );
147
while ( it.hasNext() ) {
149
Q_ASSERT(it.value());
150
hostList.append( it.value()->hostName() );
156
QStringList SensorBrowserModel::listSensors( const QString &hostName ) const
158
QMapIterator<int, HostInfo*> it( mHostInfoMap );
159
while ( it.hasNext() ) {
161
Q_ASSERT(it.value());
162
if ( it.value()->hostName() == hostName ) {
163
Q_ASSERT(mSensorInfoMap.contains(it.key()));
164
return listSensors( it.key() );
167
return QStringList();
170
QStringList SensorBrowserModel::listSensors( int parentId) const
172
SensorInfo *sensor=mSensorInfoMap.value(parentId);
173
if(sensor) return QStringList(sensor->name());
175
QStringList childSensors;
176
QList<int> children = mTreeMap.value(parentId);
177
for(int i=0; i < children.size(); i++) {
178
childSensors+= listSensors(children[i]);
182
SensorInfo *SensorBrowserModel::getSensorInfo(QModelIndex index) const
184
if(!index.isValid()) return NULL;
185
return mSensorInfoMap.value(index.internalId());
187
int SensorBrowserModel::makeSensor(HostInfo *hostInfo, int parentId, const QString &sensorName, const QString &name, const QString &sensorType) {
188
//sensorName is the full version. e.g. mem/free
189
//name is the short version. e.g. free
190
//sensortype is e.g. Integer
191
QList<int> children = mTreeMap.value(parentId);
192
for(int i=0; i<children.size(); i++)
193
if(mSensorInfoMap.contains(children[i])) {
194
Q_ASSERT(mSensorInfoMap.value(children[i]));
195
if(mSensorInfoMap.value(children[i])->name() == sensorName)
199
QModelIndex parentModelIndex;
200
if(hostInfo->id() == parentId) {
201
parentModelIndex = createIndex(mHostInfoMap.keys().indexOf(parentId), 0 , parentId);
203
int parentsParentId = mParentsTreeMap.value(parentId);
204
parentModelIndex = createIndex(mTreeMap.value(parentsParentId).indexOf(parentId), 0, parentId);
206
Q_ASSERT(parentModelIndex.isValid());
207
QList<int> &parentTreemap = mTreeMap[parentId];
208
SensorInfo *sensorInfo = new SensorInfo(hostInfo, sensorName, name, sensorType);
209
beginInsertRows( parentModelIndex , parentTreemap.size(), parentTreemap.size() );
210
parentTreemap << mIdCount;
211
mParentsTreeMap.insert( mIdCount, parentId );
212
mSensorInfoMap.insert(mIdCount, sensorInfo);
213
mHostSensorsMap[hostInfo->id()].insert(sensorName, true);
216
return mIdCount-1; //NOTE mIdCount is next available number. Se we use it, then increment it, but return the number of the one that we use
219
void SensorBrowserModel::removeSensor(HostInfo *hostInfo, int parentId, const QString &sensorName) {
220
//sensorName is the full version. e.g. mem/free
221
QList<int> children = mTreeMap.value(parentId);
224
for(index=0; index<children.size(); index++)
225
if(mSensorInfoMap.contains(children[index])) {
226
Q_ASSERT(mSensorInfoMap.value(children[index]));
227
if(mSensorInfoMap.value(children[index])->name() == sensorName) {
228
idCount = children[index];
233
kDebug(1215) << "removeSensor called for sensor that doesn't exist in the tree: " << sensorName ;
236
QModelIndex parentModelIndex;
237
int parentsParentId = -1;
238
if(hostInfo->id() == parentId) {
239
parentModelIndex = createIndex(mHostInfoMap.keys().indexOf(parentId), 0 , parentId);
241
parentsParentId = mParentsTreeMap.value(parentId);
242
parentModelIndex = createIndex(mTreeMap.value(parentsParentId).indexOf(parentId), 0, parentId);
244
Q_ASSERT(parentModelIndex.isValid());
245
QList<int> &parentTreemap = mTreeMap[parentId];
246
beginRemoveRows( parentModelIndex, index, index );
247
parentTreemap.removeAll(idCount);
248
mParentsTreeMap.remove(idCount);
249
SensorInfo *sensorInfo = mSensorInfoMap.take(idCount);
251
mHostSensorsMap[hostInfo->id()].remove(sensorName);
254
if(parentsParentId != -1)
255
removeEmptyParentTreeBranches(hostInfo->id(), parentId, parentsParentId);
257
void SensorBrowserModel::removeEmptyParentTreeBranches(int hostId, int id, int parentId) {
259
return; //We don't want to remove hosts
261
if(!mTreeMap.value(id).isEmpty()) return; // We should have no children
263
QModelIndex parentModelIndex;
264
int parentsParentId = -1;
265
if(hostId == parentId) {
266
parentModelIndex = createIndex(mHostInfoMap.keys().indexOf(parentId), 0 , parentId);
268
parentsParentId = mParentsTreeMap.value(parentId);
269
parentModelIndex = createIndex(mTreeMap.value(parentsParentId).indexOf(parentId), 0, parentId);
272
int index = mTreeMap.value(parentId).indexOf(id);
273
int idCount = mTreeMap.value(parentId).at(index);
275
QList<int> &parentTreemap = mTreeMap[parentId];
276
beginRemoveRows( parentModelIndex, index, index );
277
parentTreemap.removeAll(idCount);
278
mParentsTreeMap.remove(idCount);
279
mTreeMap.remove(idCount);
280
mTreeNodeNames.remove(idCount);
283
if(parentsParentId != -1)
284
removeEmptyParentTreeBranches(hostId, parentId, parentsParentId);
286
int SensorBrowserModel::makeTreeBranch(int parentId, const QString &name) {
287
QList<int> children = mTreeMap.value(parentId);
288
for(int i=0; i<children.size(); i++)
289
if(mTreeNodeNames.value(children[i]) == name) return children[i];
291
QModelIndex parentModelIndex;
292
if(mHostInfoMap.contains(parentId)) {
293
parentModelIndex = createIndex(mHostInfoMap.keys().indexOf(parentId), 0 , parentId);
295
int parentsParentId = mParentsTreeMap.value(parentId);
296
parentModelIndex = createIndex(mTreeMap.value(parentsParentId).indexOf(parentId), 0, parentId);
298
Q_ASSERT(parentModelIndex.isValid());
299
QList<int> &parentTreemap = mTreeMap[parentId];
300
beginInsertRows( parentModelIndex , parentTreemap.size(), parentTreemap.size() );
301
parentTreemap << mIdCount;
302
mParentsTreeMap.insert( mIdCount, parentId );
303
mTreeMap[mIdCount]; //create with empty qlist
304
mTreeNodeNames.insert(mIdCount, name);
311
void SensorBrowserModel::answerReceived( int hostId, const QList<QByteArray>&answer )
313
/* An answer has the following example format:
315
cpu/system/idle integer
316
cpu/system/sys integer
317
cpu/system/nice integer
318
cpu/system/user integer
321
HostInfo *hostInfo = getHostInfo(hostId);
323
kDebug(1215) << "Invalid hostId " << hostId ;
326
/* We keep a copy of the previous sensor names so that we can detect what sensors have been removed */
327
QHash<QString,bool> oldSensorNames = mHostSensorsMap.value(hostId);
328
for ( int i = 0; i < answer.count(); ++i ) {
329
if ( answer[ i ].isEmpty() )
332
QList<QByteArray> words = answer[ i ].split('\t');
333
if(words.size() != 2) {
334
kDebug(1215) << "Invalid data " << answer[i];
335
continue; /* Something wrong with this line of data */
337
QString sensorName = QString::fromUtf8(words[ 0 ]);
338
QString sensorType = QString::fromUtf8(words[ 1 ]);
339
oldSensorNames.remove(sensorName); /* This sensor has not been removed */
340
if ( hasSensor(hostId, sensorName)) {
343
if(sensorName.isEmpty()) continue;
345
if(sensorType == "string") continue;
347
/* The sensor browser can display sensors in a hierarchical order.
348
* Sensors can be grouped into nodes by seperating the hierarchical
349
* nodes through slashes in the sensor name. E. g. cpu/system/user is
350
* the sensor user in the cpu node. There is no limit for the
352
int currentNodeId = hostId; //Start from the host branch and work our way down the tree
353
QStringList absolutePath = sensorName.split( '/' );
354
for ( int j = 0; j < absolutePath.count()-1; ++j ) {
355
// Localize the sensor name part by part.
356
QString name = KSGRD::SensorMgr->translateSensorPath( absolutePath[ j ] );
357
currentNodeId = makeTreeBranch(currentNodeId, name);
359
QString name = KSGRD::SensorMgr->translateSensorPath( absolutePath[ absolutePath.size()-1] );
360
makeSensor(hostInfo, currentNodeId, sensorName, name, sensorType);
362
/* Now we have to remove sensors that were not found */
363
QHashIterator<QString, bool> it( oldSensorNames );
364
while ( it.hasNext() ) {
367
int currentNodeId = hostId; //Start from the host branch and work our way down the tree
368
QStringList absolutePath = it.key().split( '/' );
369
for ( int j = 0; j < absolutePath.count()-1; ++j ) {
370
// Localize the sensor name part by part.
371
QString name = KSGRD::SensorMgr->translateSensorPath( absolutePath[ j ] );
372
currentNodeId = makeTreeBranch(currentNodeId, name);
374
removeSensor(hostInfo, currentNodeId, it.key());
376
emit sensorsAddedToHost( createIndex( mHostInfoMap.keys().indexOf(hostId), 0, hostId ) );
380
QModelIndex SensorBrowserModel::parent ( const QModelIndex & index ) const {
381
if(!index.isValid() || index.column() != 0)
382
return QModelIndex();
383
if(mHostInfoMap.contains(index.internalId())) return QModelIndex();
384
if(!mParentsTreeMap.contains(index.internalId())) {
385
kDebug(1215) << "Something is wrong with the model. Doesn't contain " << index.internalId();
386
return QModelIndex();
388
int parentId = mParentsTreeMap.value(index.internalId());
390
QModelIndex parentModelIndex;
391
if(mHostInfoMap.contains(parentId)) {
392
parentModelIndex = createIndex(mHostInfoMap.keys().indexOf(parentId), 0 , parentId);
394
int parentsParentId = mParentsTreeMap.value(parentId);
395
parentModelIndex = createIndex(mTreeMap.value(parentsParentId).indexOf(parentId), 0, parentId);
397
Q_ASSERT(parentModelIndex.isValid());
398
return parentModelIndex;
401
int SensorBrowserModel::rowCount ( const QModelIndex & parent ) const {
402
if(!parent.isValid()) return mHostInfoMap.size();
403
if(parent.column() != 0) return 0;
404
return mTreeMap.value(parent.internalId()).size();
407
Qt::ItemFlags SensorBrowserModel::flags ( const QModelIndex & index ) const {
408
if(!index.isValid()) return 0;
409
if(mSensorInfoMap.contains(index.internalId())) return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
410
else return Qt::ItemIsEnabled;
413
SensorBrowserWidget::SensorBrowserWidget( QWidget* parent, KSGRD::SensorManager* sm ) : QWidget( parent )
415
QVBoxLayout *layout = new QVBoxLayout;
416
m_treeWidget = new SensorBrowserTreeWidget(this, sm);
417
KFilterProxySearchLine * search_line = new KFilterProxySearchLine(this);
418
search_line->setProxy(&m_treeWidget->model());
419
layout->addWidget(search_line);
420
layout->addWidget(m_treeWidget);
423
SensorBrowserWidget::~SensorBrowserWidget()
426
SensorBrowserTreeWidget::SensorBrowserTreeWidget( QWidget* parent, KSGRD::SensorManager* sm ) : QTreeView( parent ), mSensorManager( sm )
428
mSortFilterProxyModel.setSourceModel(&mSensorBrowserModel);
429
mSortFilterProxyModel.setShowAllChildren(true);
430
setModel(&mSortFilterProxyModel);
431
connect( mSensorManager, SIGNAL( update() ), &mSensorBrowserModel, SLOT( update() ) );
432
connect( mSensorManager, SIGNAL(hostAdded(KSGRD::SensorAgent*,const QString &)), &mSensorBrowserModel, SLOT( hostAdded(KSGRD::SensorAgent*,const QString &)) );
433
connect( mSensorManager, SIGNAL(hostConnectionLost(const QString &)), &mSensorBrowserModel, SLOT( hostRemoved(const QString &)) );
434
// connect( mSensorManager, SIGNAL(hostAdded(KSGRD::SensorAgent*,const QString &)), SLOT(updateView()) );
435
// connect( mSensorManager, SIGNAL(hostConnectionLost(const QString &)), SLOT(updateView()) );
436
connect( &mSortFilterProxyModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), SLOT(updateView()) );
438
setDragDropMode(QAbstractItemView::DragOnly);
439
setUniformRowHeights(true);
441
//setMinimumWidth( 1 );
443
connect( &mSensorBrowserModel, SIGNAL(sensorsAddedToHost(const QModelIndex&)), this, SLOT(expandItem(const QModelIndex&)));
445
KSGRD::SensorManagerIterator it( mSensorManager );
446
while ( it.hasNext() ) {
447
KSGRD::SensorAgent* sensorAgent = it.next().value();
448
QString hostName = mSensorManager->hostName( sensorAgent );
449
mSensorBrowserModel.addHost(sensorAgent, hostName);
454
SensorBrowserTreeWidget::~SensorBrowserTreeWidget()
458
void SensorBrowserTreeWidget::updateView()
460
if(mSensorManager->count() == 1) {
461
setRootIsDecorated( false );
462
//expand the top level
463
for(int i = 0; i < mSortFilterProxyModel.rowCount(); i++)
464
expand(mSortFilterProxyModel.index(i,0));
466
setRootIsDecorated( true );
468
void SensorBrowserTreeWidget::expandItem(const QModelIndex &model_index)
470
expand(mSortFilterProxyModel.mapFromSource(model_index));
472
void SensorBrowserTreeWidget::retranslateUi() {
474
this->setToolTip( i18n( "Drag sensors to empty cells of a worksheet "));
475
this->setWhatsThis( i18n( "The sensor browser lists the connected hosts and the sensors "
476
"that they provide. Click and drag sensors into drop zones "
477
"of a worksheet. A display will appear "
478
"that visualizes the "
479
"values provided by the sensor. Some sensor displays can "
480
"display values of multiple sensors. Simply drag other "
481
"sensors on to the display to add more sensors." ) );
484
void SensorBrowserTreeWidget::changeEvent( QEvent * event )
486
if (event->type() == QEvent::LanguageChange) {
488
mSensorBrowserModel.retranslate();
489
mSensorBrowserModel.update();
491
QWidget::changeEvent(event);
494
void SensorBrowserTreeWidget::disconnect()
496
QModelIndexList indexlist = selectionModel()->selectedRows();
497
for(int i=0; i < indexlist.size(); i++)
499
mSensorBrowserModel.disconnectHost(indexlist.value(i).internalId());
503
void SensorBrowserTreeWidget::hostReconfigured( const QString& )
505
// TODO: not yet implemented.
508
void SensorBrowserModel::clear() {
509
qDeleteAll(mHostInfoMap);
510
mHostInfoMap.clear();
514
void SensorBrowserModel::disconnectHost(uint id)
516
disconnectHost(mHostInfoMap.value(id));
518
void SensorBrowserModel::disconnectHost(const HostInfo *hostInfo)
520
KSGRD::SensorMgr->disengage( hostInfo->sensorAgent() );
522
void SensorBrowserModel::disconnectHost(const QString &hostname)
524
HostInfo* toDelete = findHostInfoByHostName(hostname);
525
if (toDelete != NULL)
526
disconnectHost(toDelete);
528
HostInfo* SensorBrowserModel::findHostInfoByHostName(const QString &hostName) const {
529
HostInfo* toReturn = NULL;
530
QMapIterator<int, HostInfo*> it( mHostInfoMap );
531
while (it.hasNext() && toReturn == NULL) {
533
if (it.value()->hostName() == hostName) {
534
toReturn = it.value();
539
void SensorBrowserModel::hostAdded(KSGRD::SensorAgent *sensorAgent, const QString &hostName) {
540
addHost(sensorAgent,hostName);
544
void SensorBrowserModel::hostRemoved(const QString &hostName) {
545
HostInfo* toRemove = findHostInfoByHostName(hostName);
546
if (toRemove != NULL) {
548
int hostId = toRemove->id();
549
removeAllSensorUnderBranch(toRemove,hostId);
550
removeEmptyParentTreeBranches(hostId,hostId,hostId);
552
delete mHostInfoMap.take(hostId);
553
mTreeMap.take(hostId);
554
mHostSensorsMap.take(hostId);
560
void SensorBrowserModel::removeAllSensorUnderBranch(HostInfo* hostInfo, int parentId) {
562
QList<int> children = mTreeMap.value(parentId);
564
for (int i = 0; i < children.size(); i++) {
566
if (mTreeMap.contains(children[i])) {
567
//well our children is not a sensor so remove what is under him
568
removeAllSensorUnderBranch(hostInfo,children[i]);
571
//well this should be a sensor so remove it
572
if (mSensorInfoMap.contains(children[i])) {
573
SensorInfo* sensorToRemove = mSensorInfoMap.value(children[i]);
574
Q_ASSERT(sensorToRemove);
575
removeSensor(hostInfo, parentId, sensorToRemove->name());
582
void SensorBrowserModel::addHost(KSGRD::SensorAgent *sensorAgent, const QString &hostName)
584
beginInsertRows( QModelIndex() , mHostInfoMap.size(), mHostInfoMap.size() );
585
HostInfo* hostInfo = new HostInfo( mIdCount, sensorAgent, hostName);
586
mHostInfoMap.insert(mIdCount, hostInfo);
587
mTreeMap.insert(mIdCount, QList<int>());
588
mHostSensorsMap.insert(mIdCount, QHash<QString, bool>());
591
hostInfo->sensorAgent()->sendRequest( "monitors", this, mIdCount-1 );
594
void SensorBrowserModel::update()
596
QMapIterator<int, HostInfo*> it( mHostInfoMap );
597
while ( it.hasNext() ) {
599
KSGRD::SensorAgent* sensorAgent = it.value()->sensorAgent();
601
sensorAgent->sendRequest( "monitors", this, id );
604
QMimeData * SensorBrowserModel::mimeData ( const QModelIndexList & indexes ) const { //virtual
605
QMimeData *mimeData = new QMimeData();
606
if(indexes.size() != 1) return mimeData;
607
SensorInfo *sensor = getSensorInfo(indexes[0]);
608
if(!sensor) return mimeData;
609
// Create text drag object as
610
// "<hostname> <sensorname> <sensortype> <sensordescription>".
611
// Only the description may contain blanks.
613
Q_ASSERT(sensor->hostInfo());
614
QString mDragText = sensor->hostInfo()->hostName() + ' ' +
615
sensor->name() + ' ' +
616
sensor->type()+ ' ' +
617
sensor->description();
620
mimeData->setData( "application/x-ksysguard", mDragText.toUtf8() );
624
SensorInfo::SensorInfo( HostInfo *hostInfo, const QString &name,
625
const QString &desc, const QString &type )
626
: mName( name ), mDesc( desc ), mType( type ), mHostInfo( hostInfo )
631
QString SensorInfo::name() const
636
QString SensorInfo::type() const
641
QString SensorInfo::description() const
646
HostInfo *SensorInfo::hostInfo() const
653
#include "SensorBrowser.moc"