OSDN Git Service

[Fix] ヘルプ表示時に検索文字列が強調表示されない不具合を修正
[hengband/hengband.git] / src / core / show-file.c
1 #include "core/show-file.h"
2 #include "core/asking-player.h"
3 #include "io/files-util.h"
4 #include "io/input-key-acceptor.h"
5 #include "main/sound-of-music.h"
6 #include "system/angband-version.h"
7 #include "term/gameterm.h"
8 #include "term/screen-processor.h"
9 #include "term/term-color-types.h"
10 #include "util/angband-files.h"
11 #include "util/int-char-converter.h"
12 #include "util/string-processor.h"
13 #include "view/display-messages.h"
14
15 /*!
16  * todo 表示とそれ以外を分割する
17  * @brief ファイル内容の一行をコンソールに出力する
18  * Display single line of on-line help file
19  * @param str 出力する文字列
20  * @param cy コンソールの行
21  * @param shower 確認中
22  * @return なし
23  * @details
24  * <pre>
25  * You can insert some special color tag to change text color.
26  * Such as...
27  * WHITETEXT [[[[y|SOME TEXT WHICH IS DISPLAYED IN YELLOW| WHITETEXT
28  * A colored segment is between "[[[[y|" and the last "|".
29  * You can use any single character in place of the "|".
30  * </pre>
31  */
32 static void show_file_aux_line(concptr str, int cy, concptr shower)
33 {
34     char lcstr[1024];
35     if (shower) {
36         strcpy(lcstr, str);
37         str_tolower(lcstr);
38     }
39
40     int cx = 0;
41     term_gotoxy(cx, cy);
42
43     static const char tag_str[] = "[[[[";
44     byte color = TERM_WHITE;
45     char in_tag = '\0';
46     for (int i = 0; str[i];) {
47         int len = strlen(&str[i]);
48         int showercol = len + 1;
49         int bracketcol = len + 1;
50         int endcol = len;
51         concptr ptr;
52         if (shower) {
53             ptr = angband_strstr(&lcstr[i], shower);
54             if (ptr)
55                 showercol = ptr - &lcstr[i];
56         }
57
58         ptr = in_tag ? angband_strchr(&str[i], in_tag) : angband_strstr(&str[i], tag_str);
59         if (ptr)
60             bracketcol = ptr - &str[i];
61         if (bracketcol < endcol)
62             endcol = bracketcol;
63         if (showercol < endcol)
64             endcol = showercol;
65
66         term_addstr(endcol, color, &str[i]);
67         cx += endcol;
68         i += endcol;
69
70         if (shower && endcol == showercol) {
71             int showerlen = strlen(shower);
72             term_addstr(showerlen, TERM_YELLOW, &str[i]);
73             cx += showerlen;
74             i += showerlen;
75             continue;
76         }
77
78         if (endcol != bracketcol)
79             continue;
80
81         if (in_tag) {
82             i++;
83             in_tag = '\0';
84             color = TERM_WHITE;
85             continue;
86         }
87
88         i += sizeof(tag_str) - 1;
89         color = color_char_to_attr(str[i]);
90         if (color == 255 || str[i + 1] == '\0') {
91             color = TERM_WHITE;
92             term_addstr(-1, TERM_WHITE, tag_str);
93             cx += sizeof(tag_str) - 1;
94             continue;
95         }
96
97         i++;
98         in_tag = str[i];
99         i++;
100     }
101
102     term_erase(cx, cy, 255);
103 }
104
105 /*!
106  * todo 表示とそれ以外を分割する
107  * @brief ファイル内容をコンソールに出力する
108  * Recursive file perusal.
109  * @param creature_ptr プレーヤーへの参照ポインタ
110  * @param show_version TRUEならばコンソール上にゲームのバージョンを表示する
111  * @param name ファイル名の文字列
112  * @param what 内容キャプションの文字列
113  * @param line 表示の現在行
114  * @param mode オプション
115  * @return なし
116  * @details
117  * <pre>
118  * Process various special text in the input file, including
119  * the "menu" structures used by the "help file" system.
120  * Return FALSE on 'q' to exit from a deep, otherwise TRUE.
121  * </pre>
122  */
123 bool show_file(player_type *creature_ptr, bool show_version, concptr name, concptr what, int line, BIT_FLAGS mode)
124 {
125     int wid, hgt;
126     term_get_size(&wid, &hgt);
127
128     char finder_str[81];
129     strcpy(finder_str, "");
130
131     char shower_str[81];
132     strcpy(shower_str, "");
133
134     char caption[128];
135     strcpy(caption, "");
136
137     char hook[68][32];
138     for (int i = 0; i < 68; i++) {
139         hook[i][0] = '\0';
140     }
141
142     char filename[1024];
143     strcpy(filename, name);
144     int n = strlen(filename);
145
146     concptr tag = NULL;
147     for (int i = 0; i < n; i++) {
148         if (filename[i] == '#') {
149             filename[i] = '\0';
150             tag = filename + i + 1;
151             break;
152         }
153     }
154
155     name = filename;
156     FILE *fff = NULL;
157     char path[1024];
158     if (what) {
159         strcpy(caption, what);
160         strcpy(path, name);
161         fff = angband_fopen(path, "r");
162     }
163
164     if (!fff) {
165         sprintf(caption, _("ヘルプ・ファイル'%s'", "Help file '%s'"), name);
166         path_build(path, sizeof(path), ANGBAND_DIR_HELP, name);
167         fff = angband_fopen(path, "r");
168     }
169
170     if (!fff) {
171         sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
172         path_build(path, sizeof(path), ANGBAND_DIR_INFO, name);
173         fff = angband_fopen(path, "r");
174     }
175
176     if (!fff) {
177         path_build(path, sizeof(path), ANGBAND_DIR, name);
178
179         for (int i = 0; path[i]; i++)
180             if ('\\' == path[i])
181                 path[i] = PATH_SEP[0];
182
183         sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
184         fff = angband_fopen(path, "r");
185     }
186
187     if (!fff) {
188         msg_format(_("'%s'をオープンできません。", "Cannot open '%s'."), name);
189         msg_print(NULL);
190
191         return TRUE;
192     }
193
194     int skey;
195     int next = 0;
196     int size = 0;
197     int back = 0;
198     bool menu = FALSE;
199     char buf[1024];
200     bool reverse = (line < 0);
201     while (TRUE) {
202         char *str = buf;
203         if (angband_fgets(fff, buf, sizeof(buf)))
204             break;
205         if (!prefix(str, "***** ")) {
206             next++;
207             continue;
208         }
209
210         if ((str[6] == '[') && isalpha(str[7])) {
211             int k = str[7] - 'A';
212             menu = TRUE;
213             if ((str[8] == ']') && (str[9] == ' ')) {
214                 strncpy(hook[k], str + 10, 31);
215                 hook[k][31] = '\0';
216             }
217
218             continue;
219         }
220
221         if (str[6] != '<')
222             continue;
223
224         size_t len = strlen(str);
225         if (str[len - 1] == '>') {
226             str[len - 1] = '\0';
227             if (tag && streq(str + 7, tag))
228                 line = next;
229         }
230     }
231
232     size = next;
233     int rows = hgt - 4;
234     if (line == -1)
235         line = ((size - 1) / rows) * rows;
236
237     term_clear();
238
239     concptr find = NULL;
240     concptr shower = NULL;
241     while (TRUE) {
242         if (line >= size - rows)
243             line = size - rows;
244         if (line < 0)
245             line = 0;
246
247         if (next > line) {
248             angband_fclose(fff);
249             fff = angband_fopen(path, "r");
250             if (!fff)
251                 return FALSE;
252
253             next = 0;
254         }
255
256         while (next < line) {
257             if (angband_fgets(fff, buf, sizeof(buf)))
258                 break;
259             if (prefix(buf, "***** "))
260                 continue;
261             next++;
262         }
263
264         int row_count;
265         for (row_count = 0; row_count < rows;) {
266             concptr str = buf;
267             if (!row_count)
268                 line = next;
269             if (angband_fgets(fff, buf, sizeof(buf)))
270                 break;
271             if (prefix(buf, "***** "))
272                 continue;
273             next++;
274             if (find && !row_count) {
275                 char lc_buf[1024];
276                 strcpy(lc_buf, str);
277                 str_tolower(lc_buf);
278                 if (!angband_strstr(lc_buf, find))
279                     continue;
280             }
281
282             find = NULL;
283             show_file_aux_line(str, row_count + 2, shower);
284             row_count++;
285         }
286
287         while (row_count < rows) {
288             term_erase(0, row_count + 2, 255);
289             row_count++;
290         }
291
292         if (find) {
293             bell();
294             line = back;
295             find = NULL;
296             continue;
297         }
298
299         prt(format(_("[変愚蛮怒 %d.%d.%d, %s, %d/%d]", "[Hengband %d.%d.%d, %s, Line %d/%d]"), FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH, caption,
300                 line, size),
301             0, 0);
302
303         if (show_version) {
304             char title[127];
305             put_version(title);
306             prt(format("[%s]", title), 0, 0);
307         } else {
308             prt(format(_("[%s, %d/%d]", "[%s, Line %d/%d]"), caption, line, size), 0, 0);
309         }
310
311         if (size <= rows) {
312             prt(_("[キー:(?)ヘルプ (ESC)終了]", "[Press ESC to exit.]"), hgt - 1, 0);
313         } else {
314 #ifdef JP
315             if (reverse)
316                 prt("[キー:(RET/スペース)↑ (-)↓ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
317             else
318                 prt("[キー:(RET/スペース)↓ (-)↑ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
319 #else
320             prt("[Press Return, Space, -, =, /, |, or ESC to exit.]", hgt - 1, 0);
321 #endif
322         }
323
324         skey = inkey_special(TRUE);
325         switch (skey) {
326         case '?':
327             if (strcmp(name, _("jhelpinfo.txt", "helpinfo.txt")) != 0)
328                 show_file(creature_ptr, TRUE, _("jhelpinfo.txt", "helpinfo.txt"), NULL, 0, mode);
329             break;
330         case '=':
331             prt(_("強調: ", "Show: "), hgt - 1, 0);
332
333             char back_str[81];
334             strcpy(back_str, shower_str);
335             if (askfor(shower_str, 80)) {
336                 if (shower_str[0]) {
337                     str_tolower(shower_str);
338                     shower = shower_str;
339                 } else
340                     shower = NULL;
341             } else
342                 strcpy(shower_str, back_str);
343             break;
344
345         case '/':
346         case KTRL('s'):
347             prt(_("検索: ", "Find: "), hgt - 1, 0);
348             strcpy(back_str, finder_str);
349             if (askfor(finder_str, 80)) {
350                 if (finder_str[0]) {
351                     find = finder_str;
352                     back = line;
353                     line = line + 1;
354                     str_tolower(finder_str);
355                     shower = finder_str;
356                 } else
357                     shower = NULL;
358             } else
359                 strcpy(finder_str, back_str);
360             break;
361
362         case '#': {
363             char tmp[81];
364             prt(_("行: ", "Goto Line: "), hgt - 1, 0);
365             strcpy(tmp, "0");
366
367             if (askfor(tmp, 80))
368                 line = atoi(tmp);
369             break;
370         }
371
372         case SKEY_TOP:
373             line = 0;
374             break;
375
376         case SKEY_BOTTOM:
377             line = ((size - 1) / rows) * rows;
378             break;
379
380         case '%': {
381             char tmp[81];
382             prt(_("ファイル・ネーム: ", "Goto File: "), hgt - 1, 0);
383             strcpy(tmp, _("jhelp.hlp", "help.hlp"));
384
385             if (askfor(tmp, 80)) {
386                 if (!show_file(creature_ptr, TRUE, tmp, NULL, 0, mode))
387                     skey = 'q';
388             }
389
390             break;
391         }
392
393         case '-':
394             line = line + (reverse ? rows : -rows);
395             if (line < 0)
396                 line = 0;
397             break;
398
399         case SKEY_PGUP:
400             line = line - rows;
401             if (line < 0)
402                 line = 0;
403             break;
404
405         case '\n':
406         case '\r':
407             line = line + (reverse ? -1 : 1);
408             if (line < 0)
409                 line = 0;
410             break;
411
412         case '8':
413         case SKEY_UP:
414             line--;
415             if (line < 0)
416                 line = 0;
417             break;
418
419         case '2':
420         case SKEY_DOWN:
421             line++;
422             break;
423
424         case ' ':
425             line = line + (reverse ? -rows : rows);
426             if (line < 0)
427                 line = 0;
428             break;
429
430         case SKEY_PGDOWN:
431             line = line + rows;
432             break;
433         }
434
435         if (menu) {
436             int key = -1;
437             if (!(skey & SKEY_MASK) && isalpha(skey))
438                 key = skey - 'A';
439
440             if ((key > -1) && hook[key][0]) {
441                 /* Recurse on that file */
442                 if (!show_file(creature_ptr, TRUE, hook[key], NULL, 0, mode))
443                     skey = 'q';
444             }
445         }
446
447         if (skey == '|') {
448             FILE *ffp;
449             char buff[1024];
450             char xtmp[82];
451
452             strcpy(xtmp, "");
453
454             if (!get_string(_("ファイル名: ", "File name: "), xtmp, 80))
455                 continue;
456             angband_fclose(fff);
457             path_build(buff, sizeof(buff), ANGBAND_DIR_USER, xtmp);
458
459             /* Hack -- Re-Open the file */
460             fff = angband_fopen(path, "r");
461
462             ffp = angband_fopen(buff, "w");
463
464             if (!(fff && ffp)) {
465                 msg_print(_("ファイルを開けません。", "Failed to open file."));
466                 skey = ESCAPE;
467                 break;
468             }
469
470             sprintf(xtmp, "%s: %s", creature_ptr->name, what ? what : caption);
471             angband_fputs(ffp, xtmp, 80);
472             angband_fputs(ffp, "\n", 80);
473
474             while (!angband_fgets(fff, buff, sizeof(buff)))
475                 angband_fputs(ffp, buff, 80);
476             angband_fclose(fff);
477             angband_fclose(ffp);
478             fff = angband_fopen(path, "r");
479         }
480
481         if ((skey == ESCAPE) || (skey == '<'))
482             break;
483
484         if (skey == KTRL('q'))
485             skey = 'q';
486
487         if (skey == 'q')
488             break;
489     }
490
491     angband_fclose(fff);
492     return (skey != 'q');
493 }
494
495 /*
496  * Convert string to lower case
497  */
498 void str_tolower(char *str)
499 {
500     for (; *str; str++) {
501 #ifdef JP
502         if (iskanji(*str)) {
503             str++;
504             continue;
505         }
506 #endif
507         *str = (char)tolower(*str);
508     }
509 }