26 #include "ui_LogDialog.h"
30 #include <QTextStream>
33 #include <QMessageBox>
36 #include <QFileDialog>
37 #include <QMutexLocker>
38 #include <QStringListModel>
40 #include <QStandardItemModel>
45 QWidget* pp, Qt::WindowFlags wf) :
46 QDialog(pp,wf), _log(0), _logProx(0), _decorator(dec), _proc(0),
47 _logFile(0), _logMutex(new QMutex()), _errorLinesDetected(false)
49 _proc =
new QProcess(
this);
50 _proc->setObjectName(
"proc");
51 _ui =
new Ui::LogDialog;
54 _ui->buttonBox->setStandardButtons(
55 _ui->buttonBox->standardButtons() | QDialogButtonBox::Help);
59 _log =
new QStringListModel(
this);
62 QFont ttFont(
"fixed"); ttFont.setStyleHint(QFont::TypeWriter);
63 _ui->logView->setFont(ttFont);
76 if (!title.isEmpty()) {
77 setWindowTitle(title);
79 if (!desc.isEmpty()) {
80 _ui->lInfo->setText(desc);
84 _ui->statusLayout->addWidget(statWid);
87 _ui->infoDisplay->setVisible(
false);
91 testArgs <<
"--quiet" <<
"--non-interactive";
92 QString tcRun = QApplication::applicationDirPath() +
"/tuchulcha-run";
93 if (QProcess::execute(tcRun,testArgs)) {
94 tcRun = QString::null;
96 QString tcRunD = QApplication::applicationDirPath() +
"/tuchulcha-run_d";
97 if (QProcess::execute(tcRunD,testArgs)) {
98 tcRunD = QString::null;
102 QString procName = tcRun;
104 if ((!tcRunD.isNull()
105 && settings.value(
"suffixedPlugins",
false).toBool())
110 if (procName.isNull()) {
112 printStatus(QString(
"<br><span class=\"error\">%1</span></br>")
113 .arg(tr(
"no working %1 process executable found")
114 .arg(
"<tt>tuchulcha-run</tt>")));
115 _ui->lProcName->setText(
117 QString(
" <span style=\"font-weight:bold;color:red\">(%1)</span>")
124 +QString(
" <span style=\"color:blue\"><tt>%1</tt></span>")
125 .arg(QFileInfo(procName).baseName());
129 _logFile->open(QIODevice::WriteOnly|QIODevice::Truncate|QIODevice::Text);
130 procText = QString(
"%1 %2: <span style=\"color:blue\"><tt>%3</tt></span>")
131 .arg(procText).arg(tr(
"Log"))
132 .arg(QFileInfo(
_logFile->fileName()).fileName());
133 _ui->lProcName->setText(procText);
139 QIODevice::ReadWrite|QIODevice::Text);
142 QString(
"%1 PID: <span style=\"color:blue\"><tt>%2</tt></span>")
143 .arg(procText).arg(
_proc->pid());
144 _ui->lProcName->setText(procText);
149 QTimer::singleShot(0,
this,SLOT(reject()));
153 _ui->infoDisplay->document()->setDefaultStyleSheet(
154 "*{white-space:pre;font-weight:normal}\n"
155 "h3{font-weight:bold;font-variant:small-caps}\n"
156 ".file {font-family:monospace;}\n"
157 "td {padding: 0 4px;}\n"
158 ".error {color:red;font-weight:bold;}\n"
159 ".success {color:green;}\n"
160 ".warning {color:orange;}\n"
161 ".info {color:#444;}\n"
162 ".debug {color:gray;}\n"
166 LogDialog::~LogDialog() {
176 settings.beginGroup(
"LogDialog");
177 _ui->checkDD->setChecked(settings.value(
"showDebugOutput",
178 _ui->checkDD->isChecked()).toBool());
179 _ui->checkScroll->setChecked(settings.value(
"autoScroll",
180 _ui->checkScroll->isChecked()).toBool());
181 _ui->sBufSize->setValue(settings.value(
"maxLines",
182 _ui->sBufSize->value()).toInt());
188 settings.beginGroup(
"LogDialog");
189 settings.setValue(
"showDebugOutput",
_ui->checkDD->isChecked());
190 settings.setValue(
"autoScroll",
_ui->checkScroll->isChecked());
191 settings.setValue(
"maxLines",
_ui->sBufSize->value());
198 if (
_proc && (
_proc->state() != QProcess::NotRunning)) {
200 QString(
"<br><span class=\"warning\">%1</span><br>")
201 .arg(tr(
"waiting for process to quit..."))+
202 QString(
"<span class=\"warning\">%1</span><br>")
203 .arg(tr(
"asking for termination in 1 second")));
204 _proc->write(
"quit\n");
205 connect(
_proc,SIGNAL(finished(
int)),SLOT(close()));
206 QTimer::singleShot(1000,
this,SLOT(
terminate()));
215 if (
_proc && (
_proc->state() != QProcess::NotRunning)
216 && (force || QMessageBox::question(
217 this,tr(
"confirm terminate"),
218 tr(
"Process still running.<br>Terminate running process?"),
219 QMessageBox::No,QMessageBox::Yes)==QMessageBox::Yes)) {
220 printStatus(QString(
"<span class=\"warning\">%1</span><br>")
221 .arg(tr(
"asking for kill in 3 seconds")));
223 QTimer::singleShot(3000,
this,SLOT(
kill()));
228 if (
_proc && (
_proc->state() != QProcess::NotRunning)
229 && (force || QMessageBox::question(
230 this,tr(
"confirm kill"),
231 tr(
"Process did not respond.<br>Kill running process?"),
232 QMessageBox::No,QMessageBox::Yes)==QMessageBox::Yes)) {
238 if (
_proc->state() == QProcess::Starting) {
239 _proc->waitForStarted(msecs);
241 if (
_proc->state() == QProcess::Running) {
242 return _proc->waitForFinished(msecs);
249 QString origS = QString::fromLocal8Bit(
_proc->readAllStandardOutput());
251 QTextStream orig(&origS,QIODevice::ReadOnly);
252 QStringList logList =
_log->stringList();
254 const int maxBuf =
_ui->sBufSize->value();
257 cur = orig.readLine();
261 logFile << cur << endl;
264 QRegExp(
"^\\(EE\\)\\s+",Qt::CaseInsensitive))) {
268 if (maxBuf && logList.size() > maxBuf) {
269 logList.removeFirst();
272 _log->setStringList(logList);
274 if (
_ui->checkScroll->isChecked()) {
275 _ui->logView->scrollToBottom();
284 QString origS = QString::fromLocal8Bit(
_proc->readAllStandardError());
286 QTextStream orig(&origS,QIODevice::ReadOnly);
287 QStringList logList =
_log->stringList();
289 const int maxBuf =
_ui->sBufSize->value();
292 cur = orig.readLine();
297 QRegExp(
"^\\(\\w+\\)\\s+",Qt::CaseInsensitive))) {
298 cur = QString(
"(EE) %1").arg(cur);
300 logFile << cur << endl;
303 if (maxBuf && logList.size() > maxBuf) {
304 logList.removeFirst();
308 _log->setStringList(logList);
310 if (
_ui->checkScroll->isChecked()) {
311 _ui->logView->scrollToBottom();
329 _logFile->open(QIODevice::ReadOnly|QIODevice::Text);
332 const int maxBuf =
_ui->sBufSize->value();
335 cur = log.readLine();
340 if (maxBuf && logList.size() > maxBuf) {
341 logList.removeFirst();
344 _log->setStringList(logList);
346 if (
_ui->checkScroll->isChecked()) {
347 _ui->logView->scrollToBottom();
351 _logFile->open(QIODevice::WriteOnly|QIODevice::Append|QIODevice::Text);
357 _ui->progressBar->show();
359 _ui->buttonBox->setStandardButtons(
360 QDialogButtonBox::Abort|QDialogButtonBox::Help);
362 _ui->buttonBox->setStandardButtons(QDialogButtonBox::Abort);
366 QTextStream pout(
_proc);
367 foreach(
const QString& cmd, postStart) {
374 _ui->progressBar->hide();
376 _ui->buttonBox->setStandardButtons(
377 QDialogButtonBox::Close|QDialogButtonBox::Help);
379 _ui->buttonBox->setStandardButtons(QDialogButtonBox::Close);
387 switch(
_proc->error()) {
388 case QProcess::FailedToStart:
389 errorType = tr(
"process failed to start");
391 case QProcess::Crashed:
392 errorType = tr(
"process crashed after start");
394 case QProcess::Timedout:
395 errorType = tr(
"process timeout");
397 case QProcess::WriteError:
398 errorType = tr(
"write error");
400 case QProcess::ReadError:
401 errorType = tr(
"read error");
404 errorType = tr(
"unknown error");
408 printStatus(QString(
"<br><span class=\"error\">%1</span> %2<br>")
409 .arg(tr(
"Error during process execution:"))
414 _ui->infoDisplay->setVisible(
true);
415 _ui->infoDisplay->insertHtml(msg);
423 _logProx->setFilterRegExp(QString());
428 if (
_ui->checkScroll->isChecked()) {
429 _ui->logView->scrollToBottom();
435 QString fName = QFileDialog::getSaveFileName(
437 tr(
"Text File (*.txt *.log);;Html File (*.html *.htm)"),
441 if (selFilter.contains(
".txt")) {
445 _logFile->open(QIODevice::WriteOnly|QIODevice::Append|QIODevice::Text);
448 else if (selFilter.contains(
".html")) {
450 _ui->checkDD->setChecked(
true);
451 _ui->sBufSize->setValue(0);
452 _ui->logView->selectAll();
453 QMimeData* content =
_ui->logView->getSelectedContent();
454 Q_ASSERT(content->hasHtml());
457 QIODevice::WriteOnly|QIODevice::Truncate|QIODevice::Text)) {
458 QTextStream ostrm(&out);
459 ostrm << content->html() << endl;
463 _ui->logView->clearSelection();
467 qDebug(
"Invalid filter selected: %s",
468 selFilter.toLocal8Bit().constData());
480 QRegExp sWildExp(flt,Qt::CaseInsensitive, QRegExp::WildcardUnix);
481 QItemSelectionModel* selection =
_ui->logView->selectionModel();
482 const QAbstractItemModel* model = selection->model();
483 if (model->rowCount() <= 0) {
486 int rowStart = up ? model->rowCount()-1 : 0;
487 if (selection->currentIndex().isValid()) {
488 rowStart = selection->currentIndex().row() + offset;
489 rowStart = qMin(rowStart,model->rowCount()-1);
490 rowStart = qMax(rowStart,0);
492 int row; QModelIndex ind;
493 for (
int ii=0; ii < model->rowCount(); ++ii) {
494 row = ((up?rowStart-ii:rowStart+ii)+model->rowCount())
496 ind = model->index(row,0);
497 if (model->data(ind,Qt::DisplayRole).toString().contains(sWildExp)) {
498 selection->setCurrentIndex(
499 ind,QItemSelectionModel::SelectCurrent);
500 _ui->logView->setSelectionModel(selection);
501 _ui->logView->scrollTo(ind,QAbstractItemView::PositionAtCenter);
virtual QString logFileName() const =0
logfile name for output logging
void saveSettings()
store config to settings
void on_checkDD_toggled(bool)
handle debug checkbox
void on_proc_finished(int=0)
setup close button and hide progress bar
void on_proc_error(QProcess::ProcessError)
handle errors running the process
log decorator base class to handle different kinds of log dialogs
QFile * _logFile
log content output
virtual QString title() const
title string
virtual void processLine(QString line)
decorator hook to allow log line parsing
void reprint()
reparse log output
bool _errorLinesDetected
true if log contains error lines
Ui::LogDialog * _ui
designer ui
Declaration of class LogViewProxyModel.
void printStatus(QString msg)
print status message
virtual QString desc() const
description string
Declaration of classes in the LogDecorators namespace.
virtual void finishProcessing()
inform about finished processing
Declaration of class LogDialog.
void searchLog(QString filter, int offset=0, bool up=false)
search for given string
void terminate(bool force=false)
terminate process
void loadSettings()
load config from settings
bool hasErrorLines()
query for possible error lines in logfile
void on_bSearchUp_clicked()
search up
static QRegExp debugFilterRegex()
regexp for filtering out debug lines
void on_bSearchDown_clicked()
search down
virtual bool ready(QWidget *parent) const
check if process may be started
void on_proc_readyReadStandardError()
update content by querying process (stderr)
QStringListModel * _log
log model
void helpRequested(QString)
request help on specified page
virtual QStringList postStartCommands(QWidget *parent) const
commands sent to the proccess after start
QProcess * _proc
tuchulcha-run process
virtual QString filenameHint() const
hint for filename on save dialog
virtual QWidget * statusWidget()
custom status widget
virtual QString helpAnchor()
html anchor on help page
void kill(bool force=false)
kill process
void on_sBufSize_valueChanged(int)
handle buf size changes
QMutex * _logMutex
avoid parallel writes to log window
void on_eFilter_textEdited(QString)
handle search filter changes
void on_proc_readyReadStandardOutput()
update content by querying process (stdout)
virtual void done(int r)
handle process termination
void on_buttonBox_helpRequested()
handle help request
LogDialog(LogDecorators::Decorator *decorator, QWidget *parent=0, Qt::WindowFlags f=0)
constructor
void on_bSaveLog_clicked()
save logfile
LogDecorators::Decorator * _decorator
decorator implementation
void on_proc_started()
setup abort button and show progress bar
bool debugOutput
debug output mode
bool waitForFinished(int msecs=1500)
wait for process to finish
LogViewProxyModel * _logProx
log model proxy
virtual QStringList arguments() const =0
determine command line arguments
class for highlighting and handling filtering of log output