1 /* 2 Actiona 3 Copyright (C) 2005 Jonathan Mercier-Ganady 4 5 Actiona is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 Actiona is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 Contact : jmgr@jmgr.info 19 */ 20 21 #include "executer.h" 22 #include "script.h" 23 #include "actionfactory.h" 24 #include "executionwindow.h" 25 #include "codeinitializer.h" 26 #include "actiondefinition.h" 27 #include "actionexception.h" 28 #include "scriptagent.h" 29 #include "actioninstance.h" 30 #include "code/codetools.h" 31 #include "code/image.h" 32 #include "code/rawdata.h" 33 #include "code/prettyprinting.h" 34 #include "codeactiona.h" 35 36 #ifdef ACT_PROFILE 37 #include "highresolutiontimer.h" 38 #endif 39 40 #include <QDesktopWidget> 41 #include <QAction> 42 #include <QMainWindow> 43 #include <QApplication> 44 #include <QLocale> 45 #include <QProgressDialog> 46 #include <QScriptEngine> 47 #include <QScriptValueIterator> 48 49 namespace LibExecuter 50 { 51 Executer::ExecutionStatus Executer::mExecutionStatus = Executer::Stopped; 52 Executer(QObject * parent)53 Executer::Executer(QObject *parent) 54 : QObject(parent), 55 mExecutionWindow(new ExecutionWindow()), 56 mConsoleWidget(new ActionTools::ConsoleWidget()) 57 58 { 59 connect(mExecutionWindow, &ExecutionWindow::canceled, this, &Executer::stopExecution); 60 connect(mExecutionWindow, &ExecutionWindow::paused, this, &Executer::pauseExecution); 61 connect(mExecutionWindow, &ExecutionWindow::debug, this, &Executer::debugExecution); 62 connect(&mExecutionTimer, &QTimer::timeout, this, &Executer::updateTimerProgress); 63 connect(&mScriptEngineDebugger, &QScriptEngineDebugger::evaluationSuspended, mExecutionWindow, &ExecutionWindow::onEvaluationPaused); 64 connect(&mScriptEngineDebugger, &QScriptEngineDebugger::evaluationResumed, mExecutionWindow, &ExecutionWindow::onEvaluationResumed); 65 connect(&mScriptEngineDebugger, &QScriptEngineDebugger::evaluationSuspended, this, &Executer::executionPaused); 66 connect(&mScriptEngineDebugger, &QScriptEngineDebugger::evaluationResumed, this, &Executer::executionResumed); 67 68 mScriptEngineDebugger.setAutoShowStandardWindow(false); 69 70 mConsoleWidget->setWindowFlags(Qt::Tool | 71 Qt::WindowStaysOnTopHint | 72 Qt::CustomizeWindowHint | 73 Qt::WindowTitleHint); 74 } 75 ~Executer()76 Executer::~Executer() 77 { 78 delete mExecutionWindow; 79 delete mConsoleWidget; 80 } 81 setup(ActionTools::Script * script,ActionTools::ActionFactory * actionFactory,bool showExecutionWindow,int executionWindowPosition,int executionWindowScreen,bool showConsoleWindow,int consoleWindowPosition,int consoleWindowScreen,int pauseBefore,int pauseAfter,Tools::Version actionaVersion,Tools::Version scriptVersion,bool isActExec,QStandardItemModel * consoleModel)82 void Executer::setup(ActionTools::Script *script, 83 ActionTools::ActionFactory *actionFactory, 84 bool showExecutionWindow, 85 int executionWindowPosition, 86 int executionWindowScreen, 87 bool showConsoleWindow, 88 int consoleWindowPosition, 89 int consoleWindowScreen, 90 int pauseBefore, 91 int pauseAfter, 92 Tools::Version actionaVersion, 93 Tools::Version scriptVersion, 94 bool isActExec, 95 QStandardItemModel *consoleModel) 96 { 97 mScript = script; 98 mScriptEngine = new QScriptEngine(this); 99 100 for(QString extension: mScriptEngine->availableExtensions()) 101 mScriptEngine->importExtension(extension); 102 103 mActionFactory = actionFactory; 104 mShowExecutionWindow = showExecutionWindow; 105 mExecutionWindowPosition = executionWindowPosition; 106 mExecutionWindowScreen = executionWindowScreen; 107 mShowConsoleWindow = showConsoleWindow; 108 mConsoleWindowPosition = consoleWindowPosition; 109 mConsoleWindowScreen = consoleWindowScreen; 110 mPauseBefore = pauseBefore; 111 mPauseAfter = pauseAfter; 112 mCurrentActionIndex = 0; 113 mExecutionStarted = false; 114 mExecutionEnded = false; 115 mExecuteOnlySelection = false; 116 mProgressDialog = nullptr; 117 mActiveActionsCount = 0; 118 mExecutionPaused = false; 119 mActionaVersion = actionaVersion; 120 mScriptVersion = scriptVersion; 121 mIsActExec = isActExec; 122 123 Code::setupPrettyPrinting(*mScriptEngine); 124 125 mScriptEngineDebugger.attachTo(mScriptEngine); 126 mDebuggerWindow = mScriptEngineDebugger.standardWindow(); 127 128 mScriptAgent = new ScriptAgent(mScriptEngine); 129 130 connect(mScriptAgent, &ScriptAgent::executionStopped, this, &Executer::stopExecution); 131 connect(mScriptAgent, &ScriptAgent::evaluationStarted, mExecutionWindow, &ExecutionWindow::enableDebug); 132 connect(mScriptAgent, &ScriptAgent::evaluationStopped, mExecutionWindow, &ExecutionWindow::disableDebug); 133 134 QScriptEngineAgent *debuggerAgent = mScriptEngine->agent(); 135 mScriptEngine->setAgent(mScriptAgent); 136 mScriptAgent->setDebuggerAgent(debuggerAgent); 137 138 mConsoleWidget->setup(consoleModel); 139 140 mExecutionTimer.setSingleShot(false); 141 mExecutionTimer.setInterval(5); 142 mConsoleWidget->updateClearButton(); 143 } 144 currentActionInstance() const145 ActionTools::ActionInstance *Executer::currentActionInstance() const 146 { 147 if(mCurrentActionIndex < 0 || mCurrentActionIndex >= mScript->actionCount()) 148 return nullptr; 149 150 return mScript->actionAt(mCurrentActionIndex); 151 } 152 printCall(QScriptContext * context,ActionTools::ConsoleWidget::Type type)153 void printCall(QScriptContext *context, ActionTools::ConsoleWidget::Type type) 154 { 155 QApplication::processEvents();//Call this to prevent UI freeze when calling print often 156 157 QScriptValue calleeData = context->callee().data(); 158 auto executer = qobject_cast<Executer *>(calleeData.toQObject()); 159 QString message; 160 ScriptAgent *agent = executer->scriptAgent(); 161 162 if(!agent) 163 return; 164 165 for(int argumentIndex = 0; argumentIndex < context->argumentCount(); ++argumentIndex) 166 message += context->argument(argumentIndex).toString(); 167 168 switch(executer->scriptAgent()->context()) 169 { 170 case ScriptAgent::Parameters: 171 executer->consoleWidget()->addScriptParameterLine(message, 172 agent->currentParameter(), 173 agent->currentLine(), 174 agent->currentColumn(), 175 type); 176 break; 177 case ScriptAgent::Actions: 178 { 179 ActionTools::ActionInstance *currentAction = executer->script()->actionAt(executer->currentActionIndex()); 180 qint64 currentActionRuntimeId = -1; 181 if(currentAction) 182 currentActionRuntimeId = currentAction->runtimeId(); 183 184 executer->consoleWidget()->addUserLine(message, 185 currentActionRuntimeId, 186 context->engine()->globalObject().property(QStringLiteral("currentParameter")).toString(), 187 context->engine()->globalObject().property(QStringLiteral("currentSubParameter")).toString(), 188 agent->currentLine(), 189 agent->currentColumn(), 190 context->backtrace(), 191 type); 192 } 193 break; 194 default: 195 return; 196 } 197 } 198 printFunction(QScriptContext * context,QScriptEngine * engine)199 QScriptValue printFunction(QScriptContext *context, QScriptEngine *engine) 200 { 201 if(!Executer::isExecuterRunning()) 202 return QScriptValue(); 203 204 if(context->argumentCount() < 1) 205 return engine->undefinedValue(); 206 207 printCall(context, ActionTools::ConsoleWidget::Information); 208 209 return engine->undefinedValue(); 210 } 211 printWarningFunction(QScriptContext * context,QScriptEngine * engine)212 QScriptValue printWarningFunction(QScriptContext *context, QScriptEngine *engine) 213 { 214 if(!Executer::isExecuterRunning()) 215 return QScriptValue(); 216 217 if(context->argumentCount() < 1) 218 return engine->undefinedValue(); 219 220 printCall(context, ActionTools::ConsoleWidget::Warning); 221 222 return engine->undefinedValue(); 223 } 224 printErrorFunction(QScriptContext * context,QScriptEngine * engine)225 QScriptValue printErrorFunction(QScriptContext *context, QScriptEngine *engine) 226 { 227 if(!Executer::isExecuterRunning()) 228 return QScriptValue(); 229 230 if(context->argumentCount() < 1) 231 return engine->undefinedValue(); 232 233 printCall(context, ActionTools::ConsoleWidget::Error); 234 235 return engine->undefinedValue(); 236 } 237 clearConsoleFunction(QScriptContext * context,QScriptEngine * engine)238 QScriptValue clearConsoleFunction(QScriptContext *context, QScriptEngine *engine) 239 { 240 if(!Executer::isExecuterRunning()) 241 return QScriptValue(); 242 243 QApplication::processEvents();//Call this to prevent UI freeze when calling clear often 244 245 QScriptValue calleeData = context->callee().data(); 246 auto executer = qobject_cast<Executer *>(calleeData.toQObject()); 247 248 executer->consoleWidget()->clearExceptSeparators(); 249 250 return engine->undefinedValue(); 251 } 252 callProcedureFunction(QScriptContext * context,QScriptEngine * engine)253 QScriptValue callProcedureFunction(QScriptContext *context, QScriptEngine *engine) 254 { 255 if(!Executer::isExecuterRunning()) 256 return QScriptValue(); 257 258 if(context->argumentCount() < 1) 259 return engine->undefinedValue(); 260 261 QScriptValue calleeData = context->callee().data(); 262 auto executer = qobject_cast<Executer *>(calleeData.toQObject()); 263 ActionTools::ActionInstance *currentActionInstance = executer->currentActionInstance(); 264 265 if(currentActionInstance) 266 currentActionInstance->callProcedure(context->argument(0).toString()); 267 268 return engine->undefinedValue(); 269 } 270 startExecution(bool onlySelection,const QString & filename)271 bool Executer::startExecution(bool onlySelection, const QString &filename) 272 { 273 Q_ASSERT(mScriptAgent); 274 Q_ASSERT(mScriptEngine); 275 276 #ifdef ACT_PROFILE 277 Tools::HighResolutionTimer timer("Executer::startExecution"); 278 #endif 279 280 Code::CodeTools::addClassToScriptEngine<CodeActiona>(QStringLiteral("Actiona"), mScriptEngine); 281 CodeActiona::setActExec(mIsActExec); 282 CodeActiona::setActionaVersion(mActionaVersion); 283 CodeActiona::setScriptVersion(mScriptVersion); 284 Code::CodeTools::addClassGlobalFunctionToScriptEngine(QStringLiteral("Actiona"), &CodeActiona::version, QStringLiteral("version"), mScriptEngine); 285 Code::CodeTools::addClassGlobalFunctionToScriptEngine(QStringLiteral("Actiona"), &CodeActiona::scriptVersion, QStringLiteral("scriptVersion"), mScriptEngine); 286 Code::CodeTools::addClassGlobalFunctionToScriptEngine(QStringLiteral("Actiona"), &CodeActiona::isActExec, QStringLiteral("isActExec"), mScriptEngine); 287 Code::CodeTools::addClassGlobalFunctionToScriptEngine(QStringLiteral("Actiona"), &CodeActiona::isActiona, QStringLiteral("isActiona"), mScriptEngine); 288 289 mScriptAgent->setContext(ScriptAgent::ActionInit); 290 CodeInitializer::initialize(mScriptEngine, mScriptAgent, mActionFactory, filename); 291 mScriptAgent->setContext(ScriptAgent::Parameters); 292 293 QScriptValue script = mScriptEngine->newObject(); 294 mScriptEngine->globalObject().setProperty(QStringLiteral("Script"), script, QScriptValue::ReadOnly); 295 script.setProperty(QStringLiteral("nextLine"), 1); 296 script.setProperty(QStringLiteral("doNotResetPreviousActions"), false); 297 script.setProperty(QStringLiteral("line"), 1, QScriptValue::ReadOnly); 298 QScriptValue callProcedureFun = mScriptEngine->newFunction(callProcedureFunction); 299 callProcedureFun.setData(mScriptEngine->newQObject(this)); 300 script.setProperty(QStringLiteral("callProcedure"), callProcedureFun); 301 302 QScriptValue console = mScriptEngine->newObject(); 303 mScriptEngine->globalObject().setProperty(QStringLiteral("Console"), console, QScriptValue::ReadOnly); 304 305 QScriptValue function = mScriptEngine->newFunction(printFunction); 306 function.setData(mScriptEngine->newQObject(this)); 307 console.setProperty(QStringLiteral("print"), function); 308 309 function = mScriptEngine->newFunction(printWarningFunction); 310 function.setData(mScriptEngine->newQObject(this)); 311 console.setProperty(QStringLiteral("printWarning"), function); 312 313 function = mScriptEngine->newFunction(printErrorFunction); 314 function.setData(mScriptEngine->newQObject(this)); 315 console.setProperty(QStringLiteral("printError"), function); 316 317 function = mScriptEngine->newFunction(clearConsoleFunction); 318 function.setData(mScriptEngine->newQObject(this)); 319 console.setProperty(QStringLiteral("clear"), function); 320 321 mExecuteOnlySelection = onlySelection; 322 mCurrentActionIndex = 0; 323 mActiveActionsCount = 0; 324 mExecutionPaused = false; 325 326 bool initSucceeded = true; 327 int lastBeginProcedure = -1; 328 329 mScript->clearProcedures(); 330 mScript->clearCallStack(); 331 332 const QMap<QString, ActionTools::Resource> &resources = mScript->resources(); 333 for(const QString &key: resources.keys()) 334 { 335 const ActionTools::Resource &resource = resources.value(key); 336 QScriptValue value; 337 338 switch(resource.type()) 339 { 340 case ActionTools::Resource::BinaryType: 341 case ActionTools::Resource::TypeCount: 342 value = Code::RawData::constructor(resource.data(), mScriptEngine); 343 break; 344 case ActionTools::Resource::TextType: 345 value = QString::fromUtf8(resource.data()); 346 break; 347 case ActionTools::Resource::ImageType: 348 { 349 QImage image; 350 351 if(!image.loadFromData(resource.data())) 352 { 353 mConsoleWidget->addResourceLine(tr("Invalid image resource"), key, ActionTools::ConsoleWidget::Error); 354 355 return false; 356 } 357 358 value = Code::Image::constructor(image, mScriptEngine); 359 } 360 break; 361 } 362 363 mScriptEngine->globalObject().setProperty(key, value, QScriptValue::ReadOnly | QScriptValue::Undeletable); 364 } 365 366 for(int actionIndex = 0; actionIndex < mScript->actionCount(); ++actionIndex) 367 { 368 ActionTools::ActionInstance *actionInstance = mScript->actionAt(actionIndex); 369 actionInstance->reset(); 370 actionInstance->clearRuntimeParameters(); 371 actionInstance->setupExecution(mScriptEngine, mScript, actionIndex); 372 mActionEnabled.append(true); 373 374 qint64 currentActionRuntimeId = -1; 375 if(actionInstance) 376 currentActionRuntimeId = actionInstance->runtimeId(); 377 378 if(canExecuteAction(actionIndex) == CanExecute) 379 { 380 ++mActiveActionsCount; 381 382 if(actionInstance->definition()->id() == QLatin1String("ActionBeginProcedure")) 383 { 384 if(lastBeginProcedure != -1) 385 { 386 mConsoleWidget->addActionLine(tr("Invalid Begin procedure action, you have to end the previous procedure before starting another one"), currentActionRuntimeId, QString(), QString(), -1, -1, ActionTools::ConsoleWidget::Error); 387 388 return false; 389 } 390 391 lastBeginProcedure = actionIndex; 392 393 const ActionTools::SubParameter &nameParameter = actionInstance->subParameter(QStringLiteral("name"), QStringLiteral("value")); 394 const QString &procedureName = nameParameter.value(); 395 396 if(procedureName.isEmpty()) 397 { 398 mConsoleWidget->addActionLine(tr("A procedure name cannot be empty"), currentActionRuntimeId, QString(), QString(), -1, -1, ActionTools::ConsoleWidget::Error); 399 400 return false; 401 } 402 403 if(mScript->findProcedure(procedureName) != -1) 404 { 405 mConsoleWidget->addActionLine(tr("A procedure with the name \"%1\" has already been declared").arg(procedureName), currentActionRuntimeId, QString(), QString(), -1, -1, ActionTools::ConsoleWidget::Error); 406 407 return false; 408 } 409 410 mScript->addProcedure(procedureName, actionIndex); 411 } 412 else if(actionInstance->definition()->id() == QLatin1String("ActionEndProcedure")) 413 { 414 if(lastBeginProcedure == -1) 415 { 416 mConsoleWidget->addActionLine(tr("Invalid End procedure"), currentActionRuntimeId, QString(), QString(), -1, -1, ActionTools::ConsoleWidget::Error); 417 418 return false; 419 } 420 421 ActionTools::ActionInstance *beginProcedureActionInstance = mScript->actionAt(lastBeginProcedure); 422 423 actionInstance->setRuntimeParameter(QStringLiteral("procedureBeginLine"), lastBeginProcedure); 424 beginProcedureActionInstance->setRuntimeParameter(QStringLiteral("procedureEndLine"), actionIndex); 425 426 lastBeginProcedure = -1; 427 } 428 } 429 } 430 431 if(lastBeginProcedure != -1) 432 { 433 ActionTools::ActionInstance *actionInstance = mScript->actionAt(lastBeginProcedure); 434 qint64 actionRuntimeId = -1; 435 if(actionInstance) 436 actionRuntimeId = actionInstance->runtimeId(); 437 438 mConsoleWidget->addActionLine(tr("Begin procedure action without end procedure"), actionRuntimeId, QString(), QString(), -1, -1, ActionTools::ConsoleWidget::Error); 439 440 return false; 441 } 442 443 for(int parameterIndex = 0; parameterIndex < mScript->parameterCount(); ++parameterIndex) 444 { 445 mScriptAgent->setCurrentParameter(parameterIndex); 446 447 const ActionTools::ScriptParameter &scriptParameter = mScript->parameter(parameterIndex); 448 QRegExp nameRegExp(QStringLiteral("[a-z_][a-z0-9_]*"), Qt::CaseInsensitive); 449 450 if(!nameRegExp.exactMatch(scriptParameter.name())) 451 { 452 mConsoleWidget->addScriptParameterLine(tr("Incorrect parameter name: \"%1\"").arg(scriptParameter.name()), 453 parameterIndex, 454 -1, 455 -1, 456 ActionTools::ConsoleWidget::Error); 457 initSucceeded = false; 458 continue; 459 } 460 461 QString value; 462 if(scriptParameter.isCode()) 463 { 464 QScriptValue result = mScriptEngine->evaluate(scriptParameter.value()); 465 if(result.isError()) 466 { 467 mConsoleWidget->addScriptParameterLine(tr(R"(Error while evaluating parameter "%1", error message: "%2")") 468 .arg(scriptParameter.name()) 469 .arg(result.toString()), 470 parameterIndex, 471 -1, 472 -1, 473 ActionTools::ConsoleWidget::Error); 474 initSucceeded = false; 475 continue; 476 } 477 else 478 value = result.toString(); 479 } 480 else 481 value = scriptParameter.value(); 482 483 mScriptEngine->globalObject().setProperty(scriptParameter.name(), value, QScriptValue::ReadOnly | QScriptValue::Undeletable); 484 } 485 486 if(!initSucceeded || mScript->actionCount() == 0) 487 return false; 488 489 if(mShowExecutionWindow) 490 { 491 QRect screenRect = QApplication::desktop()->availableGeometry(mExecutionWindowScreen); 492 QPoint position; 493 494 if(mExecutionWindowPosition >= 0 && mExecutionWindowPosition <= 2)//Left 495 position.setX(screenRect.left()); 496 else if(mExecutionWindowPosition >= 3 && mExecutionWindowPosition <= 5)//HCenter 497 position.setX(screenRect.left() + screenRect.width() / 2 - mExecutionWindow->width() / 2); 498 else if(mExecutionWindowPosition >= 6 && mExecutionWindowPosition <= 8)//Right 499 position.setX(screenRect.left() + screenRect.width() - mExecutionWindow->width()); 500 501 if(mExecutionWindowPosition == 0 || mExecutionWindowPosition == 3 || mExecutionWindowPosition == 6)//Up 502 position.setY(screenRect.top()); 503 else if(mExecutionWindowPosition == 1 || mExecutionWindowPosition == 4 || mExecutionWindowPosition == 7)//VCenter 504 position.setY(screenRect.top() + screenRect.height() / 2 - mExecutionWindow->height() / 2); 505 else if(mExecutionWindowPosition == 2 || mExecutionWindowPosition == 5 || mExecutionWindowPosition == 8)//Down 506 position.setY(screenRect.top() + screenRect.height() - mExecutionWindow->height()); 507 508 mExecutionWindow->setPauseStatus(false); 509 mExecutionWindow->move(position); 510 mExecutionWindow->show(); 511 } 512 513 if(mShowConsoleWindow) 514 { 515 QRect screenRect = QApplication::desktop()->availableGeometry(mConsoleWindowScreen); 516 QPoint position; 517 518 if(mConsoleWindowPosition >= 0 && mConsoleWindowPosition <= 2)//Left 519 position.setX(screenRect.left()); 520 else if(mConsoleWindowPosition >= 3 && mConsoleWindowPosition <= 5)//HCenter 521 position.setX(screenRect.left() + screenRect.width() / 2 - mConsoleWidget->width() / 2); 522 else if(mConsoleWindowPosition >= 6 && mConsoleWindowPosition <= 8)//Right 523 position.setX(screenRect.left() + screenRect.width() - mConsoleWidget->width()); 524 525 if(mConsoleWindowPosition == 0 || mConsoleWindowPosition == 3 || mConsoleWindowPosition == 6)//Up 526 position.setY(screenRect.top()); 527 else if(mConsoleWindowPosition == 1 || mConsoleWindowPosition == 4 || mConsoleWindowPosition == 7)//VCenter 528 position.setY(screenRect.top() + screenRect.height() / 2 - mConsoleWidget->height() / 2); 529 else if(mConsoleWindowPosition == 2 || mConsoleWindowPosition == 5 || mConsoleWindowPosition == 8)//Down 530 position.setY(screenRect.top() + screenRect.height() - mConsoleWidget->height()); 531 532 mConsoleWidget->move(position); 533 mConsoleWidget->show(); 534 } 535 536 mExecutionStarted = true; 537 538 mScriptAgent->setContext(ScriptAgent::Actions); 539 540 mHasExecuted = true; 541 542 executeCurrentAction(); 543 544 return true; 545 } 546 stopExecution()547 void Executer::stopExecution() 548 { 549 if(!mExecutionStarted) 550 return; 551 552 mScriptAgent->pause(false); 553 mScriptAgent->stopExecution(false); 554 mScriptEngineDebugger.action(QScriptEngineDebugger::ContinueAction)->trigger(); 555 556 mExecutionStarted = false; 557 mExecutionStatus = Stopped; 558 559 if(mScriptEngine) 560 mScriptEngine->abortEvaluation(); 561 562 mExecutionTimer.stop(); 563 564 if(mCurrentActionIndex >= 0 && mCurrentActionIndex < mScript->actionCount()) 565 { 566 currentActionInstance()->disconnect(); 567 if(!mExecutionEnded) 568 currentActionInstance()->doStopExecution(); 569 } 570 571 mScript->executionStopped(); 572 573 mScriptEngineDebugger.detach(); 574 575 if(mScriptAgent) 576 { 577 mScriptAgent->deleteLater(); 578 mScriptAgent = nullptr; 579 } 580 if(mScriptEngine) 581 { 582 mScriptEngine->deleteLater(); 583 mScriptEngine = nullptr; 584 } 585 586 delete mProgressDialog; 587 mProgressDialog = nullptr; 588 mDebuggerWindow->hide(); 589 mExecutionWindow->hide(); 590 mConsoleWidget->hide(); 591 592 emit executionStopped(); 593 } 594 pauseExecution()595 void Executer::pauseExecution() 596 { 597 pauseOrDebug(false); 598 } 599 debugExecution()600 void Executer::debugExecution() 601 { 602 pauseOrDebug(true); 603 } 604 executionException(int exception,const QString & message)605 void Executer::executionException(int exception, 606 const QString &message) 607 { 608 ActionTools::ActionInstance *actionInstance = currentActionInstance(); 609 bool standardException = (exception >= 0 && exception < ActionTools::ActionException::ExceptionCount); 610 bool customException = false; 611 612 for(ActionTools::ActionException *actionException: actionInstance->definition()->exceptions()) 613 { 614 if(actionException->id() == exception) 615 { 616 customException = true; 617 break; 618 } 619 } 620 621 if(!standardException && !customException) 622 { 623 mConsoleWidget->addDesignErrorLine(tr("Action design error: Invalid exception emitted (%1, line %2)") 624 .arg(actionInstance->definition()->name()) 625 .arg(mCurrentActionIndex+1), ActionTools::ConsoleWidget::Error); 626 stopExecution(); 627 return; 628 } 629 630 ActionTools::ActionException::ExceptionActionInstance exceptionActionInstance = actionInstance->exceptionActionInstance(static_cast<ActionTools::ActionException::Exception>(exception)); 631 ActionTools::ConsoleWidget::Type exceptionType; 632 bool shouldStopExecution; 633 switch(exceptionActionInstance.action()) 634 { 635 case ActionTools::ActionException::SkipExceptionAction: 636 exceptionType = ActionTools::ConsoleWidget::Information; 637 actionExecutionEnded(); 638 639 shouldStopExecution = false; 640 break; 641 case ActionTools::ActionException::GotoLineExceptionAction: 642 { 643 exceptionType = ActionTools::ConsoleWidget::Information; 644 645 if(canExecuteAction(exceptionActionInstance.line()) != CanExecute) 646 { 647 ActionTools::ActionInstance *currentAction = mScript->actionAt(mCurrentActionIndex); 648 qint64 currentActionRuntimeId = -1; 649 if(currentAction) 650 currentActionRuntimeId = currentAction->runtimeId(); 651 652 mConsoleWidget->addExceptionLine(tr("Invalid exception line: %1").arg(exceptionActionInstance.line()), 653 currentActionRuntimeId, 654 exception, 655 ActionTools::ConsoleWidget::Error); 656 shouldStopExecution = true; 657 } 658 else 659 { 660 QScriptValue script = mScriptEngine->globalObject().property(QStringLiteral("Script")); 661 script.setProperty(QStringLiteral("nextLine"), mScriptEngine->newVariant(QVariant(exceptionActionInstance.line()))); 662 actionExecutionEnded(); 663 shouldStopExecution = false; 664 } 665 } 666 break; 667 default: 668 exceptionType = ActionTools::ConsoleWidget::Error; 669 670 shouldStopExecution = true; 671 } 672 673 if(shouldStopExecution) 674 { 675 QString finalMessage = tr("Script line %1: ").arg(mCurrentActionIndex+1); 676 677 ActionTools::ActionInstance *currentAction = mScript->actionAt(mCurrentActionIndex); 678 qint64 currentActionRuntimeId = -1; 679 if(currentAction) 680 currentActionRuntimeId = currentAction->runtimeId(); 681 682 mConsoleWidget->addActionLine(finalMessage + message, 683 currentActionRuntimeId, 684 mScriptEngine->globalObject().property(QStringLiteral("currentParameter")).toString(), 685 mScriptEngine->globalObject().property(QStringLiteral("currentSubParameter")).toString(), 686 mScriptAgent->currentLine(), 687 mScriptAgent->currentColumn(), 688 exceptionType); 689 690 stopExecution(); 691 } 692 } 693 actionExecutionEnded()694 void Executer::actionExecutionEnded() 695 { 696 mExecutionTimer.stop(); 697 currentActionInstance()->disconnect(); 698 699 emit actionEnded(mCurrentActionIndex, mActiveActionsCount); 700 701 mExecutionStatus = PostPause; 702 703 mExecutionTimer.start(); 704 mExecutionTime.start(); 705 if(currentActionInstance()->pauseAfter() + mPauseAfter > 0) 706 { 707 mExecutionWindow->setProgressEnabled(true); 708 mExecutionWindow->setProgressMinimum(0); 709 mExecutionWindow->setProgressMaximum(currentActionInstance()->pauseAfter() + mPauseAfter); 710 mExecutionWindow->setProgressValue(0); 711 } 712 else 713 mExecutionWindow->setProgressEnabled(false); 714 715 mExecutionEnded = true; 716 } 717 disableAction(bool disable)718 void Executer::disableAction(bool disable) 719 { 720 mActionEnabled[mCurrentActionIndex] = !disable; 721 } 722 startNextAction()723 void Executer::startNextAction() 724 { 725 mExecutionEnded = false; 726 727 QScriptValue script = mScriptEngine->globalObject().property(QStringLiteral("Script")); 728 QString nextLineString = script.property(QStringLiteral("nextLine")).toString(); 729 int previousLine = mCurrentActionIndex; 730 731 bool ok; 732 int nextLine = nextLineString.toInt(&ok); 733 734 if(!ok) 735 { 736 nextLine = mScript->labelLine(nextLineString); 737 738 if(nextLine == -1) 739 { 740 executionException(ActionTools::ActionException::CodeErrorException, tr("Unable to find the label named \"%1\"").arg(nextLineString)); 741 return; 742 } 743 } 744 else 745 --nextLine;//Make the nextLine value 0-based instead of 1-based 746 747 if(nextLine < 0 || nextLine == mScript->actionCount())//End of the script 748 mCurrentActionIndex = nextLine; 749 else 750 { 751 switch(canExecuteAction(nextLine)) 752 { 753 case IncorrectLine: 754 executionException(ActionTools::ActionException::CodeErrorException, tr("Incorrect Script.nextLine value: %1").arg(nextLineString)); 755 return; 756 case InvalidAction: 757 executionException(ActionTools::ActionException::CodeErrorException, tr("The action at line %1 is invalid").arg(nextLineString)); 758 return; 759 case DisabledAction: 760 case UnselectedAction: 761 case CanExecute: 762 mCurrentActionIndex = nextLine; 763 break; 764 } 765 } 766 767 bool doNotResetPreviousActions = script.property(QStringLiteral("doNotResetPreviousActions")).toBool(); 768 769 if(doNotResetPreviousActions) 770 { 771 script.setProperty(QStringLiteral("doNotResetPreviousActions"), false); 772 } 773 else if(mCurrentActionIndex >= 0) 774 { 775 for(int actionIndex = mCurrentActionIndex; actionIndex < previousLine; ++actionIndex) 776 mScript->actionAt(actionIndex)->reset(); 777 } 778 779 executeCurrentAction(); 780 } 781 startActionExecution()782 void Executer::startActionExecution() 783 { 784 mExecutionStatus = Executing; 785 786 mExecutionEnded = false; 787 788 int actionTimeout = currentActionInstance()->timeout(); 789 if(actionTimeout > 0) 790 { 791 mExecutionTimer.start(); 792 mExecutionTime.start(); 793 mExecutionWindow->setProgressEnabled(true); 794 mExecutionWindow->setProgressMinimum(0); 795 mExecutionWindow->setProgressMaximum(actionTimeout); 796 mExecutionWindow->setProgressValue(0); 797 } 798 else 799 mExecutionWindow->setProgressEnabled(false); 800 801 emit actionStarted(mCurrentActionIndex, mActiveActionsCount); 802 803 currentActionInstance()->doStartExecution(); 804 } 805 updateTimerProgress()806 void Executer::updateTimerProgress() 807 { 808 if(mExecutionPaused) 809 return; 810 811 ActionTools::ActionInstance *actionInstance = currentActionInstance(); 812 switch(mExecutionStatus) 813 { 814 case PrePause: 815 if(mExecutionTime.elapsed() >= actionInstance->pauseBefore() + mPauseBefore) 816 { 817 mExecutionTimer.stop(); 818 startActionExecution(); 819 } 820 mExecutionWindow->setProgressValue(mExecutionTime.elapsed()); 821 break; 822 case Executing://Timeout 823 if(mExecutionTime.elapsed() >= actionInstance->timeout()) 824 { 825 mExecutionTimer.stop(); 826 actionInstance->disconnect(); 827 actionInstance->doStopExecution(); 828 829 executionException(ActionTools::ActionException::TimeoutException, QString()); 830 } 831 mExecutionWindow->setProgressValue(mExecutionTime.elapsed()); 832 break; 833 case PostPause: 834 if(mExecutionTime.elapsed() >= actionInstance->pauseAfter() + mPauseAfter) 835 { 836 mExecutionTimer.stop(); 837 startNextAction(); 838 } 839 mExecutionWindow->setProgressValue(mExecutionTime.elapsed()); 840 break; 841 default: 842 Q_ASSERT(false && "updateTimerProgress() called, but execution is stopped"); 843 break; 844 } 845 } 846 showProgressDialog(const QString & title,int maximum)847 void Executer::showProgressDialog(const QString &title, int maximum) 848 { 849 if(!mProgressDialog) 850 mProgressDialog = new QProgressDialog(nullptr, Qt::WindowStaysOnTopHint); 851 852 connect(mProgressDialog, &QProgressDialog::canceled, this, &Executer::stopExecution); 853 854 mProgressDialog->setWindowTitle(title); 855 mProgressDialog->setMaximum(maximum); 856 mProgressDialog->setValue(0); 857 858 mProgressDialog->show(); 859 } 860 updateProgressDialog(const QString & caption)861 void Executer::updateProgressDialog(const QString &caption) 862 { 863 mProgressDialog->setLabelText(caption); 864 } 865 updateProgressDialog(int value)866 void Executer::updateProgressDialog(int value) 867 { 868 mProgressDialog->setValue(value); 869 } 870 hideProgressDialog()871 void Executer::hideProgressDialog() 872 { 873 delete mProgressDialog; 874 mProgressDialog = nullptr; 875 } 876 executionPaused()877 void Executer::executionPaused() 878 { 879 mExecutionPaused = true; 880 881 if(!mPauseInterrupt) 882 { 883 if(mShowDebuggerOnCodeError) 884 mDebuggerWindow->show(); 885 else 886 mScriptEngineDebugger.action(QScriptEngineDebugger::ContinueAction)->trigger(); 887 } 888 else 889 mPauseInterrupt = false; 890 } 891 executionResumed()892 void Executer::executionResumed() 893 { 894 mExecutionPaused = false; 895 } 896 consolePrint(const QString & text)897 void Executer::consolePrint(const QString &text) 898 { 899 consolePrint(text, ActionTools::ConsoleWidget::Information); 900 } 901 consolePrintWarning(const QString & text)902 void Executer::consolePrintWarning(const QString &text) 903 { 904 consolePrint(text, ActionTools::ConsoleWidget::Warning); 905 } 906 consolePrintError(const QString & text)907 void Executer::consolePrintError(const QString &text) 908 { 909 consolePrint(text, ActionTools::ConsoleWidget::Error); 910 } 911 canExecuteAction(const QString & line) const912 Executer::ExecuteActionResult Executer::canExecuteAction(const QString &line) const 913 { 914 bool ok; 915 int nextLine = line.toInt(&ok); 916 917 if(!ok) 918 nextLine = mScript->labelLine(line); 919 else 920 --nextLine; 921 922 return canExecuteAction(nextLine); 923 } 924 consolePrint(const QString & text,ActionTools::ConsoleWidget::Type type)925 void Executer::consolePrint(const QString &text, ActionTools::ConsoleWidget::Type type) 926 { 927 ActionTools::ActionInstance *currentAction = mScript->actionAt(currentActionIndex()); 928 qint64 currentActionRuntimeId = -1; 929 if(currentAction) 930 currentActionRuntimeId = currentAction->runtimeId(); 931 932 consoleWidget()->addUserLine(text, 933 currentActionRuntimeId, 934 mScriptEngine->globalObject().property(QStringLiteral("currentParameter")).toString(), 935 mScriptEngine->globalObject().property(QStringLiteral("currentSubParameter")).toString(), 936 mScriptAgent->currentLine(), 937 mScriptAgent->currentColumn(), 938 mScriptEngine->currentContext()->backtrace(), 939 type); 940 } 941 pauseOrDebug(bool debug)942 void Executer::pauseOrDebug(bool debug) 943 { 944 if(mExecutionStatus == Stopped) 945 return; 946 947 mExecutionPaused = !mExecutionPaused; 948 949 mPauseInterrupt = !debug; 950 951 if(mScriptEngine->isEvaluating()) 952 { 953 if(mExecutionPaused) 954 { 955 mScriptEngineDebugger.action(QScriptEngineDebugger::InterruptAction)->trigger(); 956 957 if(debug) 958 mDebuggerWindow->show(); 959 } 960 else 961 { 962 mScriptEngineDebugger.action(QScriptEngineDebugger::ContinueAction)->trigger(); 963 964 if(debug) 965 mDebuggerWindow->hide(); 966 } 967 968 mScriptAgent->pause(mExecutionPaused); 969 } 970 else 971 { 972 ActionTools::ActionInstance *currentAction = currentActionInstance(); 973 if(currentAction) 974 { 975 if(mExecutionPaused) 976 currentAction->doPauseExecution(); 977 else 978 currentAction->doResumeExecution(); 979 } 980 } 981 982 mExecutionWindow->setPauseStatus(mExecutionPaused); 983 } 984 canExecuteAction(int index) const985 Executer::ExecuteActionResult Executer::canExecuteAction(int index) const 986 { 987 if(index < 0 || index >= mScript->actionCount()) 988 return IncorrectLine; 989 990 ActionTools::ActionInstance *actionInstance = mScript->actionAt(index); 991 if(!actionInstance) 992 return InvalidAction; 993 994 if(!mActionEnabled[index] || !actionInstance->isEnabled()) 995 return DisabledAction; 996 997 if(mExecuteOnlySelection && !actionInstance->isSelected()) 998 return UnselectedAction; 999 1000 return CanExecute; 1001 } 1002 executeCurrentAction()1003 void Executer::executeCurrentAction() 1004 { 1005 //Skip disabled actions 1006 if(mCurrentActionIndex >= 0) 1007 { 1008 while(mCurrentActionIndex < mScript->actionCount() && canExecuteAction(mCurrentActionIndex) != CanExecute) 1009 ++mCurrentActionIndex; 1010 } 1011 1012 if(mCurrentActionIndex < 0 || mCurrentActionIndex >= mScript->actionCount()) 1013 { 1014 stopExecution(); 1015 return; 1016 } 1017 1018 int nextLine = mCurrentActionIndex + 2; 1019 if(nextLine > mScript->actionCount()) 1020 nextLine = -1; 1021 1022 QScriptValue script = mScriptEngine->globalObject().property(QStringLiteral("Script")); 1023 script.setProperty(QStringLiteral("nextLine"), mScriptEngine->newVariant(QVariant(nextLine))); 1024 script.setProperty(QStringLiteral("line"), mCurrentActionIndex + 1, QScriptValue::ReadOnly); 1025 1026 ActionTools::ActionInstance *actionInstance = currentActionInstance(); 1027 1028 const ActionTools::ExceptionActionInstancesHash &exceptionActionInstancesHash = actionInstance->exceptionActionInstances(); 1029 const ActionTools::ActionException::ExceptionActionInstance &exceptionAction = exceptionActionInstancesHash.value(ActionTools::ActionException::CodeErrorException); 1030 mShowDebuggerOnCodeError = (exceptionAction.action() == ActionTools::ActionException::StopExecutionExceptionAction); 1031 1032 mExecutionWindow->setCurrentActionName(actionInstance->definition()->name()); 1033 mExecutionWindow->setCurrentActionColor(actionInstance->color()); 1034 1035 connect(actionInstance, &ActionTools::ActionInstance::executionEndedSignal, this, &Executer::actionExecutionEnded); 1036 connect(actionInstance, &ActionTools::ActionInstance::executionException, this, &Executer::executionException); 1037 connect(actionInstance, &ActionTools::ActionInstance::disableAction, this, &Executer::disableAction); 1038 connect(actionInstance, &ActionTools::ActionInstance::showProgressDialog, this, &Executer::showProgressDialog); 1039 connect(actionInstance, static_cast<void (ActionTools::ActionInstance::*)(int)>(&ActionTools::ActionInstance::updateProgressDialog), 1040 this, static_cast<void (Executer::*)(int)>(&Executer::updateProgressDialog)); 1041 connect(actionInstance, static_cast<void (ActionTools::ActionInstance::*)(const QString &)>(&ActionTools::ActionInstance::updateProgressDialog), 1042 this, static_cast<void (Executer::*)(const QString &)>(&Executer::updateProgressDialog)); 1043 connect(actionInstance, &ActionTools::ActionInstance::hideProgressDialog, this, &Executer::hideProgressDialog); 1044 connect(actionInstance, &ActionTools::ActionInstance::consolePrint, this, static_cast<void (Executer::*)(const QString &)>(&Executer::consolePrint)); 1045 connect(actionInstance, &ActionTools::ActionInstance::consolePrintWarning, this, &Executer::consolePrintWarning); 1046 connect(actionInstance, &ActionTools::ActionInstance::consolePrintError, this, &Executer::consolePrintError); 1047 1048 mExecutionStatus = PrePause; 1049 1050 mExecutionTimer.start(); 1051 mExecutionTime.start(); 1052 if(currentActionInstance()->pauseBefore() + mPauseBefore > 0) 1053 { 1054 mExecutionWindow->setProgressEnabled(true); 1055 mExecutionWindow->setProgressMinimum(0); 1056 mExecutionWindow->setProgressMaximum(currentActionInstance()->pauseBefore() + mPauseBefore); 1057 mExecutionWindow->setProgressValue(0); 1058 } 1059 else 1060 mExecutionWindow->setProgressEnabled(false); 1061 1062 mExecutionEnded = true; 1063 } 1064 } 1065