add more spacing
[personal-kdebase.git] / workspace / libs / ksysguard / processui / ksysguardprocesslist.cpp
blob945d12fb8fab4cb49d0a17f791a84014a723902c
1 /*
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"
26 #include <QTimer>
27 #include <QList>
28 #include <QShowEvent>
29 #include <QHideEvent>
30 #include <QSortFilterProxyModel>
31 #include <QHeaderView>
32 #include <QAction>
33 #include <QMenu>
34 #include <QSet>
35 #include <QComboBox>
36 #include <QStyledItemDelegate>
37 #include <QPainter>
38 #include <QProcess>
39 #include <QLineEdit>
40 #include <QSignalMapper>
43 #include <signal.h> //For SIGTERM
45 #include <kapplication.h>
46 #include <klocale.h>
47 #include <kmessagebox.h>
48 #include <kdialog.h>
49 #include <kicon.h>
50 #include <kdebug.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"
61 #endif
63 #include <sys/types.h>
64 #include <unistd.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
68 #ifdef DO_MODELCHECK
69 #include "modeltest.h"
70 #endif
72 class ProgressBarItemDelegate : public QStyledItemDelegate
74 public:
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());
85 Q_CHECK_PTR(process);
86 if(index.column() == ProcessModel::HeadingCPUUsage) {
87 if(numCpuCores == -1)
88 numCpuCores = index.data(ProcessModel::NumberOfProcessorsRole).toInt();
89 percentage = (process->userUsage + process->sysUsage) / numCpuCores;
90 } else if(index.column() == ProcessModel::HeadingMemory) {
91 long long memory = 0;
92 if(process->vmURSS != -1)
93 memory = process->vmURSS;
94 else
95 memory = process->vmRSS;
96 if(totalMemory == -1)
97 totalMemory = index.data(ProcessModel::TotalMemoryRole).toLongLong();
98 if(totalMemory > 0)
99 percentage = (int)(memory*100/totalMemory);
100 else
101 percentage = 0;
102 } else if(index.column() == ProcessModel::HeadingSharedMemory) {
103 if(process->vmURSS != -1) {
104 if(totalMemory == -1)
105 totalMemory = index.data(ProcessModel::TotalMemoryRole).toLongLong();
106 if(totalMemory > 0)
107 percentage = (int)((process->vmRSS - process->vmURSS)*100/totalMemory);
108 else
109 percentage = 0;
111 } else
112 percentage = -1;
114 if (percentage >= 0)
115 drawPercentageDisplay(painter,option,index.data(Qt::DisplayRole).toString());
116 else
117 QStyledItemDelegate::paint(painter, option, index);
120 private:
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);
157 #else
158 monitorio = 0;
159 #endif
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 */
177 ProcessModel mModel;
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;
196 QAction *renice;
197 QAction *kill;
198 QAction *selectParent;
199 QAction *selectTracer;
200 QAction *window;
201 QAction *monitorio;
202 QAction *resume;
203 QAction *sigStop;
204 QAction *sigCont;
205 QAction *sigHup;
206 QAction *sigInt;
207 QAction *sigTerm;
208 QAction *sigKill;
209 QAction *sigUsr1;
210 QAction *sigUsr2;
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);
220 #ifdef DO_MODELCHECK
221 new ModelTest(&d->mModel, this);
222 #endif
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;
278 if (d->monitorio)
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) {
282 addAction(action);
283 connect(action, SIGNAL(triggered(bool)), signalMapper, SLOT(map()));
284 signalMapper->setMapping(action, action);
286 connect(signalMapper, SIGNAL(mapped(QObject *)), SLOT(actionTriggered(QObject *)));
288 retranslateUi();
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()
309 delete d;
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);
333 expandInit();
335 void KSysGuardProcessList::filterTextChanged(const QString &newText) {
336 d->mFilterModel.setFilterRegExp(newText.trimmed());
337 expandInit();
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);
422 if(result == 0) {
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());
434 if(process)
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());
442 if(process)
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());
462 if(process) {
463 DisplayProcessDlg *dialog = new DisplayProcessDlg( this,process );
464 dialog->show();
466 #endif
467 } else {
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());
475 if(process)
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);
494 updateList();
498 void KSysGuardProcessList::selectAndJumpToProcess(int pid) {
499 KSysGuard::Process *process = d->mModel.getProcess(pid);
500 if(!process) return;
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();
511 QAction *action;
512 int index = d->mUi->treeView->header()->logicalIndexAt(point);
513 if(index >= 0) {
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);
564 break;
565 case ProcessModel::UnitsMB:
566 actionMB->setChecked(true);
567 break;
568 case ProcessModel::UnitsGB:
569 actionGB->setChecked(true);
570 break;
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);
579 return;
580 } else if(result == actionMB) {
581 d->mModel.setUnits(ProcessModel::UnitsMB);
582 return;
583 } else if(result == actionGB) {
584 d->mModel.setUnits(ProcessModel::UnitsGB);
585 return;
587 int i = result->data().toInt();
588 //We set data to be negative to hide a column, and positive to show a column
589 if(i < 0)
590 d->mUi->treeView->hideColumn(-1-i);
591 else {
592 d->mUi->treeView->showColumn(i);
593 d->mUi->treeView->resizeColumnToContents(i);
594 d->mUi->treeView->resizeColumnToContents(d->mFilterModel.columnCount());
596 menu->deleteLater();
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)) {
618 if(!expanded) {
619 disconnect(d->mUi->treeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(expandAllChildren(const QModelIndex &)));
620 expanded = true;
622 d->mUi->treeView->expand(index);
625 if(expanded)
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)) {
636 if(!expanded) {
637 disconnect(d->mUi->treeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(expandAllChildren(const QModelIndex &)));
638 expanded = true;
640 d->mUi->treeView->expand(index);
643 if(expanded)
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);
669 retranslateUi();
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()
686 if(isVisible()) {
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);
708 if(!success) {
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;
741 return processes;
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."));
753 return;
756 int sched = -2;
757 int iosched = -2;
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) {
780 case -2:
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;
790 break;
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;
797 break;
799 switch(reniceDlg.newIOSched) {
800 case -2:
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;
809 break;
810 case KSysGuard::Process::Idle:
811 if(reniceDlg.newIOSched != (int)process->ioPriorityClass) {
812 changeIOSchedulerPids << process->pid;
814 break;
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;
822 break;
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."));
830 return;
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"));
838 return;
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"));
844 return;
847 updateList();
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);
857 if(!success) {
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";
874 switch(newIoSched) {
875 case KSysGuard::Process::Idle:
876 arguments << "3";
877 break;
878 case KSysGuard::Process::BestEffort:
879 arguments << "2" << "-n" << QString::number(newIoSchedPriority);
880 break;
881 case KSysGuard::Process::RealTime:
882 arguments << "1" << "-n" << QString::number(newIoSchedPriority);
883 break;
884 default:
885 Q_ASSERT(false);
886 return false; //should never happen - wtf?
888 } else {
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\" ";
897 switch(newIoSched) {
898 case KSysGuard::Process::Idle:
899 sh += "-c 3";
900 break;
901 case KSysGuard::Process::BestEffort:
902 sh += "-c 2 -n " + QString::number(newIoSchedPriority);
903 break;
904 case KSysGuard::Process::RealTime:
905 sh += "-c 1 -n " + QString::number(newIoSchedPriority);
906 break;
907 default:
908 Q_ASSERT(false);
909 return false; //should never happen - wtf?
911 sh += "; done";
913 arguments << sh;
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);
929 if(!success) {
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");
940 if(su.isEmpty()) {
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."));
947 return false;
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);
956 } else {
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;
965 sh += "; done";
967 arguments << sh;
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);
982 if(!success) {
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";
997 if(sig != SIGTERM) {
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."));
1027 return;
1029 else
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)
1042 return;
1047 Q_ASSERT(selectedPids.size() == selectedAsStrings.size());
1048 if(!killProcesses(selectedPids, SIGTERM)) return;
1049 foreach(KSysGuard::Process *process, processes) {
1050 process->timeKillWasSent.start();
1052 updateList();
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());
1124 } else {
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);
1133 return true;
1137 return false;
1140 ProcessModel *KSysGuardProcessList::processModel() {
1141 return &d->mModel;