|
@@ -0,0 +1,3300 @@
|
|
|
+#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<EditableJsonElementReference>* children;
|
|
|
+
|
|
|
+ EditableJsonElementReference(EditableJsonElement* root,
|
|
|
+ RCArray<EditableJsonElementReference>* children)
|
|
|
+ : root(root),
|
|
|
+ children(children)
|
|
|
+ {}
|
|
|
+
|
|
|
+public:
|
|
|
+ EditableJsonElementReference(EditableJsonElement* root)
|
|
|
+ : root(root),
|
|
|
+ children(new RCArray<EditableJsonElementReference>())
|
|
|
+ {}
|
|
|
+
|
|
|
+ ~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<char>* 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<char>* 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<EditableJsonElement*>& afterList,
|
|
|
+ Array<EditableJsonElement*>& 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<EditableJsonElement*>& afterList,
|
|
|
+ Array<EditableJsonElement*>& 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<EditableJsonElement*> 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<Regex::Result>* 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<EditableJsonElement*>& 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<EditableJsonElement*> 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<EditableJsonElement*> 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<EditableJsonElement*> toDelete;
|
|
|
+ Stack<EditableJsonElement*> 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<EditableJsonElement*> 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<Regex::Automata<char>*>(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<EditableJsonElement*> 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;
|
|
|
+ }
|
|
|
+}
|