881 lines
24 KiB
C++
881 lines
24 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "director/director.h"
|
|
#include "director/movie.h"
|
|
#include "director/cast.h"
|
|
#include "director/debugger/dt-internal.h"
|
|
|
|
#include "director/debugger.h"
|
|
|
|
#include "director/lingo/lingo-ast.h"
|
|
#include "director/lingo/lingo-code.h"
|
|
#include "director/lingo/lingo-object.h"
|
|
#include "director/lingo/lingo-the.h"
|
|
|
|
namespace Director {
|
|
namespace DT {
|
|
|
|
class RenderOldScriptVisitor : public NodeVisitor {
|
|
private:
|
|
ImGuiScript &_script;
|
|
int _indent = 0;
|
|
bool _isScriptInDebug = false;
|
|
bool _currentStatementDisplayed = false;
|
|
bool _scrollTo = false;
|
|
bool _scrollDone = true;
|
|
|
|
public:
|
|
explicit RenderOldScriptVisitor(ImGuiScript &script, bool scrollTo) : _script(script), _scrollTo(scrollTo) {
|
|
Common::Array<CFrame *> &callstack = g_lingo->_state->callstack;
|
|
if (!callstack.empty()) {
|
|
CFrame *head = callstack[callstack.size() - 1];
|
|
_isScriptInDebug = (head->sp.ctx->_id == script.id.member) && (*head->sp.name == script.handlerId);
|
|
}
|
|
_script.startOffsets.clear();
|
|
}
|
|
|
|
virtual bool visitHandlerNode(HandlerNode *node) {
|
|
ImGui::Text("on ");
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(_state->_colors._call_color, "%s", node->name->c_str());
|
|
if (!node->args->empty()) {
|
|
ImGui::SameLine();
|
|
ImGui::Text(" ");
|
|
ImGui::SameLine();
|
|
for (uint i = 0; i < node->args->size(); i++) {
|
|
Common::String *arg = (*node->args)[i];
|
|
ImGui::Text("%s", arg->c_str());
|
|
ImGui::SameLine();
|
|
if (i != (node->args->size() - 1)) {
|
|
ImGui::Text(", ");
|
|
ImGui::SameLine();
|
|
}
|
|
}
|
|
ImGui::NewLine();
|
|
}
|
|
if (_state->_dbg._goToDefinition && _scrollTo) {
|
|
ImGui::SetScrollHereY(0.5f);
|
|
_state->_dbg._goToDefinition = false;
|
|
}
|
|
|
|
indent();
|
|
for (uint i = 0; i < node->stmts->size(); i++) {
|
|
Node *stmt = (*node->stmts)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
}
|
|
unindent();
|
|
renderLine(node->endOffset);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "end");
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitScriptNode(ScriptNode *node) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
|
for (Node *child : *node->children) {
|
|
if (child->type == kHandlerNode && *((HandlerNode *)child)->name != _script.handlerId)
|
|
continue;
|
|
renderLine(child->startOffset);
|
|
child->accept(this);
|
|
}
|
|
ImGui::PopStyleVar();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitFactoryNode(FactoryNode *node) {
|
|
ImGui::Text("factory %s", node->name->c_str());
|
|
ImGui::NewLine();
|
|
indent();
|
|
for (uint i = 0; i < node->methods->size(); i++) {
|
|
Node *method = (*node->methods)[i];
|
|
renderLine(method->startOffset);
|
|
method->accept(this);
|
|
ImGui::NewLine();
|
|
}
|
|
unindent();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitCmdNode(CmdNode *node) {
|
|
ImGui::Text("%s ", node->name->c_str());
|
|
ImGui::SameLine();
|
|
if (*node->name == "go") {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "to ");
|
|
ImGui::SameLine();
|
|
}
|
|
for (uint i = 0; i < node->args->size(); i++) {
|
|
Node *arg = (*node->args)[i];
|
|
if ((i != 0) || (node->args->size() < 2) || ((*node->args)[1]->type != kMovieNode)) {
|
|
arg->accept(this);
|
|
ImGui::SameLine();
|
|
if (i != (node->args->size() - 1)) {
|
|
ImGui::Text(", ");
|
|
ImGui::SameLine();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitPutIntoNode(PutIntoNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "put ");
|
|
ImGui::SameLine();
|
|
node->val->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, " into ");
|
|
ImGui::SameLine();
|
|
node->var->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitPutAfterNode(PutAfterNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "put ");
|
|
ImGui::SameLine();
|
|
node->val->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, " after ");
|
|
ImGui::SameLine();
|
|
node->var->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitPutBeforeNode(PutBeforeNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "put ");
|
|
ImGui::SameLine();
|
|
node->val->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, " before ");
|
|
ImGui::SameLine();
|
|
node->var->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitSetNode(SetNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "set ");
|
|
ImGui::SameLine();
|
|
node->val->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, " to ");
|
|
ImGui::SameLine();
|
|
node->var->accept(this);
|
|
return true;
|
|
}
|
|
|
|
void displayDefineVar(const char *name, IDList *names) {
|
|
ImGui::Text("%s ", name);
|
|
ImGui::SameLine();
|
|
for (uint i = 0; i < names->size(); i++) {
|
|
Common::String *arg = (*names)[i];
|
|
ImGui::Text("%s", arg->c_str());
|
|
ImGui::SameLine();
|
|
if (i != (names->size() - 1)) {
|
|
ImGui::Text(" ");
|
|
ImGui::SameLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual bool visitGlobalNode(GlobalNode *node) {
|
|
displayDefineVar("global", node->names);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitPropertyNode(PropertyNode *node) {
|
|
displayDefineVar("property", node->names);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitInstanceNode(InstanceNode *node) {
|
|
displayDefineVar("instance", node->names);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitIfStmtNode(IfStmtNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "if ");
|
|
ImGui::SameLine();
|
|
node->cond->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, " then ");
|
|
if (node->stmts->size() == 1) {
|
|
ImGui::SameLine();
|
|
(*node->stmts)[0]->accept(this);
|
|
} else {
|
|
indent();
|
|
for (uint i = 0; i < node->stmts->size(); i++) {
|
|
Node *stmt = (*node->stmts)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
}
|
|
unindent();
|
|
renderLine(node->endOffset);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "endif");
|
|
ImGui::SameLine();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitIfElseStmtNode(IfElseStmtNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "if ");
|
|
ImGui::SameLine();
|
|
node->cond->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, " then ");
|
|
if (node->stmts1->size() == 1) {
|
|
ImGui::SameLine();
|
|
(*node->stmts1)[0]->accept(this);
|
|
ImGui::Text(" ");
|
|
ImGui::SameLine();
|
|
} else {
|
|
uint offset = node->cond->endOffset;
|
|
indent();
|
|
for (uint i = 0; i < node->stmts1->size(); i++) {
|
|
Node *stmt = (*node->stmts1)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
offset = stmt->endOffset;
|
|
}
|
|
unindent();
|
|
renderLine(offset);
|
|
}
|
|
ImGui::TextColored(_state->_colors._keyword_color, "else ");
|
|
if (node->stmts2->size() == 1) {
|
|
ImGui::SameLine();
|
|
(*node->stmts2)[0]->accept(this);
|
|
} else {
|
|
uint offset = node->cond->endOffset;
|
|
indent();
|
|
for (uint i = 0; i < node->stmts2->size(); i++) {
|
|
Node *stmt = (*node->stmts2)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
offset = stmt->endOffset;
|
|
}
|
|
unindent();
|
|
renderLine(offset);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "endif");
|
|
ImGui::SameLine();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitRepeatWhileNode(RepeatWhileNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "repeat while ");
|
|
ImGui::SameLine();
|
|
node->cond->accept(this);
|
|
ImGui::NewLine();
|
|
indent();
|
|
uint offset = node->cond->endOffset;
|
|
for (uint i = 0; i < node->stmts->size(); i++) {
|
|
Node *stmt = (*node->stmts)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
offset = stmt->endOffset;
|
|
}
|
|
unindent();
|
|
renderLine(offset);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "endrepeat");
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitRepeatWithToNode(RepeatWithToNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "repeat with ");
|
|
ImGui::SameLine();
|
|
ImGui::Text("%s = ", node->var->c_str());
|
|
ImGui::SameLine();
|
|
node->start->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, " %s ", node->down ? "down to" : "to");
|
|
node->end->accept(this);
|
|
ImGui::NewLine();
|
|
indent();
|
|
for (uint i = 0; i < node->stmts->size(); i++) {
|
|
Node *stmt = (*node->stmts)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
}
|
|
unindent();
|
|
renderLine(node->endOffset);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "endrepeat");
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitRepeatWithInNode(RepeatWithInNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "repeat with ");
|
|
ImGui::SameLine();
|
|
ImGui::Text("%s in ", node->var->c_str());
|
|
ImGui::SameLine();
|
|
node->list->accept(this);
|
|
ImGui::NewLine();
|
|
indent();
|
|
for (uint i = 0; i < node->stmts->size(); i++) {
|
|
Node *stmt = (*node->stmts)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
}
|
|
unindent();
|
|
renderLine(node->endOffset);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "endrepeat");
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitNextRepeatNode(NextRepeatNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "next repeat");
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitExitRepeatNode(ExitRepeatNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "exit repeat");
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitExitNode(ExitNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "exit");
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitReturnNode(ReturnNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "return");
|
|
if (node->expr) {
|
|
ImGui::Text(" ");
|
|
ImGui::SameLine();
|
|
node->expr->accept(this);
|
|
ImGui::NewLine();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitTellNode(TellNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "tell ");
|
|
node->target->accept(this);
|
|
if (node->stmts->size() == 1) {
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(_state->_colors._keyword_color, " to ");
|
|
ImGui::SameLine();
|
|
(*node->stmts)[0]->accept(this);
|
|
} else {
|
|
indent();
|
|
for (uint i = 0; i < node->stmts->size(); i++) {
|
|
Node *stmt = (*node->stmts)[i];
|
|
renderLine(stmt->startOffset);
|
|
stmt->accept(this);
|
|
ImGui::NewLine();
|
|
}
|
|
unindent();
|
|
renderLine(node->endOffset);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "endtell");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitWhenNode(WhenNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "when ");
|
|
ImGui::SameLine();
|
|
ImGui::Text("%s", node->event->c_str());
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(_state->_colors._keyword_color, " then ");
|
|
ImGui::SameLine();
|
|
ImGui::Text("%s", node->code->c_str());
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitDeleteNode(DeleteNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "delete ");
|
|
ImGui::SameLine();
|
|
node->chunk->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitHiliteNode(HiliteNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "hilite ");
|
|
ImGui::SameLine();
|
|
node->chunk->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitAssertErrorNode(AssertErrorNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "scummvmAssertError ");
|
|
ImGui::SameLine();
|
|
node->stmt->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitIntNode(IntNode *node) {
|
|
ImGui::TextColored(_state->_colors._literal_color, "%d", node->val);
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitFloatNode(FloatNode *node) {
|
|
ImGui::TextColored(_state->_colors._literal_color, "%g", node->val);
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitSymbolNode(SymbolNode *node) {
|
|
ImGui::TextColored(_state->_colors._literal_color, "%s", node->val->c_str());
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitStringNode(StringNode *node) {
|
|
ImGui::TextColored(_state->_colors._literal_color, "\"%s\"", node->val->c_str());
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitListNode(ListNode *node) {
|
|
ImGui::Text("[");
|
|
ImGui::SameLine();
|
|
for (uint i = 0; i < node->items->size(); i++) {
|
|
Node *prop = (*node->items)[i];
|
|
prop->accept(this);
|
|
if (i != (node->items->size() - 1)) {
|
|
ImGui::Text(",");
|
|
ImGui::SameLine();
|
|
}
|
|
}
|
|
ImGui::Text("]");
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitPropListNode(PropListNode *node) {
|
|
ImGui::Text("[");
|
|
ImGui::SameLine();
|
|
if (node->items->empty()) {
|
|
ImGui::Text(":");
|
|
ImGui::SameLine();
|
|
} else {
|
|
for (uint i = 0; i < node->items->size(); i++) {
|
|
Node *prop = (*node->items)[i];
|
|
prop->accept(this);
|
|
if (i != (node->items->size() - 1)) {
|
|
ImGui::Text(",");
|
|
ImGui::SameLine();
|
|
}
|
|
}
|
|
}
|
|
ImGui::Text("]");
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitPropPairNode(PropPairNode *node) {
|
|
node->key->accept(this);
|
|
ImGui::Text(":");
|
|
ImGui::SameLine();
|
|
node->val->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitFuncNode(FuncNode *node) {
|
|
const bool isBuiltin = g_lingo->_builtinCmds.contains(*node->name);
|
|
const ImVec4 color = (ImVec4)ImColor(isBuiltin ? _state->_colors._builtin_color : _state->_colors._call_color);
|
|
ImGui::TextColored(color, "%s(", node->name->c_str());
|
|
if (!isBuiltin && ImGui::IsItemHovered() && ImGui::BeginTooltip()) {
|
|
ImGui::Text("Go to definition");
|
|
ImGui::EndTooltip();
|
|
}
|
|
if (!isBuiltin && ImGui::IsItemClicked()) {
|
|
int obj = 0;
|
|
for (uint i = 0; i < _script.bytecodeArray.size(); i++) {
|
|
if (node->startOffset == _script.bytecodeArray[i].pos) {
|
|
obj = _script.bytecodeArray[i].obj;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ScriptContext *context = getScriptContext(obj, _script.id, *node->name);
|
|
if (context) {
|
|
ImGuiScript script = toImGuiScript(_script.type, CastMemberID(context->_id, _script.id.castLib), *node->name);
|
|
const Director::Movie *movie = g_director->getCurrentMovie();
|
|
|
|
int castId = context->_id;
|
|
bool childScript = false;
|
|
if (castId == -1) {
|
|
castId = movie->getCast()->getCastIdByScriptId(context->_parentNumber);
|
|
childScript = true;
|
|
}
|
|
script.byteOffsets = context->_functionByteOffsets[script.handlerId];
|
|
script.moviePath = _script.moviePath;
|
|
|
|
script.handlerName = formatHandlerName(context->_scriptId, castId, script.handlerId, context->_scriptType, childScript);
|
|
setScriptToDisplay(script);
|
|
_state->_dbg._goToDefinition = true;
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
for (uint i = 0; i < node->args->size(); i++) {
|
|
Node *arg = (*node->args)[i];
|
|
arg->accept(this);
|
|
if (i != (node->args->size() - 1)) {
|
|
ImGui::Text(",");
|
|
ImGui::SameLine();
|
|
}
|
|
}
|
|
ImGui::Text(")");
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitVarNode(VarNode *node) {
|
|
ImGui::TextColored(_state->_colors._var_color, "%s", node->name->c_str());
|
|
if (ImGui::IsItemHovered() && g_lingo->_globalvars.contains(*node->name)) {
|
|
const Datum &val = g_lingo->_globalvars.getVal(*node->name);
|
|
ImGui::BeginTooltip();
|
|
ImGui::Text("Click to add to watches.");
|
|
Common::String s = val.asString(true);
|
|
s.wordWrap(150);
|
|
if (s.size() > 4000) {
|
|
uint chop = s.size() - 4000;
|
|
s.chop(s.size() - 4000);
|
|
s += Common::String::format("... [chopped %d chars]", chop);
|
|
}
|
|
ImGui::Text("= %s", s.c_str());
|
|
ImGui::EndTooltip();
|
|
}
|
|
if (ImGui::IsItemClicked()) {
|
|
_state->_variables[*node->name] = true;
|
|
}
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitParensNode(ParensNode *node) {
|
|
ImGui::Text("(");
|
|
ImGui::SameLine();
|
|
node->expr->accept(this);
|
|
ImGui::Text(")");
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitUnaryOpNode(UnaryOpNode *node) {
|
|
char op = '?';
|
|
if (node->op == LC::c_negate) {
|
|
op = '-';
|
|
} else if (node->op == LC::c_not) {
|
|
op = '!';
|
|
}
|
|
ImGui::Text("%c", op);
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitBinaryOpNode(BinaryOpNode *node) {
|
|
node->a->accept(this);
|
|
static struct {
|
|
inst op;
|
|
const char *name;
|
|
} ops[] = {
|
|
{LC::c_add, "+"},
|
|
{LC::c_sub, "-"},
|
|
{LC::c_mul, "*"},
|
|
{LC::c_div, "/"},
|
|
{LC::c_mod, "mod"},
|
|
{LC::c_gt, ">"},
|
|
{LC::c_lt, "<"},
|
|
{LC::c_eq, "="},
|
|
{LC::c_neq, "<>"},
|
|
{LC::c_ge, ">="},
|
|
{LC::c_le, "<="},
|
|
{LC::c_and, "and"},
|
|
{LC::c_or, "or"},
|
|
{LC::c_ampersand, "&"},
|
|
{LC::c_concat, "&&"},
|
|
{LC::c_contains, "contains"},
|
|
{LC::c_starts, "starts"},
|
|
};
|
|
for (auto &op : ops) {
|
|
if (op.op == node->op) {
|
|
ImGui::Text(" %s ", op.name);
|
|
ImGui::SameLine();
|
|
break;
|
|
}
|
|
}
|
|
node->b->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitFrameNode(FrameNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "frame ");
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitMovieNode(MovieNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "movie ");
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitIntersectsNode(IntersectsNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "sprite ");
|
|
ImGui::SameLine();
|
|
node->sprite1->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "intersects ");
|
|
node->sprite2->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitWithinNode(WithinNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "sprite ");
|
|
ImGui::SameLine();
|
|
node->sprite1->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "within ");
|
|
node->sprite2->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitTheNode(TheNode *node) {
|
|
ImGui::TextColored(_state->_colors._the_color, "the %s", node->prop->c_str());
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitTheOfNode(TheOfNode *node) {
|
|
ImGui::TextColored(_state->_colors._the_color, "the %s of ", node->prop->c_str());
|
|
ImGui::SameLine();
|
|
node->obj->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitTheNumberOfNode(TheNumberOfNode *node) {
|
|
ImGui::TextColored(_state->_colors._the_color, "the number of ");
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitTheLastNode(TheLastNode *node) {
|
|
// TODO: change the node to know if it's 'in' or 'of'
|
|
ImGui::TextColored(_state->_colors._the_color, "the last %s in/of ", toString(node->type).c_str());
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitTheDateTimeNode(TheDateTimeNode *node) {
|
|
const char *key1 = "";
|
|
switch (node->field) {
|
|
case kTheAbbr:
|
|
key1 = "abbreviated";
|
|
break;
|
|
case kTheLong:
|
|
key1 = "long";
|
|
break;
|
|
case kTheShort:
|
|
key1 = "short";
|
|
break;
|
|
}
|
|
const char *key2 = node->entity == kTheDate ? "date" : "time";
|
|
ImGui::TextColored(_state->_colors._the_color, "the %s %s", key1, key2);
|
|
ImGui::SameLine();
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitMenuNode(MenuNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "menu ");
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitMenuItemNode(MenuItemNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "menuitem ");
|
|
ImGui::SameLine();
|
|
node->arg1->accept(this);
|
|
ImGui::TextColored(_state->_colors._keyword_color, "of menu ");
|
|
ImGui::SameLine();
|
|
node->arg2->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitSoundNode(SoundNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "sound ");
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitSpriteNode(SpriteNode *node) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, "sprite ");
|
|
ImGui::SameLine();
|
|
node->arg->accept(this);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visitChunkExprNode(ChunkExprNode *node) {
|
|
const char *key1 = "";
|
|
switch (node->type) {
|
|
case kChunkChar:
|
|
key1 = "char";
|
|
break;
|
|
case kChunkWord:
|
|
key1 = "word";
|
|
break;
|
|
case kChunkItem:
|
|
key1 = "item";
|
|
break;
|
|
case kChunkLine:
|
|
key1 = "line";
|
|
break;
|
|
}
|
|
ImGui::Text("%s", key1);
|
|
ImGui::SameLine();
|
|
node->start->accept(this);
|
|
if (node->end) {
|
|
ImGui::TextColored(_state->_colors._keyword_color, " to ");
|
|
ImGui::SameLine();
|
|
node->end->accept(this);
|
|
}
|
|
ImGui::TextColored(_state->_colors._keyword_color, " of ");
|
|
ImGui::SameLine();
|
|
node->src->accept(this);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
static Common::String toString(ChunkType chunkType) {
|
|
// TODO: this method could be used in ChunkExprNode
|
|
switch (chunkType) {
|
|
case kChunkChar:
|
|
return "char";
|
|
case kChunkWord:
|
|
return "word";
|
|
case kChunkItem:
|
|
return "item";
|
|
case kChunkLine:
|
|
return "line";
|
|
}
|
|
return "<unknown>";
|
|
}
|
|
|
|
void indent() {
|
|
_indent++;
|
|
}
|
|
|
|
void unindent() {
|
|
if (_indent > 0)
|
|
_indent--;
|
|
}
|
|
|
|
void renderIndentation() const {
|
|
for (int i = 0; i < _indent; i++) {
|
|
ImGui::Text(" ");
|
|
ImGui::SameLine();
|
|
}
|
|
}
|
|
|
|
void renderLine(uint32 pc) {
|
|
bool showCurrentStatement = false;
|
|
_script.startOffsets.push_back(pc);
|
|
|
|
if (_script.pc != 0 && pc >= _script.pc) {
|
|
if (!_currentStatementDisplayed) {
|
|
showCurrentStatement = true;
|
|
_currentStatementDisplayed = true;
|
|
}
|
|
} else if (_isScriptInDebug && g_lingo->_exec._state == kPause) {
|
|
// check current statement
|
|
if (!_currentStatementDisplayed) {
|
|
if (g_lingo->_state->pc <= pc) {
|
|
showCurrentStatement = true;
|
|
_currentStatementDisplayed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
ImDrawList *dl = ImGui::GetWindowDrawList();
|
|
const ImVec2 pos = ImGui::GetCursorScreenPos();
|
|
const float width = ImGui::GetContentRegionAvail().x;
|
|
const ImVec2 mid(pos.x + 7, pos.y + 7);
|
|
|
|
ImVec4 color = _state->_colors._bp_color_disabled;
|
|
const Director::Breakpoint *bp = getBreakpoint(_script.handlerId, _script.id.member, pc);
|
|
if (bp)
|
|
color = _state->_colors._bp_color_enabled;
|
|
|
|
ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
|
|
|
|
// click on breakpoint column?
|
|
if (ImGui::IsItemClicked(0)) {
|
|
if (color == _state->_colors._bp_color_enabled) {
|
|
g_lingo->delBreakpoint(bp->id);
|
|
color = _state->_colors._bp_color_disabled;
|
|
} else {
|
|
Director::Breakpoint newBp;
|
|
newBp.type = kBreakpointFunction;
|
|
newBp.scriptId = _script.id.member;
|
|
newBp.funcName = _script.handlerId;
|
|
newBp.funcOffset = pc;
|
|
g_lingo->addBreakpoint(newBp);
|
|
color = _state->_colors._bp_color_enabled;
|
|
}
|
|
}
|
|
|
|
if (color == _state->_colors._bp_color_disabled && ImGui::IsItemHovered()) {
|
|
color = _state->_colors._bp_color_hover;
|
|
}
|
|
|
|
// draw breakpoint
|
|
if (!bp || bp->enabled)
|
|
dl->AddCircleFilled(mid, 4.0f, ImColor(color));
|
|
else
|
|
dl->AddCircle(mid, 4.0f, ImColor(_state->_colors._line_color));
|
|
|
|
// draw current statement
|
|
if (showCurrentStatement) {
|
|
dl->AddQuadFilled(ImVec2(pos.x, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 4.f), ImVec2(pos.x + 9.f, pos.y + 10.f), ImVec2(pos.x, pos.y + 10.f), ImColor(_state->_colors._current_statement));
|
|
dl->AddTriangleFilled(ImVec2(pos.x + 8.f, pos.y), ImVec2(pos.x + 14.f, pos.y + 7.f), ImVec2(pos.x + 8.f, pos.y + 14.f), ImColor(_state->_colors._current_statement));
|
|
if (!_scrollDone && _scrollTo && g_lingo->_state->callstack.size() != _state->_dbg._callstackSize) {
|
|
ImGui::SetScrollHereY(0.5f);
|
|
_scrollDone = true;
|
|
}
|
|
dl->AddRectFilled(ImVec2(pos.x + 16.f, pos.y), ImVec2(pos.x + width, pos.y + 16.f), ImColor(IM_COL32(0xFF, 0xFF, 0x00, 0x20)), 0.4f);
|
|
}
|
|
// draw separator
|
|
dl->AddLine(ImVec2(pos.x + 16.0f, pos.y), ImVec2(pos.x + 16.0f, pos.y + 17), ImColor(_state->_colors._line_color));
|
|
|
|
ImGui::SetItemTooltip("Click to add a breakpoint");
|
|
ImGui::SameLine();
|
|
|
|
// draw offset
|
|
ImGui::Text("[%5d] ", pc == 0xFFFFFFFF ? -1 : pc);
|
|
ImGui::SameLine();
|
|
renderIndentation();
|
|
}
|
|
};
|
|
|
|
void renderOldScriptAST(ImGuiScript &script, bool showByteCode, bool scrollTo) {
|
|
RenderOldScriptVisitor oldVisitor(script, scrollTo);
|
|
script.oldAst->accept(&oldVisitor);
|
|
}
|
|
|
|
} // namespace DT
|
|
|
|
} // namespace Director
|