KDbg 2.5.6.
[kdbg.git] / kdbg / procattach.cpp
blob2f024ec8cc820aba5f9959a90cbcec99ab80fbf4
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "procattach.h"
8 #include <QHeaderView>
9 #include <QProcess>
10 #include <QTreeWidget>
11 #include <ctype.h>
12 #include <kglobal.h>
13 #include <kiconloader.h>
14 #include <klocale.h> /* i18n */
15 #include "config.h"
18 ProcAttachPS::ProcAttachPS(QWidget* parent) :
19 QDialog(parent),
20 m_pidCol(-1),
21 m_ppidCol(-1)
23 setupUi(this);
24 on_processList_currentItemChanged(); // update OK button state
26 m_ps = new QProcess;
27 connect(m_ps, SIGNAL(readyReadStandardOutput()),
28 this, SLOT(slotTextReceived()));
29 connect(m_ps, SIGNAL(finished(int,QProcess::ExitStatus)),
30 this, SLOT(slotPSDone()));
32 processList->setColumnWidth(0, 300);
33 processList->header()->setResizeMode(0, QHeaderView::Interactive);
34 processList->header()->setResizeMode(1, QHeaderView::ResizeToContents);
35 processList->header()->setResizeMode(2, QHeaderView::ResizeToContents);
36 processList->headerItem()->setTextAlignment(1, Qt::AlignRight);
37 processList->headerItem()->setTextAlignment(2, Qt::AlignRight);
38 processList->header()->setStretchLastSection(false);
40 runPS();
43 ProcAttachPS::~ProcAttachPS()
45 delete m_ps; // kills a running ps
48 void ProcAttachPS::runPS()
50 // clear the parse state from previous runs
51 m_token.clear();
52 m_line.clear();
53 m_pidCol = -1;
54 m_ppidCol = -1;
56 // set the command line
57 const char* const psCommand[] = {
58 #ifdef PS_COMMAND
59 PS_COMMAND,
60 #else
61 "/bin/false",
62 #endif
65 QStringList args;
66 for (int i = 1; psCommand[i] != 0; i++) {
67 args.push_back(psCommand[i]);
70 m_ps->start(psCommand[0], args);
73 void ProcAttachPS::slotTextReceived()
75 QByteArray data = m_ps->readAllStandardOutput();
76 char* buffer = data.data();
77 char* end = buffer + data.size();
79 // avoid expensive updates while items are inserted
80 processList->setUpdatesEnabled(false);
82 while (buffer < end)
84 // check new line
85 if (*buffer == '\n')
87 // push a tokens onto the line
88 if (!m_token.isEmpty()) {
89 m_line.push_back(QString::fromLatin1(m_token));
90 m_token.clear();
92 // and insert the line in the list
93 pushLine();
94 m_line.clear();
95 ++buffer;
97 // blanks: the last column gets the rest of the line, including blanks
98 else if ((m_pidCol < 0 || int(m_line.size()) < processList->columnCount()-1) &&
99 isspace(*buffer))
101 // push a token onto the line
102 if (!m_token.isEmpty()) {
103 m_line.push_back(QString::fromLatin1(m_token));
104 m_token.clear();
106 do {
107 ++buffer;
108 } while (buffer < end && isspace(*buffer));
110 // tokens
111 else
113 const char* start = buffer;
114 do {
115 ++buffer;
116 } while (buffer < end && !isspace(*buffer));
117 // append to the current token
118 m_token += QByteArray(start, buffer-start);
121 processList->setUpdatesEnabled(true);
124 void ProcAttachPS::pushLine()
126 if (m_line.size() < 3) // we need the PID, PPID, and COMMAND columns
127 return;
129 if (m_pidCol < 0)
131 // create columns if we don't have them yet
132 bool allocate = processList->columnCount() == 3;
134 // we assume that the last column is the command
135 m_line.pop_back();
137 if (allocate)
138 processList->setColumnCount(1 + m_line.size());
140 for (size_t i = 0; i < m_line.size(); i++)
142 // we don't allocate the PID and PPID columns,
143 // but we need to know where in the ps output they are
144 if (m_line[i] == "PID") {
145 m_pidCol = i;
146 } else if (m_line[i] == "PPID") {
147 m_ppidCol = i;
148 } else if (allocate) {
149 processList->headerItem()->setText(i+1, m_line[i]);
150 // these columns are normally numbers
151 processList->headerItem()->setTextAlignment(i+1,
152 Qt::AlignRight);
153 processList->header()->setResizeMode(i+1,
154 QHeaderView::ResizeToContents);
158 else
160 // insert a line
161 // find the parent process
162 QTreeWidgetItem* parent = 0;
163 if (m_ppidCol >= 0 && m_ppidCol < int(m_line.size())) {
164 QList<QTreeWidgetItem*> items =
165 processList->findItems(m_line[m_ppidCol], Qt::MatchFixedString|Qt::MatchRecursive, 1);
166 if (!items.isEmpty())
167 parent = items.front();
170 // we assume that the last column is the command
171 QTreeWidgetItem* item;
172 if (parent == 0) {
173 item = new QTreeWidgetItem(processList, QStringList(m_line.back()));
174 } else {
175 item = new QTreeWidgetItem(parent, QStringList(m_line.back()));
177 item->setExpanded(true);
178 m_line.pop_back();
179 int k = 3;
180 for (size_t i = 0; i < m_line.size(); i++)
182 // display the pid and ppid columns' contents in columns 1 and 2
183 int col;
184 if (int(i) == m_pidCol)
185 col = 1;
186 else if (int(i) == m_ppidCol)
187 col = 2;
188 else
189 col = k++;
190 item->setText(col, m_line[i]);
191 item->setTextAlignment(col, Qt::AlignRight);
194 if (m_ppidCol >= 0 && m_pidCol >= 0) { // need PID & PPID for this
196 * It could have happened that a process was earlier inserted,
197 * whose parent process is the current process. Such processes
198 * were placed at the root. Here we go through all root items
199 * and check whether we must reparent them.
201 int i = 0;
202 while (i < processList->topLevelItemCount())
204 QTreeWidgetItem* it = processList->topLevelItem(i);
205 if (it->text(2) == m_line[m_pidCol]) {
206 processList->takeTopLevelItem(i);
207 item->addChild(it);
208 } else
209 ++i;
215 void ProcAttachPS::slotPSDone()
217 on_filterEdit_textChanged(filterEdit->text());
220 QString ProcAttachPS::text() const
222 QTreeWidgetItem* item = processList->currentItem();
224 if (item == 0)
225 return QString();
227 return item->text(1);
230 void ProcAttachPS::on_buttonRefresh_clicked()
232 if (m_ps->state() == QProcess::NotRunning)
234 processList->clear();
235 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(false); // selection was cleared
236 runPS();
240 void ProcAttachPS::on_filterEdit_textChanged(const QString& text)
242 for (int i = 0; i < processList->topLevelItemCount(); i++)
243 setVisibility(processList->topLevelItem(i), text);
247 * Sets the visibility of \a i and
248 * returns whether it was made visible.
250 bool ProcAttachPS::setVisibility(QTreeWidgetItem* i, const QString& text)
252 i->setHidden(false);
253 bool visible = false;
254 for (int j = 0; j < i->childCount(); j++)
256 if (setVisibility(i->child(j), text))
257 visible = true;
259 // look for text in the process name and in the PID
260 visible = visible || text.isEmpty() ||
261 i->text(0).indexOf(text, 0, Qt::CaseInsensitive) >= 0 ||
262 i->text(1).indexOf(text) >= 0;
264 if (!visible)
265 i->setHidden(true);
267 // disable the OK button if the selected item becomes invisible
268 if (i->isSelected())
269 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(visible);
271 return visible;
274 void ProcAttachPS::on_processList_currentItemChanged()
276 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(processList->currentItem() != 0);
280 ProcAttach::ProcAttach(QWidget* parent) :
281 QDialog(parent),
282 m_label(this),
283 m_processId(this),
284 m_buttonOK(this),
285 m_buttonCancel(this),
286 m_layout(this),
287 m_buttons()
289 QString title = KGlobal::caption();
290 title += i18n(": Attach to process");
291 setWindowTitle(title);
293 m_label.setMinimumSize(330, 24);
294 m_label.setText(i18n("Specify the process number to attach to:"));
296 m_processId.setMinimumSize(330, 24);
297 m_processId.setMaxLength(100);
298 m_processId.setFrame(true);
300 m_buttonOK.setMinimumSize(100, 30);
301 connect(&m_buttonOK, SIGNAL(clicked()), SLOT(accept()));
302 m_buttonOK.setText(i18n("OK"));
303 m_buttonOK.setDefault(true);
305 m_buttonCancel.setMinimumSize(100, 30);
306 connect(&m_buttonCancel, SIGNAL(clicked()), SLOT(reject()));
307 m_buttonCancel.setText(i18n("Cancel"));
309 m_layout.addWidget(&m_label);
310 m_layout.addWidget(&m_processId);
311 m_layout.addLayout(&m_buttons);
312 m_layout.addStretch(10);
313 m_buttons.addStretch(10);
314 m_buttons.addWidget(&m_buttonOK);
315 m_buttons.addSpacing(40);
316 m_buttons.addWidget(&m_buttonCancel);
317 m_buttons.addStretch(10);
319 m_layout.activate();
321 m_processId.setFocus();
322 resize(350, 120);
325 ProcAttach::~ProcAttach()
330 #include "procattach.moc"