OSDN Git Service

[Refactor] #39964 Separated autopick-pref-processor.c/h from autopick.c/h
[hengband/hengband.git] / src / autopick / autopick.c
1 /*!
2  * @file autopick.c
3  * @brief 自動拾い機能の実装 / Object Auto-picker/Destroyer
4  * @date 2014/01/02
5  * @author
6  * Copyright (c) 2002  Mogami\n
7  *\n
8  * This software may be copied and distributed for educational, research, and\n
9  * not for profit purposes provided that this copyright and statement are\n
10  * included in all such copies.\n
11  * 2014 Deskull rearranged comment for Doxygen.\n
12  */
13
14 #include "angband.h"
15 #include "util.h"
16 #include "autopick/autopick-commands-table.h"
17 #include "autopick/autopick-dirty-flags.h"
18 #include "autopick/autopick-flags-table.h"
19 #include "autopick/autopick-initializer.h"
20 #include "autopick/autopick-key-flag-process.h"
21 #include "autopick/autopick-matcher.h"
22 #include "autopick/autopick-menu-data-table.h"
23 #include "autopick/autopick-methods-table.h"
24 #include "autopick/autopick-keys-table.h"
25 #include "autopick/autopick-destroyer.h"
26 #include "autopick/autopick-describer.h"
27 #include "autopick/autopick-entry.h"
28 #include "autopick/autopick-reader-writer.h"
29 #include "autopick/autopick-finder.h"
30 #include "autopick/autopick-adder.h"
31 #include "autopick/autopick-pref-processor.h"
32 #include "gameterm.h"
33 #include "autopick/autopick.h"
34 #include "core.h"
35 #include "core/show-file.h"
36 #include "cmd/cmd-save.h"
37 #include "io/read-pref-file.h"
38
39 #include "mind.h"
40
41 #include "market/store.h"
42 #include "player-status.h"
43 #include "player-move.h"
44 #include "player-class.h"
45 #include "player-race.h"
46 #include "player-inventory.h"
47 #include "view/display-player.h" // 暫定。後で消す.
48 #include "object/object-kind.h"
49 #include "object-ego.h"
50 #include "object-flavor.h"
51 #include "object-hook.h"
52
53 #include "floor.h"
54 #include "world.h"
55 #include "monster.h"
56 #include "monsterrace.h"
57 #include "view/display-main-window.h" // 暫定。後で消す.
58
59 /*
60  * Auto inscription
61  */
62 static void auto_inscribe_item(player_type *player_ptr, object_type *o_ptr, int idx)
63 {
64         if (idx < 0 || !autopick_list[idx].insc) return;
65
66         if (!o_ptr->inscription)
67                 o_ptr->inscription = quark_add(autopick_list[idx].insc);
68
69         player_ptr->window |= (PW_EQUIP | PW_INVEN);
70         player_ptr->update |= (PU_BONUS);
71 }
72
73
74 /*
75  *  Auto-destroy marked item
76  */
77 static void autopick_delayed_alter_aux(player_type *player_ptr, INVENTORY_IDX item)
78 {
79         object_type *o_ptr;
80         o_ptr = REF_ITEM(player_ptr, player_ptr->current_floor_ptr, item);
81
82         if (o_ptr->k_idx == 0 || !(o_ptr->marked & OM_AUTODESTROY)) return;
83
84         GAME_TEXT o_name[MAX_NLEN];
85         object_desc(player_ptr, o_name, o_ptr, 0);
86         if (item >= 0)
87         {
88                 inven_item_increase(player_ptr, item, -(o_ptr->number));
89                 inven_item_optimize(player_ptr, item);
90         }
91         else
92         {
93                 delete_object_idx(player_ptr, 0 - item);
94         }
95
96         msg_format(_("%sを自動破壊します。", "Auto-destroying %s."), o_name);
97 }
98
99
100 /*
101  *  Auto-destroy marked items in inventry and on floor
102  */
103 void autopick_delayed_alter(player_type *owner_ptr)
104 {
105         INVENTORY_IDX item;
106
107         /*
108          * Scan inventry in reverse order to prevent
109          * skipping after inven_item_optimize()
110          */
111         for (item = INVEN_TOTAL - 1; item >= 0; item--)
112                 autopick_delayed_alter_aux(owner_ptr, item);
113
114         floor_type *floor_ptr = owner_ptr->current_floor_ptr;
115         item = floor_ptr->grid_array[owner_ptr->y][owner_ptr->x].o_idx;
116         while (item)
117         {
118                 OBJECT_IDX next = floor_ptr->o_list[item].next_o_idx;
119                 autopick_delayed_alter_aux(owner_ptr, -item);
120                 item = next;
121         }
122 }
123
124
125 /*
126  * Auto-inscription and/or destroy
127  *
128  * Auto-destroyer works only on inventory or on floor stack only when
129  * requested.
130  */
131 void autopick_alter_item(player_type *player_ptr, INVENTORY_IDX item, bool destroy)
132 {
133         object_type *o_ptr;
134         o_ptr = REF_ITEM(player_ptr, player_ptr->current_floor_ptr, item);
135         int idx = find_autopick_list(player_ptr, o_ptr);
136         auto_inscribe_item(player_ptr, o_ptr, idx);
137         if (destroy && item <= INVEN_PACK)
138                 auto_destroy_item(player_ptr, o_ptr, idx);
139 }
140
141
142 /*
143  * Automatically pickup/destroy items in this grid.
144  */
145 void autopick_pickup_items(player_type* player_ptr, grid_type *g_ptr)
146 {
147         OBJECT_IDX this_o_idx, next_o_idx = 0;
148         for (this_o_idx = g_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
149         {
150                 object_type *o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
151                 next_o_idx = o_ptr->next_o_idx;
152                 int idx = find_autopick_list(player_ptr, o_ptr);
153                 auto_inscribe_item(player_ptr, o_ptr, idx);
154                 bool is_auto_pickup = idx >= 0;
155                 is_auto_pickup &= (autopick_list[idx].action & (DO_AUTOPICK | DO_QUERY_AUTOPICK)) != 0;
156                 if (!is_auto_pickup)
157                 {
158                         auto_destroy_item(player_ptr, o_ptr, idx);
159                         continue;
160                 }
161
162                 disturb(player_ptr, FALSE, FALSE);
163                 if (!inven_carry_okay(o_ptr))
164                 {
165                         GAME_TEXT o_name[MAX_NLEN];
166                         object_desc(player_ptr, o_name, o_ptr, 0);
167                         msg_format(_("ザックには%sを入れる隙間がない。", "You have no room for %s."), o_name);
168                         o_ptr->marked |= OM_NOMSG;
169                         continue;
170                 }
171
172                 if (!(autopick_list[idx].action & DO_QUERY_AUTOPICK))
173                 {
174                         py_pickup_aux(player_ptr, this_o_idx);
175                         continue;
176                 }
177
178                 char out_val[MAX_NLEN + 20];
179                 GAME_TEXT o_name[MAX_NLEN];
180                 if (o_ptr->marked & OM_NO_QUERY)
181                 {
182                         continue;
183                 }
184
185                 object_desc(player_ptr, o_name, o_ptr, 0);
186                 sprintf(out_val, _("%sを拾いますか? ", "Pick up %s? "), o_name);
187                 if (!get_check(out_val))
188                 {
189                         o_ptr->marked |= OM_NOMSG | OM_NO_QUERY;
190                         continue;
191                 }
192
193                 py_pickup_aux(player_ptr, this_o_idx);
194         }
195 }
196
197
198 static const char autoregister_header[] = "?:$AUTOREGISTER";
199
200 /*
201  *  Clear auto registered lines in the picktype.prf .
202  */
203 static bool clear_auto_register(player_type *player_ptr)
204 {
205         char pref_file[1024];
206         path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_WITH_PNAME));
207         FILE *pref_fff;
208         pref_fff = my_fopen(pref_file, "r");
209
210         if (!pref_fff)
211         {
212                 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_DEFAULT));
213                 pref_fff = my_fopen(pref_file, "r");
214         }
215
216         if (!pref_fff)
217         {
218                 return TRUE;
219         }
220
221         char tmp_file[1024];
222         FILE *tmp_fff;
223         tmp_fff = my_fopen_temp(tmp_file, sizeof(tmp_file));
224         if (!tmp_fff)
225         {
226                 fclose(pref_fff);
227                 msg_format(_("一時ファイル %s を作成できませんでした。", "Failed to create temporary file %s."), tmp_file);
228                 msg_print(NULL);
229                 return FALSE;
230         }
231
232         bool autoregister = FALSE;
233         int num = 0;
234         char buf[1024];
235         while (TRUE)
236         {
237                 if (my_fgets(pref_fff, buf, sizeof(buf))) break;
238
239                 if (autoregister)
240                 {
241                         if (buf[0] != '#' && buf[0] != '?') num++;
242                         continue;
243                 }
244
245                 if (streq(buf, autoregister_header))
246                 {
247                         autoregister = TRUE;
248                 }
249                 else
250                 {
251                         fprintf(tmp_fff, "%s\n", buf);
252                 }
253         }
254
255         my_fclose(pref_fff);
256         my_fclose(tmp_fff);
257
258         bool okay = TRUE;
259         if (num)
260         {
261                 msg_format(_("以前のキャラクター用の自動設定(%d行)が残っています。",
262                         "Auto registered lines (%d lines) for previous character are remaining."), num);
263                 strcpy(buf, _("古い設定行は削除します。よろしいですか?", "These lines will be deleted.  Are you sure? "));
264
265                 if (!get_check(buf))
266                 {
267                         okay = FALSE;
268                         autoregister = FALSE;
269
270                         msg_print(_("エディタのカット&ペースト等を使って必要な行を避難してください。",
271                                 "Use cut & paste of auto picker editor (_) to keep old prefs."));
272                 }
273         }
274
275         if (autoregister)
276         {
277                 tmp_fff = my_fopen(tmp_file, "r");
278                 pref_fff = my_fopen(pref_file, "w");
279
280                 while (!my_fgets(tmp_fff, buf, sizeof(buf)))
281                         fprintf(pref_fff, "%s\n", buf);
282
283                 my_fclose(pref_fff);
284                 my_fclose(tmp_fff);
285         }
286
287         fd_kill(tmp_file);
288         return okay;
289 }
290
291
292 /*
293  *  Automatically register an auto-destroy preference line
294  */
295 bool autopick_autoregister(player_type *player_ptr, object_type *o_ptr)
296 {
297         char buf[1024];
298         char pref_file[1024];
299         FILE *pref_fff;
300         autopick_type an_entry, *entry = &an_entry;
301         int match_autopick = find_autopick_list(player_ptr, o_ptr);
302         if (match_autopick != -1)
303         {
304                 concptr what;
305                 byte act = autopick_list[match_autopick].action;
306                 if (act & DO_AUTOPICK) what = _("自動で拾う", "auto-pickup");
307                 else if (act & DO_AUTODESTROY) what = _("自動破壊する", "auto-destroy");
308                 else if (act & DONT_AUTOPICK) what = _("放置する", "leave on floor");
309                 else what = _("確認して拾う", "query auto-pickup");
310
311                 msg_format(_("そのアイテムは既に%sように設定されています。", "The object is already registered to %s."), what);
312                 return FALSE;
313         }
314
315         if ((object_is_known(o_ptr) && object_is_artifact(o_ptr)) ||
316                 ((o_ptr->ident & IDENT_SENSE) &&
317                 (o_ptr->feeling == FEEL_TERRIBLE || o_ptr->feeling == FEEL_SPECIAL)))
318         {
319                 GAME_TEXT o_name[MAX_NLEN];
320                 object_desc(player_ptr, o_name, o_ptr, 0);
321                 msg_format(_("%sは破壊不能だ。", "You cannot auto-destroy %s."), o_name);
322                 return FALSE;
323         }
324
325         if (!player_ptr->autopick_autoregister)
326         {
327                 if (!clear_auto_register(player_ptr)) return FALSE;
328         }
329
330         path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_WITH_PNAME));
331         pref_fff = my_fopen(pref_file, "r");
332
333         if (!pref_fff)
334         {
335                 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_DEFAULT));
336                 pref_fff = my_fopen(pref_file, "r");
337         }
338
339         if (pref_fff)
340         {
341                 while (TRUE)
342                 {
343                         if (my_fgets(pref_fff, buf, sizeof(buf)))
344                         {
345                                 player_ptr->autopick_autoregister = FALSE;
346                                 break;
347                         }
348
349                         if (streq(buf, autoregister_header))
350                         {
351                                 player_ptr->autopick_autoregister = TRUE;
352                                 break;
353                         }
354                 }
355
356                 fclose(pref_fff);
357         }
358         else
359         {
360                 /*
361                  * File could not be opened for reading.  Assume header not
362                  * present.
363                  */
364                 player_ptr->autopick_autoregister = FALSE;
365         }
366
367         pref_fff = my_fopen(pref_file, "a");
368         if (!pref_fff)
369         {
370                 msg_format(_("%s を開くことができませんでした。", "Failed to open %s."), pref_file);
371                 msg_print(NULL);
372                 return FALSE;
373         }
374
375         if (!player_ptr->autopick_autoregister)
376         {
377                 fprintf(pref_fff, "%s\n", autoregister_header);
378
379                 fprintf(pref_fff, "%s\n", _("# *警告!!* 以降の行は自動登録されたものです。",
380                         "# *Warning!* The lines below will be deleted later."));
381                 fprintf(pref_fff, "%s\n", _("# 後で自動的に削除されますので、必要な行は上の方へ移動しておいてください。",
382                         "# Keep it by cut & paste if you need these lines for future characters."));
383                 player_ptr->autopick_autoregister = TRUE;
384         }
385
386         autopick_entry_from_object(player_ptr, entry, o_ptr);
387         entry->action = DO_AUTODESTROY;
388         add_autopick_list(entry);
389
390         concptr tmp = autopick_line_from_entry(entry);
391         fprintf(pref_fff, "%s\n", tmp);
392         string_free(tmp);
393         fclose(pref_fff);
394         return TRUE;
395 }
396
397
398 /*
399  * Delete or insert string
400  */
401 static void toggle_keyword(text_body_type *tb, BIT_FLAGS flg)
402 {
403         int by1, by2;
404         bool add = TRUE;
405         bool fixed = FALSE;
406         if (tb->mark)
407         {
408                 by1 = MIN(tb->my, tb->cy);
409                 by2 = MAX(tb->my, tb->cy);
410         }
411         else /* if (!tb->mark) */
412         {
413                 by1 = by2 = tb->cy;
414         }
415
416         for (int y = by1; y <= by2; y++)
417         {
418                 autopick_type an_entry, *entry = &an_entry;
419                 if (!autopick_new_entry(entry, tb->lines_list[y], !fixed)) continue;
420
421                 string_free(tb->lines_list[y]);
422                 if (!fixed)
423                 {
424                         if (!IS_FLG(flg)) add = TRUE;
425                         else add = FALSE;
426
427                         fixed = TRUE;
428                 }
429
430                 if (FLG_NOUN_BEGIN <= flg && flg <= FLG_NOUN_END)
431                 {
432                         int i;
433                         for (i = FLG_NOUN_BEGIN; i <= FLG_NOUN_END; i++)
434                                 REM_FLG(i);
435                 }
436                 else if (FLG_UNAWARE <= flg && flg <= FLG_STAR_IDENTIFIED)
437                 {
438                         int i;
439                         for (i = FLG_UNAWARE; i <= FLG_STAR_IDENTIFIED; i++)
440                                 REM_FLG(i);
441                 }
442                 else if (FLG_ARTIFACT <= flg && flg <= FLG_AVERAGE)
443                 {
444                         int i;
445                         for (i = FLG_ARTIFACT; i <= FLG_AVERAGE; i++)
446                                 REM_FLG(i);
447                 }
448                 else if (FLG_RARE <= flg && flg <= FLG_COMMON)
449                 {
450                         int i;
451                         for (i = FLG_RARE; i <= FLG_COMMON; i++)
452                                 REM_FLG(i);
453                 }
454
455                 if (add) ADD_FLG(flg);
456                 else REM_FLG(flg);
457
458                 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
459                 tb->dirty_flags |= DIRTY_ALL;
460                 tb->changed = TRUE;
461         }
462 }
463
464
465 /*
466  * Change command letter
467  */
468 static void toggle_command_letter(text_body_type *tb, byte flg)
469 {
470         autopick_type an_entry;
471         autopick_type *entry = &an_entry;
472         int by1, by2;
473         bool add = TRUE;
474         bool fixed = FALSE;
475         if (tb->mark)
476         {
477                 by1 = MIN(tb->my, tb->cy);
478                 by2 = MAX(tb->my, tb->cy);
479         }
480         else /* if (!tb->mark) */
481         {
482                 by1 = by2 = tb->cy;
483         }
484
485         for (int y = by1; y <= by2; y++)
486         {
487                 int wid = 0;
488
489                 if (!autopick_new_entry(entry, tb->lines_list[y], FALSE)) continue;
490
491                 string_free(tb->lines_list[y]);
492
493                 if (!fixed)
494                 {
495                         if (!(entry->action & flg)) add = TRUE;
496                         else add = FALSE;
497
498                         fixed = TRUE;
499                 }
500
501                 if (entry->action & DONT_AUTOPICK) wid--;
502                 else if (entry->action & DO_AUTODESTROY) wid--;
503                 else if (entry->action & DO_QUERY_AUTOPICK) wid--;
504                 if (!(entry->action & DO_DISPLAY)) wid--;
505
506                 if (flg != DO_DISPLAY)
507                 {
508                         entry->action &= ~(DO_AUTOPICK | DONT_AUTOPICK | DO_AUTODESTROY | DO_QUERY_AUTOPICK);
509                         if (add) entry->action |= flg;
510                         else entry->action |= DO_AUTOPICK;
511                 }
512                 else
513                 {
514                         entry->action &= ~(DO_DISPLAY);
515                         if (add) entry->action |= flg;
516                 }
517
518                 if (tb->cy == y)
519                 {
520                         if (entry->action & DONT_AUTOPICK) wid++;
521                         else if (entry->action & DO_AUTODESTROY) wid++;
522                         else if (entry->action & DO_QUERY_AUTOPICK) wid++;
523                         if (!(entry->action & DO_DISPLAY)) wid++;
524
525                         if (wid > 0) tb->cx++;
526                         if (wid < 0 && tb->cx > 0) tb->cx--;
527                 }
528
529                 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
530                 tb->dirty_flags |= DIRTY_ALL;
531                 tb->changed = TRUE;
532         }
533 }
534
535
536 /*
537  * Delete or insert string
538  */
539 static void add_keyword(text_body_type *tb, BIT_FLAGS flg)
540 {
541         int by1, by2;
542         if (tb->mark)
543         {
544                 by1 = MIN(tb->my, tb->cy);
545                 by2 = MAX(tb->my, tb->cy);
546         }
547         else
548         {
549                 by1 = by2 = tb->cy;
550         }
551
552         for (int y = by1; y <= by2; y++)
553         {
554                 autopick_type an_entry, *entry = &an_entry;
555                 if (!autopick_new_entry(entry, tb->lines_list[y], FALSE)) continue;
556
557                 if (IS_FLG(flg))
558                 {
559                         autopick_free_entry(entry);
560                         continue;
561                 }
562
563                 string_free(tb->lines_list[y]);
564                 if (FLG_NOUN_BEGIN <= flg && flg <= FLG_NOUN_END)
565                 {
566                         int i;
567                         for (i = FLG_NOUN_BEGIN; i <= FLG_NOUN_END; i++)
568                                 REM_FLG(i);
569                 }
570
571                 ADD_FLG(flg);
572                 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
573                 tb->dirty_flags |= DIRTY_ALL;
574                 tb->changed = TRUE;
575         }
576 }
577
578
579 /*
580  * Check if this line is expression or not.
581  * And update it if it is.
582  */
583 static void check_expression_line(text_body_type *tb, int y)
584 {
585         concptr s = tb->lines_list[y];
586
587         if ((s[0] == '?' && s[1] == ':') ||
588                 (tb->states[y] & LSTAT_BYPASS))
589         {
590                 tb->dirty_flags |= DIRTY_EXPRESSION;
591         }
592 }
593
594
595 /*
596  * Add an empty line at the last of the file
597  */
598 static bool add_empty_line(text_body_type *tb)
599 {
600         int num_lines;
601         for (num_lines = 0; tb->lines_list[num_lines]; num_lines++);
602
603         if (num_lines >= MAX_LINES - 2) return FALSE;
604         if (!tb->lines_list[num_lines - 1][0]) return FALSE;
605
606         tb->lines_list[num_lines] = string_make("");
607         tb->dirty_flags |= DIRTY_EXPRESSION;
608         tb->changed = TRUE;
609         return TRUE;
610 }
611
612
613 /*
614  * Insert return code and split the line
615  */
616 static bool insert_return_code(text_body_type *tb)
617 {
618         char buf[MAX_LINELEN];
619         int i, j, num_lines;
620
621         for (num_lines = 0; tb->lines_list[num_lines]; num_lines++);
622
623         if (num_lines >= MAX_LINES - 2) return FALSE;
624         num_lines--;
625
626         for (; tb->cy < num_lines; num_lines--)
627         {
628                 tb->lines_list[num_lines + 1] = tb->lines_list[num_lines];
629                 tb->states[num_lines + 1] = tb->states[num_lines];
630         }
631
632         for (i = j = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
633         {
634 #ifdef JP
635                 if (iskanji(tb->lines_list[tb->cy][i]))
636                         buf[j++] = tb->lines_list[tb->cy][i++];
637 #endif
638                 buf[j++] = tb->lines_list[tb->cy][i];
639         }
640
641         buf[j] = '\0';
642         tb->lines_list[tb->cy + 1] = string_make(&tb->lines_list[tb->cy][i]);
643         string_free(tb->lines_list[tb->cy]);
644         tb->lines_list[tb->cy] = string_make(buf);
645         tb->dirty_flags |= DIRTY_EXPRESSION;
646         tb->changed = TRUE;
647         return TRUE;
648 }
649
650
651 /*
652  * Choose an item for search
653  */
654 static bool get_object_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
655 {
656         concptr q = _("どのアイテムを検索しますか? ", "Enter which item? ");
657         concptr s = _("アイテムを持っていない。", "You have nothing to enter.");
658         object_type *o_ptr;
659         o_ptr = choose_object(player_ptr, NULL, q, s, USE_INVEN | USE_FLOOR | USE_EQUIP, 0);
660         if (!o_ptr) return FALSE;
661
662         *o_handle = o_ptr;
663         string_free(*search_strp);
664         char buf[MAX_NLEN + 20];
665         object_desc(player_ptr, buf, *o_handle, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
666         *search_strp = string_make(format("<%s>", buf));
667         return TRUE;
668 }
669
670
671 /*
672  * Prepare for search by destroyed object
673  */
674 static bool get_destroyed_object_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
675 {
676         if (!autopick_last_destroyed_object.k_idx) return FALSE;
677
678         *o_handle = &autopick_last_destroyed_object;
679         string_free(*search_strp);
680         char buf[MAX_NLEN + 20];
681         object_desc(player_ptr, buf, *o_handle, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
682         *search_strp = string_make(format("<%s>", buf));
683         return TRUE;
684 }
685
686
687 /*
688  * Choose an item or string for search
689  */
690 static byte get_string_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
691 {
692         /*
693          * Text color
694          * TERM_YELLOW : Overwrite mode
695          * TERM_WHITE : Insert mode
696          */
697         byte color = TERM_YELLOW;
698         char buf[MAX_NLEN + 20];
699         const int len = 80;
700         char prompt[] = _("検索(^I:持ち物 ^L:破壊された物): ", "Search key(^I:inven ^L:destroyed): ");
701         int col = sizeof(prompt) - 1;
702         if (*search_strp) strcpy(buf, *search_strp);
703         else buf[0] = '\0';
704
705         if (*o_handle) color = TERM_L_GREEN;
706
707         prt(prompt, 0, 0);
708         int pos = 0;
709         while (TRUE)
710         {
711                 bool back = FALSE;
712                 Term_erase(col, 0, 255);
713                 Term_putstr(col, 0, -1, color, buf);
714                 Term_gotoxy(col + pos, 0);
715
716                 int skey = inkey_special(TRUE);
717                 switch (skey)
718                 {
719                 case SKEY_LEFT:
720                 case KTRL('b'):
721                 {
722                         int i = 0;
723                         color = TERM_WHITE;
724                         if (pos == 0) break;
725
726                         while (TRUE)
727                         {
728                                 int next_pos = i + 1;
729
730 #ifdef JP
731                                 if (iskanji(buf[i])) next_pos++;
732 #endif
733                                 if (next_pos >= pos) break;
734
735                                 i = next_pos;
736                         }
737
738                         pos = i;
739                         break;
740                 }
741
742                 case SKEY_RIGHT:
743                 case KTRL('f'):
744                         color = TERM_WHITE;
745                         if ('\0' == buf[pos]) break;
746
747 #ifdef JP
748                         if (iskanji(buf[pos])) pos += 2;
749                         else pos++;
750 #else
751                         pos++;
752 #endif
753                         break;
754
755                 case ESCAPE:
756                         return 0;
757
758                 case KTRL('r'):
759                         back = TRUE;
760                         /* Fall through */
761
762                 case '\n':
763                 case '\r':
764                 case KTRL('s'):
765                         if (*o_handle) return (back ? -1 : 1);
766                         string_free(*search_strp);
767                         *search_strp = string_make(buf);
768                         *o_handle = NULL;
769                         return (back ? -1 : 1);
770
771                 case KTRL('i'):
772                         return get_object_for_search(player_ptr, o_handle, search_strp);
773
774                 case KTRL('l'):
775                         if (get_destroyed_object_for_search(player_ptr, o_handle, search_strp))
776                                 return 1;
777                         break;
778
779                 case '\010':
780                 {
781                         int i = 0;
782                         color = TERM_WHITE;
783                         if (pos == 0) break;
784
785                         while (TRUE)
786                         {
787                                 int next_pos = i + 1;
788 #ifdef JP
789                                 if (iskanji(buf[i])) next_pos++;
790 #endif
791                                 if (next_pos >= pos) break;
792
793                                 i = next_pos;
794                         }
795
796                         pos = i;
797                 }
798                         /* Fall through */
799
800                 case 0x7F:
801                 case KTRL('d'):
802                 {
803                         int dst, src;
804                         color = TERM_WHITE;
805                         if (buf[pos] == '\0') break;
806
807                         src = pos + 1;
808 #ifdef JP
809                         if (iskanji(buf[pos])) src++;
810 #endif
811                         dst = pos;
812                         while ('\0' != (buf[dst++] = buf[src++]));
813
814                         break;
815                 }
816
817                 default:
818                 {
819                         char tmp[100];
820                         char c;
821                         if (skey & SKEY_MASK) break;
822
823                         c = (char)skey;
824                         if (color != TERM_WHITE)
825                         {
826                                 if (color == TERM_L_GREEN)
827                                 {
828                                         *o_handle = NULL;
829                                         string_free(*search_strp);
830                                         *search_strp = NULL;
831                                 }
832
833                                 buf[0] = '\0';
834                                 color = TERM_WHITE;
835                         }
836
837                         strcpy(tmp, buf + pos);
838 #ifdef JP
839                         if (iskanji(c))
840                         {
841                                 char next;
842                                 inkey_base = TRUE;
843                                 next = inkey();
844
845                                 if (pos + 1 < len)
846                                 {
847                                         buf[pos++] = c;
848                                         buf[pos++] = next;
849                                 }
850                                 else
851                                 {
852                                         bell();
853                                 }
854                         }
855                         else
856 #endif
857                         {
858 #ifdef JP
859                                 if (pos < len && (isprint(c) || iskana(c)))
860 #else
861                                 if (pos < len && isprint(c))
862 #endif
863                                 {
864                                         buf[pos++] = c;
865                                 }
866                                 else
867                                 {
868                                         bell();
869                                 }
870                         }
871
872                         buf[pos] = '\0';
873                         my_strcat(buf, tmp, len + 1);
874
875                         break;
876                 }
877                 }
878
879                 if (*o_handle == NULL || color == TERM_L_GREEN) continue;
880
881                 *o_handle = NULL;
882                 buf[0] = '\0';
883                 string_free(*search_strp);
884                 *search_strp = NULL;
885         }
886 }
887
888
889 /*
890  * Search next line matches for o_ptr
891  */
892 static void search_for_object(player_type *player_ptr, text_body_type *tb, object_type *o_ptr, bool forward)
893 {
894         autopick_type an_entry, *entry = &an_entry;
895         GAME_TEXT o_name[MAX_NLEN];
896         int bypassed_cy = -1;
897         int i = tb->cy;
898         object_desc(player_ptr, o_name, o_ptr, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
899         str_tolower(o_name);
900
901         while (TRUE)
902         {
903                 bool match;
904                 if (forward)
905                 {
906                         if (!tb->lines_list[++i]) break;
907                 }
908                 else
909                 {
910                         if (--i < 0) break;
911                 }
912
913                 if (!autopick_new_entry(entry, tb->lines_list[i], FALSE)) continue;
914
915                 match = is_autopick_match(player_ptr, o_ptr, entry, o_name);
916                 autopick_free_entry(entry);
917                 if (!match)     continue;
918
919                 if (tb->states[i] & LSTAT_BYPASS)
920                 {
921                         if (bypassed_cy == -1) bypassed_cy = i;
922                         continue;
923                 }
924
925                 tb->cx = 0;
926                 tb->cy = i;
927                 if (bypassed_cy != -1)
928                 {
929                         tb->dirty_flags |= DIRTY_SKIP_INACTIVE;
930                 }
931
932                 return;
933         }
934
935         if (bypassed_cy == -1)
936         {
937                 tb->dirty_flags |= DIRTY_NOT_FOUND;
938                 return;
939         }
940
941         tb->cx = 0;
942         tb->cy = bypassed_cy;
943         tb->dirty_flags |= DIRTY_INACTIVE;
944 }
945
946
947 /*
948  * Search next line matches to the string
949  */
950 static void search_for_string(text_body_type *tb, concptr search_str, bool forward)
951 {
952         int bypassed_cy = -1;
953         int bypassed_cx = 0;
954
955         int i = tb->cy;
956         while (TRUE)
957         {
958                 concptr pos;
959                 if (forward)
960                 {
961                         if (!tb->lines_list[++i]) break;
962                 }
963                 else
964                 {
965                         if (--i < 0) break;
966                 }
967
968                 pos = my_strstr(tb->lines_list[i], search_str);
969                 if (!pos) continue;
970
971                 if ((tb->states[i] & LSTAT_BYPASS) &&
972                         !(tb->states[i] & LSTAT_EXPRESSION))
973                 {
974                         if (bypassed_cy == -1)
975                         {
976                                 bypassed_cy = i;
977                                 bypassed_cx = (int)(pos - tb->lines_list[i]);
978                         }
979
980                         continue;
981                 }
982
983                 tb->cx = (int)(pos - tb->lines_list[i]);
984                 tb->cy = i;
985
986                 if (bypassed_cy != -1)
987                 {
988                         tb->dirty_flags |= DIRTY_SKIP_INACTIVE;
989                 }
990
991                 return;
992         }
993
994         if (bypassed_cy == -1)
995         {
996                 tb->dirty_flags |= DIRTY_NOT_FOUND;
997                 return;
998         }
999
1000         tb->cx = bypassed_cx;
1001         tb->cy = bypassed_cy;
1002         tb->dirty_flags |= DIRTY_INACTIVE;
1003 }
1004
1005
1006 /*
1007  * Display the menu, and get a command
1008  */
1009 static int do_command_menu(int level, int start)
1010 {
1011         int max_len = 0;
1012         int col0 = 5 + level * 7;
1013         int row0 = 1 + level * 3;
1014         int menu_id_list[26];
1015         bool redraw = TRUE;
1016         char linestr[MAX_LINELEN];
1017
1018         byte menu_key = 0;
1019         for (int i = start; menu_data[i].level >= level; i++)
1020         {
1021                 /* Ignore lower level sub menus */
1022                 if (menu_data[i].level > level) continue;
1023
1024                 int len = strlen(menu_data[i].name);
1025                 if (len > max_len) max_len = len;
1026
1027                 menu_id_list[menu_key] = i;
1028                 menu_key++;
1029         }
1030
1031         while (menu_key < 26)
1032         {
1033                 menu_id_list[menu_key] = -1;
1034                 menu_key++;
1035         }
1036
1037         int max_menu_wid = max_len + 3 + 3;
1038
1039         /* Prepare box line */
1040         linestr[0] = '\0';
1041         strcat(linestr, "+");
1042         for (int i = 0; i < max_menu_wid + 2; i++)
1043         {
1044                 strcat(linestr, "-");
1045         }
1046
1047         strcat(linestr, "+");
1048
1049         while (TRUE)
1050         {
1051                 if (redraw)
1052                 {
1053                         int row1 = row0 + 1;
1054                         Term_putstr(col0, row0, -1, TERM_WHITE, linestr);
1055
1056                         menu_key = 0;
1057                         for (int i = start; menu_data[i].level >= level; i++)
1058                         {
1059                                 char com_key_str[3];
1060                                 concptr str;
1061                                 if (menu_data[i].level > level) continue;
1062
1063                                 if (menu_data[i].com_id == -1)
1064                                 {
1065                                         strcpy(com_key_str, _("▼", ">"));
1066                                 }
1067                                 else if (menu_data[i].key != -1)
1068                                 {
1069                                         com_key_str[0] = '^';
1070                                         com_key_str[1] = menu_data[i].key + '@';
1071                                         com_key_str[2] = '\0';
1072                                 }
1073                                 else
1074                                 {
1075                                         com_key_str[0] = '\0';
1076                                 }
1077
1078                                 str = format("| %c) %-*s %2s | ", menu_key + 'a', max_len, menu_data[i].name, com_key_str);
1079
1080                                 Term_putstr(col0, row1++, -1, TERM_WHITE, str);
1081
1082                                 menu_key++;
1083                         }
1084
1085                         Term_putstr(col0, row1, -1, TERM_WHITE, linestr);
1086                         redraw = FALSE;
1087                 }
1088
1089                 prt(format(_("(a-%c) コマンド:", "(a-%c) Command:"), menu_key + 'a' - 1), 0, 0);
1090                 char key = inkey();
1091
1092                 if (key == ESCAPE) return 0;
1093
1094                 int com_id;
1095                 bool is_alphabet = key >= 'a' && key <= 'z';
1096                 if (!is_alphabet)
1097                 {
1098                         com_id = get_com_id(key);
1099                         if (com_id)
1100                         {
1101                                 return com_id;
1102                         }
1103
1104                         continue;
1105                 }
1106
1107                 int menu_id = menu_id_list[key - 'a'];
1108
1109                 if (menu_id < 0) continue;
1110
1111                 com_id = menu_data[menu_id].com_id;
1112
1113                 if (com_id == -1)
1114                 {
1115                         com_id = do_command_menu(level + 1, menu_id + 1);
1116
1117                         if (com_id) return com_id;
1118                         else redraw = TRUE;
1119                 }
1120                 else if (com_id)
1121                 {
1122                         return com_id;
1123                 }
1124         }
1125 }
1126
1127
1128 static chain_str_type *new_chain_str(concptr str)
1129 {
1130         chain_str_type *chain;
1131         size_t len = strlen(str);
1132         chain = (chain_str_type *)ralloc(sizeof(chain_str_type) + len * sizeof(char));
1133         strcpy(chain->s, str);
1134         chain->next = NULL;
1135         return chain;
1136 }
1137
1138
1139 static void kill_yank_chain(text_body_type *tb)
1140 {
1141         chain_str_type *chain = tb->yank;
1142         tb->yank = NULL;
1143         tb->yank_eol = TRUE;
1144
1145         while (chain)
1146         {
1147                 chain_str_type *next = chain->next;
1148                 size_t len = strlen(chain->s);
1149
1150                 rnfree(chain, sizeof(chain_str_type) + len * sizeof(char));
1151
1152                 chain = next;
1153         }
1154 }
1155
1156
1157 static void add_str_to_yank(text_body_type *tb, concptr str)
1158 {
1159         tb->yank_eol = FALSE;
1160         if (NULL == tb->yank)
1161         {
1162                 tb->yank = new_chain_str(str);
1163                 return;
1164         }
1165
1166         chain_str_type *chain;
1167         chain = tb->yank;
1168
1169         while (TRUE)
1170         {
1171                 if (!chain->next)
1172                 {
1173                         chain->next = new_chain_str(str);
1174                         return;
1175                 }
1176
1177                 /* Go to next */
1178                 chain = chain->next;
1179         }
1180 }
1181
1182
1183 /*
1184  * Do work for the copy editor-command
1185  */
1186 static void copy_text_to_yank(text_body_type *tb)
1187 {
1188         int len = strlen(tb->lines_list[tb->cy]);
1189         if (tb->cx > len) tb->cx = len;
1190
1191         if (!tb->mark)
1192         {
1193                 tb->cx = 0;
1194                 tb->my = tb->cy;
1195                 tb->mx = len;
1196         }
1197
1198         kill_yank_chain(tb);
1199         if (tb->my != tb->cy)
1200         {
1201                 int by1 = MIN(tb->my, tb->cy);
1202                 int by2 = MAX(tb->my, tb->cy);
1203
1204                 for (int y = by1; y <= by2; y++)
1205                 {
1206                         add_str_to_yank(tb, tb->lines_list[y]);
1207                 }
1208
1209                 add_str_to_yank(tb, "");
1210                 tb->mark = 0;
1211                 tb->dirty_flags |= DIRTY_ALL;
1212                 return;
1213         }
1214
1215         char buf[MAX_LINELEN];
1216         int bx1 = MIN(tb->mx, tb->cx);
1217         int bx2 = MAX(tb->mx, tb->cx);
1218         if (bx2 > len) bx2 = len;
1219
1220         if (bx1 == 0 && bx2 == len)
1221         {
1222                 add_str_to_yank(tb, tb->lines_list[tb->cy]);
1223                 add_str_to_yank(tb, "");
1224         }
1225         else
1226         {
1227                 int end = bx2 - bx1;
1228                 for (int i = 0; i < bx2 - bx1; i++)
1229                 {
1230                         buf[i] = tb->lines_list[tb->cy][bx1 + i];
1231                 }
1232
1233                 buf[end] = '\0';
1234                 add_str_to_yank(tb, buf);
1235         }
1236
1237         tb->mark = 0;
1238         tb->dirty_flags |= DIRTY_ALL;
1239 }
1240
1241
1242 /*
1243  * Draw text
1244  */
1245 static void draw_text_editor(player_type *player_ptr, text_body_type *tb)
1246 {
1247         int i;
1248         int by1 = 0, by2 = 0;
1249
1250         Term_get_size(&tb->wid, &tb->hgt);
1251
1252         /*
1253          * Top line (-1), description line (-3), separator (-1)
1254          *  == -5
1255          */
1256         tb->hgt -= 2 + DESCRIPT_HGT;
1257
1258 #ifdef JP
1259         /* Don't let cursor at second byte of kanji */
1260         for (i = 0; tb->lines_list[tb->cy][i]; i++)
1261                 if (iskanji(tb->lines_list[tb->cy][i]))
1262                 {
1263                         i++;
1264                         if (i == tb->cx)
1265                         {
1266                                 /*
1267                                  * Move to a correct position in the
1268                                  * left or right
1269                                  */
1270                                 if (i & 1) tb->cx--;
1271                                 else tb->cx++;
1272                                 break;
1273                         }
1274                 }
1275 #endif
1276         if (tb->cy < tb->upper || tb->upper + tb->hgt <= tb->cy)
1277                 tb->upper = tb->cy - (tb->hgt) / 2;
1278         if (tb->upper < 0)
1279                 tb->upper = 0;
1280         if ((tb->cx < tb->left + 10 && tb->left > 0) || tb->left + tb->wid - 5 <= tb->cx)
1281                 tb->left = tb->cx - (tb->wid) * 2 / 3;
1282         if (tb->left < 0)
1283                 tb->left = 0;
1284
1285         if (tb->old_wid != tb->wid || tb->old_hgt != tb->hgt)
1286                 tb->dirty_flags |= DIRTY_SCREEN;
1287         else if (tb->old_upper != tb->upper || tb->old_left != tb->left)
1288                 tb->dirty_flags |= DIRTY_ALL;
1289
1290         if (tb->dirty_flags & DIRTY_SCREEN)
1291         {
1292                 tb->dirty_flags |= (DIRTY_ALL | DIRTY_MODE);
1293                 Term_clear();
1294         }
1295
1296         if (tb->dirty_flags & DIRTY_MODE)
1297         {
1298                 char buf[MAX_LINELEN];
1299                 int sepa_length = tb->wid;
1300                 for (i = 0; i < sepa_length; i++)
1301                         buf[i] = '-';
1302                 buf[i] = '\0';
1303                 Term_putstr(0, tb->hgt + 1, sepa_length, TERM_WHITE, buf);
1304         }
1305
1306         if (tb->dirty_flags & DIRTY_EXPRESSION)
1307         {
1308                 byte state = 0;
1309                 for (int y = 0; tb->lines_list[y]; y++)
1310                 {
1311                         char f;
1312                         concptr v;
1313                         concptr s = tb->lines_list[y];
1314                         char *ss, *s_keep;
1315                         int s_len;
1316
1317                         tb->states[y] = state;
1318
1319                         if (*s++ != '?') continue;
1320                         if (*s++ != ':') continue;
1321
1322                         if (streq(s, "$AUTOREGISTER"))
1323                                 state |= LSTAT_AUTOREGISTER;
1324
1325                         s_len = strlen(s);
1326                         ss = (char *)string_make(s);
1327                         s_keep = ss;
1328
1329                         v = process_pref_file_expr(player_ptr, &ss, &f);
1330
1331                         if (streq(v, "0")) state |= LSTAT_BYPASS;
1332                         else state &= ~LSTAT_BYPASS;
1333
1334                         C_KILL(s_keep, s_len + 1, char);
1335
1336                         tb->states[y] = state | LSTAT_EXPRESSION;
1337                 }
1338
1339                 tb->dirty_flags |= DIRTY_ALL;
1340         }
1341
1342         if (tb->mark)
1343         {
1344                 tb->dirty_flags |= DIRTY_ALL;
1345
1346                 by1 = MIN(tb->my, tb->cy);
1347                 by2 = MAX(tb->my, tb->cy);
1348         }
1349
1350         for (i = 0; i < tb->hgt; i++)
1351         {
1352                 int j;
1353                 int leftcol = 0;
1354                 concptr msg;
1355                 byte color;
1356                 int y = tb->upper + i;
1357
1358                 if (!(tb->dirty_flags & DIRTY_ALL) && (tb->dirty_line != y))
1359                         continue;
1360
1361                 msg = tb->lines_list[y];
1362                 if (!msg) break;
1363
1364                 for (j = 0; *msg; msg++, j++)
1365                 {
1366                         if (j == tb->left) break;
1367 #ifdef JP
1368                         if (j > tb->left)
1369                         {
1370                                 leftcol = 1;
1371                                 break;
1372                         }
1373                         if (iskanji(*msg))
1374                         {
1375                                 msg++;
1376                                 j++;
1377                         }
1378 #endif
1379                 }
1380
1381                 Term_erase(0, i + 1, tb->wid);
1382                 if (tb->states[y] & LSTAT_AUTOREGISTER)
1383                 {
1384                         color = TERM_L_RED;
1385                 }
1386                 else
1387                 {
1388                         if (tb->states[y] & LSTAT_BYPASS) color = TERM_SLATE;
1389                         else color = TERM_WHITE;
1390                 }
1391
1392                 if (!tb->mark || (y < by1 || by2 < y))
1393                 {
1394                         Term_putstr(leftcol, i + 1, tb->wid - 1, color, msg);
1395                 }
1396                 else if (by1 != by2)
1397                 {
1398                         Term_putstr(leftcol, i + 1, tb->wid - 1, TERM_YELLOW, msg);
1399                 }
1400                 else
1401                 {
1402                         int x0 = leftcol + tb->left;
1403                         int len = strlen(tb->lines_list[tb->cy]);
1404                         int bx1 = MIN(tb->mx, tb->cx);
1405                         int bx2 = MAX(tb->mx, tb->cx);
1406
1407                         if (bx2 > len) bx2 = len;
1408
1409                         Term_gotoxy(leftcol, i + 1);
1410                         if (x0 < bx1) Term_addstr(bx1 - x0, color, msg);
1411                         if (x0 < bx2) Term_addstr(bx2 - bx1, TERM_YELLOW, msg + (bx1 - x0));
1412                         Term_addstr(-1, color, msg + (bx2 - x0));
1413                 }
1414         }
1415
1416         for (; i < tb->hgt; i++)
1417         {
1418                 Term_erase(0, i + 1, tb->wid);
1419         }
1420
1421         bool is_dirty_diary = (tb->dirty_flags & (DIRTY_ALL | DIRTY_NOT_FOUND | DIRTY_NO_SEARCH)) != 0;
1422         bool is_updated = tb->old_cy != tb->cy || is_dirty_diary || tb->dirty_line == tb->cy;
1423         if (is_updated) return;
1424
1425         autopick_type an_entry, *entry = &an_entry;
1426         concptr str1 = NULL, str2 = NULL;
1427         for (i = 0; i < DESCRIPT_HGT; i++)
1428         {
1429                 Term_erase(0, tb->hgt + 2 + i, tb->wid);
1430         }
1431
1432         if (tb->dirty_flags & DIRTY_NOT_FOUND)
1433         {
1434                 str1 = format(_("パターンが見つかりません: %s", "Pattern not found: %s"), tb->search_str);
1435         }
1436         else if (tb->dirty_flags & DIRTY_SKIP_INACTIVE)
1437         {
1438                 str1 = format(_("無効状態の行をスキップしました。(%sを検索中)",
1439                         "Some inactive lines are skipped. (Searching %s)"), tb->search_str);
1440         }
1441         else if (tb->dirty_flags & DIRTY_INACTIVE)
1442         {
1443                 str1 = format(_("無効状態の行だけが見付かりました。(%sを検索中)",
1444                         "Found only an inactive line. (Searching %s)"), tb->search_str);
1445         }
1446         else if (tb->dirty_flags & DIRTY_NO_SEARCH)
1447         {
1448                 str1 = _("検索するパターンがありません(^S で検索)。", "No pattern to search. (Press ^S to search.)");
1449         }
1450         else if (tb->lines_list[tb->cy][0] == '#')
1451         {
1452                 str1 = _("この行はコメントです。", "This line is a comment.");
1453         }
1454         else if (tb->lines_list[tb->cy][0] && tb->lines_list[tb->cy][1] == ':')
1455         {
1456                 switch (tb->lines_list[tb->cy][0])
1457                 {
1458                 case '?':
1459                         str1 = _("この行は条件分岐式です。", "This line is a Conditional Expression.");
1460                         break;
1461                 case 'A':
1462                         str1 = _("この行はマクロの実行内容を定義します。", "This line defines a Macro action.");
1463                         break;
1464                 case 'P':
1465                         str1 = _("この行はマクロのトリガー・キーを定義します。", "This line defines a Macro trigger key.");
1466                         break;
1467                 case 'C':
1468                         str1 = _("この行はキー配置を定義します。", "This line defines a Keymap.");
1469                         break;
1470                 }
1471
1472                 switch (tb->lines_list[tb->cy][0])
1473                 {
1474                 case '?':
1475                         if (tb->states[tb->cy] & LSTAT_BYPASS)
1476                         {
1477                                 str2 = _("現在の式の値は「偽(=0)」です。", "The expression is 'False'(=0) currently.");
1478                         }
1479                         else
1480                         {
1481                                 str2 = _("現在の式の値は「真(=1)」です。", "The expression is 'True'(=1) currently.");
1482                         }
1483                         break;
1484
1485                 default:
1486                         if (tb->states[tb->cy] & LSTAT_AUTOREGISTER)
1487                         {
1488                                 str2 = _("この行は後で削除されます。", "This line will be delete later.");
1489                         }
1490
1491                         else if (tb->states[tb->cy] & LSTAT_BYPASS)
1492                         {
1493                                 str2 = _("この行は現在は無効な状態です。", "This line is bypassed currently.");
1494                         }
1495                         break;
1496                 }
1497         }
1498         else if (autopick_new_entry(entry, tb->lines_list[tb->cy], FALSE))
1499         {
1500                 char buf[MAX_LINELEN];
1501                 char temp[MAX_LINELEN];
1502                 concptr t;
1503
1504                 describe_autopick(buf, entry);
1505
1506                 if (tb->states[tb->cy] & LSTAT_AUTOREGISTER)
1507                 {
1508                         strcat(buf, _("この行は後で削除されます。", "  This line will be delete later."));
1509                 }
1510
1511                 if (tb->states[tb->cy] & LSTAT_BYPASS)
1512                 {
1513                         strcat(buf, _("この行は現在は無効な状態です。", "  This line is bypassed currently."));
1514                 }
1515
1516                 roff_to_buf(buf, 81, temp, sizeof(temp));
1517                 t = temp;
1518                 for (i = 0; i < 3; i++)
1519                 {
1520                         if (t[0] == 0)
1521                                 break;
1522                         else
1523                         {
1524                                 prt(t, tb->hgt + 1 + 1 + i, 0);
1525                                 t += strlen(t) + 1;
1526                         }
1527                 }
1528                 autopick_free_entry(entry);
1529         }
1530
1531         if (str1) prt(str1, tb->hgt + 1 + 1, 0);
1532         if (str2) prt(str2, tb->hgt + 1 + 2, 0);
1533 }
1534
1535
1536 /*
1537  * Kill segment of a line
1538  */
1539 static void kill_line_segment(text_body_type *tb, int y, int x0, int x1, bool whole)
1540 {
1541         concptr s = tb->lines_list[y];
1542         if (whole && x0 == 0 && s[x1] == '\0' && tb->lines_list[y + 1])
1543         {
1544                 string_free(tb->lines_list[y]);
1545
1546                 int i;
1547                 for (i = y; tb->lines_list[i + 1]; i++)
1548                         tb->lines_list[i] = tb->lines_list[i + 1];
1549                 tb->lines_list[i] = NULL;
1550
1551                 tb->dirty_flags |= DIRTY_EXPRESSION;
1552
1553                 return;
1554         }
1555
1556         if (x0 == x1) return;
1557
1558         char buf[MAX_LINELEN];
1559         char *d = buf;
1560         for (int x = 0; x < x0; x++)
1561                 *(d++) = s[x];
1562
1563         for (int x = x1; s[x]; x++)
1564                 *(d++) = s[x];
1565
1566         *d = '\0';
1567         string_free(tb->lines_list[y]);
1568         tb->lines_list[y] = string_make(buf);
1569         check_expression_line(tb, y);
1570         tb->changed = TRUE;
1571 }
1572
1573
1574 /*
1575  * Get a trigger key and insert ASCII string for the trigger
1576  */
1577 static bool insert_macro_line(text_body_type *tb)
1578 {
1579         int i, n = 0;
1580         flush();
1581         inkey_base = TRUE;
1582         i = inkey();
1583         char buf[1024];
1584         while (i)
1585         {
1586                 buf[n++] = (char)i;
1587                 inkey_base = TRUE;
1588                 inkey_scan = TRUE;
1589                 i = inkey();
1590         }
1591
1592         buf[n] = '\0';
1593         flush();
1594
1595         char tmp[1024];
1596         ascii_to_text(tmp, buf);
1597         if (!tmp[0]) return FALSE;
1598
1599         tb->cx = 0;
1600         insert_return_code(tb);
1601         string_free(tb->lines_list[tb->cy]);
1602         tb->lines_list[tb->cy] = string_make(format("P:%s", tmp));
1603
1604         i = macro_find_exact(buf);
1605         if (i == -1)
1606         {
1607                 tmp[0] = '\0';
1608         }
1609         else
1610         {
1611                 ascii_to_text(tmp, macro__act[i]);
1612         }
1613
1614         insert_return_code(tb);
1615         string_free(tb->lines_list[tb->cy]);
1616         tb->lines_list[tb->cy] = string_make(format("A:%s", tmp));
1617
1618         return TRUE;
1619 }
1620
1621
1622 /*
1623  * Get a command key and insert ASCII string for the key
1624  */
1625 static bool insert_keymap_line(text_body_type *tb)
1626 {
1627         BIT_FLAGS mode;
1628         if (rogue_like_commands)
1629         {
1630                 mode = KEYMAP_MODE_ROGUE;
1631         }
1632         else
1633         {
1634                 mode = KEYMAP_MODE_ORIG;
1635         }
1636
1637         flush();
1638         char buf[2];
1639         buf[0] = inkey();
1640         buf[1] = '\0';
1641
1642         flush();
1643         char tmp[1024];
1644         ascii_to_text(tmp, buf);
1645         if (!tmp[0]) return FALSE;
1646
1647         tb->cx = 0;
1648         insert_return_code(tb);
1649         string_free(tb->lines_list[tb->cy]);
1650         tb->lines_list[tb->cy] = string_make(format("C:%d:%s", mode, tmp));
1651
1652         concptr act = keymap_act[mode][(byte)(buf[0])];
1653         if (act)
1654         {
1655                 ascii_to_text(tmp, act);
1656         }
1657
1658         insert_return_code(tb);
1659         string_free(tb->lines_list[tb->cy]);
1660         tb->lines_list[tb->cy] = string_make(format("A:%s", tmp));
1661
1662         return TRUE;
1663 }
1664
1665
1666 /*
1667  * Execute a single editor command
1668  */
1669 static bool do_editor_command(player_type *player_ptr, text_body_type *tb, int com_id)
1670 {
1671         switch (com_id)
1672         {
1673         case EC_QUIT:
1674                 if (tb->changed)
1675                 {
1676                         if (!get_check(_("全ての変更を破棄してから終了します。よろしいですか? ",
1677                                 "Discard all changes and quit. Are you sure? "))) break;
1678                 }
1679
1680                 return QUIT_WITHOUT_SAVE;
1681
1682         case EC_SAVEQUIT:
1683                 return QUIT_AND_SAVE;
1684
1685         case EC_REVERT:
1686                 if (!get_check(_("全ての変更を破棄して元の状態に戻します。よろしいですか? ",
1687                         "Discard all changes and revert to original file. Are you sure? "))) break;
1688
1689                 free_text_lines(tb->lines_list);
1690                 tb->lines_list = read_pickpref_text_lines(player_ptr, &tb->filename_mode);
1691                 tb->dirty_flags |= DIRTY_ALL | DIRTY_MODE | DIRTY_EXPRESSION;
1692                 tb->cx = tb->cy = 0;
1693                 tb->mark = 0;
1694
1695                 tb->changed = FALSE;
1696                 break;
1697
1698         case EC_HELP:
1699                 (void)show_file(player_ptr, TRUE, _("jeditor.txt", "editor.txt"), NULL, 0, 0);
1700                 tb->dirty_flags |= DIRTY_SCREEN;
1701
1702                 break;
1703
1704         case EC_RETURN:
1705                 if (tb->mark)
1706                 {
1707                         tb->mark = 0;
1708                         tb->dirty_flags |= DIRTY_ALL;
1709                 }
1710
1711                 insert_return_code(tb);
1712                 tb->cy++;
1713                 tb->cx = 0;
1714
1715                 tb->dirty_flags |= DIRTY_ALL;
1716                 break;
1717
1718         case EC_LEFT:
1719         {
1720                 if (0 < tb->cx)
1721                 {
1722                         int len;
1723 #ifdef JP
1724                         int i;
1725 #endif
1726                         tb->cx--;
1727                         len = strlen(tb->lines_list[tb->cy]);
1728                         if (len < tb->cx) tb->cx = len;
1729 #ifdef JP
1730                         for (i = 0; tb->lines_list[tb->cy][i]; i++)
1731                         {
1732                                 if (iskanji(tb->lines_list[tb->cy][i]))
1733                                 {
1734                                         i++;
1735                                         if (i == tb->cx)
1736                                         {
1737                                                 tb->cx--;
1738                                                 break;
1739                                         }
1740                                 }
1741                         }
1742 #endif
1743                 }
1744                 else if (tb->cy > 0)
1745                 {
1746                         tb->cy--;
1747                         tb->cx = strlen(tb->lines_list[tb->cy]);
1748                 }
1749
1750                 break;
1751         }
1752         case EC_DOWN:
1753                 if (!tb->lines_list[tb->cy + 1])
1754                 {
1755                         if (!add_empty_line(tb)) break;
1756                 }
1757
1758                 tb->cy++;
1759                 break;
1760
1761         case EC_UP:
1762                 if (tb->cy > 0) tb->cy--;
1763                 break;
1764
1765         case EC_RIGHT:
1766         {
1767 #ifdef JP
1768                 if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
1769 #endif
1770                 tb->cx++;
1771                 int len = strlen(tb->lines_list[tb->cy]);
1772                 if (len < tb->cx)
1773                 {
1774                         tb->cx = len;
1775                         if (!tb->lines_list[tb->cy + 1])
1776                         {
1777                                 if (!add_empty_line(tb)) break;
1778                         }
1779
1780                         tb->cy++;
1781                         tb->cx = 0;
1782                 }
1783
1784                 break;
1785         }
1786         case EC_BOL:
1787                 tb->cx = 0;
1788                 break;
1789
1790         case EC_EOL:
1791                 tb->cx = strlen(tb->lines_list[tb->cy]);
1792                 break;
1793
1794         case EC_PGUP:
1795                 while (0 < tb->cy && tb->upper <= tb->cy)
1796                         tb->cy--;
1797                 while (0 < tb->upper && tb->cy + 1 < tb->upper + tb->hgt)
1798                         tb->upper--;
1799                 break;
1800
1801         case EC_PGDOWN:
1802                 while (tb->cy < tb->upper + tb->hgt)
1803                 {
1804                         if (!tb->lines_list[tb->cy + 1])
1805                         {
1806                                 if (!add_empty_line(tb)) break;
1807                         }
1808
1809                         tb->cy++;
1810                 }
1811
1812                 tb->upper = tb->cy;
1813                 break;
1814
1815         case EC_TOP:
1816                 tb->cy = 0;
1817                 break;
1818
1819         case EC_BOTTOM:
1820                 while (TRUE)
1821                 {
1822                         if (!tb->lines_list[tb->cy + 1])
1823                         {
1824                                 if (!add_empty_line(tb)) break;
1825                         }
1826
1827                         tb->cy++;
1828                 }
1829
1830                 tb->cx = 0;
1831                 break;
1832
1833         case EC_CUT:
1834         {
1835                 copy_text_to_yank(tb);
1836                 if (tb->my == tb->cy)
1837                 {
1838                         int bx1 = MIN(tb->mx, tb->cx);
1839                         int bx2 = MAX(tb->mx, tb->cx);
1840                         int len = strlen(tb->lines_list[tb->cy]);
1841                         if (bx2 > len) bx2 = len;
1842
1843                         kill_line_segment(tb, tb->cy, bx1, bx2, TRUE);
1844                         tb->cx = bx1;
1845                 }
1846                 else
1847                 {
1848                         int by1 = MIN(tb->my, tb->cy);
1849                         int by2 = MAX(tb->my, tb->cy);
1850
1851                         for (int y = by2; y >= by1; y--)
1852                         {
1853                                 int len = strlen(tb->lines_list[y]);
1854
1855                                 kill_line_segment(tb, y, 0, len, TRUE);
1856                         }
1857
1858                         tb->cy = by1;
1859                         tb->cx = 0;
1860                 }
1861
1862                 tb->mark = 0;
1863                 tb->dirty_flags |= DIRTY_ALL;
1864                 tb->changed = TRUE;
1865                 break;
1866         }
1867         case EC_COPY:
1868                 copy_text_to_yank(tb);
1869
1870                 /*
1871                  * Move cursor position to the end of the selection
1872                  *
1873                  * Pressing ^C ^V correctly duplicates the selection.
1874                  */
1875                 if (tb->my != tb->cy)
1876                 {
1877                         tb->cy = MAX(tb->cy, tb->my);
1878                         if (!tb->lines_list[tb->cy + 1])
1879                         {
1880                                 if (!add_empty_line(tb)) break;
1881                         }
1882
1883                         tb->cy++;
1884                         break;
1885                 }
1886
1887                 tb->cx = MAX(tb->cx, tb->mx);
1888                 if (!tb->lines_list[tb->cy][tb->cx])
1889                 {
1890                         if (!tb->lines_list[tb->cy + 1])
1891                         {
1892                                 if (!add_empty_line(tb)) break;
1893                         }
1894
1895                         tb->cy++;
1896                         tb->cx = 0;
1897                 }
1898
1899                 break;
1900
1901         case EC_PASTE:
1902         {
1903                 chain_str_type *chain = tb->yank;
1904                 int len = strlen(tb->lines_list[tb->cy]);
1905                 if (!chain) break;
1906                 if (tb->cx > len) tb->cx = len;
1907
1908                 if (tb->mark)
1909                 {
1910                         tb->mark = 0;
1911                         tb->dirty_flags |= DIRTY_ALL;
1912                 }
1913
1914                 while (chain)
1915                 {
1916                         concptr yank_str = chain->s;
1917                         char buf[MAX_LINELEN];
1918                         int i;
1919                         char rest[MAX_LINELEN], *rest_ptr = rest;
1920                         for (i = 0; i < tb->cx; i++)
1921                                 buf[i] = tb->lines_list[tb->cy][i];
1922
1923                         strcpy(rest, &(tb->lines_list[tb->cy][i]));
1924                         while (*yank_str && i < MAX_LINELEN - 1)
1925                         {
1926                                 buf[i++] = *yank_str++;
1927                         }
1928
1929                         buf[i] = '\0';
1930                         chain = chain->next;
1931                         if (chain || tb->yank_eol)
1932                         {
1933                                 insert_return_code(tb);
1934                                 string_free(tb->lines_list[tb->cy]);
1935                                 tb->lines_list[tb->cy] = string_make(buf);
1936                                 tb->cx = 0;
1937                                 tb->cy++;
1938
1939                                 continue;
1940                         }
1941
1942                         tb->cx = strlen(buf);
1943                         while (*rest_ptr && i < MAX_LINELEN - 1)
1944                         {
1945                                 buf[i++] = *rest_ptr++;
1946                         }
1947
1948                         buf[i] = '\0';
1949                         string_free(tb->lines_list[tb->cy]);
1950                         tb->lines_list[tb->cy] = string_make(buf);
1951                         break;
1952                 }
1953
1954                 tb->dirty_flags |= DIRTY_ALL;
1955                 tb->dirty_flags |= DIRTY_EXPRESSION;
1956                 tb->changed = TRUE;
1957                 break;
1958         }
1959         case EC_BLOCK:
1960         {
1961                 if (tb->mark)
1962                 {
1963                         tb->mark = 0;
1964                         tb->dirty_flags |= DIRTY_ALL;
1965                         break;
1966                 }
1967
1968                 tb->mark = MARK_MARK;
1969                 if (com_id == tb->old_com_id)
1970                 {
1971                         int tmp = tb->cy;
1972                         tb->cy = tb->my;
1973                         tb->my = tmp;
1974                         tmp = tb->cx;
1975                         tb->cx = tb->mx;
1976                         tb->mx = tmp;
1977                         tb->dirty_flags |= DIRTY_ALL;
1978                         break;
1979                 }
1980
1981                 int len = strlen(tb->lines_list[tb->cy]);
1982
1983                 tb->my = tb->cy;
1984                 tb->mx = tb->cx;
1985                 if (tb->cx > len) tb->mx = len;
1986                 break;
1987         }
1988         case EC_KILL_LINE:
1989         {
1990                 int len = strlen(tb->lines_list[tb->cy]);
1991                 if (tb->cx > len) tb->cx = len;
1992
1993                 if (tb->mark)
1994                 {
1995                         tb->mark = 0;
1996                         tb->dirty_flags |= DIRTY_ALL;
1997                 }
1998
1999                 if (tb->old_com_id != com_id)
2000                 {
2001                         kill_yank_chain(tb);
2002                         tb->yank = NULL;
2003                 }
2004
2005                 if (tb->cx < len)
2006                 {
2007                         add_str_to_yank(tb, &(tb->lines_list[tb->cy][tb->cx]));
2008                         kill_line_segment(tb, tb->cy, tb->cx, len, FALSE);
2009                         tb->dirty_line = tb->cy;
2010                         break;
2011                 }
2012
2013                 if (tb->yank_eol) add_str_to_yank(tb, "");
2014
2015                 tb->yank_eol = TRUE;
2016                 do_editor_command(player_ptr, tb, EC_DELETE_CHAR);
2017                 break;
2018         }
2019         case EC_DELETE_CHAR:
2020         {
2021                 if (tb->mark)
2022                 {
2023                         tb->mark = 0;
2024                         tb->dirty_flags |= DIRTY_ALL;
2025                 }
2026
2027 #ifdef JP
2028                 if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
2029 #endif
2030                 tb->cx++;
2031                 int len = strlen(tb->lines_list[tb->cy]);
2032                 if (len >= tb->cx)
2033                 {
2034                         do_editor_command(player_ptr, tb, EC_BACKSPACE);
2035                         break;
2036                 }
2037
2038                 if (tb->lines_list[tb->cy + 1])
2039                 {
2040                         tb->cy++;
2041                         tb->cx = 0;
2042                 }
2043                 else
2044                 {
2045                         tb->cx = len;
2046                         break;
2047                 }
2048
2049                 do_editor_command(player_ptr, tb, EC_BACKSPACE);
2050                 break;
2051         }
2052         case EC_BACKSPACE:
2053         {
2054                 int len, i, j, k;
2055                 char buf[MAX_LINELEN];
2056                 if (tb->mark)
2057                 {
2058                         tb->mark = 0;
2059                         tb->dirty_flags |= DIRTY_ALL;
2060                 }
2061
2062                 len = strlen(tb->lines_list[tb->cy]);
2063                 if (len < tb->cx) tb->cx = len;
2064
2065                 if (tb->cx == 0)
2066                 {
2067                         if (tb->cy == 0) break;
2068                         tb->cx = strlen(tb->lines_list[tb->cy - 1]);
2069                         strcpy(buf, tb->lines_list[tb->cy - 1]);
2070                         strcat(buf, tb->lines_list[tb->cy]);
2071                         string_free(tb->lines_list[tb->cy - 1]);
2072                         string_free(tb->lines_list[tb->cy]);
2073                         tb->lines_list[tb->cy - 1] = string_make(buf);
2074
2075                         for (i = tb->cy; tb->lines_list[i + 1]; i++)
2076                                 tb->lines_list[i] = tb->lines_list[i + 1];
2077
2078                         tb->lines_list[i] = NULL;
2079                         tb->cy--;
2080                         tb->dirty_flags |= DIRTY_ALL;
2081                         tb->dirty_flags |= DIRTY_EXPRESSION;
2082                         tb->changed = TRUE;
2083                         break;
2084                 }
2085
2086                 for (i = j = k = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
2087                 {
2088                         k = j;
2089 #ifdef JP
2090                         if (iskanji(tb->lines_list[tb->cy][i]))
2091                                 buf[j++] = tb->lines_list[tb->cy][i++];
2092 #endif
2093                         buf[j++] = tb->lines_list[tb->cy][i];
2094                 }
2095
2096                 while (j > k)
2097                 {
2098                         tb->cx--;
2099                         j--;
2100                 }
2101
2102                 for (; tb->lines_list[tb->cy][i]; i++)
2103                 {
2104                         buf[j++] = tb->lines_list[tb->cy][i];
2105                 }
2106
2107                 buf[j] = '\0';
2108                 string_free(tb->lines_list[tb->cy]);
2109                 tb->lines_list[tb->cy] = string_make(buf);
2110                 tb->dirty_line = tb->cy;
2111                 check_expression_line(tb, tb->cy);
2112                 tb->changed = TRUE;
2113                 break;
2114         }
2115         case EC_SEARCH_STR:
2116         {
2117                 byte search_dir;
2118                 tb->dirty_flags |= DIRTY_SCREEN;
2119                 search_dir = get_string_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str);
2120
2121                 if (!search_dir) break;
2122
2123                 if (search_dir == 1) do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
2124                 else do_editor_command(player_ptr, tb, EC_SEARCH_BACK);
2125                 break;
2126         }
2127         case EC_SEARCH_FORW:
2128                 if (tb->search_o_ptr)
2129                 {
2130                         search_for_object(player_ptr, tb, tb->search_o_ptr, TRUE);
2131                         break;
2132                 }
2133
2134                 if (tb->search_str && tb->search_str[0])
2135                 {
2136                         search_for_string(tb, tb->search_str, TRUE);
2137                         break;
2138                 }
2139
2140                 tb->dirty_flags |= DIRTY_NO_SEARCH;
2141                 break;
2142
2143         case EC_SEARCH_BACK:
2144                 if (tb->search_o_ptr)
2145                 {
2146                         search_for_object(player_ptr, tb, tb->search_o_ptr, FALSE);
2147                         break;
2148                 }
2149
2150                 if (tb->search_str && tb->search_str[0])
2151                 {
2152                         search_for_string(tb, tb->search_str, FALSE);
2153                         break;
2154                 }
2155
2156                 tb->dirty_flags |= DIRTY_NO_SEARCH;
2157                 break;
2158
2159         case EC_SEARCH_OBJ:
2160                 tb->dirty_flags |= DIRTY_SCREEN;
2161
2162                 if (!get_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str)) break;
2163
2164                 do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
2165                 break;
2166
2167         case EC_SEARCH_DESTROYED:
2168                 if (!get_destroyed_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str))
2169                 {
2170                         tb->dirty_flags |= DIRTY_NO_SEARCH;
2171                         break;
2172                 }
2173
2174                 do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
2175                 break;
2176
2177         case EC_INSERT_OBJECT:
2178         {
2179                 autopick_type an_entry, *entry = &an_entry;
2180                 if (!entry_from_choosed_object(player_ptr, entry))
2181                 {
2182                         tb->dirty_flags |= DIRTY_SCREEN;
2183                         break;
2184                 }
2185
2186                 tb->cx = 0;
2187                 insert_return_code(tb);
2188                 string_free(tb->lines_list[tb->cy]);
2189                 tb->lines_list[tb->cy] = autopick_line_from_entry_kill(entry);
2190                 tb->dirty_flags |= DIRTY_SCREEN;
2191                 break;
2192         }
2193         case EC_INSERT_DESTROYED:
2194                 if (!tb->last_destroyed) break;
2195
2196                 tb->cx = 0;
2197                 insert_return_code(tb);
2198                 string_free(tb->lines_list[tb->cy]);
2199                 tb->lines_list[tb->cy] = string_make(tb->last_destroyed);
2200                 tb->dirty_flags |= DIRTY_ALL;
2201                 tb->changed = TRUE;
2202                 break;
2203
2204         case EC_INSERT_BLOCK:
2205         {
2206                 char expression[80];
2207                 sprintf(expression, "?:[AND [EQU $RACE %s] [EQU $CLASS %s] [GEQ $LEVEL %02d]]",
2208 #ifdef JP
2209                         rp_ptr->E_title, cp_ptr->E_title,
2210 #else
2211                         rp_ptr->title, cp_ptr->title,
2212 #endif
2213                         player_ptr->lev);
2214                 tb->cx = 0;
2215                 insert_return_code(tb);
2216                 string_free(tb->lines_list[tb->cy]);
2217                 tb->lines_list[tb->cy] = string_make(expression);
2218                 tb->cy++;
2219                 insert_return_code(tb);
2220                 string_free(tb->lines_list[tb->cy]);
2221                 tb->lines_list[tb->cy] = string_make("?:1");
2222                 tb->dirty_flags |= DIRTY_ALL;
2223                 tb->changed = TRUE;
2224                 break;
2225         }
2226
2227         case EC_INSERT_MACRO:
2228                 draw_text_editor(player_ptr, tb);
2229                 Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
2230                 Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW, _("P:<トリガーキー>: ", "P:<Trigger key>: "));
2231                 if (!insert_macro_line(tb)) break;
2232
2233                 tb->cx = 2;
2234                 tb->dirty_flags |= DIRTY_ALL;
2235                 tb->changed = TRUE;
2236                 break;
2237
2238         case EC_INSERT_KEYMAP:
2239                 draw_text_editor(player_ptr, tb);
2240                 Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
2241                 Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW,
2242                         format(_("C:%d:<コマンドキー>: ", "C:%d:<Keypress>: "), (rogue_like_commands ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG)));
2243
2244                 if (!insert_keymap_line(tb)) break;
2245
2246                 tb->cx = 2;
2247                 tb->dirty_flags |= DIRTY_ALL;
2248                 tb->changed = TRUE;
2249                 break;
2250
2251         case EC_CL_AUTOPICK: toggle_command_letter(tb, DO_AUTOPICK); break;
2252         case EC_CL_DESTROY: toggle_command_letter(tb, DO_AUTODESTROY); break;
2253         case EC_CL_LEAVE: toggle_command_letter(tb, DONT_AUTOPICK); break;
2254         case EC_CL_QUERY: toggle_command_letter(tb, DO_QUERY_AUTOPICK); break;
2255         case EC_CL_NO_DISP: toggle_command_letter(tb, DO_DISPLAY); break;
2256
2257         case EC_IK_UNAWARE: toggle_keyword(tb, FLG_UNAWARE); break;
2258         case EC_IK_UNIDENTIFIED: toggle_keyword(tb, FLG_UNIDENTIFIED); break;
2259         case EC_IK_IDENTIFIED: toggle_keyword(tb, FLG_IDENTIFIED); break;
2260         case EC_IK_STAR_IDENTIFIED: toggle_keyword(tb, FLG_STAR_IDENTIFIED); break;
2261         case EC_KK_WEAPONS: toggle_keyword(tb, FLG_WEAPONS); break;
2262         case EC_KK_FAVORITE_WEAPONS: toggle_keyword(tb, FLG_FAVORITE_WEAPONS); break;
2263         case EC_KK_ARMORS: toggle_keyword(tb, FLG_ARMORS); break;
2264         case EC_KK_MISSILES: toggle_keyword(tb, FLG_MISSILES); break;
2265         case EC_KK_DEVICES: toggle_keyword(tb, FLG_DEVICES); break;
2266         case EC_KK_LIGHTS: toggle_keyword(tb, FLG_LIGHTS); break;
2267         case EC_KK_JUNKS: toggle_keyword(tb, FLG_JUNKS); break;
2268         case EC_KK_CORPSES: toggle_keyword(tb, FLG_CORPSES); break;
2269         case EC_KK_SPELLBOOKS: toggle_keyword(tb, FLG_SPELLBOOKS); break;
2270         case EC_KK_SHIELDS: toggle_keyword(tb, FLG_SHIELDS); break;
2271         case EC_KK_BOWS: toggle_keyword(tb, FLG_BOWS); break;
2272         case EC_KK_RINGS: toggle_keyword(tb, FLG_RINGS); break;
2273         case EC_KK_AMULETS: toggle_keyword(tb, FLG_AMULETS); break;
2274         case EC_KK_SUITS: toggle_keyword(tb, FLG_SUITS); break;
2275         case EC_KK_CLOAKS: toggle_keyword(tb, FLG_CLOAKS); break;
2276         case EC_KK_HELMS: toggle_keyword(tb, FLG_HELMS); break;
2277         case EC_KK_GLOVES: toggle_keyword(tb, FLG_GLOVES); break;
2278         case EC_KK_BOOTS: toggle_keyword(tb, FLG_BOOTS); break;
2279         case EC_OK_COLLECTING: toggle_keyword(tb, FLG_COLLECTING); break;
2280         case EC_OK_BOOSTED: toggle_keyword(tb, FLG_BOOSTED); break;
2281         case EC_OK_MORE_DICE: toggle_keyword(tb, FLG_MORE_DICE); break;
2282         case EC_OK_MORE_BONUS: toggle_keyword(tb, FLG_MORE_BONUS); break;
2283         case EC_OK_WORTHLESS: toggle_keyword(tb, FLG_WORTHLESS); break;
2284         case EC_OK_ARTIFACT: toggle_keyword(tb, FLG_ARTIFACT); break;
2285         case EC_OK_EGO: toggle_keyword(tb, FLG_EGO); break;
2286         case EC_OK_GOOD: toggle_keyword(tb, FLG_GOOD); break;
2287         case EC_OK_NAMELESS: toggle_keyword(tb, FLG_NAMELESS); break;
2288         case EC_OK_AVERAGE: toggle_keyword(tb, FLG_AVERAGE); break;
2289         case EC_OK_RARE: toggle_keyword(tb, FLG_RARE); break;
2290         case EC_OK_COMMON: toggle_keyword(tb, FLG_COMMON); break;
2291         case EC_OK_WANTED: toggle_keyword(tb, FLG_WANTED); break;
2292         case EC_OK_UNIQUE: toggle_keyword(tb, FLG_UNIQUE); break;
2293         case EC_OK_HUMAN: toggle_keyword(tb, FLG_HUMAN); break;
2294         case EC_OK_UNREADABLE:
2295                 toggle_keyword(tb, FLG_UNREADABLE);
2296                 add_keyword(tb, FLG_SPELLBOOKS);
2297                 break;
2298         case EC_OK_REALM1:
2299                 toggle_keyword(tb, FLG_REALM1);
2300                 add_keyword(tb, FLG_SPELLBOOKS);
2301                 break;
2302         case EC_OK_REALM2:
2303                 toggle_keyword(tb, FLG_REALM2);
2304                 add_keyword(tb, FLG_SPELLBOOKS);
2305                 break;
2306         case EC_OK_FIRST:
2307                 toggle_keyword(tb, FLG_FIRST);
2308                 add_keyword(tb, FLG_SPELLBOOKS);
2309                 break;
2310         case EC_OK_SECOND:
2311                 toggle_keyword(tb, FLG_SECOND);
2312                 add_keyword(tb, FLG_SPELLBOOKS);
2313                 break;
2314         case EC_OK_THIRD:
2315                 toggle_keyword(tb, FLG_THIRD);
2316                 add_keyword(tb, FLG_SPELLBOOKS);
2317                 break;
2318         case EC_OK_FOURTH:
2319                 toggle_keyword(tb, FLG_FOURTH);
2320                 add_keyword(tb, FLG_SPELLBOOKS);
2321                 break;
2322         }
2323
2324         tb->old_com_id = com_id;
2325         return FALSE;
2326 }
2327
2328
2329 /*
2330  * Insert single letter at cursor position.
2331  */
2332 static void insert_single_letter(text_body_type *tb, int key)
2333 {
2334         int i, j, len;
2335         char buf[MAX_LINELEN];
2336
2337         for (i = j = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
2338         {
2339                 buf[j++] = tb->lines_list[tb->cy][i];
2340         }
2341
2342 #ifdef JP
2343         if (iskanji(key))
2344         {
2345                 int next;
2346
2347                 inkey_base = TRUE;
2348                 next = inkey();
2349                 if (j + 2 < MAX_LINELEN)
2350                 {
2351                         buf[j++] = (char)key;
2352                         buf[j++] = (char)next;
2353                         tb->cx += 2;
2354                 }
2355                 else
2356                         bell();
2357         }
2358         else
2359 #endif
2360         {
2361                 if (j + 1 < MAX_LINELEN)
2362                         buf[j++] = (char)key;
2363                 tb->cx++;
2364         }
2365
2366         for (; tb->lines_list[tb->cy][i] && j + 1 < MAX_LINELEN; i++)
2367                 buf[j++] = tb->lines_list[tb->cy][i];
2368         buf[j] = '\0';
2369
2370         string_free(tb->lines_list[tb->cy]);
2371         tb->lines_list[tb->cy] = string_make(buf);
2372         len = strlen(tb->lines_list[tb->cy]);
2373         if (len < tb->cx) tb->cx = len;
2374
2375         tb->dirty_line = tb->cy;
2376         check_expression_line(tb, tb->cy);
2377         tb->changed = TRUE;
2378 }
2379
2380
2381 /*
2382  * Check special key code and get a movement command id
2383  */
2384 static int analyze_move_key(text_body_type *tb, int skey)
2385 {
2386         int com_id;
2387         if (!(skey & SKEY_MASK)) return 0;
2388
2389         switch (skey & ~SKEY_MOD_MASK)
2390         {
2391         case SKEY_DOWN:   com_id = EC_DOWN;   break;
2392         case SKEY_LEFT:   com_id = EC_LEFT;   break;
2393         case SKEY_RIGHT:  com_id = EC_RIGHT;  break;
2394         case SKEY_UP:     com_id = EC_UP;     break;
2395         case SKEY_PGUP:   com_id = EC_PGUP;   break;
2396         case SKEY_PGDOWN: com_id = EC_PGDOWN; break;
2397         case SKEY_TOP:    com_id = EC_TOP;    break;
2398         case SKEY_BOTTOM: com_id = EC_BOTTOM; break;
2399         default:
2400                 return 0;
2401         }
2402
2403         if (!(skey & SKEY_MOD_SHIFT))
2404         {
2405                 /*
2406                  * Un-shifted cursor keys cancells
2407                  * selection created by shift+cursor.
2408                  */
2409                 if (tb->mark & MARK_BY_SHIFT)
2410                 {
2411                         tb->mark = 0;
2412                         tb->dirty_flags |= DIRTY_ALL;
2413                 }
2414
2415                 return com_id;
2416         }
2417
2418         if (tb->mark) return com_id;
2419
2420         int len = strlen(tb->lines_list[tb->cy]);
2421         tb->mark = MARK_MARK | MARK_BY_SHIFT;
2422         tb->my = tb->cy;
2423         tb->mx = tb->cx;
2424         if (tb->cx > len) tb->mx = len;
2425
2426         if (com_id == EC_UP || com_id == EC_DOWN)
2427         {
2428                 tb->dirty_flags |= DIRTY_ALL;
2429         }
2430         else
2431         {
2432                 tb->dirty_line = tb->cy;
2433         }
2434
2435         return com_id;
2436 }
2437
2438 /*
2439  * In-game editor of Object Auto-picker/Destoryer
2440  * @param player_ptr プレーヤーへの参照ポインタ
2441  */
2442 void do_cmd_edit_autopick(player_type *player_ptr)
2443 {
2444         static int cx_save = 0;
2445         static int cy_save = 0;
2446         text_body_type text_body, *tb = &text_body;
2447         autopick_type an_entry, *entry = &an_entry;
2448         char buf[MAX_LINELEN];
2449         int i;
2450         int key = -1;
2451         static s32b old_autosave_turn = 0L;
2452         byte quit = 0;
2453
2454         tb->changed = FALSE;
2455         tb->cx = cx_save;
2456         tb->cy = cy_save;
2457         tb->upper = tb->left = 0;
2458         tb->mark = 0;
2459         tb->mx = tb->my = 0;
2460         tb->old_cy = tb->old_upper = tb->old_left = -1;
2461         tb->old_wid = tb->old_hgt = -1;
2462         tb->old_com_id = 0;
2463
2464         tb->yank = NULL;
2465         tb->search_o_ptr = NULL;
2466         tb->search_str = NULL;
2467         tb->last_destroyed = NULL;
2468         tb->dirty_flags = DIRTY_ALL | DIRTY_MODE | DIRTY_EXPRESSION;
2469         tb->dirty_line = -1;
2470         tb->filename_mode = PT_DEFAULT;
2471
2472         if (current_world_ptr->game_turn < old_autosave_turn)
2473         {
2474                 while (old_autosave_turn > current_world_ptr->game_turn) old_autosave_turn -= TURNS_PER_TICK * TOWN_DAWN;
2475         }
2476
2477         if (current_world_ptr->game_turn > old_autosave_turn + 100L)
2478         {
2479                 do_cmd_save_game(player_ptr, TRUE);
2480                 old_autosave_turn = current_world_ptr->game_turn;
2481         }
2482
2483         update_playtime();
2484         init_autopick();
2485         if (autopick_last_destroyed_object.k_idx)
2486         {
2487                 autopick_entry_from_object(player_ptr, entry, &autopick_last_destroyed_object);
2488                 tb->last_destroyed = autopick_line_from_entry_kill(entry);
2489         }
2490
2491         tb->lines_list = read_pickpref_text_lines(player_ptr, &tb->filename_mode);
2492         for (i = 0; i < tb->cy; i++)
2493         {
2494                 if (!tb->lines_list[i])
2495                 {
2496                         tb->cy = tb->cx = 0;
2497                         break;
2498                 }
2499         }
2500
2501         screen_save();
2502         while (!quit)
2503         {
2504                 int com_id = 0;
2505                 draw_text_editor(player_ptr, tb);
2506                 prt(_("(^Q:終了 ^W:セーブして終了, ESC:メニュー, その他:入力)",
2507                         "(^Q:Quit, ^W:Save&Quit, ESC:Menu, Other:Input text)"), 0, 0);
2508                 if (!tb->mark)
2509                 {
2510                         prt(format("(%d,%d)", tb->cx, tb->cy), 0, 60);
2511                 }
2512                 else
2513                 {
2514                         prt(format("(%d,%d)-(%d,%d)", tb->mx, tb->my, tb->cx, tb->cy), 0, 60);
2515                 }
2516
2517                 Term_gotoxy(tb->cx - tb->left, tb->cy - tb->upper + 1);
2518                 tb->dirty_flags = 0;
2519                 tb->dirty_line = -1;
2520                 tb->old_cy = tb->cy;
2521                 tb->old_upper = tb->upper;
2522                 tb->old_left = tb->left;
2523                 tb->old_wid = tb->wid;
2524                 tb->old_hgt = tb->hgt;
2525
2526                 key = inkey_special(TRUE);
2527
2528                 if (key & SKEY_MASK)
2529                 {
2530                         com_id = analyze_move_key(tb, key);
2531                 }
2532                 else if (key == ESCAPE)
2533                 {
2534                         com_id = do_command_menu(0, 0);
2535                         tb->dirty_flags |= DIRTY_SCREEN;
2536                 }
2537                 else if (!iscntrl((unsigned char)key))
2538                 {
2539                         if (tb->mark)
2540                         {
2541                                 tb->mark = 0;
2542                                 tb->dirty_flags |= DIRTY_ALL;
2543                         }
2544
2545                         insert_single_letter(tb, key);
2546                         continue;
2547                 }
2548                 else
2549                 {
2550                         com_id = get_com_id((char)key);
2551                 }
2552
2553                 if (com_id) quit = do_editor_command(player_ptr, tb, com_id);
2554         }
2555
2556         screen_load();
2557         strcpy(buf, pickpref_filename(player_ptr, tb->filename_mode));
2558
2559         if (quit == QUIT_AND_SAVE)
2560                 write_text_lines(buf, tb->lines_list);
2561
2562         free_text_lines(tb->lines_list);
2563         string_free(tb->search_str);
2564         string_free(tb->last_destroyed);
2565         kill_yank_chain(tb);
2566
2567         process_autopick_file(player_ptr, buf);
2568         current_world_ptr->start_time = (u32b)time(NULL);
2569         cx_save = tb->cx;
2570         cy_save = tb->cy;
2571 }