diff options
Diffstat (limited to 'ksysguard/gui/WorkSheet.cpp')
-rw-r--r-- | ksysguard/gui/WorkSheet.cpp | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/ksysguard/gui/WorkSheet.cpp b/ksysguard/gui/WorkSheet.cpp new file mode 100644 index 000000000..d465a998d --- /dev/null +++ b/ksysguard/gui/WorkSheet.cpp @@ -0,0 +1,698 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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 <tqclipboard.h> +#include <tqcursor.h> +#include <tqdragobject.h> +#include <tqfile.h> +#include <tqlayout.h> + +#include <kdebug.h> +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <tdepopupmenu.h> + +#include <SensorManager.h> + +#include "DancingBars.h" +#include "DummyDisplay.h" +#include "FancyPlotter.h" +#include "ListView.h" +#include "LogFile.h" +#include "MultiMeter.h" +#include "ProcessController.h" +#include "SensorLogger.h" +#include "WorkSheet.h" +#include "WorkSheetSettings.h" + +WorkSheet::WorkSheet( TQWidget *parent, const char *name ) + : TQWidget( parent, name ) +{ + mGridLayout = 0; + mRows = mColumns = 0; + mDisplayList = 0; + mModified = false; + mFileName = ""; + + setAcceptDrops( true ); +} + +WorkSheet::WorkSheet( uint rows, uint columns, uint interval, TQWidget* parent, + const char *name ) + : TQWidget( parent, name ) +{ + mRows = mColumns = 0; + mGridLayout = 0; + mDisplayList = 0; + updateInterval( interval ); + mModified = false; + mFileName = ""; + + createGrid( rows, columns ); + + // Initialize worksheet with dummy displays. + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) + replaceDisplay( r, c ); + + mGridLayout->activate(); + + setAcceptDrops( true ); +} + +WorkSheet::~WorkSheet() +{ +} + +bool WorkSheet::load( const TQString &fileName ) +{ + setModified( false ); + + mFileName = fileName; + TQFile file( mFileName ); + if ( !file.open( IO_ReadOnly ) ) { + KMessageBox::sorry( this, i18n( "Cannot open the file %1." ).arg( mFileName ) ); + return false; + } + + TQDomDocument doc; + + // Read in file and check for a valid XML header. + if ( !doc.setContent( &file) ) { + KMessageBox::sorry( this, i18n( "The file %1 does not contain valid XML." ) + .arg( mFileName ) ); + return false; + } + + // Check for proper document type. + if ( doc.doctype().name() != "KSysGuardWorkSheet" ) { + KMessageBox::sorry( this, i18n( "The file %1 does not contain a valid worksheet " + "definition, which must have a document type 'KSysGuardWorkSheet'.") + .arg( mFileName ) ); + return false; + } + + // Check for proper size. + TQDomElement element = doc.documentElement(); + updateInterval( element.attribute( "interval" ).toUInt() ); + if ( updateInterval() < 1 || updateInterval() > 300 ) + updateInterval( 2 ); + + bool rowsOk, columnsOk; + uint rows = element.attribute( "rows" ).toUInt( &rowsOk ); + uint columns = element.attribute( "columns" ).toUInt( &columnsOk ); + if ( !( rowsOk && columnsOk ) ) { + KMessageBox::sorry( this, i18n("The file %1 has an invalid worksheet size.") + .arg( mFileName ) ); + return false; + } + + createGrid( rows, columns ); + + uint i; + /* Load lists of hosts that are needed for the work sheet and try + * to establish a connection. */ + TQDomNodeList dnList = element.elementsByTagName( "host" ); + for ( i = 0; i < dnList.count(); ++i ) { + TQDomElement element = dnList.item( i ).toElement(); + bool ok; + int port = element.attribute( "port" ).toInt( &ok ); + if ( !ok ) + port = -1; + KSGRD::SensorMgr->engage( element.attribute( "name" ), + element.attribute( "shell" ), + element.attribute( "command" ), port ); + } + //if no hosts are specified, at least connect to localhost + if(dnList.count() == 0) + KSGRD::SensorMgr->engage( "localhost", "", "ksysguardd", -1); + + // Load the displays and place them into the work sheet. + dnList = element.elementsByTagName( "display" ); + for ( i = 0; i < dnList.count(); ++i ) { + TQDomElement element = dnList.item( i ).toElement(); + uint row = element.attribute( "row" ).toUInt(); + uint column = element.attribute( "column" ).toUInt(); + if ( row >= mRows || column >= mColumns) { + kdDebug(1215) << "Row or Column out of range (" << row << ", " + << column << ")" << endl; + return false; + } + + replaceDisplay( row, column, element ); + } + + // Fill empty cells with dummy displays + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) + if ( !mDisplayList[ r ][ c ] ) + replaceDisplay( r, c ); + + setModified( false ); + + return true; +} + +bool WorkSheet::save( const TQString &fileName ) +{ + mFileName = fileName; + + TQDomDocument doc( "KSysGuardWorkSheet" ); + doc.appendChild( doc.createProcessingInstruction( + "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); + + // save work sheet information + TQDomElement ws = doc.createElement( "WorkSheet" ); + doc.appendChild( ws ); + ws.setAttribute( "interval", updateInterval() ); + ws.setAttribute( "rows", mRows ); + ws.setAttribute( "columns", mColumns ); + + TQStringList hosts; + collectHosts( hosts ); + + // save host information (name, shell, etc.) + TQStringList::Iterator it; + for ( it = hosts.begin(); it != hosts.end(); ++it ) { + TQString shell, command; + int port; + + if ( KSGRD::SensorMgr->hostInfo( *it, shell, command, port ) ) { + TQDomElement host = doc.createElement( "host" ); + ws.appendChild( host ); + host.setAttribute( "name", *it ); + host.setAttribute( "shell", shell ); + host.setAttribute( "command", command ); + host.setAttribute( "port", port ); + } + } + + for ( uint r = 0; r < mRows; ++r ) + for (uint c = 0; c < mColumns; ++c ) + if ( !mDisplayList[ r ][ c ]->isA( "DummyDisplay" ) ) { + KSGRD::SensorDisplay* display = (KSGRD::SensorDisplay*)mDisplayList[ r ][ c ]; + TQDomElement element = doc.createElement( "display" ); + ws.appendChild( element ); + element.setAttribute( "row", r ); + element.setAttribute( "column", c ); + element.setAttribute( "class", display->className() ); + + display->saveSettings( doc, element ); + } + + TQFile file( mFileName ); + if ( !file.open( IO_WriteOnly ) ) { + KMessageBox::sorry( this, i18n( "Cannot save file %1" ).arg( mFileName ) ); + return false; + } + + TQTextStream s( &file ); + s.setEncoding( TQTextStream::UnicodeUTF8 ); + s << doc; + file.close(); + + setModified( false ); + + return true; +} + +void WorkSheet::cut() +{ + if ( !currentDisplay() || currentDisplay()->isA( "DummyDisplay" ) ) + return; + + TQClipboard* clip = TQApplication::clipboard(); + + clip->setText( currentDisplayAsXML() ); + + removeDisplay( currentDisplay() ); +} + +void WorkSheet::copy() +{ + if ( !currentDisplay() || currentDisplay()->isA( "DummyDisplay" ) ) + return; + + TQClipboard* clip = TQApplication::clipboard(); + + clip->setText( currentDisplayAsXML() ); +} + +void WorkSheet::paste() +{ + uint row, column; + if ( !currentDisplay( &row, &column ) ) + return; + + TQClipboard* clip = TQApplication::clipboard(); + + TQDomDocument doc; + /* Get text from clipboard and check for a valid XML header and + * proper document type. */ + if ( !doc.setContent( clip->text() ) || doc.doctype().name() != "KSysGuardDisplay" ) { + KMessageBox::sorry( this, i18n("The clipboard does not contain a valid display " + "description." ) ); + return; + } + + TQDomElement element = doc.documentElement(); + replaceDisplay( row, column, element ); +} + +void WorkSheet::setFileName( const TQString &fileName ) +{ + mFileName = fileName; + setModified( true ); +} + +const TQString& WorkSheet::fileName() const +{ + return mFileName; +} + +bool WorkSheet::modified() const +{ + return mModified; +} + +void WorkSheet::setTitle( const TQString &title ) +{ + mTitle = title; +} + +TQString WorkSheet::title() const +{ + return mTitle; +} + +KSGRD::SensorDisplay *WorkSheet::addDisplay( const TQString &hostName, + const TQString &sensorName, + const TQString &sensorType, + const TQString& sensorDescr, + uint row, uint column ) +{ + if ( !KSGRD::SensorMgr->engageHost( hostName ) ) { + TQString msg = i18n( "It is impossible to connect to \'%1\'." ).arg( hostName ); + KMessageBox::error( this, msg ); + + return 0; + } + + /* If the by 'row' and 'column' specified display is a TQGroupBox dummy + * display we replace the widget. Otherwise we just try to add + * the new sensor to an existing display. */ + if ( mDisplayList[ row ][ column ]->isA( "DummyDisplay" ) ) { + KSGRD::SensorDisplay* newDisplay = 0; + /* If the sensor type is supported by more than one display + * type we popup a menu so the user can select what display is + * wanted. */ + if ( sensorType == "integer" || sensorType == "float" ) { + TDEPopupMenu pm; + pm.insertTitle( i18n( "Select Display Type" ) ); + pm.insertItem( i18n( "&Signal Plotter" ), 1 ); + pm.insertItem( i18n( "&Multimeter" ), 2 ); + pm.insertItem( i18n( "&BarGraph" ), 3 ); + pm.insertItem( i18n( "S&ensorLogger" ), 4 ); + switch ( pm.exec( TQCursor::pos() ) ) { + case 1: + newDisplay = new FancyPlotter( this, "FancyPlotter", sensorDescr ); + break; + case 2: + newDisplay = new MultiMeter( this, "MultiMeter", sensorDescr ); + break; + case 3: + newDisplay = new DancingBars( this, "DancingBars", sensorDescr ); + break; + case 4: + newDisplay = new SensorLogger( this, "SensorLogger", sensorDescr ); + break; + default: + return 0; + } + } else if ( sensorType == "listview" ) + newDisplay = new ListView( this, "ListView", sensorDescr ); + else if ( sensorType == "logfile" ) + newDisplay = new LogFile( this, "LogFile", sensorDescr ); + else if ( sensorType == "sensorlogger" ) + newDisplay = new SensorLogger( this, "SensorLogger", sensorDescr ); + else if ( sensorType == "table" ) + newDisplay = new ProcessController( this ); + else { + kdDebug(1215) << "Unkown sensor type: " << sensorType << endl; + return 0; + } + + replaceDisplay( row, column, newDisplay ); + } + + mDisplayList[ row ][ column ]->addSensor( hostName, sensorName, sensorType, sensorDescr ); + + setModified( true ); + + return ((KSGRD::SensorDisplay*)mDisplayList[ row ][ column ] ); +} + +void WorkSheet::settings() +{ + WorkSheetSettings dlg( this ); + + /* The sheet name should be changed with the "Save as..." function, + * so we don't have to display the display frame. */ + dlg.setSheetTitle( mTitle ); + dlg.setRows( mRows ); + dlg.setColumns( mColumns ); + dlg.setInterval( updateInterval() ); + + if ( dlg.exec() ) { + updateInterval( dlg.interval() ); + for (uint r = 0; r < mRows; ++r) + for (uint c = 0; c < mColumns; ++c) + if ( mDisplayList[ r ][ c ]->useGlobalUpdateInterval() ) + mDisplayList[ r ][ c ]->setUpdateInterval( updateInterval() ); + + resizeGrid( dlg.rows(), dlg.columns() ); + + mTitle = dlg.sheetTitle(); + emit titleChanged( this ); + + setModified( true ); + } +} + +void WorkSheet::showPopupMenu( KSGRD::SensorDisplay *display ) +{ + display->configureSettings(); +} + +void WorkSheet::setModified( bool modified ) +{ + if ( modified != mModified ) { + mModified = modified; + if ( !modified ) + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) + mDisplayList[ r ][ c ]->setModified( false ); + + emit sheetModified( this ); + } +} + +void WorkSheet::applyStyle() +{ + for ( uint r = 0; r < mRows; ++r ) + for (uint c = 0; c < mColumns; ++c ) + mDisplayList[ r ][ c ]->applyStyle(); +} + +void WorkSheet::dragEnterEvent( TQDragEnterEvent *e ) +{ + e->accept( TQTextDrag::canDecode( e ) ); +} + +void WorkSheet::dropEvent( TQDropEvent *e ) +{ + TQString dragObject; + + if ( TQTextDrag::decode( e, dragObject) ) { + // The host name, sensor name and type are seperated by a ' '. + TQStringList parts = TQStringList::split( ' ', dragObject ); + + TQString hostName = parts[ 0 ]; + TQString sensorName = parts[ 1 ]; + TQString sensorType = parts[ 2 ]; + TQString sensorDescr = parts[ 3 ]; + + if ( hostName.isEmpty() || sensorName.isEmpty() || sensorType.isEmpty() ) { + return; + } + + /* Find the sensor display that is supposed to get the drop + * event and replace or add sensor. */ + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) + if ( mDisplayList[ r ][ c ]->geometry().contains( e->pos() ) ) { + addDisplay( hostName, sensorName, sensorType, sensorDescr, r, c ); + return; + } + } +} + +TQSize WorkSheet::sizeHint() const +{ + return TQSize( 200,150 ); +} + +void WorkSheet::customEvent( TQCustomEvent *e ) +{ + if ( e->type() == TQEvent::User ) { + // SensorDisplays send out this event if they want to be removed. + + removeDisplay( (KSGRD::SensorDisplay*)e->data() ); + } +} + +bool WorkSheet::replaceDisplay( uint row, uint column, TQDomElement& element ) +{ + TQString classType = element.attribute( "class" ); + KSGRD::SensorDisplay* newDisplay; + if ( classType == "FancyPlotter" ) + newDisplay = new FancyPlotter( this ); + else if ( classType == "MultiMeter" ) + newDisplay = new MultiMeter( this ); + else if ( classType == "DancingBars" ) + newDisplay = new DancingBars( this ); + else if ( classType == "ListView" ) + newDisplay = new ListView( this ); + else if ( classType == "LogFile" ) + newDisplay = new LogFile( this ); + else if ( classType == "SensorLogger" ) + newDisplay = new SensorLogger( this ); + else if ( classType == "ProcessController" ) + newDisplay = new ProcessController( this ); + else { + kdDebug(1215) << "Unkown class " << classType << endl; + return false; + } + + if ( newDisplay->useGlobalUpdateInterval() ) + newDisplay->setUpdateInterval( updateInterval() ); + + // load display specific settings + if ( !newDisplay->restoreSettings( element ) ) + return false; + + replaceDisplay( row, column, newDisplay ); + + return true; +} + +void WorkSheet::replaceDisplay( uint row, uint column, KSGRD::SensorDisplay* newDisplay ) +{ + // remove the old display at this location + delete mDisplayList[ row ][ column ]; + + // insert new display + if ( !newDisplay ) + mDisplayList[ row ][ column ] = new DummyDisplay( this, "DummyDisplay" ); + else { + mDisplayList[ row ][ column ] = newDisplay; + if ( mDisplayList[ row ][ column ]->useGlobalUpdateInterval() ) + mDisplayList[ row ][ column ]->setUpdateInterval( updateInterval() ); + connect( newDisplay, TQT_SIGNAL( showPopupMenu( KSGRD::SensorDisplay* ) ), + TQT_SLOT( showPopupMenu( KSGRD::SensorDisplay* ) ) ); + connect( newDisplay, TQT_SIGNAL( modified( bool ) ), + TQT_SLOT( setModified( bool ) ) ); + } + + + mGridLayout->addWidget( mDisplayList[ row ][ column ], row, column ); + + if ( isVisible() ) { + mDisplayList[ row ][ column ]->show(); + } + + setMinimumSize(sizeHint()); + + setModified( true ); +} + +void WorkSheet::removeDisplay( KSGRD::SensorDisplay *display ) +{ + if ( !display ) + return; + + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) + if ( mDisplayList[ r ][ c ] == display ) { + replaceDisplay( r, c ); + setModified( true ); + return; + } +} + +void WorkSheet::collectHosts( TQStringList &list ) +{ + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) + if ( !mDisplayList[ r ][ c ]->isA( "DummyDisplay" ) ) + ((KSGRD::SensorDisplay*)mDisplayList[ r ][ c ])->hosts( list ); +} + +void WorkSheet::createGrid( uint rows, uint columns ) +{ + mRows = rows; + mColumns = columns; + + // create grid layout with specified dimentions + mGridLayout = new TQGridLayout( this, mRows, mColumns, 5 ); + + mDisplayList = new KSGRD::SensorDisplay**[ mRows ]; + for ( uint r = 0; r < mRows; ++r ) { + mDisplayList[ r ] = new KSGRD::SensorDisplay*[ mColumns ]; + for ( uint c = 0; c < mColumns; ++c ) + mDisplayList[ r ][ c ] = 0; + } + + /* set stretch factors for rows and columns */ + for ( uint r = 0; r < mRows; ++r ) + mGridLayout->setRowStretch( r, 100 ); + for ( uint c = 0; c < mColumns; ++c ) + mGridLayout->setColStretch( c, 100 ); +} + +void WorkSheet::resizeGrid( uint newRows, uint newColumns ) +{ + uint r, c; + + /* Create new array for display pointers */ + KSGRD::SensorDisplay*** newDisplayList = new KSGRD::SensorDisplay**[ newRows ]; + for ( r = 0; r < newRows; ++r ) { + newDisplayList[ r ] = new KSGRD::SensorDisplay*[ newColumns ]; + for ( c = 0; c < newColumns; ++c ) { + if ( c < mColumns && r < mRows ) + newDisplayList[ r ][ c ] = mDisplayList[ r ][ c ]; + else + newDisplayList[ r ][ c ] = 0; + } + } + + /* remove obsolete displays */ + for ( r = 0; r < mRows; ++r ) { + for ( c = 0; c < mColumns; ++c ) + if ( r >= newRows || c >= newColumns ) + delete mDisplayList[ r ][ c ]; + delete mDisplayList[ r ]; + } + delete [] mDisplayList; + + /* now we make the new display the regular one */ + mDisplayList = newDisplayList; + + /* create new displays */ + for ( r = 0; r < newRows; ++r ) + for ( c = 0; c < newColumns; ++c ) + if ( r >= mRows || c >= mColumns ) + replaceDisplay( r, c ); + + /* set stretch factors for new rows and columns (if any) */ + for ( r = mRows; r < newRows; ++r ) + mGridLayout->setRowStretch( r, 100 ); + for ( c = mColumns; c < newColumns; ++c ) + mGridLayout->setColStretch( c, 100 ); + + /* Obviously Qt does not shrink the size of the QGridLayout + * automatically. So we simply force the rows and columns that + * are no longer used to have a strech factor of 0 and hence be + * invisible. */ + for ( r = newRows; r < mRows; ++r ) + mGridLayout->setRowStretch( r, 0 ); + for ( c = newColumns; c < mColumns; ++c ) + mGridLayout->setColStretch( c, 0 ); + + mRows = newRows; + mColumns = newColumns; + + fixTabOrder(); + + mGridLayout->activate(); +} + +KSGRD::SensorDisplay *WorkSheet::display( uint row, uint column ) { + if(row >= mRows || column >= mColumns) return 0; + return mDisplayList[row][column]; +} +KSGRD::SensorDisplay *WorkSheet::currentDisplay( uint *row, uint *column ) +{ + for ( uint r = 0 ; r < mRows; ++r ) + for ( uint c = 0 ; c < mColumns; ++c ) + if ( mDisplayList[ r ][ c ]->hasFocus() ) { + if ( row ) + *row = r; + if ( column ) + *column = c; + return ( mDisplayList[ r ][ c ] ); + } + + return 0; +} + +void WorkSheet::fixTabOrder() +{ + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) { + if ( c + 1 < mColumns ) + setTabOrder( mDisplayList[ r ][ c ], mDisplayList[ r ][ c + 1 ] ); + else if ( r + 1 < mRows ) + setTabOrder( mDisplayList[ r ][ c ], mDisplayList[ r + 1 ][ 0 ] ); + } +} + +TQString WorkSheet::currentDisplayAsXML() +{ + KSGRD::SensorDisplay* display = currentDisplay(); + if ( !display ) + return TQString::null; + + /* We create an XML description of the current display. */ + TQDomDocument doc( "KSysGuardDisplay" ); + doc.appendChild( doc.createProcessingInstruction( + "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); + + TQDomElement element = doc.createElement( "display" ); + doc.appendChild( element ); + element.setAttribute( "class", display->className() ); + display->saveSettings( doc, element ); + + return doc.toString(); +} + +void WorkSheet::setIsOnTop( bool /* onTop */ ) +{ +/* + for ( uint r = 0; r < mRows; ++r ) + for ( uint c = 0; c < mColumns; ++c ) + mDisplayList[ r ][ c ]->setIsOnTop( onTop ); +*/ +} + +#include "WorkSheet.moc" |