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>
36 #include <QTextDocument>
38 #define HEADING_X_ICON_SIZE 16
45 #include <sys/types.h>
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));
64 mNumProcessorCores
= 1;
66 mShowChildTotals
= true;
67 mIsChangingLayout
= false;
70 ProcessModelPrivate::~ProcessModelPrivate()
73 KSysGuard::Processes::returnInstance(mHostName
);
74 foreach(WindowInfo wininfo
, mPidToWindowInfo
) {
75 delete wininfo
.netWinInfo
;
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
84 if(host
.isEmpty() || host
== "localhost") {
85 d
->mHostName
= QString();
86 d
->mIsLocalhost
= true;
89 d
->mIsLocalhost
= false;
97 ProcessModel::~ProcessModel()
102 KSysGuard::Processes
*ProcessModel::processController()
104 return d
->mProcesses
;
107 void ProcessModelPrivate::windowRemoved(WId wid
) {
109 long long pid
= mWIdToPid
.value(wid
, 0);
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
);
122 Q_ASSERT(count
-1 == mPidToWindowInfo
.count(pid
) || count
== mPidToWindowInfo
.count(pid
));
123 KSysGuard::Process
*process
= mProcesses
->getProcess(pid
);
128 row
= process
->index
;
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
);
138 void ProcessModelPrivate::setupWindows() {
140 QList
<WId
>::ConstIterator it
;
141 for ( it
= KWindowSystem::windows().begin(); it
!= KWindowSystem::windows().end(); ++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
)));
151 void ProcessModelPrivate::setupProcesses() {
154 mPidToWindowInfo
.clear();
155 KSysGuard::Processes::returnInstance(mHostName
);
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
174 void ProcessModelPrivate::windowChanged(WId wid
, unsigned int properties
)
176 if(! (properties
& NET::WMVisibleName
|| properties
& NET::WMName
|| properties
& NET::WMIcon
|| properties
& NET::WMState
)) return;
181 void ProcessModelPrivate::windowAdded(WId wid
)
183 foreach(WindowInfo w
, mPidToWindowInfo
) {
184 if(w
.wid
== wid
) return; //already added
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 ) ) {
192 return; //info is invalid - window just closed or something probably
194 long long pid
= info
->pid();
201 w
.icon
= KWindowSystem::icon(wid
, HEADING_X_ICON_SIZE
, HEADING_X_ICON_SIZE
, true);
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
211 row
= process
->index
;
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
);
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
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");
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
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
267 process
= d
->mProcesses
->getProcess(0);
270 int num_rows
= process
->children
.count();
274 int ProcessModel::columnCount ( const QModelIndex
& ) const
276 return d
->mHeadings
.count();
279 bool ProcessModel::hasChildren ( const QModelIndex
& parent
= QModelIndex() ) const
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
293 process
= d
->mProcesses
->getProcess(0);
296 bool has_children
= !process
->children
.isEmpty();
298 Q_ASSERT((rowCount(parent
) > 0) == 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();
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());
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
]);
326 return QModelIndex();
330 bool ProcessModel::isSimpleMode() const
335 void ProcessModelPrivate::processChanged(KSysGuard::Process
*process
, bool onlyTotalCpu
)
339 row
= process
->index
;
341 row
= process
->parent
->children
.indexOf(process
);
343 int totalUpdated
= 0;
344 Q_ASSERT(row
!= -1); //Something has gone very wrong
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
);
353 if(process
->changes
== KSysGuard::Process::Nothing
) {
354 return; //Nothing changed
356 if(process
->changes
& KSysGuard::Process::Uids
) {
358 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingUser
, process
);
359 emit q
->dataChanged(index
, index
);
361 if(process
->changes
& KSysGuard::Process::Tty
) {
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
)) {
368 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingCPUUsage
, process
);
369 emit q
->dataChanged(index
, index
);
371 if(process
->changes
& KSysGuard::Process::NiceLevels
) {
373 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingNiceness
, process
);
374 emit q
->dataChanged(index
, index
);
376 if(process
->changes
& KSysGuard::Process::VmSize
) {
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
)) {
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
) {
391 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingName
, process
);
392 emit q
->dataChanged(index
, index
);
394 if(process
->changes
& KSysGuard::Process::Command
) {
396 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingCommand
, process
);
397 emit q
->dataChanged(index
, index
);
399 if(process
->changes
& KSysGuard::Process::Login
) {
401 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingUser
, process
);
402 emit q
->dataChanged(index
, index
);
407 void ProcessModelPrivate::beginInsertRow( KSysGuard::Process
*process
)
410 if(mIsChangingLayout
) {
411 mIsChangingLayout
= false;
412 emit q
->layoutChanged();
416 int row
= mProcesses
->getAllProcesses().count();
417 q
->beginInsertRows( QModelIndex(), row
, row
);
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() {
431 void ProcessModelPrivate::beginRemoveRow( KSysGuard::Process
*process
)
433 if(mIsChangingLayout
) {
434 mIsChangingLayout
= false;
435 emit q
->layoutChanged();
439 Q_ASSERT(process
->pid
> 0);
442 return q
->beginRemoveRows(QModelIndex(), process
->index
, process
->index
);
444 int row
= process
->parent
->children
.indexOf(process
);
446 kDebug(1215) << "A serious problem occurred in remove row.";
450 return q
->beginRemoveRows(q
->getQModelIndex(process
->parent
,0), row
, row
);
453 void ProcessModelPrivate::endRemoveRow()
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;
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
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
492 row
= process
->index
;
494 row
= process
->parent
->children
.indexOf(process
);
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());
507 return QModelIndex();
509 return getQModelIndex(process
->parent
,0);
512 QVariant
ProcessModel::headerData(int section
, Qt::Orientation orientation
,
515 if(orientation
!= Qt::Horizontal
)
517 if(section
< 0 || section
>= d
->mHeadings
.count())
518 return QVariant(); //is this needed?
520 case Qt::TextAlignmentRole
:
525 case HeadingSharedMemory
:
527 // return QVariant(Qt::AlignRight);
529 case HeadingCPUUsage
:
530 return QVariant(Qt::AlignCenter
);
535 case Qt::ToolTipRole
:
539 return i18n("The process name");
541 return i18n("The user that owns this process");
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.");
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
);
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>");
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>");
560 return i18n("<qt>The command with which this process was launched</qt>");
562 return i18n("<qt>The title of any windows that this process is showing</qt>");
564 return i18n("The unique Process ID that identifies this process");
569 case Qt::DisplayRole
:
570 return d
->mHeadings
[section
];
575 void ProcessModel::setSimpleMode(bool simple
)
577 if(d
->mSimple
== simple
) return;
579 if(!d
->mIsChangingLayout
) {
580 emit
layoutAboutToBeChanged ();
584 d
->mIsChangingLayout
= false;
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
);
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
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
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;
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;
636 d
->mUidCanLogin
[uid
] = 1;
640 QString
ProcessModelPrivate::getTooltipForUser(const KSysGuard::Process
*ps
) const
642 QString userTooltip
= "<qt><p style='white-space:pre'>";
644 userTooltip
+= i18n("Login Name: %1<br/>", getUsernameForUser(ps
->uid
, true));
648 userTooltip
+= i18n("This user is not recognized for some reason.");
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)) {
663 userTooltip
+= i18n("Effective User: %1<br/>", getUsernameForUser(ps
->euid
, true));
665 userTooltip
+= i18n("Setuid User: %1<br/>", getUsernameForUser(ps
->suid
, true));
667 userTooltip
+= i18n("File System User: %1<br/>", getUsernameForUser(ps
->fsuid
, true));
668 userTooltip
+= "<br/>";
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)) {
676 userTooltip
+= i18n("<br/>Effective Group: %1", getGroupnameForGroup(ps
->egid
));
678 userTooltip
+= i18n("<br/>Setuid Group: %1", getGroupnameForGroup(ps
->sgid
));
680 userTooltip
+= i18n("<br/>File System Group: %1", getGroupnameForGroup(ps
->fsgid
));
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 {
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()) {
703 username
= ""; //empty, but not null
709 username
= user
.loginName();
712 if(username
.isEmpty())
713 return QString::number(uid
);
715 return i18n("%1 (uid: %2)", username
, (long int)uid
);
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()) {
727 if (index
.column() >= d
->mHeadings
.count()) {
732 case Qt::DisplayRole
: {
733 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
734 switch(index
.column()) {
736 return process
->name
;
738 return (qlonglong
)process
->pid
;
740 if(!process
->login
.isEmpty()) return process
->login
;
741 if(process
->uid
== process
->euid
)
742 return d
->getUsernameForUser(process
->uid
, false);
744 return d
->getUsernameForUser(process
->uid
, false) + ", " + d
->getUsernameForUser(process
->euid
, false);
745 case HeadingNiceness
:
746 return process
->niceLevel
;
749 case HeadingCPUUsage
:
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
761 return QString::number((int)(total
+0.5)) + '%';
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
);
770 return formatMemoryInfo(process
->vmURSS
);
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
);
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;
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
);
805 case Qt::ToolTipRole
: {
806 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
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()) {
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 :(
822 if(mPidToWindowInfo.contains(process->pid)) {
824 wid = mPidToWindowInfo[process->pid].wid;
825 icon = KWindowSystem::icon(wid);
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
);
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
);
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
;
858 case HeadingCommand
: {
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
;
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() );
878 case KSysGuard::Process::RoundRobin
:
879 case KSysGuard::Process::Fifo
:
880 tooltip
+= i18n("Scheduler priority: %1", process
->niceLevel
);
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
)
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
)
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)
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)
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
;
933 case HeadingVmSize
: {
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
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
);
943 tooltip
+= i18n("Memory usage: %1<br />", KGlobal::locale()->formatByteSize(process
->vmURSS
* 1024));
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
);
948 tooltip
+= i18n("RSS Memory usage: %1", KGlobal::locale()->formatByteSize(process
->vmRSS
* 1024));
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>");
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
);
960 tooltip
+= i18n("Shared library memory usage: %1", KGlobal::locale()->formatByteSize((process
->vmRSS
- process
->vmURSS
) * 1024));
964 case HeadingXTitle
: {
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
];
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
);
987 return QVariant(QVariant::String
);
990 case Qt::TextAlignmentRole
:
991 switch(index
.column() ) {
993 case HeadingCPUUsage
:
994 return QVariant(Qt::AlignCenter
);
997 case HeadingSharedMemory
:
999 return QVariant(Qt::AlignRight
);
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());
1011 switch(index
.column()) {
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
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
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 ;
1036 if(d
->mSimple
|| !d
->mShowChildTotals
)
1037 cpu
= process
->userUsage
+ process
->sysUsage
;
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.
1045 return (double)(base
- (cpu
*100) -(hasWindow
?50:0) - memory
*100.0/d
->mMemTotal
);
1047 return (double)(base
- (cpu
*100) -(hasWindow
?50:0));
1049 case HeadingCPUUsage
: {
1051 if(d
->mSimple
|| !d
->mShowChildTotals
)
1052 cpu
= process
->userUsage
+ process
->sysUsage
;
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
1060 if(process
->vmURSS
== -1)
1061 return (long long)-process
->vmRSS
;
1063 return (long long)-process
->vmURSS
;
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
);
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()) {
1077 return process
->name
;
1079 return (qlonglong
)process
->pid
;
1081 if(!process
->login
.isEmpty()) return process
->login
;
1082 if(process
->uid
== process
->euid
)
1083 return d
->getUsernameForUser(process
->uid
, false);
1085 return d
->getUsernameForUser(process
->uid
, false) + ", " + d
->getUsernameForUser(process
->euid
, false);
1086 case HeadingNiceness
:
1087 return process
->niceLevel
;
1089 return process
->tty
;
1090 case HeadingCPUUsage
:
1093 if(d
->mShowChildTotals
&& !d
->mSimple
) total
= process
->totalUserUsage
+ process
->totalSysUsage
;
1094 else total
= process
->userUsage
+ process
->sysUsage
;
1095 return total
/ d
->mNumProcessorCores
;
1098 if(process
->vmRSS
== 0) return QVariant(QVariant::String
);
1099 if(process
->vmURSS
== -1) {
1100 return (long long)process
->vmRSS
;
1102 return (long long)process
->vmURSS
;
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
;
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
);
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
);
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
1156 WindowInfo w
= d
->mPidToWindowInfo
.value(process
->pid
);
1158 return QIcon(d
->mBlankPixmap
);
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(),
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
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) {
1192 font
.setItalic(true);
1198 default: //This is a very very common case, so the route to this must be very minimal
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");
1230 headings
<< i18nc("process heading", "Window Title");
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
;
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()
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
;
1265 foreach( KSysGuard::Process
*process
, d
->mProcesses
->getAllProcesses()) {
1266 if(process
->numChildren
> 0) {
1269 row
= process
->index
;
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
)
1286 ProcessModel::Units
ProcessModel::units() const
1288 return (Units
) d
->mUnits
;
1291 QString
ProcessModel::formatMemoryInfo(long amountInKB
) const
1295 return i18n("%1 K", amountInKB
);
1297 return i18n("%1 M", (amountInKB
+512)/1024); //Round to nearest megabyte
1299 return i18n("%1 G", (amountInKB
+512*1024)/(1024*1024)); //Round to nearest gigabyte
1304 QString
ProcessModel::hostName() const {
1305 return d
->mHostName
;
1307 QStringList
ProcessModel::mimeTypes() const
1310 types
<< "text/plain";
1311 types
<< "text/csv";
1312 types
<< "text/html";
1315 QMimeData
*ProcessModel::mimeData(const QModelIndexList
&indexes
) const
1317 QMimeData
*mimeData
= new QMimeData();
1319 QString textCsvHeaders
;
1321 QString textPlainHeaders
;
1323 QString textHtmlHeaders
;
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())
1336 textHtml
+= "</tr><tr>";
1339 for(int i
= 0; i
< d
->mHeadings
.size(); i
++) {
1341 QString heading
= d
->mHeadings
[i
];
1342 textHtmlHeaders
+= "<th>" + heading
+ "</th>";
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();
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());
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
;
1380 return defaultFlags
;