OSDN Git Service

upgrade to 3.6.2
[jnethack/source.git] / win / curses / cursmesg.c
1 /* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
2 /* NetHack 3.6 cursmesg.c */
3 /* Copyright (c) Karl Garrison, 2010. */
4 /* NetHack may be freely redistributed.  See license for details. */
5
6 #include "curses.h"
7 #include "hack.h"
8 #include "wincurs.h"
9 #include "cursmesg.h"
10 #include <ctype.h>
11
12 /*
13  * Note: references to "More>>" mean ">>", the curses rendition of "--More--".
14  */
15
16 /* player can type ESC at More>> prompt to avoid seeing more messages
17    for the current move; but hero might get more than one move per turn,
18    so the input routines need to be able to cancel this */
19 long curs_mesg_suppress_turn = -1;
20
21 /* Message window routines for curses interface */
22
23 /* Private declatations */
24
25 typedef struct nhpm {
26     char *str;                  /* Message text */
27     long turn;                  /* Turn number for message */
28     struct nhpm *prev_mesg;     /* Pointer to previous message */
29     struct nhpm *next_mesg;     /* Pointer to next message */
30 } nhprev_mesg;
31
32 static void scroll_window(winid wid);
33 static void unscroll_window(winid wid);
34 static void directional_scroll(winid wid, int nlines);
35 static void mesg_add_line(const char *mline);
36 static nhprev_mesg *get_msg_line(boolean reverse, int mindex);
37
38 static int turn_lines = 0;
39 static int mx = 0;
40 static int my = 0;              /* message window text location */
41 static nhprev_mesg *first_mesg = NULL;
42 static nhprev_mesg *last_mesg = NULL;
43 static int max_messages;
44 static int num_messages = 0;
45 static int last_messages = 0;
46
47 /* Write string to the message window.  Attributes set by calling function. */
48
49 void
50 curses_message_win_puts(const char *message, boolean recursed)
51 {
52     int height, width, border_space, linespace;
53     char *tmpstr;
54     WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
55     boolean bold, border = curses_window_has_border(MESSAGE_WIN);
56     int message_length = (int) strlen(message);
57
58 #if 0
59     /*
60      * This was useful when curses used genl_putmsghistory() but is not
61      * needed now that it has its own curses_putmsghistory() which is
62      * capable of putting something into the ^P recall history without
63      * displaying it at the same time.
64      */
65     if (strncmp("Count:", message, 6) == 0) {
66         curses_count_window(message);
67         return;
68     }
69 #endif
70
71     if (curs_mesg_suppress_turn == moves) {
72         return; /* user has typed ESC to avoid seeing remaining messages. */
73     }
74
75     curses_get_window_size(MESSAGE_WIN, &height, &width);
76     border_space = (border ? 1 : 0);
77     if (mx < border_space)
78         mx = border_space;
79     if (my < border_space)
80         my = border_space;
81
82     if (strcmp(message, "#") == 0) {    /* Extended command or Count: */
83         if ((strcmp(toplines, "#") != 0)
84             /* Bottom of message window */
85             && (my >= (height - 1 + border_space)) && (height != 1)) {
86             scroll_window(MESSAGE_WIN);
87             mx = width;
88             my--;
89             strcpy(toplines, message);
90         }
91         return;
92     }
93
94     if (!recursed) {
95         strcpy(toplines, message);
96         mesg_add_line(message);
97     }
98
99     linespace = width - 3 - (mx - border_space);
100
101     if (linespace < message_length) {
102         if (my - border_space >= height - 1) {
103             /* bottom of message win */
104             if (++turn_lines >= height) { /* || height == 1) */
105                 /* Pause until key is hit - Esc suppresses any further
106                    messages that turn */
107                 if (curses_more() == '\033') {
108                     curs_mesg_suppress_turn = moves;
109                     return;
110                 }
111                 /* turn_lines reset to 0 by more()->block()->got_input() */
112             } else {
113                 scroll_window(MESSAGE_WIN);
114             }
115         } else {
116             if (mx != border_space) {
117                 my++;
118                 mx = border_space;
119                 ++turn_lines;
120             }
121         }
122     }
123
124     bold = (height > 1 && !last_messages);
125     if (bold)
126         curses_toggle_color_attr(win, NONE, A_BOLD, ON);
127
128     /* will this message fit as-is or do we need to split it? */
129     if (mx == border_space && message_length > width - 2) {
130         /* split needed */
131         tmpstr = curses_break_str(message, (width - 2), 1);
132         mvwprintw(win, my, mx, "%s", tmpstr), mx += (int) strlen(tmpstr);
133         /* one space to separate first part of message from rest [is this
134            actually useful?] */
135         if (mx < width - 2)
136             ++mx;
137         free(tmpstr);
138         if (bold)
139             curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
140         tmpstr = curses_str_remainder(message, (width - 2), 1);
141         curses_message_win_puts(tmpstr, TRUE);
142         free(tmpstr);
143     } else {
144         mvwprintw(win, my, mx, "%s", message), mx += message_length;
145         /* two spaces to separate this message from next one if they happen
146            to fit on the same line; (FIXME:  it would be better if this was
147            done at start of next message rather than end of this one since
148            it impacts placement of "More>>") */
149         if (mx < width - 2) {
150             if (++mx < width - 2)
151                 ++mx;
152         }
153         if (bold)
154             curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
155     }
156     wrefresh(win);
157 }
158
159 void
160 curses_got_input(void)
161 {
162     /* if messages are being suppressed, reenable them */
163     curs_mesg_suppress_turn = -1;
164
165     /* misleadingly named; represents number of lines delivered since
166        player was sure to have had a chance to read them; if player
167        has just given input then there aren't any such lines right;
168        that includes responding to More>> even though it stays same turn */
169     turn_lines = 0;
170 }
171
172 int
173 curses_block(boolean noscroll) /* noscroll - blocking because of msgtype
174                                 * = stop/alert else blocking because
175                                 * window is full, so need to scroll after */
176 {
177     static const char resp[] = " \r\n\033"; /* space, enter, esc */
178     static int prev_x = -1, prev_y = -1, blink = 0;
179     int height, width, moreattr, oldcrsr, ret = 0,
180         brdroffset = curses_window_has_border(MESSAGE_WIN) ? 1 : 0;
181     WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
182
183     curses_get_window_size(MESSAGE_WIN, &height, &width);
184     if (mx - brdroffset > width - 3) { /* -3: room for ">>_" */
185         if (my - brdroffset < height - 1)
186             ++my, mx = brdroffset;
187         else
188             mx = width - 3 + brdroffset;
189     }
190     /* if ">>" (--More--) is being rendered at the same spot as before,
191        toggle attributes so that the first '>' starts blinking if it wasn't
192        or stops blinking if it was */
193     if (mx == prev_x && my == prev_y) {
194         blink = 1 - blink;
195     } else {
196         prev_x = mx, prev_y = my;
197         blink = 0;
198     }
199     moreattr = !iflags.wc2_guicolor ? A_REVERSE : NONE;
200     curses_toggle_color_attr(win, MORECOLOR, moreattr, ON);
201     if (blink) {
202         wattron(win, A_BLINK);
203         mvwprintw(win, my, mx, ">"), mx += 1;
204         wattroff(win, A_BLINK);
205         waddstr(win, ">"), mx += 1;
206     } else {
207         mvwprintw(win, my, mx, ">>"), mx += 2;
208     }
209     curses_toggle_color_attr(win, MORECOLOR, moreattr, OFF);
210     wrefresh(win);
211
212     /* cancel mesg suppression; all messages will have had chance to be read */
213     curses_got_input();
214
215     oldcrsr = curs_set(1);
216     do {
217         ret = wgetch(win);
218         if (ret == ERR || ret == '\0')
219             ret = '\n';
220         /* msgtype=stop should require space/enter rather than any key,
221            as we want to prevent YASD from direction keys. */
222     } while (!index(resp, (char) ret));
223     if (oldcrsr >= 0)
224         (void) curs_set(oldcrsr);
225
226     if (height == 1) {
227         curses_clear_unhighlight_message_window();
228     } else {
229         mx -= 2, mvwprintw(win, my, mx, "  "); /* back up and blank out ">>" */
230         if (!noscroll) {
231             scroll_window(MESSAGE_WIN);
232         }
233         wrefresh(win);
234     }
235     return ret;
236 }
237
238 int
239 curses_more()
240 {
241     return curses_block(FALSE);
242 }
243
244
245 /* Clear the message window if one line; otherwise unhighlight old messages */
246
247 void
248 curses_clear_unhighlight_message_window()
249 {
250     int mh, mw, count,
251         brdroffset = curses_window_has_border(MESSAGE_WIN) ? 1 : 0;
252     WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
253
254     turn_lines = 0;
255     curses_get_window_size(MESSAGE_WIN, &mh, &mw);
256
257     if (mh == 1) {
258         curses_clear_nhwin(MESSAGE_WIN);
259         mx = my = brdroffset;
260     } else {
261         mx = mw + brdroffset; /* Force new line on new turn */
262
263         for (count = 0; count < mh; count++)
264             mvwchgat(win, count + brdroffset, brdroffset,
265                      mw, COLOR_PAIR(8), A_NORMAL, NULL);
266         wnoutrefresh(win);
267     }
268     wmove(win, my, mx);
269 }
270
271
272 /* Reset message window cursor to starting position, and display most
273    recent messages. */
274
275 void
276 curses_last_messages()
277 {
278     boolean border = curses_window_has_border(MESSAGE_WIN);
279     nhprev_mesg *mesg;
280     int i, j, height, width;
281
282     curses_get_window_size(MESSAGE_WIN, &height, &width);
283
284     if (border)
285         mx = my = 1;
286     else
287         mx = my = 0;
288
289     ++last_messages;
290     for (j = 0, i = num_messages - 1; i > 0 && j < height; --i, ++j) {
291         mesg = get_msg_line(TRUE, i);
292         if (mesg && mesg->str && *mesg->str)
293             curses_message_win_puts(mesg->str, TRUE);
294     }
295     curses_message_win_puts(toplines, TRUE);
296     --last_messages;
297 }
298
299
300 /* Initialize list for message history */
301
302 void
303 curses_init_mesg_history()
304 {
305     max_messages = iflags.msg_history;
306
307     if (max_messages < 1) {
308         max_messages = 1;
309     }
310
311     if (max_messages > MESG_HISTORY_MAX) {
312         max_messages = MESG_HISTORY_MAX;
313     }
314 }
315
316 /* Delete message history at game end. */
317
318 void
319 curses_teardown_messages(void)
320 {
321     nhprev_mesg *current_mesg;
322
323     while ((current_mesg = first_mesg) != 0) {
324         first_mesg = current_mesg->next_mesg;
325         free(current_mesg->str);
326         free(current_mesg);
327     }
328     last_mesg = (nhprev_mesg *) 0;
329     num_messages = 0;
330 }
331
332 /* Display previous message window messages in reverse chron order */
333
334 void
335 curses_prev_mesg()
336 {
337     int count;
338     winid wid;
339     long turn = 0;
340     anything Id;
341     nhprev_mesg *mesg;
342     menu_item *selected = NULL;
343     boolean do_lifo = (iflags.prevmsg_window != 'f');
344
345     wid = curses_get_wid(NHW_MENU);
346     curses_create_nhmenu(wid);
347     Id = zeroany;
348
349     for (count = 0; count < num_messages; ++count) {
350         mesg = get_msg_line(do_lifo, count);
351         if (turn != mesg->turn && count != 0) {
352             curses_add_menu(wid, NO_GLYPH, &Id, 0, 0, A_NORMAL, "---", FALSE);
353         }
354         curses_add_menu(wid, NO_GLYPH, &Id, 0, 0, A_NORMAL, mesg->str, FALSE);
355         turn = mesg->turn;
356     }
357     if (!count)
358         curses_add_menu(wid, NO_GLYPH, &Id, 0, 0, A_NORMAL,
359                         "[No past messages available.]", FALSE);
360
361     curses_end_menu(wid, "");
362     if (!do_lifo)
363         curs_menu_set_bottom_heavy(wid);
364     curses_select_menu(wid, PICK_NONE, &selected);
365     curses_del_wid(wid);
366 }
367
368
369 /* Display at the bottom of the message window without remembering the
370    line for ^P recall.  Used for putstr(WIN_MESSAGE,ATR_NOHISTORY,text)
371    which core currently uses for 'Count: 123' and dolook's autodescribe.
372    popup_dialog is not currently implemented for this function. */
373
374 void
375 curses_count_window(const char *count_text)
376 {
377     static WINDOW *countwin = NULL;
378     int startx, starty, winx, winy;
379     int messageh, messagew;
380
381     if (!count_text) {
382         if (countwin)
383             delwin(countwin), countwin = NULL;
384         counting = FALSE;
385         return;
386     }
387     counting = TRUE;
388
389     curses_get_window_xy(MESSAGE_WIN, &winx, &winy);
390     curses_get_window_size(MESSAGE_WIN, &messageh, &messagew);
391
392     if (curses_window_has_border(MESSAGE_WIN)) {
393         winx++;
394         winy++;
395     }
396
397     winy += messageh - 1;
398
399 #ifdef PDCURSES
400     if (countwin)
401         curses_destroy_win(countwin), countwin = NULL;
402 #endif /* PDCURSES */
403     /* this used to specify a width of 25; that was adequate for 'Count: 123'
404        but not for dolook's autodescribe when it refers to a named monster */
405     if (!countwin)
406         countwin = newwin(1, messagew, winy, winx);
407     startx = 0;
408     starty = 0;
409
410     mvwprintw(countwin, starty, startx, "%s", count_text);
411     wrefresh(countwin);
412 }
413
414 /* Gets a "line" (buffer) of input. */
415 void
416 curses_message_win_getline(const char *prompt, char *answer, int buffer)
417 {
418     int height, width; /* of window */
419     char *tmpbuf, *p_answer; /* combined prompt + answer */
420     int nlines, maxlines, i; /* prompt + answer */
421     int promptline;
422     int promptx;
423     char **linestarts; /* pointers to start of each line */
424     char *tmpstr; /* for free() */
425     int maxy, maxx; /* linewrap / scroll */
426     int ch;
427     WINDOW *win = curses_get_nhwin(MESSAGE_WIN);
428     int border_space = 0;
429     int len = 0; /* of answer string */
430     boolean border = curses_window_has_border(MESSAGE_WIN);
431
432     *answer = '\0';
433     orig_cursor = curs_set(0);
434
435     curses_get_window_size(MESSAGE_WIN, &height, &width);
436     if (border) {
437         height -= 2, width -= 2;
438         border_space = 1;
439         if (mx < 1)
440             mx = 1;
441         if (my < 1)
442             my = 1;
443     }
444     maxy = height - 1 + border_space;
445     maxx = width - 1 + border_space;
446
447     tmpbuf = (char *) alloc((unsigned) ((int) strlen(prompt) + buffer + 2));
448     maxlines = buffer / width * 2;
449     Strcpy(tmpbuf, prompt);
450     Strcat(tmpbuf, " ");
451     nlines = curses_num_lines(tmpbuf, width);
452     maxlines += nlines * 2;
453     linestarts = (char **) alloc((unsigned) (maxlines * sizeof (char *)));
454     p_answer = tmpbuf + strlen(tmpbuf);
455     linestarts[0] = tmpbuf;
456
457     if (mx > border_space) { /* newline */
458         if (my >= maxy)
459             scroll_window(MESSAGE_WIN);
460         else
461             my++;
462         mx = border_space;
463     }
464
465     curses_toggle_color_attr(win, NONE, A_BOLD, ON);
466
467     for (i = 0; i < nlines - 1; i++) {
468         tmpstr = curses_break_str(linestarts[i], width - 1, 1);
469         linestarts[i + 1] = linestarts[i] + (int) strlen(tmpstr);
470         if (*linestarts[i + 1] == ' ')
471             linestarts[i + 1]++;
472         mvwaddstr(win, my, mx, tmpstr);
473         free(tmpstr);
474         if (++my >= maxy) {
475             scroll_window(MESSAGE_WIN);
476             my--;
477         }
478     }
479     mvwaddstr(win, my, mx, linestarts[nlines - 1]);
480     mx = promptx = (int) strlen(linestarts[nlines - 1]) + border_space;
481     promptline = nlines - 1;
482
483     while (1) {
484         mx = (int) strlen(linestarts[nlines - 1]) + border_space;
485         if (mx > maxx) {
486             if (nlines < maxlines) {
487                 tmpstr = curses_break_str(linestarts[nlines - 1],
488                                           width - 1, 1);
489                 mx = (int) strlen(tmpstr) + border_space;
490                 mvwprintw(win, my, mx, "%*c", maxx - mx + 1, ' ');
491                 if (++my > maxy) {
492                     scroll_window(MESSAGE_WIN);
493                     my--;
494                 }
495                 mx = border_space;
496                 linestarts[nlines] = linestarts[nlines - 1]
497                                     + (int) strlen(tmpstr);
498                 if (*linestarts[nlines] == ' ')
499                     linestarts[nlines]++;
500                 mvwaddstr(win, my, mx, linestarts[nlines]);
501                 mx = (int) strlen(linestarts[nlines]) + border_space;
502                 nlines++;
503                 free(tmpstr);
504             } else {
505                 p_answer[--len] = '\0';
506                 mvwaddch(win, my, --mx, ' ');
507             }
508         }
509         wmove(win, my, mx);
510         curs_set(1);
511         wrefresh(win);
512         curses_got_input(); /* despite its name, before rathre than after... */
513 #ifdef PDCURSES
514         ch = wgetch(win);
515 #else
516         ch = getch();
517 #endif
518 #if 0   /* [erase_char (delete one character) and kill_char (delete all
519          * characters) are from tty and not currently set up for curses] */
520         if (ch == erase_char) {
521             ch = '\177'; /* match switch-case below */
522
523         /* honor kill_char if it's ^U or similar, but not if it's '@' */
524         } else if (ch == kill_char && (ch < ' ' || ch >= '\177')) { /*ASCII*/
525             if (len == 0) /* nothing to kill; just start over */
526                 continue;
527             ch = '\033'; /* get rid of all current input, then start over */
528         }
529 #endif
530         curs_set(0);
531         switch (ch) {
532         case '\033': /* DOESCAPE */
533             /* if there isn't any input yet, return ESC */
534             if (len == 0) {
535                 Strcpy(answer, "\033");
536                 return;
537             }
538             /* otherwise, discard current input and start over;
539                first need to blank it from the screen */
540             while (nlines - 1 > promptline) {
541                 if (nlines-- > height) {
542                     unscroll_window(MESSAGE_WIN);
543                     tmpstr = curses_break_str(linestarts[nlines - height],
544                                               width - 1, 1);
545                     mvwaddstr(win, border_space, border_space, tmpstr);
546                     free(tmpstr);
547                 } else {
548                     mx = border_space;
549                     mvwprintw(win, my, mx, "%*c", maxx - mx, ' ');
550                     my--;
551                 }
552             }
553             mx = promptx;
554             mvwprintw(win, my, mx, "%*c", maxx - mx, ' ');
555             *p_answer = '\0';
556             len = 0;
557             break;
558         case ERR: /* should not happen */
559             *answer = '\0';
560             free(tmpbuf);
561             free(linestarts);
562             curs_set(orig_cursor);
563             curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
564             return;
565         case '\r':
566         case '\n':
567             free(linestarts);
568             (void) strncpy(answer, p_answer, buffer);
569             answer[buffer - 1] = '\0';
570             Strcpy(toplines, tmpbuf);
571             mesg_add_line(tmpbuf);
572             free(tmpbuf);
573             curs_set(orig_cursor);
574             curses_toggle_color_attr(win, NONE, A_BOLD, OFF);
575             if (++my > maxy) {
576                 scroll_window(MESSAGE_WIN);
577                 my--;
578             }
579             mx = border_space;
580             return;
581         case '\177': /* DEL/Rubout */
582         case KEY_DC: /* delete-character */
583         case '\b': /* ^H (Backspace: '\011') */
584         case KEY_BACKSPACE:
585             if (len < 1) {
586                 len = 1;
587                 mx = promptx;
588             }
589             p_answer[--len] = '\0';
590             mvwaddch(win, my, --mx, ' ');
591             /* try to unwrap back to the previous line if there is one */
592             if (nlines > 1 && (int) strlen(linestarts[nlines - 2]) < width) {
593                 mvwaddstr(win, my - 1, border_space, linestarts[nlines - 2]);
594                 if (nlines-- > height) {
595                     unscroll_window(MESSAGE_WIN);
596                     tmpstr = curses_break_str(linestarts[nlines - height],
597                                               width - 1, 1);
598                     mvwaddstr(win, border_space, border_space, tmpstr);
599                     free(tmpstr);
600                 } else {
601                     /* clean up the leftovers on the next line,
602                        if we didn't scroll it away */
603                     mvwprintw(win, my--, border_space, "%*c",
604                               (int) strlen(linestarts[nlines]), ' ');
605                 }
606             }
607             break;
608         default:
609             p_answer[len++] = ch;
610             if (len >= buffer)
611                 len = buffer - 1;
612             else
613                 mvwaddch(win, my, mx, ch);
614             p_answer[len] = '\0';
615         }
616     }
617 }
618
619 /* Scroll lines upward in given window, or clear window if only one line. */
620 static void
621 scroll_window(winid wid)
622 {
623     directional_scroll(wid, 1);
624 }
625
626 static void
627 unscroll_window(winid wid)
628 {
629     directional_scroll(wid, -1);
630 }
631
632 static void
633 directional_scroll(winid wid, int nlines)
634 {
635     int wh, ww, s_top, s_bottom;
636     boolean border = curses_window_has_border(wid);
637     WINDOW *win = curses_get_nhwin(wid);
638
639     curses_get_window_size(wid, &wh, &ww);
640     if (wh == 1) {
641         curses_clear_nhwin(wid);
642         return;
643     }
644     if (border) {
645         s_top = 1;
646         s_bottom = wh;
647     } else {
648         s_top = 0;
649         s_bottom = wh - 1;
650     }
651     scrollok(win, TRUE);
652     wsetscrreg(win, s_top, s_bottom);
653     wscrl(win, nlines);
654     scrollok(win, FALSE);
655     if (wid == MESSAGE_WIN) {
656         if (border)
657             mx = 1;
658         else
659             mx = 0;
660     }
661     if (border) {
662         box(win, 0, 0);
663     }
664     wrefresh(win);
665 }
666
667
668 /* Add given line to message history */
669
670 static void
671 mesg_add_line(const char *mline)
672 {
673     nhprev_mesg *current_mesg;
674
675     /*
676      * Messages are kept in a doubly linked list, with head 'first_mesg',
677      * tail 'last_mesg', and a maximum capacity of 'max_messages'.
678      */
679     if (num_messages < max_messages) {
680         /* create a new list element */
681         current_mesg = (nhprev_mesg *) alloc((unsigned) sizeof (nhprev_mesg));
682         current_mesg->str = dupstr(mline);
683     } else {
684         /* instead of discarding list element being forced out, reuse it */
685         current_mesg = first_mesg;
686         /* whenever new 'mline' is shorter, extra allocation size of the
687            original element will be frittered away, until eventually we'll
688            discard this 'str' and dupstr() a replacement; we could easily
689            track the allocation size but don't really need to do so */
690         if (strlen(mline) <= strlen(current_mesg->str)) {
691             Strcpy(current_mesg->str, mline);
692         } else {
693             free((genericptr_t) current_mesg->str);
694             current_mesg->str = dupstr(mline);
695         }
696     }
697     current_mesg->turn = moves;
698
699     if (num_messages == 0) {
700         /* very first message; set up head */
701         first_mesg = current_mesg;
702     } else {
703         /* not first message; tail exists */
704         last_mesg->next_mesg = current_mesg;
705     }
706     current_mesg->prev_mesg = last_mesg;
707     last_mesg = current_mesg; /* new tail */
708
709     if (num_messages < max_messages) {
710         /* wasn't at capacity yet */
711         ++num_messages;
712     } else {
713         /* at capacity; old head is being removed */
714         first_mesg = first_mesg->next_mesg; /* new head */
715         first_mesg->prev_mesg = NULL; /* head has no prev_mesg */
716     }
717     /* since 'current_mesg' might be reusing 'first_mesg' and has now
718        become 'last_mesg', this update must be after head replacement */
719     last_mesg->next_mesg = NULL; /* tail has no next_mesg */
720 }
721
722
723 /* Returns specified line from message history, or NULL if out of bounds */
724
725 static nhprev_mesg *
726 get_msg_line(boolean reverse, int mindex)
727 {
728     int count;
729     nhprev_mesg *current_mesg;
730
731     if (reverse) {
732         current_mesg = last_mesg;
733         for (count = 0; count < mindex; count++) {
734             if (!current_mesg)
735                 break;
736             current_mesg = current_mesg->prev_mesg;
737         }
738     } else {
739         current_mesg = first_mesg;
740         for (count = 0; count < mindex; count++) {
741             if (!current_mesg)
742                 break;
743             current_mesg = current_mesg->next_mesg;
744         }
745     }
746     return current_mesg;
747 }
748
749 /* save/restore code retrieves one ^P message at a time during save and
750    puts it into save file; if any new messages are added to the list while
751    that is taking place, the results are likely to be scrambled */
752 char *
753 curses_getmsghistory(init)
754 boolean init;
755 {
756     static int nxtidx;
757     nhprev_mesg *mesg;
758
759     if (init)
760         nxtidx = 0;
761     else
762         ++nxtidx;
763
764     if (nxtidx < num_messages) {
765         /* we could encode mesg->turn with the text of the message,
766            but then that text might need to be truncated, and more
767            significantly, restoring the save file with another
768            interface wouldn't know how find and decode or remove it;
769            likewise, restoring another interface's save file with
770            curses wouldn't find the expected turn info;
771            so, we live without that */
772         mesg = get_msg_line(FALSE, nxtidx);
773     } else
774         mesg = (nhprev_mesg *) 0;
775
776     return mesg ? mesg->str : (char *) 0;
777 }
778
779 /*
780  * This is called by the core savefile restore routines.
781  * Each time we are called, we stuff the string into our message
782  * history recall buffer. The core will send the oldest message
783  * first (actually it sends them in the order they exist in the
784  * save file, but that is supposed to be the oldest first).
785  * These messages get pushed behind any which have been issued
786  * during this session since they come from a previous session
787  * and logically precede anything (like "Restoring save file...")
788  * that's happened now.
789  *
790  * Called with a null pointer to finish up restoration.
791  *
792  * It's also called by the quest pager code when a block message
793  * has a one-line summary specified.  We put that line directly
794  * into message history for ^P recall without having displayed it.
795  */
796 void
797 curses_putmsghistory(msg, restoring_msghist)
798 const char *msg;
799 boolean restoring_msghist;
800 {
801     static boolean initd = FALSE;
802     static int stash_count;
803     static nhprev_mesg *stash_head = 0;
804
805     if (restoring_msghist && !initd) {
806         /* hide any messages we've gathered since starting current session
807            so that the ^P data will start out empty as we add ones being
808            restored from save file; we'll put these back after that's done */
809         stash_count = num_messages, num_messages = 0;
810         stash_head = first_mesg, first_mesg = (nhprev_mesg *) 0;
811         last_mesg = (nhprev_mesg *) 0; /* no need to remember the tail */
812         initd = TRUE;
813     }
814
815     if (msg) {
816         mesg_add_line(msg);
817         /* treat all saved and restored messages as turn #1 */
818         last_mesg->turn = 1L;
819     } else if (stash_count) {
820         nhprev_mesg *mesg;
821         long mesg_turn;
822
823         /* put any messages generated during the beginning of the current
824            session back; they logically follow any from the previous
825            session's save file */
826         while (stash_count > 0) {
827             /* we could manipulate the linked list directly but treating
828                stashed messages as newly occurring ones is much simpler;
829                we ignore the backlinks because the list is destroyed as it
830                gets processed hence there can't be any other traversals */
831             mesg = stash_head;
832             stash_head = mesg->next_mesg;
833             --stash_count;
834             mesg_turn = mesg->turn;
835             mesg_add_line(mesg->str);
836             /* added line became new tail */
837             last_mesg->turn = mesg_turn;
838             free((genericptr_t) mesg->str);
839             free((genericptr_t) mesg);
840         }
841         initd = FALSE; /* reset */
842     }
843 }
844
845 /*cursmesg.c*/