/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: http://www.qt-project.org/ ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** **************************************************************************/ #include "scriptmanager_p.h" #include "metatypedeclarations.h" #include #include #include #include #include #include #include #include #include #include #include #include // Script function template to pop up a message box // with a certain icon and buttons. template static QScriptValue messageBox(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() < 3) return QScriptValue(engine, -1); QWidget *parent = qscriptvalue_cast(context->argument(0)); const QString title = context->argument(1).toString(); const QString msg = context->argument(2).toString(); QMessageBox msgBox(static_cast(MsgBoxIcon), title, msg, static_cast(MsgBoxButtons), parent); return QScriptValue(engine, msgBox.exec()); } static QScriptValue inputDialogGetText(QScriptContext *context, QScriptEngine *engine) { const int argumentCount = context->argumentCount(); if (argumentCount < 3) return QScriptValue(engine, QScriptValue::NullValue); QWidget *parent = qscriptvalue_cast(context->argument(0)); const QString title = context->argument(1).toString(); const QString label = context->argument(2).toString(); const QString defaultValue = argumentCount > 3 ? context->argument(3).toString() : QString(); bool ok; const QString rc = QInputDialog::getText(parent, title, label, QLineEdit::Normal, defaultValue, &ok); if (!ok) return QScriptValue(engine, QScriptValue::NullValue); return QScriptValue(engine, rc); } static QScriptValue inputDialogGetInteger(QScriptContext *context, QScriptEngine *engine) { const int argumentCount = context->argumentCount(); if (argumentCount < 3) return QScriptValue(engine, QScriptValue::NullValue); QWidget *parent = qscriptvalue_cast(context->argument(0)); const QString title = context->argument(1).toString(); const QString label = context->argument(2).toString(); const int defaultValue = argumentCount > 3 ? context->argument(3).toInt32() : 0; const int minValue = argumentCount > 4 ? context->argument(4).toInt32() : INT_MIN; const int maxValue = argumentCount > 5 ? context->argument(5).toInt32() : INT_MAX; bool ok; const int rc = QInputDialog::getInteger(parent, title, label, defaultValue, minValue, maxValue, 1, &ok); if (!ok) return QScriptValue(engine, QScriptValue::NullValue); return QScriptValue(engine, rc); } static QScriptValue inputDialogGetDouble(QScriptContext *context, QScriptEngine *engine) { const int argumentCount = context->argumentCount(); if (argumentCount < 3) return QScriptValue(engine, QScriptValue::NullValue); QWidget *parent = qscriptvalue_cast(context->argument(0)); const QString title = context->argument(1).toString(); const QString label = context->argument(2).toString(); const double defaultValue = argumentCount > 3 ? context->argument(3).toNumber() : 0; // Use QInputDialog defaults const double minValue = argumentCount > 4 ? context->argument(4).toNumber() : INT_MIN; const double maxValue = argumentCount > 5 ? context->argument(5).toNumber() : INT_MAX; bool ok; const double rc = QInputDialog::getDouble(parent, title, label, defaultValue, minValue, maxValue, 1, &ok); if (!ok) return QScriptValue(engine, QScriptValue::NullValue); return QScriptValue(engine, rc); } static QScriptValue inputDialogGetItem(QScriptContext *context, QScriptEngine *engine) { const int argumentCount = context->argumentCount(); if (argumentCount < 4) return QScriptValue(engine, QScriptValue::NullValue); QWidget *parent = qscriptvalue_cast(context->argument(0)); const QString title = context->argument(1).toString(); const QString label = context->argument(2).toString(); const QStringList items = qscriptvalue_cast(context->argument(3)); const int defaultItem = argumentCount > 4 ? context->argument(4).toInt32() : 0; const bool editable = argumentCount > 5 ? context->argument(5).toInt32() : 0; bool ok; const QString rc = QInputDialog::getItem (parent, title, label, items, defaultItem, editable, &ok); if (!ok) return QScriptValue(engine, QScriptValue::NullValue); return QScriptValue(engine, rc); } // Script function template to pop up a file box // with a certain icon and buttons. template static QScriptValue fileBox(QScriptContext *context, QScriptEngine *engine) { const int argumentCount = context->argumentCount(); if (argumentCount < 2) return QScriptValue(engine, QScriptValue::NullValue); QWidget *parent = qscriptvalue_cast(context->argument(0)); const QString title = context->argument(1).toString(); const QString directory = argumentCount > 2 ? context->argument(2).toString() : QString(); const QString filter = argumentCount > 3 ? context->argument(3).toString() : QString(); QFileDialog fileDialog(parent, title, directory, filter); fileDialog.setAcceptMode(static_cast(TAcceptMode)); fileDialog.setFileMode (static_cast(TFileMode)); if (fileDialog.exec() == QDialog::Rejected) return QScriptValue(engine, QScriptValue::NullValue); const QStringList rc = fileDialog.selectedFiles(); QTC_CHECK(!rc.empty()); return TFileMode == QFileDialog::ExistingFiles ? engine->toScriptValue(rc) : engine->toScriptValue(rc.front()); } // ------ ScriptManagerPrivate namespace Core { namespace Internal { ScriptManagerPrivate::ScriptManagerPrivate(QObject *parent) : ScriptManager(parent), m_engine(0) { } // Split a backtrace of the form: // "(BuildManagerCommand(ls))@:0 // demoProjectExplorer()@:237 // ()@:276 // ()@:0" static void parseBackTrace(const QStringList &backTrace, ScriptManagerPrivate::Stack &stack) { const QChar at = QLatin1Char('@'); const QChar colon = QLatin1Char(':'); stack.clear(); foreach (const QString &line, backTrace) { const int atPos = line.lastIndexOf(at); if (atPos == -1) continue; const int colonPos = line.indexOf(colon, atPos + 1); if (colonPos == -1) continue; ScriptManagerPrivate::StackFrame frame; frame.function = line.left(atPos); frame.fileName = line.mid(atPos + 1, colonPos - atPos - 1); frame.lineNumber = line.right(line.size() - colonPos - 1).toInt(); stack.push_back(frame); } } bool ScriptManagerPrivate::runScript(const QString &script, QString *errorMessage) { Stack stack; return runScript(script, errorMessage, &stack); } bool ScriptManagerPrivate::runScript(const QString &script, QString *errorMessage, Stack *stack) { ensureEngineInitialized(); stack->clear(); m_engine->pushContext(); m_engine->evaluate(script); const bool failed = m_engine->hasUncaughtException (); if (failed) { const int errorLineNumber = m_engine->uncaughtExceptionLineNumber(); const QStringList backTrace = m_engine->uncaughtExceptionBacktrace(); parseBackTrace(backTrace, *stack); const QString backtrace = backTrace.join(QString(QLatin1Char('\n'))); *errorMessage = ScriptManager::tr("Exception at line %1: %2\n%3").arg(errorLineNumber).arg(engineError(m_engine)).arg(backtrace); } m_engine->popContext(); return !failed; } ScriptManager::QScriptEnginePtr ScriptManagerPrivate::ensureEngineInitialized() { if (!m_engine.isNull()) return m_engine; m_engine = QScriptEnginePtr(new QScriptEngine); // register QObjects that occur as properties SharedTools::registerQObject(m_engine.data()); SharedTools::registerQObject(m_engine.data()); SharedTools::registerQObject(m_engine.data()); qScriptRegisterSequenceMetaType >(m_engine.data()); // CLASSIC: registerInterfaceWithDefaultPrototype(m_engine); // Message box conveniences m_engine->globalObject().setProperty(QLatin1String("critical"), m_engine->newFunction(messageBox, 3)); m_engine->globalObject().setProperty(QLatin1String("warning"), m_engine->newFunction(messageBox, 3)); m_engine->globalObject().setProperty(QLatin1String("information"), m_engine->newFunction(messageBox, 3)); // StandardButtons has overloaded operator '|' - grrr. enum { MsgBoxYesNo = 0x00014000 }; m_engine->globalObject().setProperty(QLatin1String("yesNoQuestion"), m_engine->newFunction(messageBox, 3)); m_engine->globalObject().setProperty(QLatin1String("getText"), m_engine->newFunction(inputDialogGetText, 3)); m_engine->globalObject().setProperty(QLatin1String("getInteger"), m_engine->newFunction(inputDialogGetInteger, 3)); m_engine->globalObject().setProperty(QLatin1String("getDouble"), m_engine->newFunction(inputDialogGetDouble, 3)); m_engine->globalObject().setProperty(QLatin1String("getItem"), m_engine->newFunction(inputDialogGetItem, 3)); // file box m_engine->globalObject().setProperty(QLatin1String("getOpenFileNames"), m_engine->newFunction(fileBox , 2)); m_engine->globalObject().setProperty(QLatin1String("getOpenFileName"), m_engine->newFunction(fileBox , 2)); m_engine->globalObject().setProperty(QLatin1String("getSaveFileName"), m_engine->newFunction(fileBox , 2)); m_engine->globalObject().setProperty(QLatin1String("getExistingDirectory"), m_engine->newFunction(fileBox , 2)); return m_engine; } QString ScriptManagerPrivate::engineError(const QScriptEnginePtr &scriptEngine) { QScriptValue error = scriptEngine->evaluate(QLatin1String("Error")); if (error.isValid()) return error.toString(); return ScriptManager::tr("Unknown error"); } } // namespace Internal } // namespace Core