summaryrefslogtreecommitdiffstats
path: root/ksysguard/gui/SensorDisplayLib/ProcessList.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ksysguard/gui/SensorDisplayLib/ProcessList.cc')
-rw-r--r--ksysguard/gui/SensorDisplayLib/ProcessList.cc941
1 files changed, 941 insertions, 0 deletions
diff --git a/ksysguard/gui/SensorDisplayLib/ProcessList.cc b/ksysguard/gui/SensorDisplayLib/ProcessList.cc
new file mode 100644
index 000000000..6d158c872
--- /dev/null
+++ b/ksysguard/gui/SensorDisplayLib/ProcessList.cc
@@ -0,0 +1,941 @@
+/*
+ KSysGuard, the KDE System Guard
+
+ Copyright (C) 1997 Bernd Johannes Wuebben
+
+ Copyright (C) 1998 Nicolas Leclercq <[email protected]>
+
+ Copyright (c) 1999, 2000, 2001, 2002 Chris Schlaeger <[email protected]>
+
+ 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 <[email protected]>.
+ 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 <qbitmap.h>
+#include <qheader.h>
+#include <qimage.h>
+#include <qpopupmenu.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);
+
+QDict<QString> ProcessList::aliases;
+
+int ProcessLVI::compare( QListViewItem *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(QWidget *parent, const char* name)
+ : KListView(parent, name)
+{
+ iconCache.setAutoDelete(true);
+
+ columnDict.setAutoDelete(true);
+ columnDict.insert("running",
+ new QString(i18n("process status", "running")));
+ columnDict.insert("sleeping",
+ new QString(i18n("process status", "sleeping")));
+ columnDict.insert("disk sleep",
+ new QString(i18n("process status", "disk sleep")));
+ columnDict.insert("zombie", new QString(i18n("process status", "zombie")));
+ columnDict.insert("stopped",
+ new QString(i18n("process status", "stopped")));
+ columnDict.insert("paging", new QString(i18n("process status", "paging")));
+ columnDict.insert("idle", new QString(i18n("process status", "idle")));
+
+ if (aliases.isEmpty())
+ {
+#ifdef Q_OS_LINUX
+ aliases.insert("init", new QString("penguin"));
+#else
+ aliases.insert("init", new QString("system"));
+#endif
+ /* kernel stuff */
+ aliases.insert("bdflush", new QString("kernel"));
+ aliases.insert("dhcpcd", new QString("kernel"));
+ aliases.insert("kapm-idled", new QString("kernel"));
+ aliases.insert("keventd", new QString("kernel"));
+ aliases.insert("khubd", new QString("kernel"));
+ aliases.insert("klogd", new QString("kernel"));
+ aliases.insert("kreclaimd", new QString("kernel"));
+ aliases.insert("kreiserfsd", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU0", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU1", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU2", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU3", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU4", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU5", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU6", new QString("kernel"));
+ aliases.insert("ksoftirqd_CPU7", new QString("kernel"));
+ aliases.insert("kswapd", new QString("kernel"));
+ aliases.insert("kupdated", new QString("kernel"));
+ aliases.insert("mdrecoveryd", new QString("kernel"));
+ aliases.insert("scsi_eh_0", new QString("kernel"));
+ aliases.insert("scsi_eh_1", new QString("kernel"));
+ aliases.insert("scsi_eh_2", new QString("kernel"));
+ aliases.insert("scsi_eh_3", new QString("kernel"));
+ aliases.insert("scsi_eh_4", new QString("kernel"));
+ aliases.insert("scsi_eh_5", new QString("kernel"));
+ aliases.insert("scsi_eh_6", new QString("kernel"));
+ aliases.insert("scsi_eh_7", new QString("kernel"));
+ /* daemon and other service providers */
+ aliases.insert("artsd", new QString("daemon"));
+ aliases.insert("atd", new QString("daemon"));
+ aliases.insert("automount", new QString("daemon"));
+ aliases.insert("cardmgr", new QString("daemon"));
+ aliases.insert("cron", new QString("daemon"));
+ aliases.insert("cupsd", new QString("daemon"));
+ aliases.insert("in.identd", new QString("daemon"));
+ aliases.insert("lpd", new QString("daemon"));
+ aliases.insert("mingetty", new QString("daemon"));
+ aliases.insert("nscd", new QString("daemon"));
+ aliases.insert("portmap", new QString("daemon"));
+ aliases.insert("rpc.statd", new QString("daemon"));
+ aliases.insert("rpciod", new QString("daemon"));
+ aliases.insert("sendmail", new QString("daemon"));
+ aliases.insert("sshd", new QString("daemon"));
+ aliases.insert("syslogd", new QString("daemon"));
+ aliases.insert("usbmgr", new QString("daemon"));
+ aliases.insert("wwwoffled", new QString("daemon"));
+ aliases.insert("xntpd", new QString("daemon"));
+ aliases.insert("ypbind", new QString("daemon"));
+ /* kde applications */
+ aliases.insert("appletproxy", new QString("kdeapp"));
+ aliases.insert("dcopserver", new QString("kdeapp"));
+ aliases.insert("kcookiejar", new QString("kdeapp"));
+ aliases.insert("kde", new QString("kdeapp"));
+ aliases.insert("kded", new QString("kdeapp"));
+ aliases.insert("kdeinit", new QString("kdeapp"));
+ aliases.insert("kdesktop", new QString("kdeapp"));
+ aliases.insert("kdesud", new QString("kdeapp"));
+ aliases.insert("kdm", new QString("kdeapp"));
+ aliases.insert("khotkeys", new QString("kdeapp"));
+ aliases.insert("kio_file", new QString("kdeapp"));
+ aliases.insert("kio_uiserver", new QString("kdeapp"));
+ aliases.insert("klauncher", new QString("kdeapp"));
+ aliases.insert("ksmserver", new QString("kdeapp"));
+ aliases.insert("kwrapper", new QString("kdeapp"));
+ aliases.insert("kwrited", new QString("kdeapp"));
+ aliases.insert("kxmlrpcd", new QString("kdeapp"));
+ aliases.insert("startkde", new QString("kdeapp"));
+ /* other processes */
+ aliases.insert("bash", new QString("shell"));
+ aliases.insert("cat", new QString("tools"));
+ aliases.insert("egrep", new QString("tools"));
+ aliases.insert("emacs", new QString("wordprocessing"));
+ aliases.insert("fgrep", new QString("tools"));
+ aliases.insert("find", new QString("tools"));
+ aliases.insert("grep", new QString("tools"));
+ aliases.insert("ksh", new QString("shell"));
+ aliases.insert("screen", new QString("openterm"));
+ aliases.insert("sh", new QString("shell"));
+ aliases.insert("sort", new QString("tools"));
+ aliases.insert("ssh", new QString("shell"));
+ aliases.insert("su", new QString("tools"));
+ aliases.insert("tcsh", new QString("shell"));
+ aliases.insert("tee", new QString("tools"));
+ aliases.insert("vi", new QString("wordprocessing"));
+ }
+
+ /* The filter mode is controlled by a combo box of the parent. If
+ * the mode is changed we get a signal. */
+ connect(parent, SIGNAL(setFilterMode(int)),
+ this, SLOT(setFilterMode(int)));
+
+ /* We need to catch this signal to show various popup menues. */
+ connect(this,
+ SIGNAL(rightButtonPressed(QListViewItem*, const QPoint&, int)),
+ this,
+ SLOT(handleRMBPressed(QListViewItem*, const QPoint&, 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(), SIGNAL(clicked(int)), this, 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(QListView::Extended);
+
+ // Create popup menu for RMB clicks on table header
+ headerPM = new QPopupMenu();
+ headerPM->insertItem(i18n("Remove Column"), HEADER_REMOVE);
+ headerPM->insertItem(i18n("Add Column"), HEADER_ADD);
+ headerPM->insertItem(i18n("Help on Column"), HEADER_HELP);
+
+ connect(header(), SIGNAL(sizeChange(int, int, int)),
+ this, SLOT(sizeChanged(int, int, int)));
+ connect(header(), SIGNAL(indexChange(int, int, int)),
+ this, SLOT(indexChanged(int, int, int)));
+
+ killSupported = false;
+ setModified(false);
+}
+
+ProcessList::~ProcessList()
+{
+ delete(headerPM);
+}
+
+const QValueList<int>&
+ProcessList::getSelectedPIds()
+{
+ selectedPIds.clear();
+ // iterate through all selected visible items of the listview
+ QListViewItemIterator it(this, QListViewItemIterator::Visible | QListViewItemIterator::Selected );
+ for ( ; it.current(); ++it )
+ selectedPIds.append(it.current()->text(1).toInt());
+
+ return (selectedPIds);
+}
+
+const QStringList&
+ProcessList::getSelectedAsStrings()
+{
+ selectedAsStrings.clear();
+ // iterate through all selected visible items of the listview
+ QListViewItemIterator it(this, QListViewItemIterator::Visible | QListViewItemIterator::Selected );
+ QString spaces;
+ for ( ; it.current(); ++it ) {
+ spaces.fill(QChar(' '), 7 - it.current()->text(1).length());
+ selectedAsStrings.append("(PID: " + it.current()->text(1) + ")" + spaces + " " + it.current()->text(0));
+ }
+
+ return (selectedAsStrings);
+}
+bool
+ProcessList::update(const QString& list)
+{
+ /* Disable painting to avoid flickering effects,
+ * especially when in tree view mode.
+ * Ditto for the scrollbar. */
+ setUpdatesEnabled(false);
+ viewport()->setUpdatesEnabled(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;
+ QString 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();
+
+ QListViewItemIterator 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.
+ setUpdatesEnabled(true);
+ viewport()->setUpdatesEnabled(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(QDomElement& el)
+{
+ QDomNodeList dnList = el.elementsByTagName("column");
+ for (uint i = 0; i < dnList.count(); ++i)
+ {
+ QDomElement 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(QDomDocument& doc, QDomElement& display)
+{
+ for (int i = 0; i < columns(); ++i)
+ {
+ QDomElement 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 QListView 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.at(i)->pid()) ||
+ matchesFilter(pl.at(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.at(i)->ppid() == pid)
+ return (false);
+
+ return (true);
+}
+
+void
+ProcessList::extendTree(QPtrList<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)
+{
+ QString name = p->name();
+ if (aliases[name])
+ name = *aliases[name];
+
+ /* Get icon from icon list that might be appropriate for a process
+ * with this name. */
+ QPixmap 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. */
+ QImage 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. */
+ QPixmap icon(24, 16, pix.depth());
+ if (!treeViewEnabled)
+ {
+ icon.fill();
+ bitBlt(&icon, 4, 0, &pix, 0, 0, pix.width(), pix.height());
+ QBitmap 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 QPixmap(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();
+
+ QListViewItemIterator 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 QListView.
+ * 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 QString& label, const QString& type)
+{
+ QListView::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. */
+ QFontMetrics 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 ?
+ QListView::Manual : QListView::Maximum);
+ header()->moveSection(i, index[i]);
+ }
+ setSorting(sortColumn, increasing);
+ }
+}
+
+void
+ProcessList::handleRMBPressed(QListViewItem* lvi, const QPoint& 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 (QString::compare(header()->label(i), i18n("Nice")) == 0)
+ currentNiceValue = lvi->text(i).toInt();
+
+ QPopupMenu processPM;
+ if (columnWidth(col) != 0)
+ processPM.insertItem(i18n("Hide Column"), 5);
+ QPopupMenu* hiddenPM = new QPopupMenu(&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);
+
+ QPopupMenu* signalPM = new QPopupMenu(&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, QListView::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();
+ QListViewItemIterator it(this, QListViewItemIterator::Visible | QListViewItemIterator::Selected);
+
+ // iterate through all selected visible items of the listview
+ for ( ; it.current(); ++it )
+ {
+ selectedPIds.append(it.current()->text(1).toInt());
+ }
+
+ QString 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, QString::null, i18n("Send"), KStdGuiItem::cancel()))
+ {
+ case KMessageBox::Yes:
+ {
+ QValueList<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, QListView::Maximum);
+ setColumnWidth(col, savedWidth[col]);
+ setModified(true);
+ }
+ }
+}
+
+void
+ProcessList::selectAllItems(bool select)
+{
+ selectedPIds.clear();
+
+ QListViewItemIterator it(this, QListViewItemIterator::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)
+{
+ QListViewItemIterator it(this, QListViewItemIterator::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"