// // Created by bruno on 5.2.2025. // Modified to use dynamic limits. // #include "texteditor.h" #include "font.h" // 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->max_lines_asm = max_lines_asm; editor->max_lines_display = 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)); memset(editor->outRect, 0, sizeof(SDL_Rect)); memset(editor->cursorRect, 0, sizeof(SDL_Rect)); memset(editor->rect, 0, sizeof(SDL_Rect)); // Allocate dynamic array for lines. editor->lines = (Line *) malloc(sizeof(Line) * editor->max_lines_asm); 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->max_lines_asm; 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 * editor->max_lines_asm + 1)); editor->displayString = (char *) malloc(sizeof(char) * (editor->max_line_width * editor->max_lines_display + 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 max_lines_display. 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->max_lines_display * (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->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 = 2; 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); } } // Insert a new line at a specific position. void insert_line(TextEditor *editor, int position, const char *text, SDL_Renderer *renderer) { if (editor->line_count >= editor->max_lines_asm || 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) { insert_line(editor, editor->cursor_line + (editor->cursor_pos ? 1 : 0), "", 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 (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 { // Backspace behavior (delete character before cursor) if (editor->cursor_pos == 0 && editor->line_count > 1) { // Merge with the previous line 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--; if (editor->cursor_line >= editor->line_count) { editor->cursor_line = editor->line_count - 1; } editor->cursor_pos = strlen(editor->lines[editor->cursor_line].text); } 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; if (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->max_lines_display) { editor->cursor_line_offset = new_line - editor->max_lines_display + 1; } 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->max_lines_display; 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, 255); 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 = 3 + 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++; } strcat(editor->displayString, "\n"); charDstRect.x = 4; charDstRect.y += charDstRect.h + 1; } } SDL_SetRenderTarget(renderer, NULL); } void editor_render(TextEditor *editor, SDL_Renderer *renderer, 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_RenderCopy(renderer, editor->texture, editor->rect, editor->outRect); if (isActive && cursorBlink) { SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255); SDL_RenderFillRect(renderer, editor->cursorRect); } } void generate_string(TextEditor *editor) { editor->outputString[0] = '\0'; for (int i = 0; i < editor->max_lines_asm; i++) { if (editor->lines[i].active) { if (strlen(editor->lines[i].text)) { strcat(editor->outputString, editor->lines[i].text); strcat(editor->outputString, "\n"); } } } } void fill_editor_from_string(TextEditor *editor, const char *content, SDL_Renderer *renderer) { if (!editor || !content) { printf("Invalid editor or content pointer!\n"); return; } // Clear the current editor content for (int i = 0; i < editor->max_lines_asm; i++) { editor->lines[i].text[0] = '\0'; editor->lines[i].active = 0; } editor->line_count = 0; editor->cursor_line = 0; editor->cursor_pos = 0; // Parse the content and fill the editor lines const char *ptr = content; int line_index = 0; while (*ptr && line_index < editor->max_lines_asm) { int char_count = 0; // Ensure the text buffer does not overflow while (*ptr && *ptr != '\n' && char_count < editor->max_line_width - 1) { 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++; } // Update the total number of lines in use editor->line_count = line_index; // 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->max_lines_asm; 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); } }