Extend copyright to 2018.
[kdbg.git] / kdbg / procattach.cpp
blob3c32400ff113aab76a5636bb6bb262905820aa68
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 <klocalizedstring.h> /* i18n */
13 #include "config.h"
16 ProcAttachPS::ProcAttachPS(QWidget* parent) :
17 QDialog(parent),
18 m_pidCol(-1),
19 m_ppidCol(-1)
21 setupUi(this);
22 on_processList_currentItemChanged(); // update OK button state
24 m_ps = new QProcess;
25 connect(m_ps, SIGNAL(readyReadStandardOutput()),
26 this, SLOT(slotTextReceived()));
27 connect(m_ps, SIGNAL(finished(int,QProcess::ExitStatus)),
28 this, SLOT(slotPSDone()));
30 processList->setColumnWidth(0, 300);
31 processList->header()->setSectionResizeMode(0, QHeaderView::Interactive);
32 processList->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
33 processList->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
34 processList->headerItem()->setTextAlignment(1, Qt::AlignRight);
35 processList->headerItem()->setTextAlignment(2, Qt::AlignRight);
36 processList->header()->setStretchLastSection(false);
38 runPS();
41 ProcAttachPS::~ProcAttachPS()
43 delete m_ps; // kills a running ps
46 void ProcAttachPS::runPS()
48 // clear the parse state from previous runs
49 m_token.clear();
50 m_line.clear();
51 m_pidCol = -1;
52 m_ppidCol = -1;
54 // set the command line
55 const char* const psCommand[] = {
56 #ifdef PS_COMMAND
57 PS_COMMAND,
58 #else
59 "/bin/false",
60 #endif
63 QStringList args;
64 for (int i = 1; psCommand[i] != 0; i++) {
65 args.push_back(psCommand[i]);
68 m_ps->start(psCommand[0], args);
71 void ProcAttachPS::slotTextReceived()
73 QByteArray data = m_ps->readAllStandardOutput();
74 char* buffer = data.data();
75 char* end = buffer + data.size();
77 // avoid expensive updates while items are inserted
78 processList->setUpdatesEnabled(false);
80 while (buffer < end)
82 // check new line
83 if (*buffer == '\n')
85 // push a tokens onto the line
86 if (!m_token.isEmpty()) {
87 m_line.push_back(QString::fromLatin1(m_token));
88 m_token.clear();
90 // and insert the line in the list
91 pushLine();
92 m_line.clear();
93 ++buffer;
95 // blanks: the last column gets the rest of the line, including blanks
96 else if ((m_pidCol < 0 || int(m_line.size()) < processList->columnCount()-1) &&
97 isspace(*buffer))
99 // push a token onto the line
100 if (!m_token.isEmpty()) {
101 m_line.push_back(QString::fromLatin1(m_token));
102 m_token.clear();
104 do {
105 ++buffer;
106 } while (buffer < end && isspace(*buffer));
108 // tokens
109 else
111 const char* start = buffer;
112 do {
113 ++buffer;
114 } while (buffer < end && !isspace(*buffer));
115 // append to the current token
116 m_token += QByteArray(start, buffer-start);
119 processList->setUpdatesEnabled(true);
122 void ProcAttachPS::pushLine()
124 if (m_line.size() < 3) // we need the PID, PPID, and COMMAND columns
125 return;
127 if (m_pidCol < 0)
129 // create columns if we don't have them yet
130 bool allocate = processList->columnCount() == 3;
132 // we assume that the last column is the command
133 m_line.pop_back();
135 if (allocate)
136 processList->setColumnCount(1 + m_line.size());
138 for (size_t i = 0; i < m_line.size(); i++)
140 // we don't allocate the PID and PPID columns,
141 // but we need to know where in the ps output they are
142 if (m_line[i] == "PID") {
143 m_pidCol = i;
144 } else if (m_line[i] == "PPID") {
145 m_ppidCol = i;
146 } else if (allocate) {
147 processList->headerItem()->setText(i+1, m_line[i]);
148 // these columns are normally numbers
149 processList->headerItem()->setTextAlignment(i+1,
150 Qt::AlignRight);
151 processList->header()->setSectionResizeMode(i+1,
152 QHeaderView::ResizeToContents);
156 else
158 // insert a line
159 // find the parent process
160 QTreeWidgetItem* parent = 0;
161 if (m_ppidCol >= 0 && m_ppidCol < int(m_line.size())) {
162 QList<QTreeWidgetItem*> items =
163 processList->findItems(m_line[m_ppidCol], Qt::MatchFixedString|Qt::MatchRecursive, 1);
164 if (!items.isEmpty())
165 parent = items.front();
168 // we assume that the last column is the command
169 QTreeWidgetItem* item;
170 if (parent == 0) {
171 item = new QTreeWidgetItem(processList, QStringList(m_line.back()));
172 } else {
173 item = new QTreeWidgetItem(parent, QStringList(m_line.back()));
175 item->setExpanded(true);
176 m_line.pop_back();
177 int k = 3;
178 for (size_t i = 0; i < m_line.size(); i++)
180 // display the pid and ppid columns' contents in columns 1 and 2
181 int col;
182 if (int(i) == m_pidCol)
183 col = 1;
184 else if (int(i) == m_ppidCol)
185 col = 2;
186 else
187 col = k++;
188 item->setText(col, m_line[i]);
189 item->setTextAlignment(col, Qt::AlignRight);
192 if (m_ppidCol >= 0 && m_pidCol >= 0) { // need PID & PPID for this
194 * It could have happened that a process was earlier inserted,
195 * whose parent process is the current process. Such processes
196 * were placed at the root. Here we go through all root items
197 * and check whether we must reparent them.
199 int i = 0;
200 while (i < processList->topLevelItemCount())
202 QTreeWidgetItem* it = processList->topLevelItem(i);
203 if (it->text(2) == m_line[m_pidCol]) {
204 processList->takeTopLevelItem(i);
205 item->addChild(it);
206 } else
207 ++i;
213 void ProcAttachPS::slotPSDone()
215 on_filterEdit_textChanged(filterEdit->text());
218 QString ProcAttachPS::text() const
220 QTreeWidgetItem* item = processList->currentItem();
222 if (item == 0)
223 return QString();
225 return item->text(1);
228 void ProcAttachPS::on_buttonRefresh_clicked()
230 if (m_ps->state() == QProcess::NotRunning)
232 processList->clear();
233 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(false); // selection was cleared
234 runPS();
238 void ProcAttachPS::on_filterEdit_textChanged(const QString& text)
240 for (int i = 0; i < processList->topLevelItemCount(); i++)
241 setVisibility(processList->topLevelItem(i), text);
245 * Sets the visibility of \a i and
246 * returns whether it was made visible.
248 bool ProcAttachPS::setVisibility(QTreeWidgetItem* i, const QString& text)
250 i->setHidden(false);
251 bool visible = false;
252 for (int j = 0; j < i->childCount(); j++)
254 if (setVisibility(i->child(j), text))
255 visible = true;
257 // look for text in the process name and in the PID
258 visible = visible || text.isEmpty() ||
259 i->text(0).indexOf(text, 0, Qt::CaseInsensitive) >= 0 ||
260 i->text(1).indexOf(text) >= 0;
262 if (!visible)
263 i->setHidden(true);
265 // disable the OK button if the selected item becomes invisible
266 if (i->isSelected())
267 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(visible);
269 return visible;
272 void ProcAttachPS::on_processList_currentItemChanged()
274 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(processList->currentItem() != 0);
278 ProcAttach::ProcAttach(QWidget* parent) :
279 QDialog(parent),
280 m_label(this),
281 m_processId(this),
282 m_buttonOK(this),
283 m_buttonCancel(this),
284 m_layout(this),
285 m_buttons()
287 m_label.setMinimumSize(330, 24);
288 m_label.setText(i18n("Specify the process number to attach to:"));
290 m_processId.setMinimumSize(330, 24);
291 m_processId.setMaxLength(100);
292 m_processId.setFrame(true);
294 m_buttonOK.setMinimumSize(100, 30);
295 connect(&m_buttonOK, SIGNAL(clicked()), SLOT(accept()));
296 m_buttonOK.setText(i18n("OK"));
297 m_buttonOK.setDefault(true);
299 m_buttonCancel.setMinimumSize(100, 30);
300 connect(&m_buttonCancel, SIGNAL(clicked()), SLOT(reject()));
301 m_buttonCancel.setText(i18n("Cancel"));
303 m_layout.addWidget(&m_label);
304 m_layout.addWidget(&m_processId);
305 m_layout.addLayout(&m_buttons);
306 m_layout.addStretch(10);
307 m_buttons.addStretch(10);
308 m_buttons.addWidget(&m_buttonOK);
309 m_buttons.addSpacing(40);
310 m_buttons.addWidget(&m_buttonCancel);
311 m_buttons.addStretch(10);
313 m_layout.activate();
315 m_processId.setFocus();
316 resize(350, 120);
319 ProcAttach::~ProcAttach()
324 #include "procattach.moc"