Fix incorrect visibility in Attach to Process when the filter is changed.
[kdbg.git] / kdbg / procattach.cpp
blob8f19ab13317a30ee1f0da874737f53eea3561d4f
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 <Q3ListView>
9 #include <QProcess>
10 #include <ctype.h>
11 #include <kglobal.h>
12 #include <kiconloader.h>
13 #include <klocale.h> /* i18n */
14 #include "config.h"
17 ProcAttachPS::ProcAttachPS(QWidget* parent) :
18 QDialog(parent),
19 m_pidCol(-1),
20 m_ppidCol(-1)
22 setupUi(this);
23 on_processList_selectionChanged(); //update OK button disabled state.
25 m_ps = new QProcess;
26 connect(m_ps, SIGNAL(readyReadStandardOutput()),
27 this, SLOT(slotTextReceived()));
28 connect(m_ps, SIGNAL(finished(int,QProcess::ExitStatus)),
29 this, SLOT(slotPSDone()));
31 processList->setColumnWidth(0, 300);
32 processList->setColumnWidthMode(0, Q3ListView::Manual);
33 processList->setColumnAlignment(1, Qt::AlignRight);
34 processList->setColumnAlignment(2, Qt::AlignRight);
36 runPS();
39 ProcAttachPS::~ProcAttachPS()
41 delete m_ps; // kills a running ps
44 void ProcAttachPS::runPS()
46 // clear the parse state from previous runs
47 m_token = "";
48 m_line.clear();
49 m_pidCol = -1;
50 m_ppidCol = -1;
52 // set the command line
53 char* const psCommand[] = {
54 #ifdef PS_COMMAND
55 PS_COMMAND,
56 #else
57 "/bin/false",
58 #endif
61 QStringList args;
62 for (int i = 1; psCommand[i] != 0; i++) {
63 args.push_back(psCommand[i]);
66 m_ps->start(psCommand[0], args);
69 void ProcAttachPS::slotTextReceived()
71 QByteArray data = m_ps->readAllStandardOutput();
72 char* buffer = data.data();
73 char* end = buffer + data.size();
75 while (buffer < end)
77 // check new line
78 if (*buffer == '\n')
80 // push a tokens onto the line
81 if (!m_token.isEmpty()) {
82 m_line.push_back(QString::fromLatin1(m_token));
83 m_token = "";
85 // and insert the line in the list
86 pushLine();
87 m_line.clear();
88 ++buffer;
90 // blanks: the last column gets the rest of the line, including blanks
91 else if ((m_pidCol < 0 || int(m_line.size()) < processList->columns()-1) &&
92 isspace(*buffer))
94 // push a token onto the line
95 if (!m_token.isEmpty()) {
96 m_line.push_back(QString::fromLatin1(m_token));
97 m_token = "";
99 do {
100 ++buffer;
101 } while (buffer < end && isspace(*buffer));
103 // tokens
104 else
106 const char* start = buffer;
107 do {
108 ++buffer;
109 } while (buffer < end && !isspace(*buffer));
110 // append to the current token
111 m_token += Q3CString(start, buffer-start+1); // must count the '\0'
116 void ProcAttachPS::pushLine()
118 if (m_line.size() < 3) // we need the PID, PPID, and COMMAND columns
119 return;
121 if (m_pidCol < 0)
123 // create columns if we don't have them yet
124 bool allocate = processList->columns() == 3;
126 // we assume that the last column is the command
127 m_line.pop_back();
129 for (int i = 0; i < m_line.size(); i++) {
130 // we don't allocate the PID and PPID columns,
131 // but we need to know where in the ps output they are
132 if (m_line[i] == "PID") {
133 m_pidCol = i;
134 } else if (m_line[i] == "PPID") {
135 m_ppidCol = i;
136 } else if (allocate) {
137 processList->addColumn(m_line[i]);
138 // these columns are normally numbers
139 processList->setColumnAlignment(processList->columns()-1,
140 Qt::AlignRight);
144 else
146 // insert a line
147 // find the parent process
148 Q3ListViewItem* parent = 0;
149 if (m_ppidCol >= 0 && m_ppidCol < int(m_line.size())) {
150 parent = processList->findItem(m_line[m_ppidCol], 1);
153 // we assume that the last column is the command
154 Q3ListViewItem* item;
155 if (parent == 0) {
156 item = new Q3ListViewItem(processList, m_line.back());
157 } else {
158 item = new Q3ListViewItem(parent, m_line.back());
160 item->setOpen(true);
161 m_line.pop_back();
162 int k = 3;
163 for (int i = 0; i < m_line.size(); i++)
165 // display the pid and ppid columns' contents in columns 1 and 2
166 if (int(i) == m_pidCol)
167 item->setText(1, m_line[i]);
168 else if (int(i) == m_ppidCol)
169 item->setText(2, m_line[i]);
170 else
171 item->setText(k++, m_line[i]);
174 if (m_ppidCol >= 0 && m_pidCol >= 0) { // need PID & PPID for this
176 * It could have happened that a process was earlier inserted,
177 * whose parent process is the current process. Such processes
178 * were placed at the root. Here we go through all root items
179 * and check whether we must reparent them.
181 Q3ListViewItem* i = processList->firstChild();
182 while (i != 0)
184 // advance before we reparent the item
185 Q3ListViewItem* it = i;
186 i = i->nextSibling();
187 if (it->text(2) == m_line[m_pidCol]) {
188 processList->takeItem(it);
189 item->insertItem(it);
196 void ProcAttachPS::slotPSDone()
198 on_filterEdit_textChanged(filterEdit->text());
201 QString ProcAttachPS::text() const
203 Q3ListViewItem* item = processList->selectedItem();
205 if (item == 0)
206 return QString();
208 return item->text(1);
211 void ProcAttachPS::on_buttonRefresh_clicked()
213 if (m_ps->state() == QProcess::NotRunning)
215 processList->clear();
216 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(false); // selection was cleared
217 runPS();
221 void ProcAttachPS::on_filterEdit_textChanged(const QString& text)
223 for (Q3ListViewItem* i = processList->firstChild(); i; i = i->nextSibling())
224 setVisibility(i, text);
228 * Sets the visibility of \a i and
229 * returns whether it was made visible.
231 bool ProcAttachPS::setVisibility(Q3ListViewItem* i, const QString& text)
233 i->setVisible(true);
234 bool visible = false;
235 for (Q3ListViewItem* j = i->firstChild(); j; j = j->nextSibling())
237 if (setVisibility(j, text))
238 visible = true;
240 // look for text in the process name and in the PID
241 visible = visible || text.isEmpty() ||
242 i->text(0).find(text, 0, false) >= 0 ||
243 i->text(1).find(text) >= 0;
245 if (!visible)
246 i->setVisible(false);
248 // disable the OK button if the selected item becomes invisible
249 if (i->isSelected())
250 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(visible);
252 return visible;
255 void ProcAttachPS::on_processList_selectionChanged()
257 dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(processList->selectedItem() != 0);
261 ProcAttach::ProcAttach(QWidget* parent) :
262 QDialog(parent, "procattach", true),
263 m_label(this, "label"),
264 m_processId(this, "procid"),
265 m_buttonOK(this, "ok"),
266 m_buttonCancel(this, "cancel"),
267 m_layout(this, 8),
268 m_buttons(4)
270 QString title = KGlobal::caption();
271 title += i18n(": Attach to process");
272 setCaption(title);
274 m_label.setMinimumSize(330, 24);
275 m_label.setText(i18n("Specify the process number to attach to:"));
277 m_processId.setMinimumSize(330, 24);
278 m_processId.setMaxLength(100);
279 m_processId.setFrame(true);
281 m_buttonOK.setMinimumSize(100, 30);
282 connect(&m_buttonOK, SIGNAL(clicked()), SLOT(accept()));
283 m_buttonOK.setText(i18n("OK"));
284 m_buttonOK.setDefault(true);
286 m_buttonCancel.setMinimumSize(100, 30);
287 connect(&m_buttonCancel, SIGNAL(clicked()), SLOT(reject()));
288 m_buttonCancel.setText(i18n("Cancel"));
290 m_layout.addWidget(&m_label);
291 m_layout.addWidget(&m_processId);
292 m_layout.addLayout(&m_buttons);
293 m_layout.addStretch(10);
294 m_buttons.addStretch(10);
295 m_buttons.addWidget(&m_buttonOK);
296 m_buttons.addSpacing(40);
297 m_buttons.addWidget(&m_buttonCancel);
298 m_buttons.addStretch(10);
300 m_layout.activate();
302 m_processId.setFocus();
303 resize(350, 120);
306 ProcAttach::~ProcAttach()
311 #include "procattach.moc"