add more spacing
[personal-kdebase.git] / workspace / libs / ksysguard / processui / ProcessModel.cc
blob8a5341d4db5e2da6816ba3e93cbb9e253c299438
1 /*
2 KSysGuard, the KDE System Guard
4 Copyright (c) 1999, 2000 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.
26 #include <kapplication.h>
27 #include <kiconloader.h>
28 #include <kdebug.h>
29 #include <klocale.h>
30 #include <QBitmap>
31 #include <QFont>
32 #include <QIcon>
33 #include <QPixmap>
34 #include <QList>
35 #include <QMimeData>
36 #include <QTextDocument>
38 #define HEADING_X_ICON_SIZE 16
40 #define GET_OWN_ID
42 #ifdef GET_OWN_ID
43 /* For getuid*/
44 #include <unistd.h>
45 #include <sys/types.h>
46 #endif
48 #include "ProcessModel.moc"
49 #include "ProcessModel_p.moc"
50 #include "ProcessModel.h"
51 #include "ProcessModel_p.h"
53 #include "processcore/processes.h"
54 #include "processcore/process.h"
56 extern KApplication* Kapp;
58 ProcessModelPrivate::ProcessModelPrivate() : mBlankPixmap(HEADING_X_ICON_SIZE,1)
60 mBlankPixmap.fill(QColor(0,0,0,0));
61 mSimple = true;
62 mIsLocalhost = true;
63 mMemTotal = -1;
64 mNumProcessorCores = 1;
65 mProcesses = NULL;
66 mShowChildTotals = true;
67 mIsChangingLayout = false;
70 ProcessModelPrivate::~ProcessModelPrivate()
72 if(mProcesses)
73 KSysGuard::Processes::returnInstance(mHostName);
74 foreach(WindowInfo wininfo, mPidToWindowInfo) {
75 delete wininfo.netWinInfo;
77 mProcesses = NULL;
79 ProcessModel::ProcessModel(QObject* parent, const QString &host)
80 : QAbstractItemModel(parent), d(new ProcessModelPrivate)
82 KGlobal::locale()->insertCatalog("processui"); //Make sure we include the translation stuff. This needs to be run before any i18n call here
83 d->q=this;
84 if(host.isEmpty() || host == "localhost") {
85 d->mHostName = QString();
86 d->mIsLocalhost = true;
87 } else {
88 d->mHostName = host;
89 d->mIsLocalhost = false;
91 setupHeader();
92 d->setupProcesses();
93 d->setupWindows();
94 d->mUnits = UnitsKB;
97 ProcessModel::~ProcessModel()
99 delete d;
102 KSysGuard::Processes *ProcessModel::processController()
104 return d->mProcesses;
107 void ProcessModelPrivate::windowRemoved(WId wid) {
108 #ifdef Q_WS_X11
109 long long pid = mWIdToPid.value(wid, 0);
110 if(pid <= 0) return;
112 int count = mPidToWindowInfo.count(pid);
113 QMultiHash<long long, WindowInfo>::iterator i = mPidToWindowInfo.find(pid);
114 while (i != mPidToWindowInfo.end() && i.key() == pid) {
115 if(i.value().wid == wid) {
116 delete i.value().netWinInfo;
117 i = mPidToWindowInfo.erase(i);
118 // break;
119 } else
120 i++;
122 Q_ASSERT(count-1 == mPidToWindowInfo.count(pid) || count == mPidToWindowInfo.count(pid));
123 KSysGuard::Process *process = mProcesses->getProcess(pid);
124 if(!process) return;
126 int row;
127 if(mSimple)
128 row = process->index;
129 else
130 row = process->parent->children.indexOf(process);
131 QModelIndex index1 = q->createIndex(row, ProcessModel::HeadingName, process);
132 emit q->dataChanged(index1, index1);
133 QModelIndex index2 = q->createIndex(row, ProcessModel::HeadingXTitle, process);
134 emit q->dataChanged(index2, index2);
135 #endif
138 void ProcessModelPrivate::setupWindows() {
139 #ifdef Q_WS_X11
140 QList<WId>::ConstIterator it;
141 for ( it = KWindowSystem::windows().begin(); it != KWindowSystem::windows().end(); ++it ) {
142 windowAdded(*it);
145 connect( KWindowSystem::self(), SIGNAL( windowChanged (WId, unsigned int )), this, SLOT(windowChanged(WId, unsigned int)));
146 connect( KWindowSystem::self(), SIGNAL( windowAdded (WId )), this, SLOT(windowAdded(WId)));
147 connect( KWindowSystem::self(), SIGNAL( windowRemoved (WId )), this, SLOT(windowRemoved(WId)));
148 #endif
151 void ProcessModelPrivate::setupProcesses() {
152 if(mProcesses) {
153 mWIdToPid.clear();
154 mPidToWindowInfo.clear();
155 KSysGuard::Processes::returnInstance(mHostName);
156 q->reset();
159 mProcesses = KSysGuard::Processes::getInstance(mHostName);
161 connect( mProcesses, SIGNAL( processChanged(KSysGuard::Process *, bool)), this, SLOT(processChanged(KSysGuard::Process *, bool)));
162 connect( mProcesses, SIGNAL( beginAddProcess(KSysGuard::Process *)), this, SLOT(beginInsertRow( KSysGuard::Process *)));
163 connect( mProcesses, SIGNAL( endAddProcess()), this, SLOT(endInsertRow()));
164 connect( mProcesses, SIGNAL( beginRemoveProcess(KSysGuard::Process *)), this, SLOT(beginRemoveRow( KSysGuard::Process *)));
165 connect( mProcesses, SIGNAL( endRemoveProcess()), this, SLOT(endRemoveRow()));
166 connect( mProcesses, SIGNAL( beginMoveProcess(KSysGuard::Process *, KSysGuard::Process *)), this,
167 SLOT( beginMoveProcess(KSysGuard::Process *, KSysGuard::Process *)));
168 connect( mProcesses, SIGNAL( endMoveProcess()), this, SLOT(endMoveRow()));
169 mNumProcessorCores = mProcesses->numberProcessorCores();
170 if(mNumProcessorCores < 1) mNumProcessorCores=1; //Default to 1 if there was an error getting the number
173 #ifdef Q_WS_X11
174 void ProcessModelPrivate::windowChanged(WId wid, unsigned int properties)
176 if(! (properties & NET::WMVisibleName || properties & NET::WMName || properties & NET::WMIcon || properties & NET::WMState)) return;
177 windowAdded(wid);
181 void ProcessModelPrivate::windowAdded(WId wid)
183 foreach(WindowInfo w, mPidToWindowInfo) {
184 if(w.wid == wid) return; //already added
186 //The name changed
187 KXErrorHandler handler;
188 NETWinInfo *info = new NETWinInfo( QX11Info::display(), wid, QX11Info::appRootWindow(),
189 NET::WMPid | NET::WMVisibleName | NET::WMName | NET::WMState );
190 if (handler.error( false ) ) {
191 delete info;
192 return; //info is invalid - window just closed or something probably
194 long long pid = info->pid();
195 if(pid <= 0) {
196 delete info;
197 return;
200 WindowInfo w;
201 w.icon = KWindowSystem::icon(wid, HEADING_X_ICON_SIZE, HEADING_X_ICON_SIZE, true);
202 w.wid = wid;
203 w.netWinInfo = info;
204 mPidToWindowInfo.insertMulti(pid, w);
205 mWIdToPid[wid] = pid;
207 KSysGuard::Process *process = mProcesses->getProcess(pid);
208 if(!process) return; //shouldn't really happen.. maybe race condition etc
209 int row;
210 if(mSimple)
211 row = process->index;
212 else
213 row = process->parent->children.indexOf(process);
214 QModelIndex index1 = q->createIndex(row, ProcessModel::HeadingName, process);
215 emit q->dataChanged(index1, index1);
216 QModelIndex index2 = q->createIndex(row, ProcessModel::HeadingXTitle, process);
217 emit q->dataChanged(index2, index2);
219 #endif
221 void ProcessModel::update(int updateDurationMS) {
222 // kDebug() << "update all processes: " << QTime::currentTime().toString("hh:mm:ss.zzz");
223 d->mProcesses->updateAllProcesses(updateDurationMS);
224 if(d->mMemTotal <= 0)
225 d->mMemTotal = d->mProcesses->totalPhysicalMemory();
226 if(d->mIsChangingLayout) {
227 d->mIsChangingLayout = false;
228 emit layoutChanged();
230 // kDebug() << "finished: " << QTime::currentTime().toString("hh:mm:ss.zzz");
233 QString ProcessModelPrivate::getStatusDescription(KSysGuard::Process::ProcessStatus status) const
235 switch( status) {
236 case KSysGuard::Process::Running:
237 return i18n("- Process is doing some work");
238 case KSysGuard::Process::Sleeping:
239 return i18n("- Process is waiting for something to happen");
240 case KSysGuard::Process::Stopped:
241 return i18n("- Process has been stopped. It will not respond to user input at the moment");
242 case KSysGuard::Process::Zombie:
243 return i18n("- Process has finished and is now dead, but the parent process has not cleaned up");
244 default:
245 return QString();
249 KSysGuard::Process *ProcessModel::getProcessAtIndex(int index) const
251 Q_ASSERT(d->mSimple);
252 return d->mProcesses->getAllProcesses().at(index);
254 int ProcessModel::rowCount(const QModelIndex &parent) const
256 if(d->mSimple) {
257 if(parent.isValid()) return 0; //In flat mode, none of the processes have children
258 return d->mProcesses->getAllProcesses().count();
261 //Deal with the case that we are showing it as a tree
262 KSysGuard::Process *process;
263 if(parent.isValid()) {
264 if(parent.column() != 0) return 0; //For a treeview we say that only the first column has children
265 process = reinterpret_cast< KSysGuard::Process * > (parent.internalPointer()); //when parent is invalid, it must be the root level which we set as 0
266 } else {
267 process = d->mProcesses->getProcess(0);
269 Q_ASSERT(process);
270 int num_rows = process->children.count();
271 return num_rows;
274 int ProcessModel::columnCount ( const QModelIndex & ) const
276 return d->mHeadings.count();
279 bool ProcessModel::hasChildren ( const QModelIndex & parent = QModelIndex() ) const
282 if(d->mSimple) {
283 if(parent.isValid()) return 0; //In flat mode, none of the processes have children
284 return !d->mProcesses->getAllProcesses().isEmpty();
287 //Deal with the case that we are showing it as a tree
288 KSysGuard::Process *process;
289 if(parent.isValid()) {
290 if(parent.column() != 0) return false; //For a treeview we say that only the first column has children
291 process = reinterpret_cast< KSysGuard::Process * > (parent.internalPointer()); //when parent is invalid, it must be the root level which we set as 0
292 } else {
293 process = d->mProcesses->getProcess(0);
295 Q_ASSERT(process);
296 bool has_children = !process->children.isEmpty();
298 Q_ASSERT((rowCount(parent) > 0) == has_children);
299 return has_children;
302 QModelIndex ProcessModel::index ( int row, int column, const QModelIndex & parent ) const
304 if(row<0) return QModelIndex();
305 if(column<0 || column >= d->mHeadings.count() ) return QModelIndex();
307 if(d->mSimple) {
308 if( parent.isValid()) return QModelIndex();
309 if( d->mProcesses->getAllProcesses().count() <= row) return QModelIndex();
310 return createIndex( row, column, d->mProcesses->getAllProcesses().at(row));
313 //Deal with the case that we are showing it as a tree
314 KSysGuard::Process *parent_process = 0;
316 if(parent.isValid()) //not valid for init, and init has ppid of 0
317 parent_process = reinterpret_cast< KSysGuard::Process * > (parent.internalPointer());
318 else
319 parent_process = d->mProcesses->getProcess(0);
320 Q_ASSERT(parent_process);
322 if(parent_process->children.count() > row)
323 return createIndex(row,column, parent_process->children[row]);
324 else
326 return QModelIndex();
330 bool ProcessModel::isSimpleMode() const
332 return d->mSimple;
335 void ProcessModelPrivate::processChanged(KSysGuard::Process *process, bool onlyTotalCpu)
337 int row;
338 if(mSimple)
339 row = process->index;
340 else
341 row = process->parent->children.indexOf(process);
343 int totalUpdated = 0;
344 Q_ASSERT(row != -1); //Something has gone very wrong
345 if(onlyTotalCpu) {
346 if(mShowChildTotals) {
347 //Only the total cpu usage changed, so only update that
348 QModelIndex index = q->createIndex(row, ProcessModel::HeadingCPUUsage, process);
349 emit q->dataChanged(index, index);
351 return;
352 } else {
353 if(process->changes == KSysGuard::Process::Nothing) {
354 return; //Nothing changed
356 if(process->changes & KSysGuard::Process::Uids) {
357 totalUpdated++;
358 QModelIndex index = q->createIndex(row, ProcessModel::HeadingUser, process);
359 emit q->dataChanged(index, index);
361 if(process->changes & KSysGuard::Process::Tty) {
362 totalUpdated++;
363 QModelIndex index = q->createIndex(row, ProcessModel::HeadingTty, process);
364 emit q->dataChanged(index, index);
366 if(process->changes & (KSysGuard::Process::Usage | KSysGuard::Process::Status) || (process->changes & KSysGuard::Process::TotalUsage && mShowChildTotals)) {
367 totalUpdated++;
368 QModelIndex index = q->createIndex(row, ProcessModel::HeadingCPUUsage, process);
369 emit q->dataChanged(index, index);
371 if(process->changes & KSysGuard::Process::NiceLevels) {
372 totalUpdated++;
373 QModelIndex index = q->createIndex(row, ProcessModel::HeadingNiceness, process);
374 emit q->dataChanged(index, index);
376 if(process->changes & KSysGuard::Process::VmSize) {
377 totalUpdated++;
378 QModelIndex index = q->createIndex(row, ProcessModel::HeadingVmSize, process);
379 emit q->dataChanged(index, index);
381 if(process->changes & (KSysGuard::Process::VmSize | KSysGuard::Process::VmRSS | KSysGuard::Process::VmURSS)) {
382 totalUpdated+=2;
383 QModelIndex index = q->createIndex(row, ProcessModel::HeadingMemory, process);
384 emit q->dataChanged(index, index);
385 QModelIndex index2 = q->createIndex(row, ProcessModel::HeadingSharedMemory, process);
386 emit q->dataChanged(index2, index2);
389 if(process->changes & KSysGuard::Process::Name) {
390 totalUpdated++;
391 QModelIndex index = q->createIndex(row, ProcessModel::HeadingName, process);
392 emit q->dataChanged(index, index);
394 if(process->changes & KSysGuard::Process::Command) {
395 totalUpdated++;
396 QModelIndex index = q->createIndex(row, ProcessModel::HeadingCommand, process);
397 emit q->dataChanged(index, index);
399 if(process->changes & KSysGuard::Process::Login) {
400 totalUpdated++;
401 QModelIndex index = q->createIndex(row, ProcessModel::HeadingUser, process);
402 emit q->dataChanged(index, index);
407 void ProcessModelPrivate::beginInsertRow( KSysGuard::Process *process)
409 Q_ASSERT(process);
410 if(mIsChangingLayout) {
411 mIsChangingLayout = false;
412 emit q->layoutChanged();
415 if(mSimple) {
416 int row = mProcesses->getAllProcesses().count();
417 q->beginInsertRows( QModelIndex(), row, row );
418 return;
421 //Deal with the case that we are showing it as a tree
422 int row = process->parent->children.count();
423 QModelIndex parentModelIndex = q->getQModelIndex(process->parent, 0);
425 //Only here can we actually change the model. First notify the view/proxy models then modify
426 q->beginInsertRows(parentModelIndex, row, row);
428 void ProcessModelPrivate::endInsertRow() {
429 q->endInsertRows();
431 void ProcessModelPrivate::beginRemoveRow( KSysGuard::Process *process )
433 if(mIsChangingLayout) {
434 mIsChangingLayout = false;
435 emit q->layoutChanged();
438 Q_ASSERT(process);
439 Q_ASSERT(process->pid > 0);
441 if(mSimple) {
442 return q->beginRemoveRows(QModelIndex(), process->index, process->index);
443 } else {
444 int row = process->parent->children.indexOf(process);
445 if(row == -1) {
446 kDebug(1215) << "A serious problem occurred in remove row.";
447 return;
450 return q->beginRemoveRows(q->getQModelIndex(process->parent,0), row, row);
453 void ProcessModelPrivate::endRemoveRow()
455 q->endRemoveRows();
459 void ProcessModelPrivate::beginMoveProcess(KSysGuard::Process *process, KSysGuard::Process *new_parent)
461 if(mSimple) return; //We don't need to move processes when in simple mode
462 if(!mIsChangingLayout) {
463 emit q->layoutAboutToBeChanged ();
464 mIsChangingLayout = true;
467 //FIXME
468 int current_row = process->parent->children.indexOf(process);
469 int new_row = new_parent->children.count();
470 Q_ASSERT(current_row != -1);
472 QList<QModelIndex> fromIndexes;
473 QList<QModelIndex> toIndexes;
474 for(int i=0; i < q->columnCount(); i++) {
475 fromIndexes << q->createIndex(current_row, i, process);
476 toIndexes << q->createIndex(new_row, i, process);
478 q->changePersistentIndexList(fromIndexes, toIndexes);
480 void ProcessModelPrivate::endMoveRow()
485 QModelIndex ProcessModel::getQModelIndex( KSysGuard::Process *process, int column) const
487 Q_ASSERT(process);
488 int pid = process->pid;
489 if(pid == 0) return QModelIndex(); //pid 0 is our fake process meaning the very root (never drawn). To represent that, we return QModelIndex() which also means the top element
490 int row = 0;
491 if(d->mSimple) {
492 row = process->index;
493 } else {
494 row = process->parent->children.indexOf(process);
496 Q_ASSERT(row != -1);
497 return createIndex(row, column, process);
500 QModelIndex ProcessModel::parent ( const QModelIndex & index ) const
502 if(!index.isValid()) return QModelIndex();
503 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
504 Q_ASSERT(process);
506 if(d->mSimple)
507 return QModelIndex();
508 else
509 return getQModelIndex(process->parent,0);
512 QVariant ProcessModel::headerData(int section, Qt::Orientation orientation,
513 int role) const
515 if(orientation != Qt::Horizontal)
516 return QVariant();
517 if(section < 0 || section >= d->mHeadings.count())
518 return QVariant(); //is this needed?
519 switch( role ) {
520 case Qt::TextAlignmentRole:
522 switch(section) {
523 case HeadingPid:
524 case HeadingMemory:
525 case HeadingSharedMemory:
526 case HeadingVmSize:
527 // return QVariant(Qt::AlignRight);
528 case HeadingUser:
529 case HeadingCPUUsage:
530 return QVariant(Qt::AlignCenter);
533 return QVariant();
535 case Qt::ToolTipRole:
537 switch(section) {
538 case HeadingName:
539 return i18n("The process name");
540 case HeadingUser:
541 return i18n("The user that owns this process");
542 case HeadingTty:
543 return i18n("The controlling terminal that this process is running on.");
544 case HeadingNiceness:
545 return i18n("The priority that this process is being run with. Ranges from 19 (very nice, least priority) to -19 (top priority)");
546 case HeadingCPUUsage:
547 if(d->mNumProcessorCores == 1)
548 return i18n("The current CPU usage of the process.");
549 else
550 // i18n: %1 is always greater than 1, so do not worry about
551 // nonsensical verbosity of the singular part.
552 return i18np("The current total CPU usage of the process, divided by the %1 processor core in the machine.", "The current total CPU usage of the process, divided by the %1 processor cores in the machine.", d->mNumProcessorCores);
553 case HeadingVmSize:
554 return i18n("<qt>This is the amount of virtual memory space that the process is using, included shared libraries, graphics memory, files on disk, and so on. This number is almost meaningless.</qt>");
555 case HeadingMemory:
556 return i18n("<qt>This is the amount of real physical memory that this process is using by itself. It does not include any swapped out memory, nor the code size of its shared libraries. This is often the most useful figure to judge the memory use of a program.</qt>");
557 case HeadingSharedMemory:
558 return i18n("<qt>This is the amount of real physical memory that this process's shared libraries are using. This memory is shared among all processes that use this library</qt>");
559 case HeadingCommand:
560 return i18n("<qt>The command with which this process was launched</qt>");
561 case HeadingXTitle:
562 return i18n("<qt>The title of any windows that this process is showing</qt>");
563 case HeadingPid:
564 return i18n("The unique Process ID that identifies this process");
565 default:
566 return QVariant();
569 case Qt::DisplayRole:
570 return d->mHeadings[section];
571 default:
572 return QVariant();
575 void ProcessModel::setSimpleMode(bool simple)
577 if(d->mSimple == simple) return;
579 if(!d->mIsChangingLayout) {
580 emit layoutAboutToBeChanged ();
583 d->mSimple = simple;
584 d->mIsChangingLayout = false;
586 int flatrow;
587 int treerow;
588 QList<QModelIndex> flatIndexes;
589 QList<QModelIndex> treeIndexes;
590 foreach( KSysGuard::Process *process, d->mProcesses->getAllProcesses()) {
591 flatrow = process->index;
592 treerow = process->parent->children.indexOf(process);
593 flatIndexes.clear();
594 treeIndexes.clear();
596 for(int i=0; i < columnCount(); i++) {
597 flatIndexes << createIndex(flatrow, i, process);
598 treeIndexes << createIndex(treerow, i, process);
600 if(d->mSimple) //change from tree mode to flat mode
601 changePersistentIndexList(treeIndexes, flatIndexes);
602 else // change from flat mode to tree mode
603 changePersistentIndexList(flatIndexes, treeIndexes);
605 emit layoutChanged();
609 bool ProcessModel::canUserLogin(long long uid ) const
611 if(uid == 65534) {
612 //nobody user
613 return false;
616 if(!d->mIsLocalhost) return true; //We only deal with localhost. Just always return true for non localhost
618 int canLogin = d->mUidCanLogin.value(uid, -1); //Returns 0 if we cannot login, 1 if we can, and the default is -1 meaning we don't know
619 if(canLogin != -1) return canLogin; //We know whether they can log in
621 //We got the default, -1, so we don't know. Look it up
623 KUser user(uid);
624 if(!user.isValid()) {
625 //for some reason the user isn't recognised. This might happen under certain security situations.
626 //Just return true to be safe
627 d->mUidCanLogin[uid] = 1;
628 return true;
630 QString shell = user.shell();
631 if(shell == "/bin/false" ) //FIXME - add in any other shells it could be for false
633 d->mUidCanLogin[uid] = 0;
634 return false;
636 d->mUidCanLogin[uid] = 1;
637 return true;
640 QString ProcessModelPrivate::getTooltipForUser(const KSysGuard::Process *ps) const
642 QString userTooltip = "<qt><p style='white-space:pre'>";
643 if(!mIsLocalhost) {
644 userTooltip += i18n("Login Name: %1<br/>", getUsernameForUser(ps->uid, true));
645 } else {
646 KUser user(ps->uid);
647 if(!user.isValid())
648 userTooltip += i18n("This user is not recognized for some reason.");
649 else {
650 if(!user.property(KUser::FullName).isValid())
651 userTooltip += i18n("<b>%1</b><br/>", user.property(KUser::FullName).toString());
652 userTooltip += i18n("Login Name: %1 (uid: %2)<br/>", user.loginName(), ps->uid);
653 if(!user.property(KUser::RoomNumber).isValid())
654 userTooltip += i18n(" Room Number: %1<br/>", user.property(KUser::RoomNumber).toString());
655 if(!user.property(KUser::WorkPhone).isValid())
656 userTooltip += i18n(" Work Phone: %1<br/>", user.property(KUser::WorkPhone).toString());
659 if( (ps->uid != ps->euid && ps->euid != -1) ||
660 (ps->uid != ps->suid && ps->suid != -1) ||
661 (ps->uid != ps->fsuid && ps->fsuid != -1)) {
662 if(ps->euid != -1)
663 userTooltip += i18n("Effective User: %1<br/>", getUsernameForUser(ps->euid, true));
664 if(ps->suid != -1)
665 userTooltip += i18n("Setuid User: %1<br/>", getUsernameForUser(ps->suid, true));
666 if(ps->fsuid != -1)
667 userTooltip += i18n("File System User: %1<br/>", getUsernameForUser(ps->fsuid, true));
668 userTooltip += "<br/>";
670 if(ps->gid != -1) {
671 userTooltip += i18n("Group: %1", getGroupnameForGroup(ps->gid));
672 if( (ps->gid != ps->egid && ps->egid != -1) ||
673 (ps->gid != ps->sgid && ps->sgid != -1) ||
674 (ps->gid != ps->fsgid && ps->fsgid != -1)) {
675 if(ps->egid != -1)
676 userTooltip += i18n("<br/>Effective Group: %1", getGroupnameForGroup(ps->egid));
677 if(ps->sgid != -1)
678 userTooltip += i18n("<br/>Setuid Group: %1", getGroupnameForGroup(ps->sgid));
679 if(ps->fsgid != -1)
680 userTooltip += i18n("<br/>File System Group: %1", getGroupnameForGroup(ps->fsgid));
683 return userTooltip;
686 QString ProcessModel::getStringForProcess(KSysGuard::Process *process) const {
687 return i18nc("Short description of a process. PID, name, user", "<numid>%1</numid>: %2, owned by user %3", (long)(process->pid), process->name, d->getUsernameForUser(process->uid, false));
690 QString ProcessModelPrivate::getGroupnameForGroup(long long gid) const {
691 if(mIsLocalhost) {
692 QString groupname = KUserGroup(gid).name();
693 if(!groupname.isEmpty())
694 return i18n("%1 (gid: <numid>%2</numid>)", groupname, gid);
696 return QString::number(gid);
699 QString ProcessModelPrivate::getUsernameForUser(long long uid, bool withuid) const {
700 QString &username = mUserUsername[uid];
701 if(username.isNull()) {
702 if(!mIsLocalhost) {
703 username = ""; //empty, but not null
704 } else {
705 KUser user(uid);
706 if(!user.isValid())
707 username = "";
708 else
709 username = user.loginName();
712 if(username.isEmpty())
713 return QString::number(uid);
714 if(withuid)
715 return i18n("%1 (uid: %2)", username, (long int)uid);
716 return username;
719 QVariant ProcessModel::data(const QModelIndex &index, int role) const
721 //This function must be super duper ultra fast because it's called thousands of times every few second :(
722 //I think it should be optomised for role first, hence the switch statement (fastest possible case)
724 if (!index.isValid()) {
725 return QVariant();
727 if (index.column() >= d->mHeadings.count()) {
728 return QVariant();
731 switch (role){
732 case Qt::DisplayRole: {
733 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
734 switch(index.column()) {
735 case HeadingName:
736 return process->name;
737 case HeadingPid:
738 return (qlonglong)process->pid;
739 case HeadingUser:
740 if(!process->login.isEmpty()) return process->login;
741 if(process->uid == process->euid)
742 return d->getUsernameForUser(process->uid, false);
743 else
744 return d->getUsernameForUser(process->uid, false) + ", " + d->getUsernameForUser(process->euid, false);
745 case HeadingNiceness:
746 return process->niceLevel;
747 case HeadingTty:
748 return process->tty;
749 case HeadingCPUUsage:
751 double total;
752 if(d->mShowChildTotals && !d->mSimple) total = process->totalUserUsage + process->totalSysUsage;
753 else total = process->userUsage + process->sysUsage;
754 total = total / d->mNumProcessorCores;
756 if(total < 1 && process->status != KSysGuard::Process::Sleeping && process->status != KSysGuard::Process::Running)
757 return process->translatedStatus(); //tell the user when the process is a zombie or stopped
758 if(total < 0.5)
759 return "";
761 return QString::number((int)(total+0.5)) + '%';
763 case HeadingMemory:
764 if(process->vmRSS == 0) return QVariant(QVariant::String);
765 if(process->vmURSS == -1) {
766 //If we don't have the URSS (the memory used by only the process, not the shared libraries)
767 //then return the RSS (physical memory used by the process + shared library) as the next best thing
768 return formatMemoryInfo(process->vmRSS);
769 } else {
770 return formatMemoryInfo(process->vmURSS);
772 case HeadingVmSize:
773 if(process->vmSize == 0) return QVariant(QVariant::String);
774 return formatMemoryInfo(process->vmSize);
775 case HeadingSharedMemory:
776 if(process->vmRSS - process->vmURSS <= 0 || process->vmURSS == -1) return QVariant(QVariant::String);
777 return formatMemoryInfo(process->vmRSS - process->vmURSS);
778 case HeadingCommand:
780 return process->command;
781 // It would be nice to embolden the process name in command, but this requires that the itemdelegate to support html text
782 // QString command = process->command;
783 // command.replace(process->name, "<b>" + process->name + "</b>");
784 // return "<qt>" + command;
786 #ifdef Q_WS_X11
787 case HeadingXTitle:
789 if(!d->mPidToWindowInfo.contains(process->pid)) return QVariant(QVariant::String);
790 WindowInfo w = d->mPidToWindowInfo.value(process->pid);
791 if(!w.netWinInfo) return QVariant(QVariant::String);
792 const char *name = w.netWinInfo->visibleName();
793 if( !name || name[0] == 0 )
794 name = w.netWinInfo->name();
795 if(name && name[0] != 0)
796 return QString::fromUtf8(name);
797 return QVariant(QVariant::String);
799 #endif
800 default:
801 return QVariant();
803 break;
805 case Qt::ToolTipRole: {
806 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
807 QString tracer;
808 if(process->tracerpid > 0) {
809 KSysGuard::Process *process_tracer = d->mProcesses->getProcess(process->tracerpid);
810 if(process_tracer) { //it is possible for this to be not the case in certain race conditions
811 KSysGuard::Process *process_tracer = d->mProcesses->getProcess(process->tracerpid);
812 tracer = i18nc("tooltip. name,pid ","This process is being debugged by %1 (<numid>%2</numid>)", process_tracer->name, (long int)process->tracerpid);
815 switch(index.column()) {
816 case HeadingName: {
817 QString tooltip = "<qt><p style='white-space:pre'>";
818 /* It would be nice to be able to show the icon in the tooltip, but Qt4 won't let us put
819 * a picture in a tooltip :(
821 QIcon icon;
822 if(mPidToWindowInfo.contains(process->pid)) {
823 WId wid;
824 wid = mPidToWindowInfo[process->pid].wid;
825 icon = KWindowSystem::icon(wid);
827 if(icon.isValid()) {
828 tooltip = i18n("<qt><table><tr><td>%1", icon);
831 if(process->parent_pid == 0) {
832 //Give a quick explanation of init and kthreadd
833 if(process->name == "init") {
834 tooltip += i18n("<b>Init</b> is the parent of all other processes and cannot be killed.<br/>");
835 } else if(process->name == "kthreadd") {
836 tooltip += i18n("<b>KThreadd</b> manages kernel threads. The children processes run in the kernel, controlling hard disk access, etc.<br/>");
838 tooltip += i18nc("name column tooltip. first item is the name","<b>%1</b><br />Process ID: <numid>%2</numid>", process->name, (long int)process->pid);
840 else {
841 KSysGuard::Process *parent_process = d->mProcesses->getProcess(process->parent_pid);
842 if(parent_process) { //it should not be possible for this process to not exist, but check just incase
843 tooltip = i18nc("name column tooltip. first item is the name","<b>%1</b><br />Process ID: <numid>%2</numid><br />Parent: %3<br />Parent's ID: <numid>%4</numid>", process->name, (long int)process->pid, parent_process->name, (long int)process->parent_pid);
844 } else {
845 tooltip = i18nc("name column tooltip. first item is the name","<b>%1</b><br />Process ID: <numid>%2</numid><br />Parent's ID: <numid>%3</numid>", process->name, (long int)process->pid, (long int)process->parent_pid);
848 if(!process->command.isEmpty()) {
849 tooltip+= i18n("<br/>Command: %1", process->command);
851 if(!process->tty.isEmpty())
852 tooltip += i18n( "<br />Running on: %1", QString(process->tty));
853 if(!tracer.isEmpty())
854 return tooltip + "<br />" + tracer;
855 return tooltip;
858 case HeadingCommand: {
859 QString tooltip =
860 i18n("<qt>This process was run with the following command:<br />%1", process->command);
861 if(!process->tty.isEmpty())
862 tooltip += i18n( "<br /><br />Running on: %1", QString(process->tty));
863 if(tracer.isEmpty()) return tooltip;
864 return tooltip + "<br />" + tracer;
866 case HeadingUser: {
867 if(!tracer.isEmpty())
868 return d->getTooltipForUser(process) + "<br />" + tracer;
869 return d->getTooltipForUser(process);
871 case HeadingNiceness: {
872 QString tooltip = "<qt><p style='white-space:pre'>";
873 switch(process->scheduler) {
874 case KSysGuard::Process::Other:
875 case KSysGuard::Process::Batch:
876 tooltip += i18n("Nice level: %1 (%2)", process->niceLevel, process->niceLevelAsString() );
877 break;
878 case KSysGuard::Process::RoundRobin:
879 case KSysGuard::Process::Fifo:
880 tooltip += i18n("Scheduler priority: %1", process->niceLevel);
881 break;
883 if(process->scheduler != KSysGuard::Process::Other)
884 tooltip += i18n("<br/>Scheduler: %1", process->schedulerAsString());
886 if(process->ioPriorityClass != KSysGuard::Process::None) {
887 if((process->ioPriorityClass == KSysGuard::Process::RealTime || process->ioPriorityClass == KSysGuard::Process::BestEffort) && process->ioniceLevel != -1)
888 tooltip += i18n("<br/>I/O Nice level: %1 (%2)", process->ioniceLevel, process->ioniceLevelAsString() );
889 tooltip += i18n("<br/>I/O Class: %1", process->ioPriorityClassAsString() );
891 if(tracer.isEmpty()) return tooltip;
892 return tooltip + "<br />" + tracer;
894 case HeadingCPUUsage: {
895 QString tooltip = ki18n("<qt><p style='white-space:pre'>"
896 "Process status: %1 %2<br />"
897 "User CPU usage: %3%<br />System CPU usage: %4%</qt>")
898 .subs(process->translatedStatus())
899 .subs(d->getStatusDescription(process->status))
900 .subs((float)(process->userUsage) / d->mNumProcessorCores)
901 .subs((float)(process->sysUsage) / d->mNumProcessorCores)
902 .toString();
904 if(process->numChildren > 0) {
905 tooltip += ki18n("<br />Number of children: %1<br />Total User CPU usage: %2%<br />"
906 "Total System CPU usage: %3%<br />Total CPU usage: %4%")
907 .subs(process->numChildren)
908 .subs((float)(process->totalUserUsage)/ d->mNumProcessorCores)
909 .subs((float)(process->totalSysUsage) / d->mNumProcessorCores)
910 .subs((float)(process->totalUserUsage + process->totalSysUsage) / d->mNumProcessorCores)
911 .toString();
913 if(process->userTime > 0)
914 tooltip += ki18n("<br /><br />CPU time spent running as user: %1 seconds")
915 .subs(process->userTime / 100.0, 0, 'f', 1)
916 .toString();
917 if(process->sysTime > 0)
918 tooltip += ki18n("<br />CPU time spent running in kernel: %1 seconds")
919 .subs(process->sysTime / 100.0, 0, 'f', 1)
920 .toString();
921 if(process->niceLevel != 0)
922 tooltip += i18n("<br />Nice level: %1 (%2)", process->niceLevel, process->niceLevelAsString() );
923 if(process->ioPriorityClass != KSysGuard::Process::None) {
924 if((process->ioPriorityClass == KSysGuard::Process::RealTime || process->ioPriorityClass == KSysGuard::Process::BestEffort) && process->ioniceLevel != -1)
925 tooltip += i18n("<br/>I/O Nice level: %1 (%2)", process->ioniceLevel, process->ioniceLevelAsString() );
926 tooltip += i18n("<br/>I/O Class: %1", process->ioPriorityClassAsString() );
929 if(!tracer.isEmpty())
930 return tooltip + "<br />" + tracer;
931 return tooltip;
933 case HeadingVmSize: {
934 return QVariant();
936 case HeadingMemory: {
937 QString tooltip = "<qt><p style='white-space:pre'>";
938 if(process->vmURSS != -1) {
939 //We don't have information about the URSS, so just fallback to RSS
940 if(d->mMemTotal > 0)
941 tooltip += i18n("Memory usage: %1 out of %2 (%3 %)<br />", KGlobal::locale()->formatByteSize(process->vmURSS * 1024), KGlobal::locale()->formatByteSize(d->mMemTotal*1024), process->vmURSS*100/d->mMemTotal);
942 else
943 tooltip += i18n("Memory usage: %1<br />", KGlobal::locale()->formatByteSize(process->vmURSS * 1024));
945 if(d->mMemTotal > 0)
946 tooltip += i18n("RSS Memory usage: %1 out of %2 (%3 %)", KGlobal::locale()->formatByteSize(process->vmRSS * 1024), KGlobal::locale()->formatByteSize(d->mMemTotal*1024), process->vmRSS*100/d->mMemTotal);
947 else
948 tooltip += i18n("RSS Memory usage: %1", KGlobal::locale()->formatByteSize(process->vmRSS * 1024));
949 return tooltip;
951 case HeadingSharedMemory: {
952 QString tooltip = "<qt><p style='white-space:pre'>";
953 if(process->vmURSS == -1) {
954 tooltip += i18n("<qt>Your system does not seem to have this information available to be read.</qt>");
955 return tooltip;
957 if(d->mMemTotal >0)
958 tooltip += i18n("Shared library memory usage: %1 out of %2 (%3 %)", KGlobal::locale()->formatByteSize((process->vmRSS - process->vmURSS) * 1024), KGlobal::locale()->formatByteSize(d->mMemTotal*1024), (process->vmRSS-process->vmURSS)*100/d->mMemTotal);
959 else
960 tooltip += i18n("Shared library memory usage: %1", KGlobal::locale()->formatByteSize((process->vmRSS - process->vmURSS) * 1024));
962 return tooltip;
964 case HeadingXTitle: {
965 QString tooltip;
966 QList<WindowInfo> values = d->mPidToWindowInfo.values(process->pid);
967 if(values.isEmpty()) return QVariant(QVariant::String);
968 for(int i = 0; i< values.size(); i++) {
969 WindowInfo w = values[i];
970 if(w.netWinInfo) {
971 const char *name = w.netWinInfo->visibleName();
972 if( !name || name[0] == 0 )
973 name = w.netWinInfo->name();
974 if(name && name[0] != 0) {
975 if( i==0 && values.size()==1)
976 return QString::fromUtf8(name);
977 tooltip += "<li>" + QString::fromUtf8(name) + "</li>";
981 if(!tooltip.isEmpty())
982 return "<qt><p style='white-space:pre'><ul>" + tooltip + "</ul>";
983 return QVariant(QVariant::String);
986 default:
987 return QVariant(QVariant::String);
990 case Qt::TextAlignmentRole:
991 switch(index.column() ) {
992 case HeadingUser:
993 case HeadingCPUUsage:
994 return QVariant(Qt::AlignCenter);
995 case HeadingPid:
996 case HeadingMemory:
997 case HeadingSharedMemory:
998 case HeadingVmSize:
999 return QVariant(Qt::AlignRight);
1001 return QVariant();
1002 case UidRole: {
1003 if(index.column() != 0) return QVariant(); //If we query with this role, then we want the raw UID for this.
1004 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1005 return process->uid;
1007 case SortingValueRole: {
1008 //We have a special understanding with the filter sort. This returns an int (in a qvariant) that can be sorted by
1009 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1010 Q_ASSERT(process);
1011 switch(index.column()) {
1012 case HeadingUser: {
1013 //Sorting by user will be the default and the most common.
1014 //We want to sort in the most useful way that we can. We need to return a number though.
1015 //This code is based on that sorting ascendingly should put the current user at the top
1017 //First the user we are running as should be at the top. We add 0 for this
1018 //Then any other users in the system. We add 100,000,000 for this (remember it's ascendingly sorted)
1019 //Then at the bottom the 'system' processes. We add 200,000,000 for this
1021 //We subtract the uid to sort ascendingly by that, then subtract the cpu usage to sort by that, then finally subtract the memory
1023 long long base = 0;
1024 long long memory = 0;
1025 if(process->vmURSS != -1) memory = process->vmURSS;
1026 else memory = process->vmRSS;
1027 if(d->mIsLocalhost && process->uid == getuid())
1028 base = 0; //own user
1029 else if(process->uid < 100 || !canUserLogin(process->uid))
1030 base = 200000000 - process->uid * 10000; //system user
1031 else
1032 base = 100000000 - process->uid * 10000;
1033 //One special exception is a traced process since that's probably important. We should put that at the top
1034 if(process->tracerpid >0) return base - 9999 ;
1035 int cpu;
1036 if(d->mSimple || !d->mShowChildTotals)
1037 cpu = process->userUsage + process->sysUsage;
1038 else
1039 cpu = process->totalUserUsage + process->totalSysUsage;
1040 if(cpu == 0 && process->status != KSysGuard::Process::Running && process->status != KSysGuard::Process::Sleeping)
1041 cpu = 1; //stopped or zombied processes should be near the top of the list
1042 bool hasWindow = d->mPidToWindowInfo.contains(process->pid);
1043 //However we can of course have lots of processes with the same user. Next we sort by CPU.
1044 if(d->mMemTotal>0)
1045 return (double)(base - (cpu*100) -(hasWindow?50:0) - memory*100.0/d->mMemTotal);
1046 else
1047 return (double)(base - (cpu*100) -(hasWindow?50:0));
1049 case HeadingCPUUsage: {
1050 int cpu;
1051 if(d->mSimple || !d->mShowChildTotals)
1052 cpu = process->userUsage + process->sysUsage;
1053 else
1054 cpu = process->totalUserUsage + process->totalSysUsage;
1055 if(cpu == 0 && process->status != KSysGuard::Process::Running && process->status != KSysGuard::Process::Sleeping)
1056 cpu = 1; //stopped or zombied processes should be near the top of the list
1057 return -cpu;
1059 case HeadingMemory:
1060 if(process->vmURSS == -1)
1061 return (long long)-process->vmRSS;
1062 else
1063 return (long long)-process->vmURSS;
1064 case HeadingVmSize:
1065 return (long long)-process->vmSize;
1066 case HeadingSharedMemory:
1067 if(process->vmURSS == -1) return (long long)0;
1068 return (long long)-(process->vmRSS - process->vmURSS);
1070 return QVariant();
1072 case PlainValueRole: //Used to return a plain value. For copying to a clipboard etc
1074 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1075 switch(index.column()) {
1076 case HeadingName:
1077 return process->name;
1078 case HeadingPid:
1079 return (qlonglong)process->pid;
1080 case HeadingUser:
1081 if(!process->login.isEmpty()) return process->login;
1082 if(process->uid == process->euid)
1083 return d->getUsernameForUser(process->uid, false);
1084 else
1085 return d->getUsernameForUser(process->uid, false) + ", " + d->getUsernameForUser(process->euid, false);
1086 case HeadingNiceness:
1087 return process->niceLevel;
1088 case HeadingTty:
1089 return process->tty;
1090 case HeadingCPUUsage:
1092 double total;
1093 if(d->mShowChildTotals && !d->mSimple) total = process->totalUserUsage + process->totalSysUsage;
1094 else total = process->userUsage + process->sysUsage;
1095 return total / d->mNumProcessorCores;
1097 case HeadingMemory:
1098 if(process->vmRSS == 0) return QVariant(QVariant::String);
1099 if(process->vmURSS == -1) {
1100 return (long long)process->vmRSS;
1101 } else {
1102 return (long long)process->vmURSS;
1104 case HeadingVmSize:
1105 return (long long)process->vmSize;
1106 case HeadingSharedMemory:
1107 if(process->vmRSS - process->vmURSS < 0 || process->vmURSS == -1) return QVariant(QVariant::String);
1108 return (long long)(process->vmRSS - process->vmURSS);
1109 case HeadingCommand:
1111 return process->command;
1113 #ifdef Q_WS_X11
1114 case HeadingXTitle:
1116 if(!d->mPidToWindowInfo.contains(process->pid)) return QVariant(QVariant::String);
1117 WindowInfo w = d->mPidToWindowInfo.value(process->pid);
1118 if(!w.netWinInfo) return QVariant(QVariant::String);
1119 const char *name = w.netWinInfo->visibleName();
1120 if( !name || name[0] == 0 )
1121 name = w.netWinInfo->name();
1122 if(name && name[0] != 0)
1123 return QString::fromUtf8(name);
1124 return QVariant(QVariant::String);
1126 #endif
1127 default:
1128 return QVariant();
1130 break;
1132 #ifdef Q_WS_X11
1133 case WindowIdRole: {
1134 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1135 if(!d->mPidToWindowInfo.contains(process->pid)) return QVariant();
1136 WindowInfo w = d->mPidToWindowInfo.value(process->pid);
1137 return (int)w.wid;
1139 #endif
1140 case TotalMemoryRole: {
1141 return d->mMemTotal;
1143 case NumberOfProcessorsRole: {
1144 return d->mNumProcessorCores;
1146 case Qt::DecorationRole: {
1147 if(index.column() == HeadingName) {
1148 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1149 if(!d->mPidToWindowInfo.contains(process->pid)) {
1150 if(d->mSimple) //When not in tree mode, we need to pad the name column where we do not have an icon
1151 return QIcon(d->mBlankPixmap);
1152 else //When in tree mode, the padding looks pad, so do not pad in this case
1153 return QVariant();
1156 WindowInfo w = d->mPidToWindowInfo.value(process->pid);
1157 if(w.icon.isNull())
1158 return QIcon(d->mBlankPixmap);
1159 return w.icon;
1161 } else if (index.column() == HeadingCPUUsage) {
1162 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1163 if(process->status == KSysGuard::Process::Stopped || process->status == KSysGuard::Process::Zombie) {
1164 // QPixmap pix = KIconLoader::global()->loadIcon("button_cancel", KIconLoader::Small,
1165 // KIconLoader::SizeSmall, KIconLoader::DefaultState, QStringList(),
1166 // 0L, true);
1170 return QVariant();
1172 case Qt::BackgroundRole: {
1173 if(index.column() != HeadingUser) return QVariant();
1174 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1175 if(process->tracerpid >0) {
1176 //It's being debugged, so probably important. Let's mark it as such
1177 return QColor("yellow");
1179 if(d->mIsLocalhost && process->uid == getuid()) { //own user
1180 return QColor(0, 208, 214, 50);
1182 if(process->uid < 100 || !canUserLogin(process->uid))
1183 return QColor(218, 220,215, 50); //no color for system tasks
1184 //other users
1185 return QColor(2, 154, 54, 50);
1187 case Qt::FontRole: {
1188 if(index.column() == HeadingCPUUsage) {
1189 KSysGuard::Process *process = reinterpret_cast< KSysGuard::Process * > (index.internalPointer());
1190 if(process->userUsage == 0) {
1191 QFont font;
1192 font.setItalic(true);
1193 return font;
1196 return QVariant();
1198 default: //This is a very very common case, so the route to this must be very minimal
1199 return QVariant();
1202 return QVariant(); //never get here, but make compilier happy
1205 bool ProcessModel::hasGUIWindow(long long pid) const
1207 return d->mPidToWindowInfo.contains(pid);
1210 bool ProcessModel::isLocalhost() const
1212 return d->mIsLocalhost;
1216 void ProcessModel::setupHeader() {
1217 QStringList headings;
1218 headings << i18nc("process heading", "Name");
1219 headings << i18nc("process heading", "User Name");
1220 headings << i18nc("process heading", "PID");
1221 headings << i18nc("process heading", "TTY");
1222 headings << i18nc("process heading", "Niceness");
1223 // xgettext: no-c-format
1224 headings << i18nc("process heading", "CPU %");
1225 headings << i18nc("process heading", "Virtual Size");
1226 headings << i18nc("process heading", "Memory");
1227 headings << i18nc("process heading", "Shared Mem");
1228 headings << i18nc("process heading", "Command");
1229 #ifdef Q_WS_X11
1230 headings << i18nc("process heading", "Window Title");
1231 #endif
1233 if(d->mHeadings.isEmpty()) { // If it's empty, this is the first time this has been called, so insert the headings
1234 beginInsertColumns(QModelIndex(), 0, headings.count()-1);
1235 d->mHeadings = headings;
1236 endInsertColumns();
1237 } else {
1238 // This was called to retranslate the headings. Just use the new translations and call headerDataChanged
1239 Q_ASSERT(d->mHeadings.count() == headings.count());
1240 d->mHeadings = headings;
1241 headerDataChanged(Qt::Horizontal, 0 , headings.count()-1);
1246 void ProcessModel::retranslateUi()
1248 setupHeader();
1251 KSysGuard::Process *ProcessModel::getProcess(long long pid) {
1252 return d->mProcesses->getProcess(pid);
1255 bool ProcessModel::showTotals() const {
1256 return d->mShowChildTotals;
1259 void ProcessModel::setShowTotals(bool showTotals) //slot
1261 if(showTotals == d->mShowChildTotals) return;
1262 d->mShowChildTotals = showTotals;
1264 QModelIndex index;
1265 foreach( KSysGuard::Process *process, d->mProcesses->getAllProcesses()) {
1266 if(process->numChildren > 0) {
1267 int row;
1268 if(d->mSimple)
1269 row = process->index;
1270 else
1271 row = process->parent->children.indexOf(process);
1272 index = createIndex(row, HeadingCPUUsage, process);
1273 emit dataChanged(index, index);
1278 long long ProcessModel::totalMemory() const
1280 return d->mMemTotal;
1282 void ProcessModel::setUnits(Units units)
1284 d->mUnits = units;
1286 ProcessModel::Units ProcessModel::units() const
1288 return (Units) d->mUnits;
1291 QString ProcessModel::formatMemoryInfo(long amountInKB) const
1293 switch(d->mUnits) {
1294 case UnitsKB:
1295 return i18n("%1 K", amountInKB);
1296 case UnitsMB:
1297 return i18n("%1 M", (amountInKB+512)/1024); //Round to nearest megabyte
1298 case UnitsGB:
1299 return i18n("%1 G", (amountInKB+512*1024)/(1024*1024)); //Round to nearest gigabyte
1301 return ""; //error
1304 QString ProcessModel::hostName() const {
1305 return d->mHostName;
1307 QStringList ProcessModel::mimeTypes() const
1309 QStringList types;
1310 types << "text/plain";
1311 types << "text/csv";
1312 types << "text/html";
1313 return types;
1315 QMimeData *ProcessModel::mimeData(const QModelIndexList &indexes) const
1317 QMimeData *mimeData = new QMimeData();
1318 QString textCsv;
1319 QString textCsvHeaders;
1320 QString textPlain;
1321 QString textPlainHeaders;
1322 QString textHtml;
1323 QString textHtmlHeaders;
1324 QString display;
1325 int firstColumn = -1;
1326 bool firstrow = true;
1327 foreach (QModelIndex index, indexes) {
1328 if (index.isValid()) {
1329 if(firstColumn == -1)
1330 firstColumn = index.column();
1331 else if(firstColumn != index.column())
1332 continue;
1333 else {
1334 textCsv += '\n';
1335 textPlain += '\n';
1336 textHtml += "</tr><tr>";
1337 firstrow = false;
1339 for(int i = 0; i < d->mHeadings.size(); i++) {
1340 if(firstrow) {
1341 QString heading = d->mHeadings[i];
1342 textHtmlHeaders += "<th>" + heading + "</th>";
1343 if(i) {
1344 textCsvHeaders += ',';
1345 textPlainHeaders += ", ";
1347 textPlainHeaders += heading;
1348 heading.replace('"', "\"\"");
1349 textCsvHeaders += '"' + heading + '"';
1351 QModelIndex index2 = createIndex(index.row(), i, reinterpret_cast< KSysGuard::Process * > (index.internalPointer()));
1352 QString display = data(index2, PlainValueRole).toString();
1353 if(i) {
1354 textCsv += ',';
1355 textPlain += ", ";
1357 textHtml += "<td>" + Qt::escape(display) + "</td>";
1358 textPlain += display;
1359 display.replace('"',"\"\"");
1360 textCsv += '"' + display + '"';
1364 textHtml = "<html><table><tr>" + textHtmlHeaders + "</tr><tr>" + textHtml + "</tr></table>";
1365 textCsv = textCsvHeaders + '\n' + textCsv;
1366 textPlain = textPlainHeaders + '\n' + textPlain;
1368 mimeData->setText(textPlain);
1369 mimeData->setHtml(textHtml);
1370 mimeData->setData("text/csv", textCsv.toUtf8());
1371 return mimeData;
1374 Qt::ItemFlags ProcessModel::flags(const QModelIndex &index) const
1376 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
1377 if (index.isValid())
1378 return Qt::ItemIsDragEnabled | defaultFlags;
1379 else
1380 return defaultFlags;