1 /*
2  * Schism Tracker - a cross-platform Impulse Tracker clone
3  * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4  * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5  * copyright (c) 2009 Storlek & Mrs. Brisby
6  * copyright (c) 2010-2012 Storlek
7  * URL: http://schismtracker.org/
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 /* this is a little ad-hoc; i did some work trying to bring it back into CVS
25    LARGELY because I can't remember all the font characters. :)
26 */
27 #include "headers.h"
28 #include "it.h"
29 #include "dmoz.h"
30 #include "page.h"
31 #include "version.h"
32 #include "log.h"
33 
34 #include "sdlmain.h"
35 #include <string.h>
36 
37 static const uint8_t itfmap_chars[] = {
38 128, 129, 130, ' ', 128, 129, 141, ' ', 142, 143, 144, ' ', 168, 'C', '-', '0',
39 131, ' ', 132, ' ', 131, ' ', 132, ' ', 145, ' ', 146, ' ', 168, 'D', '-', '1',
40 133, 134, 135, ' ', 140, 134, 135, ' ', 147, 148, 149, ' ', 168, 'E', '-', '2',
41 ' ', ' ', ' ', ' ', ' ', 139, 134, 138, 153, 148, 152, ' ', 168, 'F', '-', '3',
42 174, ' ', ' ', ' ', 155, 132, ' ', 131, 146, ' ', 145, ' ', 168, 'G', '-', '4',
43 175, ' ', ' ', ' ', 156, 137, 129, 136, 151, 143, 150, ' ', 168, 'A', '-', '5',
44 176, ' ', ' ', ' ', 157, ' ', 184, 184, 191, '6', '4', 192, 168, 'B', '-', '6',
45 176, 177, ' ', ' ', 158, 163, 250, 250, 250, 250, 250, ' ', 168, 'C', '#', '7',
46 176, 178, ' ', ' ', 159, 164, ' ', ' ', ' ', 185, 186, ' ', 168, 'D', '#', '8',
47 176, 179, 180, ' ', 160, 165, ' ', ' ', ' ', 189, 190, ' ', 168, 'E', '#', '9',
48 176, 179, 181, ' ', 161, 166, ' ', ' ', ' ', 187, 188, ' ', 168, 'F', '#', '1',
49 176, 179, 182, ' ', 162, 167, 126, 126, 126, ' ', ' ', ' ', 168, 'G', '#', '2',
50 154, 154, 154, 154, ' ', ' ', 205, 205, 205, ' ', 183, ' ', 168, 'A', '#', '3',
51 169, 170, 171, 172, ' ', ' ', '^', '^', '^', ' ', 173, ' ', 168, 'B', '#', '4',
52 193, 194, 195, 196, 197, 198, 199, 200, 201, ' ', ' ', ' ', ' ', ' ', ' ', ' ',
53 };
54 static const uint8_t helptext_gen[] =
55 	"Tab         Next box   \xa8 Alt-C  Copy\n"
56 	"Shift-Tab   Prev. box  \xa8 Alt-P  Paste\n"
57 	"F2-F4       Switch box \xa8 Alt-M  Mix paste\n"
58 	"\x18\x19\x1a\x1b        Dump core  \xa8 Alt-Z  Clear\n"
59 	"Ctrl-S/F10  Save font  \xa8 Alt-H  Flip horiz\n"
60 	"Ctrl-R/F9   Load font  \xa8 Alt-V  Flip vert\n"
61 	"Backspace   Reset font \xa8 Alt-I  Invert\n"
62 	"Ctrl-Bksp   BIOS font  \xa8 Alt-Bk Reset text\n"
63 	"                       \xa8 0-9    Palette\n"
64 	"Ctrl-Q      Exit       \xa8  (+10 with shift)\n";
65 
66 static const uint8_t helptext_editbox[] =
67 "Space       Plot/clear point\n"
68 "Ins/Del     Fill/clear horiz.\n"
69 "...w/Shift  Fill/clear vert.\n"
70 "\n"
71 "+/-         Next/prev. char.\n"
72 "PgUp/PgDn   Next/previous row\n"
73 "Home/End    Top/bottom corner\n"
74 "\n" "Shift-\x18\x19\x1a\x1b  Shift character\n"
75 "[/]         Rotate 90\xf8\n";
76 
77 static const uint8_t helptext_charmap[] =
78 "Home/End    First/last char.\n";
79 
80 static const uint8_t helptext_fontlist[] =
81 "Home/End    First/last font\n"
82 "Enter       Load/save file\n"
83 "Escape      Hide font list\n"
84 "\n\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a"
85 "\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\n\n"
86 "Remember to save as font.cfg\n"
87 "to change the default font!\n";
88 
89 /* --------------------------------------------------------------------- */
90 /* statics & local constants
91 note: x/y are for the top left corner of the frame, but w/h define the size of its *contents* */
92 
93 #define EDITBOX_X 0
94 #define EDITBOX_Y 0
95 #define EDITBOX_W 9
96 #define EDITBOX_H 11
97 
98 #define CHARMAP_X 17
99 #define CHARMAP_Y 0
100 #define CHARMAP_W 16
101 #define CHARMAP_H 16
102 
103 #define ITFMAP_X 41
104 #define ITFMAP_Y 0
105 #define ITFMAP_W 16
106 #define ITFMAP_H 15
107 
108 #define FONTLIST_X 65
109 #define FONTLIST_Y 0
110 #define VISIBLE_FONTS 22 /* this should be called FONTLIST_H... */
111 
112 #define HELPTEXT_X 0
113 #define HELPTEXT_Y 31
114 
115 /* don't randomly mess with these for obvious reasons */
116 #define INNER_X(x) ((x) + 3)
117 #define INNER_Y(y) ((y) + 4)
118 
119 #define FRAME_RIGHT 3
120 #define FRAME_BOTTOM 3
121 
122 #define WITHIN(n,l,u) ((n) >= (l) && (n) < (u))
123 #define POINT_IN(x,y,item) \
124 	(WITHIN((x), INNER_X(item##_X), INNER_X(item##_X) + item##_W) \
125 	&& WITHIN((y), INNER_Y(item##_Y), INNER_Y(item##_Y) + item##_H))
126 #define POINT_IN_FRAME(x,y,item) \
127 	(WITHIN((x), item##_X, INNER_X(item##_X) + item##_W + FRAME_RIGHT) \
128 	&& WITHIN((y), item##_Y, INNER_Y(item##_Y) + item##_H + FRAME_BOTTOM))
129 
130 static int edit_x = 3, edit_y = 3;
131 static uint8_t current_char = 'A';
132 static int itfmap_pos = -1;
133 
134 static enum {
135 	EDITBOX, CHARMAP, ITFMAP, FONTLIST
136 } selected_item = EDITBOX;
137 
138 static enum {
139 	MODE_OFF, MODE_LOAD, MODE_SAVE
140 } fontlist_mode = MODE_OFF;
141 
142 static dmoz_filelist_t flist;
143 static int top_font = 0, cur_font = 0;
144 
145 
fontlist_reposition(void)146 static void fontlist_reposition(void)
147 {
148 	if (cur_font < 0)
149 		cur_font = 0; /* weird! */
150 	if (cur_font < top_font)
151 		top_font = cur_font;
152 	else if (cur_font > top_font + (VISIBLE_FONTS - 1))
153 		top_font = cur_font - (VISIBLE_FONTS - 1);
154 }
155 
fontgrep(dmoz_file_t * f)156 static int fontgrep(dmoz_file_t *f)
157 {
158 	const char *ext;
159 
160 	if (f->sort_order == -100)
161 		return 1; /* this is our font.cfg, at the top of the list */
162 	if (f->type & TYPE_BROWSABLE_MASK)
163 		return 0; /* we don't care about directories and stuff */
164 	ext = get_extension(f->base);
165 	return (strcasecmp(ext, ".itf") == 0 || strcasecmp(ext, ".fnt") == 0);
166 }
167 
load_fontlist(void)168 static void load_fontlist(void)
169 {
170 	char *font_dir, *p;
171 	struct stat st = {};
172 
173 	dmoz_free(&flist, NULL);
174 
175 	top_font = cur_font = 0;
176 
177 	font_dir = dmoz_path_concat_len(cfg_dir_dotschism, "fonts", strlen(cfg_dir_dotschism), 5);
178 	mkdir(font_dir, 0755);
179 	p = dmoz_path_concat_len(font_dir, "font.cfg", strlen(font_dir), 8);
180 	dmoz_add_file(&flist, p, str_dup("font.cfg"), &st, -100); /* put it on top */
181 	if (dmoz_read(font_dir, &flist, NULL, NULL) < 0)
182 		log_perror(font_dir);
183 	free(font_dir);
184 	dmoz_filter_filelist(&flist, fontgrep, &cur_font, NULL);
185 	while (dmoz_worker());
186 	fontlist_reposition();
187 
188 	/* p is freed by dmoz_free */
189 }
190 
191 
192 
193 static uint8_t clipboard[8] = { 0 };
194 
195 #define INCR_WRAPPED(n) (((n) & 0xf0) | (((n) + 1) & 0xf))
196 #define DECR_WRAPPED(n) (((n) & 0xf0) | (((n) - 1) & 0xf))
197 
198 /* if this is nonzero, the screen will be redrawn. none of the functions
199  * except main should call draw_anything -- set this instead. */
draw_frame(const char * name,int x,int y,int inner_width,int inner_height,int active)200 static void draw_frame(const char* name, int x, int y, int inner_width, int inner_height, int active)
201 {
202 	int n, c;
203 	int len = strlen(name);
204 
205 	if (len > inner_width + 2)
206 		len = inner_width + 2;
207 	c = (status.flags & INVERTED_PALETTE) ? 1 : 3;
208 
209 	draw_box(x, y + 1, x + inner_width + 5,
210 			  y + inner_height + 6, BOX_THIN | BOX_CORNER | BOX_OUTSET);
211 	draw_box(x + 1, y + 2, x + inner_width + 4,
212 			  y + inner_height + 5, BOX_THIN | BOX_INNER | BOX_INSET);
213 
214 	draw_char(128, x, y, c, 2);
215 	for (n = 0; n < len + 1; n++)
216 		draw_char(129, x + n + 1, y, c, 2);
217 	draw_char(130, x + n, y, c, 2);
218 	draw_char(131, x, y + 1, c, 2);
219 	draw_char(137, x + len + 1, y + 1, c, 2);
220 
221 	switch (active) {
222 	case 0:                 /* inactive */
223 		n = 0;
224 		break;
225 	case -1:                        /* disabled */
226 		n = 1;
227 		break;
228 	default:                        /* active */
229 		n = 3;
230 		break;
231 	}
232 	draw_text_len(name, len, x + 1, y + 1, n, 2);
233 }
234 
235 /* --------------------------------------------------------------------- */
236 
draw_editbox(void)237 static inline void draw_editbox(void)
238 {
239 	int c;
240 	char buf[12];
241 	int ci = current_char << 3, i, j, fg;
242 
243 	for (i = 0; i < 8; i++) {
244 		draw_char('1' + i, INNER_X(EDITBOX_X) + i + 1,
245 				   INNER_Y(EDITBOX_Y) + 2, (i == edit_x ? 3 : 1), 0);
246 		draw_char('1' + i, INNER_X(EDITBOX_X),
247 				   INNER_Y(EDITBOX_Y) + i + 3, (i == edit_y ? 3 : 1), 0);
248 
249 		for (j = 0; j < 8; j++) {
250 			if (font_data[ci + j] & (128 >> i)) {
251 				c = 15;
252 				fg = 6;
253 			} else {
254 				c = 173;
255 				fg = 1;
256 			}
257 			if (selected_item == EDITBOX && i == edit_x && j == edit_y)
258 				draw_char(c, INNER_X(EDITBOX_X) + 1 + i,
259 						   INNER_Y(EDITBOX_Y) + 3 + j, 0, 3);
260 			else
261 				draw_char(c, INNER_X(EDITBOX_X) + 1 + i,
262 						   INNER_Y(EDITBOX_Y) + 3 + j, fg, 0);
263 		}
264 	}
265 	draw_char(current_char, INNER_X(EDITBOX_X), INNER_Y(EDITBOX_Y), 5, 0);
266 
267 	sprintf(buf, "%3d $%02X", current_char, current_char);
268 	draw_text(buf, INNER_X(EDITBOX_X) + 2, INNER_Y(EDITBOX_Y), 5, 0);
269 }
270 
draw_charmap(void)271 static inline void draw_charmap(void)
272 {
273 	int n = 256;
274 
275 	if (selected_item == CHARMAP) {
276 		while (n) {
277 			n--;
278 			draw_char(n, INNER_X(CHARMAP_X) + n % 16, INNER_Y(CHARMAP_Y) + n / 16,
279 					   (n == current_char ? 0 : 1), (n == current_char ? 3 : 0));
280 		}
281 	} else {
282 		while (n) {
283 			n--;
284 			draw_char(n, INNER_X(CHARMAP_X) + n % 16, INNER_Y(CHARMAP_Y) + n / 16,
285 					   (n == current_char ? 3 : 1), 0);
286 		}
287 	}
288 }
289 
draw_itfmap(void)290 static inline void draw_itfmap(void)
291 {
292 	int n, fg, bg;
293 	uint8_t *ptr;
294 
295 	if (itfmap_pos < 0 || itfmap_chars[itfmap_pos] != current_char) {
296 		ptr = (unsigned char *) strchr((char *) itfmap_chars, current_char);
297 		if (ptr == NULL)
298 			itfmap_pos = -1;
299 		else
300 			itfmap_pos = ptr - itfmap_chars;
301 	}
302 
303 	for (n = 0; n < 240; n++) {
304 		fg = 1;
305 		bg = 0;
306 		if (n == itfmap_pos) {
307 			if (selected_item == ITFMAP) {
308 				fg = 0;
309 				bg = 3;
310 			} else {
311 				fg = 3;
312 			}
313 		}
314 		draw_char(itfmap_chars[n],
315 				   INNER_X(ITFMAP_X) + n % 16, INNER_Y(ITFMAP_Y) + n / 16, fg, bg);
316 	}
317 }
318 
draw_fontlist(void)319 static inline void draw_fontlist(void)
320 {
321 	int x, pos = 0, n = top_font, cfg, cbg;
322 	dmoz_file_t *f;
323 	char *ptr;
324 
325 	if (selected_item == FONTLIST) {
326 		cfg = 0;
327 		cbg = 3;
328 	} else {
329 		cfg = 3;
330 		cbg = 0;
331 	}
332 
333 	if (top_font < 0) top_font = 0;
334 	if (n < 0) n = 0;
335 
336 	while (n < flist.num_files && pos < VISIBLE_FONTS) {
337 		x = 1;
338 		f = flist.files[n];
339 		if (!f) break;
340 		ptr = f->base;
341 		if (n == cur_font) {
342 			draw_char(183, INNER_X(FONTLIST_X), INNER_Y(FONTLIST_Y) + pos, cfg, cbg);
343 			while (x < 9 && *ptr && (n == 0 || *ptr != '.')) {
344 				draw_char(*ptr,
345 						   INNER_X(FONTLIST_X) + x,
346 						   INNER_Y(FONTLIST_Y) + pos, cfg, cbg);
347 				x++;
348 				ptr++;
349 			}
350 			while (x < 9) {
351 				draw_char(0,
352 						   INNER_X(FONTLIST_X) + x,
353 						   INNER_Y(FONTLIST_Y) + pos, cfg, cbg);
354 				x++;
355 			}
356 		} else {
357 			draw_char(173, INNER_X(FONTLIST_X), INNER_Y(FONTLIST_Y) + pos, 2, 0);
358 			while (x < 9 && *ptr && (n == 0 || *ptr != '.')) {
359 				draw_char(*ptr,
360 						   INNER_X(FONTLIST_X) + x, INNER_Y(FONTLIST_Y) + pos, 5, 0);
361 				x++;
362 				ptr++;
363 			}
364 			while (x < 9) {
365 				draw_char(0, INNER_X(FONTLIST_X) + x, INNER_Y(FONTLIST_Y) + pos, 5, 0);
366 				x++;
367 			}
368 		}
369 		n++;
370 		pos++;
371 	}
372 }
373 
draw_helptext(void)374 static inline void draw_helptext(void)
375 {
376 	const uint8_t *ptr = helptext_gen;
377 	const uint8_t *eol;
378 	int line;
379 	int column;
380 
381 	for (line = INNER_Y(HELPTEXT_Y); *ptr; line++) {
382 		eol = (unsigned char *) strchr((char *) ptr, '\n');
383 		if (!eol)
384 			eol = (unsigned char *) strchr((char *) ptr, '\0');
385 		for (column = INNER_X(HELPTEXT_X); ptr < eol; ptr++, column++)
386 			draw_char(*ptr, column, line, 12, 0);
387 		ptr++;
388 	}
389 	for (line = 0; line < 10; line++)
390 		draw_char(168, INNER_X(HELPTEXT_X) + 43, INNER_Y(HELPTEXT_Y) + line, 12, 0);
391 
392 	/* context sensitive stuff... oooh :) */
393 	switch (selected_item) {
394 	case EDITBOX:
395 		ptr = helptext_editbox;
396 		break;
397 	case CHARMAP:
398 	case ITFMAP:
399 		ptr = helptext_charmap;
400 		break;
401 	case FONTLIST:
402 		ptr = helptext_fontlist;
403 		break;
404 	}
405 	for (line = INNER_Y(HELPTEXT_Y); *ptr; line++) {
406 		eol = (unsigned char *) strchr((char *) ptr, '\n');
407 		if (!eol)
408 			eol = (unsigned char *) strchr((char *) ptr, '\0');
409 		draw_char(168, INNER_X(HELPTEXT_X) + 43, line, 12, 0);
410 		for (column = INNER_X(HELPTEXT_X) + 45; ptr < eol; ptr++, column++)
411 			draw_char(*ptr, column, line, 12, 0);
412 		ptr++;
413 	}
414 	draw_text(ver_short_copyright, 77 - strlen(ver_short_copyright), 46, 1, 0);
415 }
416 
draw_time(void)417 static inline void draw_time(void)
418 {
419 	char buf[16];
420 	sprintf(buf, "%.2d:%.2d:%.2d", status.tmnow.tm_hour, status.tmnow.tm_min, status.tmnow.tm_sec);
421 	draw_text(buf, 3, 46, 1, 0);
422 }
423 
424 extern unsigned int color_set[16];
425 
draw_screen(void)426 static void draw_screen(void)
427 {
428 	draw_frame("Edit Box", EDITBOX_X, EDITBOX_Y, 9, 11, !!(selected_item == EDITBOX));
429 	draw_editbox();
430 
431 	draw_frame("Current Font", CHARMAP_X, CHARMAP_Y, 16, 16, !!(selected_item == CHARMAP));
432 	draw_charmap();
433 
434 	draw_frame("Preview", ITFMAP_X, ITFMAP_Y, 16, 15, !!(selected_item == ITFMAP));
435 	draw_itfmap();
436 
437 	switch (fontlist_mode) {
438 	case MODE_LOAD:
439 		draw_frame("Load/Browse", FONTLIST_X, FONTLIST_Y, 9,
440 			   VISIBLE_FONTS, !!(selected_item == FONTLIST));
441 		draw_fontlist();
442 		break;
443 	case MODE_SAVE:
444 		draw_frame("Save As...", FONTLIST_X, FONTLIST_Y, 9,
445 			   VISIBLE_FONTS, !!(selected_item == FONTLIST));
446 		draw_fontlist();
447 		break;
448 	default:                        /* Off? (I sure hope so!) */
449 		break;
450 	}
451 
452 	draw_frame("Quick Help", HELPTEXT_X, HELPTEXT_Y, 74, 12, -1);
453 	draw_helptext();
454 
455 	draw_time();
456 }
handle_key_editbox(struct key_event * k)457 static void handle_key_editbox(struct key_event * k)
458 {
459 	uint8_t tmp[8] = { 0 };
460 	int ci = current_char << 3;
461 	int n, bit;
462 	uint8_t *ptr = font_data + ci;
463 
464 	switch (k->sym) {
465 	case SDLK_UP:
466 		if (k->mod & KMOD_SHIFT) {
467 			int s = ptr[0];
468 			for (n = 0; n < 7; n++)
469 				ptr[n] = ptr[n + 1];
470 			ptr[7] = s;
471 		} else {
472 			if (--edit_y < 0)
473 				edit_y = 7;
474 		}
475 		break;
476 	case SDLK_DOWN:
477 		if (k->mod & KMOD_SHIFT) {
478 			int s = ptr[7];
479 			for (n = 7; n; n--)
480 				ptr[n] = ptr[n - 1];
481 			ptr[0] = s;
482 		} else {
483 			edit_y = (edit_y + 1) % 8;
484 		}
485 		break;
486 	case SDLK_LEFT:
487 		if (k->mod & KMOD_SHIFT) {
488 			for (n = 0; n < 8; n++, ptr++)
489 				*ptr = (*ptr >> 7) | (*ptr << 1);
490 		} else {
491 			if (--edit_x < 0)
492 				edit_x = 7;
493 		}
494 		break;
495 	case SDLK_RIGHT:
496 		if (k->mod & KMOD_SHIFT) {
497 			for (n = 0; n < 8; n++, ptr++)
498 				*ptr = (*ptr << 7) | (*ptr >> 1);
499 		} else {
500 			edit_x = (edit_x + 1) % 8;
501 		}
502 		break;
503 	case SDLK_HOME:
504 		edit_x = edit_y = 0;
505 		break;
506 	case SDLK_END:
507 		edit_x = edit_y = 7;
508 		break;
509 	case SDLK_SPACE:
510 		ptr[edit_y] ^= (128 >> edit_x);
511 		break;
512 	case SDLK_INSERT:
513 		if (k->mod & KMOD_SHIFT) {
514 			for (n = 0; n < 8; n++)
515 				ptr[n] |= (128 >> edit_x);
516 		} else {
517 			ptr[edit_y] = 255;
518 		}
519 		break;
520 	case SDLK_DELETE:
521 		if (k->mod & KMOD_SHIFT) {
522 			for (n = 0; n < 8; n++)
523 				ptr[n] &= ~(128 >> edit_x);
524 		} else {
525 			ptr[edit_y] = 0;
526 		}
527 		break;
528 	case SDLK_LEFTBRACKET:
529 		for (n = 0; n < 8; n++)
530 			for (bit = 0; bit < 8; bit++)
531 				if (ptr[n] & (1 << bit))
532 					tmp[bit] |= 1 << (7 - n);
533 		memcpy(ptr, tmp, 8);
534 		break;
535 	case SDLK_RIGHTBRACKET:
536 		for (n = 0; n < 8; n++)
537 			for (bit = 0; bit < 8; bit++)
538 				if (ptr[n] & (1 << bit))
539 					tmp[7 - bit] |= 1 << n;
540 		memcpy(ptr, tmp, 8);
541 		break;
542 	case SDLK_PLUS:
543 	case SDLK_EQUALS:
544 		current_char++;
545 		break;
546 	case SDLK_MINUS:
547 	case SDLK_UNDERSCORE:
548 		current_char--;
549 		break;
550 	case SDLK_PAGEUP:
551 		current_char -= 16;
552 		break;
553 	case SDLK_PAGEDOWN:
554 		current_char += 16;
555 		break;
556 	default:
557 		return;
558 	}
559 
560 	status.flags |= NEED_UPDATE;
561 }
562 
handle_key_charmap(struct key_event * k)563 static void handle_key_charmap(struct key_event * k)
564 {
565 	switch (k->sym) {
566 	case SDLK_UP:
567 		current_char -= 16;
568 		break;
569 	case SDLK_DOWN:
570 		current_char += 16;
571 		break;
572 	case SDLK_LEFT:
573 		current_char = DECR_WRAPPED(current_char);
574 		break;
575 	case SDLK_RIGHT:
576 		current_char = INCR_WRAPPED(current_char);
577 		break;
578 	case SDLK_HOME:
579 		current_char = 0;
580 		break;
581 	case SDLK_END:
582 		current_char = 255;
583 		break;
584 	default:
585 		return;
586 	}
587 	status.flags |= NEED_UPDATE;
588 }
589 
handle_key_itfmap(struct key_event * k)590 static void handle_key_itfmap(struct key_event * k)
591 {
592 	switch (k->sym) {
593 	case SDLK_UP:
594 		if (itfmap_pos < 0) {
595 			itfmap_pos = 224;
596 		} else {
597 			itfmap_pos -= 16;
598 			if (itfmap_pos < 0)
599 				itfmap_pos += 240;
600 		}
601 		current_char = itfmap_chars[itfmap_pos];
602 		break;
603 	case SDLK_DOWN:
604 		if (itfmap_pos < 0)
605 			itfmap_pos = 16;
606 		else
607 			itfmap_pos = (itfmap_pos + 16) % 240;
608 		current_char = itfmap_chars[itfmap_pos];
609 		break;
610 	case SDLK_LEFT:
611 		if (itfmap_pos < 0)
612 			itfmap_pos = 15;
613 		else
614 			itfmap_pos = DECR_WRAPPED(itfmap_pos);
615 		current_char = itfmap_chars[itfmap_pos];
616 		break;
617 	case SDLK_RIGHT:
618 		if (itfmap_pos < 0)
619 			itfmap_pos = 0;
620 		else
621 			itfmap_pos = INCR_WRAPPED(itfmap_pos);
622 		current_char = itfmap_chars[itfmap_pos];
623 		break;
624 	case SDLK_HOME:
625 		current_char = itfmap_chars[0];
626 		itfmap_pos = 0;
627 		break;
628 	case SDLK_END:
629 		current_char = itfmap_chars[239];
630 		itfmap_pos = 239;
631 		break;
632 	default:
633 		return;
634 	}
635 	status.flags |= NEED_UPDATE;
636 }
637 
confirm_font_save_ok(void * vf)638 static void confirm_font_save_ok(void *vf)
639 {
640 	char *f = vf;
641 	if (font_save(f) != 0) {
642 		fprintf(stderr, "%s\n", SDL_GetError());
643 		return;
644 	}
645 	selected_item = EDITBOX;
646 }
647 
handle_key_fontlist(struct key_event * k)648 static void handle_key_fontlist(struct key_event * k)
649 {
650 	int new_font = cur_font;
651 
652 	switch (k->sym) {
653 	case SDLK_HOME:
654 		new_font = 0;
655 		break;
656 	case SDLK_END:
657 		new_font = flist.num_files - 1;
658 		break;
659 	case SDLK_UP:
660 		new_font--;
661 		break;
662 	case SDLK_DOWN:
663 		new_font++;
664 		break;
665 	case SDLK_PAGEUP:
666 		new_font -= VISIBLE_FONTS;
667 		break;
668 	case SDLK_PAGEDOWN:
669 		new_font += VISIBLE_FONTS;
670 		break;
671 	case SDLK_ESCAPE:
672 		selected_item = EDITBOX;
673 		fontlist_mode = MODE_OFF;
674 		break;
675 	case SDLK_RETURN:
676 		if (k->state == KEY_PRESS)
677 			return;
678 		switch (fontlist_mode) {
679 		case MODE_LOAD:
680 			if (cur_font < flist.num_files
681 			&& flist.files[cur_font]
682 			&& font_load(flist.files[cur_font]->base) != 0) {
683 				fprintf(stderr, "%s\n", SDL_GetError());
684 				font_reset();
685 			}
686 			break;
687 		case MODE_SAVE:
688 			if (cur_font < flist.num_files && flist.files[cur_font]) {
689 				if (strcasecmp(flist.files[cur_font]->base,"font.cfg") != 0) {
690 					dialog_create(DIALOG_OK_CANCEL, "Overwrite font file?",
691 						confirm_font_save_ok, NULL, 1, flist.files[cur_font]->base);
692 					return;
693 				}
694 				confirm_font_save_ok(flist.files[cur_font]->base);
695 			}
696 			selected_item = EDITBOX;
697 			/* fontlist_mode = MODE_OFF; */
698 			break;
699 		default:
700 			/* should never happen */
701 			return;
702 		}
703 		break;
704 	default:
705 		return;
706 	}
707 
708 	if (new_font != cur_font) {
709 		new_font = CLAMP(new_font, 0, flist.num_files - 1);
710 		if (new_font == cur_font)
711 			return;
712 		cur_font = new_font;
713 		fontlist_reposition();
714 	}
715 	status.flags |= NEED_UPDATE;
716 }
717 
718 /* --------------------------------------------------------------------- */
719 
handle_mouse_editbox(struct key_event * k)720 static void handle_mouse_editbox(struct key_event *k)
721 {
722 	int n, ci = current_char << 3, xrel, yrel;
723 	uint8_t *ptr = font_data + ci;
724 
725 	xrel = k->x - INNER_X(EDITBOX_X);
726 	yrel = k->y - INNER_Y(EDITBOX_Y);
727 
728 	if (xrel > 0 && yrel > 2) {
729 		edit_x = xrel - 1;
730 		edit_y = yrel - 3;
731 		switch (k->mouse_button) {
732 		case MOUSE_BUTTON_LEFT: /* set */
733 			ptr[edit_y] |= (128 >> edit_x);
734 			break;
735 		case MOUSE_BUTTON_MIDDLE: /* invert */
736 			if (k->state == KEY_RELEASE)
737 				return;
738 			ptr[edit_y] ^= (128 >> edit_x);
739 			break;
740 		case MOUSE_BUTTON_RIGHT: /* clear */
741 			ptr[edit_y] &= ~(128 >> edit_x);
742 			break;
743 		}
744 	} else if (xrel == 0 && yrel == 2) {
745 		/* clicking at the origin modifies the entire character */
746 		switch (k->mouse_button) {
747 		case MOUSE_BUTTON_LEFT: /* set */
748 			for (n = 0; n < 8; n++)
749 				ptr[n] = 255;
750 			break;
751 		case MOUSE_BUTTON_MIDDLE: /* invert */
752 			if (k->state == KEY_RELEASE)
753 				return;
754 			for (n = 0; n < 8; n++)
755 				ptr[n] ^= 255;
756 			break;
757 		case MOUSE_BUTTON_RIGHT: /* clear */
758 			for (n = 0; n < 8; n++)
759 				ptr[n] = 0;
760 			break;
761 		}
762 	} else if (xrel == 0 && yrel > 2) {
763 		edit_y = yrel - 3;
764 		switch (k->mouse_button) {
765 		case MOUSE_BUTTON_LEFT: /* set */
766 			ptr[edit_y] = 255;
767 			break;
768 		case MOUSE_BUTTON_MIDDLE: /* invert */
769 			if (k->state == KEY_RELEASE)
770 				return;
771 			ptr[edit_y] ^= 255;
772 			break;
773 		case MOUSE_BUTTON_RIGHT: /* clear */
774 			ptr[edit_y] = 0;
775 			break;
776 		}
777 	} else if (yrel == 2 && xrel > 0) {
778 		edit_x = xrel - 1;
779 		switch (k->mouse_button) {
780 		case MOUSE_BUTTON_LEFT: /* set */
781 			for (n = 0; n < 8; n++)
782 				ptr[n] |= (128 >> edit_x);
783 			break;
784 		case MOUSE_BUTTON_MIDDLE: /* invert */
785 			if (k->state == KEY_RELEASE)
786 				return;
787 			for (n = 0; n < 8; n++)
788 				ptr[n] ^= (128 >> edit_x);
789 			break;
790 		case MOUSE_BUTTON_RIGHT: /* clear */
791 			for (n = 0; n < 8; n++)
792 				ptr[n] &= ~(128 >> edit_x);
793 			break;
794 		}
795 	}
796 }
797 
handle_mouse_charmap(struct key_event * k)798 static void handle_mouse_charmap(struct key_event *k)
799 {
800 	int xrel = k->x - INNER_X(CHARMAP_X), yrel = k->y - INNER_Y(CHARMAP_Y);
801 	if (!k->mouse) return;
802 	current_char = 16 * yrel + xrel;
803 }
804 
handle_mouse_itfmap(struct key_event * k)805 static void handle_mouse_itfmap(struct key_event *k)
806 {
807 	int xrel = k->x - INNER_X(ITFMAP_X), yrel = k->y - INNER_Y(ITFMAP_Y);
808 	if (!k->mouse) return;
809 	itfmap_pos = 16 * yrel + xrel;
810 	current_char = itfmap_chars[itfmap_pos];
811 }
812 
handle_mouse(struct key_event * k)813 static void handle_mouse(struct key_event * k)
814 {
815 	int x = k->x, y = k->y;
816 	if (POINT_IN_FRAME(x, y, EDITBOX)) {
817 		selected_item = EDITBOX;
818 		if (POINT_IN(x, y, EDITBOX))
819 			handle_mouse_editbox(k);
820 	} else if (POINT_IN_FRAME(x, y, CHARMAP)) {
821 		selected_item = CHARMAP;
822 		if (POINT_IN(x, y, CHARMAP))
823 			handle_mouse_charmap(k);
824 	} else if (POINT_IN_FRAME(x, y, ITFMAP)) {
825 		selected_item = ITFMAP;
826 		if (POINT_IN(x, y, ITFMAP))
827 			handle_mouse_itfmap(k);
828 	} else {
829 		//printf("stray click\n");
830 		return;
831 	}
832 	status.flags |= NEED_UPDATE;
833 }
834 
835 
fontedit_handle_key(struct key_event * k)836 static int fontedit_handle_key(struct key_event * k)
837 {
838 	int n, ci = current_char << 3;
839 	uint8_t *ptr = font_data + ci;
840 
841 	if (k->mouse == MOUSE_SCROLL_UP || k->mouse == MOUSE_SCROLL_DOWN) {
842 		/* err... */
843 		return 0;
844 	}
845 
846 	if (k->mouse == MOUSE_CLICK) {
847 		handle_mouse(k);
848 		return 1;
849 	}
850 
851 	/* kp is special */
852 	switch (k->orig_sym) {
853 	case SDLK_KP0:
854 		if (k->state == KEY_RELEASE)
855 			return 1;
856 		k->sym += 10;
857 		/* fall through */
858 	case SDLK_KP1...SDLK_KP9:
859 		if (k->state == KEY_RELEASE)
860 			return 1;
861 		n = k->sym - SDLK_KP1;
862 		if (k->mod & KMOD_SHIFT)
863 			n += 10;
864 		palette_load_preset(n);
865 		palette_apply();
866 		status.flags |= NEED_UPDATE;
867 		return 1;
868 	default:
869 		break;
870 	};
871 
872 	switch (k->sym) {
873 	case '0':
874 		if (k->state == KEY_RELEASE)
875 			return 1;
876 		k->sym += 10;
877 		/* fall through */
878 	case '1'...'9':
879 		if (k->state == KEY_RELEASE)
880 			return 1;
881 		n = k->sym - '1';
882 		if (k->mod & KMOD_SHIFT)
883 			n += 10;
884 		palette_load_preset(n);
885 		palette_apply();
886 		status.flags |= NEED_UPDATE;
887 		return 1;
888 	case SDLK_F2:
889 		if (k->state == KEY_RELEASE)
890 			return 1;
891 		selected_item = EDITBOX;
892 		status.flags |= NEED_UPDATE;
893 		return 1;
894 	case SDLK_F3:
895 		if (k->state == KEY_RELEASE)
896 			return 1;
897 		selected_item = CHARMAP;
898 		status.flags |= NEED_UPDATE;
899 		return 1;
900 	case SDLK_F4:
901 		if (k->state == KEY_RELEASE)
902 			return 1;
903 		selected_item = ITFMAP;
904 		status.flags |= NEED_UPDATE;
905 		return 1;
906 	case SDLK_TAB:
907 		if (k->state == KEY_RELEASE)
908 			return 1;
909 		if (k->mod & KMOD_SHIFT) {
910 			if (selected_item == 0)
911 				selected_item = (fontlist_mode == MODE_OFF ? 2 : 3);
912 			else
913 				selected_item--;
914 		} else {
915 			selected_item = (selected_item + 1) % (fontlist_mode == MODE_OFF ? 3 : 4);
916 		}
917 		status.flags |= NEED_UPDATE;
918 		return 1;
919 	case SDLK_c:
920 		if (k->state == KEY_RELEASE)
921 			return 1;
922 		if (k->mod & KMOD_ALT) {
923 			memcpy(clipboard, ptr, 8);
924 			return 1;
925 		}
926 		break;
927 	case SDLK_p:
928 		if (k->state == KEY_RELEASE)
929 			return 1;
930 		if (k->mod & KMOD_ALT) {
931 			memcpy(ptr, clipboard, 8);
932 			status.flags |= NEED_UPDATE;
933 			return 1;
934 		}
935 		break;
936 	case SDLK_m:
937 		if (k->state == KEY_RELEASE)
938 			return 1;
939 		if (k->mod & KMOD_CTRL) {
940 			SDL_ToggleCursor();
941 			return 1;
942 		} else if (k->mod & KMOD_ALT) {
943 			for (n = 0; n < 8; n++)
944 				ptr[n] |= clipboard[n];
945 			status.flags |= NEED_UPDATE;
946 			return 1;
947 		}
948 		break;
949 	case SDLK_z:
950 		if (k->state == KEY_RELEASE)
951 			return 1;
952 		if (k->mod & KMOD_ALT) {
953 			memset(ptr, 0, 8);
954 			status.flags |= NEED_UPDATE;
955 			return 1;
956 		}
957 		break;
958 	case SDLK_h:
959 		if (k->state == KEY_RELEASE)
960 			return 1;
961 		if (k->mod & KMOD_ALT) {
962 			for (n = 0; n < 8; n++) {
963 				int r = ptr[n];
964 				r = ((r >> 1) & 0x55) | ((r << 1) & 0xaa);
965 				r = ((r >> 2) & 0x33) | ((r << 2) & 0xcc);
966 				r = ((r >> 4) & 0x0f) | ((r << 4) & 0xf0);
967 				ptr[n] = r;
968 			}
969 			status.flags |= NEED_UPDATE;
970 			return 1;
971 		}
972 		break;
973 	case SDLK_v:
974 		if (k->state == KEY_RELEASE)
975 			return 1;
976 		if (k->mod & KMOD_ALT) {
977 			for (n = 0; n < 4; n++) {
978 				uint8_t r = ptr[n];
979 				ptr[n] = ptr[7 - n];
980 				ptr[7 - n] = r;
981 			}
982 			status.flags |= NEED_UPDATE;
983 			return 1;
984 		}
985 		break;
986 	case SDLK_i:
987 		if (k->state == KEY_RELEASE)
988 			return 1;
989 		if (k->mod & KMOD_ALT) {
990 			for (n = 0; n < 8; n++)
991 				font_data[ci + n] ^= 255;
992 			status.flags |= NEED_UPDATE;
993 			return 1;
994 		}
995 		break;
996 
997 		/* ----------------------------------------------------- */
998 
999 	case SDLK_l:
1000 	case SDLK_r:
1001 		if (k->state == KEY_RELEASE)
1002 			return 1;
1003 		if (!(k->mod & KMOD_CTRL)) break;
1004 		/* fall through */
1005 	case SDLK_F9:
1006 		if (k->state == KEY_RELEASE)
1007 			return 1;
1008 		load_fontlist();
1009 		fontlist_mode = MODE_LOAD;
1010 		selected_item = FONTLIST;
1011 		status.flags |= NEED_UPDATE;
1012 		return 1;
1013 	case SDLK_s:
1014 		if (k->state == KEY_RELEASE)
1015 			return 1;
1016 		if (!(k->mod & KMOD_CTRL)) break;
1017 		/* fall through */
1018 	case SDLK_F10:
1019 		/* a bit weird, but this ensures that font.cfg
1020 		 * is always the default font to save to, but
1021 		 * without the annoyance of moving the cursor
1022 		 * back to it every time f10 is pressed. */
1023 		if (fontlist_mode != MODE_SAVE) {
1024 			cur_font = top_font = 0;
1025 			load_fontlist();
1026 			fontlist_mode = MODE_SAVE;
1027 		}
1028 		selected_item = FONTLIST;
1029 		status.flags |= NEED_UPDATE;
1030 		return 1;
1031 	case SDLK_BACKSPACE:
1032 		if (k->state == KEY_RELEASE)
1033 			return 1;
1034 		if (k->mod & KMOD_CTRL) {
1035 			font_reset_bios();
1036 		} else if (k->mod & KMOD_ALT) {
1037 			font_reset_char(current_char);
1038 		} else {
1039 			font_reset_upper();
1040 		}
1041 		status.flags |= NEED_UPDATE;
1042 		return 1;
1043 	case SDLK_RETURN:
1044 		return 0;
1045 	case SDLK_q:
1046 		if (k->mod & KMOD_CTRL)
1047 			return 0;
1048 		if (k->state == KEY_RELEASE)
1049 			return 1;
1050 		break;
1051 	default:
1052 		if (k->state == KEY_RELEASE)
1053 			return 1;
1054 		break;
1055 	}
1056 
1057 	switch (selected_item) {
1058 	case EDITBOX:
1059 		handle_key_editbox(k);
1060 		break;
1061 	case CHARMAP:
1062 		handle_key_charmap(k);
1063 		break;
1064 	case ITFMAP:
1065 		handle_key_itfmap(k);
1066 		break;
1067 	case FONTLIST:
1068 		handle_key_fontlist(k);
1069 		break;
1070 	default:
1071 		break;
1072 	}
1073 	return 1;
1074 }
1075 
1076 
1077 static struct widget fontedit_widget_hack[1];
1078 
fontedit_key_hack(struct key_event * k)1079 static int fontedit_key_hack(struct key_event *k)
1080 {
1081 	switch (k->sym) {
1082 	case SDLK_r: case SDLK_l: case SDLK_s:
1083 	case SDLK_c: case SDLK_p: case SDLK_m:
1084 	case SDLK_z: case SDLK_v: case SDLK_h:
1085 	case SDLK_i: case SDLK_q: case SDLK_w:
1086 	case SDLK_F1...SDLK_F12:
1087 		return fontedit_handle_key(k);
1088 	case SDLK_RETURN:
1089 		if (status.dialog_type & (DIALOG_MENU|DIALOG_BOX)) return 0;
1090 		if (selected_item == FONTLIST) {
1091 			handle_key_fontlist(k);
1092 			return 1;
1093 		}
1094 	default:
1095 		break;
1096 	};
1097 	return 0;
1098 }
1099 
do_nil(void)1100 static void do_nil(void) {}
fontedit_load_page(struct page * page)1101 void fontedit_load_page(struct page *page)
1102 {
1103 	page->title = "";
1104 	page->draw_full = draw_screen;
1105 	page->total_widgets = 1;
1106 	page->pre_handle_key = fontedit_key_hack;
1107 	page->widgets = fontedit_widget_hack;
1108 	create_other(fontedit_widget_hack, 0, fontedit_handle_key, do_nil);
1109 }
1110