summaryrefslogtreecommitdiffstats
path: root/ksysguard/gui/WorkSheet.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ksysguard/gui/WorkSheet.cc')
-rw-r--r--ksysguard/gui/WorkSheet.cc698
1 files changed, 698 insertions, 0 deletions
diff --git a/ksysguard/gui/WorkSheet.cc b/ksysguard/gui/WorkSheet.cc
new file mode 100644
index 000000000..f6555a1c6
--- /dev/null
+++ b/ksysguard/gui/WorkSheet.cc
@@ -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 <qclipboard.h>
+#include <qcursor.h>
+#include <qdragobject.h>
+#include <qfile.h>
+#include <qlayout.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.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( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ mGridLayout = 0;
+ mRows = mColumns = 0;
+ mDisplayList = 0;
+ mModified = false;
+ mFileName = "";
+
+ setAcceptDrops( true );
+}
+
+WorkSheet::WorkSheet( uint rows, uint columns, uint interval, QWidget* parent,
+ const char *name )
+ : QWidget( 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 QString &fileName )
+{
+ setModified( false );
+
+ mFileName = fileName;
+ QFile file( mFileName );
+ if ( !file.open( IO_ReadOnly ) ) {
+ KMessageBox::sorry( this, i18n( "Cannot open the file %1." ).arg( mFileName ) );
+ return false;
+ }
+
+ QDomDocument 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.
+ QDomElement 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. */
+ QDomNodeList dnList = element.elementsByTagName( "host" );
+ for ( i = 0; i < dnList.count(); ++i ) {
+ QDomElement 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 ) {
+ QDomElement 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 QString &fileName )
+{
+ mFileName = fileName;
+
+ QDomDocument doc( "KSysGuardWorkSheet" );
+ doc.appendChild( doc.createProcessingInstruction(
+ "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) );
+
+ // save work sheet information
+ QDomElement ws = doc.createElement( "WorkSheet" );
+ doc.appendChild( ws );
+ ws.setAttribute( "interval", updateInterval() );
+ ws.setAttribute( "rows", mRows );
+ ws.setAttribute( "columns", mColumns );
+
+ QStringList hosts;
+ collectHosts( hosts );
+
+ // save host information (name, shell, etc.)
+ QStringList::Iterator it;
+ for ( it = hosts.begin(); it != hosts.end(); ++it ) {
+ QString shell, command;
+ int port;
+
+ if ( KSGRD::SensorMgr->hostInfo( *it, shell, command, port ) ) {
+ QDomElement 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 ];
+ QDomElement element = doc.createElement( "display" );
+ ws.appendChild( element );
+ element.setAttribute( "row", r );
+ element.setAttribute( "column", c );
+ element.setAttribute( "class", display->className() );
+
+ display->saveSettings( doc, element );
+ }
+
+ QFile file( mFileName );
+ if ( !file.open( IO_WriteOnly ) ) {
+ KMessageBox::sorry( this, i18n( "Cannot save file %1" ).arg( mFileName ) );
+ return false;
+ }
+
+ QTextStream s( &file );
+ s.setEncoding( QTextStream::UnicodeUTF8 );
+ s << doc;
+ file.close();
+
+ setModified( false );
+
+ return true;
+}
+
+void WorkSheet::cut()
+{
+ if ( !currentDisplay() || currentDisplay()->isA( "DummyDisplay" ) )
+ return;
+
+ QClipboard* clip = QApplication::clipboard();
+
+ clip->setText( currentDisplayAsXML() );
+
+ removeDisplay( currentDisplay() );
+}
+
+void WorkSheet::copy()
+{
+ if ( !currentDisplay() || currentDisplay()->isA( "DummyDisplay" ) )
+ return;
+
+ QClipboard* clip = QApplication::clipboard();
+
+ clip->setText( currentDisplayAsXML() );
+}
+
+void WorkSheet::paste()
+{
+ uint row, column;
+ if ( !currentDisplay( &row, &column ) )
+ return;
+
+ QClipboard* clip = QApplication::clipboard();
+
+ QDomDocument 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;
+ }
+
+ QDomElement element = doc.documentElement();
+ replaceDisplay( row, column, element );
+}
+
+void WorkSheet::setFileName( const QString &fileName )
+{
+ mFileName = fileName;
+ setModified( true );
+}
+
+const QString& WorkSheet::fileName() const
+{
+ return mFileName;
+}
+
+bool WorkSheet::modified() const
+{
+ return mModified;
+}
+
+void WorkSheet::setTitle( const QString &title )
+{
+ mTitle = title;
+}
+
+QString WorkSheet::title() const
+{
+ return mTitle;
+}
+
+KSGRD::SensorDisplay *WorkSheet::addDisplay( const QString &hostName,
+ const QString &sensorName,
+ const QString &sensorType,
+ const QString& sensorDescr,
+ uint row, uint column )
+{
+ if ( !KSGRD::SensorMgr->engageHost( hostName ) ) {
+ QString 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 QGroupBox 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" ) {
+ KPopupMenu 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( QCursor::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( QDragEnterEvent *e )
+{
+ e->accept( QTextDrag::canDecode( e ) );
+}
+
+void WorkSheet::dropEvent( QDropEvent *e )
+{
+ QString dragObject;
+
+ if ( QTextDrag::decode( e, dragObject) ) {
+ // The host name, sensor name and type are seperated by a ' '.
+ QStringList parts = QStringList::split( ' ', dragObject );
+
+ QString hostName = parts[ 0 ];
+ QString sensorName = parts[ 1 ];
+ QString sensorType = parts[ 2 ];
+ QString 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;
+ }
+ }
+}
+
+QSize WorkSheet::sizeHint() const
+{
+ return QSize( 200,150 );
+}
+
+void WorkSheet::customEvent( QCustomEvent *e )
+{
+ if ( e->type() == QEvent::User ) {
+ // SensorDisplays send out this event if they want to be removed.
+
+ removeDisplay( (KSGRD::SensorDisplay*)e->data() );
+ }
+}
+
+bool WorkSheet::replaceDisplay( uint row, uint column, QDomElement& element )
+{
+ QString 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, SIGNAL( showPopupMenu( KSGRD::SensorDisplay* ) ),
+ SLOT( showPopupMenu( KSGRD::SensorDisplay* ) ) );
+ connect( newDisplay, SIGNAL( modified( bool ) ),
+ 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( QStringList &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 QGridLayout( 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 ] );
+ }
+}
+
+QString WorkSheet::currentDisplayAsXML()
+{
+ KSGRD::SensorDisplay* display = currentDisplay();
+ if ( !display )
+ return QString::null;
+
+ /* We create an XML description of the current display. */
+ QDomDocument doc( "KSysGuardDisplay" );
+ doc.appendChild( doc.createProcessingInstruction(
+ "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) );
+
+ QDomElement 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"