/* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. KSysGuard is currently maintained by Chris Schlaeger <cs@kde.org>. Please do not commit any changes without consulting me first. Thanks! */ #include <qdragobject.h> #include <qtooltip.h> #include <qwhatsthis.h> #include <kdebug.h> #include <kiconloader.h> #include <klocale.h> #include <kmessagebox.h> #include <ksgrd/SensorManager.h> #include "SensorBrowser.h" class HostItem : public QListViewItem { public: HostItem( SensorBrowser *parent, const QString &text, int id, KSGRD::SensorAgent *host) : QListViewItem( parent, text ), mInited( false ), mId( id ), mHost( host ), mParent( parent ) { setExpandable( true ); } void setOpen( bool open ) { if ( open && !mInited ) { mInited = true; // request sensor list from host mHost->sendRequest( "monitors", mParent, mId ); } QListViewItem::setOpen( open ); } private: bool mInited; int mId; KSGRD::SensorAgent* mHost; SensorBrowser* mParent; }; SensorBrowser::SensorBrowser( QWidget* parent, KSGRD::SensorManager* sm, const char* name) : KListView( parent, name ), mSensorManager( sm ) { mHostInfoList.setAutoDelete(true); connect( mSensorManager, SIGNAL( update() ), SLOT( update() ) ); connect( this, SIGNAL( clicked( QListViewItem* ) ), SLOT( newItemSelected( QListViewItem* ) ) ); connect( this, SIGNAL( returnPressed( QListViewItem* ) ), SLOT( newItemSelected( QListViewItem* ) ) ); addColumn( i18n( "Sensor Browser" ) ); addColumn( i18n( "Sensor Type" ) ); setFullWidth( true ); QToolTip::add( this, i18n( "Drag sensors to empty cells of a worksheet " "or the panel applet." ) ); setRootIsDecorated( true ); // The sensor browser can be completely hidden. setMinimumWidth( 1 ); QWhatsThis::add( this, i18n( "The sensor browser lists the connected hosts and the sensors " "that they provide. Click and drag sensors into drop zones " "of a worksheet or the panel applet. A display will appear " "that visualizes the " "values provided by the sensor. Some sensor displays can " "display values of multiple sensors. Simply drag other " "sensors on to the display to add more sensors." ) ); } SensorBrowser::~SensorBrowser() { } void SensorBrowser::disconnect() { QPtrListIterator<HostInfo> it( mHostInfoList ); for ( ; it.current(); ++it ) if ( (*it)->listViewItem()->isSelected() ) { kdDebug(1215) << "Disconnecting " << (*it)->hostName() << endl; KSGRD::SensorMgr->requestDisengage( (*it)->sensorAgent() ); } } void SensorBrowser::hostReconfigured( const QString& ) { // TODO: not yet implemented. } void SensorBrowser::update() { static int id = 0; KSGRD::SensorManagerIterator it( mSensorManager ); mHostInfoList.clear(); clear(); KSGRD::SensorAgent* host; for ( int i = 0 ; ( host = it.current() ); ++it, ++i ) { QString hostName = mSensorManager->hostName( host ); HostItem* lvi = new HostItem( this, hostName, id, host ); QPixmap pix = KGlobal::iconLoader()->loadIcon( "computer", KIcon::Desktop, KIcon::SizeSmall ); lvi->setPixmap( 0, pix ); HostInfo* hostInfo = new HostInfo( id, host, hostName, lvi ); mHostInfoList.append( hostInfo ); ++id; } setMouseTracking( false ); } void SensorBrowser::newItemSelected( QListViewItem *item ) { static bool showAnnoyingPopup = true; if ( item && item->pixmap( 0 ) && showAnnoyingPopup) { showAnnoyingPopup = false; KMessageBox::information( this, i18n( "Drag sensors to empty fields in a worksheet." ), QString::null, "ShowSBUseInfo" ); } } void SensorBrowser::answerReceived( int id, const QString &answer ) { /* An answer has the following format: cpu/idle integer cpu/sys integer cpu/nice integer cpu/user integer ps table */ QPtrListIterator<HostInfo> it( mHostInfoList ); /* Check if id is still valid. It can get obsolete by rapid calls * of update() or when the sensor died. */ for ( ; it.current(); ++it ) if ( (*it)->id() == id ) break; if ( !it.current() ) return; KSGRD::SensorTokenizer lines( answer, '\n' ); for ( uint i = 0; i < lines.count(); ++i ) { if ( lines[ i ].isEmpty() ) break; KSGRD::SensorTokenizer words( lines[ i ], '\t' ); QString sensorName = words[ 0 ]; QString sensorType = words[ 1 ]; /* Calling update() a rapid sequence will create pending * requests that do not get erased by calling * clear(). Subsequent updates will receive the old pending * answers so we need to make sure that we register each * sensor only once. */ if ( (*it)->isRegistered( sensorName ) ) return; /* The sensor browser can display sensors in a hierachical order. * Sensors can be grouped into nodes by seperating the hierachical * nodes through slashes in the sensor name. E. g. cpu/user is * the sensor user in the cpu node. There is no limit for the * depth of nodes. */ KSGRD::SensorTokenizer absolutePath( sensorName, '/' ); QListViewItem* parent = (*it)->listViewItem(); for ( uint j = 0; j < absolutePath.count(); ++j ) { // Localize the sensor name part by part. QString name = KSGRD::SensorMgr->translateSensorPath( absolutePath[ j ] ); bool found = false; QListViewItem* sibling = parent->firstChild(); while ( sibling && !found ) { if (sibling->text( 0 ) == name ) { // The node or sensor is already known. found = true; } else sibling = sibling->nextSibling(); } if ( !found ) { QListViewItem* lvi = new QListViewItem( parent, name ); if ( j == absolutePath.count() - 1 ) { QPixmap pix = KGlobal::iconLoader()->loadIcon( "ksysguardd", KIcon::Desktop, KIcon::SizeSmall ); lvi->setPixmap( 0, pix ); lvi->setText( 1, KSGRD::SensorMgr->translateSensorType( sensorType ) ); // add sensor info to internal data structure (*it)->addSensor( lvi, sensorName, name, sensorType ); } else parent = lvi; // The child indicator might need to be updated. repaintItem( parent ); } else parent = sibling; } } repaintItem( (*it)->listViewItem() ); } void SensorBrowser::viewportMouseMoveEvent( QMouseEvent *e ) { /* setMouseTracking(false) seems to be broken. With current Qt * mouse tracking cannot be turned off. So we have to check each event * whether the LMB is really pressed. */ if ( !( e->state() & LeftButton ) ) return; QListViewItem* item = itemAt( e->pos() ); if ( !item ) // no item under cursor return; // Make sure that a sensor and not a node or hostname has been picked. QPtrListIterator<HostInfo> it( mHostInfoList ); for ( ; it.current() && !(*it)->isRegistered( item ); ++it ); if ( !it.current() ) return; // Create text drag object as // "<hostname> <sensorname> <sensortype> <sensordescription>". // Only the description may contain blanks. mDragText = (*it)->hostName() + " " + (*it)->sensorName( item ) + " " + (*it)->sensorType( item ) + " " + (*it)->sensorDescription( item ); QDragObject* dragObject = new QTextDrag( mDragText, this ); dragObject->dragCopy(); } QStringList SensorBrowser::listHosts() { QStringList hostList; QPtrListIterator<HostInfo> it( mHostInfoList ); for ( ; it.current(); ++it ) hostList.append( (*it)->hostName() ); return hostList; } QStringList SensorBrowser::listSensors( const QString &hostName ) { QPtrListIterator<HostInfo> it( mHostInfoList ); for ( ; it.current(); ++it ) { if ( (*it)->hostName() == hostName ) { return (*it)->allSensorNames(); } } return QStringList(); } /** Helper classes */ SensorInfo::SensorInfo( QListViewItem *lvi, const QString &name, const QString &desc, const QString &type ) : mLvi( lvi ), mName( name ), mDesc( desc ), mType( type ) { } QListViewItem* SensorInfo::listViewItem() const { return mLvi; } const QString& SensorInfo::name() const { return mName; } const QString& SensorInfo::type() const { return mType; } const QString& SensorInfo::description() const { return mDesc; } HostInfo::HostInfo( int id, const KSGRD::SensorAgent *agent, const QString &name, QListViewItem *lvi ) : mId( id ), mSensorAgent( agent ), mHostName( name ), mLvi( lvi ) { mSensorList.setAutoDelete( true ); } int HostInfo::id() const { return ( mId ); } const KSGRD::SensorAgent* HostInfo::sensorAgent() const { return mSensorAgent; } const QString& HostInfo::hostName() const { return mHostName; } QListViewItem* HostInfo::listViewItem() const { return mLvi; } const QString& HostInfo::sensorName( const QListViewItem *lvi ) const { QPtrListIterator<SensorInfo> it( mSensorList ); for ( ; it.current() && (*it)->listViewItem() != lvi; ++it ); Q_ASSERT( it.current() ); return ( (*it)->name() ); } QStringList HostInfo::allSensorNames() const { QStringList list; QPtrListIterator<SensorInfo> it( mSensorList ); for ( ; it.current(); ++it ) list.append( it.current()->name() ); return list; } const QString& HostInfo::sensorType( const QListViewItem *lvi ) const { QPtrListIterator<SensorInfo> it( mSensorList ); for ( ; it.current() && (*it)->listViewItem() != lvi; ++it ); Q_ASSERT( it.current() ); return ( (*it)->type() ); } const QString& HostInfo::sensorDescription( const QListViewItem *lvi ) const { QPtrListIterator<SensorInfo> it( mSensorList ); for ( ; it.current() && (*it)->listViewItem() != lvi; ++it ); Q_ASSERT( it.current() ); return ( (*it)->description() ); } void HostInfo::addSensor( QListViewItem *lvi, const QString& name, const QString& desc, const QString& type ) { SensorInfo* info = new SensorInfo( lvi, name, desc, type ); mSensorList.append( info ); } bool HostInfo::isRegistered( const QString& name ) const { QPtrListIterator<SensorInfo> it( mSensorList ); for ( ; it.current(); ++it ) if ( (*it)->name() == name ) return true; return false; } bool HostInfo::isRegistered( QListViewItem *lvi ) const { QPtrListIterator<SensorInfo> it( mSensorList ); for ( ; it.current(); ++it ) if ( (*it)->listViewItem() == lvi ) return true; return false; } #include "SensorBrowser.moc"