/* This file is part of the KDE project Copyright (C) 1999-2001 Bernd Gehrmann 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 #include #include #include #include #include #include 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 *tqparent, const char *name) : KListBox(tqparent, 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"