Files
MontaukOS/programs/src/spreadsheet/edit.cpp
T

246 lines
7.7 KiB
C++

/*
* edit.cpp
* Editing, selection, clipboard, formatting, undo/redo, scroll
* Copyright (c) 2026 Daniel Hammer
*/
#include "spreadsheet.h"
// ============================================================================
// Undo/redo
// ============================================================================
void undo_push() {
for (int i = g_undo_pos; i < g_undo_count; i++) {
if (g_undo[i]) { montauk::mfree(g_undo[i]); g_undo[i] = nullptr; }
}
if (g_undo[UNDO_MAX]) { montauk::mfree(g_undo[UNDO_MAX]); g_undo[UNDO_MAX] = nullptr; }
g_undo_count = g_undo_pos;
if (g_undo_count >= UNDO_MAX) {
if (g_undo[0]) montauk::mfree(g_undo[0]);
for (int i = 0; i < UNDO_MAX - 1; i++) g_undo[i] = g_undo[i + 1];
g_undo[UNDO_MAX - 1] = nullptr;
g_undo_count = UNDO_MAX - 1;
}
UndoEntry* e = (UndoEntry*)montauk::malloc(sizeof(UndoEntry));
if (!e) return;
for (int r = 0; r < MAX_ROWS; r++)
for (int c = 0; c < MAX_COLS; c++) {
str_cpy(e->cells[r][c].input, g_cells[r][c].input, CELL_TEXT_MAX);
e->cells[r][c].align = g_cells[r][c].align;
e->cells[r][c].fmt = g_cells[r][c].fmt;
e->cells[r][c].bold = g_cells[r][c].bold;
}
g_undo[g_undo_count] = e;
g_undo_count++;
g_undo_pos = g_undo_count;
}
static void undo_restore(UndoEntry* e) {
for (int r = 0; r < MAX_ROWS; r++)
for (int c = 0; c < MAX_COLS; c++) {
str_cpy(g_cells[r][c].input, e->cells[r][c].input, CELL_TEXT_MAX);
g_cells[r][c].align = e->cells[r][c].align;
g_cells[r][c].fmt = e->cells[r][c].fmt;
g_cells[r][c].bold = e->cells[r][c].bold;
}
eval_all_cells();
}
void undo_do() {
if (g_undo_pos <= 0) return;
if (g_undo_pos == g_undo_count && !g_undo[g_undo_count]) {
UndoEntry* e = (UndoEntry*)montauk::malloc(sizeof(UndoEntry));
if (e) {
for (int r = 0; r < MAX_ROWS; r++)
for (int c = 0; c < MAX_COLS; c++) {
str_cpy(e->cells[r][c].input, g_cells[r][c].input, CELL_TEXT_MAX);
e->cells[r][c].align = g_cells[r][c].align;
e->cells[r][c].fmt = g_cells[r][c].fmt;
e->cells[r][c].bold = g_cells[r][c].bold;
}
g_undo[g_undo_count] = e;
}
}
g_undo_pos--;
if (g_undo[g_undo_pos]) undo_restore(g_undo[g_undo_pos]);
}
void redo_do() {
if (g_undo_pos >= g_undo_count) return;
g_undo_pos++;
UndoEntry* e = g_undo[g_undo_pos];
if (e) undo_restore(e);
}
// ============================================================================
// Editing
// ============================================================================
void start_editing() {
if (g_editing) return;
g_editing = true;
Cell* c = &g_cells[g_sel_row][g_sel_col];
str_cpy(g_edit_buf, c->input, CELL_TEXT_MAX);
g_edit_len = str_len(g_edit_buf);
g_edit_cursor = g_edit_len;
}
void commit_edit() {
if (!g_editing) return;
g_editing = false;
undo_push();
Cell* c = &g_cells[g_sel_row][g_sel_col];
str_cpy(c->input, g_edit_buf, CELL_TEXT_MAX);
g_modified = true;
eval_all_cells();
}
void cancel_edit() {
g_editing = false;
}
// ============================================================================
// Selection helpers
// ============================================================================
void sel_range(int* c0, int* r0, int* c1, int* r1) {
if (!g_has_selection) {
*c0 = *c1 = g_sel_col;
*r0 = *r1 = g_sel_row;
return;
}
*c0 = g_sel_col < g_anchor_col ? g_sel_col : g_anchor_col;
*c1 = g_sel_col > g_anchor_col ? g_sel_col : g_anchor_col;
*r0 = g_sel_row < g_anchor_row ? g_sel_row : g_anchor_row;
*r1 = g_sel_row > g_anchor_row ? g_sel_row : g_anchor_row;
}
void clear_selection() {
g_has_selection = false;
g_anchor_col = g_sel_col;
g_anchor_row = g_sel_row;
}
void copy_selection() {
int c0, r0, c1, r1;
sel_range(&c0, &r0, &c1, &r1);
g_clip_count = 0;
g_clip_cols = c1 - c0 + 1;
g_clip_rows = r1 - r0 + 1;
for (int r = r0; r <= r1 && g_clip_count < CLIP_MAX_CELLS; r++) {
for (int c = c0; c <= c1 && g_clip_count < CLIP_MAX_CELLS; c++) {
ClipCell* cc = &g_clipboard[g_clip_count];
str_cpy(cc->text, g_cells[r][c].input, CELL_TEXT_MAX);
cc->rel_col = c - c0;
cc->rel_row = r - r0;
cc->align = g_cells[r][c].align;
cc->fmt = g_cells[r][c].fmt;
cc->bold = g_cells[r][c].bold;
g_clip_count++;
}
}
}
void cut_selection() {
copy_selection();
undo_push();
int c0, r0, c1, r1;
sel_range(&c0, &r0, &c1, &r1);
for (int r = r0; r <= r1; r++)
for (int c = c0; c <= c1; c++)
g_cells[r][c].input[0] = '\0';
eval_all_cells();
g_modified = true;
}
void paste_at_cursor() {
if (g_clip_count == 0) return;
undo_push();
for (int i = 0; i < g_clip_count; i++) {
int tc = g_sel_col + g_clipboard[i].rel_col;
int tr = g_sel_row + g_clipboard[i].rel_row;
if (tc >= 0 && tc < MAX_COLS && tr >= 0 && tr < MAX_ROWS) {
str_cpy(g_cells[tr][tc].input, g_clipboard[i].text, CELL_TEXT_MAX);
g_cells[tr][tc].align = g_clipboard[i].align;
g_cells[tr][tc].fmt = g_clipboard[i].fmt;
g_cells[tr][tc].bold = g_clipboard[i].bold;
}
}
eval_all_cells();
g_modified = true;
}
// ============================================================================
// Formatting helpers (apply to selection)
// ============================================================================
void apply_bold_toggle() {
undo_push();
int c0, r0, c1, r1;
sel_range(&c0, &r0, &c1, &r1);
bool new_bold = !g_cells[r0][c0].bold;
for (int r = r0; r <= r1; r++)
for (int c = c0; c <= c1; c++)
g_cells[r][c].bold = new_bold;
g_modified = true;
}
void apply_align(CellAlign a) {
undo_push();
int c0, r0, c1, r1;
sel_range(&c0, &r0, &c1, &r1);
for (int r = r0; r <= r1; r++)
for (int c = c0; c <= c1; c++)
g_cells[r][c].align = a;
g_modified = true;
}
void apply_format(NumFormat f) {
undo_push();
int c0, r0, c1, r1;
sel_range(&c0, &r0, &c1, &r1);
for (int r = r0; r <= r1; r++)
for (int c = c0; c <= c1; c++)
g_cells[r][c].fmt = f;
eval_all_cells();
g_modified = true;
}
// ============================================================================
// Scroll clamping
// ============================================================================
void clamp_scroll() {
int pbh = g_pathbar_open ? PATHBAR_H : 0;
int max_x = content_width() - (g_win_w - ROW_HEADER_W);
int max_y = content_height() - (g_win_h - TOOLBAR_H - pbh - FORMULA_BAR_H - STATUS_BAR_H);
if (max_x < 0) max_x = 0;
if (max_y < 0) max_y = 0;
if (g_scroll_x < 0) g_scroll_x = 0;
if (g_scroll_x > max_x) g_scroll_x = max_x;
if (g_scroll_y < 0) g_scroll_y = 0;
if (g_scroll_y > max_y) g_scroll_y = max_y;
}
void ensure_sel_visible() {
int pbh = g_pathbar_open ? PATHBAR_H : 0;
int area_w = g_win_w - ROW_HEADER_W;
int area_h = g_win_h - TOOLBAR_H - pbh - FORMULA_BAR_H - COL_HEADER_H - STATUS_BAR_H;
int cx = col_x(g_sel_col) - ROW_HEADER_W;
int cy = g_sel_row * ROW_H;
if (cx < g_scroll_x) g_scroll_x = cx;
if (cx + g_col_widths[g_sel_col] > g_scroll_x + area_w)
g_scroll_x = cx + g_col_widths[g_sel_col] - area_w;
if (cy < g_scroll_y) g_scroll_y = cy;
if (cy + ROW_H > g_scroll_y + area_h)
g_scroll_y = cy + ROW_H - area_h;
clamp_scroll();
}