|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 1999-2001 Bernd Gehrmann <bernd@kdevelop.org>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "processwidget.h"
|
|
|
|
#include "processlinemaker.h"
|
|
|
|
|
|
|
|
#include <kdeversion.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kprocess.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqapplication.h>
|
|
|
|
|
|
|
|
|
|
|
|
ProcessListBoxItem::ProcessListBoxItem(const TQString &s, Type type)
|
|
|
|
: TQListBoxText(s), t(type)
|
|
|
|
{
|
|
|
|
TQString clean = s;
|
|
|
|
clean.replace( TQChar('\t'), TQString(" ") );
|
|
|
|
clean.replace( TQChar('\n'), TQString() );
|
|
|
|
clean.replace( TQChar('\r'), TQString() );
|
|
|
|
setText( clean );
|
|
|
|
|
|
|
|
setCustomHighlighting(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ProcessListBoxItem::isCustomItem()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned char normalize(int a)
|
|
|
|
{
|
|
|
|
return (a < 0 ? 0 : a > 255 ? 255 : a);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline double blend1(double a, double b, double k)
|
|
|
|
{
|
|
|
|
return a + (b - a) * k;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQColor ProcessListBoxItem::blend(const TQColor &c1, const TQColor &c2, double k) const
|
|
|
|
{
|
|
|
|
if (k < 0.0) return c1;
|
|
|
|
if (k > 1.0) return c2;
|
|
|
|
|
|
|
|
int r = normalize((int)blend1((double)c1.red(), (double)c2.red(), k));
|
|
|
|
int g = normalize((int)blend1((double)c1.green(), (double)c2.green(), k));
|
|
|
|
int b = normalize((int)blend1((double)c1.blue(), (double)c2.blue(), k));
|
|
|
|
|
|
|
|
return TQColor(tqRgb(r, g, b));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessListBoxItem::paint(TQPainter *p)
|
|
|
|
{
|
|
|
|
TQColor dim, warn, err, back;
|
|
|
|
if (listBox()) {
|
|
|
|
const TQColorGroup& group = listBox()->tqpalette().active();
|
|
|
|
if (isSelected()) {
|
|
|
|
back = group.button();
|
|
|
|
warn = group.buttonText();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
back = group.base();
|
|
|
|
warn = group.text();
|
|
|
|
}
|
|
|
|
err = group.linkVisited();
|
|
|
|
dim = blend(warn, back);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
warn = TQt::black;
|
|
|
|
dim = TQt::darkBlue;
|
|
|
|
err = TQt::darkRed;
|
|
|
|
if (isSelected())
|
|
|
|
back = TQt::lightGray;
|
|
|
|
else
|
|
|
|
back = TQt::white;
|
|
|
|
}
|
|
|
|
p->fillRect(p->window(), TQBrush(back));
|
|
|
|
p->setPen((t==Error)? err :
|
|
|
|
(t==Diagnostic)? warn : dim);
|
|
|
|
TQListBoxText::paint(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ProcessWidget::ProcessWidget(TQWidget *parent, const char *name)
|
|
|
|
: KListBox(parent, name)
|
|
|
|
{
|
|
|
|
setFocusPolicy(TQ_NoFocus);
|
|
|
|
|
|
|
|
// Don't override the palette, as that can mess up styles. Instead, draw
|
|
|
|
// the background ourselves (see ProcessListBoxItem::paint).
|
|
|
|
|
|
|
|
|
|
|
|
childproc = new KProcess();
|
|
|
|
childproc->setUseShell(true);
|
|
|
|
|
|
|
|
procLineMaker = new ProcessLineMaker( childproc );
|
|
|
|
|
|
|
|
connect( procLineMaker, TQT_SIGNAL(receivedStdoutLine(const TQCString&)),
|
|
|
|
this, TQT_SLOT(insertStdoutLine(const TQCString&) ));
|
|
|
|
connect( procLineMaker, TQT_SIGNAL(receivedStderrLine(const TQCString&)),
|
|
|
|
this, TQT_SLOT(insertStderrLine(const TQCString&) ));
|
|
|
|
connect( procLineMaker, TQT_SIGNAL(receivedPartialStdoutLine(const TQCString&)),
|
|
|
|
this, TQT_SLOT(addPartialStdoutLine(const TQCString&) ));
|
|
|
|
connect( procLineMaker, TQT_SIGNAL(receivedPartialStderrLine(const TQCString&)),
|
|
|
|
this, TQT_SLOT(addPartialStderrLine(const TQCString&) ));
|
|
|
|
|
|
|
|
connect(childproc, TQT_SIGNAL(processExited(KProcess*)),
|
|
|
|
this, TQT_SLOT(slotProcessExited(KProcess*) )) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ProcessWidget::~ProcessWidget()
|
|
|
|
{
|
|
|
|
delete childproc;
|
|
|
|
delete procLineMaker;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessWidget::startJob(const TQString &dir, const TQString &command)
|
|
|
|
{
|
|
|
|
procLineMaker->clearBuffers();
|
|
|
|
procLineMaker->blockSignals( false );
|
|
|
|
|
|
|
|
clear();
|
|
|
|
insertItem(new ProcessListBoxItem(command, ProcessListBoxItem::Diagnostic));
|
|
|
|
childproc->clearArguments();
|
|
|
|
if (!dir.isNull()) {
|
|
|
|
childproc->setWorkingDirectory( dir );
|
|
|
|
}
|
|
|
|
|
|
|
|
*childproc << command;
|
|
|
|
childproc->start(KProcess::OwnGroup, KProcess::AllOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessWidget::killJob( int signo )
|
|
|
|
{
|
|
|
|
procLineMaker->blockSignals( true );
|
|
|
|
|
|
|
|
childproc->kill( signo );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ProcessWidget::isRunning()
|
|
|
|
{
|
|
|
|
return childproc->isRunning();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessWidget::slotProcessExited(KProcess *)
|
|
|
|
{
|
|
|
|
procLineMaker->flush();
|
|
|
|
if( !stdoutbuf.isEmpty() )
|
|
|
|
insertStdoutLine("");
|
|
|
|
if( !stderrbuf.isEmpty() )
|
|
|
|
insertStderrLine("");
|
|
|
|
childFinished(childproc->normalExit(), childproc->exitStatus());
|
|
|
|
maybeScrollToBottom();
|
|
|
|
emit processExited(childproc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessWidget::insertStdoutLine(const TQCString &line)
|
|
|
|
{
|
|
|
|
if( !stdoutbuf.isEmpty() )
|
|
|
|
{
|
|
|
|
stdoutbuf += line;
|
|
|
|
insertItem( new ProcessListBoxItem( TQString::fromLocal8Bit(stdoutbuf),
|
|
|
|
ProcessListBoxItem::Normal ),
|
|
|
|
lastRowStdout+1 );
|
|
|
|
stdoutbuf.truncate( 0 );
|
|
|
|
}else
|
|
|
|
{
|
|
|
|
insertItem( new ProcessListBoxItem( TQString::fromLocal8Bit( line ),
|
|
|
|
ProcessListBoxItem::Normal) );
|
|
|
|
}
|
|
|
|
lastRowStdout = count() - 1;
|
|
|
|
maybeScrollToBottom();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessWidget::insertStderrLine(const TQCString &line)
|
|
|
|
{
|
|
|
|
if( !stderrbuf.isEmpty() )
|
|
|
|
{
|
|
|
|
stderrbuf += line;
|
|
|
|
insertItem( new ProcessListBoxItem( TQString::fromLocal8Bit( stderrbuf ),
|
|
|
|
ProcessListBoxItem::Error ),
|
|
|
|
lastRowStderr+1 );
|
|
|
|
stderrbuf.truncate( 0 );
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
insertItem( new ProcessListBoxItem( TQString::fromLocal8Bit( line ),
|
|
|
|
ProcessListBoxItem::Error) );
|
|
|
|
}
|
|
|
|
lastRowStderr = count() - 1;
|
|
|
|
maybeScrollToBottom();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessWidget::childFinished(bool normal, int status)
|
|
|
|
{
|
|
|
|
TQString s;
|
|
|
|
ProcessListBoxItem::Type t;
|
|
|
|
|
|
|
|
if (normal) {
|
|
|
|
if (status) {
|
|
|
|
s = i18n("*** Exited with status: %1 ***").tqarg(status);
|
|
|
|
t = ProcessListBoxItem::Error;
|
|
|
|
} else {
|
|
|
|
s = i18n("*** Exited normally ***");
|
|
|
|
t = ProcessListBoxItem::Diagnostic;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( childproc->signalled() && childproc->exitSignal() == SIGSEGV )
|
|
|
|
{
|
|
|
|
s = i18n("*** Process aborted. Segmentation fault ***");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s = i18n("*** Process aborted ***");
|
|
|
|
}
|
|
|
|
t = ProcessListBoxItem::Error;
|
|
|
|
}
|
|
|
|
|
|
|
|
insertItem(new ProcessListBoxItem(s, t));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQSize ProcessWidget::tqminimumSizeHint() const
|
|
|
|
{
|
|
|
|
// I'm not sure about this, but when I don't use override tqminimumSizeHint(),
|
|
|
|
// the initial size in clearly too small
|
|
|
|
|
|
|
|
return TQSize( TQListBox::tqsizeHint().width(),
|
|
|
|
(fontMetrics().lineSpacing()+2)*4 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Should be called right after an insertItem(),
|
|
|
|
will automatic scroll the listbox if it is already at the bottom
|
|
|
|
to prevent automatic scrolling when the user has scrolled up
|
|
|
|
*/
|
|
|
|
void ProcessWidget::maybeScrollToBottom()
|
|
|
|
{
|
|
|
|
if ( verticalScrollBar()->value() == verticalScrollBar()->maxValue() )
|
|
|
|
{
|
|
|
|
setBottomItem( count() -1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessWidget::addPartialStderrLine(const TQCString& linepart)
|
|
|
|
{
|
|
|
|
stderrbuf += linepart;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessWidget::addPartialStdoutLine(const TQCString& linepart)
|
|
|
|
{
|
|
|
|
stdoutbuf += linepart;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "processwidget.moc"
|