#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) { if (*text == 0) { return false; } /* Compute the indentation. If the text cannot be inserted with the * indentation, stop here. */ int spacesCount = 0; int totalIndentationSize = 0; int textLen = strlen(text); char * insertionPosition = const_cast(cursorLocation()); if (indentation) { // Compute the indentation spacesCount = indentationBeforeCursor(); const char * textAreaBuffer = contentView()->text(); if (insertionPosition > textAreaBuffer && UTF8Helper::PreviousCodePointIs(textAreaBuffer, insertionPosition, ':')) { spacesCount += k_indentationSpaces; } // Check the text will not overflow the buffer totalIndentationSize = UTF8Helper::CountOccurrences(text, '\n') * spacesCount; if (contentView()->getText()->textLength() + textLen + totalIndentationSize >= contentView()->getText()->bufferSize()) { return false; } } // Insert the text if (!insertTextAtLocation(text, insertionPosition)) { return true; } // Insert the indentation if (indentation) { UTF8Helper::PerformAtCodePoints( insertionPosition, '\n', [](int codePointOffset, void * text, int indentation, int bufferLength) { int offset = codePointOffset + UTF8Decoder::CharSizeOfCodePoint('\n'); InsertSpacesAtLocation(indentation, (char *)text + offset, bufferLength); }, [](int c1, void * c2, int c3, int c4) {}, (void *)insertionPosition, spacesCount, contentView()->getText()->bufferSize() - (insertionPosition - contentView()->getText()->text()), UCodePointNull, true, nullptr, insertionPosition + textLen); } const char * endOfInsertedText = insertionPosition + textLen + totalIndentationSize; const char * cursorPositionInCommand = TextInputHelpers::CursorPositionInCommand(insertionPosition, endOfInsertedText); // Remove the Empty code points UTF8Helper::RemoveCodePoint(insertionPosition, UCodePointEmpty, &cursorPositionInCommand, endOfInsertedText); // Set the cursor location const char * nextCursorLocation = forceCursorRightOfText ? endOfInsertedText : cursorPositionInCommand; 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(event)) { return true; } else if (event == Ion::Events::Left) { return TextInput::moveCursorLeft(); } else if (event == Ion::Events::Right) { return TextInput::moveCursorRight(); } 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 removePreviousGlyph(); } 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