dtor first
[personal-kdebase.git] / workspace / ksysguard / gui / ksysguard.cc
blobf0ec72c5e97b46d663d1f74c58a10d6b42627f30
1 /*
2 KSysGuard, the KDE System Guard
4 Copyright (c) 2006 - 2008 John Tapsell <john.tapsell@kde.org>
5 Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org>
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public
9 License version 2 or at your option version 3 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 KSysGuard has been written with some source code and ideas from
22 ktop (<1.0). Early versions of ktop have been written by Bernd
23 Johannes Wuebben <wuebben@math.cornell.edu> and Nicolas Leclercq
24 <nicknet@planete.net>.
28 #include <assert.h>
29 #include <ctype.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
35 #include <kaboutdata.h>
36 #include <kaction.h>
37 #include <kactioncollection.h>
38 #include <kapplication.h>
39 #include <kcmdlineargs.h>
40 #include <kdebug.h>
41 #include <kedittoolbar.h>
42 #include <kglobal.h>
43 #include <kglobalsettings.h>
44 #include <kicon.h>
45 #include <klocale.h>
46 #include <kmessagebox.h>
47 #include <ksgrd/SensorAgent.h>
48 #include <ksgrd/SensorManager.h>
49 #include <kstandarddirs.h>
50 #include <kstatusbar.h>
51 #include <kstandardaction.h>
52 #include <ktoggleaction.h>
53 #include <ktoolbar.h>
54 #include <kurl.h>
55 #include <kwindowsystem.h>
56 #include <QSplitter>
58 #include "../version.h"
59 #include "SensorBrowser.h"
60 #include "Workspace.h"
61 #include "WorkSheet.h"
62 #include "StyleEngine.h"
63 #include "HostConnector.h"
64 #include "ProcessController.h"
66 #include "ksysguard.h"
70 //Comment out to stop ksysguard from forking. Good for debugging
71 //#define FORK_KSYSGUARD
73 static const char Description[] = I18N_NOOP( "KDE System Monitor" );
74 TopLevel* topLevel;
76 /**
77 This is the constructor for the main widget. It sets up the menu and the
78 TaskMan widget.
80 TopLevel::TopLevel()
81 : KXmlGuiWindow( NULL )
83 QDBusConnection::sessionBus().registerObject("/", this, QDBusConnection::ExportScriptableSlots);
84 mTimerId = -1;
86 mSplitter = new QSplitter( this );
87 mSplitter->setOrientation( Qt::Horizontal );
88 mSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
89 setCentralWidget( mSplitter );
91 mSensorBrowser = 0;
93 mWorkSpace = new Workspace( mSplitter );
94 connect( mWorkSpace, SIGNAL( setCaption( const QString&) ),
95 SLOT( setCaption( const QString&) ) );
96 connect( mWorkSpace, SIGNAL( currentChanged( int ) ),
97 SLOT( currentTabChanged( int ) ) );
99 /* Create the status bar. It displays some information about the
100 * number of processes and the memory consumption of the local
101 * host. */
102 const int STATUSBAR_STRETCH=1;
104 sbProcessCount = new QLabel();
105 statusBar()->addWidget( sbProcessCount, STATUSBAR_STRETCH );
107 sbCpuStat = new QLabel();
108 statusBar()->addWidget( sbCpuStat, STATUSBAR_STRETCH );
110 sbMemTotal = new QLabel();
111 statusBar()->addWidget( sbMemTotal, STATUSBAR_STRETCH );
113 sbSwapTotal = new QLabel();
114 statusBar()->addWidget( sbSwapTotal, STATUSBAR_STRETCH );
116 statusBar()->hide();
118 // create actions for menu entries
119 mNewWorksheetAction = actionCollection()->addAction("new_worksheet");
120 mNewWorksheetAction->setIcon(KIcon("tab-new"));
121 connect(mNewWorksheetAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( newWorkSheet() ));
122 mInsertWorksheetAction = actionCollection()->addAction("import_worksheet");
123 mInsertWorksheetAction->setIcon(KIcon("document-open") );
124 connect(mInsertWorksheetAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( importWorkSheet() ));
125 mTabExportAction = actionCollection()->addAction( "export_worksheet" );
126 mTabExportAction->setIcon( KIcon("document-save-as") );
127 connect(mTabExportAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( exportWorkSheet() ));
128 mTabRemoveAction = actionCollection()->addAction( "remove_worksheet" );
129 mTabRemoveAction->setIcon( KIcon("tab-close") );
130 connect(mTabRemoveAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( removeWorkSheet() ));
131 mMonitorRemoteAction = actionCollection()->addAction( "connect_host" );
132 mMonitorRemoteAction->setIcon( KIcon("network-connect") );
133 connect(mMonitorRemoteAction, SIGNAL(triggered(bool)), SLOT( connectHost() ));
134 //knewstuff2 action
135 mHotNewWorksheetAction = actionCollection()->addAction( "get_new_worksheet" );
136 mHotNewWorksheetAction->setIcon( KIcon("network-server") );
137 connect(mHotNewWorksheetAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( getHotNewWorksheet() ));
139 mQuitAction = NULL;
141 mConfigureSheetAction = actionCollection()->addAction( "configure_sheet" );
142 mConfigureSheetAction->setIcon( KIcon("configure") );
143 connect(mConfigureSheetAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( configure() ));
145 retranslateUi();
148 void TopLevel::retranslateUi()
150 setPlainCaption( i18n( "System Monitor" ) );
151 mNewWorksheetAction->setText(i18n( "&New Tab..." ));
152 mInsertWorksheetAction->setText(i18n( "Import Tab Fr&om File..." ));
153 mTabExportAction->setText( i18n( "Save Tab &As..." ) );
154 mTabRemoveAction->setText( i18n( "&Close Tab" ) );
155 mMonitorRemoteAction->setText( i18n( "Monitor &Remote Machine..." ) );
156 mHotNewWorksheetAction->setText( i18n( "&Download New Tabs..." ) );
157 mConfigureSheetAction->setText( i18n( "Tab &Properties" ) );
158 if(mQuitAction) {
159 KAction *tmpQuitAction = KStandardAction::quit( NULL, NULL, NULL );
160 mQuitAction->setText(tmpQuitAction->text());
161 mQuitAction->setWhatsThis(tmpQuitAction->whatsThis());
162 mQuitAction->setToolTip(tmpQuitAction->toolTip());
163 delete tmpQuitAction;
164 } else
165 mQuitAction = KStandardAction::quit( this, SLOT( close() ), actionCollection() );
169 void TopLevel::currentTabChanged(int index)
171 QWidget *wdg = mWorkSpace->widget(index);
172 WorkSheet *sheet = (WorkSheet *)(wdg);
173 Q_ASSERT(sheet);
174 bool locked = !sheet || sheet->isLocked();
175 mTabRemoveAction->setVisible(!locked);
176 mTabExportAction->setVisible(!locked);
177 mMonitorRemoteAction->setVisible(!locked);
179 if(!locked && !mSensorBrowser) {
180 startSensorBrowserWidget();
182 if(mSensorBrowser) {
183 if(mSensorBrowser->isVisible() && locked) //going from visible to not visible to save the state
184 mSplitterSize = mSplitter->sizes();
185 mSensorBrowser->setVisible(!locked);
189 void TopLevel::startSensorBrowserWidget()
191 if(mSensorBrowser) return;
192 mSensorBrowser = new SensorBrowserWidget( 0, KSGRD::SensorMgr );
193 mSplitter->insertWidget(2,mSensorBrowser);
194 mSplitter->setSizes( mSplitterSize );
198 * DBUS Interface functions
201 void TopLevel::showOnCurrentDesktop()
203 KWindowSystem::setOnDesktop( winId(), KWindowSystem::currentDesktop() );
204 kapp->updateUserTimestamp();
205 KWindowSystem::forceActiveWindow( winId() );
208 void TopLevel::importWorkSheet( const QString &fileName )
210 mWorkSpace->importWorkSheet( KUrl( fileName ) );
213 void TopLevel::removeWorkSheet( const QString &fileName )
215 mWorkSpace->removeWorkSheet( fileName );
218 void TopLevel::getHotNewWorksheet()
220 mWorkSpace->getHotNewWorksheet( );
223 QStringList TopLevel::listSensors( const QString &hostName )
225 if(!mSensorBrowser) {
226 setUpdatesEnabled(false);
227 startSensorBrowserWidget();
228 mSensorBrowser->setVisible(false);
229 setUpdatesEnabled(true);
231 return mSensorBrowser->listSensors( hostName );
234 QStringList TopLevel::listHosts()
236 if(!mSensorBrowser) {
237 setUpdatesEnabled(false);
238 startSensorBrowserWidget();
239 mSensorBrowser->setVisible(false);
240 setUpdatesEnabled(true);
242 return mSensorBrowser->listHosts();
245 void TopLevel::initStatusBar()
247 KSGRD::SensorMgr->engage( "localhost", "", "ksysguardd" );
248 /* Request info about the swap space size and the units it is
249 * measured in. The requested info will be received by
250 * answerReceived(). */
251 KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/used?",
252 (KSGRD::SensorClient*)this, 7 );
254 KToggleAction *sb = dynamic_cast<KToggleAction*>(action("options_show_statusbar"));
255 if (sb)
256 connect(sb, SIGNAL(toggled(bool)), this, SLOT(updateStatusBar()));
257 setupGUI(QSize(800,600), ToolBar | Keys | StatusBar | Save | Create);
260 void TopLevel::updateStatusBar()
262 if ( mTimerId == -1 )
263 mTimerId = startTimer( 2000 );
265 // call timerEvent to fill the status bar with real values
266 timerEvent( 0 );
269 void TopLevel::connectHost()
271 HostConnector hostConnector( this );
273 // hostConnector.setHostNames( mHostList );
274 // hostConnector.setCommands( mCommandList );
276 // hostConnector.setCurrentHostName( "" );
278 if ( !hostConnector.exec() )
279 return;
281 // mHostList = hostConnector.hostNames();
282 // mCommandList = hostConnector.commands();
284 QString shell = "";
285 QString command = "";
286 int port = -1;
288 /* Check which radio button is selected and set parameters
289 * appropriately. */
290 if ( hostConnector.useSsh() )
291 shell = "ssh";
292 else if ( hostConnector.useRsh() )
293 shell = "rsh";
294 else if ( hostConnector.useDaemon() )
295 port = hostConnector.port();
296 else
297 command = hostConnector.currentCommand();
299 KSGRD::SensorMgr->engage( hostConnector.currentHostName(), shell, command, port );
302 void TopLevel::disconnectHost()
304 if(mSensorBrowser)
305 mSensorBrowser->disconnect();
308 void TopLevel::editToolbars()
310 saveMainWindowSettings( KConfigGroup( KGlobal::config(), "MainWindow" ) );
311 KEditToolBar dlg( actionCollection() );
312 connect( &dlg, SIGNAL( newToolBarConfig() ), this,
313 SLOT( slotNewToolbarConfig() ) );
315 dlg.exec();
318 void TopLevel::slotNewToolbarConfig()
320 createGUI();
321 applyMainWindowSettings( KConfigGroup( KGlobal::config(), "MainWindow" ) );
324 bool TopLevel::event( QEvent *e )
326 if ( e->type() == QEvent::User ) {
327 /* Due to the asynchronous communication between ksysguard and its
328 * back-ends, we sometimes need to show message boxes that were
329 * triggered by objects that have died already. */
330 KMessageBox::error( this, static_cast<KSGRD::SensorManager::MessageEvent*>(e)->message() );
332 return true;
335 return KXmlGuiWindow::event( e );
338 void TopLevel::timerEvent( QTimerEvent* )
340 if ( statusBar()->isVisibleTo( this ) ) {
341 /* Request some info about the memory status. The requested
342 * information will be received by answerReceived(). */
343 KSGRD::SensorMgr->sendRequest( "localhost", "pscount",
344 (KSGRD::SensorClient*)this, 0 );
345 KSGRD::SensorMgr->sendRequest( "localhost", "cpu/idle",
346 (KSGRD::SensorClient*)this, 1 );
347 KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/free",
348 (KSGRD::SensorClient*)this, 2 );
349 KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/used",
350 (KSGRD::SensorClient*)this, 3 );
351 KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/application",
352 (KSGRD::SensorClient*)this, 4 );
353 KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/free",
354 (KSGRD::SensorClient*)this, 5 );
355 KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/used",
356 (KSGRD::SensorClient*)this, 6 );
360 void TopLevel::changeEvent( QEvent * event )
362 if (event->type() == QEvent::LanguageChange) {
363 KSGRD::SensorMgr->retranslate();
364 setUpdatesEnabled(false);
365 setupGUI(ToolBar | Keys | StatusBar | Create);
366 retranslateUi();
367 setUpdatesEnabled(true);
369 KXmlGuiWindow::changeEvent(event);
372 bool TopLevel::queryClose()
374 if ( !mWorkSpace->saveOnQuit() )
375 return false;
377 KConfigGroup cg( KGlobal::config(), "MainWindow" );
378 saveProperties( cg );
379 KGlobal::config()->sync();
381 return true;
384 void TopLevel::readProperties( const KConfigGroup& cfg )
387 /* we can ignore 'isMaximized' because we can't set the window
388 maximized, so we save the coordinates instead */
389 // if ( cfg.readEntry( "isMinimized" , false) == true )
390 // showMinimized();
392 mSplitterSize = cfg.readEntry( "SplitterSizeList",QList<int>() );
393 if ( mSplitterSize.isEmpty() ) {
394 // start with a 30/70 ratio
395 mSplitterSize.append( 10 );
396 mSplitterSize.append( 90 );
399 KSGRD::SensorMgr->readProperties( cfg );
400 KSGRD::Style->readProperties( cfg );
402 mWorkSpace->readProperties( cfg );
404 QList<WorkSheet *> workSheets = mWorkSpace->getWorkSheets();
405 ProcessController *processController = NULL;
406 foreach(WorkSheet *sheet, workSheets) {
407 processController = sheet->getLocalProcessController();
408 if(processController != NULL) {
409 for(int i = 0; i < processController->actions().size(); i++) {
410 actionCollection()->addAction("processAction" + QString::number(i), processController->actions().at(i));
415 updateStatusBar();
418 void TopLevel::saveProperties( KConfigGroup& cfg )
420 cfg.writeEntry( "isMinimized", isMinimized() );
422 if(mSensorBrowser && mSensorBrowser->isVisible())
423 cfg.writeEntry( "SplitterSizeList", mSplitter->sizes());
424 else if(mSplitterSize.size() == 2 && mSplitterSize.value(0) != 0 && mSplitterSize.value(1) != 0)
425 cfg.writeEntry( "SplitterSizeList", mSplitterSize );
427 KSGRD::Style->saveProperties( cfg );
428 KSGRD::SensorMgr->saveProperties( cfg );
430 saveMainWindowSettings( cfg );
431 mWorkSpace->saveProperties( cfg );
434 void TopLevel::answerReceived( int id, const QList<QByteArray> &answerList )
436 // we have received an answer from the daemon.
437 QByteArray answer;
438 if(!answerList.isEmpty()) answer = answerList[0];
439 QString s;
440 static QString unit;
441 static qlonglong mFree = 0;
442 static qlonglong mUsedApplication = 0;
443 static qlonglong mUsedTotal = 0;
444 static qlonglong sUsed = 0;
445 static qlonglong sFree = 0;
447 switch ( id ) {
448 case 0:
449 s = i18np( " 1 process ", " %1 processes ", answer.toInt() );
450 sbProcessCount->setText( s );
452 break;
454 case 1:
455 s = i18n( " CPU: %1% ", (int) (100 - answer.toFloat()) );
456 sbCpuStat->setText( s );
457 break;
459 case 2:
460 mFree = answer.toLongLong();
461 break;
463 case 3:
464 mUsedTotal = answer.toLongLong();
465 break;
467 case 4:
468 mUsedApplication = answer.toLongLong();
469 s = i18nc( "Arguments are formatted byte sizes (used/total)", " Memory: %1 / %2 " ,
470 KGlobal::locale()->formatByteSize( mUsedApplication*1024),
471 KGlobal::locale()->formatByteSize( (mFree+mUsedTotal)*1024 ) );
472 sbMemTotal->setText( s );
473 break;
475 case 5:
476 sFree = answer.toLong();
477 break;
479 case 6:
480 sUsed = answer.toLong();
481 setSwapInfo( sUsed, sFree, unit );
482 break;
484 case 7: {
485 KSGRD::SensorIntegerInfo info( answer );
486 unit = KSGRD::SensorMgr->translateUnit( info.unit() );
487 break;
492 void TopLevel::setSwapInfo( long used, long free, const QString & )
494 QString msg;
495 if ( used == 0 && free == 0 ) // no swap available
496 msg = i18n( " No swap space available " );
497 else {
498 msg = i18nc( "Arguments are formatted byte sizes (used/total)", " Swap: %1 / %2 " ,
499 KGlobal::locale()->formatByteSize( used*1024 ),
500 KGlobal::locale()->formatByteSize( (free+used)*1024) );
503 sbSwapTotal->setText( msg );
507 * Once upon a time...
509 extern "C" KDE_EXPORT int kdemain( int argc, char** argv )
511 // initpipe is used to keep the parent process around till the child
512 // has registered with dbus
513 #ifdef FORK_KSYSGUARD
514 int initpipe[ 2 ];
515 pipe( initpipe );
516 /* This forking will put ksysguard in it's own session not having a
517 * controlling terminal attached to it. This prevents ssh from
518 * using this terminal for password requests. Thus, you
519 * need a ssh with ssh-askpass support to popup an X dialog to
520 * enter the password. */
521 pid_t pid;
522 if ( ( pid = fork() ) < 0 )
523 return -1;
524 else
525 if ( pid != 0 ) {
526 close( initpipe[ 1 ] );
528 // wait till init is complete
529 char c;
530 while( read( initpipe[ 0 ], &c, 1 ) < 0 );
532 // then exit
533 close( initpipe[ 0 ] );
534 exit( 0 );
537 close( initpipe[ 0 ] );
538 setsid();
539 #endif
541 KAboutData aboutData( "ksysguard", 0, ki18n( "System Monitor" ),
542 KSYSGUARD_VERSION, ki18n(Description), KAboutData::License_GPL,
543 ki18n( "(c) 1996-2008 The KDE System Monitor Developers" ) );
544 aboutData.addAuthor( ki18n("John Tapsell"), ki18n("Current Maintainer"), "john.tapsell@kde.org" );
545 aboutData.addAuthor( ki18n("Chris Schlaeger"), ki18n("Previous Maintainer"), "cs@kde.org" );
546 aboutData.addAuthor( ki18n("Greg Martyn"), KLocalizedString(), "greg.martyn@gmail.com" );
547 aboutData.addAuthor( ki18n("Tobias Koenig"), KLocalizedString(), "tokoe@kde.org" );
548 aboutData.addAuthor( ki18n("Nicolas Leclercq"), KLocalizedString(), "nicknet@planete.net" );
549 aboutData.addAuthor( ki18n("Alex Sanda"), KLocalizedString(), "alex@darkstart.ping.at" );
550 aboutData.addAuthor( ki18n("Bernd Johannes Wuebben"), KLocalizedString(), "wuebben@math.cornell.edu" );
551 aboutData.addAuthor( ki18n("Ralf Mueller"), KLocalizedString(), "rlaf@bj-ig.de" );
552 aboutData.addAuthor( ki18n("Hamish Rodda"), KLocalizedString(), "rodda@kde.org" );
553 aboutData.addAuthor( ki18n("Torsten Kasch"), ki18n( "Solaris Support\n"
554 "Parts derived (by permission) from the sunos5\n"
555 "module of William LeFebvre's \"top\" utility." ),
556 "tk@Genetik.Uni-Bielefeld.DE" );
557 aboutData.setProgramIconName("utilities-system-monitor");
559 KCmdLineArgs::init( argc, argv, &aboutData );
561 KCmdLineOptions options;
562 options.add("+[worksheet]", ki18n( "Optional worksheet files to load" ));
563 KCmdLineArgs::addCmdLineOptions( options );
564 // initialize KDE application
565 KApplication *app = new KApplication;
567 KSGRD::SensorMgr = new KSGRD::SensorManager();
568 KSGRD::Style = new KSGRD::StyleEngine();
570 #ifdef FORK_KSYSGUARD
571 char c = 0;
572 write( initpipe[ 1 ], &c, 1 );
573 close( initpipe[ 1 ] );
574 #endif
575 topLevel = new TopLevel();
577 topLevel->initStatusBar();
579 // create top-level widget
580 topLevel->readProperties( KConfigGroup( KGlobal::config(), "MainWindow" ) );
582 //There seems to be some serious bugs with the session restore code. Disabling
583 // if ( app->isSessionRestored() )
584 // topLevel->restore( 1 );
586 topLevel->show();
587 KSGRD::SensorMgr->setBroadcaster( topLevel ); // SensorMgr uses a QPointer for toplevel, so it is okay if topLevel is deleted first
589 // run the application
590 int result = app->exec();
592 delete app;
593 delete KSGRD::SensorMgr;
594 delete KSGRD::Style;
596 return result;
599 #include "ksysguard.moc"