You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/lib/widgets/processwidget.cpp

284 lines
7.7 KiB

/* This file is part of the KDE project
Copyright (C) 1999-2001 Bernd Gehrmann <bernd@tdevelop.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 <tdeversion.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 ***").arg(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::minimumSizeHint() const
{
// I'm not sure about this, but when I don't use override minimumSizeHint(),
// the initial size in clearly too small
return TQSize( TQListBox::sizeHint().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"