#include #include #include #include #include #include #include #include static inline const char * minPointer(const char * x, const char * y) { return x < y ? x : y; } /* TextArea */ TextArea::TextArea(Responder * parentResponder, View * contentView, const KDFont * font) : TextInput(parentResponder, contentView), InputEventHandler(nullptr), m_delegate(nullptr) { } static inline void InsertSpacesAtLocation(int spacesCount, char * buffer, int bufferSize) { assert(buffer != nullptr); assert(strlen(buffer) + spacesCount < bufferSize); size_t sizeToMove = strlen(buffer) + 1; size_t spaceCharSize = UTF8Decoder::CharSizeOfCodePoint(' '); size_t spacesLength = spacesCount * spaceCharSize; memmove(buffer + spacesLength, buffer, sizeToMove); for (int i = 0; i < spacesCount; i++) { int spaceOffset = i * spaceCharSize; UTF8Decoder::CodePointToChars(' ', buffer + spaceOffset, bufferSize - spaceOffset); } } bool TextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) { constexpr int bufferSize = TextField::maxBufferSize(); char buffer[bufferSize]; size_t textLength = strlcpy(buffer, text, bufferSize); // Add indentation spaces if (indentation) { // Compute the indentation int spacesCount = indentationBeforeCursor(); const char * teaxtAreaBuffer = contentView()->text(); if (cursorLocation() > teaxtAreaBuffer && UTF8Helper::PreviousCodePointIs(teaxtAreaBuffer, cursorLocation(), ':')) { spacesCount += k_indentationSpaces; } // Check the text will not overflow the buffer int totalIndentationSize = UTF8Helper::CountOccurrences(text, '\n') * spacesCount; if (contentView()->getText()->textLength() + textLength + totalIndentationSize >= contentView()->getText()->bufferSize() || textLength == 0) { return false; } UTF8Helper::PerformAtCodePoints( buffer, '\n', [](int codePointOffset, void * text, int indentation) { int offset = codePointOffset + UTF8Decoder::CharSizeOfCodePoint('\n'); InsertSpacesAtLocation(indentation, (char *)text + offset, TextField::maxBufferSize() - offset); //TODO }, [](int c1, void * c2, int c3) {}, (void *)buffer, spacesCount); } const char * cursorPositionInCommand = TextInputHelpers::CursorPositionInCommand(buffer); // Remove the Empty code points UTF8Helper::RemoveCodePoint(buffer, UCodePointEmpty, &cursorPositionInCommand); // Insert the text if (insertTextAtLocation(buffer, cursorLocation())) { // Set the cursor location const char * nextCursorLocation = cursorLocation() + (forceCursorRightOfText ? strlen(buffer) : cursorPositionInCommand - buffer); setCursorLocation(nextCursorLocation); } return true; } bool TextArea::handleEvent(Ion::Events::Event event) { if (m_delegate != nullptr && m_delegate->textAreaDidReceiveEvent(this, event)) { return true; } else if (handleBoxEvent(app(), event)) { return true; } else if (event == Ion::Events::Left) { if (cursorLocation() <= text()) { assert(cursorLocation() == text()); return false; } UTF8Decoder decoder(text(), cursorLocation()); decoder.previousCodePoint(); return setCursorLocation(decoder.stringPosition()); } else if (event == Ion::Events::Right) { if (UTF8Helper::CodePointIs(cursorLocation(), UCodePointNull)) { return false; } UTF8Decoder decoder(cursorLocation()); decoder.nextCodePoint(); return setCursorLocation(decoder.stringPosition()); } else if (event == Ion::Events::Up) { contentView()->moveCursorGeo(0, -1); } else if (event == Ion::Events::Down) { contentView()->moveCursorGeo(0, 1); } else if (event == Ion::Events::ShiftLeft) { contentView()->moveCursorGeo(-INT_MAX/2, 0); } else if (event == Ion::Events::ShiftRight) { contentView()->moveCursorGeo(INT_MAX/2, 0); } else if (event == Ion::Events::Backspace) { return removeCodePoint(); } else if (event.hasText()) { return handleEventWithText(event.text()); } else if (event == Ion::Events::EXE) { return handleEventWithText("\n"); } else if (event == Ion::Events::Clear) { if (!contentView()->removeEndOfLine()) { contentView()->removeStartOfLine(); } } else if (event == Ion::Events::Paste) { return handleEventWithText(Clipboard::sharedClipboard()->storedText()); } else { return false; } scrollToCursor(); return true; } void TextArea::setText(char * textBuffer, size_t textBufferSize) { contentView()->setText(textBuffer, textBufferSize); contentView()->moveCursorGeo(0, 0); } int TextArea::indentationBeforeCursor() const { int indentationSize = 0; /* Compute the number of spaces at the beginning of the line. Increase the * indentation size when encountering spaces, reset it to 0 when encountering * another code point, until reaching the beginning of the line. */ UTF8Helper::PerformAtCodePoints(const_cast