2 KSysGuard, the KDE System Guard
4 Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org>
5 Copyright (c) 2006 - 2007 John Tapsell <john.tapsell@kde.org>
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library 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 GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
24 #include "../config-ksysguard.h"
30 #include <QSortFilterProxyModel>
31 #include <QHeaderView>
36 #include <QStyledItemDelegate>
40 #include <QSignalMapper>
43 #include <signal.h> //For SIGTERM
45 #include <kapplication.h>
47 #include <kmessagebox.h>
51 #include <kstandarddirs.h>
52 #include <KWindowSystem>
54 #include "ksysguardprocesslist.moc"
55 #include "ksysguardprocesslist.h"
56 #include "ReniceDlg.h"
57 #include "ui_ProcessWidgetUI.h"
59 #ifdef WITH_MONITOR_PROCESS_IO
60 #include "DisplayProcessDlg.h"
63 #include <sys/types.h>
66 //Trolltech have a testing class for classes that inherit QAbstractItemModel. If you want to run with this run-time testing enabled, put the modeltest.* files in this directory and uncomment the next line
67 //#define DO_MODELCHECK
69 #include "modeltest.h"
72 class ProgressBarItemDelegate
: public QStyledItemDelegate
75 ProgressBarItemDelegate(QObject
*parent
) : QStyledItemDelegate(parent
), startProgressColor(0x00, 0x71, 0xBC, 100), endProgressColor(0x83, 0xDD, 0xF5, 100), totalMemory(-1), numCpuCores(-1) {}
77 virtual void paint(QPainter
*painter
, const QStyleOptionViewItem
&opt
, const QModelIndex
&index
) const
79 QStyleOptionViewItemV4 option
= opt
;
80 initStyleOption(&option
,index
);
82 Q_ASSERT(index
.model());
83 QModelIndex realIndex
= (reinterpret_cast< const QAbstractProxyModel
*> (index
.model()))->mapToSource(index
);
84 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (realIndex
.internalPointer());
86 if(index
.column() == ProcessModel::HeadingCPUUsage
) {
88 numCpuCores
= index
.data(ProcessModel::NumberOfProcessorsRole
).toInt();
89 percentage
= (process
->userUsage
+ process
->sysUsage
) / numCpuCores
;
90 } else if(index
.column() == ProcessModel::HeadingMemory
) {
92 if(process
->vmURSS
!= -1)
93 memory
= process
->vmURSS
;
95 memory
= process
->vmRSS
;
97 totalMemory
= index
.data(ProcessModel::TotalMemoryRole
).toLongLong();
99 percentage
= (int)(memory
*100/totalMemory
);
102 } else if(index
.column() == ProcessModel::HeadingSharedMemory
) {
103 if(process
->vmURSS
!= -1) {
104 if(totalMemory
== -1)
105 totalMemory
= index
.data(ProcessModel::TotalMemoryRole
).toLongLong();
107 percentage
= (int)((process
->vmRSS
- process
->vmURSS
)*100/totalMemory
);
115 drawPercentageDisplay(painter
,option
,index
.data(Qt::DisplayRole
).toString());
117 QStyledItemDelegate::paint(painter
, option
, index
);
121 void drawPercentageDisplay(QPainter
*painter
, const QStyleOptionViewItem
& option
, const QString
& text
) const
123 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem
,&option
,painter
);
125 const QRect rect
= option
.rect
;
126 if(percentage
* rect
.width() > 100 ) { //make sure the line will have a width of more than 1 pixel
127 QPen old
= painter
->pen();
128 painter
->setPen(Qt::NoPen
);
129 QLinearGradient
linearGrad( QPointF(rect
.x(),rect
.y()), QPointF(rect
.x() + rect
.width(), rect
.y()));
130 linearGrad
.setColorAt(0, startProgressColor
);
131 linearGrad
.setColorAt(1, endProgressColor
);
132 painter
->fillRect( rect
.x(), rect
.y(), rect
.width() * percentage
/100 , rect
.height(), QBrush(linearGrad
));
133 painter
->setPen( old
);
135 painter
->drawText(rect
,option
.displayAlignment
,text
+ ' ');
138 mutable int percentage
;
139 QColor startProgressColor
;
140 QColor endProgressColor
;
141 mutable long long totalMemory
;
142 mutable int numCpuCores
;
145 struct KSysGuardProcessListPrivate
{
147 KSysGuardProcessListPrivate(KSysGuardProcessList
* q
, const QString
&hostName
)
148 : mModel(q
, hostName
), mFilterModel(q
), mUi(new Ui::ProcessWidget()), mProcessContextMenu(NULL
), mUpdateTimer(NULL
)
150 renice
= new QAction(i18np("Renice Process...", "Renice Processes...", 1), q
);
151 selectParent
= new QAction(i18n("Jump to Parent Process"), q
);
153 selectTracer
= new QAction(i18n("Jump to Process Debugging This One"), q
);
154 window
= new QAction(i18n("Show Application Window"), q
);
155 #ifdef WITH_MONITOR_PROCESS_IO
156 monitorio
= new QAction(i18n("Monitor Input && Output"), q
);
160 resume
= new QAction(i18n("Resume Stopped Process"), q
);
161 kill
= new QAction(i18np("Kill Process", "Kill Processes", 1), q
);
162 kill
->setIcon(KIcon("process-stop"));
164 sigStop
= new QAction(i18n("Suspend (STOP)"), q
);
165 sigCont
= new QAction(i18n("Continue (CONT)"), q
);
166 sigHup
= new QAction(i18n("Hangup (HUP)"), q
);
167 sigInt
= new QAction(i18n("Interrupt (INT)"), q
);
168 sigTerm
= new QAction(i18n("Terminate (TERM)"), q
);
169 sigKill
= new QAction(i18n("Kill (KILL)"), q
);
170 sigUsr1
= new QAction(i18n("User 1 (USR1)"), q
);
171 sigUsr2
= new QAction(i18n("User 2 (USR2)"), q
);
174 ~KSysGuardProcessListPrivate() { delete mUi
; mUi
= NULL
; }
176 /** The process model. This contains all the data on all the processes running on the system */
179 /** The process filter. The mModel is connected to this, and this filter model connects to the view. This lets us
180 * sort the view and filter (by using the combo box or the search line)
182 ProcessFilter mFilterModel
;
184 /** The graphical user interface for this process list widget, auto-generated by Qt Designer */
185 Ui::ProcessWidget
*mUi
;
187 /** The context menu when you right click on a process */
188 QMenu
*mProcessContextMenu
;
190 /** A timer to call updateList() every mUpdateIntervalMSecs */
191 QTimer
*mUpdateTimer
;
193 /** The time to wait, in milliseconds, between updating the process list */
194 int mUpdateIntervalMSecs
;
198 QAction
*selectParent
;
199 QAction
*selectTracer
;
213 KSysGuardProcessList::KSysGuardProcessList(QWidget
* parent
, const QString
&hostName
)
214 : QWidget(parent
), d(new KSysGuardProcessListPrivate(this, hostName
))
216 d
->mUpdateIntervalMSecs
= 2000; //Set 2 seconds as the default update interval
217 d
->mUi
->setupUi(this);
218 d
->mFilterModel
.setSourceModel(&d
->mModel
);
219 d
->mUi
->treeView
->setModel(&d
->mFilterModel
);
221 new ModelTest(&d
->mModel
, this);
223 d
->mUi
->treeView
->setItemDelegate(new ProgressBarItemDelegate(d
->mUi
->treeView
));
225 d
->mUi
->treeView
->header()->setContextMenuPolicy(Qt::CustomContextMenu
);
226 connect(d
->mUi
->treeView
->header(), SIGNAL(customContextMenuRequested(const QPoint
&)), this, SLOT(showColumnContextMenu(const QPoint
&)));
228 d
->mProcessContextMenu
= new QMenu(d
->mUi
->treeView
);
229 d
->mUi
->treeView
->setContextMenuPolicy(Qt::CustomContextMenu
);
230 connect(d
->mUi
->treeView
, SIGNAL(customContextMenuRequested(const QPoint
&)), this, SLOT(showProcessContextMenu(const QPoint
&)));
232 d
->mUi
->treeView
->header()->setClickable(true);
233 d
->mUi
->treeView
->header()->setSortIndicatorShown(true);
234 d
->mUi
->treeView
->header()->setCascadingSectionResizes(true);
235 connect(d
->mUi
->btnKillProcess
, SIGNAL(clicked()), this, SLOT(killSelectedProcesses()));
236 connect(d
->mUi
->txtFilter
, SIGNAL(textChanged(const QString
&)), this, SLOT(filterTextChanged(const QString
&)));
237 connect(d
->mUi
->cmbFilter
, SIGNAL(currentIndexChanged(int)), this, SLOT(setStateInt(int)));
238 connect(d
->mUi
->treeView
, SIGNAL(expanded(const QModelIndex
&)), this, SLOT(expandAllChildren(const QModelIndex
&)));
239 connect(d
->mUi
->treeView
->selectionModel(), SIGNAL(selectionChanged(const QItemSelection
& , const QItemSelection
& )), this, SLOT(selectionChanged()));
240 connect(&d
->mFilterModel
, SIGNAL(rowsInserted( const QModelIndex
&, int, int)), this, SLOT(rowsInserted(const QModelIndex
&, int, int)));
241 setMinimumSize(sizeHint());
243 /* Hide the vm size column by default since it's not very useful */
244 d
->mUi
->treeView
->header()->hideSection(ProcessModel::HeadingVmSize
);
245 d
->mUi
->treeView
->header()->hideSection(ProcessModel::HeadingNiceness
);
246 d
->mUi
->treeView
->header()->hideSection(ProcessModel::HeadingTty
);
247 d
->mUi
->treeView
->header()->hideSection(ProcessModel::HeadingCommand
);
248 d
->mUi
->treeView
->header()->hideSection(ProcessModel::HeadingPid
);
249 d
->mFilterModel
.setFilterKeyColumn(-1);
251 d
->mUi
->treeView
->header()->resizeSection(ProcessModel::HeadingCPUUsage
, d
->mUi
->treeView
->header()->sectionSizeHint(ProcessModel::HeadingCPUUsage
));
252 d
->mUi
->treeView
->header()->resizeSection(ProcessModel::HeadingMemory
, d
->mUi
->treeView
->header()->sectionSizeHint(ProcessModel::HeadingMemory
));
253 d
->mUi
->treeView
->header()->resizeSection(ProcessModel::HeadingSharedMemory
, d
->mUi
->treeView
->header()->sectionSizeHint(ProcessModel::HeadingSharedMemory
));
255 //Process names can have mixed case. Make the filter case insensitive.
256 d
->mFilterModel
.setFilterCaseSensitivity(Qt::CaseInsensitive
);
257 d
->mFilterModel
.setSortCaseSensitivity(Qt::CaseInsensitive
);
259 d
->mUi
->txtFilter
->installEventFilter(this);
260 d
->mUi
->treeView
->installEventFilter(this);
262 //Logical column 0 will always be the tree bit with the process name. We expand this automatically in code,
263 //so don't let the user change it
264 d
->mUi
->treeView
->header()->setResizeMode(0, QHeaderView::ResizeToContents
);
265 d
->mUi
->treeView
->header()->setStretchLastSection(true);
266 d
->mUi
->treeView
->setDragEnabled(true);
267 d
->mUi
->treeView
->setDragDropMode(QAbstractItemView::DragOnly
);
270 //Sort by username by default
271 d
->mUi
->treeView
->sortByColumn(ProcessModel::HeadingUser
, Qt::AscendingOrder
);
272 d
->mFilterModel
.sort(ProcessModel::HeadingUser
, Qt::AscendingOrder
);
274 // Add all the actions to the main widget, and get all the actions to call actionTriggered when clicked
275 QSignalMapper
*signalMapper
= new QSignalMapper(this);
276 QList
<QAction
*> actions
;
277 actions
<< d
->renice
<< d
->kill
<< d
->selectParent
<< d
->selectTracer
<< d
->window
;
279 actions
<< d
->monitorio
;
280 actions
<< d
->resume
<< d
->sigStop
<< d
->sigCont
<< d
->sigHup
<< d
->sigInt
<< d
->sigTerm
<< d
->sigKill
<< d
->sigUsr1
<< d
->sigUsr2
;
281 foreach(QAction
*action
, actions
) {
283 connect(action
, SIGNAL(triggered(bool)), signalMapper
, SLOT(map()));
284 signalMapper
->setMapping(action
, action
);
286 connect(signalMapper
, SIGNAL(mapped(QObject
*)), SLOT(actionTriggered(QObject
*)));
290 d
->mFilterModel
.setDynamicSortFilter(true);
292 d
->mUpdateTimer
= new QTimer(this);
293 d
->mUpdateTimer
->setSingleShot(true);
294 connect(d
->mUpdateTimer
, SIGNAL(timeout()), this, SLOT(updateList()));
295 d
->mUpdateTimer
->start(d
->mUpdateIntervalMSecs
);
297 d
->mUi
->btnKillProcess
->setIcon(KIcon("process-stop"));
299 //If the view resorts continually, then it can be hard to keep track of processes. By doing it only every few seconds it reduces the 'jumping around'
300 QTimer
*mTimer
= new QTimer(this);
301 connect(mTimer
, SIGNAL(timeout()), &d
->mFilterModel
, SLOT(invalidate()));
302 // mTimer->start(4000);
303 // QT BUG? We have to disable the sorting for now because there seems to be a bug in Qt introduced in Qt 4.4beta which makes the view scroll back to the top
304 d
->mModel
.update(d
->mUpdateIntervalMSecs
);
307 KSysGuardProcessList::~KSysGuardProcessList()
312 QTreeView
*KSysGuardProcessList::treeView() const {
313 return d
->mUi
->treeView
;
316 QLineEdit
*KSysGuardProcessList::filterLineEdit() const {
317 return d
->mUi
->txtFilter
;
320 ProcessFilter::State
KSysGuardProcessList::state() const
322 return d
->mFilterModel
.filter();
324 void KSysGuardProcessList::setStateInt(int state
) {
325 setState((ProcessFilter::State
) state
);
326 d
->mUi
->treeView
->scrollTo( d
->mUi
->treeView
->currentIndex());
328 void KSysGuardProcessList::setState(ProcessFilter::State state
)
329 { //index is the item the user selected in the combo box
330 d
->mFilterModel
.setFilter(state
);
331 d
->mModel
.setSimpleMode( (state
!= ProcessFilter::AllProcessesInTreeForm
) );
332 d
->mUi
->cmbFilter
->setCurrentIndex( (int)state
);
335 void KSysGuardProcessList::filterTextChanged(const QString
&newText
) {
336 d
->mFilterModel
.setFilterRegExp(newText
.trimmed());
338 d
->mUi
->btnKillProcess
->setEnabled( d
->mUi
->treeView
->selectionModel()->hasSelection() );
339 d
->mUi
->treeView
->scrollTo( d
->mUi
->treeView
->currentIndex());
341 void KSysGuardProcessList::selectionChanged()
343 int numSelected
= d
->mUi
->treeView
->selectionModel()->selectedRows().size();
344 d
->mUi
->btnKillProcess
->setEnabled( numSelected
!= 0 );
346 d
->renice
->setText(i18np("Renice Process...", "Renice Processes...", numSelected
));
347 d
->kill
->setText(i18np("Kill Process", "Kill Processes", numSelected
));
349 void KSysGuardProcessList::showProcessContextMenu(const QModelIndex
&index
) {
350 if(!index
.isValid()) return;
351 QRect rect
= d
->mUi
->treeView
->visualRect(index
);
352 QPoint
point(rect
.x() + rect
.width()/4, rect
.y() + rect
.height()/2 );
353 showProcessContextMenu(point
);
355 void KSysGuardProcessList::showProcessContextMenu(const QPoint
&point
) {
356 d
->mProcessContextMenu
->clear();
358 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
359 int numProcesses
= selectedIndexes
.size();
361 if(numProcesses
== 0) return; //No processes selected, so no context menu
363 QModelIndex realIndex
= d
->mFilterModel
.mapToSource(selectedIndexes
.at(0));
364 KSysGuard::Process
*process
= reinterpret_cast<KSysGuard::Process
*> (realIndex
.internalPointer());
368 //If the selected process is a zombie, do not bother offering renice and kill options
369 bool showSignalingEntries
= numProcesses
!= 1 || process
->status
!= KSysGuard::Process::Zombie
;
370 if(showSignalingEntries
) {
371 d
->mProcessContextMenu
->addAction(d
->renice
);
372 QMenu
*signalMenu
= d
->mProcessContextMenu
->addMenu(i18n("Send Signal"));
373 signalMenu
->addAction(d
->sigStop
);
374 signalMenu
->addAction(d
->sigCont
);
375 signalMenu
->addAction(d
->sigHup
);
376 signalMenu
->addAction(d
->sigInt
);
377 signalMenu
->addAction(d
->sigTerm
);
378 signalMenu
->addAction(d
->sigKill
);
379 signalMenu
->addAction(d
->sigUsr1
);
380 signalMenu
->addAction(d
->sigUsr2
);
383 if(numProcesses
== 1 && process
->parent_pid
> 1) {
384 //As a design decision, I do not show the 'Jump to parent process' option when the
385 //parent is just 'init'.
387 KSysGuard::Process
*parent_process
= d
->mModel
.getProcess(process
->parent_pid
);
388 if(parent_process
) { //it should not be possible for this process to not exist, but check just incase
389 d
->selectParent
->setText(i18n("Jump to Parent Process (%1)", parent_process
->name
));
390 d
->mProcessContextMenu
->addAction(d
->selectParent
);
394 if(numProcesses
== 1 && process
->tracerpid
> 0) {
395 //If the process is being debugged, offer to select it
396 d
->mProcessContextMenu
->addAction(d
->selectTracer
);
399 if (numProcesses
== 1 && !d
->mModel
.data(realIndex
, ProcessModel::WindowIdRole
).isNull()) {
400 d
->mProcessContextMenu
->addAction(d
->window
);
402 if (d
->monitorio
&& numProcesses
== 1 && d
->mModel
.isLocalhost() && (process
->uid
==0 || process
->uid
== getuid()) && process
->pid
!= getpid() && process
->pid
!= getppid()) { //Don't attach to ourselves - crashes
403 d
->mProcessContextMenu
->addAction(d
->monitorio
);
406 if(numProcesses
== 1 && process
->status
== KSysGuard::Process::Stopped
) {
407 //If the process is being debugged, offer to select it
408 d
->mProcessContextMenu
->addAction(d
->resume
);
411 if (showSignalingEntries
) {
412 d
->mProcessContextMenu
->addSeparator();
413 d
->mProcessContextMenu
->addAction(d
->kill
);
416 d
->mProcessContextMenu
->popup(d
->mUi
->treeView
->viewport()->mapToGlobal(point
));
418 void KSysGuardProcessList::actionTriggered(QObject
*object
) {
419 //Reset the text back to normal
420 d
->selectParent
->setText(i18n("Jump to Parent Process"));
421 QAction
*result
= dynamic_cast<QAction
*>(object
);
423 //Escape was pressed. Do nothing.
424 } else if(result
== d
->renice
) {
425 reniceSelectedProcesses();
426 } else if(result
== d
->kill
) {
427 killSelectedProcesses();
428 } else if(result
== d
->selectParent
) {
429 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
430 int numProcesses
= selectedIndexes
.size();
431 if(numProcesses
== 0) return; //No processes selected
432 QModelIndex realIndex
= d
->mFilterModel
.mapToSource(selectedIndexes
.at(0));
433 KSysGuard::Process
*process
= reinterpret_cast<KSysGuard::Process
*> (realIndex
.internalPointer());
435 selectAndJumpToProcess(process
->parent_pid
);
436 } else if(result
== d
->selectTracer
) {
437 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
438 int numProcesses
= selectedIndexes
.size();
439 if(numProcesses
== 0) return; //No processes selected
440 QModelIndex realIndex
= d
->mFilterModel
.mapToSource(selectedIndexes
.at(0));
441 KSysGuard::Process
*process
= reinterpret_cast<KSysGuard::Process
*> (realIndex
.internalPointer());
443 selectAndJumpToProcess(process
->tracerpid
);
444 } else if(result
== d
->window
) {
445 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
446 int numProcesses
= selectedIndexes
.size();
447 if(numProcesses
== 0) return; //No processes selected
448 foreach( const QModelIndex
&index
, selectedIndexes
) {
449 QModelIndex realIndex
= d
->mFilterModel
.mapToSource(index
);
450 QVariant widVar
= d
->mModel
.data(realIndex
, ProcessModel::WindowIdRole
);
451 if( !widVar
.isNull() ) {
452 int wid
= widVar
.toInt();
453 KWindowSystem::activateWindow(wid
);
456 #ifdef WITH_MONITOR_PROCESS_IO
457 } else if(result
== d
->monitorio
) {
458 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
459 if(selectedIndexes
.isEmpty()) return; //No processes selected
460 QModelIndex realIndex
= d
->mFilterModel
.mapToSource(selectedIndexes
.at(0));
461 KSysGuard::Process
*process
= reinterpret_cast<KSysGuard::Process
*> (realIndex
.internalPointer());
463 DisplayProcessDlg
*dialog
= new DisplayProcessDlg( this,process
);
468 QList
< long long > pidlist
;
469 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
470 int numProcesses
= selectedIndexes
.size();
471 if(numProcesses
== 0) return; //No processes selected
472 foreach( const QModelIndex
&index
, selectedIndexes
) {
473 QModelIndex realIndex
= d
->mFilterModel
.mapToSource(index
);
474 KSysGuard::Process
*process
= reinterpret_cast<KSysGuard::Process
*> (realIndex
.internalPointer());
476 pidlist
<< process
->pid
;
478 if(result
== d
->resume
|| result
== d
->sigCont
)
479 killProcesses(pidlist
, SIGCONT
); //Despite the function name, this sends a signal, rather than kill it. Silly unix :)
480 else if(result
== d
->sigStop
)
481 killProcesses(pidlist
, SIGSTOP
);
482 else if(result
== d
->sigHup
)
483 killProcesses(pidlist
, SIGHUP
);
484 else if(result
== d
->sigInt
)
485 killProcesses(pidlist
, SIGINT
);
486 else if(result
== d
->sigTerm
)
487 killProcesses(pidlist
, SIGTERM
);
488 else if(result
== d
->sigKill
)
489 killProcesses(pidlist
, SIGKILL
);
490 else if(result
== d
->sigUsr1
)
491 killProcesses(pidlist
, SIGUSR1
);
492 else if(result
== d
->sigUsr2
)
493 killProcesses(pidlist
, SIGUSR2
);
498 void KSysGuardProcessList::selectAndJumpToProcess(int pid
) {
499 KSysGuard::Process
*process
= d
->mModel
.getProcess(pid
);
501 QModelIndex filterIndex
= d
->mFilterModel
.mapFromSource( d
->mModel
.getQModelIndex(process
, 0));
502 d
->mUi
->treeView
->clearSelection();
503 d
->mUi
->treeView
->setCurrentIndex(filterIndex
);
504 d
->mUi
->treeView
->scrollTo( filterIndex
, QAbstractItemView::PositionAtCenter
);
508 void KSysGuardProcessList::showColumnContextMenu(const QPoint
&point
){
509 QMenu
*menu
= new QMenu();
512 int index
= d
->mUi
->treeView
->header()->logicalIndexAt(point
);
514 //selected a column. Give the option to hide it
515 action
= new QAction(menu
);
516 action
->setData(-index
-1); //We set data to be negative (and minus 1) to hide a column, and positive to show a column
517 action
->setText(i18n("Hide Column '%1'", d
->mFilterModel
.headerData(index
, Qt::Horizontal
, Qt::DisplayRole
).toString()));
518 menu
->addAction(action
);
519 if(d
->mUi
->treeView
->header()->sectionsHidden()) {
520 menu
->addSeparator();
525 if(d
->mUi
->treeView
->header()->sectionsHidden()) {
526 int num_headings
= d
->mFilterModel
.columnCount();
527 for(int i
= 0; i
< num_headings
; ++i
) {
528 if(d
->mUi
->treeView
->header()->isSectionHidden(i
)) {
529 action
= new QAction(menu
);
530 action
->setText(i18n("Show Column '%1'", d
->mFilterModel
.headerData(i
, Qt::Horizontal
, Qt::DisplayRole
).toString()));
531 action
->setData(i
); //We set data to be negative (and minus 1) to hide a column, and positive to show a column
532 menu
->addAction(action
);
536 QAction
*actionKB
= NULL
;
537 QAction
*actionMB
= NULL
;
538 QAction
*actionGB
= NULL
;
540 if( index
== ProcessModel::HeadingVmSize
|| index
== ProcessModel::HeadingMemory
|| index
== ProcessModel::HeadingSharedMemory
) {
541 //If the user right clicks on a column that contains a memory size, show a toggle option for displaying
542 //the memory in different units. e.g. "2000 k" or "2 m"
543 menu
->addSeparator()->setText(i18n("Display Units"));
544 QActionGroup
*unitsGroup
= new QActionGroup(menu
);
545 actionKB
= new QAction(menu
);
546 actionKB
->setText(i18n("Kilobytes"));
547 actionKB
->setCheckable(true);
548 menu
->addAction(actionKB
);
549 unitsGroup
->addAction(actionKB
);
550 actionMB
= new QAction(menu
);
551 actionMB
->setText(i18n("Megabytes"));
552 actionMB
->setCheckable(true);
553 menu
->addAction(actionMB
);
554 unitsGroup
->addAction(actionMB
);
555 actionGB
= new QAction(menu
);
556 actionGB
->setText(i18n("Gigabytes"));
557 actionGB
->setCheckable(true);
558 menu
->addAction(actionGB
);
559 unitsGroup
->addAction(actionGB
);
560 unitsGroup
->setExclusive(true);
561 switch(d
->mModel
.units()) {
562 case ProcessModel::UnitsKB
:
563 actionKB
->setChecked(true);
565 case ProcessModel::UnitsMB
:
566 actionMB
->setChecked(true);
568 case ProcessModel::UnitsGB
:
569 actionGB
->setChecked(true);
575 QAction
*result
= menu
->exec(d
->mUi
->treeView
->header()->mapToGlobal(point
));
576 if(!result
) return; //Menu cancelled
577 if(result
== actionKB
) {
578 d
->mModel
.setUnits(ProcessModel::UnitsKB
);
580 } else if(result
== actionMB
) {
581 d
->mModel
.setUnits(ProcessModel::UnitsMB
);
583 } else if(result
== actionGB
) {
584 d
->mModel
.setUnits(ProcessModel::UnitsGB
);
587 int i
= result
->data().toInt();
588 //We set data to be negative to hide a column, and positive to show a column
590 d
->mUi
->treeView
->hideColumn(-1-i
);
592 d
->mUi
->treeView
->showColumn(i
);
593 d
->mUi
->treeView
->resizeColumnToContents(i
);
594 d
->mUi
->treeView
->resizeColumnToContents(d
->mFilterModel
.columnCount());
599 void KSysGuardProcessList::expandAllChildren(const QModelIndex
&parent
)
601 //This is called when the user expands a node. This then expands all of its
602 //children. This will trigger this function again recursively.
603 QModelIndex sourceParent
= d
->mFilterModel
.mapToSource(parent
);
604 for(int i
= 0; i
< d
->mModel
.rowCount(sourceParent
); i
++) {
605 d
->mUi
->treeView
->expand(d
->mFilterModel
.mapFromSource(d
->mModel
.index(i
,0, sourceParent
)));
609 void KSysGuardProcessList::rowsInserted(const QModelIndex
& parent
, int start
, int end
)
611 if(d
->mModel
.isSimpleMode()) return; //No tree - no need to expand init
612 if(parent
.isValid()) return; //Not a root node
613 //It is a root node that we just inserted - expand it
614 bool expanded
= false;
615 for(int i
= start
; i
<= end
; i
++) {
616 QModelIndex index
= d
->mFilterModel
.index(i
, 0, QModelIndex());
617 if(!d
->mUi
->treeView
->isExpanded(index
)) {
619 disconnect(d
->mUi
->treeView
, SIGNAL(expanded(const QModelIndex
&)), this, SLOT(expandAllChildren(const QModelIndex
&)));
622 d
->mUi
->treeView
->expand(index
);
626 connect(d
->mUi
->treeView
, SIGNAL(expanded(const QModelIndex
&)), this, SLOT(expandAllChildren(const QModelIndex
&)));
628 void KSysGuardProcessList::expandInit()
630 if(d
->mModel
.isSimpleMode()) return; //No tree - no need to expand init
632 bool expanded
= false;
633 for(int i
= 0; i
< d
->mFilterModel
.rowCount(QModelIndex()); i
++) {
634 QModelIndex index
= d
->mFilterModel
.index(i
, 0, QModelIndex());
635 if(!d
->mUi
->treeView
->isExpanded(index
)) {
637 disconnect(d
->mUi
->treeView
, SIGNAL(expanded(const QModelIndex
&)), this, SLOT(expandAllChildren(const QModelIndex
&)));
640 d
->mUi
->treeView
->expand(index
);
644 connect(d
->mUi
->treeView
, SIGNAL(expanded(const QModelIndex
&)), this, SLOT(expandAllChildren(const QModelIndex
&)));
647 void KSysGuardProcessList::hideEvent ( QHideEvent
* event
) //virtual protected from QWidget
649 //Stop updating the process list if we are hidden
650 d
->mUpdateTimer
->stop();
651 QWidget::hideEvent(event
);
654 void KSysGuardProcessList::showEvent ( QShowEvent
* event
) //virtual protected from QWidget
656 //Start updating the process list again if we are shown again
657 if(!d
->mUpdateTimer
->isActive()) {
658 d
->mUpdateTimer
->start(d
->mUpdateIntervalMSecs
);
661 QWidget::showEvent(event
);
664 void KSysGuardProcessList::changeEvent( QEvent
* event
)
666 if (event
->type() == QEvent::LanguageChange
) {
667 d
->mModel
.retranslateUi();
668 d
->mUi
->retranslateUi(this);
671 QWidget::changeEvent(event
);
674 void KSysGuardProcessList::retranslateUi()
676 d
->mUi
->cmbFilter
->setItemIcon(ProcessFilter::AllProcesses
, KIcon("view-process-all"));
677 d
->mUi
->cmbFilter
->setItemIcon(ProcessFilter::AllProcessesInTreeForm
, KIcon("view-process-all-tree"));
678 d
->mUi
->cmbFilter
->setItemIcon(ProcessFilter::SystemProcesses
, KIcon("view-process-system"));
679 d
->mUi
->cmbFilter
->setItemIcon(ProcessFilter::UserProcesses
, KIcon("view-process-users"));
680 d
->mUi
->cmbFilter
->setItemIcon(ProcessFilter::OwnProcesses
, KIcon("view-process-own"));
681 d
->mUi
->cmbFilter
->setItemIcon(ProcessFilter::ProgramsOnly
, KIcon("view-process-all"));
684 void KSysGuardProcessList::updateList()
687 d
->mModel
.update(d
->mUpdateIntervalMSecs
);
688 d
->mUpdateTimer
->start(d
->mUpdateIntervalMSecs
);
692 int KSysGuardProcessList::updateIntervalMSecs() const
694 return d
->mUpdateIntervalMSecs
;
697 void KSysGuardProcessList::setUpdateIntervalMSecs(int intervalMSecs
)
699 d
->mUpdateIntervalMSecs
= intervalMSecs
;
700 d
->mUpdateTimer
->setInterval(d
->mUpdateIntervalMSecs
);
703 bool KSysGuardProcessList::reniceProcesses(const QList
<long long> &pids
, int niceValue
)
705 QList
< long long> unreniced_pids
;
706 for (int i
= 0; i
< pids
.size(); ++i
) {
707 bool success
= d
->mModel
.processController()->setNiceness(pids
.at(i
), niceValue
);
709 unreniced_pids
<< pids
.at(i
);
712 if(unreniced_pids
.isEmpty()) return true; //All processes were reniced successfully
713 if(!d
->mModel
.isLocalhost()) return false; //We can't use kdesu to renice non-localhost processes
715 QStringList arguments
;
716 arguments
<< "--attach" << QString::number(window()->winId()) << "--noignorebutton";
717 arguments
<< "--" << "renice" << QString::number(niceValue
);
719 for (int i
= 0; i
< unreniced_pids
.size(); ++i
) {
720 arguments
<< QString::number(unreniced_pids
.at(i
));
723 QString su
= KStandardDirs::findExe("kdesu"); //kdesu is a libexec program, so it will not be in the path. findExe will find it correctly anyway
724 if(su
.isEmpty()) return false; //Cannot find kdesu
726 QProcess
*reniceProcess
= new QProcess(NULL
);
727 connect(reniceProcess
, SIGNAL(error(QProcess::ProcessError
)), this, SLOT(reniceFailed()));
728 connect(reniceProcess
, SIGNAL(finished( int, QProcess::ExitStatus
) ), this, SLOT(updateList()));
729 reniceProcess
->start(su
, arguments
);
730 return true; //No way to tell if it was successful :(
733 QList
<KSysGuard::Process
*> KSysGuardProcessList::selectedProcesses() const
735 QList
<KSysGuard::Process
*> processes
;
736 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
737 for(int i
= 0; i
< selectedIndexes
.size(); ++i
) {
738 KSysGuard::Process
*process
= reinterpret_cast<KSysGuard::Process
*> (d
->mFilterModel
.mapToSource(selectedIndexes
.at(i
)).internalPointer());
739 processes
<< process
;
745 void KSysGuardProcessList::reniceSelectedProcesses()
747 QList
<KSysGuard::Process
*> processes
= selectedProcesses();
748 QStringList selectedAsStrings
;
750 if (processes
.isEmpty())
752 KMessageBox::sorry(this, i18n("You must select a process first."));
758 foreach(KSysGuard::Process
*process
, processes
) {
759 selectedAsStrings
<< d
->mModel
.getStringForProcess(process
);
760 if(sched
== -2) sched
= (int)process
->scheduler
;
761 else if(sched
!= -1 && sched
!= (int)process
->scheduler
) sched
= -1; //If two processes have different schedulers, disable the cpu scheduler stuff
762 if(iosched
== -2) iosched
= (int)process
->ioPriorityClass
;
763 else if(iosched
!= -1 && iosched
!= (int)process
->ioPriorityClass
) iosched
= -1; //If two processes have different schedulers, disable the cpu scheduler stuff
767 int firstPriority
= processes
.first()->niceLevel
;
768 int firstIOPriority
= processes
.first()->ioniceLevel
;
770 bool supportsIoNice
= d
->mModel
.processController()->supportsIoNiceness();
771 if(!supportsIoNice
) { iosched
= -2; firstIOPriority
= -2; }
772 ReniceDlg
reniceDlg(d
->mUi
->treeView
, selectedAsStrings
, firstPriority
, sched
, firstIOPriority
, iosched
);
773 if(reniceDlg
.exec() == QDialog::Rejected
) return;
775 QList
<long long> renicePids
;
776 QList
<long long> changeCPUSchedulerPids
;
777 QList
<long long> changeIOSchedulerPids
;
778 foreach(KSysGuard::Process
*process
, processes
) {
779 switch(reniceDlg
.newCPUSched
) {
781 case -1: //Invalid, not changed etc.
782 break; //So do nothing
783 case KSysGuard::Process::Other
:
784 case KSysGuard::Process::Fifo
:
785 if(reniceDlg
.newCPUSched
!= (int)process
->scheduler
) {
786 changeCPUSchedulerPids
<< process
->pid
;
787 renicePids
<< process
->pid
;
788 } else if(reniceDlg
.newCPUPriority
!= process
->niceLevel
)
789 renicePids
<< process
->pid
;
792 case KSysGuard::Process::RoundRobin
:
793 case KSysGuard::Process::Batch
:
794 if(reniceDlg
.newCPUSched
!= (int)process
->scheduler
|| reniceDlg
.newCPUPriority
!= process
->niceLevel
) {
795 changeCPUSchedulerPids
<< process
->pid
;
799 switch(reniceDlg
.newIOSched
) {
801 case -1: //Invalid, not changed etc.
802 break; //So do nothing
803 case KSysGuard::Process::None
:
804 if(reniceDlg
.newIOSched
!= (int)process
->ioPriorityClass
) {
805 // Unfortunately linux doesn't actually let us set the ioniceness back to none after being set to something else
806 if(process
->ioPriorityClass
!= KSysGuard::Process::BestEffort
|| reniceDlg
.newIOPriority
!= process
->ioniceLevel
)
807 changeIOSchedulerPids
<< process
->pid
;
810 case KSysGuard::Process::Idle
:
811 if(reniceDlg
.newIOSched
!= (int)process
->ioPriorityClass
) {
812 changeIOSchedulerPids
<< process
->pid
;
815 case KSysGuard::Process::BestEffort
:
816 if(process
->ioPriorityClass
== KSysGuard::Process::None
&& reniceDlg
.newIOPriority
== (process
->niceLevel
+ 20)/5)
817 break; //Don't set to BestEffort if it's on None and the nicelevel wouldn't change
818 case KSysGuard::Process::RealTime
:
819 if(reniceDlg
.newIOSched
!= (int)process
->ioPriorityClass
|| reniceDlg
.newIOPriority
!= process
->ioniceLevel
) {
820 changeIOSchedulerPids
<< process
->pid
;
826 if(!changeCPUSchedulerPids
.isEmpty()) {
827 Q_ASSERT(reniceDlg
.newCPUSched
>= 0);
828 if(!changeCpuScheduler(changeCPUSchedulerPids
, (KSysGuard::Process::Scheduler
) reniceDlg
.newCPUSched
, reniceDlg
.newCPUPriority
)) {
829 KMessageBox::sorry(this, i18n("You do not have sufficient privileges to change the CPU scheduler. Aborting."));
834 if(!renicePids
.isEmpty()) {
835 Q_ASSERT(reniceDlg
.newCPUPriority
<= 20 && reniceDlg
.newCPUPriority
>= -20);
836 if(!reniceProcesses(renicePids
, reniceDlg
.newCPUPriority
)) {
837 KMessageBox::sorry(this, i18n("You do not have sufficient privileges to change the CPU priority. Aborting"));
841 if(!changeIOSchedulerPids
.isEmpty()) {
842 if(!changeIoScheduler(changeIOSchedulerPids
, (KSysGuard::Process::IoPriorityClass
) reniceDlg
.newIOSched
, reniceDlg
.newIOPriority
)) {
843 KMessageBox::sorry(this, i18n("You do not have sufficient privileges to change the IO scheduler and priority. Aborting"));
850 bool KSysGuardProcessList::changeIoScheduler(const QList
< long long> &pids
, KSysGuard::Process::IoPriorityClass newIoSched
, int newIoSchedPriority
)
852 if(newIoSched
== KSysGuard::Process::None
) newIoSched
= KSysGuard::Process::BestEffort
;
853 if(newIoSched
== KSysGuard::Process::Idle
) newIoSchedPriority
= 0;
854 QList
< long long> unchanged_pids
;
855 for (int i
= 0; i
< pids
.size(); ++i
) {
856 bool success
= d
->mModel
.processController()->setIoNiceness(pids
.at(i
), newIoSched
, newIoSchedPriority
);
858 unchanged_pids
<< pids
.at(i
);
861 if(unchanged_pids
.isEmpty()) return true;
862 if(!d
->mModel
.isLocalhost()) return false; //We can't use kdesu to kill non-localhost processes
865 QString su
= KStandardDirs::findExe("kdesu");
866 if(su
.isEmpty()) return false; //Cannot find kdesu
868 //We must use kdesu to kill the process
870 QStringList arguments
;
871 arguments
<< "--attach" << QString::number(window()->winId()) << "--noignorebutton";
872 if(unchanged_pids
.size() == 1) {
873 arguments
<< "--" << "ionice" << "-p" << QString::number(unchanged_pids
.at(0)) << "-c";
875 case KSysGuard::Process::Idle
:
878 case KSysGuard::Process::BestEffort
:
879 arguments
<< "2" << "-n" << QString::number(newIoSchedPriority
);
881 case KSysGuard::Process::RealTime
:
882 arguments
<< "1" << "-n" << QString::number(newIoSchedPriority
);
886 return false; //should never happen - wtf?
889 //Cope with multiple pids by doing a for loop
890 arguments
<< "--" << "sh" << "-c";
891 QString
sh("for f in ");
893 for (int i
= 0; i
< unchanged_pids
.size(); ++i
) {
894 sh
+= QString::number(unchanged_pids
.at(i
)) + " ";
896 sh
+= "; do ionice -p \"$f\" ";
898 case KSysGuard::Process::Idle
:
901 case KSysGuard::Process::BestEffort
:
902 sh
+= "-c 2 -n " + QString::number(newIoSchedPriority
);
904 case KSysGuard::Process::RealTime
:
905 sh
+= "-c 1 -n " + QString::number(newIoSchedPriority
);
909 return false; //should never happen - wtf?
916 QProcess
*process
= new QProcess(NULL
);
917 connect(process
, SIGNAL(error(QProcess::ProcessError
)), this, SLOT(ioniceFailed()));
918 connect(process
, SIGNAL(finished( int, QProcess::ExitStatus
) ), this, SLOT(updateList()));
919 process
->start(su
, arguments
);
920 return true; //assume it ran successfully :( We cannot seem to actually check if it did. There must be a better solution
923 bool KSysGuardProcessList::changeCpuScheduler(const QList
< long long> &pids
, KSysGuard::Process::Scheduler newCpuSched
, int newCpuSchedPriority
)
925 if(newCpuSched
== KSysGuard::Process::Other
|| newCpuSched
== KSysGuard::Process::Batch
) newCpuSchedPriority
= 0;
926 QList
< long long> unchanged_pids
;
927 for (int i
= 0; i
< pids
.size(); ++i
) {
928 bool success
= d
->mModel
.processController()->setScheduler(pids
.at(i
), newCpuSched
, newCpuSchedPriority
);
930 unchanged_pids
<< pids
.at(i
);
933 if(unchanged_pids
.isEmpty()) return true;
934 if(!d
->mModel
.isLocalhost()) {
935 KMessageBox::sorry(this, i18n("No."));
936 return false; //We can't use kdesu to kill non-localhost processes
939 QString su
= KStandardDirs::findExe("kdesu");
941 KMessageBox::sorry(this, i18n("Could not find kdesu executable"));
942 return false; //Cannot find kdesu
944 QString setscheduler
= KStandardDirs::findExe("setscheduler");
945 if(setscheduler
.isEmpty()) {
946 KMessageBox::sorry(this, i18n("Could not find setscheduler executable. This should have been installed alongside system monitor."));
950 //We must use kdesu to kill the process
952 QStringList arguments
;
953 arguments
<< "--attach" << QString::number(window()->winId()) << "--noignorebutton";
954 if(unchanged_pids
.size() == 1) {
955 arguments
<< "--" << setscheduler
<< QString::number(unchanged_pids
.at(0)) << QString::number((int)newCpuSched
) << QString::number(newCpuSchedPriority
);
957 //Cope with multiple pids by doing a for loop
958 arguments
<< "--" << "sh" << "-c";
959 QString
sh("for f in ");
961 for (int i
= 0; i
< unchanged_pids
.size(); ++i
) {
962 sh
+= QString::number(unchanged_pids
.at(i
)) + " ";
964 sh
+= "; do " + setscheduler
+ "\"$f\" " + newCpuSched
+ " " + newCpuSchedPriority
;
970 QProcess
*process
= new QProcess(NULL
);
971 connect(process
, SIGNAL(error(QProcess::ProcessError
)), this, SLOT(ioniceFailed()));
972 connect(process
, SIGNAL(finished( int, QProcess::ExitStatus
) ), this, SLOT(updateList()));
973 process
->start(su
, arguments
);
974 return true; //assume it ran successfully :( We cannot seem to actually check if it did. There must be a better solution
977 bool KSysGuardProcessList::killProcesses(const QList
< long long> &pids
, int sig
)
979 QList
< long long> unkilled_pids
;
980 for (int i
= 0; i
< pids
.size(); ++i
) {
981 bool success
= d
->mModel
.processController()->sendSignal(pids
.at(i
), sig
);
983 unkilled_pids
<< pids
.at(i
);
986 if(unkilled_pids
.isEmpty()) return true;
987 if(!d
->mModel
.isLocalhost()) return false; //We can't use kdesu to kill non-localhost processes
989 QString su
= KStandardDirs::findExe("kdesu");
990 if(su
.isEmpty()) return false; //Cannot find kdesu
992 kDebug() << "running " << su
;
993 //We must use kdesu to kill the process
994 QStringList arguments
;
995 arguments
<< "--attach" << QString::number(window()->winId()) << "--noignorebutton";
996 arguments
<< "--" << "kill";
998 arguments
<< ('-' + QString::number(sig
));
1001 for (int i
= 0; i
< unkilled_pids
.size(); ++i
) {
1002 arguments
<< QString::number(unkilled_pids
.at(i
));
1005 QProcess
*killProcess
= new QProcess(NULL
);
1006 connect(killProcess
, SIGNAL(error(QProcess::ProcessError
)), this, SLOT(killFailed()));
1007 connect(killProcess
, SIGNAL(finished( int, QProcess::ExitStatus
) ), this, SLOT(updateList()));
1008 killProcess
->start(su
, arguments
);
1009 return true; //assume it ran successfully :( We cannot seem to actually check if it did. There must be a better solution
1012 void KSysGuardProcessList::killSelectedProcesses()
1014 QModelIndexList selectedIndexes
= d
->mUi
->treeView
->selectionModel()->selectedRows();
1015 QStringList selectedAsStrings
;
1016 QList
< long long> selectedPids
;
1018 QList
<KSysGuard::Process
*> processes
= selectedProcesses();
1019 foreach(KSysGuard::Process
*process
, processes
) {
1020 selectedPids
<< process
->pid
;
1021 selectedAsStrings
<< d
->mModel
.getStringForProcess(process
);
1024 if (selectedAsStrings
.isEmpty())
1026 KMessageBox::sorry(this, i18n("You must select a process first."));
1031 QString msg
= i18np("Do you want to kill the selected process?",
1032 "Do you want to kill the %1 selected processes?",
1033 selectedAsStrings
.count());
1035 int res
= KMessageBox::warningContinueCancelList(this, msg
, selectedAsStrings
,
1036 i18n("Kill Process"),
1037 KGuiItem(i18n("Kill")),
1038 KStandardGuiItem::cancel(),
1039 "killconfirmation");
1040 if (res
!= KMessageBox::Continue
)
1047 Q_ASSERT(selectedPids
.size() == selectedAsStrings
.size());
1048 if(!killProcesses(selectedPids
, SIGTERM
)) return;
1049 foreach(KSysGuard::Process
*process
, processes
) {
1050 process
->timeKillWasSent
.start();
1055 void KSysGuardProcessList::reniceFailed()
1057 KMessageBox::sorry(this, i18n("You do not have the permission to renice the process and there "
1058 "was a problem trying to run as root"));
1060 void KSysGuardProcessList::killFailed()
1062 KMessageBox::sorry(this, i18n("You do not have the permission to kill the process and there "
1063 "was a problem trying to run as root"));
1065 void KSysGuardProcessList::ioniceFailed()
1067 KMessageBox::sorry(this, i18n("You do not have the permission to set the I/O priority and there "
1068 "was a problem trying to run as root"));
1070 bool KSysGuardProcessList::showTotals() const {
1071 return d
->mModel
.showTotals();
1074 void KSysGuardProcessList::setShowTotals(bool showTotals
) //slot
1076 d
->mModel
.setShowTotals(showTotals
);
1079 ProcessModel::Units
KSysGuardProcessList::units() const {
1080 return d
->mModel
.units();
1083 void KSysGuardProcessList::setUnits(ProcessModel::Units unit
) {
1084 d
->mModel
.setUnits(unit
);
1087 void KSysGuardProcessList::saveSettings(KConfigGroup
&cg
) {
1088 /* Note that the ksysguard program does not use these functions. It saves the settings itself to an xml file instead */
1089 cg
.writeEntry("units", (int)(units()));
1090 cg
.writeEntry("showTotals", showTotals());
1091 cg
.writeEntry("filterState", (int)(state()));
1092 cg
.writeEntry("updateIntervalMSecs", updateIntervalMSecs());
1093 cg
.writeEntry("headerState", d
->mUi
->treeView
->header()->saveState());
1094 //If we change, say, the header between versions of ksysguard, then the old headerState settings will not be valid.
1095 //The version property lets us keep track of which version we are
1096 cg
.writeEntry("version", PROCESSHEADERVERSION
);
1099 void KSysGuardProcessList::loadSettings(const KConfigGroup
&cg
) {
1100 /* Note that the ksysguard program does not use these functions. It saves the settings itself to an xml file instead */
1101 setUnits((ProcessModel::Units
) cg
.readEntry("units", (int)ProcessModel::UnitsKB
));
1102 setShowTotals(cg
.readEntry("showTotals", true));
1103 setStateInt(cg
.readEntry("filterState", (int)ProcessFilter::AllProcesses
));
1104 setUpdateIntervalMSecs(cg
.readEntry("updateIntervalMSecs", 2000));
1105 int version
= cg
.readEntry("version", 0);
1106 if(version
== PROCESSHEADERVERSION
) { //If the header has changed, the old settings are no longer valid. Only restore if version is the same
1107 restoreHeaderState(cg
.readEntry("headerState", QByteArray()));
1111 void KSysGuardProcessList::restoreHeaderState(const QByteArray
& state
) {
1112 d
->mUi
->treeView
->header()->restoreState(state
);
1113 d
->mFilterModel
.sort( d
->mUi
->treeView
->header()->sortIndicatorSection(), d
->mUi
->treeView
->header()->sortIndicatorOrder() );
1116 bool KSysGuardProcessList::eventFilter(QObject
*obj
, QEvent
*event
) {
1117 if (event
->type() == QEvent::KeyPress
) {
1118 QKeyEvent
*keyEvent
= static_cast<QKeyEvent
*>(event
);
1119 if(obj
== d
->mUi
->treeView
) {
1120 if( keyEvent
->key() == Qt::Key_Enter
|| keyEvent
->key() == Qt::Key_Return
) {
1121 d
->mUi
->treeView
->selectionModel()->select(d
->mUi
->treeView
->currentIndex(), QItemSelectionModel::Select
| QItemSelectionModel::Rows
);
1122 showProcessContextMenu(d
->mUi
->treeView
->currentIndex());
1125 // obj must be txtFilter
1126 if(keyEvent
->matches(QKeySequence::MoveToNextLine
) || keyEvent
->matches(QKeySequence::SelectNextLine
) ||
1127 keyEvent
->matches(QKeySequence::MoveToPreviousLine
) || keyEvent
->matches(QKeySequence::SelectPreviousLine
) ||
1128 keyEvent
->matches(QKeySequence::MoveToNextPage
) || keyEvent
->matches(QKeySequence::SelectNextPage
) ||
1129 keyEvent
->matches(QKeySequence::MoveToPreviousPage
) || keyEvent
->matches(QKeySequence::SelectPreviousPage
) ||
1130 keyEvent
->key() == Qt::Key_Enter
|| keyEvent
->key() == Qt::Key_Return
)
1132 QApplication::sendEvent(d
->mUi
->treeView
, event
);
1140 ProcessModel
*KSysGuardProcessList::processModel() {