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>
32 #include <kmessagebox.h>
33 #include <ksgrd/SensorManager.h>
35 #include "SensorBrowser.h"
36 //#define SENSOR_MODEL_DO_TEST
37 //uncomment the above to test the model
38 #ifdef SENSOR_MODEL_DO_TEST
39 #include "modeltest.h"
42 SensorBrowserModel::SensorBrowserModel()
44 #ifdef SENSOR_MODEL_DO_TEST
49 SensorBrowserModel::~SensorBrowserModel()
52 qDeleteAll( mHostInfoMap
);
54 qDeleteAll( mSensorInfoMap
);
55 mSensorInfoMap
.clear();
58 int SensorBrowserModel::columnCount( const QModelIndex
&) const { //virtual
62 QVariant
SensorBrowserModel::data( const QModelIndex
& index
, int role
) const { //virtual
63 if(!index
.isValid()) return QVariant();
65 case Qt::DisplayRole
: {
66 if(index
.column()==0) {
67 uint id
= index
.internalId();
68 if(mSensorInfoMap
.contains(id
)) {
69 Q_ASSERT(mSensorInfoMap
.value(id
));
70 SensorInfo
*sensorInfo
= mSensorInfoMap
.value(id
);
71 return sensorInfo
->description() + " (" +sensorInfo
->type() +')' ;
73 if(mTreeNodeNames
.contains(id
)) return mTreeNodeNames
.value(id
);
74 if(mHostInfoMap
.contains(id
)) {
75 Q_ASSERT(mHostInfoMap
.value(id
));
76 return mHostInfoMap
.value(id
)->hostName();
81 case Qt::DecorationRole
: {
82 if(index
.column() == 0) {
83 HostInfo
*host
= getHostInfo(index
.internalId());
84 KSGRD::SensorAgent
*agent
;
85 if(host
!= NULL
&& (agent
= host
->sensorAgent())) {
86 if(agent
->daemonOnLine())
87 return KIcon("computer");
89 return KIcon("dialog-warning");
96 case Qt::ToolTipRole
: {
97 if(index
.column() == 0) {
98 HostInfo
*host
= getHostInfo(index
.internalId());
99 KSGRD::SensorAgent
*agent
;
100 if(host
!= NULL
&& (agent
= host
->sensorAgent())) {
101 if(agent
->daemonOnLine())
102 return agent
->hostName();
104 return agent
->reasonForOffline();
114 QVariant
SensorBrowserModel::headerData ( int section
, Qt::Orientation
, int role
) const { //virtual
115 if(role
!= Qt::DisplayRole
) return QVariant();
116 if(section
==0) return i18n("Sensor Browser");
120 void SensorBrowserModel::retranslate() {
121 emit
headerDataChanged(Qt::Horizontal
, 0,0);
124 QModelIndex
SensorBrowserModel::index ( int row
, int column
, const QModelIndex
& parent
) const { //virtual
125 if(column
!= 0) return QModelIndex();
127 if(!parent
.isValid()) {
128 ids
= mHostInfoMap
.keys();
131 ids
= mTreeMap
.value(parent
.internalId());
133 if( row
>= ids
.size() || row
< 0) {
134 return QModelIndex();
136 QModelIndex index
= createIndex(row
, column
, ids
[row
]);
137 Q_ASSERT(index
.isValid());
141 QStringList
SensorBrowserModel::listHosts() const
143 QStringList hostList
;
145 QMapIterator
<int, HostInfo
*> it( mHostInfoMap
);
146 while ( it
.hasNext() ) {
148 Q_ASSERT(it
.value());
149 hostList
.append( it
.value()->hostName() );
155 QStringList
SensorBrowserModel::listSensors( const QString
&hostName
) const
157 QMapIterator
<int, HostInfo
*> it( mHostInfoMap
);
158 while ( it
.hasNext() ) {
160 Q_ASSERT(it
.value());
161 if ( it
.value()->hostName() == hostName
) {
162 Q_ASSERT(mSensorInfoMap
.contains(it
.key()));
163 return listSensors( it
.key() );
166 return QStringList();
169 QStringList
SensorBrowserModel::listSensors( int parentId
) const
171 SensorInfo
*sensor
=mSensorInfoMap
.value(parentId
);
172 if(sensor
) return QStringList(sensor
->name());
174 QStringList childSensors
;
175 QList
<int> children
= mTreeMap
.value(parentId
);
176 for(int i
=0; i
< children
.size(); i
++) {
177 childSensors
+= listSensors(children
[i
]);
181 SensorInfo
*SensorBrowserModel::getSensorInfo(QModelIndex index
) const
183 if(!index
.isValid()) return NULL
;
184 return mSensorInfoMap
.value(index
.internalId());
186 int SensorBrowserModel::makeSensor(HostInfo
*hostInfo
, int parentId
, const QString
&sensorName
, const QString
&name
, const QString
&sensorType
) {
187 //sensorName is the full version. e.g. mem/free
188 //name is the short version. e.g. free
189 //sensortype is e.g. Integer
190 QList
<int> children
= mTreeMap
.value(parentId
);
191 for(int i
=0; i
<children
.size(); i
++)
192 if(mSensorInfoMap
.contains(children
[i
])) {
193 Q_ASSERT(mSensorInfoMap
.value(children
[i
]));
194 if(mSensorInfoMap
.value(children
[i
])->name() == sensorName
)
198 QModelIndex parentModelIndex
;
199 if(hostInfo
->id() == parentId
) {
200 parentModelIndex
= createIndex(mHostInfoMap
.keys().indexOf(parentId
), 0 , parentId
);
202 int parentsParentId
= mParentsTreeMap
.value(parentId
);
203 parentModelIndex
= createIndex(mTreeMap
.value(parentsParentId
).indexOf(parentId
), 0, parentId
);
205 Q_ASSERT(parentModelIndex
.isValid());
206 QList
<int> &parentTreemap
= mTreeMap
[parentId
];
207 SensorInfo
*sensorInfo
= new SensorInfo(hostInfo
, sensorName
, name
, sensorType
);
208 beginInsertRows( parentModelIndex
, parentTreemap
.size(), parentTreemap
.size() );
209 parentTreemap
<< mIdCount
;
210 mParentsTreeMap
.insert( mIdCount
, parentId
);
211 mSensorInfoMap
.insert(mIdCount
, sensorInfo
);
212 mHostSensorsMap
[hostInfo
->id()].insert(sensorName
, true);
215 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
218 void SensorBrowserModel::removeSensor(HostInfo
*hostInfo
, int parentId
, const QString
&sensorName
) {
219 //sensorName is the full version. e.g. mem/free
220 QList
<int> children
= mTreeMap
.value(parentId
);
223 for(index
=0; index
<children
.size(); index
++)
224 if(mSensorInfoMap
.contains(children
[index
])) {
225 Q_ASSERT(mSensorInfoMap
.value(children
[index
]));
226 if(mSensorInfoMap
.value(children
[index
])->name() == sensorName
) {
227 idCount
= children
[index
];
232 kDebug(1215) << "removeSensor called for sensor that doesn't exist in the tree: " << sensorName
;
235 QModelIndex parentModelIndex
;
236 int parentsParentId
= -1;
237 if(hostInfo
->id() == parentId
) {
238 parentModelIndex
= createIndex(mHostInfoMap
.keys().indexOf(parentId
), 0 , parentId
);
240 parentsParentId
= mParentsTreeMap
.value(parentId
);
241 parentModelIndex
= createIndex(mTreeMap
.value(parentsParentId
).indexOf(parentId
), 0, parentId
);
243 Q_ASSERT(parentModelIndex
.isValid());
244 QList
<int> &parentTreemap
= mTreeMap
[parentId
];
245 beginRemoveRows( parentModelIndex
, index
, index
);
246 parentTreemap
.removeAll(idCount
);
247 mParentsTreeMap
.remove(idCount
);
248 SensorInfo
*sensorInfo
= mSensorInfoMap
.take(idCount
);
250 mHostSensorsMap
[hostInfo
->id()].remove(sensorName
);
253 if(parentsParentId
!= -1)
254 removeEmptyParentTreeBranches(hostInfo
->id(), parentId
, parentsParentId
);
256 void SensorBrowserModel::removeEmptyParentTreeBranches(int hostId
, int id
, int parentId
) {
258 return; //We don't want to remove hosts
260 if(!mTreeMap
.value(id
).isEmpty()) return; // We should have no children
262 QModelIndex parentModelIndex
;
263 int parentsParentId
= -1;
264 if(hostId
== parentId
) {
265 parentModelIndex
= createIndex(mHostInfoMap
.keys().indexOf(parentId
), 0 , parentId
);
267 parentsParentId
= mParentsTreeMap
.value(parentId
);
268 parentModelIndex
= createIndex(mTreeMap
.value(parentsParentId
).indexOf(parentId
), 0, parentId
);
271 int index
= mTreeMap
.value(parentId
).indexOf(id
);
272 int idCount
= mTreeMap
.value(parentId
).at(index
);
274 QList
<int> &parentTreemap
= mTreeMap
[parentId
];
275 beginRemoveRows( parentModelIndex
, index
, index
);
276 parentTreemap
.removeAll(idCount
);
277 mParentsTreeMap
.remove(idCount
);
278 mTreeMap
.remove(idCount
);
279 mTreeNodeNames
.remove(idCount
);
282 if(parentsParentId
!= -1)
283 removeEmptyParentTreeBranches(hostId
, parentId
, parentsParentId
);
285 int SensorBrowserModel::makeTreeBranch(int parentId
, const QString
&name
) {
286 QList
<int> children
= mTreeMap
.value(parentId
);
287 for(int i
=0; i
<children
.size(); i
++)
288 if(mTreeNodeNames
.value(children
[i
]) == name
) return children
[i
];
290 QModelIndex parentModelIndex
;
291 if(mHostInfoMap
.contains(parentId
)) {
292 parentModelIndex
= createIndex(mHostInfoMap
.keys().indexOf(parentId
), 0 , parentId
);
294 int parentsParentId
= mParentsTreeMap
.value(parentId
);
295 parentModelIndex
= createIndex(mTreeMap
.value(parentsParentId
).indexOf(parentId
), 0, parentId
);
297 Q_ASSERT(parentModelIndex
.isValid());
298 QList
<int> &parentTreemap
= mTreeMap
[parentId
];
299 beginInsertRows( parentModelIndex
, parentTreemap
.size(), parentTreemap
.size() );
300 parentTreemap
<< mIdCount
;
301 mParentsTreeMap
.insert( mIdCount
, parentId
);
302 mTreeMap
[mIdCount
]; //create with empty qlist
303 mTreeNodeNames
.insert(mIdCount
, name
);
310 void SensorBrowserModel::answerReceived( int hostId
, const QList
<QByteArray
>&answer
)
312 /* An answer has the following example format:
314 cpu/system/idle integer
315 cpu/system/sys integer
316 cpu/system/nice integer
317 cpu/system/user integer
320 HostInfo
*hostInfo
= getHostInfo(hostId
);
322 kDebug(1215) << "Invalid hostId " << hostId
;
325 /* We keep a copy of the previous sensor names so that we can detect what sensors have been removed */
326 QHash
<QString
,bool> oldSensorNames
= mHostSensorsMap
.value(hostId
);
327 for ( int i
= 0; i
< answer
.count(); ++i
) {
328 if ( answer
[ i
].isEmpty() )
331 QList
<QByteArray
> words
= answer
[ i
].split('\t');
332 if(words
.size() != 2) {
333 kDebug(1215) << "Invalid data " << answer
[i
];
334 continue; /* Something wrong with this line of data */
336 QString sensorName
= QString::fromUtf8(words
[ 0 ]);
337 QString sensorType
= QString::fromUtf8(words
[ 1 ]);
338 oldSensorNames
.remove(sensorName
); /* This sensor has not been removed */
339 if ( hasSensor(hostId
, sensorName
)) {
342 if(sensorName
.isEmpty()) continue;
344 if(sensorType
== "string") continue;
346 /* The sensor browser can display sensors in a hierarchical order.
347 * Sensors can be grouped into nodes by seperating the hierarchical
348 * nodes through slashes in the sensor name. E. g. cpu/system/user is
349 * the sensor user in the cpu node. There is no limit for the
351 int currentNodeId
= hostId
; //Start from the host branch and work our way down the tree
352 QStringList absolutePath
= sensorName
.split( '/' );
353 for ( int j
= 0; j
< absolutePath
.count()-1; ++j
) {
354 // Localize the sensor name part by part.
355 QString name
= KSGRD::SensorMgr
->translateSensorPath( absolutePath
[ j
] );
356 currentNodeId
= makeTreeBranch(currentNodeId
, name
);
358 QString name
= KSGRD::SensorMgr
->translateSensorPath( absolutePath
[ absolutePath
.size()-1] );
359 makeSensor(hostInfo
, currentNodeId
, sensorName
, name
, sensorType
);
361 /* Now we have to remove sensors that were not found */
362 QHashIterator
<QString
, bool> it( oldSensorNames
);
363 while ( it
.hasNext() ) {
366 int currentNodeId
= hostId
; //Start from the host branch and work our way down the tree
367 QStringList absolutePath
= it
.key().split( '/' );
368 for ( int j
= 0; j
< absolutePath
.count()-1; ++j
) {
369 // Localize the sensor name part by part.
370 QString name
= KSGRD::SensorMgr
->translateSensorPath( absolutePath
[ j
] );
371 currentNodeId
= makeTreeBranch(currentNodeId
, name
);
373 removeSensor(hostInfo
, currentNodeId
, it
.key());
375 emit
sensorsAddedToHost( createIndex( mHostInfoMap
.keys().indexOf(hostId
), 0, hostId
) );
378 QModelIndex
SensorBrowserModel::parent ( const QModelIndex
& index
) const { //virtual
379 if(!index
.isValid() || index
.column() != 0)
380 return QModelIndex();
381 if(mHostInfoMap
.contains(index
.internalId())) return QModelIndex();
382 if(!mParentsTreeMap
.contains(index
.internalId())) {
383 kDebug(1215) << "Something is wrong with the model. Doesn't contain " << index
.internalId();
384 return QModelIndex();
386 int parentId
= mParentsTreeMap
.value(index
.internalId());
388 QModelIndex parentModelIndex
;
389 if(mHostInfoMap
.contains(parentId
)) {
390 parentModelIndex
= createIndex(mHostInfoMap
.keys().indexOf(parentId
), 0 , parentId
);
392 int parentsParentId
= mParentsTreeMap
.value(parentId
);
393 parentModelIndex
= createIndex(mTreeMap
.value(parentsParentId
).indexOf(parentId
), 0, parentId
);
395 Q_ASSERT(parentModelIndex
.isValid());
396 return parentModelIndex
;
398 int SensorBrowserModel::rowCount ( const QModelIndex
& parent
) const { //virtual
399 if(!parent
.isValid()) return mHostInfoMap
.size();
400 if(parent
.column() != 0) return 0;
401 return mTreeMap
.value(parent
.internalId()).size();
403 Qt::ItemFlags
SensorBrowserModel::flags ( const QModelIndex
& index
) const { //virtual
404 if(!index
.isValid()) return 0;
405 if(mSensorInfoMap
.contains(index
.internalId())) return Qt::ItemIsDragEnabled
| Qt::ItemIsSelectable
| Qt::ItemIsEnabled
;
406 else return Qt::ItemIsEnabled
;
409 SensorBrowserWidget::SensorBrowserWidget( QWidget
* parent
, KSGRD::SensorManager
* sm
)
410 : QTreeView( parent
), mSensorManager( sm
)
412 setModel(&mSensorBrowserModel
);
413 connect( mSensorManager
, SIGNAL( update() ), &mSensorBrowserModel
, SLOT( update() ) );
415 // setRootIsDecorated( false );
416 setDragDropMode(QAbstractItemView::DragOnly
);
418 //setMinimumWidth( 1 );
420 connect( &mSensorBrowserModel
, SIGNAL(sensorsAddedToHost(const QModelIndex
&)), this, SLOT(expand(const QModelIndex
&)));
422 KSGRD::SensorManagerIterator
it( mSensorManager
);
423 while ( it
.hasNext() ) {
424 KSGRD::SensorAgent
* sensorAgent
= it
.next().value();
425 QString hostName
= mSensorManager
->hostName( sensorAgent
);
426 mSensorBrowserModel
.addHost(sensorAgent
, hostName
);
427 kDebug() << "Adding host " << hostName
;
431 SensorBrowserWidget::~SensorBrowserWidget()
435 void SensorBrowserWidget::retranslateUi() {
437 this->setToolTip( i18n( "Drag sensors to empty cells of a worksheet "
438 "or the panel applet." ) );
439 this->setWhatsThis( i18n( "The sensor browser lists the connected hosts and the sensors "
440 "that they provide. Click and drag sensors into drop zones "
441 "of a worksheet or the panel applet. A display will appear "
442 "that visualizes the "
443 "values provided by the sensor. Some sensor displays can "
444 "display values of multiple sensors. Simply drag other "
445 "sensors on to the display to add more sensors." ) );
448 void SensorBrowserWidget::changeEvent( QEvent
* event
)
450 if (event
->type() == QEvent::LanguageChange
) {
452 mSensorBrowserModel
.retranslate();
453 mSensorBrowserModel
.update();
455 QWidget::changeEvent(event
);
458 void SensorBrowserWidget::disconnect()
460 QModelIndexList indexlist
= selectionModel()->selectedRows();
461 for(int i
=0; i
< indexlist
.size(); i
++)
463 mSensorBrowserModel
.disconnectHost(indexlist
.value(i
).internalId());
467 void SensorBrowserWidget::hostReconfigured( const QString
& )
469 // TODO: not yet implemented.
472 void SensorBrowserModel::clear() {
473 qDeleteAll(mHostInfoMap
);
474 mHostInfoMap
.clear();
478 void SensorBrowserModel::disconnectHost(uint id
)
480 disconnectHost(mHostInfoMap
.value(id
));
482 void SensorBrowserModel::disconnectHost(const HostInfo
*hostInfo
)
484 KSGRD::SensorMgr
->disengage( hostInfo
->sensorAgent() );
486 void SensorBrowserModel::disconnectHost(const QString
&hostname
)
488 QMapIterator
<int, HostInfo
*> it( mHostInfoMap
);
489 while ( it
.hasNext() ) {
491 if(it
.value()->hostName() == hostname
) {
492 disconnectHost(it
.value());
498 void SensorBrowserModel::addHost(KSGRD::SensorAgent
*sensorAgent
, const QString
&hostName
)
500 beginInsertRows( QModelIndex() , mHostInfoMap
.size(), mHostInfoMap
.size() );
501 HostInfo
* hostInfo
= new HostInfo( mIdCount
, sensorAgent
, hostName
);
502 mHostInfoMap
.insert(mIdCount
, hostInfo
);
503 mTreeMap
.insert(mIdCount
, QList
<int>());
504 mHostSensorsMap
.insert(mIdCount
, QHash
<QString
, bool>());
507 kDebug() << "Adding host " << hostName
<< "and sending monitors";
508 hostInfo
->sensorAgent()->sendRequest( "monitors", this, mIdCount
-1 );
511 void SensorBrowserModel::update()
513 kDebug() << "Updating";
514 QMapIterator
<int, HostInfo
*> it( mHostInfoMap
);
515 while ( it
.hasNext() ) {
517 KSGRD::SensorAgent
* sensorAgent
= it
.value()->sensorAgent();
519 sensorAgent
->sendRequest( "monitors", this, id
);
522 QMimeData
* SensorBrowserModel::mimeData ( const QModelIndexList
& indexes
) const { //virtual
523 QMimeData
*mimeData
= new QMimeData();
524 if(indexes
.size() != 1) return mimeData
;
525 SensorInfo
*sensor
= getSensorInfo(indexes
[0]);
526 if(!sensor
) return mimeData
;
527 // Create text drag object as
528 // "<hostname> <sensorname> <sensortype> <sensordescription>".
529 // Only the description may contain blanks.
531 Q_ASSERT(sensor
->hostInfo());
532 QString mDragText
= sensor
->hostInfo()->hostName() + ' ' +
533 sensor
->name() + ' ' +
534 sensor
->type()+ ' ' +
535 sensor
->description();
538 mimeData
->setData( "application/x-ksysguard", mDragText
.toUtf8() );
542 SensorInfo::SensorInfo( HostInfo
*hostInfo
, const QString
&name
,
543 const QString
&desc
, const QString
&type
)
544 : mName( name
), mDesc( desc
), mType( type
), mHostInfo( hostInfo
)
549 QString
SensorInfo::name() const
554 QString
SensorInfo::type() const
559 QString
SensorInfo::description() const
564 HostInfo
*SensorInfo::hostInfo() const
571 #include "SensorBrowser.moc"