#include "JsonEditor.h" #include "Bild.h" #include "Globals.h" #include "MausEreignis.h" #include "Regex.h" #include "Schrift.h" #include "TastaturEreignis.h" #include "TextFeld.h" using namespace Framework; using namespace JSON; const char* errors[] = {"Unexpected start of object", "Unexpected start of array", "Unexpected value", "Unexpected ','", "Unexpected ':'", "Unexpected end of object", "Unexpected end of array", "Unexpected start of string", "Invalid json value", ""}; class EditableJsonElementReference : public virtual AbstractElement, public virtual ReferenceCounter, public virtual AbstractBool, public virtual AbstractNumber, public virtual AbstractString, public virtual AbstractArray, public virtual AbstractObject { private: EditableJsonElement* root; RCArray* children; EditableJsonElementReference(EditableJsonElement* root, RCArray* children) : root(root), children(children) {} public: EditableJsonElementReference(EditableJsonElement* root) : root(root), children(new RCArray()) {} ~EditableJsonElementReference() { children->release(); } AbstractType getType() const override { if (root->hasError() || root->getValue().istGleich("null")) { return AbstractType::NULL_; } else if (root->getOpeningControllChar() == '{') { return AbstractType::OBJECT; } else if (root->getOpeningControllChar() == '[') { return AbstractType::ARRAY; } else if (root->getValue().istGleich("true") || root->getValue().istGleich("false")) { return AbstractType::BOOLEAN; } else if (root->getValue().hatAt(0, "\"")) { return AbstractType::STRING; } else { return AbstractType::NUMBER; } } const AbstractBool* asAbstractBool() const override { return this; } const AbstractNumber* asAbstractNumber() const override { return this; } const AbstractString* asAbstractString() const override { return this; } const AbstractArray* asAbstractArray() const override { return this; } const AbstractObject* asAbstractObject() const override { return this; } Text toString() const override { return root->getRecursiveContent(); } bool getBool() const override { return (bool)root->getValue(); } double getNumber() const override { return (double)root->getValue(); } Text getString() const override { Text txt = root->getValue().getText() + 1; txt.remove(txt.getLength() - 1, 1); return (double)root->getValue(); } AbstractElement* zAbstractValue(int i) const override { EditableJsonElement* e = root->zFirstChildren(); while (e && i > 0) { e = e->zMextSibling(); i--; } if (e) { EditableJsonElementReference* result = new EditableJsonElementReference(e, children); children->add(result); return result; } return 0; } int getLength() const override { EditableJsonElement* e = root->zFirstChildren(); int i = 0; while (e) { e = e->zMextSibling(); i++; } return i; } AbstractElement* zAbstractValue(Text field) const override { EditableJsonElement* e = root->zFirstChildren(); while (e) { if (e->getKey().istGleich(field)) { EditableJsonElementReference* result = new EditableJsonElementReference(e, children); children->add(result); return result; } e = e->zMextSibling(); } return 0; } int getFieldCount() const override { return getLength(); } Text getFieldKey(int i) const override { EditableJsonElement* e = root->zFirstChildren(); while (e && i > 0) { e = e->zMextSibling(); i--; } if (e) { return e->getKey(); } return 0; } bool hasValue(Text field) const override { EditableJsonElement* e = root->zFirstChildren(); while (e) { if (e->getKey().istGleich(field)) { return 1; } e = e->zMextSibling(); } return 0; } }; SyntaxError::SyntaxError(int start, int end, SyntaxErrorType errorType) : start(start), end(end), errorType(errorType) {} SyntaxError::SyntaxError() : start(-1), end(-1), errorType(SyntaxErrorType::OTHER) {} int SyntaxError::getStart() const { return start; } int SyntaxError::getEnd() const { return end; } SyntaxErrorType SyntaxError::getErrorType() const { return errorType; } Framework::JSON::ParserState::ParserState( JsonStructure parent, bool valueExpected) : index(0), keyStart(-1), keyEnd(0), valueStart(-1), valueEnd(0), errorStart(0), parent(parent), inString(0), escaped(0), colon(parent != JsonStructure::OBJECT), key(parent != JsonStructure::OBJECT), value(0), inValue(0), valueValidationNeeded(0), openingControllChar(0), closingControllChar(0), valueExpected(valueExpected) {} bool Framework::JSON::ParserState::next( const char* current, const char* next, SyntaxError& error) { if (current[0] == 0) return next == 0; bool stop = 0; if (current[0] == '{' && !inString) { stop = 1; if (!colon || value) { error.start = index; error.end = index + 1; error.errorType = SyntaxErrorType::UNEXPECTED_OBJECT_START; } value = 1; openingControllChar = current[0]; } else if (current[0] == '[' && !inString) { stop = 1; if (!colon || value) { error.start = index; error.end = index + 1; error.errorType = SyntaxErrorType::UNEXPECTED_ARRAY_START; } value = 1; openingControllChar = current[0]; } else if (current[0] == '}' && !inString) { stop = current[1] == ']' || current[1] == '}'; if (!stop) { const char* n = current + 1; while (*n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n')) { n++; } stop = *n == ']' || *n == '}'; if (!stop && next) { n = next; while ( *n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n')) { n++; } stop = *n == ']' || *n == '}'; } } if (parent != JsonStructure::OBJECT || (!value && valueExpected)) { error.start = index; error.end = index + 1; error.errorType = SyntaxErrorType::UNEXPECTED_END_OF_OBJECT; } value = 1; closingControllChar = current[0]; } else if (current[0] == ']' && !inString) { stop = current[1] == ']' || current[1] == '}'; if (!stop) { const char* n = current + 1; while (*n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n')) { n++; } stop = *n == ']' || *n == '}'; if (!stop && next) { n = next; while ( *n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n')) { n++; } stop = *n == ']' || *n == '}'; } } if (parent != JsonStructure::ARRAY || (!value && valueExpected)) { error.start = index; error.end = index + 1; error.errorType = SyntaxErrorType::UNEXPECTED_END_OF_ARRAY; } value = 1; closingControllChar = current[0]; } else if (current[0] == '"' && !escaped) { inString = !inString; if (inString) { if (!key) { keyStart = index + 1; } else if (colon && !value) { valueStart = index; } else { error.start = index; error.end = index + 1; error.errorType = SyntaxErrorType::UNEXPECTED_START_OF_STRING; } } else { if (!key) { keyEnd = index; key = 1; } else if (colon && !value) { valueEnd = index + 1; value = 1; } } } else if (current[0] == '\\' && inString) { escaped = !escaped; } else if (current[0] == ':' && !inString) { if (key && !colon) { colon = 1; } else { error.start = index; error.end = index + 1; error.errorType = SyntaxErrorType::UNEXPECTED_COLON; } } else if (current[0] == ',' && !inString) { stop = 1; if (!value) { error.start = index; error.end = index + 1; error.errorType = SyntaxErrorType::UNEXPECTED_COMMA; } } else { escaped = 0; if (!inString && current[0] != ' ' && current[0] != '\t' && current[0] != '\n' && current[0] != '\r') { if (colon && !value) { if (!inValue) { valueStart = index; inValue = 1; } } else { if (!inValue) { errorStart = index; inValue = 1; } } if (inValue && (current[1] == ':' || current[1] == '"' || current[1] == '{' || current[1] == '[' || current[1] == ']' || current[1] == '}' || current[1] == ',')) { inValue = 0; if (colon && !value) { value = 1; valueEnd = index + 1; valueValidationNeeded = 1; } else { error.start = errorStart; error.end = index; error.errorType = SyntaxErrorType::UNEXPECTED_VALUE; } } } else if (inValue) { inValue = 0; if (colon && !value) { valueEnd = index; value = 1; valueValidationNeeded = 1; } else { error.start = errorStart; error.end = index; error.errorType = SyntaxErrorType::UNEXPECTED_VALUE; } } } index++; return stop; } EditableJsonElement::EditableJsonElement(Regex::Automata* valueValidator) : ReferenceCounter(), content(), valueValidator(valueValidator), valueStart(-1), keyStart(-1), newLineCount(0) { children = 0; siblings = 0; lastChild = 0; parent = 0; hidden = false; parentStructure = JsonStructure::VALUE; } Framework::JSON::EditableJsonElement::EditableJsonElement( Regex::Automata* valueValidator, const char* content) : EditableJsonElement(valueValidator) { setContent(content); valueValidator->release(); } EditableJsonElement::~EditableJsonElement() { if (children) { children->release(); } if (siblings) { siblings->release(); } } void Framework::JSON::EditableJsonElement::setContentOnly(const char** content, int& startPos, Array& afterList, Array& deleteList) { errors.leeren(); int start = startPos; EditableJsonElement* before = zBefore(0, false, 0); ParserState p(parentStructure, before ? before->getContent()[before->getContent().getLength() - 1] == ',' : 0); SyntaxError error; key = ""; value = ""; this->content = ""; while (true) { const char* current = (*content) + startPos++; const char* next = 0; if (afterList.getEintragAnzahl()) { next = afterList.get(0)->getContent(); } if (p.next(current, next, error)) { break; } if (error.getErrorType() != SyntaxErrorType::OTHER) { errors.add(error); error = SyntaxError(); } if (!*current) { startPos--; } if (!(*content)[startPos]) { if (!afterList.getEintragAnzahl()) { break; } else { if (p.keyStart >= 0 && (p.keyEnd < p.keyStart || p.keyEnd + start >= 0) && parentStructure == JsonStructure::OBJECT) { int s = p.keyStart + start < 0 ? 0 : p.keyStart + start; int e = p.keyEnd < p.keyStart ? p.index : p.keyEnd + start; key += Text(*content, s, e - s); } if (p.valueStart >= 0 && (p.valueEnd < p.valueStart || p.valueEnd + start >= 0) && parentStructure == JsonStructure::OBJECT) { int s = p.valueStart + start < 0 ? 0 : p.valueStart + start; int e = p.valueEnd < p.valueStart ? p.index + start : p.valueEnd + start; value += Text(*content, s, e - s); } this->content += Text(*content, start < 0 ? 0 : start, start < 0 ? startPos : startPos - start); EditableJsonElement* next = afterList.get(0); int i = 0; afterList.remove(i); if (next->children) { afterList.add(next->children, i); i++; } if (next->siblings) { afterList.add(next->siblings, i); } next->disconnect(); deleteList.add(next); *content = next->getContent(); startPos = 0; start = -p.index; } } } if (error.getErrorType() != SyntaxErrorType::OTHER) { errors.add(error); error = SyntaxError(); } if (p.keyStart >= 0 && (p.keyEnd < p.keyStart || p.keyEnd + start >= 0) && parentStructure == JsonStructure::OBJECT) { int s = p.keyStart + start < 0 ? 0 : p.keyStart + start; int e = p.keyEnd < p.keyStart ? p.index : p.keyEnd + start; key += Text(*content, s, e - s); } if (p.valueStart >= 0 && (p.valueEnd < p.valueStart || p.valueEnd + start >= 0)) { int s = p.valueStart + start < 0 ? 0 : p.valueStart + start; int e = p.valueEnd < p.valueStart ? p.index + start : p.valueEnd + start; value += Text(*content, s, e - s); } if (p.valueValidationNeeded) { if (!isValueValid(value, 0, value.getLength())) { errors.add(SyntaxError( p.valueStart, p.valueEnd, SyntaxErrorType::INVALID_VALUE)); } } this->content += Text(*content, start < 0 ? 0 : start, start < 0 ? startPos : startPos - start); newLineCount = this->content.anzahlVon('\n'); openingControllChar = p.openingControllChar; closingControllChar = p.closingControllChar; valueStart = p.valueStart; keyStart = p.keyStart; if ((*content)[startPos] && ((*content)[startPos - 1] == '[' || (*content)[startPos - 1] == '{')) { EditableJsonElement* next = new EditableJsonElement(valueValidator); addChildren(next); next->setContentRecursive(content, startPos, afterList, deleteList); } // TODO: set parent structure of children // TODO: update tree } void Framework::JSON::EditableJsonElement::setContentRecursive( const char** content, int& startPos, Array& afterList, Array& deleteList) { setContentOnly(content, startPos, afterList, deleteList); char lastControl = closingControllChar; while ((*content)[startPos] && !((lastControl == ']' && parentStructure == JsonStructure::ARRAY) || (lastControl == '}' && parentStructure == JsonStructure::OBJECT))) { if (closingControllChar == ']' && parentStructure == JsonStructure::ARRAY) { break; } EditableJsonElement* next = new EditableJsonElement(valueValidator); addSibling(next); next->setContentOnly(content, startPos, afterList, deleteList); lastControl = next->getClosingControllChar(); } } void Framework::JSON::EditableJsonElement::format( int indent, bool insertNewLine) { Text content = this->content; content.removeWhitespaceAfter(0); int index = 0; if (insertNewLine) { content.insert(0, "\n"); for (int i = 0; i < indent; i++) { content.insert(1, " "); } index = indent * 4 + 1; } bool keyStart = false; bool keyEnd = false; bool colonFound = false; bool valueStart = false; bool valueEnd = false; bool escaped = false; for (int i = index; i < content.getLength(); i++) { if ((content[i] == '{' || content[i] == '[' || content[i] == ',') && !(keyStart && !keyEnd) && !(valueStart && !valueEnd)) { if (content[i] == ',') { i -= content.removeWhitespaceBefore(i); } content.removeWhitespaceAfter(i + 1); } else if ((content[i] == '}' || content[i] == ']') && !(keyStart && !keyEnd) && !(valueStart && !valueEnd)) { content.removeWhitespaceAfter(i + 1); if (!parent || parent->children != this) { i -= content.removeWhitespaceBefore(i); content.insert(i, '\n'); for (int j = 0; j < indent - 1; j++) { content.insert(i + 1, " "); } i += 1 + (indent > 0 ? (indent - 1) * 4 : 0); } else { i -= content.removeWhitespaceBefore(i); } } else if (content[i] == '"' && !escaped) { // format whitespaces arround values and keys if (!keyStart) { keyStart = true; } else { if (!keyEnd) { keyEnd = true; content.removeWhitespaceAfter(i + 1); } else { if (colonFound) { if (!valueStart) { valueStart = true; } else if (!valueEnd) { valueEnd = true; content.removeWhitespaceAfter(i + 1); } } } } } else if (content[i] == ':' && !(keyStart && !keyEnd) && !(valueStart && !valueEnd)) { if (!colonFound && keyEnd) { colonFound = true; content.removeWhitespaceAfter(i + 1); content.insert(i + 1, " "); } } else if (content[i] == '\t' && !(keyStart && !keyEnd) && !(valueStart && !valueEnd)) { content.remove(i, 1); i--; } else if (content[i] == '\\') { if ((keyStart && !keyEnd) || (valueStart && !valueEnd)) { escaped = !escaped; } } else { escaped = false; } } int count = 0; Array empty; const char* tmp = content.getText(); setContentOnly(&tmp, count, empty, empty); if (count < content.getLength()) { throw "Illegal State Exception: tree changed after formatting"; } if (children) { children->formatRecursive(indent + 1, true); } } void Framework::JSON::EditableJsonElement::formatRecursive( int indent, bool insertNewLine) { format(indent, insertNewLine); EditableJsonElement* tmp = siblings; while (tmp) { tmp->format(indent, true); tmp = tmp->siblings; } } bool Framework::JSON::EditableJsonElement::isValueValid( const char* content, int start, int end) { Text value(content, start, end - start); if (value.istGleich("null") || value.istGleich("true") || value.istGleich("false")) { return true; } RCArray* results = valueValidator->match(value, value.getLength()); bool ok = results->getEintragAnzahl() == 1; results->release(); return ok; } void Framework::JSON::EditableJsonElement::setContent( const char* content, int& startPos, Array& afterList) { if (children) { afterList.add(children); } if (siblings) { afterList.add(siblings); } EditableJsonElement* p = parent; EditableJsonElement* last = this; while (p) { if (p->siblings) { afterList.add(p->siblings); p->siblings = 0; } p->lastChild = last; last = p; p = p->parent; } lastChild = 0; children = 0; siblings = 0; if (parent) { parent->lastChild = this; } Array deleteList; setContentRecursive(&content, startPos, afterList, deleteList); if (content[startPos]) { EditableJsonElement* next = new EditableJsonElement(valueValidator); if (parent) { parent->addSibling(next); } else { addSibling(next); } next->setContent(content, startPos, afterList); } else if (afterList.getEintragAnzahl()) { if (openingControllChar) { while (afterList.getEintragAnzahl() && (!lastChild || !((openingControllChar == '[' && lastChild->closingControllChar == ']') || (openingControllChar == '{' && lastChild->closingControllChar == '}')))) { EditableJsonElement* next = afterList.get(0); afterList.remove(0); EditableJsonElement* end = next; while (end) { if ((end->closingControllChar == ']' && this->openingControllChar == '[') || (end->closingControllChar == '}' && this->openingControllChar == '{')) { if (end->siblings) { afterList.add(end->siblings, 0); end->siblings = 0; } break; } end = end->siblings; } addChildren(next); next->checkSyntax(); while (next->siblings) { next = next->siblings; next->checkSyntax(); } } } EditableJsonElement* p = parent; EditableJsonElement* c = this; while (afterList.getEintragAnzahl()) { if (p) { if (p->siblings) { afterList.add(p->siblings); p->siblings = 0; if (p->parent) { p->parent->lastChild = p; } } while ( afterList.getEintragAnzahl() && (!p->lastChild || !((p->openingControllChar == '[' && p->lastChild->closingControllChar == ']') || (p->openingControllChar == '{' && p->lastChild->closingControllChar == '}')))) { EditableJsonElement* next = afterList.get(0); afterList.remove(0); EditableJsonElement* end = next; while (end) { if ((end->closingControllChar == ']' && p->openingControllChar == '[') || (end->closingControllChar == '}' && p->openingControllChar == '{')) { if (end->siblings) { afterList.add(end->siblings, 0); end->siblings = 0; } break; } end = end->siblings; } p->addChildren(next); next->checkSyntax(); while (next->siblings) { next = next->siblings; next->checkSyntax(); } } c = p; p = p->parent; } else { while (afterList.getEintragAnzahl()) { EditableJsonElement* next = afterList.get(0); afterList.remove(0); c->addSibling(next); next->checkSyntax(); while (next->siblings) { next = next->siblings; next->checkSyntax(); } } } } } for (EditableJsonElement* e : deleteList) { e->release(); } } void Framework::JSON::EditableJsonElement::checkSyntax() { errors.leeren(); EditableJsonElement* before = zBefore(0, false, 0); ParserState p(parentStructure, before ? before->getContent()[before->getContent().getLength() - 1] == ',' : 0); SyntaxError error; key = ""; value = ""; int pos = 0; while (pos < content.getLength()) { const char* current = content.getText() + pos++; p.next(current, 0, error); if (error.getErrorType() != SyntaxErrorType::OTHER) { errors.add(error); error = SyntaxError(); } } if (error.getErrorType() != SyntaxErrorType::OTHER) { errors.add(error); error = SyntaxError(); } if (p.keyStart >= 0 && p.keyEnd >= p.keyStart && parentStructure == JsonStructure::OBJECT) { key += Text(content, p.keyStart, p.keyEnd - p.keyStart); } if (p.valueStart >= 0 && p.valueEnd >= p.valueStart) { value += Text(content, p.valueStart, p.valueEnd - p.valueStart); } if (p.valueValidationNeeded) { if (!isValueValid(value, 0, value.getLength())) { errors.add(SyntaxError( p.valueStart, p.valueEnd, SyntaxErrorType::INVALID_VALUE)); } } openingControllChar = p.openingControllChar; closingControllChar = p.closingControllChar; valueStart = p.valueStart; keyStart = p.keyStart; } void EditableJsonElement::setContent(const char* content) { Array afterList; int p = 0; setContent(content, p, afterList); } void EditableJsonElement::addChildren(EditableJsonElement* content) { JsonStructure currentStructure = openingControllChar == '[' ? JsonStructure::ARRAY : (openingControllChar == '{' ? JsonStructure::OBJECT : JsonStructure::VALUE); if (this->children) { this->lastChild->siblings = content; } else { this->children = content; this->lastChild = content; } if (content->parent != this) { content->parent = this; content->parentStructure = currentStructure; for (; lastChild->siblings; lastChild = lastChild->siblings) { lastChild->siblings->parent = this; lastChild->siblings->parentStructure = currentStructure; } } while (lastChild->siblings) { lastChild = lastChild->siblings; } } void Framework::JSON::EditableJsonElement::setNextSibling( EditableJsonElement* content) { this->siblings = content; EditableJsonElement* current = this; if (content->parent != parent) { for (; current->siblings; current = current->siblings) { current->siblings->parent = parent; current->siblings->parentStructure = parentStructure; } } if (parent) { parent->lastChild = current; while (parent->lastChild->siblings) { parent->lastChild = parent->lastChild->siblings; } } } void EditableJsonElement::addSibling(EditableJsonElement* content) { EditableJsonElement* current = parent ? parent->lastChild : this; while (current->siblings) { current = current->siblings; } current->siblings = content; if (content->parent != parent) { for (; current->siblings; current = current->siblings) { current->siblings->parent = parent; current->siblings->parentStructure = parentStructure; } } if (parent) { parent->lastChild = current; while (parent->lastChild->siblings) { parent->lastChild = parent->lastChild->siblings; } } } void Framework::JSON::EditableJsonElement::format() { int indent = getIndent(); formatRecursive(indent, false); } const Text& Framework::JSON::EditableJsonElement::getContent() { return content; } Text Framework::JSON::EditableJsonElement::getRecursiveContent() { Text result = content + (children ? children->getRecursiveContent() : Text("")); EditableJsonElement* s = siblings; while (s) { result += s->content + (s->children ? s->children->getRecursiveContent() : Text("")); s = s->siblings; } return result; } int Framework::JSON::EditableJsonElement::lineCount() { return newLineCount; } bool Framework::JSON::EditableJsonElement::lineCount(bool includeChildren, bool includeSiblings, EditableJsonElement* stop, int& count) { count += newLineCount; if (this == stop) { return false; } if (includeChildren && children) { if (!children->lineCount(true, true, stop, count)) { return false; } } if (includeSiblings && siblings) { return siblings->lineCount(true, true, stop, count); }; return true; } bool Framework::JSON::EditableJsonElement::isHidden() { return hidden; } void Framework::JSON::EditableJsonElement::setHidden(bool hidden) { this->hidden = hidden; } EditableJsonElement* Framework::JSON::EditableJsonElement::zParent() { return parent; } EditableJsonElement* Framework::JSON::EditableJsonElement::zMextSibling() { return siblings; } EditableJsonElement* Framework::JSON::EditableJsonElement::zFirstChildren() { return children; } EditableJsonElement* Framework::JSON::EditableJsonElement::zLastChildren() { return lastChild; } char Framework::JSON::EditableJsonElement::getOpeningControllChar() { return openingControllChar; } char Framework::JSON::EditableJsonElement::getClosingControllChar() { return closingControllChar; } Text& Framework::JSON::EditableJsonElement::getKey() { return key; } Text& Framework::JSON::EditableJsonElement::getValue() { return value; } void Framework::JSON::EditableJsonElement::disconnect() { children = 0; lastChild = 0; siblings = 0; parent = 0; } void Framework::JSON::EditableJsonElement::removeChild( EditableJsonElement* zElement) { // TODO: needed? if (children == zElement) { children = 0; lastChild = 0; } else { EditableJsonElement* current = children; while (current) { if (current->siblings == zElement) { current->siblings = 0; if (current->parent) { current->parent->lastChild = current; } break; } current = current->siblings; } } } void Framework::JSON::EditableJsonElement::getWordBounds( int pos, int* left, int* right) { bool escaped = false; int lastStartStr = -1; int lastBlank = 0; bool found = false; int len = content.getLength(); for (int i = 0; i < len; i++) { if ((content[i] == ' ' || content[i] == ',' || content[i] == '\n') && lastStartStr == -1) { if (i >= pos) { if (left) { *left = lastBlank; } if (right) { *right = i; } found = true; break; } lastBlank = i + 1; } else if (content[i] == '"') { if (lastStartStr == -1) { lastStartStr = i + 1; if (i >= pos) { if (left) { *left = i; } if (right) { *right = i + 1; } found = true; break; } } else if (!escaped) { if (i >= pos) { if (left) { *left = lastStartStr; } if (right) { *right = i; } found = true; break; } lastStartStr = -1; } } else if (content[i] == '\\' && lastStartStr >= 0) { escaped = !escaped; } else { escaped = false; } } if (!found && lastBlank >= 0) { if (left) { *left = lastBlank; } if (right) { *right = len; } } } bool Framework::JSON::EditableJsonElement::hasError(int column) const { for (const SyntaxError& error : errors) { if (error.getStart() <= column && error.getEnd() >= column) { return true; } } return false; } SyntaxError Framework::JSON::EditableJsonElement::getError(int column) const { for (const SyntaxError& error : errors) { if (error.getStart() <= column && error.getEnd() >= column) { return error; } } return SyntaxError(); } bool Framework::JSON::EditableJsonElement::hasError() const { return errors.getEintragAnzahl() > 0; } void Framework::JSON::EditableJsonElement::makeVisible() { EditableJsonElement* current = parent; while (current) { current->setHidden(false); current = current->zParent(); } } bool Framework::JSON::EditableJsonElement::isVisible() const { EditableJsonElement* current = parent; while (current) { if (current->isHidden()) { return false; } current = current->zParent(); } return true; } int Framework::JSON::EditableJsonElement::getIndent() const { int indent = 0; EditableJsonElement* tmp = parent; while (tmp) { indent++; tmp = tmp->parent; } return indent; } int Framework::JSON::EditableJsonElement::getColor(int index) { int color = 0xFFA0A0A0; if (hasError(index)) { color = 0xFFFF3030; } else if (keyStart >= 0 && index >= keyStart - 1 && index < keyStart + key.getLength() + 1) { color = 0xFF7070C0; } else if (valueStart >= 0 && index >= valueStart && index < valueStart + value.getLength()) { if (value[0] == '"') { color = 0xFF70C070; } else if (value.istGleich("null")) { color = 0xFFC0A070; } else if (value.istGleich("true") || value.istGleich("false")) { color = 0xFFC070C0; } else { color = 0xFF70C0C0; } } return color; } void Framework::JSON::EditableJsonElement::removeUntil( int startIndex, EditableJsonElement* end, int endIndex) { Text* result = content.getTeilText(0, startIndex); if (end == this) { result->append(content.getTeilText(endIndex)); setContent(*result); result->release(); return; } else { Array toDelete; Stack stack; if (siblings) { stack.push(siblings); } if (children) { stack.push(children); } if (!stack.getSize()) { EditableJsonElement* p = parent; while (p) { if (p->siblings) { stack.push(p->siblings); break; } p = p->parent; } } if (stack.getSize()) { EditableJsonElement* next = stack.pop(); while (true) { if (next->siblings) { stack.push(siblings); } if (next->children) { stack.push(children); } if (!stack.getSize() && next != end) { EditableJsonElement* p = next->parent; while (p) { if (p->siblings) { stack.push(p->siblings); break; } p = p->parent; } } if (next->parent) { next->parent->removeChild(next); } else { EditableJsonElement* p = this; while (p->parent) { p = p->parent; } while (p->siblings != next) { p = p->siblings; } p->siblings = 0; } toDelete.add(next); if (!stack.getSize()) { next = 0; break; } if (next == end) { break; } next = stack.pop(); } if (next == end) { result->append(next->content.getTeilText(endIndex)); } for (EditableJsonElement* del : toDelete) { del->disconnect(); del->release(); } Array afterList; while (stack.getSize()) { afterList.add(stack.pop()); } int p = 0; setContent(*result, p, afterList); } else { setContent(*result); result->release(); return; } } } EditableJsonElement* Framework::JSON::EditableJsonElement::zBefore( EditableJsonElement* zRoot, bool onlyVisible, int* lineCount) { if (this == zRoot) { return 0; } EditableJsonElement* parent = this->parent; EditableJsonElement* current = parent ? parent->children : zRoot; EditableJsonElement* last = parent; while (current) { if (current == this) { break; } last = current; current = current->siblings; } if (last && last->children != this && (!last->hidden || !onlyVisible)) { current = last->lastChild; while (current) { last = current; if (current->children) { if ((!current->hidden || !onlyVisible)) { current = current->lastChild; } else if (*lineCount) { EditableJsonElement* e = current->children; while (e) { int lc = 0; e->lineCount(true, false, 0, lc); *lineCount -= lc; e = e->siblings; } break; } } else { break; } } } else if (last && last->hidden && onlyVisible && lineCount) { EditableJsonElement* e = last->children; while (e) { int lc = 0; e->lineCount(true, false, 0, lc); *lineCount -= lc; e = e->siblings; } } return last; } EditableJsonElement* Framework::JSON::EditableJsonElement::zAfter( bool onlyVisible, int* lineCount) { EditableJsonElement* zElement = this; if (zElement->children) { if (!zElement->hidden || !onlyVisible) { return zElement->children; } else if (lineCount) { EditableJsonElement* e = zElement->children; while (e) { int lc = 0; e->lineCount(true, false, 0, lc); *lineCount += lc; e = e->siblings; } } } if (zElement->siblings) { return zElement->siblings; } while (zElement->parent) { zElement = zElement->parent; if (zElement->siblings) { return zElement->siblings; } } return 0; } JsonEditor::JsonEditor(UIInit uiInit) : ZeichnungHintergrund(), uiInit(uiInit) { valueValidator = Regex::parse("^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$"); validator = 0; content = 0; renderStart = {0, 0}; renderStartLine = 0; lineCount = 0; cursorPos.x = -1; cursorPos.y = -1; textCursor = {0, 0}; selectionStart = {0, 0}; renderStartOffset = 0; selectionEnd = {0, 0}; textRenderer = new TextRenderer(); textRenderer->setSchriftSize(12); textRenderer->setZeilenAbstand(2); textRenderer->setZeichenAbstand(1); pressed = 0; time = 0; drag = 0; drawCursor = 0; dragSart = {-1, -1}; pressedPos = {-1, -1}; dragStartPos = {0, -1}; timeSicePress = 0.0; lastClickCursorPos = {0, 0}; tps = 0; renderings = 0; renderedLines = 0; renderStopLine = 0; errorDescription = uiInit.createTextFeld(uiInit.initParam); errorDescription->setStyle(TextFeld::Style::Text | TextFeld::Style::Center | TextFeld::Style::Rahmen | TextFeld::Style::Hintergrund | TextFeld::Style::HAlpha); errorDescription->removeStyle(TextFeld::Style::Sichtbar); errorDescription->setSchriftFarbe(0xFFFFFFFF); errorDescription->setRahmenFarbe(0xFFA0A0A0); errorDescription->setHintergrundFarbe(0xA0000000); hasSyntaxError = false; } JsonEditor::~JsonEditor() { if (content) content->release(); if (validator) validator->release(); textRenderer->release(); errorDescription->release(); } void Framework::JSON::JsonEditor::doMausEreignis(MausEreignis& me, bool userRet) { LOCK(&cs); rend |= cursorPos.x != me.mx || cursorPos.y != me.my; cursorPos.x = me.mx; cursorPos.y = me.my; if (me.id == ME_RLinks) { pressedPos = cursorPos; if (timeSicePress < 0.5) { if (lastClickCursorPos.line == textCursor.line && lastClickCursorPos.column == textCursor.column && lastClickCursorPos.line && lastClickCursorPos.column > 0) { EditableJsonElement* current = textCursor.line; int start = 0; int end = 0; current->getWordBounds(textCursor.column, &start, &end); selectionStart = {current, start}; selectionEnd = {current, end}; unifyPosition(selectionEnd); textCursor = selectionEnd; } } lastClickCursorPos = textCursor; pressed = true; drag = false; rend = 1; time = 0; timeSicePress = 0.0; } if (me.id == ME_PLinks || (me.id == ME_Betritt && getMausStand(M_Links))) { if (me.mx >= getInnenBreite() - 15) { int line = (int)(me.my / ((double)getInnenHeight() / lineCount)); if (line >= lineCount) { line = lineCount - 1; } scrollToLine(line + 1, Center); } if (!drag) { drag = true; textCursor = getScreenPosition(cursorPos.x, cursorPos.y); selectionStart = textCursor; selectionEnd = textCursor; dragSart = cursorPos; dragStartPos = textCursor; rend = 1; time = 0; } } if (me.id == ME_Bewegung) { if (drag) { if (dragSart.x >= getInnenBreite() - 15) { int line = (int)(me.my / ((double)getInnenHeight() / lineCount)); if (line >= lineCount) { line = lineCount - 1; } scrollToLine(line + 1, Center); } else { EditorPosition pos = getScreenPosition(cursorPos.x, cursorPos.y); if (pos.line == dragStartPos.line) { if (pos.column < dragStartPos.column) { selectionStart = pos; selectionEnd = dragStartPos; } else if (pos.column > dragStartPos.column) { selectionStart = dragStartPos; selectionEnd = pos; } } else { if (cursorPos.y < dragSart.y) { selectionStart = pos; selectionEnd = dragStartPos; } else if (cursorPos.y > dragSart.y) { selectionStart = dragStartPos; selectionEnd = pos; } } textCursor = pos; } } } if (me.id == ME_UScroll) { if (renderStart.column > 0) { const char* content = renderStart.line->getContent(); int index = renderStart.column - 2; while (index > 0 && content[index] != '\n') { index--; } if (content[index] == '\n') { renderStart.column = index + 1; rend = 1; renderStartLine--; unifyPosition(renderStart); return; } } while (true) { EditableJsonElement* next = renderStart.line->zBefore(content, true, &renderStartLine); if (next) { renderStart.line = next; const char* content = next->getContent(); int index = next->getContent().getLength() - 1; while (index > 0 && content[index] != '\n') { index--; } if (content[index] == '\n') { renderStart.column = index + 1; rend = 1; renderStartLine--; unifyPosition(renderStart); return; } } else { renderStart.column = 0; rend = 1; renderStartLine = 1; return; } } } if (me.id == ME_DScroll) { const char* content = renderStart.line->getContent(); int length = renderStart.line->getContent().getLength(); int index = renderStart.column; while (index < length && content[index] != '\n') { index++; } if (content[index] == '\n') { renderStart.column = index + 1; rend = 1; renderStartLine++; unifyPosition(renderStart); return; } while (true) { EditableJsonElement* next = renderStart.line->zAfter(true, &renderStartLine); if (next) { renderStart.line = next; const char* content = next->getContent(); int length = next->getContent().getLength(); int index = 0; while (index < length && content[index] != '\n') { index++; } if (content[index] == '\n') { renderStart.column = index + 1; rend = 1; renderStartLine++; unifyPosition(renderStart); return; } } else { return; } } } } EditorPosition Framework::JSON::JsonEditor::getScreenPosition( int localX, int localY) { if (!renderStart.line) return {0, 0}; int y = 0; int lineNumberWidth = 0; for (int i = lineCount; i > 0; i /= 10) { lineNumberWidth++; } lineNumberWidth = lineNumberWidth * textRenderer->getMaxCharWidth() + textRenderer->getZeichenAbstand() * (lineNumberWidth - 1); int x = lineNumberWidth + 26; EditableJsonElement* current = renderStart.line; int index = renderStart.column; EditableJsonElement* last = current; while (y < gr.y && current) { int length = current->getContent().getLength(); const char* content = current->getContent(); for (int i = index; i < length; i++) { if (y >= localY - textRenderer->getZeilenHeight() - textRenderer->getZeilenAbstand() && x >= localX - textRenderer->getCharWidth(content[i]) / 2 - textRenderer->getZeichenAbstand()) { EditorPosition result = {current, i}; unifyPosition(result); return result; } if (content[i] == '\n') { if (y >= localY - textRenderer->getZeilenHeight() - textRenderer->getZeilenAbstand()) { EditorPosition result = {current, i}; unifyPosition(result); return result; } y += textRenderer->getZeilenHeight() + textRenderer->getZeilenAbstand(); x = lineNumberWidth + 26; } else { x += textRenderer->getCharWidth(content[i]) + textRenderer->getZeichenAbstand(); } } last = current; current = current->zAfter(true, 0); index = 0; } return {last, last->getContent().getLength()}; } void Framework::JSON::JsonEditor::deleteSelection() { if (selectionStart.line && selectionEnd.line) { selectionStart.line->removeUntil( selectionStart.column, selectionEnd.line, selectionEnd.column); selectionEnd = selectionStart; textCursor = selectionStart; fixTree(selectionStart.line); if (dragStartPos.line) { dragStartPos = selectionStart; } if (lastClickCursorPos.line) { lastClickCursorPos = selectionStart; } } } void Framework::JSON::JsonEditor::unifyPosition(EditorPosition& pos) { while (pos.line && pos.column >= pos.line->getContent().getLength()) { if (pos.line->zFirstChildren() && pos.line->isVisible()) { pos = {pos.line->zFirstChildren(), pos.column - pos.line->getContent().getLength()}; } else if (pos.line->zMextSibling()) { pos = {pos.line->zMextSibling(), pos.column - pos.line->getContent().getLength()}; } else { EditableJsonElement* p = pos.line->zParent(); while (p) { if (p->zMextSibling()) { pos = {p->zMextSibling(), 0}; break; } p = p->zParent(); } if (!p) { pos.column = pos.line->getContent().getLength(); break; } } } } void Framework::JSON::JsonEditor::fixTree(EditableJsonElement* zElement) { EditableJsonElement* before = zElement->zBefore(content, false, 0); int beforeLength = before ? before->getContent().getLength() : 0; if (zElement->hasError() && before) { Text content = before->getContent(); before->setContent(content); if (before->getContent().getLength() != beforeLength) { before->makeVisible(); } } if (textCursor.line == zElement) { textCursor.column += beforeLength; textCursor.line = before ? before : zElement; unifyPosition(textCursor); } if (renderStart.line == zElement) { renderStart.column += beforeLength; renderStart.line = before ? before : zElement; unifyPosition(renderStart); } } void Framework::JSON::JsonEditor::setFont(Schrift* schrift) { textRenderer->setSchriftZ(schrift); } void Framework::JSON::JsonEditor::setFontSize(int size) { textRenderer->setSchriftSize(size); textRenderer->setZeilenAbstand(size / 6); textRenderer->setZeichenAbstand(size / 8); } void JsonEditor::setContent(Text content) { if (this->content) { this->content->release(); this->content = 0; this->renderStart = {0, 0}; } this->content = new EditableJsonElement( dynamic_cast*>(valueValidator->getThis())); content.ersetzen("\r", ""); content.ersetzen("\t", " "); this->content->setContent(content); textCursor = {this->content, 0}; selectionStart = {this->content, 0}; selectionEnd = {this->content, 0}; format(); } void Framework::JSON::JsonEditor::setContent(JSONValue* content) { setContent(content->toString()); content->release(); } void Framework::JSON::JsonEditor::format() { if (content) { content->format(); lineCount = 0; content->lineCount(true, true, 0, lineCount); } } void Framework::JSON::JsonEditor::setValidator( Validator::DataValidator* validator) { if (this->validator) this->validator->release(); this->validator = validator; // if (content) validate(); TODO: validate attributes } void Framework::JSON::JsonEditor::doTastaturEreignis(TastaturEreignis& te) { LOCK(&cs); if (!textCursor.line) { return; } bool shift = getTastenStand(T_Shift); bool strg = getTastenStand(T_Strg); if (te.id == TE_Press) { switch (te.virtualKey) { case T_Tab: { Text illegalStarts = "[]{},\": "; if (shift) { bool lastStr = textCursor.line->getContent().getLength() > textCursor.column ? textCursor.line->getContent()[textCursor.column] == '"' : false; int nextColumn = textCursor.column - 1; EditableJsonElement* nextLine = textCursor.line; while (nextLine) { if (nextColumn < 0) { nextLine = nextLine->zBefore(content, true, 0); if (!nextLine) { return; } nextColumn = nextLine->getContent().getLength(); } int start = 0; int end = 0; nextLine->getWordBounds(nextColumn, &start, &end); if ((start != end && !illegalStarts.hat( nextLine->getContent()[start])) || (lastStr && nextLine->getContent()[nextColumn] == '"')) { if (lastStr && nextLine->getContent()[nextColumn] == '"') { start = end; } if (selectionStart.column != start || selectionStart.line != nextLine || selectionEnd.column != end || selectionEnd.line != nextLine) { selectionStart = {nextLine, start}; selectionEnd = {nextLine, end}; unifyPosition(selectionEnd); textCursor = selectionEnd; scrollToLine(textCursor, Top); te.verarbeitet = true; time = 0; rend = 1; return; } } lastStr = nextLine->getContent()[nextColumn] == '"'; nextColumn--; } } else { bool lastStr = textCursor.line->getContent().getLength() > textCursor.column ? textCursor.line->getContent()[textCursor.column] == '"' : false; int nextColumn = textCursor.column + 1; EditableJsonElement* nextLine = textCursor.line; while (nextLine) { if (nextColumn > nextLine->getContent().getLength()) { nextLine = nextLine->zAfter(true, 0); if (!nextLine) { return; } nextColumn = 0; } int start = 0; int end = 0; nextLine->getWordBounds(nextColumn, &start, &end); if (((start != end && !illegalStarts.hat( nextLine->getContent()[start])) || (lastStr && nextLine->getContent()[nextColumn] == '"')) && (selectionStart.column != start || selectionStart.line != nextLine || selectionEnd.column != end || selectionEnd.line != nextLine)) { selectionStart = {nextLine, start}; selectionEnd = {nextLine, end}; unifyPosition(selectionEnd); textCursor = selectionEnd; scrollToLine(textCursor, Bottom); te.verarbeitet = true; time = 0; rend = 1; return; } lastStr = nextLine->getContent()[nextColumn] == '"'; nextColumn++; } } } return; case T_Entf: if (selectionStart.line == selectionEnd.line && selectionStart.column == selectionEnd.column) { if (textCursor.column < textCursor.line->getContent().getLength()) { Text content = textCursor.line->getContent(); content.remove(textCursor.column, textCursor.column + 1); textCursor.line->setContent(content); fixTree(textCursor.line); } } else { deleteSelection(); } break; case T_BackSpace: if (selectionStart.line == selectionEnd.line && selectionStart.column == selectionEnd.column) { if (textCursor.column > 0) { Text content = textCursor.line->getContent(); content.remove(textCursor.column - 1, textCursor.column); textCursor.line->setContent(content); textCursor.column--; fixTree(textCursor.line); } else { EditableJsonElement* before = textCursor.line->zBefore(content, false, 0); if (before) { before->makeVisible(); Text content = before->getContent(); content.remove( content.getLength() - 1, content.getLength()); before->setContent(content); textCursor = {before, content.getLength()}; fixTree(before); scrollToLine(textCursor, Top); } } } else { deleteSelection(); } break; case T_Enter: { deleteSelection(); Text content = textCursor.line->getContent(); content.insert(textCursor.column, "\n"); textCursor.column += 1; textCursor.line->setContent(content); fixTree(textCursor.line); scrollToLine(textCursor, Bottom); } break; case T_Links: { int nextColumn = textCursor.column - 1; EditableJsonElement* nextLine = textCursor.line; while (nextColumn < 0) { nextLine = textCursor.line->zBefore(content, true, 0); if (!nextLine) { return; } nextColumn = nextLine->getContent().getLength() - 1; } if (strg) { int start = 0; nextLine->getWordBounds(nextColumn, &start, 0); nextColumn = start; } if (shift) { if (textCursor.line == selectionStart.line && textCursor.column == selectionStart.column) { selectionStart = {nextLine, nextColumn}; unifyPosition(selectionStart); textCursor = selectionStart; } else { selectionEnd = {nextLine, nextColumn}; unifyPosition(selectionEnd); textCursor = selectionEnd; } } else { textCursor = {nextLine, nextColumn}; unifyPosition(textCursor); selectionStart = textCursor; selectionEnd = textCursor; } scrollToLine(textCursor, Top); te.verarbeitet = true; time = 0; rend = 1; return; } case T_Oben: { EditableJsonElement* current = textCursor.line; int count = 0; bool found = 0; int index = textCursor.column - 1; const char* content = current->getContent(); while (current) { while (index < 0) { current = current->zBefore(this->content, true, 0); if (!current) { break; } content = current->getContent(); index = current->getContent().getLength() - 1; } if (!current) { break; } if (content[index] == '\n') { if (found) { break; } found = 1; } if (!found) { count++; } index--; } if (!current) { break; } index++; while (current && count > 0) { while (index >= current->getContent().getLength()) { current = current->zAfter(true, 0); if (!current) { break; } content = current->getContent(); index = 0; } if (!current) { break; } if (content[index] == '\n') { break; } count--; index++; } if (!current) { break; } if (shift) { if (textCursor.line == selectionStart.line && textCursor.column == selectionStart.column) { selectionStart = {current, index}; unifyPosition(selectionStart); textCursor = selectionStart; } else { selectionEnd = {current, index}; unifyPosition(selectionEnd); textCursor = selectionEnd; } } else { textCursor = {current, index}; unifyPosition(textCursor); selectionStart = textCursor; selectionEnd = textCursor; } scrollToLine(textCursor, Top); te.verarbeitet = true; time = 0; rend = 1; return; } case T_Rechts: { int nextColumn = textCursor.column + 1; EditableJsonElement* nextLine = textCursor.line; if (nextColumn > nextLine->getContent().getLength()) { nextLine = textCursor.line->zAfter(true, 0); if (!nextLine) { return; } nextColumn = 0; } if (strg) { int end = 0; nextLine->getWordBounds(nextColumn, 0, &end); nextColumn = end; } if (shift) { if (textCursor.line == selectionEnd.line && textCursor.column == selectionEnd.column) { selectionEnd = {nextLine, nextColumn}; unifyPosition(selectionEnd); textCursor = selectionEnd; } else { selectionStart = {nextLine, nextColumn}; unifyPosition(selectionStart); textCursor = selectionStart; } } else { textCursor = {nextLine, nextColumn}; unifyPosition(textCursor); selectionStart = textCursor; selectionEnd = textCursor; } scrollToLine(textCursor, Bottom); te.verarbeitet = true; time = 0; rend = 1; return; } case T_Unten: { EditableJsonElement* current = textCursor.line; int count = 0; int index = textCursor.column - 1; const char* content = current->getContent(); while (current) { while (index < 0) { current = current->zBefore(this->content, true, 0); if (!current) { break; } content = current->getContent(); index = current->getContent().getLength() - 1; } if (!current) { break; } if (content[index] == '\n') { break; } count++; index--; } bool found = 0; current = textCursor.line; index = textCursor.column; content = current->getContent(); while (current && (count > 0 || !found)) { while (index >= current->getContent().getLength()) { current = current->zAfter(true, 0); if (!current) { break; } content = current->getContent(); index = 0; } if (found) { count--; } if (content[index] == '\n') { if (found) { break; } found = 1; } index++; } if (shift) { if (textCursor.line == selectionEnd.line && textCursor.column == selectionEnd.column) { selectionEnd = {current, index}; unifyPosition(selectionEnd); textCursor = selectionEnd; } else { selectionStart = {current, index}; unifyPosition(selectionStart); textCursor = selectionStart; } } else { textCursor = {current, index}; unifyPosition(textCursor); selectionStart = textCursor; selectionEnd = textCursor; } scrollToLine(textCursor, Bottom); te.verarbeitet = true; time = 0; rend = 1; return; } default: if (strg) { if (te.virtualKey == 'c' || te.virtualKey == 'C') { Text content = getSelectedContent(); TextKopieren(content); return; } else if (te.virtualKey == 'x' || te.virtualKey == 'X') { Text content = getSelectedContent(); TextKopieren(content); deleteSelection(); scrollToLine(textCursor, Top); return; } else if (te.virtualKey == 'v' || te.virtualKey == 'V') { deleteSelection(); EditableJsonElement* changed = textCursor.line; Text content = changed->getContent(); const char* inserted = TextInsert(); content.insert(textCursor.column, inserted); textCursor.column += textLength(inserted); unifyPosition(textCursor); scrollToLine(textCursor, Bottom); break; } else if (te.virtualKey == 'a' || te.virtualKey == 'A') { selectionStart = {content, 0}; EditableJsonElement* element = content; bool first = true; while (element->zMextSibling()) { element = element->zMextSibling(); } while (element->zLastChildren()) { element = element->zLastChildren(); } selectionEnd = {element, element->getContent().getLength()}; textCursor = selectionEnd; te.verarbeitet = true; time = 0; rend = 1; scrollToLine(textCursor, Bottom); return; } else if (te.virtualKey == 'f' || te.virtualKey == 'F') { format(); return; } } if (istSchreibbar(te.taste[0])) { deleteSelection(); Text content = textCursor.line->getContent(); content.insert(textCursor.column, te.taste); textCursor.column += textLength(te.taste); textCursor.line->setContent(content); fixTree(textCursor.line); } else { return; } } selectionStart = textCursor; selectionEnd = textCursor; te.verarbeitet = true; lineCount = 0; content->lineCount(true, true, 0, lineCount); } time = 0; rend = 1; } bool Framework::JSON::JsonEditor::tick(double tickVal) { LOCK(&cs); time += tickVal; timeSicePress += tickVal; if (time > 1.0) { tps = renderings / time; time = 0.0; renderings = 0; } rend |= drawCursor != (time < 0.5); drawCursor = time < 0.5; errorDescription->tick(tickVal); return ZeichnungHintergrund::tick(tickVal); } void Framework::JSON::JsonEditor::render(Bild& rObj) { LOCK(&cs); renderings++; renderedLines = 0; errorDescription->removeStyle(TextFeld::Style::Sichtbar); ZeichnungHintergrund::render(rObj); if (textRenderer->zSchrift() && content && rObj.setDrawOptions( pos + Punkt(getRahmenBreite(), getRahmenBreite()), Punkt(getInnenBreite(), getInnenHeight()))) { if (!renderStart.line) { renderStart = {content, 0}; renderStartLine = 1; } int y = 0; int lineNumberWidth = 0; for (int i = lineCount; i > 0; i /= 10) { lineNumberWidth++; } lineNumberWidth = lineNumberWidth * textRenderer->getMaxCharWidth() + textRenderer->getZeichenAbstand() * (lineNumberWidth - 1); bool selected = false; EditableJsonElement* element = content; while (element) { if (element == selectionStart.line) { if (element != renderStart.line) { selected = true; } else { if (selectionStart.column < renderStart.column) { selected = true; } } } if (element == selectionEnd.line) { if (element != renderStart.line) { selected = false; break; } else { if (selectionEnd.column < renderStart.column) { selected = false; } } } if (element == renderStart.line) { break; } element = element->zAfter(false, 0); } int line = renderStartLine; int x = lineNumberWidth + 26; EditableJsonElement* current = renderStart.line; EditableJsonElement* highlightStop = 0; int index = renderStart.column; bool treeBranchRendered = false; bool highlighted = false; int numberWidth = textRenderer->getTextBreite(Text(line)); textRenderer->renderText( 5 + lineNumberWidth - numberWidth, y, Text(line), rObj, 0xFF707070); bool whiteSpaceOnly = true; while (y < gr.y && current) { const Text& content = current->getContent(); int column = index; for (int i = index; i < content.getLength() && y < gr.y; i++, column++) { if (selectionStart.line == current && selectionStart.column == i) { selected = true; } if (selectionEnd.line == current && selectionEnd.column == i) { selected = false; } int color = current->getColor(i); int cursorX = x - 1; if (textCursor.line == current && textCursor.column == i && drawCursor) { rObj.drawLinieV( cursorX, y, textRenderer->getSchriftSize(), 0xFFC0C0C0); } if (content[i] == '\n') { column = -1; highlighted = false; if (!treeBranchRendered) { // | in tree view rObj.drawLinieV(lineNumberWidth + 13, y - (line > 1 ? textRenderer->getSchriftSize() / 6 : 0), textRenderer->getSchriftSize() + (line > 1 ? textRenderer->getSchriftSize() / 6 : 0), 0xFF808080); if (highlightStop) { // hover effect in tree view rObj.fillRegion(lineNumberWidth + 10, y - textRenderer->getSchriftSize() / 6, 7, textRenderer->getSchriftSize() + textRenderer->getSchriftSize() / 6, 0xFF505050); } } if (selected) { rObj.fillRegion(x, y, textRenderer->getTextBreite(" "), textRenderer->getZeilenHeight() + textRenderer->getZeilenAbstand(), 0xFF502020); } x = lineNumberWidth + 26; y += textRenderer->getZeilenHeight() + textRenderer->getZeilenAbstand(); whiteSpaceOnly = true; line++; renderedLines++; treeBranchRendered = false; int numberWidth = textRenderer->getTextBreite(Text(line)); textRenderer->renderText(5 + lineNumberWidth - numberWidth, y, Text(line), rObj, 0xFF707070); } else { textRenderer->renderChar(x, y, content[i], rObj, color, false, selected, 0xFF502020); } if (cursorPos.x > cursorX && cursorPos.x <= x && cursorPos.y >= y && cursorPos.y <= y + textRenderer->getZeilenHeight()) { if (current->hasError(i)) { const SyntaxError& error = current->getError(i); errorDescription->setText(errors[error.getErrorType()]); errorDescription->setSize( errorDescription->getNeededWidth() + 6, errorDescription->getNeededHeight() + 6); errorDescription->setPosition( cursorPos.x, cursorPos.y + 20); if (errorDescription->getX() + errorDescription->getBreite() > getInnenBreite()) { errorDescription->setX( getInnenBreite() - errorDescription->getBreite() - 5); } if (errorDescription->getY() + errorDescription->getHeight() > getInnenHeight()) { errorDescription->setY( getInnenHeight() - errorDescription->getHeight() - 5); } errorDescription->addStyle(TextFeld::Style::Sichtbar); } } if (column % 4 == 0 && content[i] == ' ' && whiteSpaceOnly) { rObj.drawLinieV( cursorX + 1 + textRenderer->getTextBreite(" ") / 2, y + 2, textRenderer->getSchriftSize() - 4, 0xFF353535); } whiteSpaceOnly &= content[i] == ' ' || content[i] == '\n'; } if (selectionStart.line == current && selectionStart.column == content.getLength()) { selected = true; } if (selectionEnd.line == current && selectionEnd.column == content.getLength()) { selected = false; } if (textCursor.line == current && textCursor.column == content.getLength() && drawCursor) { rObj.drawLinieV( x - 1, y, textRenderer->getSchriftSize(), 0xFFC0C0C0); } if (current->zFirstChildren()) { if (pressed && pressedPos.x >= lineNumberWidth + 10 && pressedPos.x <= lineNumberWidth + 16 && pressedPos.y >= y + textRenderer->getSchriftSize() / 2 - 4 && pressedPos.y <= y + textRenderer->getSchriftSize() / 2 + 2) { pressed = false; current->setHidden(!current->isHidden()); } if (cursorPos.x >= lineNumberWidth + 10 && cursorPos.x <= lineNumberWidth + 16 && cursorPos.y >= y + textRenderer->getSchriftSize() / 2 - 4 && cursorPos.y <= y + textRenderer->getSchriftSize() / 2 + 2 && !current->isHidden()) { highlightStop = current->zLastChildren(); } if (current->isHidden()) { if (!treeBranchRendered) { rObj.fillRegion(lineNumberWidth + 10, y + textRenderer->getSchriftSize() / 2 - 4, 6, 6, 0xFF000000); rObj.drawLinie( Punkt(lineNumberWidth + 10, y + textRenderer->getSchriftSize() / 2 - 4), Punkt(lineNumberWidth + 16, y + textRenderer->getSchriftSize() / 2 - 1), 0xFF808080); rObj.drawLinie( Punkt(lineNumberWidth + 10, y + textRenderer->getSchriftSize() / 2 + 2), Punkt(lineNumberWidth + 16, y + textRenderer->getSchriftSize() / 2 - 1), 0xFF808080); } for (char c : " ... ") { textRenderer->renderChar(x, y, c, rObj, 0xFF505050, false, selected, 0xFF502020); } if (current->getOpeningControllChar() == '{') { textRenderer->renderChar(x, y, '}', rObj, 0xFF505050, false, selected, 0xFF502020); } else if (current->getOpeningControllChar() == '[') { textRenderer->renderChar(x, y, ']', rObj, 0xFF505050, false, selected, 0xFF502020); } } else { if (!treeBranchRendered) { rObj.fillRegion(lineNumberWidth + 10, y + textRenderer->getSchriftSize() / 2 - 4, 6, 6, 0xFF000000); rObj.drawLinie( Punkt(lineNumberWidth + 10, y + textRenderer->getSchriftSize() / 2 - 4), Punkt(lineNumberWidth + 13, y + textRenderer->getSchriftSize() / 2 + 2), 0xFF808080); rObj.drawLinie( Punkt(lineNumberWidth + 16, y + textRenderer->getSchriftSize() / 2 - 4), Punkt(lineNumberWidth + 13, y + textRenderer->getSchriftSize() / 2 + 2), 0xFF808080); } } treeBranchRendered = true; } if (!current->zMextSibling() && current->zParent()) { // |_ in tree view rObj.drawLinieH(lineNumberWidth + 14, y + textRenderer->getSchriftSize() - 1, 3, 0xFF808080); } rObj.drawLinieV(x, y, textRenderer->getSchriftSize(), 0xFF30C030); // TODO: debug tree seperator if (current == highlightStop) { highlightStop = 0; if (!treeBranchRendered) { // hover effect in tree view rObj.fillRegion(lineNumberWidth + 10, y - textRenderer->getSchriftSize() / 6, 7, textRenderer->getSchriftSize() + textRenderer->getSchriftSize() / 6, 0xFF505050); highlighted = true; } } current = current->zAfter(true, &line); index = 0; } if (!treeBranchRendered && !highlighted) { // | in tree view rObj.drawLinieV(lineNumberWidth + 13, y - (line > 1 ? textRenderer->getSchriftSize() / 6 : 0), textRenderer->getSchriftSize() + (line > 1 ? textRenderer->getSchriftSize() / 6 : 0), 0xFF808080); } EditableJsonElement* resnderStopElement = current; renderStopLine = line; textRenderer->renderText(5, y + textRenderer->getZeilenHeight() + textRenderer->getZeilenAbstand(), Text("FPS: ") + Text(tps), rObj, 0xFFFFFFFF); bool visible = false; double sy = 0.0; double yOffset = (double)getInnenHeight() / lineCount; current = content; EditableJsonElement* hiddenStop = 0; while (current) { if (hiddenStop == current) { hiddenStop = 0; } if (current == renderStart.line) { visible = true; } if (current == resnderStopElement) { visible = false; } int f = 0xFF303030; if (visible) { f = 0xFFA0A0A0; } if (hiddenStop) { f = 0xFF505050; } if (current->hasError()) { f = 0xFFFF0000; } double size = current->lineCount() * yOffset; if ((int)(sy + size) > (int)sy || current->hasError()) { rObj.fillRegion(getInnenBreite() - 15, (int)sy, 15, (int)(sy + MAX(yOffset, 1)), f); } if (current->isHidden()) { if (!hiddenStop) { hiddenStop = current->zAfter(true, 0); } } current = current->zAfter(false, 0); sy += size; } errorDescription->render(rObj); rObj.releaseDrawOptions(); } } Text Framework::JSON::JsonEditor::getContent() { return content ? content->getRecursiveContent() : Text(""); } Text Framework::JSON::JsonEditor::getSelectedContent() { Text result = ""; if (selectionStart.line && selectionEnd.line) { if (selectionStart.line == selectionEnd.line) { result.append(selectionStart.line->getContent().getTeilText( selectionStart.column, selectionEnd.column)); } else { result.append(selectionStart.line->getContent().getTeilText( selectionStart.column, selectionStart.line->getContent().getLength())); Stack stack(0); stack.push(selectionStart.line); while (stack.getSize()) { EditableJsonElement* current = stack.pop(); if (current->zMextSibling()) { stack.push(current->zMextSibling()); } else if (stack.getSize() == 0 && current->zParent()) { EditableJsonElement* parent = current->zParent(); while (parent) { if (parent->zMextSibling()) { stack.push(current->zParent()->zMextSibling()); break; } parent = parent->zParent(); } } if (current->zFirstChildren()) { stack.push(current->zFirstChildren()); } if (current == selectionEnd.line) { break; } if (current != selectionStart.line) { result += current->getContent(); } } result.append(selectionEnd.line->getContent().getTeilText( 0, selectionEnd.column)); } } return result; } JSONValue* Framework::JSON::JsonEditor::getValidContent() { return nullptr; // TODO } void Framework::JSON::JsonEditor::scrollToLine(int line, ScrollTargetPos pos) { if (pos == Top || pos == Bottom) { if (line >= renderStartLine && line < renderStopLine) { return; // line is already visible } } EditableJsonElement* element = content; if (line == 1) { scrollToLine(line, {content, 0}, pos); return; } int l = 1; int column = 0; while (element) { int lineCount = element->lineCount(); if (lineCount + l >= line) { column = element->getContent().positionVon('\n', line - l - 1) + 1; break; } l += lineCount; EditableJsonElement* tmp = element->zAfter(true, &l); if (l >= line) { column = element->getContent().positionVon('\n', lineCount - 1) + 1; break; } element = tmp; } EditorPosition target = {element, column}; unifyPosition(target); scrollToLine(line, target, pos); } void Framework::JSON::JsonEditor::scrollToLine( EditorPosition target, ScrollTargetPos pos) { if (!target.line || !target.line->isVisible()) { return; } int lineNumber = 0; if (!content->lineCount(true, true, target.line, lineNumber)) { Text* part = target.line->getContent().getTeilText(0, target.column); lineNumber += part->anzahlVon('\n'); part->release(); if (pos == Top || pos == Bottom) { if (lineNumber >= renderStartLine && lineNumber < renderStopLine) { return; // line is already visible } } scrollToLine(lineNumber, target, pos); } } void Framework::JSON::JsonEditor::scrollToLine( int lineNum, EditorPosition target, ScrollTargetPos pos) { if (!target.line || !target.line->isVisible()) { return; } if (pos == Top || pos == Bottom) { if (lineNum >= renderStartLine && lineNum < renderStopLine) { return; // line is already visible } } if (pos == Top) { renderStart = target; const char* content = renderStart.line->getContent(); while ( renderStart.column > 0 && content[renderStart.column - 1] != '\n') { renderStart.column--; } renderStartLine = lineNum; } else { int linesUntilStart = pos == Bottom ? renderedLines - 1 : renderedLines / 2 - 1; if (linesUntilStart >= lineNum) { renderStart = {content, 0}; renderStartLine = 1; return; } EditableJsonElement* element = target.line; EditableJsonElement* before = element; while (linesUntilStart >= 0) { if (!before) { break; } element = before; int newLines = 0; if (element == target.line) { Text* part = element->getContent().getTeilText(0, target.column); newLines = part->anzahlVon('\n'); part->release(); } else { newLines = element->lineCount(); } if (newLines > linesUntilStart) { renderStart = {element, element->getContent().positionVon( '\n', newLines - linesUntilStart - 1) + 1}; unifyPosition(renderStart); renderStartLine = lineNum - linesUntilStart; return; } lineNum -= newLines; linesUntilStart -= newLines; before = element->zBefore(this->content, true, &lineNum); } renderStart = {element, 0}; renderStartLine = lineNum; } }