Files
2026-07-14 00:26:01 +02:00

593 lines
20 KiB
C

//
// 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, &current_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);
}
}
}