1 /*
2 KSysGuard, the KDE System Guard
3
4 Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <QClipboard>
24 #include <QCursor>
25 #include <QLayout>
26 #include <QTextStream>
27 #include <QGridLayout>
28 #include <QEvent>
29 #include <QDropEvent>
30 #include <QDragEnterEvent>
31 #include <QFile>
32 #include <QByteArray>
33 #include <QApplication>
34 #include <QDebug>
35 #include <QDir>
36 #include <QFileInfo>
37 #include <QMimeData>
38
39 #include <KLocalizedString>
40 #include <KMessageBox>
41 #include <QMenu>
42
43 #include <ksgrd/SensorManager.h>
44
45 #include "DancingBars.h"
46 #include "DummyDisplay.h"
47 #include "FancyPlotter.h"
48 #include "ksysguard.h"
49 #include "ListView.h"
50 #include "LogFile.h"
51 #include "MultiMeter.h"
52 #include "ProcessController.h"
53 #include "SensorLogger.h"
54 #include "WorkSheet.h"
55 #include "WorkSheetSettings.h"
56
WorkSheet(QWidget * parent)57 WorkSheet::WorkSheet( QWidget *parent )
58 : QWidget( parent )
59 {
60 mGridLayout = nullptr;
61 mRows = mColumns = 0;
62 setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
63 setAcceptDrops( true );
64 }
65
WorkSheet(int rows,int columns,float interval,QWidget * parent)66 WorkSheet::WorkSheet( int rows, int columns, float interval, QWidget* parent )
67 : QWidget( parent)
68 {
69 mGridLayout = nullptr;
70 setUpdateInterval( interval );
71
72 createGrid( rows, columns );
73
74 mGridLayout->activate();
75
76 setAcceptDrops( true );
77 }
78
~WorkSheet()79 WorkSheet::~WorkSheet()
80 {
81 }
82
load(const QString & fileName)83 bool WorkSheet::load( const QString &fileName )
84 {
85 QFile file( fileName );
86 if ( !file.open( QIODevice::ReadOnly ) ) {
87 KMessageBox::sorry( this, i18n( "Cannot open the file %1." , fileName ) );
88 return false;
89 }
90
91 QDomDocument doc;
92
93 // Read in file and check for a valid XML header.
94 if ( !doc.setContent( &file) ) {
95 KMessageBox::sorry( this, i18n( "The file %1 does not contain valid XML." ,
96 fileName ) );
97 return false;
98 }
99
100 // Check for proper document type.
101 if ( doc.doctype().name() != QLatin1String("KSysGuardWorkSheet") ) {
102 KMessageBox::sorry( this, i18n( "The file %1 does not contain a valid worksheet "
103 "definition, which must have a document type 'KSysGuardWorkSheet'.",
104 fileName ) );
105 return false;
106 }
107
108 QDomElement element = doc.documentElement();
109
110 bool rowsOk, columnsOk;
111 int rows = element.attribute( QStringLiteral("rows") ).toInt( &rowsOk );
112 int columns = element.attribute( QStringLiteral("columns") ).toInt( &columnsOk );
113 if ( !( rowsOk && columnsOk ) ) {
114 KMessageBox::sorry( this, i18n("The file %1 has an invalid worksheet size.",
115 fileName ) );
116 return false;
117 }
118
119 // Check for proper size.
120 float interval = element.attribute( QStringLiteral("interval"), QStringLiteral("0.5") ).toFloat();
121 if( interval < 0 || interval > 100000 ) //make sure the interval is fairly sane
122 interval = 0.5;
123
124 setUpdateInterval( interval );
125
126 createGrid( rows, columns );
127
128 mGridLayout->activate();
129
130 mTitle = element.attribute( QStringLiteral("title"));
131 mTranslatedTitle = mTitle.isEmpty() ? QLatin1String("") : i18n(mTitle.toUtf8().constData());
132 bool ok;
133 mSharedSettings.locked = element.attribute( QStringLiteral("locked") ).toUInt( &ok );
134 if(!ok) mSharedSettings.locked = false;
135
136 int i;
137 /* Load lists of hosts that are needed for the work sheet and try
138 * to establish a connection. */
139 QDomNodeList dnList = element.elementsByTagName( QStringLiteral("host") );
140 for ( i = 0; i < dnList.count(); ++i ) {
141 QDomElement element = dnList.item( i ).toElement();
142 bool ok;
143 int port = element.attribute( QStringLiteral("port") ).toInt( &ok );
144 if ( !ok )
145 port = -1;
146 KSGRD::SensorMgr->engage( element.attribute( QStringLiteral("name") ),
147 element.attribute( QStringLiteral("shell") ),
148 element.attribute( QStringLiteral("command") ), port );
149 }
150 //if no hosts are specified, at least connect to localhost
151 if(dnList.count() == 0)
152 KSGRD::SensorMgr->engage( QStringLiteral("localhost"), QLatin1String(""), QStringLiteral("ksysguardd"), -1);
153
154 // Load the displays and place them into the work sheet.
155 dnList = element.elementsByTagName( QStringLiteral("display") );
156 for ( i = 0; i < dnList.count(); ++i ) {
157 QDomElement element = dnList.item( i ).toElement();
158 int row = element.attribute( QStringLiteral("row") ).toInt();
159 int column = element.attribute( QStringLiteral("column") ).toInt();
160 int rowSpan = element.attribute( QStringLiteral("rowSpan"), QStringLiteral("1") ).toInt();
161 int columnSpan = element.attribute( QStringLiteral("columnSpan"), QStringLiteral("1") ).toInt();
162 if ( row < 0 || rowSpan < 0 || (row + rowSpan - 1) >= mRows || column < 0 || columnSpan < 0 || (column + columnSpan - 1) >= mColumns) {
163 qDebug() << "Row or Column out of range (" << row << ", "
164 << column << ")-(" << (row + rowSpan - 1) << ", " << (column + columnSpan - 1) << ")";
165 return false;
166 }
167 replaceDisplay( row, column, element, rowSpan, columnSpan );
168 }
169
170 mFullFileName = fileName;
171 return true;
172 }
173
save(const QString & fileName)174 bool WorkSheet::save( const QString &fileName )
175 {
176 return exportWorkSheet(fileName);
177 }
178
exportWorkSheet(const QString & fileName)179 bool WorkSheet::exportWorkSheet( const QString &fileName )
180 {
181 QDomDocument doc( QStringLiteral("KSysGuardWorkSheet") );
182 doc.appendChild( doc.createProcessingInstruction(
183 QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"") ) );
184
185 // save work sheet information
186 QDomElement ws = doc.createElement( QStringLiteral("WorkSheet") );
187 doc.appendChild( ws );
188 ws.setAttribute( QStringLiteral("title"), mTitle );
189 ws.setAttribute( QStringLiteral("locked"), mSharedSettings.locked?"1":"0" );
190 ws.setAttribute( QStringLiteral("interval"), updateInterval() );
191 ws.setAttribute( QStringLiteral("rows"), mRows );
192 ws.setAttribute( QStringLiteral("columns"), mColumns );
193
194 QStringList hosts;
195 collectHosts( hosts );
196
197 // save host information (name, shell, etc.)
198 QStringList::Iterator it;
199 for ( it = hosts.begin(); it != hosts.end(); ++it ) {
200 QString shell, command;
201 int port;
202
203 if ( KSGRD::SensorMgr->hostInfo( *it, shell, command, port ) ) {
204 QDomElement host = doc.createElement( QStringLiteral("host") );
205 ws.appendChild( host );
206 host.setAttribute( QStringLiteral("name"), *it );
207 host.setAttribute( QStringLiteral("shell"), shell );
208 host.setAttribute( QStringLiteral("command"), command );
209 host.setAttribute( QStringLiteral("port"), port );
210 }
211 }
212
213 for (int i = 0; i < mGridLayout->count(); i++)
214 {
215 KSGRD::SensorDisplay* display = static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(i)->widget());
216 if (display->metaObject()->className() != QByteArray("DummyDisplay"))
217 {
218 int row, column, rowSpan, columnSpan;
219 mGridLayout->getItemPosition(i, &row, &column, &rowSpan, &columnSpan);
220
221 QDomElement element = doc.createElement(QStringLiteral("display"));
222 ws.appendChild(element);
223 element.setAttribute(QStringLiteral("row"), row);
224 element.setAttribute(QStringLiteral("column"), column);
225 element.setAttribute(QStringLiteral("rowSpan"), rowSpan);
226 element.setAttribute(QStringLiteral("columnSpan"), columnSpan);
227 element.setAttribute(QStringLiteral("class"), display->metaObject()->className());
228
229 display->saveSettings(doc, element);
230 }
231 }
232
233 if (!QFileInfo::exists(QFileInfo(fileName).path())) {
234 QDir().mkpath(QFileInfo(fileName).path());
235 }
236
237 QFile file( fileName );
238 if ( !file.open( QIODevice::WriteOnly ) ) {
239 KMessageBox::sorry( this, i18n( "Cannot save file %1" , fileName ) );
240 return false;
241 }
242
243 QTextStream s( &file );
244 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
245 s.setCodec( "UTF-8" );
246 #endif
247 s << doc;
248 file.close();
249
250 return true;
251 }
252
cut()253 void WorkSheet::cut()
254 {
255 if ( !currentDisplay() || currentDisplay()->metaObject()->className() == QByteArray("DummyDisplay" ) )
256 return;
257
258 QClipboard* clip = QApplication::clipboard();
259
260 clip->setText( currentDisplayAsXML() );
261
262 removeDisplay( currentDisplay() );
263 }
264
copy()265 void WorkSheet::copy()
266 {
267 if ( !currentDisplay() || currentDisplay()->metaObject()->className() == QByteArray( "DummyDisplay" ) )
268 return;
269
270 QClipboard* clip = QApplication::clipboard();
271
272 clip->setText( currentDisplayAsXML() );
273 }
274
paste()275 void WorkSheet::paste()
276 {
277 int row, column;
278 if ( !currentDisplay( &row, &column ) )
279 return;
280
281 QClipboard* clip = QApplication::clipboard();
282
283 QDomDocument doc;
284 /* Get text from clipboard and check for a valid XML header and
285 * proper document type. */
286 if ( !doc.setContent( clip->text() ) || doc.doctype().name() != QLatin1String("KSysGuardDisplay") ) {
287 KMessageBox::sorry( this, i18n("The clipboard does not contain a valid display "
288 "description." ) );
289 return;
290 }
291
292 QDomElement element = doc.documentElement();
293 replaceDisplay( row, column, element );
294 }
295
setFileName(const QString & fileName)296 void WorkSheet::setFileName( const QString &fileName )
297 {
298 mFileName = fileName;
299 }
300
fullFileName() const301 QString WorkSheet::fullFileName() const
302 {
303 return mFullFileName;
304 }
305
fileName() const306 QString WorkSheet::fileName() const
307 {
308 return mFileName;
309 }
310
setTitle(const QString & title)311 void WorkSheet::setTitle( const QString &title )
312 {
313 mTitle = title;
314 mTranslatedTitle = mTitle.isEmpty() ? QLatin1String("") : i18n(mTitle.toUtf8().constData());
315 emit titleChanged(this);
316 }
317
translatedTitle() const318 QString WorkSheet::translatedTitle() const {
319 return mTranslatedTitle;
320 }
321
title() const322 QString WorkSheet::title() const {
323 return mTitle;
324 }
325
insertDisplay(DisplayType displayType,QString displayTitle,int row,int column,int rowSpan,int columnSpan)326 KSGRD::SensorDisplay* WorkSheet::insertDisplay( DisplayType displayType, QString displayTitle, int row, int column, int rowSpan, int columnSpan )
327 {
328 KSGRD::SensorDisplay* newDisplay = nullptr;
329 switch(displayType) {
330 case DisplayDummy:
331 newDisplay = new DummyDisplay( this, &mSharedSettings );
332 break;
333 case DisplayFancyPlotter:
334 newDisplay = new FancyPlotter( this, displayTitle, &mSharedSettings );
335 break;
336 case DisplayMultiMeter:
337 newDisplay = new MultiMeter( this, displayTitle, &mSharedSettings);
338 break;
339 case DisplayDancingBars:
340 newDisplay = new DancingBars( this, displayTitle, &mSharedSettings);
341 break;
342 case DisplaySensorLogger:
343 newDisplay = new SensorLogger( this, displayTitle, &mSharedSettings);
344 break;
345 case DisplayListView:
346 newDisplay = new ListView( this, displayTitle, &mSharedSettings);
347 break;
348 case DisplayLogFile:
349 newDisplay = new LogFile( this, displayTitle, &mSharedSettings );
350 break;
351 case DisplayProcessControllerRemote:
352 newDisplay = new ProcessController(this, &mSharedSettings);
353 newDisplay->setObjectName(QStringLiteral("remote process controller"));
354 break;
355 case DisplayProcessControllerLocal:
356 newDisplay = new ProcessController(this, &mSharedSettings);
357 if (!Toplevel->localProcessController())
358 Toplevel->setLocalProcessController(static_cast<ProcessController *>(newDisplay));
359 break;
360 default:
361 Q_ASSERT(false);
362 return nullptr;
363 }
364 newDisplay->applyStyle();
365 connect(&mTimer, &QTimer::timeout, newDisplay, &KSGRD::SensorDisplay::timerTick);
366 replaceDisplay( row, column, newDisplay, rowSpan, columnSpan );
367 return newDisplay;
368 }
369
addDisplay(const QString & hostName,const QString & sensorName,const QString & sensorType,const QString & sensorDescr,int row,int column)370 KSGRD::SensorDisplay *WorkSheet::addDisplay( const QString &hostName,
371 const QString &sensorName,
372 const QString &sensorType,
373 const QString& sensorDescr,
374 int row, int column )
375 {
376 KSGRD::SensorDisplay* display = static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAtPosition(row, column)->widget());
377 /* If the by 'row' and 'column' specified display is a QGroupBox dummy
378 * display we replace the widget. Otherwise we just try to add
379 * the new sensor to an existing display. */
380 if ( display->metaObject()->className() == QByteArray( "DummyDisplay" ) ) {
381 DisplayType displayType = DisplayDummy;
382 /* If the sensor type is supported by more than one display
383 * type we popup a menu so the user can select what display is
384 * wanted. */
385 if ( sensorType == QLatin1String("integer") || sensorType == QLatin1String("float") ) {
386 QMenu pm;
387 pm.addSection( i18n( "Select Display Type" ) );
388 QAction *a1 = pm.addAction( i18n( "&Line graph" ) );
389 QAction *a2 = pm.addAction( i18n( "&Digital display" ) );
390 QAction *a3 = pm.addAction( i18n( "&Bar graph" ) );
391 QAction *a4 = pm.addAction( i18n( "Log to a &file" ) );
392 QAction *execed = pm.exec( QCursor::pos() );
393 if (execed == a1)
394 displayType = DisplayFancyPlotter;
395 else if (execed == a2)
396 displayType = DisplayMultiMeter;
397 else if (execed == a3)
398 displayType = DisplayDancingBars;
399 else if (execed == a4)
400 displayType = DisplaySensorLogger;
401 else
402 return nullptr;
403 } else if ( sensorType == QLatin1String("listview") ) {
404 displayType = DisplayListView;
405 }
406 else if ( sensorType == QLatin1String("logfile") ) {
407 displayType = DisplayLogFile;
408 }
409 else if ( sensorType == QLatin1String("sensorlogger") ) {
410 displayType = DisplaySensorLogger;
411 }
412 else if ( sensorType == QLatin1String("table") ) {
413 if(hostName.isEmpty() || hostName == QLatin1String("localhost"))
414 displayType = DisplayProcessControllerLocal;
415 else
416 displayType = DisplayProcessControllerRemote;
417 }
418 else {
419 qDebug() << "Unknown sensor type: " << sensorType;
420 return nullptr;
421 }
422 display = insertDisplay(displayType, sensorDescr, row, column);
423 }
424 if (!display->addSensor( hostName, sensorName, sensorType, sensorDescr )) {
425 // Failed to add sensor, so we need to remove the display that we just added
426 removeDisplay(display);
427 return nullptr;
428 }
429
430 return display;
431 }
432
settings()433 void WorkSheet::settings()
434 {
435 WorkSheetSettings dlg( this, mSharedSettings.locked );
436 dlg.setSheetTitle( mTranslatedTitle );
437 dlg.setInterval( updateInterval() );
438
439 if(!mSharedSettings.locked) {
440 dlg.setRows( mRows );
441 dlg.setColumns( mColumns );
442 }
443
444 if ( dlg.exec() ) {
445 setUpdateInterval( dlg.interval() );
446
447 if (!mSharedSettings.locked)
448 resizeGrid( dlg.rows(), dlg.columns() );
449
450 if(mTranslatedTitle != dlg.sheetTitle()) { //Title has changed
451 if(mRows == 1 && mColumns == 1) {
452 static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(0)->widget())->setTitle(dlg.sheetTitle());
453 } else {
454 setTitle(dlg.sheetTitle());
455 }
456 }
457 }
458 }
459
showPopupMenu(KSGRD::SensorDisplay * display)460 void WorkSheet::showPopupMenu( KSGRD::SensorDisplay *display )
461 {
462 display->configureSettings();
463 }
464
applyStyle()465 void WorkSheet::applyStyle()
466 {
467 for (int i = 0; i < mGridLayout->count(); i++)
468 static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(i)->widget())->applyStyle();
469 }
470
dragEnterEvent(QDragEnterEvent * event)471 void WorkSheet::dragEnterEvent( QDragEnterEvent* event)
472 {
473 if ( !event->mimeData()->hasFormat(QStringLiteral("application/x-ksysguard")) )
474 return;
475 event->accept();
476 }
dragMoveEvent(QDragMoveEvent * event)477 void WorkSheet::dragMoveEvent( QDragMoveEvent *event )
478 {
479 /* Find the sensor display that is supposed to get the drop
480 * event and replace or add sensor. */
481 const QPoint globalPos = mapToGlobal( event->pos() );
482 for ( int i = 0; i < mGridLayout->count(); i++ ) {
483 KSGRD::SensorDisplay* display = static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(i)->widget());
484 const QRect widgetRect = QRect( display->mapToGlobal( QPoint( 0, 0 ) ),
485 display->size() );
486
487 if ( widgetRect.contains( globalPos ) ) {
488 QByteArray widgetType = display->metaObject()->className();
489 if(widgetType == "MultiMeter" || widgetType == "ProcessController" || widgetType == "table")
490 event->ignore(widgetRect);
491 else if(widgetType != "Dummy")
492 event->accept(widgetRect);
493 return;
494 }
495 }
496 }
497
dropEvent(QDropEvent * event)498 void WorkSheet::dropEvent( QDropEvent *event )
499 {
500 if ( !event->mimeData()->hasFormat(QStringLiteral("application/x-ksysguard")) )
501 return;
502
503 const QString dragObject = QString::fromUtf8(event->mimeData()->data(QStringLiteral("application/x-ksysguard")));
504
505 // The host name, sensor name and type are separated by a ' '.
506 QStringList parts = dragObject.split( ' ');
507
508 QString hostName = parts[ 0 ];
509 QString sensorName = parts[ 1 ];
510 QString sensorType = parts[ 2 ];
511 QString sensorDescr = QStringList(parts.mid( 3 )).join(' ');
512
513 if ( hostName.isEmpty() || sensorName.isEmpty() || sensorType.isEmpty() )
514 return;
515
516 /* Find the sensor display that is supposed to get the drop
517 * event and replace or add sensor. */
518 const QPoint globalPos = mapToGlobal( event->pos() );
519 for ( int i = 0; i < mGridLayout->count(); i++ ) {
520 KSGRD::SensorDisplay* display = static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(i)->widget());
521 const QSize displaySize = display->size();
522
523 const QPoint displayPoint( displaySize.width(), displaySize.height() );
524
525 const QRect widgetRect = QRect( display->mapToGlobal( QPoint( 0, 0 ) ),
526 display->mapToGlobal( displayPoint ) );
527
528
529 if ( widgetRect.contains( globalPos ) ) {
530 int row, column, rowSpan, columnSpan;
531 mGridLayout->getItemPosition(i, &row, &column, &rowSpan, &columnSpan);
532 addDisplay( hostName, sensorName, sensorType, sensorDescr, row, column );
533 return;
534 }
535 }
536 }
537
sizeHint() const538 QSize WorkSheet::sizeHint() const
539 {
540 return QSize( 800,600 );
541 }
542
event(QEvent * e)543 bool WorkSheet::event( QEvent *e )
544 {
545 if ( e->type() == QEvent::User ) {
546 // SensorDisplays send out this event if they want to be removed.
547 if ( KMessageBox::warningContinueCancel( this, i18n( "Remove this display?" ),
548 i18n("Remove Display"), KStandardGuiItem::del() )
549 == KMessageBox::Continue ) {
550 KSGRD::SensorDisplay::DeleteEvent *event = static_cast<KSGRD::SensorDisplay::DeleteEvent*>( e );
551 removeDisplay( event->display() );
552
553 return true;
554 }
555 }
556
557 return QWidget::event( e );
558 }
559
replaceDisplay(int row,int column,QDomElement & element,int rowSpan,int columnSpan)560 bool WorkSheet::replaceDisplay( int row, int column, QDomElement& element, int rowSpan, int columnSpan )
561 {
562 QString classType = element.attribute( QStringLiteral("class") );
563 QString hostName = element.attribute( QStringLiteral("hostName") );
564 DisplayType displayType = DisplayDummy;
565 KSGRD::SensorDisplay* newDisplay;
566
567 if ( classType == QLatin1String("FancyPlotter") )
568 displayType = DisplayFancyPlotter;
569 else if ( classType == QLatin1String("MultiMeter") )
570 displayType = DisplayMultiMeter;
571 else if ( classType == QLatin1String("DancingBars") )
572 displayType = DisplayDancingBars;
573 else if ( classType == QLatin1String("ListView") )
574 displayType = DisplayListView;
575 else if ( classType == QLatin1String("LogFile") )
576 displayType = DisplayLogFile;
577 else if ( classType == QLatin1String("SensorLogger") )
578 displayType = DisplaySensorLogger;
579 else if ( classType == QLatin1String("ProcessController") ) {
580 if(hostName.isEmpty() || hostName == QLatin1String("localhost"))
581 displayType = DisplayProcessControllerLocal;
582 else
583 displayType = DisplayProcessControllerRemote;
584 } else {
585 qDebug() << "Unknown class " << classType;
586 return false;
587 }
588
589 newDisplay = insertDisplay(displayType, i18n("Dummy"), row, column, rowSpan, columnSpan);
590
591 // load display specific settings
592 if ( !newDisplay->restoreSettings( element ) )
593 return false;
594
595 return true;
596 }
597
598
replaceDisplay(int row,int column,KSGRD::SensorDisplay * newDisplay,int rowSpan,int columnSpan)599 void WorkSheet::replaceDisplay( int row, int column, KSGRD::SensorDisplay* newDisplay, int rowSpan, int columnSpan )
600 {
601 if ( !newDisplay )
602 newDisplay = new DummyDisplay( this, &mSharedSettings );
603
604 // remove the old display && sensor frame at this location
605 QSet<QLayoutItem*> oldDisplays;
606 for (int i = row; i < row + rowSpan; i++)
607 for (int j = column; j < column + columnSpan; j++)
608 {
609 QLayoutItem* item = mGridLayout->itemAtPosition(i, j);
610 if (item)
611 oldDisplays.insert(item);
612 }
613
614 for (QSet<QLayoutItem*>::iterator iter = oldDisplays.begin(); iter != oldDisplays.end(); iter++)
615 {
616 QLayoutItem* item = *iter;
617
618 int oldDisplayRow, oldDisplayColumn, oldDisplayRowSpan, oldDisplayColumnSpan;
619 mGridLayout->getItemPosition(mGridLayout->indexOf(item->widget()), &oldDisplayRow, &oldDisplayColumn, &oldDisplayRowSpan, &oldDisplayColumnSpan);
620
621 mGridLayout->removeItem(item);
622 if (item->widget() != Toplevel->localProcessController())
623 delete item->widget();
624 delete item;
625
626 for (int i = oldDisplayRow; i < oldDisplayRow + oldDisplayRowSpan; i++)
627 for (int j = oldDisplayColumn; j < oldDisplayColumn + oldDisplayColumnSpan; j++)
628 if ((i < row || i >= row + rowSpan || j < column || j >= column + columnSpan) && !mGridLayout->itemAtPosition(i, j))
629 mGridLayout->addWidget(new DummyDisplay(this, &mSharedSettings), i, j);
630 }
631
632
633 mGridLayout->addWidget(newDisplay, row, column, rowSpan, columnSpan);
634
635 if (newDisplay->metaObject()->className() != QByteArray("DummyDisplay"))
636 {
637 connect(newDisplay, &KSGRD::SensorDisplay::showPopupMenu, this, &WorkSheet::showPopupMenu);
638 newDisplay->setDeleteNotifier(this);
639 }
640
641 // if there's only item, the tab's title should be the widget's title
642 if (row == 0 && mRows == rowSpan && column == 0 && mColumns == columnSpan)
643 {
644 connect(newDisplay, &KSGRD::SensorDisplay::titleChanged, this, &WorkSheet::setTitle);
645 setTitle(newDisplay->title());
646 }
647 if (isVisible())
648 newDisplay->show();
649 }
650
refreshSheet()651 void WorkSheet::refreshSheet()
652 {
653 for (int i = 0; i < mGridLayout->count(); i++)
654 static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(i)->widget())->timerTick();
655 }
656
removeDisplay(KSGRD::SensorDisplay * display)657 void WorkSheet::removeDisplay( KSGRD::SensorDisplay *display )
658 {
659 if ( !display )
660 return;
661
662 int row, column, rowSpan, columnSpan;
663 mGridLayout->getItemPosition(mGridLayout->indexOf(display), &row, &column, &rowSpan, &columnSpan);
664 replaceDisplay(row, column);
665 }
666
collectHosts(QStringList & list)667 void WorkSheet::collectHosts( QStringList &list )
668 {
669 for (int i = 0; i < mGridLayout->count(); i++)
670 static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(i)->widget())->hosts(list);
671 }
672
createGrid(int rows,int columns)673 void WorkSheet::createGrid( int rows, int columns )
674 {
675 mRows = rows;
676 mColumns = columns;
677
678 // create grid layout with specified dimensions
679 mGridLayout = new QGridLayout( this );
680 mGridLayout->setSpacing( 5 );
681
682 /* set stretch factors for rows and columns */
683 for ( int r = 0; r < mRows; ++r )
684 mGridLayout->setRowStretch( r, 100 );
685 for ( int c = 0; c < mColumns; ++c )
686 mGridLayout->setColumnStretch( c, 100 );
687
688 for (int r = 0; r < mRows; r++)
689 for (int c = 0; c < mColumns; c++)
690 replaceDisplay(r, c);
691 }
692
resizeGrid(int newRows,int newColumns)693 void WorkSheet::resizeGrid( int newRows, int newColumns )
694 {
695 int oldRows = mRows, oldColumns = mColumns;
696 mRows = newRows;
697 mColumns = newColumns;
698
699 /* delete any excess displays */
700 for (int i = 0; i < mGridLayout->count(); i++)
701 {
702 int row, column, rowSpan, columnSpan;
703 mGridLayout->getItemPosition(i, &row, &column, &rowSpan, &columnSpan);
704 if (row + rowSpan - 1 >= mRows || column + columnSpan - 1 >= mColumns)
705 {
706 QLayoutItem* item = mGridLayout->takeAt(i);
707 if (item->widget() != Toplevel->localProcessController())
708 delete item->widget();
709 delete item;
710 --i;
711 }
712 }
713
714 /* create new displays */
715 if (mRows > oldRows || mColumns > oldColumns)
716 for (int i = 0; i < mRows; ++i)
717 for (int j = 0; j < mColumns; ++j)
718 if (i >= oldRows || j >= oldColumns)
719 replaceDisplay(i, j);
720
721 /* set stretch factors for new rows and columns (if any) */
722 for ( int r = oldRows; r < mRows; ++r )
723 mGridLayout->setRowStretch( r, 100 );
724 for ( int c = oldColumns; c < mColumns; ++c )
725 mGridLayout->setColumnStretch( c, 100 );
726
727 /* Obviously Qt does not shrink the size of the QGridLayout
728 * automatically. So we simply force the rows and columns that
729 * are no longer used to have a stretch factor of 0 and hence be
730 * invisible. */
731 for ( int r = mRows; r < oldRows; ++r )
732 mGridLayout->setRowStretch( r, 0 );
733 for ( int c = mColumns; c < oldColumns; ++c )
734 mGridLayout->setColumnStretch( c, 0 );
735
736 fixTabOrder();
737
738 mGridLayout->activate();
739 }
740
currentDisplay(int * row,int * column)741 KSGRD::SensorDisplay *WorkSheet::currentDisplay( int * row, int * column )
742 {
743 int dummyRow, dummyColumn, rowSpan, columnSpan;
744 if (!row) row = &dummyRow;
745 if (!column) column = &dummyColumn;
746
747 for (int i = 0; i < mGridLayout->count(); i++)
748 {
749 KSGRD::SensorDisplay* display = static_cast<KSGRD::SensorDisplay*>(mGridLayout->itemAt(i)->widget());
750 if (display->hasFocus())
751 {
752 mGridLayout->getItemPosition(i, row, column, &rowSpan, &columnSpan);
753 return display;
754 }
755 }
756
757 return nullptr;
758 }
759
fixTabOrder()760 void WorkSheet::fixTabOrder()
761 {
762 QWidget* previous = nullptr;
763 for (int i = 0; i < mGridLayout->count(); i++)
764 {
765 QWidget* current = mGridLayout->itemAt(i)->widget();
766 if (previous)
767 setTabOrder(previous, current);
768 previous = current;
769 }
770 }
771
currentDisplayAsXML()772 QString WorkSheet::currentDisplayAsXML()
773 {
774 KSGRD::SensorDisplay* display = currentDisplay();
775 if ( !display )
776 return QString();
777
778 /* We create an XML description of the current display. */
779 QDomDocument doc( QStringLiteral("KSysGuardDisplay") );
780 doc.appendChild( doc.createProcessingInstruction(
781 QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"") ) );
782
783 QDomElement element = doc.createElement( QStringLiteral("display") );
784 doc.appendChild( element );
785 element.setAttribute( QStringLiteral("class"), display->metaObject()->className() );
786 display->saveSettings( doc, element );
787
788 return doc.toString();
789 }
790
changeEvent(QEvent * event)791 void WorkSheet::changeEvent( QEvent * event ) {
792 if (event->type() == QEvent::LanguageChange) {
793 setTitle(mTitle); //retranslate
794 }
795 }
796
setUpdateInterval(float secs)797 void WorkSheet::setUpdateInterval( float secs)
798 {
799 if(secs == 0)
800 mTimer.stop();
801 else {
802 mTimer.setInterval(secs*1000);
803 mTimer.start();
804 }
805 }
updateInterval() const806 float WorkSheet::updateInterval() const
807 {
808 if(mTimer.isActive())
809 return mTimer.interval()/1000.0;
810 else
811 return 0;
812 }
813
814
815