/*************************************************************************** * Copyright (c) 2004 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * 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 "PreCompiled.h" #ifndef _PreComp_ # include # include # include # include # include # include #endif #include #include "ReportView.h" #include "FileDialog.h" #include "PythonConsole.h" #include "PythonConsolePy.h" #include "BitmapFactory.h" using namespace Gui; using namespace Gui::DockWnd; /* TRANSLATOR Gui::DockWnd::ReportView */ /** * Constructs a ReportView which is a child of 'parent', with the * name 'name' and widget flags set to 'f' */ ReportView::ReportView( QWidget* parent ) : QWidget(parent) { setObjectName(QLatin1String("ReportOutput")); resize( 529, 162 ); QGridLayout* tabLayout = new QGridLayout( this ); tabLayout->setSpacing( 0 ); tabLayout->setMargin( 0 ); tabWidget = new QTabWidget( this ); tabWidget->setObjectName(QString::fromUtf8("tabWidget")); tabWidget->setTabPosition(QTabWidget::South); tabWidget->setTabShape(QTabWidget::Rounded); tabLayout->addWidget( tabWidget, 0, 0 ); // create the output window tabOutput = new ReportOutput(); tabOutput->setWindowTitle(trUtf8("Output")); tabOutput->setWindowIcon(BitmapFactory().pixmap("MacroEditor")); int output = tabWidget->addTab(tabOutput, tabOutput->windowTitle()); tabWidget->setTabIcon(output, tabOutput->windowIcon()); // create the python console tabPython = new PythonConsole(); tabPython->setWordWrapMode(QTextOption::NoWrap); tabPython->setWindowTitle(trUtf8("Python console")); tabPython->setWindowIcon(BitmapFactory().iconFromTheme("applications-python")); int python = tabWidget->addTab(tabPython, tabPython->windowTitle()); tabWidget->setTabIcon(python, tabPython->windowIcon()); tabWidget->setCurrentIndex(0); // raise the tab page set in the preferences ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General"); int index = hGrp->GetInt("AutoloadTab", 0); tabWidget->setCurrentIndex(index); } /** * Destroys the object and frees any allocated resources */ ReportView::~ReportView() { // no need to delete child widgets, Qt does it all for us } void ReportView::changeEvent(QEvent *e) { QWidget::changeEvent(e); if (e->type() == QEvent::LanguageChange) { tabOutput->setWindowTitle(trUtf8("Output")); tabPython->setWindowTitle(trUtf8("Python console")); for (int i=0; icount();i++) tabWidget->setTabText(i, tabWidget->widget(i)->windowTitle()); } } // ---------------------------------------------------------- namespace Gui { struct TextBlockData : public QTextBlockUserData { struct State { int length; ReportHighlighter::Paragraph type; }; QVector block; }; } ReportHighlighter::ReportHighlighter(QTextEdit* edit) : QSyntaxHighlighter(edit), type(Message) { QPalette pal = edit->palette(); txtCol = pal.windowText().color(); logCol = Qt::blue; warnCol = QColor(255, 170, 0); errCol = Qt::red; } ReportHighlighter::~ReportHighlighter() { } void ReportHighlighter::highlightBlock (const QString & text) { if (text.isEmpty()) return; TextBlockData* ud = static_cast(this->currentBlockUserData()); if (!ud) { ud = new TextBlockData; this->setCurrentBlockUserData(ud); } TextBlockData::State b; b.length = text.length(); b.type = this->type; ud->block.append(b); QVector block = ud->block; int start = 0; for (QVector::Iterator it = block.begin(); it != block.end(); ++it) { switch (it->type) { case Message: setFormat(start, it->length-start, txtCol); break; case Warning: setFormat(start, it->length-start, warnCol); break; case Error: setFormat(start, it->length-start, errCol); break; case LogText: setFormat(start, it->length-start, logCol); break; default: break; } start = it->length; } } void ReportHighlighter::setParagraphType(ReportHighlighter::Paragraph t) { type = t; } void ReportHighlighter::setTextColor( const QColor& col ) { txtCol = col; } void ReportHighlighter::setLogColor( const QColor& col ) { logCol = col; } void ReportHighlighter::setWarningColor( const QColor& col ) { warnCol = col; } void ReportHighlighter::setErrorColor( const QColor& col ) { errCol = col; } // ---------------------------------------------------------- /** * The CustomReportEvent class is used to send report events in the methods Log(), * Error(), Warning() and Message() of the ReportOutput class to itself instead of * printing the messages directly in its text view. * * This makes the methods Log(), Error(), Warning() and Message() thread-safe. * @author Werner Mayer */ class CustomReportEvent : public QEvent { public: CustomReportEvent(ReportHighlighter::Paragraph p, const QString& s) : QEvent(QEvent::Type(QEvent::User)) { par = p; msg = s;} ~CustomReportEvent() { } const QString& message() const { return msg; } ReportHighlighter::Paragraph messageType() const { return par; } private: ReportHighlighter::Paragraph par; QString msg; }; // ---------------------------------------------------------- class ReportOutput::Data { public: Data() { if (!default_stdout) { Base::PyGILStateLocker lock; default_stdout = PySys_GetObject(const_cast("stdout")); replace_stdout = new OutputStdout(); redirected_stdout = false; } if (!default_stderr) { Base::PyGILStateLocker lock; default_stderr = PySys_GetObject(const_cast("stderr")); replace_stderr = new OutputStderr(); redirected_stderr = false; } } ~Data() { if (replace_stdout) { Py_DECREF(replace_stdout); replace_stdout = 0; } if (replace_stderr) { Py_DECREF(replace_stderr); replace_stderr = 0; } } // make them static because redirection should done only once static bool redirected_stdout; static PyObject* default_stdout; static PyObject* replace_stdout; static bool redirected_stderr; static PyObject* default_stderr; static PyObject* replace_stderr; }; bool ReportOutput::Data::redirected_stdout = false; PyObject* ReportOutput::Data::default_stdout = 0; PyObject* ReportOutput::Data::replace_stdout = 0; bool ReportOutput::Data::redirected_stderr = false; PyObject* ReportOutput::Data::default_stderr = 0; PyObject* ReportOutput::Data::replace_stderr = 0; /* TRANSLATOR Gui::DockWnd::ReportOutput */ /** * Constructs a ReportOutput which is a child of 'parent', with the * name 'name' and widget flags set to 'f' */ ReportOutput::ReportOutput(QWidget* parent) : QTextEdit(parent), WindowParameter("OutputWindow"), d(new Data), gotoEnd(false) { bLog = false; reportHl = new ReportHighlighter(this); restoreFont(); setReadOnly(true); clear(); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); Base::Console().AttachObserver(this); getWindowParameter()->Attach(this); getWindowParameter()->NotifyAll(); _prefs = WindowParameter::getDefaultParameter()->GetGroup("Editor"); _prefs->Attach(this); _prefs->Notify("FontSize"); // scroll to bottom at startup to make sure that last appended text is visible ensureCursorVisible(); } /** * Destroys the object and frees any allocated resources */ ReportOutput::~ReportOutput() { getWindowParameter()->Detach(this); _prefs->Detach(this); Base::Console().DetachObserver(this); delete reportHl; delete d; } void ReportOutput::restoreFont() { QFont serifFont(QLatin1String("Courier"), 10, QFont::Normal); setFont(serifFont); } void ReportOutput::Warning(const char * s) { // Send the event to itself to allow thread-safety. Qt will delete it when done. CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::Warning, QString::fromUtf8(s)); QApplication::postEvent(this, ev); } void ReportOutput::Message(const char * s) { // Send the event to itself to allow thread-safety. Qt will delete it when done. CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::Message, QString::fromUtf8(s)); QApplication::postEvent(this, ev); } void ReportOutput::Error (const char * s) { // Send the event to itself to allow thread-safety. Qt will delete it when done. CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::Error, QString::fromUtf8(s)); QApplication::postEvent(this, ev); } void ReportOutput::Log (const char * s) { QString msg = QString::fromUtf8(s); if (msg.length() < 1000){ // Send the event to itself to allow thread-safety. Qt will delete it when done. CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::LogText, msg); QApplication::postEvent(this, ev); } } void ReportOutput::customEvent ( QEvent* ev ) { // Appends the text stored in the event to the text view if ( ev->type() == QEvent::User ) { CustomReportEvent* ce = (CustomReportEvent*)ev; reportHl->setParagraphType(ce->messageType()); QTextCursor cursor(this->document()); cursor.beginEditBlock(); cursor.movePosition(QTextCursor::End); cursor.insertText(ce->message()); cursor.endEditBlock(); if (gotoEnd) { setTextCursor(cursor); } ensureCursorVisible(); } } void ReportOutput::changeEvent(QEvent *ev) { if (ev->type() == QEvent::StyleChange) { QPalette pal = palette(); QColor color = pal.windowText().color(); unsigned long text = (color.red() << 24) | (color.green() << 16) | (color.blue() << 8); // if this parameter is not already set use the style's window text color text = getWindowParameter()->GetUnsigned("colorText", text); getWindowParameter()->SetUnsigned("colorText", text); } QTextEdit::changeEvent(ev); } void ReportOutput::contextMenuEvent ( QContextMenuEvent * e ) { QMenu* menu = createStandardContextMenu(); QAction* first = menu->actions().front(); QMenu* submenu = new QMenu( menu ); QAction* logAct = submenu->addAction(tr("Logging"), this, SLOT(onToggleLogging())); logAct->setCheckable(true); logAct->setChecked(bLog); QAction* wrnAct = submenu->addAction(tr("Warning"), this, SLOT(onToggleWarning())); wrnAct->setCheckable(true); wrnAct->setChecked(bWrn); QAction* errAct = submenu->addAction(tr("Error"), this, SLOT(onToggleError())); errAct->setCheckable(true); errAct->setChecked(bErr); submenu->addSeparator(); QAction* stdoutAct = submenu->addAction(tr("Redirect Python output"), this, SLOT(onToggleRedirectPythonStdout())); stdoutAct->setCheckable(true); stdoutAct->setChecked(d->redirected_stdout); QAction* stderrAct = submenu->addAction(tr("Redirect Python errors"), this, SLOT(onToggleRedirectPythonStderr())); stderrAct->setCheckable(true); stderrAct->setChecked(d->redirected_stderr); submenu->addSeparator(); QAction* botAct = submenu->addAction(tr("Go to end"), this, SLOT(onToggleGoToEnd())); botAct->setCheckable(true); botAct->setChecked(gotoEnd); submenu->setTitle(tr("Options")); menu->insertMenu(first, submenu); menu->insertSeparator(first); menu->addAction(tr("Clear"), this, SLOT(clear())); menu->addSeparator(); menu->addAction(tr("Save As..."), this, SLOT(onSaveAs())); menu->exec(e->globalPos()); delete menu; } void ReportOutput::onSaveAs() { QString fn = QFileDialog::getSaveFileName(this, tr("Save Report Output"), QString(), QString::fromLatin1("%1 (*.txt *.log)").arg(tr("Plain Text Files"))); if (!fn.isEmpty()) { QFileInfo fi(fn); if (fi.completeSuffix().isEmpty()) fn += QLatin1String(".log"); QFile f(fn); if (f.open(QIODevice::WriteOnly)) { QTextStream t (&f); t << toPlainText(); f.close(); } } } bool ReportOutput::isError() const { return bErr; } bool ReportOutput::isWarning() const { return bWrn; } bool ReportOutput::isLogging() const { return bLog; } void ReportOutput::onToggleError() { bErr = bErr ? false : true; getWindowParameter()->SetBool( "checkError", bErr ); } void ReportOutput::onToggleWarning() { bWrn = bWrn ? false : true; getWindowParameter()->SetBool( "checkWarning", bWrn ); } void ReportOutput::onToggleLogging() { bLog = bLog ? false : true; getWindowParameter()->SetBool( "checkLogging", bLog ); } void ReportOutput::onToggleRedirectPythonStdout() { if (d->redirected_stdout) { d->redirected_stdout = false; Base::PyGILStateLocker lock; PySys_SetObject(const_cast("stdout"), d->default_stdout); } else { d->redirected_stdout = true; Base::PyGILStateLocker lock; PySys_SetObject(const_cast("stdout"), d->replace_stdout); } getWindowParameter()->SetBool("RedirectPythonOutput", d->redirected_stdout); } void ReportOutput::onToggleRedirectPythonStderr() { if (d->redirected_stderr) { d->redirected_stderr = false; Base::PyGILStateLocker lock; PySys_SetObject(const_cast("stderr"), d->default_stderr); } else { d->redirected_stderr = true; Base::PyGILStateLocker lock; PySys_SetObject(const_cast("stderr"), d->replace_stderr); } getWindowParameter()->SetBool("RedirectPythonErrors", d->redirected_stderr); } void ReportOutput::onToggleGoToEnd() { gotoEnd = gotoEnd ? false : true; getWindowParameter()->SetBool( "checkGoToEnd", gotoEnd ); } void ReportOutput::OnChange(Base::Subject &rCaller, const char * sReason) { ParameterGrp& rclGrp = ((ParameterGrp&)rCaller); if (strcmp(sReason, "checkLogging") == 0) { bLog = rclGrp.GetBool( sReason, bLog ); } else if (strcmp(sReason, "checkWarning") == 0) { bWrn = rclGrp.GetBool( sReason, bWrn ); } else if (strcmp(sReason, "checkError") == 0) { bErr = rclGrp.GetBool( sReason, bErr ); } else if (strcmp(sReason, "colorText") == 0) { unsigned long col = rclGrp.GetUnsigned( sReason ); reportHl->setTextColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) ); } else if (strcmp(sReason, "colorLogging") == 0) { unsigned long col = rclGrp.GetUnsigned( sReason ); reportHl->setLogColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) ); } else if (strcmp(sReason, "colorWarning") == 0) { unsigned long col = rclGrp.GetUnsigned( sReason ); reportHl->setWarningColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) ); } else if (strcmp(sReason, "colorError") == 0) { unsigned long col = rclGrp.GetUnsigned( sReason ); reportHl->setErrorColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) ); } else if (strcmp(sReason, "checkGoToEnd") == 0) { gotoEnd = rclGrp.GetBool(sReason, gotoEnd); } else if (strcmp(sReason, "FontSize") == 0 || strcmp(sReason, "Font") == 0) { int fontSize = rclGrp.GetInt("FontSize", 10); QString fontFamily = QString::fromLatin1(rclGrp.GetASCII("Font", "Courier").c_str()); QFont font(fontFamily, fontSize); setFont(font); QFontMetrics metric(font); int width = metric.width(QLatin1String("0000")); setTabStopWidth(width); } else if (strcmp(sReason, "RedirectPythonOutput") == 0) { bool checked = rclGrp.GetBool(sReason); if (checked != d->redirected_stdout) onToggleRedirectPythonStdout(); } else if (strcmp(sReason, "RedirectPythonErrors") == 0) { bool checked = rclGrp.GetBool(sReason); if (checked != d->redirected_stderr) onToggleRedirectPythonStderr(); } } #include "moc_ReportView.cpp"