|
|
|
/*
|
|
|
|
KSysGuard, the KDE System Guard
|
|
|
|
|
|
|
|
Copyright (C) 1997 Bernd Johannes Wuebben
|
|
|
|
<wuebben@math.cornell.edu>
|
|
|
|
|
|
|
|
Copyright (C) 1998 Nicolas Leclercq <nicknet@planete.net>
|
|
|
|
|
|
|
|
Copyright (c) 1999, 2000, 2001, 2002 Chris Schlaeger <cs@kde.org>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of version 2 of the GNU General Public
|
|
|
|
License as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
|
|
|
KSysGuard is currently maintained by Chris Schlaeger <cs@kde.org>.
|
|
|
|
Please do not commit any changes without consulting me first. Thanks!
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <config.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <tqbitmap.h>
|
|
|
|
#include <tqheader.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqpopupmenu.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kglobal.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
|
|
|
|
#include "ProcessController.h"
|
|
|
|
#include "ProcessList.h"
|
|
|
|
#include "ReniceDlg.h"
|
|
|
|
#include "SignalIDs.h"
|
|
|
|
|
|
|
|
#define NONE -1
|
|
|
|
#define INIT_PID 1
|
|
|
|
|
|
|
|
//extern const char* intKey(const char* text);
|
|
|
|
//extern const char* timeKey(const char* text);
|
|
|
|
//extern const char* floatKey(const char* text);
|
|
|
|
|
|
|
|
TQDict<TQString> ProcessList::aliases;
|
|
|
|
|
|
|
|
int ProcessLVI::compare( TQListViewItem *item, int col, bool ascending ) const
|
|
|
|
{
|
|
|
|
int type = ((ProcessList*)listView())->columnType( col );
|
|
|
|
|
|
|
|
if ( type == ProcessList::Int ) {
|
|
|
|
int prev = (int)KGlobal::locale()->readNumber( key( col, ascending ) );
|
|
|
|
int next = (int)KGlobal::locale()->readNumber( item->key( col, ascending ) );
|
|
|
|
if ( prev < next )
|
|
|
|
return -1;
|
|
|
|
else if ( prev == next )
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type == ProcessList::Float ) {
|
|
|
|
double prev = KGlobal::locale()->readNumber( key( col, ascending ) );
|
|
|
|
double next = KGlobal::locale()->readNumber( item->key( col, ascending ) );
|
|
|
|
if ( prev < next )
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type == ProcessList::Time ) {
|
|
|
|
int hourPrev, hourNext, minutesPrev, minutesNext;
|
|
|
|
sscanf( key( col, ascending ).latin1(), "%d:%d", &hourPrev, &minutesPrev );
|
|
|
|
sscanf( item->key( col, ascending ).latin1(), "%d:%d", &hourNext, &minutesNext );
|
|
|
|
int prev = hourPrev * 60 + minutesPrev;
|
|
|
|
int next = hourNext * 60 + minutesNext;
|
|
|
|
if ( prev < next )
|
|
|
|
return -1;
|
|
|
|
else if ( prev == next )
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return key( col, ascending ).localeAwareCompare( item->key( col, ascending ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
ProcessList::ProcessList(TQWidget *parent, const char* name)
|
|
|
|
: KListView(parent, name)
|
|
|
|
{
|
|
|
|
iconCache.setAutoDelete(true);
|
|
|
|
|
|
|
|
columnDict.setAutoDelete(true);
|
|
|
|
columnDict.insert("running",
|
|
|
|
new TQString(i18n("process status", "running")));
|
|
|
|
columnDict.insert("sleeping",
|
|
|
|
new TQString(i18n("process status", "sleeping")));
|
|
|
|
columnDict.insert("disk sleep",
|
|
|
|
new TQString(i18n("process status", "disk sleep")));
|
|
|
|
columnDict.insert("zombie", new TQString(i18n("process status", "zombie")));
|
|
|
|
columnDict.insert("stopped",
|
|
|
|
new TQString(i18n("process status", "stopped")));
|
|
|
|
columnDict.insert("paging", new TQString(i18n("process status", "paging")));
|
|
|
|
columnDict.insert("idle", new TQString(i18n("process status", "idle")));
|
|
|
|
|
|
|
|
if (aliases.isEmpty())
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
aliases.insert("init", new TQString("penguin"));
|
|
|
|
#else
|
|
|
|
aliases.insert("init", new TQString("system"));
|
|
|
|
#endif
|
|
|
|
/* kernel stuff */
|
|
|
|
aliases.insert("bdflush", new TQString("kernel"));
|
|
|
|
aliases.insert("dhcpcd", new TQString("kernel"));
|
|
|
|
aliases.insert("kapm-idled", new TQString("kernel"));
|
|
|
|
aliases.insert("keventd", new TQString("kernel"));
|
|
|
|
aliases.insert("khubd", new TQString("kernel"));
|
|
|
|
aliases.insert("klogd", new TQString("kernel"));
|
|
|
|
aliases.insert("kreclaimd", new TQString("kernel"));
|
|
|
|
aliases.insert("kreiserfsd", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU0", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU1", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU2", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU3", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU4", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU5", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU6", new TQString("kernel"));
|
|
|
|
aliases.insert("ksoftirqd_CPU7", new TQString("kernel"));
|
|
|
|
aliases.insert("kswapd", new TQString("kernel"));
|
|
|
|
aliases.insert("kupdated", new TQString("kernel"));
|
|
|
|
aliases.insert("mdrecoveryd", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_0", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_1", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_2", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_3", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_4", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_5", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_6", new TQString("kernel"));
|
|
|
|
aliases.insert("scsi_eh_7", new TQString("kernel"));
|
|
|
|
/* daemon and other service providers */
|
|
|
|
aliases.insert("artsd", new TQString("daemon"));
|
|
|
|
aliases.insert("atd", new TQString("daemon"));
|
|
|
|
aliases.insert("automount", new TQString("daemon"));
|
|
|
|
aliases.insert("cardmgr", new TQString("daemon"));
|
|
|
|
aliases.insert("cron", new TQString("daemon"));
|
|
|
|
aliases.insert("cupsd", new TQString("daemon"));
|
|
|
|
aliases.insert("in.identd", new TQString("daemon"));
|
|
|
|
aliases.insert("lpd", new TQString("daemon"));
|
|
|
|
aliases.insert("mingetty", new TQString("daemon"));
|
|
|
|
aliases.insert("nscd", new TQString("daemon"));
|
|
|
|
aliases.insert("portmap", new TQString("daemon"));
|
|
|
|
aliases.insert("rpc.statd", new TQString("daemon"));
|
|
|
|
aliases.insert("rpciod", new TQString("daemon"));
|
|
|
|
aliases.insert("sendmail", new TQString("daemon"));
|
|
|
|
aliases.insert("sshd", new TQString("daemon"));
|
|
|
|
aliases.insert("syslogd", new TQString("daemon"));
|
|
|
|
aliases.insert("usbmgr", new TQString("daemon"));
|
|
|
|
aliases.insert("wwwoffled", new TQString("daemon"));
|
|
|
|
aliases.insert("xntpd", new TQString("daemon"));
|
|
|
|
aliases.insert("ypbind", new TQString("daemon"));
|
|
|
|
/* kde applications */
|
|
|
|
aliases.insert("appletproxy", new TQString("kdeapp"));
|
|
|
|
aliases.insert("dcopserver", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kcookiejar", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kde", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kded", new TQString("kdeapp"));
|
|
|
|
aliases.insert("tdeinit", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kdesktop", new TQString("kdeapp"));
|
|
|
|
aliases.insert("tdesud", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kdm", new TQString("kdeapp"));
|
|
|
|
aliases.insert("khotkeys", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kio_file", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kio_uiserver", new TQString("kdeapp"));
|
|
|
|
aliases.insert("klauncher", new TQString("kdeapp"));
|
|
|
|
aliases.insert("ksmserver", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kwrapper", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kwrited", new TQString("kdeapp"));
|
|
|
|
aliases.insert("kxmlrpcd", new TQString("kdeapp"));
|
|
|
|
aliases.insert("starttde", new TQString("kdeapp"));
|
|
|
|
/* other processes */
|
|
|
|
aliases.insert("bash", new TQString("shell"));
|
|
|
|
aliases.insert("cat", new TQString("tools"));
|
|
|
|
aliases.insert("egrep", new TQString("tools"));
|
|
|
|
aliases.insert("emacs", new TQString("wordprocessing"));
|
|
|
|
aliases.insert("fgrep", new TQString("tools"));
|
|
|
|
aliases.insert("find", new TQString("tools"));
|
|
|
|
aliases.insert("grep", new TQString("tools"));
|
|
|
|
aliases.insert("ksh", new TQString("shell"));
|
|
|
|
aliases.insert("screen", new TQString("openterm"));
|
|
|
|
aliases.insert("sh", new TQString("shell"));
|
|
|
|
aliases.insert("sort", new TQString("tools"));
|
|
|
|
aliases.insert("ssh", new TQString("shell"));
|
|
|
|
aliases.insert("su", new TQString("tools"));
|
|
|
|
aliases.insert("tcsh", new TQString("shell"));
|
|
|
|
aliases.insert("tee", new TQString("tools"));
|
|
|
|
aliases.insert("vi", new TQString("wordprocessing"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The filter mode is controlled by a combo box of the parent. If
|
|
|
|
* the mode is changed we get a signal. */
|
|
|
|
connect(parent, TQT_SIGNAL(setFilterMode(int)),
|
|
|
|
this, TQT_SLOT(setFilterMode(int)));
|
|
|
|
|
|
|
|
/* We need to catch this signal to show various popup menues. */
|
|
|
|
connect(this,
|
|
|
|
TQT_SIGNAL(rightButtonPressed(TQListViewItem*, const TQPoint&, int)),
|
|
|
|
this,
|
|
|
|
TQT_SLOT(handleRMBPressed(TQListViewItem*, const TQPoint&, int)));
|
|
|
|
|
|
|
|
/* Since Qt does not tell us the sorting details we have to do our
|
|
|
|
* own bookkeping, so we can save and restore the sorting
|
|
|
|
* settings. */
|
|
|
|
connect(header(), TQT_SIGNAL(clicked(int)), this, TQT_SLOT(sortingChanged(int)));
|
|
|
|
|
|
|
|
treeViewEnabled = false;
|
|
|
|
openAll = true;
|
|
|
|
|
|
|
|
filterMode = FILTER_ALL;
|
|
|
|
|
|
|
|
sortColumn = 1;
|
|
|
|
increasing = false;
|
|
|
|
|
|
|
|
// Elements in the process list may only live in this list.
|
|
|
|
pl.setAutoDelete(true);
|
|
|
|
|
|
|
|
setItemMargin(2);
|
|
|
|
setAllColumnsShowFocus(true);
|
|
|
|
setTreeStepSize(17);
|
|
|
|
setSorting(sortColumn, increasing);
|
|
|
|
setSelectionMode(TQListView::Extended);
|
|
|
|
|
|
|
|
// Create popup menu for RMB clicks on table header
|
|
|
|
headerPM = new TQPopupMenu();
|
|
|
|
headerPM->insertItem(i18n("Remove Column"), HEADER_REMOVE);
|
|
|
|
headerPM->insertItem(i18n("Add Column"), HEADER_ADD);
|
|
|
|
headerPM->insertItem(i18n("Help on Column"), HEADER_HELP);
|
|
|
|
|
|
|
|
connect(header(), TQT_SIGNAL(sizeChange(int, int, int)),
|
|
|
|
this, TQT_SLOT(sizeChanged(int, int, int)));
|
|
|
|
connect(header(), TQT_SIGNAL(indexChange(int, int, int)),
|
|
|
|
this, TQT_SLOT(indexChanged(int, int, int)));
|
|
|
|
|
|
|
|
killSupported = false;
|
|
|
|
setModified(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ProcessList::~ProcessList()
|
|
|
|
{
|
|
|
|
delete(headerPM);
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQValueList<int>&
|
|
|
|
ProcessList::getSelectedPIds()
|
|
|
|
{
|
|
|
|
selectedPIds.clear();
|
|
|
|
// iterate through all selected visible items of the listview
|
|
|
|
TQListViewItemIterator it(this, TQListViewItemIterator::Visible | TQListViewItemIterator::Selected );
|
|
|
|
for ( ; it.current(); ++it )
|
|
|
|
selectedPIds.append(it.current()->text(1).toInt());
|
|
|
|
|
|
|
|
return (selectedPIds);
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQStringList&
|
|
|
|
ProcessList::getSelectedAsStrings()
|
|
|
|
{
|
|
|
|
selectedAsStrings.clear();
|
|
|
|
// iterate through all selected visible items of the listview
|
|
|
|
TQListViewItemIterator it(this, TQListViewItemIterator::Visible | TQListViewItemIterator::Selected );
|
|
|
|
TQString spaces;
|
|
|
|
for ( ; it.current(); ++it ) {
|
|
|
|
spaces.fill(TQChar(' '), 7 - it.current()->text(1).length());
|
|
|
|
selectedAsStrings.append("(PID: " + it.current()->text(1) + ")" + spaces + " " + it.current()->text(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (selectedAsStrings);
|
|
|
|
}
|
|
|
|
bool
|
|
|
|
ProcessList::update(const TQString& list)
|
|
|
|
{
|
|
|
|
/* Disable painting to avoid flickering effects,
|
|
|
|
* especially when in tree view mode.
|
|
|
|
* Ditto for the scrollbar. */
|
|
|
|
tqsetUpdatesEnabled(false);
|
|
|
|
viewport()->tqsetUpdatesEnabled(false);
|
|
|
|
|
|
|
|
pl.clear();
|
|
|
|
|
|
|
|
// Convert ps answer in a list of tokenized lines
|
|
|
|
KSGRD::SensorTokenizer procs(list, '\n');
|
|
|
|
for (unsigned int i = 0; i < procs.count(); i++)
|
|
|
|
{
|
|
|
|
KSGRD::SensorPSLine* line = new KSGRD::SensorPSLine(procs[i]);
|
|
|
|
if (line->count() != (uint) columns())
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
// This is needed for debugging only.
|
|
|
|
kdDebug(1215) << list << endl;
|
|
|
|
TQString l;
|
|
|
|
for (uint j = 0; j < line->count(); j++)
|
|
|
|
l += (*line)[j] + "|";
|
|
|
|
kdDebug(1215) << "Incomplete ps line:" << l << endl;
|
|
|
|
#endif
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pl.append(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
int currItemPos = itemPos(currentItem());
|
|
|
|
int vpos = verticalScrollBar()->value();
|
|
|
|
int hpos = horizontalScrollBar()->value();
|
|
|
|
|
|
|
|
updateMetaInfo();
|
|
|
|
|
|
|
|
clear();
|
|
|
|
|
|
|
|
if (treeViewEnabled)
|
|
|
|
buildTree();
|
|
|
|
else
|
|
|
|
buildList();
|
|
|
|
|
|
|
|
TQListViewItemIterator it( this );
|
|
|
|
while ( it.current() ) {
|
|
|
|
if ( itemPos( it.current() ) == currItemPos ) {
|
|
|
|
setCurrentItem( it.current() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
verticalScrollBar()->setValue(vpos);
|
|
|
|
horizontalScrollBar()->setValue(hpos);
|
|
|
|
|
|
|
|
// Re-enable painting, and force an update.
|
|
|
|
tqsetUpdatesEnabled(true);
|
|
|
|
viewport()->tqsetUpdatesEnabled(true);
|
|
|
|
|
|
|
|
triggerUpdate();
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::setTreeView(bool tv)
|
|
|
|
{
|
|
|
|
if (treeViewEnabled = tv)
|
|
|
|
{
|
|
|
|
savedWidth[0] = columnWidth(0);
|
|
|
|
openAll = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* In tree view the first column is wider than in list view mode.
|
|
|
|
* So we shrink it to 1 pixel. The next update will resize it again
|
|
|
|
* appropriately. */
|
|
|
|
setColumnWidth(0, savedWidth[0]);
|
|
|
|
}
|
|
|
|
/* In tree view mode borders are added to the icons. So we have to clear
|
|
|
|
* the cache when we change the tree view mode. */
|
|
|
|
iconCache.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ProcessList::load(TQDomElement& el)
|
|
|
|
{
|
|
|
|
TQDomNodeList dnList = el.elementsByTagName("column");
|
|
|
|
for (uint i = 0; i < dnList.count(); ++i)
|
|
|
|
{
|
|
|
|
TQDomElement lel = dnList.item(i).toElement();
|
|
|
|
if (savedWidth.count() <= i)
|
|
|
|
savedWidth.append(lel.attribute("savedWidth").toInt());
|
|
|
|
else
|
|
|
|
savedWidth[i] = lel.attribute("savedWidth").toInt();
|
|
|
|
if (currentWidth.count() <= i)
|
|
|
|
currentWidth.append(lel.attribute("currentWidth").toInt());
|
|
|
|
else
|
|
|
|
currentWidth[i] = lel.attribute("currentWidth").toInt();
|
|
|
|
if (index.count() <= i)
|
|
|
|
index.append(lel.attribute("index").toInt());
|
|
|
|
else
|
|
|
|
index[i] = lel.attribute("index").toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
setModified(false);
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ProcessList::save(TQDomDocument& doc, TQDomElement& display)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < columns(); ++i)
|
|
|
|
{
|
|
|
|
TQDomElement col = doc.createElement("column");
|
|
|
|
display.appendChild(col);
|
|
|
|
col.setAttribute("currentWidth", columnWidth(i));
|
|
|
|
col.setAttribute("savedWidth", savedWidth[i]);
|
|
|
|
col.setAttribute("index", header()->mapToIndex(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
setModified(false);
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::sortingChanged(int col)
|
|
|
|
{
|
|
|
|
if (col == sortColumn)
|
|
|
|
increasing = !increasing;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sortColumn = col;
|
|
|
|
increasing = true;
|
|
|
|
}
|
|
|
|
setSorting(sortColumn, increasing);
|
|
|
|
setModified(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ProcessList::columnType( uint pos ) const
|
|
|
|
{
|
|
|
|
if ( pos >= mColumnTypes.count() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ( mColumnTypes[ pos ] == "d" || mColumnTypes[ pos ] == "D" )
|
|
|
|
return Int;
|
|
|
|
else if ( mColumnTypes[ pos ] == "f" || mColumnTypes[ pos ] == "F" )
|
|
|
|
return Float;
|
|
|
|
else if ( mColumnTypes[ pos ] == "t" )
|
|
|
|
return Time;
|
|
|
|
else
|
|
|
|
return Text;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ProcessList::matchesFilter(KSGRD::SensorPSLine* p) const
|
|
|
|
{
|
|
|
|
// This mechanism is likely to change in the future!
|
|
|
|
|
|
|
|
switch (filterMode)
|
|
|
|
{
|
|
|
|
case FILTER_ALL:
|
|
|
|
return (true);
|
|
|
|
|
|
|
|
case FILTER_SYSTEM:
|
|
|
|
return (p->uid() < 100 ? true : false);
|
|
|
|
|
|
|
|
case FILTER_USER:
|
|
|
|
return (p->uid() >= 100 ? true : false);
|
|
|
|
|
|
|
|
case FILTER_OWN:
|
|
|
|
default:
|
|
|
|
return (p->uid() == (long) getuid() ? true : false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::buildList()
|
|
|
|
{
|
|
|
|
/* Get the first process in the list, check whether it matches the
|
|
|
|
* filter and append it to TQListView widget if so. */
|
|
|
|
while (!pl.isEmpty())
|
|
|
|
{
|
|
|
|
KSGRD::SensorPSLine* p = pl.first();
|
|
|
|
|
|
|
|
if (matchesFilter(p))
|
|
|
|
{
|
|
|
|
ProcessLVI* pli = new ProcessLVI(this);
|
|
|
|
|
|
|
|
addProcess(p, pli);
|
|
|
|
|
|
|
|
if (selectedPIds.findIndex(p->pid()) != -1)
|
|
|
|
pli->setSelected(true);
|
|
|
|
}
|
|
|
|
pl.removeFirst();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::buildTree()
|
|
|
|
{
|
|
|
|
// remove all leaves that do not match the filter
|
|
|
|
deleteLeaves();
|
|
|
|
|
|
|
|
KSGRD::SensorPSLine* ps = pl.first();
|
|
|
|
|
|
|
|
while (ps)
|
|
|
|
{
|
|
|
|
if (ps->pid() == INIT_PID)
|
|
|
|
{
|
|
|
|
// insert root item into the tree widget
|
|
|
|
ProcessLVI* pli = new ProcessLVI(this);
|
|
|
|
addProcess(ps, pli);
|
|
|
|
|
|
|
|
// remove the process from the process list, ps is now invalid
|
|
|
|
int pid = ps->pid();
|
|
|
|
pl.remove();
|
|
|
|
|
|
|
|
if (selectedPIds.findIndex(pid) != -1)
|
|
|
|
pli->setSelected(true);
|
|
|
|
|
|
|
|
// insert all child processes of current process
|
|
|
|
extendTree(&pl, pli, pid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ps = pl.next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::deleteLeaves(void)
|
|
|
|
{
|
|
|
|
for ( ; ; )
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < pl.count() &&
|
|
|
|
(!isLeafProcess(pl.tqat(i)->pid()) ||
|
|
|
|
matchesFilter(pl.tqat(i))); i++)
|
|
|
|
;
|
|
|
|
if (i == pl.count())
|
|
|
|
return;
|
|
|
|
|
|
|
|
pl.remove(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ProcessList::isLeafProcess(int pid)
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < pl.count(); i++)
|
|
|
|
if (pl.tqat(i)->ppid() == pid)
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::extendTree(TQPtrList<KSGRD::SensorPSLine>* pl, ProcessLVI* parent, int ppid)
|
|
|
|
{
|
|
|
|
KSGRD::SensorPSLine* ps;
|
|
|
|
|
|
|
|
// start at top list
|
|
|
|
ps = pl->first();
|
|
|
|
|
|
|
|
while (ps)
|
|
|
|
{
|
|
|
|
// look for a child process of the current parent
|
|
|
|
if (ps->ppid() == ppid)
|
|
|
|
{
|
|
|
|
ProcessLVI* pli = new ProcessLVI(parent);
|
|
|
|
|
|
|
|
addProcess(ps, pli);
|
|
|
|
|
|
|
|
if (selectedPIds.findIndex(ps->pid()) != -1)
|
|
|
|
pli->setSelected(true);
|
|
|
|
|
|
|
|
if (ps->ppid() != INIT_PID && closedSubTrees.findIndex(ps->ppid()) != -1)
|
|
|
|
parent->setOpen(false);
|
|
|
|
else
|
|
|
|
parent->setOpen(true);
|
|
|
|
|
|
|
|
// remove the process from the process list, ps is now invalid
|
|
|
|
int pid = ps->pid();
|
|
|
|
pl->remove();
|
|
|
|
|
|
|
|
// now look for the childs of the inserted process
|
|
|
|
extendTree(pl, pli, pid);
|
|
|
|
|
|
|
|
/* Since buildTree can remove processes from the list we
|
|
|
|
* can't find a "current" process. So we start searching
|
|
|
|
* at the top again. It's no endless loops since this
|
|
|
|
* branch is only entered when there are children of the
|
|
|
|
* current parent in the list. When we have removed them
|
|
|
|
* all the while loop will exit. */
|
|
|
|
ps = pl->first();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ps = pl->next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
|
|
ProcessList::addProcess(KSGRD::SensorPSLine* p, ProcessLVI* pli)
|
|
|
|
{
|
|
|
|
TQString name = p->name();
|
|
|
|
if (aliases[name])
|
|
|
|
name = *aliases[name];
|
|
|
|
|
|
|
|
/* Get icon from icon list that might be appropriate for a process
|
|
|
|
* with this name. */
|
|
|
|
TQPixmap pix;
|
|
|
|
if (!iconCache[name])
|
|
|
|
{
|
|
|
|
pix = KGlobal::iconLoader()->loadIcon(name, KIcon::Small,
|
|
|
|
KIcon::SizeSmall, KIcon::DefaultState,
|
|
|
|
0L, true);
|
|
|
|
if (pix.isNull() || !pix.mask())
|
|
|
|
pix = KGlobal::iconLoader()->loadIcon("unknownapp", KIcon::User,
|
|
|
|
KIcon::SizeSmall);
|
|
|
|
|
|
|
|
if (pix.width() != 16 || pix.height() != 16)
|
|
|
|
{
|
|
|
|
/* I guess this isn't needed too often. The KIconLoader should
|
|
|
|
* scale the pixmaps already appropriately. Since I got a bug
|
|
|
|
* report claiming that it doesn't work with GNOME apps I've
|
|
|
|
* added this safeguard. */
|
|
|
|
TQImage img;
|
|
|
|
img = pix;
|
|
|
|
img.smoothScale(16, 16);
|
|
|
|
pix = img;
|
|
|
|
}
|
|
|
|
/* We copy the icon into a 24x16 pixmap to add a 4 pixel margin on
|
|
|
|
* the left and right side. In tree view mode we use the original
|
|
|
|
* icon. */
|
|
|
|
TQPixmap icon(24, 16, pix.depth());
|
|
|
|
if (!treeViewEnabled)
|
|
|
|
{
|
|
|
|
icon.fill();
|
|
|
|
bitBlt(&icon, 4, 0, &pix, 0, 0, pix.width(), pix.height());
|
|
|
|
TQBitmap mask(24, 16, true);
|
|
|
|
bitBlt(&mask, 4, 0, pix.mask(), 0, 0, pix.width(), pix.height());
|
|
|
|
icon.setMask(mask);
|
|
|
|
pix = icon;
|
|
|
|
}
|
|
|
|
iconCache.insert(name, new TQPixmap(pix));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pix = *(iconCache[name]);
|
|
|
|
|
|
|
|
// icon + process name
|
|
|
|
pli->setPixmap(0, pix);
|
|
|
|
pli->setText(0, p->name());
|
|
|
|
|
|
|
|
// insert remaining field into table
|
|
|
|
for (unsigned int col = 1; col < p->count(); col++)
|
|
|
|
{
|
|
|
|
if (mColumnTypes[col] == "S" && columnDict[(*p)[col]])
|
|
|
|
pli->setText(col, *columnDict[(*p)[col]]);
|
|
|
|
else if ( mColumnTypes[col] == "f" )
|
|
|
|
pli->setText( col, KGlobal::locale()->formatNumber( (*p)[col].toFloat() ) );
|
|
|
|
else if ( mColumnTypes[col] == "D" )
|
|
|
|
pli->setText( col, KGlobal::locale()->formatNumber( (*p)[col].toInt(), 0 ) );
|
|
|
|
else
|
|
|
|
pli->setText(col, (*p)[col]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::updateMetaInfo(void)
|
|
|
|
{
|
|
|
|
selectedPIds.clear();
|
|
|
|
closedSubTrees.clear();
|
|
|
|
|
|
|
|
TQListViewItemIterator it(this);
|
|
|
|
|
|
|
|
// iterate through all items of the listview
|
|
|
|
for ( ; it.current(); ++it )
|
|
|
|
{
|
|
|
|
if (it.current()->isSelected() && it.current()->isVisible())
|
|
|
|
selectedPIds.append(it.current()->text(1).toInt());
|
|
|
|
if (treeViewEnabled && !it.current()->isOpen())
|
|
|
|
closedSubTrees.append(it.current()->text(1).toInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In list view mode all list items are set to closed by TQListView.
|
|
|
|
* If the tree view is now selected, all item will be closed. This is
|
|
|
|
* annoying. So we use the openAll flag to force all trees to open when
|
|
|
|
* the treeViewEnbled flag was set to true. */
|
|
|
|
if (openAll)
|
|
|
|
{
|
|
|
|
if (treeViewEnabled)
|
|
|
|
closedSubTrees.clear();
|
|
|
|
openAll = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::removeColumns(void)
|
|
|
|
{
|
|
|
|
for (int i = columns() - 1; i >= 0; --i)
|
|
|
|
removeColumn(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::addColumn(const TQString& label, const TQString& type)
|
|
|
|
{
|
|
|
|
TQListView::addColumn(label);
|
|
|
|
uint col = columns() - 1;
|
|
|
|
if (type == "s" || type == "S")
|
|
|
|
setColumnAlignment(col, AlignLeft);
|
|
|
|
else if (type == "d" || type == "D")
|
|
|
|
setColumnAlignment(col, AlignRight);
|
|
|
|
else if (type == "t")
|
|
|
|
setColumnAlignment(col, AlignRight);
|
|
|
|
else if (type == "f")
|
|
|
|
setColumnAlignment(col, AlignRight);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
kdDebug(1215) << "Unknown type " << type << " of column " << label
|
|
|
|
<< " in ProcessList!" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mColumnTypes.append(type);
|
|
|
|
|
|
|
|
/* Just use some sensible default values as initial setting. */
|
|
|
|
TQFontMetrics fm = fontMetrics();
|
|
|
|
setColumnWidth(col, fm.width(label) + 10);
|
|
|
|
|
|
|
|
if (currentWidth.count() - 1 == col)
|
|
|
|
{
|
|
|
|
/* Table has been loaded from file. We can restore the settings
|
|
|
|
* when the last column has been added. */
|
|
|
|
for (uint i = 0; i < col; ++i)
|
|
|
|
{
|
|
|
|
/* In case the language has been changed the column width
|
|
|
|
* might need to be increased. */
|
|
|
|
if (currentWidth[i] == 0)
|
|
|
|
{
|
|
|
|
if (fm.width(header()->label(i)) + 10 > savedWidth[i])
|
|
|
|
savedWidth[i] = fm.width(header()->label(i)) + 10;
|
|
|
|
setColumnWidth(i, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fm.width(header()->label(i)) + 10 > currentWidth[i])
|
|
|
|
setColumnWidth(i, fm.width(header()->label(i)) + 10);
|
|
|
|
else
|
|
|
|
setColumnWidth(i, currentWidth[i]);
|
|
|
|
}
|
|
|
|
setColumnWidthMode(i, currentWidth[i] == 0 ?
|
|
|
|
TQListView::Manual : TQListView::Maximum);
|
|
|
|
header()->moveSection(i, index[i]);
|
|
|
|
}
|
|
|
|
setSorting(sortColumn, increasing);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::handleRMBPressed(TQListViewItem* lvi, const TQPoint& p, int col)
|
|
|
|
{
|
|
|
|
if (!lvi)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lvi->setSelected( true );
|
|
|
|
|
|
|
|
/* lvi is only valid until the next time we hit the main event
|
|
|
|
* loop. So we need to save the information we need after calling
|
|
|
|
* processPM->exec(). */
|
|
|
|
int currentPId = lvi->text(1).toInt();
|
|
|
|
|
|
|
|
int currentNiceValue = 0;
|
|
|
|
for (int i = 0; i < columns(); ++i)
|
|
|
|
if (TQString::compare(header()->label(i), i18n("Nice")) == 0)
|
|
|
|
currentNiceValue = lvi->text(i).toInt();
|
|
|
|
|
|
|
|
TQPopupMenu processPM;
|
|
|
|
if (columnWidth(col) != 0)
|
|
|
|
processPM.insertItem(i18n("Hide Column"), 5);
|
|
|
|
TQPopupMenu* hiddenPM = new TQPopupMenu(&processPM);
|
|
|
|
for (int i = 0; i < columns(); ++i)
|
|
|
|
if (columnWidth(i) == 0)
|
|
|
|
hiddenPM->insertItem(header()->label(i), i + 100);
|
|
|
|
if(columns())
|
|
|
|
processPM.insertItem(i18n("Show Column"), hiddenPM);
|
|
|
|
|
|
|
|
processPM.insertSeparator();
|
|
|
|
|
|
|
|
processPM.insertItem(i18n("Select All Processes"), 1);
|
|
|
|
processPM.insertItem(i18n("Unselect All Processes"), 2);
|
|
|
|
|
|
|
|
TQPopupMenu* signalPM = new TQPopupMenu(&processPM);
|
|
|
|
if (killSupported && lvi->isSelected())
|
|
|
|
{
|
|
|
|
processPM.insertSeparator();
|
|
|
|
processPM.insertItem(i18n("Select All Child Processes"), 3);
|
|
|
|
processPM.insertItem(i18n("Unselect All Child Processes"), 4);
|
|
|
|
|
|
|
|
signalPM->insertItem(i18n("SIGABRT"), MENU_ID_SIGABRT);
|
|
|
|
signalPM->insertItem(i18n("SIGALRM"), MENU_ID_SIGALRM);
|
|
|
|
signalPM->insertItem(i18n("SIGCHLD"), MENU_ID_SIGCHLD);
|
|
|
|
signalPM->insertItem(i18n("SIGCONT"), MENU_ID_SIGCONT);
|
|
|
|
signalPM->insertItem(i18n("SIGFPE"), MENU_ID_SIGFPE);
|
|
|
|
signalPM->insertItem(i18n("SIGHUP"), MENU_ID_SIGHUP);
|
|
|
|
signalPM->insertItem(i18n("SIGILL"), MENU_ID_SIGILL);
|
|
|
|
signalPM->insertItem(i18n("SIGINT"), MENU_ID_SIGINT);
|
|
|
|
signalPM->insertItem(i18n("SIGKILL"), MENU_ID_SIGKILL);
|
|
|
|
signalPM->insertItem(i18n("SIGPIPE"), MENU_ID_SIGPIPE);
|
|
|
|
signalPM->insertItem(i18n("SIGQUIT"), MENU_ID_SIGQUIT);
|
|
|
|
signalPM->insertItem(i18n("SIGSEGV"), MENU_ID_SIGSEGV);
|
|
|
|
signalPM->insertItem(i18n("SIGSTOP"), MENU_ID_SIGSTOP);
|
|
|
|
signalPM->insertItem(i18n("SIGTERM"), MENU_ID_SIGTERM);
|
|
|
|
signalPM->insertItem(i18n("SIGTSTP"), MENU_ID_SIGTSTP);
|
|
|
|
signalPM->insertItem(i18n("SIGTTIN"), MENU_ID_SIGTTIN);
|
|
|
|
signalPM->insertItem(i18n("SIGTTOU"), MENU_ID_SIGTTOU);
|
|
|
|
signalPM->insertItem(i18n("SIGUSR1"), MENU_ID_SIGUSR1);
|
|
|
|
signalPM->insertItem(i18n("SIGUSR2"), MENU_ID_SIGUSR2);
|
|
|
|
|
|
|
|
processPM.insertSeparator();
|
|
|
|
processPM.insertItem(i18n("Send Signal"), signalPM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* differ between killSupported and reniceSupported in a future
|
|
|
|
* version. */
|
|
|
|
if (killSupported && lvi->isSelected())
|
|
|
|
{
|
|
|
|
processPM.insertSeparator();
|
|
|
|
processPM.insertItem(i18n("Renice Process..."), 300);
|
|
|
|
}
|
|
|
|
|
|
|
|
int id;
|
|
|
|
switch (id = processPM.exec(p))
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
selectAllItems(id & 1);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
selectAllChilds(currentPId, id & 1);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
setColumnWidthMode(col, TQListView::Manual);
|
|
|
|
savedWidth[col] = columnWidth(col);
|
|
|
|
setColumnWidth(col, 0);
|
|
|
|
setModified(true);
|
|
|
|
break;
|
|
|
|
case 300:
|
|
|
|
{
|
|
|
|
ReniceDlg reniceDlg(this, "reniceDlg", currentNiceValue, currentPId);
|
|
|
|
|
|
|
|
int reniceVal;
|
|
|
|
if ((reniceVal = reniceDlg.exec()) != 40) {
|
|
|
|
emit reniceProcess(selectedPIds, reniceVal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* IDs < 100 are used for signals. */
|
|
|
|
if (id < 100)
|
|
|
|
{
|
|
|
|
/* we go through list to get all task also
|
|
|
|
when update interval is paused */
|
|
|
|
selectedPIds.clear();
|
|
|
|
TQListViewItemIterator it(this, TQListViewItemIterator::Visible | TQListViewItemIterator::Selected);
|
|
|
|
|
|
|
|
// iterate through all selected visible items of the listview
|
|
|
|
for ( ; it.current(); ++it )
|
|
|
|
{
|
|
|
|
selectedPIds.append(it.current()->text(1).toInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString msg = i18n("Do you really want to send signal %1 to the selected process?",
|
|
|
|
"Do you really want to send signal %1 to the %n selected processes?",
|
|
|
|
selectedPIds.count())
|
|
|
|
.arg(signalPM->text(id));
|
|
|
|
int answ;
|
|
|
|
switch(answ = KMessageBox::questionYesNo(this, msg, TQString::null, i18n("Send"), KStdGuiItem::cancel()))
|
|
|
|
{
|
|
|
|
case KMessageBox::Yes:
|
|
|
|
{
|
|
|
|
TQValueList<int>::Iterator it;
|
|
|
|
for (it = selectedPIds.begin(); it != selectedPIds.end(); ++it)
|
|
|
|
emit (killProcess(*it, id));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* IDs >= 100 are used for hidden columns. */
|
|
|
|
int col = id - 100;
|
|
|
|
setColumnWidthMode(col, TQListView::Maximum);
|
|
|
|
setColumnWidth(col, savedWidth[col]);
|
|
|
|
setModified(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::selectAllItems(bool select)
|
|
|
|
{
|
|
|
|
selectedPIds.clear();
|
|
|
|
|
|
|
|
TQListViewItemIterator it(this, TQListViewItemIterator::Visible);
|
|
|
|
|
|
|
|
// iterate through all items of the listview
|
|
|
|
for ( ; it.current(); ++it )
|
|
|
|
{
|
|
|
|
it.current()->setSelected(select);
|
|
|
|
repaintItem(it.current());
|
|
|
|
if (select)
|
|
|
|
selectedPIds.append(it.current()->text(1).toInt());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessList::selectAllChilds(int pid, bool select)
|
|
|
|
{
|
|
|
|
TQListViewItemIterator it(this, TQListViewItemIterator::Visible );
|
|
|
|
|
|
|
|
// iterate through all items of the listview
|
|
|
|
for ( ; it.current(); ++it )
|
|
|
|
{
|
|
|
|
// Check if PPID matches the pid (current is a child of pid)
|
|
|
|
if (it.current()->text(2).toInt() == pid)
|
|
|
|
{
|
|
|
|
int currPId = it.current()->text(1).toInt();
|
|
|
|
it.current()->setSelected(select);
|
|
|
|
repaintItem(it.current());
|
|
|
|
if (select)
|
|
|
|
selectedPIds.append(currPId);
|
|
|
|
else
|
|
|
|
selectedPIds.remove(currPId);
|
|
|
|
selectAllChilds(currPId, select);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "ProcessList.moc"
|