// // Created by bruno on 5.2.2025. // Modified to use dynamic limits. // #include "texteditor.h" #include "decoder.h" #include "font.h" #include "hexdump.h" unsigned long frames = 0; bool cursorShown = true; int activeEditorIndex = 0; TextEditor editors[editorCount]; // Initialize the text editor with dynamic sizes. void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Renderer *renderer, int max_line_width, int max_lines_asm, int max_lines_display, bool readOnly) { editor->max_line_width = max_line_width; editor->maxLines = max_lines_asm; editor->displayLineCount = max_lines_display; editor->line_count = 0; editor->cursor_line = 0; editor->cursor_line_offset = 0; editor->cursor_pos = 0; editor->readOnly = readOnly; editor->font = font; editor->outRect = malloc(sizeof(SDL_Rect)); editor->cursorRect = malloc(sizeof(SDL_Rect)); editor->rect = malloc(sizeof(SDL_Rect)); editor->highlightedLineRect = malloc(sizeof(SDL_Rect)); memset(editor->outRect, 0, sizeof(SDL_Rect)); memset(editor->cursorRect, 0, sizeof(SDL_Rect)); memset(editor->rect, 0, sizeof(SDL_Rect)); memset(editor->highlightedLineRect, 0, sizeof(SDL_Rect)); // Allocate dynamic array for lines. editor->lines = (Line *) malloc(sizeof(Line) * editor->maxLines); if (!editor->lines) { fprintf(stderr, "Failed to allocate memory for lines.\n"); exit(EXIT_FAILURE); } // For each line, allocate memory for the text (including space for '\0') for (int i = 0; i < editor->maxLines; i++) { editor->lines[i].text = (char *) malloc(sizeof(char) * (editor->max_line_width + 1)); if (!editor->lines[i].text) { fprintf(stderr, "Failed to allocate memory for line %d.\n", i); exit(EXIT_FAILURE); } editor->lines[i].text[0] = '\0'; editor->lines[i].active = 0; } // Allocate output and display strings. editor->outputString = (char *) malloc(sizeof(char) * ((editor->max_line_width + 1) * editor->maxLines + 1) + 1); editor->displayString = (char *) malloc( sizeof(char) * ((editor->max_line_width + 1) * editor->displayLineCount + 1) + 1); if (!editor->outputString || !editor->displayString) { fprintf(stderr, "Failed to allocate memory for output/display strings.\n"); exit(EXIT_FAILURE); } editor->outputString[0] = '\0'; editor->displayString[0] = '\0'; // Initialize with two active lines (like the original code). editor->lines[0].active = 1; editor->lines[1].active = 1; editor->line_count = 2; // Set up the editor rectangle based on font size and dynamic max_line_width and displayLineCount. editor->rect->x = 2; editor->rect->y = 2; editor->rect->w = editor->max_line_width * (font->size + 1) + ((font->size + 1) / 2); editor->rect->h = editor->displayLineCount * (font->size + 1) + 2; editor->outRect->w = editor->rect->w; editor->outRect->h = editor->rect->h; editor->outRect->x = x; editor->outRect->y = y; editor->highlightedLineRect->x = 2; editor->highlightedLineRect->y = 2; editor->highlightedLineRect->w = editor->outRect->w - (2 * editor->highlightedLineRect->x); editor->highlightedLineRect->h = editor->font->size + 1; editor->cursorRect->x = 3 + editor->cursor_pos * font->size + editor->outRect->x; editor->cursorRect->y = 2 + (editor->cursor_line - editor->cursor_line_offset) * (font->size + 1) + editor->outRect->y; editor->cursorRect->w = editor->font->size; editor->cursorRect->h = editor->font->size; // Create texture for rendering. editor->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, editor->rect->w, editor->rect->h); if (!editor->texture) { fprintf(stderr, "Failed to create texture: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } } void insert_line(TextEditor *editor, int position, const char *text, SDL_Renderer *renderer) { if (editor->line_count >= editor->maxLines || position < 0 || position > editor->line_count) { printf("Invalid position or max lines reached!\n"); return; } // Shift lines down. for (int i = editor->line_count; i > position; i--) { strcpy(editor->lines[i].text, editor->lines[i - 1].text); editor->lines[i].active = editor->lines[i - 1].active; } // Copy the text into the new line, ensuring it does not exceed max_line_width. strncpy(editor->lines[position].text, text, editor->max_line_width); editor->lines[position].text[editor->max_line_width] = '\0'; editor->lines[position].active = 1; editor->line_count++; move_cursor(editor, editor->cursor_line + 1, 0, false, renderer); generate_string_display(editor, renderer); } void insert_line_rel(TextEditor *editor, SDL_Renderer *renderer) { int line = editor->cursor_line; int pos = editor->cursor_pos; char *current_text = editor->lines[line].text; // Split the current line at the cursor position char new_line_text[editor->max_line_width + 1]; strcpy(new_line_text, ¤t_text[pos]); // Copy the text after cursor to the new line current_text[pos] = '\0'; // Truncate the current line at cursor position insert_line(editor, line + 1, new_line_text, renderer); editor->cursor_pos = 0; } void insert_character(TextEditor *editor, char ch, SDL_Renderer *renderer) { if (editor->cursor_line < 0 || editor->cursor_line >= editor->line_count) { printf("Invalid cursor position!\n"); return; } Line *line = &editor->lines[editor->cursor_line]; int len = strlen(line->text); if (editor->overwrite) { if (editor->cursor_pos > len) { printf("Position out of bounds or line is full!\n"); return; } line->text[editor->cursor_pos] = ch; } else { if (len >= editor->max_line_width || editor->cursor_pos > len) { printf("Position out of bounds or line is full!\n"); return; } // Shift characters to the right. for (int i = len; i >= editor->cursor_pos; i--) { line->text[i + 1] = line->text[i]; } line->text[editor->cursor_pos] = ch; } editor->cursor_pos++; generate_string_display(editor, renderer); } void remove_character(TextEditor *editor, bool isDelete, SDL_Renderer *renderer) { if (editor->cursor_line < 0 || editor->cursor_line >= editor->line_count) { printf("Invalid cursor position!\n"); return; } Line *line = &editor->lines[editor->cursor_line]; int len = strlen(line->text); if (isDelete) { // Delete character after cursor if (editor->cursor_pos < len) { for (int i = editor->cursor_pos; i < len; i++) { line->text[i] = line->text[i + 1]; } } else if (editor->cursor_pos == len && editor->cursor_line < editor->line_count - 1) { // Merge next line with the current line when pressing delete at the end strcat(line->text, editor->lines[editor->cursor_line + 1].text); // Shift remaining lines up for (int i = editor->cursor_line + 1; i < editor->line_count - 1; i++) { strcpy(editor->lines[i].text, editor->lines[i + 1].text); editor->lines[i].active = editor->lines[i + 1].active; } editor->lines[editor->line_count - 1].text[0] = '\0'; editor->lines[editor->line_count - 1].active = 0; editor->line_count--; } } else { // Backspace behavior (delete character before cursor) if (editor->cursor_pos == 0 && editor->cursor_line > 0) { // Append current line to previous line int prev_len = strlen(editor->lines[editor->cursor_line - 1].text); strcat(editor->lines[editor->cursor_line - 1].text, line->text); // Shift lines up for (int i = editor->cursor_line; i < editor->line_count - 1; i++) { strcpy(editor->lines[i].text, editor->lines[i + 1].text); editor->lines[i].active = editor->lines[i + 1].active; } editor->lines[editor->line_count - 1].text[0] = '\0'; editor->lines[editor->line_count - 1].active = 0; editor->line_count--; // Move cursor to the end of the previous line editor->cursor_line--; editor->cursor_pos = prev_len; } else if (editor->cursor_pos > 0) { for (int i = editor->cursor_pos - 1; i < len; i++) { line->text[i] = line->text[i + 1]; } editor->cursor_pos--; } } generate_string_display(editor, renderer); } void move_cursor_relative(TextEditor *editor, int line_offset, int pos_offset, bool keepPos, SDL_Renderer *renderer) { int new_line = editor->cursor_line + line_offset; int new_pos = editor->cursor_pos + pos_offset; move_cursor(editor, new_line, new_pos, keepPos, renderer); } void move_cursor(TextEditor *editor, int new_line, int new_pos, bool keepPos, SDL_Renderer *renderer) { if (new_line < 0) new_line = 0; int trgt = editor->line_count - editor->displayLineCount; if (trgt < 0) { trgt = 0; } if (new_line >= trgt && new_line >= editor->line_count) { new_line = editor->line_count - 1; } if (keepPos) { editor->cursor_line_offset = new_line; } if (new_line < editor->cursor_line_offset) { editor->cursor_line_offset = new_line; } if (new_line >= editor->cursor_line_offset + editor->displayLineCount) { editor->cursor_line_offset = new_line - editor->displayLineCount + 1; } if (editor->cursor_line_offset >= editor->line_count - editor->displayLineCount) { editor->cursor_line_offset = editor->line_count - editor->displayLineCount; } if (editor->cursor_line_offset < 0) { editor->cursor_line_offset = 0; } int line_length = strlen(editor->lines[new_line].text); if (new_pos < 0) new_pos = 0; if (new_pos > line_length) new_pos = line_length; editor->cursor_line = new_line; editor->cursor_pos = new_pos; generate_string_display(editor, renderer); } void generate_string_display(TextEditor *editor, SDL_Renderer *renderer) { if (editor->cursor_line_offset < 0 || editor->cursor_line_offset >= editor->line_count) { printf("Invalid start line!\n"); return; } int end_line = editor->cursor_line_offset + editor->displayLineCount; if (end_line > editor->line_count) end_line = editor->line_count; // Clear the display string. editor->displayString[0] = '\0'; SDL_SetRenderTarget(renderer, editor->texture); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); SDL_Rect charDstRect; charDstRect.x = 4; charDstRect.y = 3; charDstRect.w = editor->font->size; charDstRect.h = editor->font->size; SDL_Rect charRect; charRect.x = 0; charRect.y = 0; charRect.w = editor->font->size; charRect.h = editor->font->size; editor->cursorRect->x = 2 + editor->cursor_pos * (charDstRect.w + 1) + editor->outRect->x; editor->cursorRect->y = 2 + (editor->cursor_line - editor->cursor_line_offset) * (charDstRect.h + 1) + editor->outRect->y; for (int line = editor->cursor_line_offset; line < end_line; line++) { if (editor->lines[line].active) { strcat(editor->displayString, editor->lines[line].text); char *linePTR = editor->lines[line].text; while (*linePTR) { SDL_RenderCopy(renderer, editor->font->texture[(unsigned char) *linePTR], &charRect, &charDstRect); charDstRect.x += charDstRect.w + 1; linePTR++; } charDstRect.x = 4; charDstRect.y += charDstRect.h + 1; strcat(editor->displayString, "\n"); } } SDL_SetRenderTarget(renderer, NULL); } void editor_render(TextEditor *editor, SDL_Renderer *renderer, Encoder *enc, uint8_t editorIndex, bool isActive, bool cursorBlink) { if (isActive) { SDL_Rect bgRect; bgRect = *editor->outRect; bgRect.x -= 6; bgRect.y -= 6; bgRect.w += 12; bgRect.h += 12; SDL_SetRenderDrawColor(renderer, editor->readOnly ? 128 : 0, editor->readOnly ? 0 : 128, 64, 255); SDL_RenderFillRect(renderer, &bgRect); } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderFillRect(renderer, editor->outRect); uint32_t targetLine = 0; if (editorIndex == 0) { targetLine = (enc->byteIndex / 8) - editor->cursor_line_offset; editor->highlightedLineRect->w = editor->font->size * 2 + 2; editor->highlightedLineRect->x = (editor->font->size * 11) + (3 * (editor->font->size + 1) * (enc->byteIndex % 8)); editor->highlightedLineRect->y = 1 + ((editor->font->size + 1) * targetLine); } SDL_Rect highlightedLineAbs = *editor->highlightedLineRect; highlightedLineAbs.x += editor->outRect->x; highlightedLineAbs.y += editor->outRect->y; SDL_SetRenderDrawColor(renderer, 128, 0, 255, 255); if (targetLine >= editor->cursor_line_offset && targetLine < editor->cursor_line_offset + editor->displayLineCount) { SDL_RenderFillRect(renderer, &highlightedLineAbs); } SDL_SetTextureBlendMode(editor ->texture, SDL_BLENDMODE_BLEND); SDL_RenderCopy(renderer, editor ->texture, editor->rect, editor->outRect); if (isActive && cursorBlink) { SDL_SetRenderDrawColor(renderer, editor->overwrite ? 255 : 0, 255, editor->overwrite ? 0 : 255, 128); SDL_RenderFillRect(renderer, editor ->cursorRect); } } void generate_string(TextEditor *editor) { editor->outputString[0] = '\0'; for (int i = 0; i < editor->maxLines; i++) { if (editor->lines[i].active) { strcat(editor->outputString, editor->lines[i].text); strcat(editor->outputString, "\n"); } } } void fill_editor_from_string(TextEditor *editor, const char *content, int lineStart, bool isComplete, SDL_Renderer *renderer) { if (!editor || !content) { printf("Invalid editor or content pointer!\n"); return; } char *str = content; int numLines = 0; while (*str) if (*str++ == '\n') ++numLines; // Clear the current editor content for (int i = lineStart; i < editor->maxLines && i < lineStart + numLines + 1; i++) { editor->lines[i].text[0] = '\0'; editor->lines[i].active = 0; } if (isComplete) { editor->line_count = numLines; if (editor->line_count >= editor->maxLines) { editor->line_count = editor->maxLines - 1; } } // Parse the content and fill the editor lines const char *ptr = content; int line_index = lineStart; while (*ptr && line_index < editor->maxLines && line_index < numLines + lineStart) { int char_count = 0; // Ensure the text buffer does not overflow while (*ptr && *ptr != '\n' && char_count < editor->max_line_width) { editor->lines[line_index].text[char_count++] = *ptr++; } editor->lines[line_index].text[char_count] = '\0'; // Null-terminate editor->lines[line_index].active = 1; line_index++; // Move past the newline character if present if (*ptr == '\n') ptr++; } editor->lines[line_index].active = 1; // Update the total number of lines in use //editor->line_count = line_index + (line_index < editor->maxLines - 1 ? 1 : 0); //if (editor->line_count >= editor-> maxLines) // Generate the visual representation generate_string_display(editor, renderer); } // Free all dynamically allocated memory. void destroy_editor(TextEditor *editor) { if (editor->lines) { for (int i = 0; i < editor->maxLines; i++) { free(editor->lines[i].text); } free(editor->lines); } //free(editor->outputString); //free(editor->displayString); free(editor->outRect); free(editor->rect); free(editor->cursorRect); if (editor->texture) { SDL_DestroyTexture(editor->texture); } } SDL_Keycode ConvertKPToNonKP(SDL_Keycode keycode) { switch (keycode) { case SDLK_KP_0: return SDLK_0; case SDLK_KP_1: return SDLK_1; case SDLK_KP_2: return SDLK_2; case SDLK_KP_3: return SDLK_3; case SDLK_KP_4: return SDLK_4; case SDLK_KP_5: return SDLK_5; case SDLK_KP_6: return SDLK_6; case SDLK_KP_7: return SDLK_7; case SDLK_KP_8: return SDLK_8; case SDLK_KP_9: return SDLK_9; case SDLK_KP_PERIOD: return SDLK_PERIOD; case SDLK_KP_COMMA: return SDLK_COMMA; case SDLK_KP_DIVIDE: return SDLK_SLASH; case SDLK_KP_MULTIPLY: return SDLK_ASTERISK; case SDLK_KP_MINUS: return SDLK_MINUS; case SDLK_KP_PLUS: return SDLK_PLUS; case SDLK_KP_ENTER: return SDLK_RETURN; case SDLK_KP_EQUALS: return SDLK_EQUALS; case SDLK_KP_LEFTPAREN: return SDLK_LEFTPAREN; case SDLK_KP_RIGHTPAREN: return SDLK_RIGHTPAREN; case SDLK_KP_LEFTBRACE: return SDLK_LEFTBRACKET; case SDLK_KP_RIGHTBRACE: return SDLK_RIGHTBRACKET; case SDLK_KP_TAB: return SDLK_TAB; case SDLK_KP_BACKSPACE: return SDLK_BACKSPACE; case SDLK_KP_A: return SDLK_a; case SDLK_KP_B: return SDLK_b; case SDLK_KP_C: return SDLK_c; case SDLK_KP_D: return SDLK_d; case SDLK_KP_E: return SDLK_e; case SDLK_KP_F: return SDLK_f; case SDLK_KP_XOR: return SDLK_CARET; case SDLK_KP_PERCENT: return SDLK_PERCENT; case SDLK_KP_LESS: return SDLK_LESS; case SDLK_KP_GREATER: return SDLK_GREATER; case SDLK_KP_AMPERSAND: case SDLK_KP_DBLAMPERSAND: return SDLK_AMPERSAND; // No direct match, best alternative case SDLK_KP_VERTICALBAR: case SDLK_KP_DBLVERTICALBAR: return SDLK_BACKSLASH; // No direct match case SDLK_KP_COLON: return SDLK_COLON; case SDLK_KP_HASH: return SDLK_HASH; case SDLK_KP_SPACE: return SDLK_SPACE; case SDLK_KP_AT: return SDLK_AT; case SDLK_KP_EXCLAM: return SDLK_EXCLAIM; case SDLK_KP_MEMADD: return SDLK_PLUS; case SDLK_KP_MEMSUBTRACT: return SDLK_MINUS; case SDLK_KP_MEMMULTIPLY: return SDLK_ASTERISK; case SDLK_KP_MEMDIVIDE: return SDLK_SLASH; case SDLK_KP_MEMSTORE: case SDLK_KP_MEMRECALL: case SDLK_KP_MEMCLEAR: case SDLK_KP_PLUSMINUS: case SDLK_KP_CLEAR: case SDLK_KP_CLEARENTRY: case SDLK_KP_BINARY: case SDLK_KP_OCTAL: case SDLK_KP_DECIMAL: case SDLK_KP_HEXADECIMAL: return SDLK_UNKNOWN; default: return keycode; // If it's not a KP key, return it unchanged } } void updateDump(TextEditor *editor, SDL_Renderer *renderer) { if (editor == &transmitEditor) { if (encoder.bufferSize) { char *dump = hexdump_to_string(encoder.buffer, 0, encoder.bufferSize); fill_editor_from_string(editor, dump, 0, true, renderer); free(dump); } else { fill_editor_from_string(editor, "\n", 0, true, renderer); } } else if (editor == &receivedEditor) { if (decoder.bufferSize) { char *dump = hexdump_to_string(decoder.buffer, 0, decoder.bufferSize); fill_editor_from_string(editor, dump, 0, true, renderer); free(dump); } else { fill_editor_from_string(editor, "\n", 0, true, renderer); } } }