doc_pos_t doc_find_next(doc_ptr doc, cptr text, doc_pos_t start) { int y; for (y = start.y; y <= doc->cursor.y; y++) { int x = (y == start.y) ? start.x : 0; int ncell = doc->width - x; doc_char_ptr cell = doc_char(doc, doc_pos_create(x, y)); int i = _line_find_str(cell, ncell, text); if (i >= 0) { doc->selection.start.x = x + i; doc->selection.start.y = y; doc->selection.stop.x = x + i + strlen(text); doc->selection.stop.y = y; return doc->selection.start; } } doc->selection = doc_region_invalid(); return doc_pos_invalid(); }
doc_pos_t doc_find_prev(doc_ptr doc, cptr text, doc_pos_t start) { int y; for (y = start.y; y >= 0; y--) { int ncell = (y == start.y) ? start.x - 1 : doc->width; doc_char_ptr cell = doc_char(doc, doc_pos_create(0, y)); int i = _line_find_str(cell, ncell, text); if (i >= 0) { doc->selection.start.x = i; doc->selection.start.y = y; doc->selection.stop.x = i + strlen(text); doc->selection.stop.y = y; return doc->selection.start; } } doc->selection = doc_region_invalid(); return doc_pos_invalid(); }
doc_region_t doc_range_all(doc_ptr doc) { doc_region_t result; result.start = doc_pos_create(0, 0); result.stop = doc->cursor; return result; }
static void _display(rect_t r, int options) { doc_ptr doc = doc_alloc(r.cx); _list_spells(doc, options); doc_sync_term(doc, doc_range_all(doc), doc_pos_create(r.x, r.y)); doc_free(doc); }
void msg_line_redraw(void) { doc_sync_term( _msg_line_doc, doc_range_all(_msg_line_doc), doc_pos_create(_msg_line_rect.x, _msg_line_rect.y) ); _msg_line_sync_pos = doc_cursor(_msg_line_doc); }
static void msg_line_sync(void) { doc_sync_term( _msg_line_doc, doc_range_bottom(_msg_line_doc, _msg_line_sync_pos), doc_pos_create(_msg_line_rect.x, _msg_line_rect.y + _msg_line_sync_pos.y) ); _msg_line_sync_pos = doc_cursor(_msg_line_doc); /* inkey(); */ }
static void _display_spells_to_gain(object_type *o_ptr, rect_t r) { doc_ptr doc = doc_alloc(r.cx); int i; int realm = tval2realm(o_ptr->tval); int start_idx = o_ptr->sval * _SPELLS_PER_BOOK; doc_insert(doc, "<style:table>"); doc_printf(doc, "<color:G> %-20.20s Lvl SP Fail Desc</color>\n", "Name"); for (i = start_idx; i < start_idx + _SPELLS_PER_BOOK; i++) _list_spell(doc, realm, i, i - start_idx, _FROM_BOOK); doc_insert(doc, "</style>"); doc_sync_term(doc, doc_range_all(doc), doc_pos_create(r.x, r.y)); doc_free(doc); }
doc_pos_t doc_insert_doc(doc_ptr dest_doc, doc_ptr src_doc, int indent) { doc_pos_t src_pos = doc_pos_create(0, 0); doc_pos_t dest_pos; if (dest_doc->cursor.x > 0) doc_newline(dest_doc); dest_pos = dest_doc->cursor; while (src_pos.y <= src_doc->cursor.y) { doc_char_ptr src = doc_char(src_doc, src_pos); doc_char_ptr dest; int count = src_doc->width; int i; dest_pos.x += indent; dest = doc_char(dest_doc, dest_pos); if (count > dest_doc->width - dest_pos.x) count = dest_doc->width - dest_pos.x; if (src_pos.y == src_doc->cursor.y && count > src_doc->cursor.x) count = src_doc->cursor.x; for (i = 0; i < count; i++) { dest->a = src->a; dest->c = src->c; dest++; src++; } dest_pos.x = 0; dest_pos.y++; src_pos.x = 0; src_pos.y++; } dest_doc->cursor = dest_pos; return dest_doc->cursor; }
void msg_line_clear(void) { int i; int y = doc_cursor(_msg_line_doc).y; for (i = 0; i <= y; i++) { int row = _msg_line_rect.y + i; int cx = row ? _msg_line_rect.cx : 255; /* Hack */ Term_erase(_msg_line_rect.x, row, cx); } doc_rollback(_msg_line_doc, doc_pos_create(0, 0)); _msg_line_sync_pos = doc_cursor(_msg_line_doc); if (y > 0) { /* Note: We need not redraw the entire map if this proves too slow */ p_ptr->redraw |= PR_MAP; if (_msg_line_rect.x <= 12) p_ptr->redraw |= PR_BASIC | PR_EQUIPPY; } }
static void _doc_process_tag(doc_ptr doc, doc_tag_ptr tag) { if ( tag->type == DOC_TAG_CLOSE_COLOR || tag->type == DOC_TAG_CLOSE_STYLE || tag->type == DOC_TAG_CLOSE_INDENT ) { doc_pop_style(doc); } else if (tag->type == DOC_TAG_COLOR) { assert(tag->arg); if (tag->arg_size == 1) { if (tag->arg[0] == '*') doc_pop_style(doc); else { doc_style_t style = *doc_current_style(doc); /* copy */ switch (tag->arg[0]) { case 'd': style.color = TERM_DARK; break; case 'w': style.color = TERM_WHITE; break; case 's': style.color = TERM_SLATE; break; case 'o': style.color = TERM_ORANGE; break; case 'r': style.color = TERM_RED; break; case 'g': style.color = TERM_GREEN; break; case 'b': style.color = TERM_BLUE; break; case 'u': style.color = TERM_UMBER; break; case 'D': style.color = TERM_L_DARK; break; case 'W': style.color = TERM_L_WHITE; break; case 'v': style.color = TERM_VIOLET; break; case 'y': style.color = TERM_YELLOW; break; case 'R': style.color = TERM_L_RED; break; case 'G': style.color = TERM_L_GREEN; break; case 'B': style.color = TERM_L_BLUE; break; case 'U': style.color = TERM_L_UMBER; break; } doc_push_style(doc, &style); } } else { string_ptr arg = string_copy_sn(tag->arg, tag->arg_size); doc_style_t style = *doc_current_style(doc); /* copy */ doc_style_f f = _get_doc_style_f(doc, string_buffer(arg)); if (f) f(&style); {/* We don't copy the named style, just its color. */ /* Also, we damn well better push a style or we'll be upset when the good little user pops! */ doc_style_t copy = *doc_current_style(doc); copy.color = style.color; doc_push_style(doc, ©); } string_free(arg); } } else if (tag->type == DOC_TAG_INDENT) { doc_style_t style = *doc_current_style(doc); style.left = doc->cursor.x; doc_push_style(doc, &style); } else { string_ptr arg = string_copy_sn(tag->arg, tag->arg_size); switch (tag->type) { case DOC_TAG_STYLE: if (tag->arg_size == 1 && tag->arg[0] == '*') doc_pop_style(doc); else {/* Better silently add one if name doesn't exist ... */ doc_style_t copy = *doc_current_style(doc); doc_style_f f = _get_doc_style_f(doc, string_buffer(arg)); if (f) f(©); doc_push_style(doc, ©); } break; case DOC_TAG_VAR: _doc_process_var(doc, string_buffer(arg)); break; case DOC_TAG_TAB: { int pos = atoi(string_buffer(arg)) + doc_current_style(doc)->left; if (pos > doc->cursor.x) doc_insert_space(doc, pos - doc->cursor.x); else doc_rollback(doc, doc_pos_create(pos, doc->cursor.y)); break; } case DOC_TAG_TOPIC: { doc_bookmark_ptr mark = malloc(sizeof(doc_bookmark_t)); mark->name = arg; /* steal ownership */ arg = NULL; mark->pos = doc->cursor; vec_add(doc->bookmarks, mark); break; } case DOC_TAG_LINK: { doc_link_ptr link = malloc(sizeof(doc_link_t)); int split = string_chr(arg, 0, '#'); int ch = 'a' + int_map_count(doc->links); if (split >= 0) { substring_t left = string_left(arg, split); substring_t right = string_right(arg, string_length(arg) - split - 1); link->file = substring_copy(&left); link->topic = substring_copy(&right); } else { link->file = arg; /* steal ownership */ arg = NULL; link->topic = NULL; } link->location.start = doc->cursor; int_map_add(doc->links, ch, link); { /* TODO: This is flawed. Here's a real world example: "(see below <link:birth.txt#PrimaryStats>)." Can you see the problem? We might line break after "[a]" right before ").". Instead, "[a])." should be treated as the current word. To fix this, we'll need a parser with a token queue that we can push onto, but this raises storage issues. */ string_ptr s = string_alloc_format("<style:link>[%c]</style>", ch); doc_insert(doc, string_buffer(s)); string_free(s); link->location.stop = doc->cursor; } break; } } string_free(arg); } }
doc_pos_t doc_pos_invalid(void) { return doc_pos_create(-1, -1); }
int doc_display_aux(doc_ptr doc, cptr caption, int top, rect_t display) { int rc = _OK; int i; char finder_str[81]; char back_str[81]; int page_size; bool done = FALSE; strcpy(finder_str, ""); page_size = display.cy - 4; if (top < 0) top = 0; if (top > doc->cursor.y - page_size) top = MAX(0, doc->cursor.y - page_size); for (i = 0; i < display.cy; i++) Term_erase(display.x, display.y + i, display.cx); while (!done) { int cmd; Term_erase(display.x, display.y, display.cx); put_str(format("[%s, Line %d/%d]", caption, top, doc->cursor.y), display.y, display.x); doc_sync_term(doc, doc_region_create(0, top, doc->width, top + page_size - 1), doc_pos_create(display.x, display.y + 2)); Term_erase(display.x, display.y + display.cy - 1, display.cx); put_str("[Press ESC to exit. Press ? for help]", display.y + display.cy - 1, display.x); cmd = inkey_special(TRUE); if ('a' <= cmd && cmd <= 'z') { doc_link_ptr link = int_map_find(doc->links, cmd); if (link) { rc = doc_display_help_aux(string_buffer(link->file), string_buffer(link->topic), display); if (rc == _UNWIND) done = TRUE; continue; } } switch (cmd) { case '?': if (!strstr(caption, "helpinfo.txt")) { rc = doc_display_help_aux("helpinfo.txt", NULL, display); if (rc == _UNWIND) done = TRUE; } break; case ESCAPE: done = TRUE; break; case 'q': done = TRUE; rc = _UNWIND; break; case SKEY_TOP: case '7': top = 0; break; case SKEY_BOTTOM: case '1': top = MAX(0, doc->cursor.y - page_size); break; case SKEY_PGUP: case '9': top -= page_size; if (top < 0) top = 0; break; case SKEY_PGDOWN: case '3': top += page_size; if (top > doc->cursor.y - page_size) top = MAX(0, doc->cursor.y - page_size); break; case SKEY_UP: case '8': top--; if (top < 0) top = 0; break; case SKEY_DOWN: case '2': top++; if (top > doc->cursor.y - page_size) top = MAX(0, doc->cursor.y - page_size); break; case '>': { doc_pos_t pos = doc_next_bookmark(doc, doc_pos_create(doc->width - 1, top)); if (doc_pos_is_valid(pos)) { top = pos.y; if (top > doc->cursor.y - page_size) top = MAX(0, doc->cursor.y - page_size); } break; } case '<': { doc_pos_t pos = doc_prev_bookmark(doc, doc_pos_create(0, top)); if (doc_pos_is_valid(pos)) top = pos.y; else top = 0; break; } case '|': { FILE *fp2; char buf[1024]; char name[82]; int cb; int format = DOC_FORMAT_TEXT; strcpy(name, string_buffer(doc->name)); if (!get_string("File name: ", name, 80)) break; path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name); fp2 = my_fopen(buf, "w"); if (!fp2) { msg_format("Failed to open file: %s", buf); break; } cb = strlen(buf); if (cb > 5 && strcmp(buf + cb - 5, ".html") == 0) format = DOC_FORMAT_HTML; else if (cb > 4 && strcmp(buf + cb - 4, ".htm") == 0) format = DOC_FORMAT_HTML; doc_write_file(doc, fp2, format); my_fclose(fp2); msg_format("Created file: %s", buf); msg_print(NULL); break; } case '/': Term_erase(display.x, display.y + display.cy - 1, display.cx); put_str("Find: ", display.y + display.cy - 1, display.x); strcpy(back_str, finder_str); if (askfor(finder_str, 80)) { if (finder_str[0]) { doc_pos_t pos = doc->selection.stop; if (!doc_pos_is_valid(pos)) pos = doc_pos_create(0, top); pos = doc_find_next(doc, finder_str, pos); if (doc_pos_is_valid(pos)) { top = pos.y; if (top > doc->cursor.y - page_size) top = MAX(0, doc->cursor.y - page_size); } } } else strcpy(finder_str, back_str); break; case '\\': Term_erase(display.x, display.y + display.cy - 1, display.cx); put_str("Find: ", display.y + display.cy - 1, display.x); strcpy(back_str, finder_str); if (askfor(finder_str, 80)) { if (finder_str[0]) { doc_pos_t pos = doc->selection.start; if (!doc_pos_is_valid(pos)) pos = doc_pos_create(doc->width, top + page_size); pos = doc_find_prev(doc, finder_str, pos); if (doc_pos_is_valid(pos)) { top = pos.y; if (top > doc->cursor.y - page_size) top = MAX(0, doc->cursor.y - page_size); } } } else strcpy(finder_str, back_str); break; default: { /* BETA: Any unhandled keystroke will navigate to the next topic based upon a comparison of the first letter. This is nice, say, for viewing the Character Sheet and navigating to the various sections */ doc_pos_t pos = doc_next_bookmark_char(doc, doc_pos_create(1, top), cmd); if (!doc_pos_is_valid(pos)) /* wrap */ pos = doc_next_bookmark_char(doc, doc_pos_create(0, 0), cmd); if (doc_pos_is_valid(pos)) { top = pos.y; if (top > doc->cursor.y - page_size) top = MAX(0, doc->cursor.y - page_size); } } } } return rc; }
void doc_clear(doc_ptr doc) { doc_rollback(doc, doc_pos_create(0, 0)); }
doc_pos_t doc_insert_cols(doc_ptr dest_doc, doc_ptr src_cols[], int col_count, int spacing) { int src_y = 0; int max_src_y = 0; doc_pos_t dest_pos; int i; if (dest_doc->cursor.x > 0) doc_newline(dest_doc); dest_pos = dest_doc->cursor; for (i = 0; i < col_count; i++) { doc_ptr src_col = src_cols[i]; max_src_y = MAX(src_col->cursor.y, max_src_y); } while (src_y <= max_src_y) { for (i = 0; i < col_count; i++) { doc_ptr src_col = src_cols[i]; int count = src_col->width; if (count > dest_doc->width - dest_pos.x) count = dest_doc->width - dest_pos.x; if (src_y <= src_col->cursor.y && count > 0) { doc_char_ptr dest = doc_char(dest_doc, dest_pos); doc_char_ptr src = doc_char(src_col, doc_pos_create(0, src_y)); int j; for (j = 0; j < count; j++) { /* Hack: Attempt to prevent trailing spaces. For some reason, this is causing oook to not display character dumps properly from Windows builds. Interestingly, oook displays files correctly from Linux even without this hack, which is puzzling since the files seem identical on both platforms.*/ if (i == col_count - 1 && !src->c) { count = j; break; } dest->a = src->a; dest->c = src->c; if (!dest->c) dest->c = ' '; dest++; src++; } } dest_pos.x += count; if (i == col_count - 1) break; /* Spacing between columns */ count = spacing; if (count > dest_doc->width - dest_pos.x) count = dest_doc->width - dest_pos.x; if (count > 0) { doc_char_ptr dest = doc_char(dest_doc, dest_pos); int j; for (j = 0; j < count; j++) { dest->a = TERM_WHITE; dest->c = ' '; dest++; } } dest_pos.x += count; } dest_pos.x = 0; dest_pos.y++; src_y++; } dest_doc->cursor = dest_pos; return dest_doc->cursor; }