Add spreadsheet program
This commit is contained in:
55
src/book.c
Normal file
55
src/book.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "book.h"
|
||||
#include "config.h"
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void book_init(struct book *b, char *title)
|
||||
{
|
||||
b->n_sheets = 0;
|
||||
b->sheets = malloc(0);
|
||||
if (!book_add_sheet(b))
|
||||
exit(1);
|
||||
|
||||
(b->sheets)[1] = NULL;
|
||||
|
||||
if (title != NULL)
|
||||
b->title = strndup(title, MAX_STR);
|
||||
else
|
||||
b->title = strdup("");
|
||||
}
|
||||
|
||||
void book_delete(struct book *b)
|
||||
{
|
||||
if (b == NULL) {
|
||||
warn("attempted to delete NULL book");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
while ((b->sheets)[i] != NULL) {
|
||||
sheet_delete((b->sheets)[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
bool book_add_sheet(struct book *b)
|
||||
{
|
||||
struct sheet *s;
|
||||
|
||||
b->n_sheets++;
|
||||
|
||||
void* tmp = realloc(b->sheets, sizeof(struct sheet*) * (b->n_sheets + 1));
|
||||
if (tmp == NULL)
|
||||
return false;
|
||||
b->sheets = tmp;
|
||||
|
||||
b->sheets[b->n_sheets] = NULL;
|
||||
|
||||
s = malloc(sizeof(struct sheet));
|
||||
sheet_init(s, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_SHEET_TITLE);
|
||||
|
||||
b->sheets[b->n_sheets-1] = s;
|
||||
|
||||
return true;
|
||||
}
|
||||
20
src/book.h
Normal file
20
src/book.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef BOOK_H
|
||||
#define BOOK_H
|
||||
|
||||
#include "sheet.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct book {
|
||||
struct sheet **sheets;
|
||||
size_t n_sheets;
|
||||
char *title;
|
||||
};
|
||||
|
||||
void book_init(struct book *b, char *title);
|
||||
|
||||
void book_delete(struct book *b);
|
||||
|
||||
bool book_add_sheet(struct book *b);
|
||||
|
||||
#endif
|
||||
52
src/cell.c
Normal file
52
src/cell.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "cell.h"
|
||||
#include <stdlib.h>
|
||||
#include <err.h>
|
||||
#include <string.h>
|
||||
|
||||
void cell_parse_function(struct cell *cell);
|
||||
|
||||
void cell_init(struct cell *c, size_t x, size_t y, enum cell_type type)
|
||||
{
|
||||
c->referenced_by = NULL;
|
||||
c->x_pos = x;
|
||||
c->y_pos = y;
|
||||
c->type = type;
|
||||
c->text = calloc(1,1);
|
||||
c->value.integer = 0;
|
||||
}
|
||||
|
||||
void cell_delete(struct cell *c)
|
||||
{
|
||||
if (c == NULL) {
|
||||
warn("attempted to delete NULL cell");
|
||||
return;
|
||||
}
|
||||
|
||||
free(c->text);
|
||||
free(c);
|
||||
}
|
||||
|
||||
void cell_set_type(struct cell *cell)
|
||||
{
|
||||
if (cell->text[0] == '=') {
|
||||
//cell_parse_function(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
int i = strtoll(cell->text, &endptr, 10);
|
||||
|
||||
if (*endptr == '\0') {
|
||||
cell->type = Integer;
|
||||
cell->value.integer = i;
|
||||
return;
|
||||
}
|
||||
|
||||
int d = strtold(cell->text, &endptr);
|
||||
|
||||
if (*endptr == '\0') {
|
||||
cell->type = Floating;
|
||||
cell->value.floating = d;
|
||||
return;
|
||||
}
|
||||
}
|
||||
38
src/cell.h
Normal file
38
src/cell.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef CELL_H
|
||||
#define CELL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include "common.h"
|
||||
|
||||
enum cell_type {
|
||||
Text,
|
||||
Integer,
|
||||
Floating,
|
||||
Date
|
||||
};
|
||||
|
||||
struct cell_list {
|
||||
struct cell *cell;
|
||||
struct cell_list *next;
|
||||
};
|
||||
|
||||
struct cell {
|
||||
struct cell_list *referenced_by;
|
||||
size_t x_pos;
|
||||
size_t y_pos;
|
||||
char *text;
|
||||
enum cell_type type;
|
||||
union {
|
||||
int64_t integer;
|
||||
double floating;
|
||||
time_t date;
|
||||
} value;
|
||||
};
|
||||
|
||||
void cell_init(struct cell *c, size_t x_pos, size_t y_pos, enum cell_type type);
|
||||
void cell_delete(struct cell *c);
|
||||
void cell_set_type(struct cell *cell);
|
||||
|
||||
#endif
|
||||
65
src/celltree.c
Normal file
65
src/celltree.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "celltree.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
|
||||
/* returns true if (ax,ay) `greater than` (bx,by) */
|
||||
static bool greater_than(int x_a, int y_a, int x_b, int y_b)
|
||||
{
|
||||
if (x_a == x_b)
|
||||
return y_a > y_b;
|
||||
|
||||
return x_a > x_b;
|
||||
}
|
||||
|
||||
void celltree_delete(struct celltree *root)
|
||||
{
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cell_delete(root->cell);
|
||||
celltree_delete(root->right);
|
||||
celltree_delete(root->left);
|
||||
|
||||
free(root);
|
||||
}
|
||||
|
||||
struct cell* celltree_search(struct celltree *root, size_t x, size_t y)
|
||||
{
|
||||
if (root == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct cell *c = root->cell;
|
||||
|
||||
if (c->x_pos == x && c->y_pos == y) {
|
||||
return root->cell;
|
||||
}
|
||||
|
||||
if (greater_than(x, y, c->x_pos, c->y_pos)) {
|
||||
return celltree_search(root->right, x, y);
|
||||
} else {
|
||||
return celltree_search(root->left, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void celltree_insert(struct celltree **root, struct cell *c)
|
||||
{
|
||||
if (*root == NULL) {
|
||||
*root = malloc(sizeof **root);
|
||||
if (*root == NULL)
|
||||
exit(errno);
|
||||
(*root)->cell = c;
|
||||
(*root)->right = NULL;
|
||||
(*root)->left = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (greater_than(c->x_pos, c->y_pos, (*root)->cell->x_pos,
|
||||
(*root)->cell->y_pos)) {
|
||||
celltree_insert(&(*root)->right, c);
|
||||
} else {
|
||||
celltree_insert(&(*root)->left, c);
|
||||
}
|
||||
}
|
||||
21
src/celltree.h
Normal file
21
src/celltree.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CELLTREE_H
|
||||
#define CELLTREE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "cell.h"
|
||||
|
||||
/* TODO: implement red-black tree */
|
||||
struct celltree {
|
||||
struct cell *cell;
|
||||
struct celltree *right;
|
||||
struct celltree *left;
|
||||
bool isRed;
|
||||
};
|
||||
|
||||
void celltree_delete(struct celltree *root);
|
||||
|
||||
struct cell *celltree_search(struct celltree *root, size_t x, size_t y);
|
||||
|
||||
void celltree_insert(struct celltree **root, struct cell *c);
|
||||
|
||||
#endif
|
||||
7
src/common.h
Normal file
7
src/common.h
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
enum modes {Edit, Command, G};
|
||||
|
||||
#endif
|
||||
30
src/config.h
Normal file
30
src/config.h
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
// strings
|
||||
#define DEFAULT_BOOK_TITLE "Untitled"
|
||||
#define DEFAULT_SHEET_TITLE "Sheet "
|
||||
#define DEFAULT_HEIGHT 40
|
||||
#define DEFAULT_WIDTH 80
|
||||
#define MAX_STR 64
|
||||
|
||||
|
||||
// dimensions
|
||||
#define CELL_SIZE 12
|
||||
#define TAB_SIZE 24
|
||||
#define Y_AXIS_WIDTH 4
|
||||
#define N_CELLS_WIDTH 5
|
||||
#define N_CELLS_HEIGHT 5
|
||||
|
||||
|
||||
// colors
|
||||
#define COLOR_TITLE 20
|
||||
#define COLOR_BACKGROUND 21
|
||||
#define COLOR_LIGHTER_GRAY 22
|
||||
#define COLOR_LIGHT_GRAY 23
|
||||
#define COLOR_GRAY 24
|
||||
#define COLOR_HIGHLIGHTED 25
|
||||
#define COLOR_EDITMODE 26
|
||||
|
||||
#endif
|
||||
247
src/draw.c
Normal file
247
src/draw.c
Normal file
@@ -0,0 +1,247 @@
|
||||
|
||||
#include "draw.h"
|
||||
#include "cell.h"
|
||||
#include "celltree.h"
|
||||
#include "config.h" // colors, sizes ...
|
||||
#include "sheet.h" // struct sheet
|
||||
#include <ncurses.h> // ncurses functions
|
||||
#include <signal.h> // handle sigterm
|
||||
#include <string.h> // strlen
|
||||
|
||||
#define _STR(x) #x
|
||||
#define STR(x) _STR(x)
|
||||
|
||||
static void cleanup()
|
||||
{
|
||||
clear();
|
||||
echo();
|
||||
curs_set(1);
|
||||
refresh();
|
||||
endwin();
|
||||
}
|
||||
|
||||
static int x_pos(int x)
|
||||
{
|
||||
return x * CELL_SIZE + Y_AXIS_WIDTH;
|
||||
}
|
||||
|
||||
static int y_pos(int y)
|
||||
{
|
||||
return y + 2;
|
||||
}
|
||||
|
||||
static int get_cell_color(int const row, int const col)
|
||||
{
|
||||
return (row + col) % 2 ? COLOR_LIGHTER_GRAY : COLOR_LIGHT_GRAY;
|
||||
}
|
||||
|
||||
void draw_init(struct book *book)
|
||||
{
|
||||
initscr();
|
||||
atexit(cleanup);
|
||||
signal(SIGTERM, exit);
|
||||
noecho();
|
||||
cbreak();
|
||||
keypad(stdscr, true);
|
||||
|
||||
set_escdelay(0);
|
||||
curs_set(0);
|
||||
|
||||
start_color();
|
||||
init_color(COLOR_LIGHTER_GRAY, 900, 900, 900);
|
||||
init_pair(COLOR_LIGHTER_GRAY, COLOR_BLACK, COLOR_LIGHTER_GRAY);
|
||||
|
||||
init_color(COLOR_LIGHT_GRAY, 800, 800, 800);
|
||||
init_pair(COLOR_LIGHT_GRAY, COLOR_BLACK, COLOR_LIGHT_GRAY);
|
||||
|
||||
init_color(COLOR_GRAY, 650, 650, 650);
|
||||
init_pair(COLOR_GRAY, COLOR_BLACK, COLOR_GRAY);
|
||||
|
||||
init_color(COLOR_HIGHLIGHTED, 800, 600, 600);
|
||||
init_pair(COLOR_HIGHLIGHTED, COLOR_BLACK, COLOR_HIGHLIGHTED);
|
||||
|
||||
init_color(COLOR_EDITMODE, 600, 800, 600);
|
||||
init_pair(COLOR_EDITMODE, COLOR_BLACK, COLOR_EDITMODE);
|
||||
|
||||
init_pair(COLOR_TITLE, COLOR_BLACK, COLOR_GREEN);
|
||||
|
||||
init_pair(COLOR_BACKGROUND, COLOR_MAGENTA, COLOR_GREEN);
|
||||
|
||||
bkgdset(COLOR_PAIR(COLOR_BACKGROUND));
|
||||
|
||||
draw_book(book, 0);
|
||||
|
||||
draw_highlight(0, 0);
|
||||
draw_right_status("Normal");
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void draw_cell(struct cell const * const cell)
|
||||
{
|
||||
int x = x_pos(cell->x_pos);
|
||||
int y = y_pos(cell->y_pos);
|
||||
|
||||
int color = get_cell_color(y, x);
|
||||
|
||||
move(y, x);
|
||||
|
||||
attron(COLOR_PAIR(color));
|
||||
|
||||
switch(cell->type){
|
||||
case Integer:
|
||||
printw("%" STR(CELL_SIZE) "li", cell->value.integer);
|
||||
break;
|
||||
case Floating:
|
||||
printw("%" STR(CELL_SIZE) "lf", cell->value.floating);
|
||||
break;
|
||||
default:
|
||||
printw("%-" STR(CELL_SIZE) "s", cell->text);
|
||||
break;
|
||||
}
|
||||
|
||||
attroff(COLOR_PAIR(color));
|
||||
}
|
||||
|
||||
static void draw_celltree(struct celltree const *const root)
|
||||
{
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
draw_cell(root->cell);
|
||||
draw_celltree(root->right);
|
||||
draw_celltree(root->left);
|
||||
}
|
||||
|
||||
static void draw_row_num(int n)
|
||||
{
|
||||
attron(COLOR_PAIR(COLOR_GRAY));
|
||||
printw("%" STR(Y_AXIS_WIDTH) "d", n);
|
||||
attroff(COLOR_PAIR(COLOR_GRAY));
|
||||
}
|
||||
|
||||
static void draw_row_header(int row)
|
||||
{
|
||||
// TODO: make it dynamic
|
||||
char buf[4] = " ";
|
||||
int i = sizeof buf - 2;
|
||||
row++;
|
||||
|
||||
while (row && i >= 0) {
|
||||
buf[i] = (row % 26) + 'A' - 1;
|
||||
row /= 26;
|
||||
i--;
|
||||
}
|
||||
|
||||
attron(COLOR_PAIR(COLOR_GRAY));
|
||||
printw("%" STR(CELL_SIZE) "s", buf);
|
||||
attroff(COLOR_PAIR(COLOR_GRAY));
|
||||
}
|
||||
|
||||
static void draw_sheet(struct sheet *s)
|
||||
{
|
||||
addch('\n');
|
||||
|
||||
int width = getmaxx(stdscr) - Y_AXIS_WIDTH;
|
||||
int n_cells_wide = width / CELL_SIZE;
|
||||
int n_cells_tall = getmaxy(stdscr) - 3;
|
||||
|
||||
// print row headers
|
||||
chgat(Y_AXIS_WIDTH, 0, COLOR_GRAY, NULL);
|
||||
for (int i = 0; i < n_cells_wide; i++) {
|
||||
draw_row_header(i);
|
||||
}
|
||||
|
||||
// clear sheet before drawing on top
|
||||
for (int row = 0; row < n_cells_tall; row++) {
|
||||
int color;
|
||||
addch('\n');
|
||||
draw_row_num(row);
|
||||
|
||||
for (int col = 0; col < n_cells_wide; col++) {
|
||||
color = get_cell_color(row, col);
|
||||
|
||||
attron(COLOR_PAIR(color));
|
||||
|
||||
mvprintw(y_pos(row),
|
||||
x_pos(col),
|
||||
"%" STR(CELL_SIZE) "s", " ");
|
||||
}
|
||||
attroff(COLOR_PAIR(color));
|
||||
}
|
||||
|
||||
// draw on top of empty sheet
|
||||
draw_celltree(s->root_cell);
|
||||
}
|
||||
|
||||
static void draw_input_bar()
|
||||
{
|
||||
int width, height;
|
||||
|
||||
getmaxyx(stdscr, height, width);
|
||||
|
||||
move(height - 1, 0);
|
||||
|
||||
attron(COLOR_PAIR(COLOR_TITLE));
|
||||
for (int i = 0; i < width; i++) {
|
||||
addch(' ');
|
||||
}
|
||||
}
|
||||
|
||||
void draw_book(struct book *book, size_t tab)
|
||||
{
|
||||
move(0, 0);
|
||||
attron(COLOR_PAIR(COLOR_TITLE));
|
||||
printw(" %s ", book->title);
|
||||
attroff(COLOR_PAIR(COLOR_TITLE));
|
||||
|
||||
for (size_t i = 0; i < book->n_sheets; i++) {
|
||||
int attr = tab == i ? COLOR_GRAY : COLOR_LIGHT_GRAY;
|
||||
attron(COLOR_PAIR(attr));
|
||||
printw("%10s ", book->sheets[i]->title);
|
||||
attroff(COLOR_PAIR(attr));
|
||||
}
|
||||
|
||||
draw_sheet(book->sheets[tab]);
|
||||
|
||||
draw_input_bar();
|
||||
}
|
||||
|
||||
/* highlights relative to screen */
|
||||
void draw_highlight(int const x, int const y)
|
||||
{
|
||||
static int prev_x;
|
||||
static int prev_y;
|
||||
int color;
|
||||
|
||||
mvchgat(y_pos(y), x_pos(x), CELL_SIZE, 0, COLOR_HIGHLIGHTED,
|
||||
NULL);
|
||||
|
||||
color = get_cell_color(prev_y, prev_x);
|
||||
|
||||
mvchgat(y_pos(prev_y), x_pos(prev_x), CELL_SIZE, 0, color, NULL);
|
||||
|
||||
prev_x = x;
|
||||
prev_y = y;
|
||||
}
|
||||
|
||||
void draw_left_status(char const *const s)
|
||||
{
|
||||
int y = getmaxy(stdscr);
|
||||
|
||||
attron(COLOR_PAIR(COLOR_TITLE));
|
||||
mvprintw(y - 1, 1, "%s", s);
|
||||
attron(COLOR_PAIR(COLOR_TITLE));
|
||||
}
|
||||
|
||||
void draw_right_status(char const *const s)
|
||||
{
|
||||
int x, y;
|
||||
getmaxyx(stdscr, y, x);
|
||||
|
||||
attron(COLOR_PAIR(COLOR_TITLE));
|
||||
mvprintw(y - 1, x - 1 - strlen(s), "%s", s);
|
||||
attron(COLOR_PAIR(COLOR_TITLE));
|
||||
}
|
||||
|
||||
21
src/draw.h
Normal file
21
src/draw.h
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
#ifndef DRAW_H
|
||||
#define DRAW_H
|
||||
|
||||
#include "book.h"
|
||||
#include <stddef.h>
|
||||
#include "cell.h"
|
||||
|
||||
void draw_highlight(int const x, int const y);
|
||||
|
||||
void draw_init(struct book *book);
|
||||
|
||||
void draw_book(struct book *b, size_t tab);
|
||||
|
||||
void draw_cell(struct cell const * const cell);
|
||||
|
||||
void draw_left_status(const char *const s);
|
||||
|
||||
void draw_right_status(char const *const s);
|
||||
|
||||
#endif
|
||||
147
src/function_parse.c
Normal file
147
src/function_parse.c
Normal file
@@ -0,0 +1,147 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
static char *operators[] = {
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
">",
|
||||
"<",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static char *separators[] = {
|
||||
",",
|
||||
"(",
|
||||
")",
|
||||
NULL,
|
||||
};
|
||||
|
||||
enum type {
|
||||
error,
|
||||
operator,
|
||||
separator,
|
||||
identifier,
|
||||
number,
|
||||
};
|
||||
|
||||
struct token {
|
||||
char const * value;
|
||||
size_t value_len;
|
||||
enum type type;
|
||||
};
|
||||
|
||||
struct token_list {
|
||||
struct token *tokens;
|
||||
size_t len;
|
||||
char *error_msg;
|
||||
};
|
||||
|
||||
// returns length of string in haystack matching needle, or 0 if it
|
||||
// doesn't exist
|
||||
static int contains(char * const *haystack, char* needle)
|
||||
{
|
||||
for(char *s = *haystack; s; s++){
|
||||
if (strcmp(s, needle) == 0) {
|
||||
return strlen(s);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct token eat(char **str, int (*f)(int), enum type type)
|
||||
{
|
||||
struct token tok;
|
||||
tok.type = type;
|
||||
tok.value = *str;
|
||||
tok.value_len = 0;
|
||||
|
||||
while (f(**str)) {
|
||||
*str += 1;
|
||||
tok.value_len += 1;
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
static struct token tokenize_identifier(char** str)
|
||||
{
|
||||
return eat(str, isalnum, identifier);
|
||||
}
|
||||
|
||||
static struct token tokenize_number(char **str)
|
||||
{
|
||||
return eat(str, isdigit, identifier);
|
||||
}
|
||||
|
||||
// Returns a token array from function
|
||||
static struct token_list tokenize(char *str)
|
||||
{
|
||||
struct token_list *tok_list = malloc(sizeof *tok_list);
|
||||
|
||||
if (tok_list == NULL) {
|
||||
exit(errno);
|
||||
}
|
||||
|
||||
const size_t max_tok = 16; // TODO: dynamically grow array
|
||||
|
||||
tok_list->tokens = malloc(sizeof *(tok_list->tokens) * max_tok);
|
||||
if (tok_list->tokens == NULL)
|
||||
exit(errno);
|
||||
tok_list->len = 0;
|
||||
|
||||
// identifiers start with letters but can contain numbers
|
||||
while (*str && tok_list->len < max_tok) {
|
||||
struct token t;
|
||||
size_t n;
|
||||
|
||||
if (isalpha(*str)) {
|
||||
t = tokenize_identifier(&str);
|
||||
}
|
||||
else if (!isalnum(*str) ) {
|
||||
t = tokenize_number(&str);
|
||||
}
|
||||
else if ((n = contains(operators, str))) {
|
||||
t.type = operator;
|
||||
t.value = str;
|
||||
t.value_len = n;
|
||||
}
|
||||
else if ((n = contains(separators, str))) {
|
||||
t.type = separator;
|
||||
t.value = str;
|
||||
t.value_len = n;
|
||||
}
|
||||
else {
|
||||
t.type = error;
|
||||
t.value = str;
|
||||
t.value_len = strlen(str);
|
||||
}
|
||||
|
||||
tok_list->tokens[tok_list->len] = t;
|
||||
tok_list->len += 1;
|
||||
}
|
||||
|
||||
return *tok_list;
|
||||
}
|
||||
|
||||
// credits: Daniel J. Bernstein
|
||||
// https://web.archive.org/web/20220328102559/http://www.cse.yorku.ca/~oz/hash.html
|
||||
static unsigned long hash(unsigned char *str)
|
||||
{
|
||||
unsigned long hash = 5381;
|
||||
int c = 0;
|
||||
|
||||
for(size_t i=0; str[i] != '\0'; i++)
|
||||
hash = hash * 33 + c;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
7
src/function_parse.h
Normal file
7
src/function_parse.h
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
#ifndef FUNCTION_PARSE_H
|
||||
#define FUNCTION_PARSE_H
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
166
src/interface.c
Normal file
166
src/interface.c
Normal file
@@ -0,0 +1,166 @@
|
||||
|
||||
#include "interface.h"
|
||||
#include "cell.h"
|
||||
#include <curses.h>
|
||||
#include <stdlib.h>
|
||||
#include "celltree.h"
|
||||
#include "keymap.h"
|
||||
#include "draw.h"
|
||||
|
||||
int g_display_sel_x = 0;
|
||||
int g_display_sel_y = 0;
|
||||
int g_tab = 0;
|
||||
//int g_right_scrolled = 0;
|
||||
//int g_down_scrolled = 0;
|
||||
|
||||
struct book *g_book = NULL;
|
||||
struct cell *g_cur = NULL;
|
||||
|
||||
enum modes mode = Command;
|
||||
|
||||
void interface_attach(struct book *b)
|
||||
{
|
||||
g_book = b;
|
||||
}
|
||||
|
||||
void interface_move_right()
|
||||
{
|
||||
g_display_sel_x++;
|
||||
draw_highlight(g_display_sel_x, g_display_sel_y);
|
||||
}
|
||||
|
||||
void interface_move_left()
|
||||
{
|
||||
if (g_display_sel_x == 0)
|
||||
return;
|
||||
|
||||
g_display_sel_x--;
|
||||
draw_highlight(g_display_sel_x, g_display_sel_y);
|
||||
}
|
||||
|
||||
void interface_move_up()
|
||||
{
|
||||
if (g_display_sel_y == 0)
|
||||
return;
|
||||
|
||||
g_display_sel_y--;
|
||||
draw_highlight(g_display_sel_x, g_display_sel_y);
|
||||
}
|
||||
|
||||
void interface_move_down()
|
||||
{
|
||||
g_display_sel_y++;
|
||||
draw_highlight(g_display_sel_x, g_display_sel_y);
|
||||
}
|
||||
|
||||
void interface_next_tab()
|
||||
{
|
||||
g_tab++;
|
||||
g_tab %= g_book->n_sheets;
|
||||
draw_book(g_book, g_tab);
|
||||
}
|
||||
|
||||
void interface_prev_tab()
|
||||
{
|
||||
g_tab--;
|
||||
if (g_tab < 0) {
|
||||
g_tab = g_book->n_sheets - 1;
|
||||
}
|
||||
draw_book(g_book, g_tab);
|
||||
}
|
||||
|
||||
void interface_editor_backspace()
|
||||
{
|
||||
size_t textlen = strlen(g_cur->text);
|
||||
|
||||
if (textlen == 0)
|
||||
return;
|
||||
|
||||
int x, y;
|
||||
|
||||
addch(' ');
|
||||
getyx(stdscr, y, x);
|
||||
move(y, x - 1);
|
||||
|
||||
g_cur->text = realloc(g_cur->text, textlen);
|
||||
g_cur->text[textlen - 1] = '\0';
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void interface_editor_append(const char c)
|
||||
{
|
||||
int x, y;
|
||||
|
||||
size_t textlen = strlen(g_cur->text);
|
||||
|
||||
g_cur->text = realloc(g_cur->text, textlen + 2);
|
||||
g_cur->text[textlen] = c;
|
||||
g_cur->text[textlen + 1] = '\0';
|
||||
|
||||
getyx(stdscr, y, x);
|
||||
draw_cell(g_cur);
|
||||
move(y, x);
|
||||
refresh();
|
||||
}
|
||||
|
||||
static void start_edit_cell()
|
||||
{
|
||||
// struct cell* cur
|
||||
g_cur = celltree_search(g_book->sheets[g_tab]->root_cell, g_display_sel_x,
|
||||
g_display_sel_y);
|
||||
|
||||
if (g_cur == NULL) {
|
||||
g_cur = malloc(sizeof(struct cell));
|
||||
cell_init(g_cur, g_display_sel_x, g_display_sel_y, Text);
|
||||
celltree_insert(&g_book->sheets[g_tab]->root_cell, g_cur);
|
||||
}
|
||||
|
||||
draw_left_status(g_cur->text);
|
||||
}
|
||||
|
||||
void interface_mode_normal()
|
||||
{
|
||||
draw_right_status("Normal");
|
||||
mode = Command;
|
||||
curs_set(0);
|
||||
}
|
||||
|
||||
void interface_mode_g()
|
||||
{
|
||||
draw_right_status("g");
|
||||
mode = G;
|
||||
}
|
||||
|
||||
void interface_mode_edit()
|
||||
{
|
||||
mode = Edit;
|
||||
draw_right_status("Insert");
|
||||
curs_set(1);
|
||||
echo();
|
||||
start_edit_cell();
|
||||
}
|
||||
|
||||
void interface_commit_cell_change()
|
||||
{
|
||||
cell_set_type(g_cur);
|
||||
draw_cell(g_cur);
|
||||
}
|
||||
|
||||
void interface_handle_resize()
|
||||
{
|
||||
draw_book(g_book, g_tab);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void interface_interact()
|
||||
{
|
||||
refresh();
|
||||
|
||||
int x, y;
|
||||
getmaxyx(stdscr, y, x);
|
||||
move(y - 1, x - 1);
|
||||
|
||||
int c = getch();
|
||||
keymap_parse_key(c, mode);
|
||||
}
|
||||
69
src/interface.h
Normal file
69
src/interface.h
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
#ifndef INTERFACE_H
|
||||
#define INTERFACE_H
|
||||
|
||||
#include "book.h"
|
||||
|
||||
/* */
|
||||
void interface_commit_cell_change();
|
||||
|
||||
/* terminal resize handler */
|
||||
void interface_handle_resize();
|
||||
|
||||
/* attach book to interface */
|
||||
void interface_attach(struct book *b);
|
||||
|
||||
/* move one cell right */
|
||||
void interface_move_right();
|
||||
|
||||
/* move one cell left */
|
||||
void interface_move_left();
|
||||
|
||||
/* move one cell up */
|
||||
void interface_move_up();
|
||||
|
||||
/* move one cell down */
|
||||
void interface_move_down();
|
||||
|
||||
/* move to prev tab */
|
||||
void interface_prev_tab();
|
||||
|
||||
/* move to next tab */
|
||||
void interface_next_tab();
|
||||
|
||||
/* writes a status message in the bottom
|
||||
* right corner
|
||||
**/
|
||||
void interface_write_right_status(char const *const s);
|
||||
|
||||
/* event loop */
|
||||
void interface_interact();
|
||||
|
||||
/*
|
||||
* Insert Mode commands
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
/* Add char to input */
|
||||
void interface_editor_append(const char c);
|
||||
|
||||
/* Remove last char */
|
||||
void interface_editor_backspace();
|
||||
|
||||
|
||||
/*
|
||||
* Mode switching
|
||||
* --------------
|
||||
*/
|
||||
|
||||
/* switch to g mode TODO: find better way */
|
||||
void interface_mode_g();
|
||||
|
||||
/* switch to insert mode */
|
||||
void interface_mode_edit();
|
||||
|
||||
/* switch to normal mode */
|
||||
void interface_mode_normal();
|
||||
|
||||
|
||||
#endif
|
||||
81
src/keymap.c
Normal file
81
src/keymap.c
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
#include "keymap.h"
|
||||
#include "draw.h"
|
||||
#include "interface.h"
|
||||
#include <curses.h>
|
||||
|
||||
#define KEY_ESC 27
|
||||
|
||||
static void * const keymap_always[1024] = {
|
||||
[KEY_RESIZE] = interface_handle_resize,
|
||||
[27] /*ESC*/ = interface_mode_normal,
|
||||
};
|
||||
|
||||
static void * const keymap_normal[1024] = {
|
||||
['i'] = interface_mode_edit,
|
||||
['a'] = interface_mode_edit,
|
||||
['g'] = interface_mode_g,
|
||||
[KEY_LEFT] = interface_move_left,
|
||||
['h'] = interface_move_left,
|
||||
[KEY_DOWN] = interface_move_down,
|
||||
['j'] = interface_move_down,
|
||||
[KEY_UP] = interface_move_up,
|
||||
['k'] = interface_move_up,
|
||||
[KEY_RIGHT] = interface_move_right,
|
||||
['l'] = interface_move_right,
|
||||
};
|
||||
|
||||
static void * const keymap_edit[1024] = {
|
||||
[KEY_ESC] = interface_mode_normal,
|
||||
[KEY_BACKSPACE] = interface_editor_backspace,
|
||||
};
|
||||
|
||||
static void * const keymap_g[1024] = {
|
||||
['t'] = interface_next_tab,
|
||||
['T'] = interface_prev_tab,
|
||||
};
|
||||
|
||||
static void undefined()
|
||||
{
|
||||
int height = getmaxy(stdscr);
|
||||
move(height - 1, 0);
|
||||
printw("key not defined");
|
||||
}
|
||||
|
||||
void keymap_parse_key(size_t c, enum modes mode)
|
||||
{
|
||||
void (*cmd)(void) = NULL;
|
||||
|
||||
switch (mode) {
|
||||
case Command:
|
||||
cmd = keymap_normal[c];
|
||||
break;
|
||||
case G:
|
||||
cmd = keymap_g[c];
|
||||
break;
|
||||
case Edit:
|
||||
cmd = keymap_edit[c];
|
||||
if (cmd == NULL){
|
||||
interface_editor_append(c);
|
||||
return;
|
||||
}
|
||||
if (c == KEY_ESC) {
|
||||
interface_commit_cell_change();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(mode != Edit) {
|
||||
mode = Command;
|
||||
}
|
||||
|
||||
if (cmd == NULL) {
|
||||
cmd = keymap_always[c];
|
||||
|
||||
if (cmd == NULL) {
|
||||
cmd = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
(*cmd)();
|
||||
}
|
||||
10
src/keymap.h
Normal file
10
src/keymap.h
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
#ifndef KEYMAP_H
|
||||
#define KEYMAP_H
|
||||
|
||||
#include "common.h"
|
||||
#include <stddef.h>
|
||||
|
||||
void keymap_parse_key(size_t c, enum modes mode);
|
||||
|
||||
#endif
|
||||
33
src/main.c
Normal file
33
src/main.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "book.h"
|
||||
#include "draw.h"
|
||||
#include "config.h"
|
||||
#include "interface.h"
|
||||
#include "sheet.h"
|
||||
#include <curses.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
(void)argv;
|
||||
(void)argc;
|
||||
|
||||
struct book b;
|
||||
|
||||
book_init(&b, DEFAULT_BOOK_TITLE);
|
||||
|
||||
book_add_sheet(&b);
|
||||
book_add_sheet(&b);
|
||||
book_add_sheet(&b);
|
||||
|
||||
draw_init(&b);
|
||||
|
||||
interface_attach(&b);
|
||||
|
||||
while (1) {
|
||||
interface_interact();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
27
src/sheet.c
Normal file
27
src/sheet.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "sheet.h"
|
||||
#include "celltree.h"
|
||||
|
||||
void sheet_init(struct sheet *s, size_t width, size_t height, char *title)
|
||||
{
|
||||
s->width = width;
|
||||
s->height = height;
|
||||
s->root_cell = NULL;
|
||||
|
||||
if (title) {
|
||||
s->title = strndup(title, MAX_STR);
|
||||
} else {
|
||||
s->title = strdup("");
|
||||
}
|
||||
}
|
||||
|
||||
void sheet_delete(struct sheet *s)
|
||||
{
|
||||
if (s == NULL) {
|
||||
warn("attempted to delete NULL sheet");
|
||||
return;
|
||||
}
|
||||
|
||||
celltree_delete(s->root_cell);
|
||||
free(s->title);
|
||||
free(s);
|
||||
}
|
||||
20
src/sheet.h
Normal file
20
src/sheet.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef SHEET_H
|
||||
#define SHEET_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <err.h>
|
||||
#include "config.h"
|
||||
|
||||
struct sheet {
|
||||
struct celltree *root_cell;
|
||||
size_t width;
|
||||
size_t height;
|
||||
char* title;
|
||||
};
|
||||
|
||||
void sheet_init(struct sheet *s, size_t width, size_t height, char* title);
|
||||
void sheet_delete(struct sheet *s);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user