sss

spreadsheets for plan9
git clone git://nsmpr.xyz/sss.git
Log | Files | Refs | README

sss.c (10108B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <draw.h>
      4 #include <thread.h>
      5 #include <mouse.h>
      6 #include <cursor.h>
      7 #include <keyboard.h>
      8 
      9 #include "stack.h"
     10 #include "cells.h"
     11 
     12 /* cell drawing flags */
     13 enum {
     14 	CENTER    = 1,
     15 	HIGHLIGHT = 2,
     16 	SELECTED  = 4,
     17 	CURSOR    = 8,
     18 };
     19 
     20 Stack *hp, *hr;
     21 Stack *T;
     22 
     23 struct Selection {
     24 	Stack rows;
     25 	Stack cols;
     26 	Stack cells;
     27 	Point cursor;
     28 } selection;
     29 
     30 int defcwidth, defcheight;
     31 int mstate;
     32 
     33 enum { BG, HG, FG, BR };
     34 Image *Inorm[4], *Isel[4];
     35 
     36 Point view;
     37 
     38 Rectangle rcells, rrows, rcols, rlbar, rbbar, rtext, rnope;
     39 
     40 void (*mousefunc)(Mouse);
     41 
     42 Mousectl *mc;
     43 Keyboardctl *kc;
     44 
     45 void usage(void);
     46 void calc_rects(void);
     47 void cell_primitive(Image *dst, Rectangle r, char *s, int flag);
     48 void clear(void);
     49 void draw_columns(void);
     50 void draw_cells(void);
     51 void draw_cell(Point);
     52 void draw_rows(void);
     53 void colors(void);
     54 void mousecells(Mouse);
     55 void mousecols(Mouse);
     56 void mouserows(Mouse);
     57 void run_cmd(Mousectl *mc, Keyboardctl *kc);
     58 void edit_cell(Point addr);
     59 Value* hist_get_value(Stack*, Point);
     60 Point mouse2addr(Point);
     61 int is_selected(Point);
     62 void unselect(void);
     63 
     64 void
     65 threadmain(int argc, char **argv)
     66 {
     67 	Mouse mv;
     68 	Rune  kv;
     69 	int   rv[2];
     70 	Mouse mv_old;
     71 	ARGBEGIN{
     72 	default:
     73 		usage();
     74 		break;
     75 	}
     76 	ARGEND;
     77 
     78 	if(initdraw(0, 0, "Super Spreadsheet 64!") < 0)
     79 		sysfatal("inidraw failed: %r");
     80 	if((mc = initmouse(0, screen)) == nil)
     81 		sysfatal("initmouse failed: %r");
     82 	if((kc = initkeyboard(0)) == nil)
     83 		sysfatal("initkeyboard failed: %r");
     84 
     85 	colors();
     86 	mousefunc = nil;
     87 	mstate = 0;
     88 	view = ZP;
     89 
     90 	defcheight = font->height + 4;
     91 	defcwidth = stringwidth(font, "0") * 8 + 4;
     92 	if (argc == 1) T = table_read(argv[0]);
     93 	else T = newstack();
     94 	hp = newstack();
     95 	selection.cursor = Pt(0, 0);
     96 	push(hp, T);
     97 
     98 	calc_rects();
     99 	clear();
    100 	draw_cells();
    101 	draw_columns();
    102 	draw_rows();
    103 	cell_primitive(screen, rnope, "", 0);
    104 	flushimage(display, 1);
    105 
    106 	enum { KBD, MOUSE, RESIZE, CHEND };
    107 	Alt alts[4] = {
    108 		[KBD] {kc->c, &kv, CHANRCV},
    109 		[MOUSE] {mc->c, &mv, CHANRCV},
    110 		[RESIZE] {mc->resizec, rv, CHANRCV},
    111 		[CHEND] {0, 0, CHANEND},
    112 	};
    113 
    114 	for (;;) {
    115 		switch (alt(alts)) {
    116 		case KBD:
    117 			if (kv == 0x7f) threadexitsall(nil);
    118 			run_cmd(mc, kc);
    119 			break;
    120 		case MOUSE:
    121 			if (mv.buttons == 0){
    122 				mstate = 0;
    123 				mousefunc = nil;
    124 				if (ptinrect(mv.xy, rrows))  mousefunc = mouserows;
    125 				if (ptinrect(mv.xy, rcols))  mousefunc = mousecols;
    126 				if (ptinrect(mv.xy, rcells)) mousefunc = mousecells;
    127 			}
    128 			else {
    129 				if (mousefunc != nil) mousefunc(mv);
    130 			}
    131 			if (mv.buttons == 2) mstate = 1;
    132 			if (mstate == 1) {
    133 				view = addpt(view, subpt(mv.xy, mv_old.xy));
    134 				if (view.x > 0) view.x = 0;
    135 				if (view.y > 0) view.y = 0;
    136 				draw_cells();
    137 				draw_columns();
    138 				draw_rows();
    139 				cell_primitive(screen, rnope, "", 0);
    140 				flushimage(display, 1);
    141 			}
    142 			mv_old = mv;
    143 			break;
    144 		case RESIZE:
    145 			if(getwindow(display, Refnone) < 0)
    146 				sysfatal("resize failed: %r");
    147 			calc_rects();
    148 			clear();
    149 			draw_cells();
    150 			draw_columns();
    151 			draw_rows();
    152 			cell_primitive(screen, rnope, "", 0);
    153 			flushimage(display, 1);
    154 			break;
    155 		}
    156 	}
    157 }
    158 
    159 void
    160 cell_primitive(Image *dst, Rectangle r, char *s, int flag)
    161 {
    162 	Rectangle rr;
    163 	Image **Ipal, *Ibg, *Ibr;
    164 	int selected, center, cursor, high;
    165 	int dx;
    166 	selected = flag & SELECTED;
    167 	center   = flag & CENTER;
    168 	high     = flag & HIGHLIGHT;
    169 	cursor   = flag & CURSOR;
    170 	Ipal = selected ? Isel : Inorm;
    171 	Ibg = high ? Ipal[HG] : Ipal[BG];
    172 	Ibr = Ipal[BR];
    173 	rr = insetrect(r, 1);
    174 	if (cursor != 0) {
    175 		Ibr = Ipal[FG];
    176 		rr = insetrect(r, 2);
    177 	}
    178 	dx = 0;
    179 	if (center != 0) dx = (rr.max.x - rr.min.x - stringwidth(font, s)) / 2;
    180 	draw(dst, r, Ibr, 0, ZP);
    181 	draw(dst, rr, Ibg, 0, ZP);
    182 	string(dst, addpt(Pt(2 + dx, 2), r.min), Ipal[FG], ZP, font, s);
    183 }
    184 
    185 void
    186 colors(void)
    187 {
    188 	Inorm[BG] = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0xFFFFFFFF);
    189 	Inorm[HG] = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0xBBBBBBFF);
    190 	Inorm[FG] = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0x000000FF);
    191 	Inorm[BR] = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0x000000FF);
    192 	Isel[BG]  = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0x000000FF);
    193 	Isel[HG]  = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0x444444FF);
    194 	Isel[FG]  = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0xFFFFFFFF);
    195 	Isel[BR]  = allocimage(display, Rect(0,0,1,1), RGBA32, 1, 0x888888FF);
    196 }
    197 
    198 void
    199 usage(void)
    200 {
    201 	fprint(2, "usage: %s", argv0);
    202 	threadexitsall("usage");
    203 }
    204 
    205 void
    206 calc_rects(void)
    207 {
    208 	Rectangle r;
    209 	r = screen->r;
    210 	rtext = Rect(
    211 		r.min.x, r.min.y,
    212 		r.max.x, r.min.y + font->height + 4);
    213 
    214 	rnope = Rect(
    215 		r.min.x, rtext.max.y,
    216 		r.min.x + defcwidth, rtext.max.y + defcheight);
    217 
    218 	rrows = Rect(
    219 		r.min.x, rtext.max.y + defcheight,
    220 		r.min.x + defcwidth, r.max.y);
    221 
    222 	rcols = Rect(
    223 		r.min.x + defcwidth, rtext.max.y,
    224 		r.max.x, rtext.max.y + defcheight);
    225 
    226 	rcells = Rect(
    227 		rrows.max.x, rcols.max.y,
    228 		r.max.x, r.max.y);
    229 }
    230 
    231 void
    232 clear(void)
    233 {
    234 	draw(screen, screen->r, display->white, 0, ZP);
    235 }
    236 
    237 void
    238 draw_columns(void)
    239 {
    240 	Point pt;
    241 	Rectangle r;
    242 	long col, *scol, i;
    243 	int flag;
    244 	char *colnum;
    245 	col = 0;
    246 	pt = addpt(Pt(view.x, 0), rcols.min);
    247 	while (pt.x < rcols.max.x) {
    248 		if (pt.x + defcwidth >= rcols.min.x) {
    249 			flag = CENTER|HIGHLIGHT;
    250 			for (i = 0; i < selection.cols.size; i++) {
    251 				scol = selection.cols.data[i];
    252 				if (col == *scol) {
    253 					flag |= SELECTED;
    254 					break;
    255 				}
    256 			}
    257 			r = rectaddpt(Rect(0, 0, defcwidth, defcheight), pt);
    258 			colnum = smprint("%ld", col);
    259 			cell_primitive(screen, r, colnum, flag);
    260 			free(colnum);
    261 		}
    262 		pt.x += defcwidth;
    263 		col++;
    264 	}
    265 }
    266 
    267 void
    268 draw_rows(void)
    269 {
    270 	Point pt;
    271 	Rectangle r;
    272 	long row, *srow, i;
    273 	int flag;
    274 	char *rownum;
    275 	row = 0;
    276 	pt = addpt(Pt(0, view.y), rrows.min);
    277 	while (pt.y < rrows.max.y) {
    278 		if (pt.y + defcheight >= rrows.min.y) {
    279 			flag = CENTER|HIGHLIGHT;
    280 			for (i = 0; i < selection.rows.size; i++) {
    281 				srow = selection.rows.data[i];
    282 				if (row == *srow) {
    283 					flag |= SELECTED;
    284 					break;
    285 				}
    286 			}
    287 			r = rectaddpt(Rect(0, 0, defcwidth, defcheight), pt);
    288 			rownum = smprint("%ld", row);
    289 			cell_primitive(screen, r, rownum, flag);
    290 			free(rownum);
    291 		}
    292 		pt.y += defcheight;
    293 		row++;
    294 	}
    295 }
    296 
    297 void
    298 draw_cell(Point addr)
    299 {
    300 	Rectangle r;
    301 	Point p;
    302 	Value *v;
    303 	char *text;
    304 	int flag;
    305 	p = addpt(Pt(addr.x * defcwidth, addr.y * defcheight), view);
    306 	r.min = addpt(rcells.min, p);
    307 	r.max = addpt(r.min, Pt(defcwidth, defcheight));
    308 	v = hist_get_value(hp, addr);
    309 	text = (v == nil) ? "" : v->data;
    310 	flag = is_selected(addr);
    311 	if (eqpt(addr, selection.cursor) != 0) flag |= CURSOR;
    312 	cell_primitive(screen, r, text, flag);
    313 }
    314 
    315 
    316 void
    317 draw_cells(void)
    318 {
    319 	Rectangle r;
    320 	long x, y;
    321 	r = rectsubpt(rcells, addpt(view, rcells.min));
    322 	r.min.x = r.min.x / defcwidth;
    323 	r.max.x = r.max.x / defcwidth;
    324 	r.min.y = r.min.y / defcheight;
    325 	r.max.y = r.max.y / defcheight;
    326 	for (x = r.min.x; x <= r.max.x; x++)
    327 		for (y = r.min.y; y <= r.max.y; y++)
    328 			draw_cell(Pt(x, y));
    329 }
    330 
    331 
    332 void
    333 run_cmd(Mousectl *mc, Keyboardctl *kc)
    334 {
    335 	char buf[1024];
    336 	*buf = 0;
    337 	enter("cmd:", buf, 1024, mc, kc, 0);
    338 
    339 }
    340 
    341 void
    342 edit_cell(Point addr)
    343 {
    344 	Value *v;
    345 	Stack *N;
    346 	N = newstack();
    347 	char prompt[256], buf[1024];
    348 	snprint(prompt, 256, "%d,%d:", addr.x, addr.y);
    349 	v = hist_get_value(hp, addr);
    350 	*buf = 0;
    351 	if ((v != nil)&&(v->data != nil)) strncat(buf, v->data, 1024);
    352 	if (enter(prompt, buf, 1024, mc, kc, 0) < 0) return;
    353 	v = malloc(sizeof(Value));
    354 	v->addr = addr;
    355 	v->data = strdup(buf);
    356 	push(N, v);
    357 	push(hp, N);
    358 	T = N;
    359 }
    360 
    361 Value*
    362 hist_get_value(Stack *H, Point addr)
    363 {
    364 	Stack *T;
    365 	Value *v;
    366 	long x, y;
    367 	for (x = H->size - 1; x >= 0; x--) {
    368 		T = H->data[x];
    369 		for (y = 0; y < T->size; y++) {
    370 			v = T->data[y];
    371 			if (eqpt(addr, v->addr) != 0) return v;
    372 		}
    373 	}
    374 	return nil;
    375 }
    376 
    377 Point
    378 mouse2addr(Point xy)
    379 {
    380 	Point addr;
    381 	addr.x = (xy.x - view.x - rcells.min.x)/defcwidth;
    382 	addr.y = (xy.y - view.y - rcells.min.y)/defcheight;
    383 	return addr;
    384 }
    385 
    386 
    387 int
    388 is_selected(Point addr)
    389 {
    390 	long i;
    391 	Point *a;
    392 	long *n;
    393 	for (i = 0; i < selection.cells.size; i++) {
    394 		a = selection.cells.data[i];
    395 		if (eqpt(addr, *a) != 0) return SELECTED;
    396 	}
    397 	for (i = 0; i < selection.cols.size; i++) {
    398 		n = selection.cols.data[i];
    399 		if (*n == addr.x) return SELECTED;
    400 	}
    401 	for (i = 0; i < selection.rows.size; i++) {
    402 		n = selection.rows.data[i];
    403 		if (*n == addr.y) return SELECTED;
    404 	}
    405 	return 0;
    406 }
    407 
    408 
    409 void
    410 mousecells(Mouse mv)
    411 {
    412 	Point m, n;
    413 	static Rectangle s;
    414 	Rectangle r;
    415 	m = mouse2addr(mv.xy);
    416 	if (mv.buttons == 1) {
    417 		unselect();
    418 		if (mstate == 0) {
    419 			draw_columns();
    420 			draw_rows();
    421 			s.min = m;
    422 			mstate = 2;
    423 		}
    424 		s.max = m;
    425 		r = canonrect(s);
    426 		for (n = r.min;; n.x++) {
    427 			if (n.x > r.max.x) {
    428 				n.x = r.min.x;
    429 				n.y++;
    430 			}
    431 			if (n.y > r.max.y) break;
    432 			Point *a;
    433 			a = malloc(sizeof(Point));
    434 			*a = n;
    435 			push(&selection.cells, a);
    436 		}
    437 		selection.cursor = m;
    438 		draw_cells();
    439 		cell_primitive(screen, rnope, "", 0);
    440 		flushimage(display, 1);
    441 	}
    442 	if (mv.buttons == 4) {
    443 		edit_cell(m);
    444 		draw_cell(m);
    445 		draw_columns();
    446 		draw_rows();
    447 		cell_primitive(screen, rnope, "", 0);
    448 		flushimage(display, 1);
    449 		flushimage(display, 1);
    450 	}
    451 }
    452 
    453 void
    454 mousecols(Mouse mv)
    455 {
    456 	Point m;
    457 	static Rectangle s;
    458 	Rectangle r;
    459 	int i;
    460 	m = mouse2addr(mv.xy);
    461 	if (mv.buttons == 1) {
    462 		unselect();
    463 		if (mstate == 0) {
    464 			draw_rows();
    465 			s.min = m;
    466 			mstate = 2;
    467 		}
    468 		s.max = m;
    469 		r = canonrect(s);
    470 		for (i = r.min.x; i <= r.max.x; i++) {
    471 			long *col;
    472 			col = malloc(sizeof(long));
    473 			*col = i;
    474 			push(&selection.cols, col);
    475 		}
    476 		draw_columns();
    477 		draw_cells();
    478 		flushimage(display, 1);
    479 	}
    480 }
    481 
    482 void
    483 mouserows(Mouse mv)
    484 {
    485 	Point m;
    486 	static Rectangle s;
    487 	Rectangle r;
    488 	int i;
    489 	m = mouse2addr(mv.xy);
    490 	if (mv.buttons == 1) {
    491 		unselect();
    492 		if (mstate == 0) {
    493 			draw_columns();
    494 			s.min = m;
    495 			mstate = 2;
    496 		}
    497 		s.max = m;
    498 		r = canonrect(s);
    499 		for (i = r.min.y; i <= r.max.y; i++) {
    500 			long *row;
    501 			row = malloc(sizeof(long));
    502 			*row = i;
    503 			push(&selection.rows, row);
    504 		}
    505 		draw_rows();
    506 		draw_cells();
    507 		flushimage(display, 1);
    508 	}
    509 }
    510 
    511 void
    512 unselect(void)
    513 {
    514 	while (selection.cells.size > 0) free(pop(&selection.cells));
    515 	while (selection.rows.size  > 0) free(pop(&selection.rows));
    516 	while (selection.cols.size  > 0) free(pop(&selection.cols));
    517 }