diff options
Diffstat (limited to 'lib/widgets/processwidget.cpp')
-rw-r--r-- | lib/widgets/processwidget.cpp | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/lib/widgets/processwidget.cpp b/lib/widgets/processwidget.cpp new file mode 100644 index 00000000..54dd1751 --- /dev/null +++ b/lib/widgets/processwidget.cpp @@ -0,0 +1,283 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "processwidget.h" +#include "processlinemaker.h" + +#include <kdeversion.h> +#include <qdir.h> +#include <kdebug.h> +#include <klocale.h> +#include <kprocess.h> +#include <qpainter.h> +#include <qapplication.h> + + +ProcessListBoxItem::ProcessListBoxItem(const QString &s, Type type) + : QListBoxText(s), t(type) +{ + QString clean = s; + clean.replace( QChar('\t'), QString(" ") ); + clean.replace( QChar('\n'), QString() ); + clean.replace( QChar('\r'), QString() ); + 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; +} + +QColor ProcessListBoxItem::blend(const QColor &c1, const QColor &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 QColor(qRgb(r, g, b)); +} + +void ProcessListBoxItem::paint(QPainter *p) +{ + QColor dim, warn, err, back; + if (listBox()) { + const QColorGroup& group = listBox()->palette().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 = Qt::black; + dim = Qt::darkBlue; + err = Qt::darkRed; + if (isSelected()) + back = Qt::lightGray; + else + back = Qt::white; + } + p->fillRect(p->window(), QBrush(back)); + p->setPen((t==Error)? err : + (t==Diagnostic)? warn : dim); + QListBoxText::paint(p); +} + + +ProcessWidget::ProcessWidget(QWidget *parent, const char *name) + : KListBox(parent, name) +{ + setFocusPolicy(QWidget::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, SIGNAL(receivedStdoutLine(const QCString&)), + this, SLOT(insertStdoutLine(const QCString&) )); + connect( procLineMaker, SIGNAL(receivedStderrLine(const QCString&)), + this, SLOT(insertStderrLine(const QCString&) )); + connect( procLineMaker, SIGNAL(receivedPartialStdoutLine(const QCString&)), + this, SLOT(addPartialStdoutLine(const QCString&) )); + connect( procLineMaker, SIGNAL(receivedPartialStderrLine(const QCString&)), + this, SLOT(addPartialStderrLine(const QCString&) )); + + connect(childproc, SIGNAL(processExited(KProcess*)), + this, SLOT(slotProcessExited(KProcess*) )) ; +} + + +ProcessWidget::~ProcessWidget() +{ + delete childproc; + delete procLineMaker; +} + + +void ProcessWidget::startJob(const QString &dir, const QString &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 QCString &line) +{ + if( !stdoutbuf.isEmpty() ) + { + stdoutbuf += line; + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit(stdoutbuf), + ProcessListBoxItem::Normal ), + lastRowStdout+1 ); + stdoutbuf.truncate( 0 ); + }else + { + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit( line ), + ProcessListBoxItem::Normal) ); + } + lastRowStdout = count() - 1; + maybeScrollToBottom(); +} + + +void ProcessWidget::insertStderrLine(const QCString &line) +{ + if( !stderrbuf.isEmpty() ) + { + stderrbuf += line; + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit( stderrbuf ), + ProcessListBoxItem::Error ), + lastRowStderr+1 ); + stderrbuf.truncate( 0 ); + } else + { + insertItem( new ProcessListBoxItem( QString::fromLocal8Bit( line ), + ProcessListBoxItem::Error) ); + } + lastRowStderr = count() - 1; + maybeScrollToBottom(); +} + + +void ProcessWidget::childFinished(bool normal, int status) +{ + QString 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)); +} + + +QSize 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 QSize( QListBox::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 QCString& linepart) +{ + stderrbuf += linepart; +} + +void ProcessWidget::addPartialStdoutLine(const QCString& linepart) +{ + stdoutbuf += linepart; +} + +#include "processwidget.moc" |