Improve the usability of the Attach to Process dialog.
[kdbg.git] / kdbg / procattach.cpp
blob25806ef69333db8c1a0d7241dff2fcc14f80e7a9
1 // $Id$
3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
6 #include "procattach.h"
7 #include <qlistview.h>
8 #include <qtoolbutton.h>
9 #include <qlineedit.h>
10 #include <kprocess.h>
11 #include <ctype.h>
12 #include <kapp.h>
13 #include <kiconloader.h>
14 #include <klocale.h> /* i18n */
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
20 ProcAttachPS::ProcAttachPS(QWidget* parent) :
21 ProcAttachBase(parent),
22 m_pidCol(-1),
23 m_ppidCol(-1)
25 m_ps = new KProcess;
26 connect(m_ps, SIGNAL(receivedStdout(KProcess*, char*, int)),
27 this, SLOT(slotTextReceived(KProcess*, char*, int)));
29 QIconSet icon = SmallIconSet("clear_left");
30 filterClear->setIconSet(icon);
32 processList->setColumnWidth(0, 300);
33 processList->setColumnWidthMode(0, QListView::Manual);
34 processList->setColumnAlignment(1, Qt::AlignRight);
35 processList->setColumnAlignment(2, Qt::AlignRight);
37 // set the command line
38 static const char* const psCommand[] = {
39 #ifdef PS_COMMAND
40 PS_COMMAND,
41 #else
42 "/bin/false",
43 #endif
46 for (int i = 0; psCommand[i] != 0; i++) {
47 *m_ps << psCommand[i];
50 runPS();
53 ProcAttachPS::~ProcAttachPS()
55 delete m_ps; // kills a running ps
58 void ProcAttachPS::runPS()
60 // clear the parse state from previous runs
61 m_token = "";
62 m_line.clear();
63 m_pidCol = -1;
64 m_ppidCol = -1;
66 m_ps->start(KProcess::NotifyOnExit, KProcess::Stdout);
69 void ProcAttachPS::slotTextReceived(KProcess*, char* buffer, int buflen)
71 const char* end = buffer+buflen;
72 while (buffer < end)
74 // check new line
75 if (*buffer == '\n')
77 // push a tokens onto the line
78 if (!m_token.isEmpty()) {
79 m_line.push_back(QString::fromLatin1(m_token));
80 m_token = "";
82 // and insert the line in the list
83 pushLine();
84 m_line.clear();
85 ++buffer;
87 // blanks: the last column gets the rest of the line, including blanks
88 else if ((m_pidCol < 0 || int(m_line.size()) < processList->columns()-1) &&
89 isspace(*buffer))
91 // push a token onto the line
92 if (!m_token.isEmpty()) {
93 m_line.push_back(QString::fromLatin1(m_token));
94 m_token = "";
96 do {
97 ++buffer;
98 } while (buffer < end && isspace(*buffer));
100 // tokens
101 else
103 const char* start = buffer;
104 do {
105 ++buffer;
106 } while (buffer < end && !isspace(*buffer));
107 // append to the current token
108 m_token += QCString(start, buffer-start+1); // must count the '\0'
113 void ProcAttachPS::pushLine()
115 if (m_line.size() < 3) // we need the PID, PPID, and COMMAND columns
116 return;
118 if (m_pidCol < 0)
120 // create columns if we don't have them yet
121 bool allocate = processList->columns() == 3;
123 // we assume that the last column is the command
124 m_line.pop_back();
126 for (uint i = 0; i < m_line.size(); i++) {
127 // we don't allocate the PID and PPID columns,
128 // but we need to know where in the ps output they are
129 if (m_line[i] == "PID") {
130 m_pidCol = i;
131 } else if (m_line[i] == "PPID") {
132 m_ppidCol = i;
133 } else if (allocate) {
134 processList->addColumn(m_line[i]);
135 // these columns are normally numbers
136 processList->setColumnAlignment(processList->columns()-1,
137 Qt::AlignRight);
141 else
143 // insert a line
144 // find the parent process
145 QListViewItem* parent = 0;
146 if (m_ppidCol >= 0 && m_ppidCol < int(m_line.size())) {
147 parent = processList->findItem(m_line[m_ppidCol], 1);
150 // we assume that the last column is the command
151 QListViewItem* item;
152 if (parent == 0) {
153 item = new QListViewItem(processList, m_line.back());
154 } else {
155 item = new QListViewItem(parent, m_line.back());
157 item->setOpen(true);
158 m_line.pop_back();
159 int k = 3;
160 for (uint i = 0; i < m_line.size(); i++)
162 // display the pid and ppid columns' contents in columns 1 and 2
163 if (int(i) == m_pidCol)
164 item->setText(1, m_line[i]);
165 else if (int(i) == m_ppidCol)
166 item->setText(2, m_line[i]);
167 else
168 item->setText(k++, m_line[i]);
171 if (m_ppidCol >= 0 && m_pidCol >= 0) { // need PID & PPID for this
173 * It could have happened that a process was earlier inserted,
174 * whose parent process is the current process. Such processes
175 * were placed at the root. Here we go through all root items
176 * and check whether we must reparent them.
178 QListViewItem* i = processList->firstChild();
179 while (i != 0)
181 // advance before we reparent the item
182 QListViewItem* it = i;
183 i = i->nextSibling();
184 if (it->text(2) == m_line[m_pidCol]) {
185 processList->takeItem(it);
186 item->insertItem(it);
193 QString ProcAttachPS::text() const
195 QListViewItem* item = processList->selectedItem();
197 if (item == 0)
198 return QString();
200 return item->text(1);
203 void ProcAttachPS::refresh()
205 if (!m_ps->isRunning())
207 processList->clear();
208 runPS();
212 void ProcAttachPS::filterEdited(const QString& text)
214 QListViewItem* i = processList->firstChild();
215 setVisibility(i, text);
219 * Sets the visibility of \a i and
220 * returns whether it was made visible.
222 bool ProcAttachPS::setVisibility(QListViewItem* i, const QString& text)
224 bool visible = false;
225 for (QListViewItem* j = i->firstChild(); j; j = j->nextSibling())
227 if (setVisibility(j, text))
228 visible = true;
230 // look for text in the process name and in the PID
231 visible = visible || text.isEmpty() ||
232 i->text(0).find(text, 0, false) >= 0 ||
233 i->text(1).find(text) >= 0;
235 i->setVisible(visible);
237 return visible;
241 ProcAttach::ProcAttach(QWidget* parent) :
242 QDialog(parent, "procattach", true),
243 m_label(this, "label"),
244 m_processId(this, "procid"),
245 m_buttonOK(this, "ok"),
246 m_buttonCancel(this, "cancel"),
247 m_layout(this, 8),
248 m_buttons(4)
250 QString title = kapp->caption();
251 title += i18n(": Attach to process");
252 setCaption(title);
254 m_label.setMinimumSize(330, 24);
255 m_label.setText(i18n("Specify the process number to attach to:"));
257 m_processId.setMinimumSize(330, 24);
258 m_processId.setMaxLength(100);
259 m_processId.setFrame(true);
261 m_buttonOK.setMinimumSize(100, 30);
262 connect(&m_buttonOK, SIGNAL(clicked()), SLOT(accept()));
263 m_buttonOK.setText(i18n("OK"));
264 m_buttonOK.setDefault(true);
266 m_buttonCancel.setMinimumSize(100, 30);
267 connect(&m_buttonCancel, SIGNAL(clicked()), SLOT(reject()));
268 m_buttonCancel.setText(i18n("Cancel"));
270 m_layout.addWidget(&m_label);
271 m_layout.addWidget(&m_processId);
272 m_layout.addLayout(&m_buttons);
273 m_layout.addStretch(10);
274 m_buttons.addStretch(10);
275 m_buttons.addWidget(&m_buttonOK);
276 m_buttons.addSpacing(40);
277 m_buttons.addWidget(&m_buttonCancel);
278 m_buttons.addStretch(10);
280 m_layout.activate();
282 m_processId.setFocus();
283 resize(350, 120);
286 ProcAttach::~ProcAttach()