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.
7 #include "procattach.h"
10 #include <QTreeWidget>
13 #include <kiconloader.h>
14 #include <klocale.h> /* i18n */
18 ProcAttachPS::ProcAttachPS(QWidget
* parent
) :
24 on_processList_currentItemChanged(); // update OK button state
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);
43 ProcAttachPS::~ProcAttachPS()
45 delete m_ps
; // kills a running ps
48 void ProcAttachPS::runPS()
50 // clear the parse state from previous runs
56 // set the command line
57 const char* const psCommand
[] = {
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);
87 // push a tokens onto the line
88 if (!m_token
.isEmpty()) {
89 m_line
.push_back(QString::fromLatin1(m_token
));
92 // and insert the line in the list
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) &&
101 // push a token onto the line
102 if (!m_token
.isEmpty()) {
103 m_line
.push_back(QString::fromLatin1(m_token
));
108 } while (buffer
< end
&& isspace(*buffer
));
113 const char* start
= 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
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
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") {
146 } else if (m_line
[i
] == "PPID") {
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,
153 processList
->header()->setResizeMode(i
+1,
154 QHeaderView::ResizeToContents
);
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
;
173 item
= new QTreeWidgetItem(processList
, QStringList(m_line
.back()));
175 item
= new QTreeWidgetItem(parent
, QStringList(m_line
.back()));
177 item
->setExpanded(true);
180 for (size_t i
= 0; i
< m_line
.size(); i
++)
182 // display the pid and ppid columns' contents in columns 1 and 2
184 if (int(i
) == m_pidCol
)
186 else if (int(i
) == m_ppidCol
)
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.
202 while (i
< processList
->topLevelItemCount())
204 QTreeWidgetItem
* it
= processList
->topLevelItem(i
);
205 if (it
->text(2) == m_line
[m_pidCol
]) {
206 processList
->takeTopLevelItem(i
);
215 void ProcAttachPS::slotPSDone()
217 on_filterEdit_textChanged(filterEdit
->text());
220 QString
ProcAttachPS::text() const
222 QTreeWidgetItem
* item
= processList
->currentItem();
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
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
)
253 bool visible
= false;
254 for (int j
= 0; j
< i
->childCount(); j
++)
256 if (setVisibility(i
->child(j
), text
))
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;
267 // disable the OK button if the selected item becomes invisible
269 dialogButtons
->button(QDialogButtonBox::Ok
)->setEnabled(visible
);
274 void ProcAttachPS::on_processList_currentItemChanged()
276 dialogButtons
->button(QDialogButtonBox::Ok
)->setEnabled(processList
->currentItem() != 0);
280 ProcAttach::ProcAttach(QWidget
* parent
) :
285 m_buttonCancel(this),
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);
321 m_processId
.setFocus();
325 ProcAttach::~ProcAttach()
330 #include "procattach.moc"